Merge tag 'wireless-drivers-next-2020-05-07' of git://git.kernel.org/pub/scm/linux...
authorDavid S. Miller <davem@davemloft.net>
Thu, 7 May 2020 20:22:35 +0000 (13:22 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 7 May 2020 20:22:35 +0000 (13:22 -0700)
Kalle Valo says:

====================
wireless-drivers-next patches for v5.8

First set of patches for v5.8. Changes all over, ath10k apparently
seeing most new features this time. rtw88 also had lots of changes due
to preparation for new hardware support.

In this pull request there's also a new macro to include/linux/iopoll:
read_poll_timeout_atomic(). This is needed by rtw88 for atomic
polling.

Major changes:

ath11k

* add debugfs file for testing ADDBA and DELBA

* add 802.11 encapsulation offload on hardware support

* add htt_peer_stats_reset debugfs file

ath10k

* enable VHT160 and VHT80+80 modes

* enable radar detection in secondary segment

* sdio: disable TX complete indication to improve throughput

* sdio: decrease power consumption

* sdio: add HTT TX bundle support to increase throughput

* sdio: add rx bitrate reporting

ath9k

* improvements to AR9002 calibration logic

carl9170

* remove buggy P2P_GO support

p54usb

* add support for AirVasT USB stick

rtw88

* add support for antenna configuration

ti wlcore

* add support for AES_CMAC cipher

iwlwifi

* support for a few new FW API versions

* new hw configs
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
1998 files changed:
.clang-format
Documentation/ABI/testing/sysfs-class-net
Documentation/admin-guide/device-mapper/dm-integrity.rst
Documentation/admin-guide/kernel-parameters.txt
Documentation/admin-guide/serial-console.rst
Documentation/admin-guide/sysctl/kernel.rst
Documentation/admin-guide/sysctl/net.rst
Documentation/arm64/amu.rst
Documentation/bpf/index.rst
Documentation/core-api/timekeeping.rst
Documentation/dev-tools/kselftest.rst
Documentation/devicetree/bindings/Makefile
Documentation/devicetree/bindings/arm/sunxi/allwinner,sun4i-a10-mbus.yaml
Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml
Documentation/devicetree/bindings/display/bridge/adi,adv7123.txt
Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt
Documentation/devicetree/bindings/display/panel/leadtek,ltk500hd1829.yaml
Documentation/devicetree/bindings/display/panel/lvds.yaml
Documentation/devicetree/bindings/display/panel/xinpeng,xpp055c272.yaml
Documentation/devicetree/bindings/dma/adi,axi-dmac.txt
Documentation/devicetree/bindings/dma/socionext,uniphier-xdmac.yaml
Documentation/devicetree/bindings/hwmon/adi,axi-fan-control.yaml
Documentation/devicetree/bindings/hwmon/adt7475.yaml
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
Documentation/devicetree/bindings/iio/dac/ad5755.txt
Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml
Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml
Documentation/devicetree/bindings/interrupt-controller/loongson,liointc.yaml
Documentation/devicetree/bindings/memory-controllers/nvidia,tegra186-mc.yaml
Documentation/devicetree/bindings/mfd/rohm,bd71837-pmic.yaml
Documentation/devicetree/bindings/mfd/rohm,bd71847-pmic.yaml
Documentation/devicetree/bindings/mfd/st,stpmic1.yaml
Documentation/devicetree/bindings/net/ethernet-phy.yaml
Documentation/devicetree/bindings/net/fsl-fec.txt
Documentation/devicetree/bindings/net/mdio.yaml
Documentation/devicetree/bindings/net/nxp,tja11xx.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/net/qca,ar71xx.txt [deleted file]
Documentation/devicetree/bindings/net/qca,ar71xx.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/net/qcom,ipa.yaml
Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/net/qcom,ipq8064-mdio.yaml
Documentation/devicetree/bindings/net/qualcomm-bluetooth.txt
Documentation/devicetree/bindings/net/ti,k3-am654-cpsw-nuss.yaml
Documentation/devicetree/bindings/net/ti,k3-am654-cpts.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml
Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml
Documentation/devicetree/bindings/pwm/google,cros-ec-pwm.yaml
Documentation/devicetree/bindings/regulator/mps,mp5416.yaml
Documentation/devicetree/bindings/regulator/mps,mpq7920.yaml
Documentation/devicetree/bindings/regulator/rohm,bd71828-regulator.yaml
Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.yaml
Documentation/devicetree/bindings/regulator/rohm,bd71847-regulator.yaml
Documentation/devicetree/bindings/rng/brcm,bcm2835.yaml
Documentation/devicetree/bindings/sound/rockchip-i2s.yaml
Documentation/devicetree/bindings/sound/rockchip-spdif.txt [deleted file]
Documentation/devicetree/bindings/sound/rockchip-spdif.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.yaml
Documentation/devicetree/bindings/usb/ingenic,musb.yaml
Documentation/devicetree/bindings/usb/qcom,dwc3.txt
Documentation/devicetree/bindings/usb/renesas,usb3-peri.yaml
Documentation/devicetree/bindings/usb/renesas,usbhs.yaml
Documentation/devicetree/bindings/usb/rockchip,dwc3.txt
Documentation/devicetree/bindings/usb/usb-xhci.txt
Documentation/filesystems/afs.rst
Documentation/filesystems/debugfs.rst
Documentation/hwmon/bcm54140.rst [new file with mode: 0644]
Documentation/hwmon/index.rst
Documentation/hwmon/isl68137.rst
Documentation/kbuild/makefiles.rst
Documentation/networking/6pack.rst [new file with mode: 0644]
Documentation/networking/6pack.txt [deleted file]
Documentation/networking/PLIP.txt [deleted file]
Documentation/networking/altera_tse.rst [new file with mode: 0644]
Documentation/networking/altera_tse.txt [deleted file]
Documentation/networking/arcnet-hardware.rst [new file with mode: 0644]
Documentation/networking/arcnet-hardware.txt [deleted file]
Documentation/networking/arcnet.rst [new file with mode: 0644]
Documentation/networking/arcnet.txt [deleted file]
Documentation/networking/atm.rst [new file with mode: 0644]
Documentation/networking/atm.txt [deleted file]
Documentation/networking/ax25.rst [new file with mode: 0644]
Documentation/networking/ax25.txt [deleted file]
Documentation/networking/baycom.rst [new file with mode: 0644]
Documentation/networking/baycom.txt [deleted file]
Documentation/networking/bonding.rst [new file with mode: 0644]
Documentation/networking/bonding.txt [deleted file]
Documentation/networking/caif/Linux-CAIF.txt [deleted file]
Documentation/networking/caif/caif.rst
Documentation/networking/caif/index.rst [new file with mode: 0644]
Documentation/networking/caif/linux_caif.rst [new file with mode: 0644]
Documentation/networking/caif/spi_porting.rst [new file with mode: 0644]
Documentation/networking/caif/spi_porting.txt [deleted file]
Documentation/networking/can.rst
Documentation/networking/cdc_mbim.rst [new file with mode: 0644]
Documentation/networking/cdc_mbim.txt [deleted file]
Documentation/networking/checksum-offloads.rst
Documentation/networking/cops.rst [new file with mode: 0644]
Documentation/networking/cops.txt [deleted file]
Documentation/networking/cxacru.rst [new file with mode: 0644]
Documentation/networking/cxacru.txt [deleted file]
Documentation/networking/dccp.rst [new file with mode: 0644]
Documentation/networking/dccp.txt [deleted file]
Documentation/networking/dctcp.rst [new file with mode: 0644]
Documentation/networking/dctcp.txt [deleted file]
Documentation/networking/decnet.rst [new file with mode: 0644]
Documentation/networking/decnet.txt [deleted file]
Documentation/networking/defza.rst [new file with mode: 0644]
Documentation/networking/defza.txt [deleted file]
Documentation/networking/device_drivers/3com/3c509.rst [new file with mode: 0644]
Documentation/networking/device_drivers/3com/3c509.txt [deleted file]
Documentation/networking/device_drivers/3com/vortex.rst [new file with mode: 0644]
Documentation/networking/device_drivers/3com/vortex.txt [deleted file]
Documentation/networking/device_drivers/amazon/ena.rst [new file with mode: 0644]
Documentation/networking/device_drivers/amazon/ena.txt [deleted file]
Documentation/networking/device_drivers/aquantia/atlantic.rst [new file with mode: 0644]
Documentation/networking/device_drivers/aquantia/atlantic.txt [deleted file]
Documentation/networking/device_drivers/chelsio/cxgb.rst [new file with mode: 0644]
Documentation/networking/device_drivers/chelsio/cxgb.txt [deleted file]
Documentation/networking/device_drivers/cirrus/cs89x0.rst [new file with mode: 0644]
Documentation/networking/device_drivers/cirrus/cs89x0.txt [deleted file]
Documentation/networking/device_drivers/davicom/dm9000.rst [new file with mode: 0644]
Documentation/networking/device_drivers/davicom/dm9000.txt [deleted file]
Documentation/networking/device_drivers/dec/de4x5.rst [new file with mode: 0644]
Documentation/networking/device_drivers/dec/de4x5.txt [deleted file]
Documentation/networking/device_drivers/dec/dmfe.rst [new file with mode: 0644]
Documentation/networking/device_drivers/dec/dmfe.txt [deleted file]
Documentation/networking/device_drivers/dlink/dl2k.rst [new file with mode: 0644]
Documentation/networking/device_drivers/dlink/dl2k.txt [deleted file]
Documentation/networking/device_drivers/freescale/dpaa.rst [new file with mode: 0644]
Documentation/networking/device_drivers/freescale/dpaa.txt [deleted file]
Documentation/networking/device_drivers/freescale/gianfar.rst [new file with mode: 0644]
Documentation/networking/device_drivers/freescale/gianfar.txt [deleted file]
Documentation/networking/device_drivers/index.rst
Documentation/networking/device_drivers/intel/e100.rst
Documentation/networking/device_drivers/intel/ipw2100.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/ipw2100.txt [deleted file]
Documentation/networking/device_drivers/intel/ipw2200.rst [new file with mode: 0644]
Documentation/networking/device_drivers/intel/ipw2200.txt [deleted file]
Documentation/networking/device_drivers/intel/ixgb.rst
Documentation/networking/device_drivers/microsoft/netvsc.rst [new file with mode: 0644]
Documentation/networking/device_drivers/microsoft/netvsc.txt [deleted file]
Documentation/networking/device_drivers/neterion/s2io.rst [new file with mode: 0644]
Documentation/networking/device_drivers/neterion/s2io.txt [deleted file]
Documentation/networking/device_drivers/neterion/vxge.rst [new file with mode: 0644]
Documentation/networking/device_drivers/neterion/vxge.txt [deleted file]
Documentation/networking/device_drivers/qualcomm/rmnet.rst [new file with mode: 0644]
Documentation/networking/device_drivers/qualcomm/rmnet.txt [deleted file]
Documentation/networking/device_drivers/sb1000.rst [new file with mode: 0644]
Documentation/networking/device_drivers/sb1000.txt [deleted file]
Documentation/networking/device_drivers/smsc/smc9.rst [new file with mode: 0644]
Documentation/networking/device_drivers/smsc/smc9.txt [deleted file]
Documentation/networking/device_drivers/ti/cpsw.rst [new file with mode: 0644]
Documentation/networking/device_drivers/ti/cpsw.txt [deleted file]
Documentation/networking/device_drivers/ti/cpsw_switchdev.rst [new file with mode: 0644]
Documentation/networking/device_drivers/ti/cpsw_switchdev.txt [deleted file]
Documentation/networking/device_drivers/ti/tlan.rst [new file with mode: 0644]
Documentation/networking/device_drivers/ti/tlan.txt [deleted file]
Documentation/networking/device_drivers/toshiba/spider_net.rst [new file with mode: 0644]
Documentation/networking/device_drivers/toshiba/spider_net.txt [deleted file]
Documentation/networking/devlink/devlink-region.rst
Documentation/networking/devlink/devlink-trap.rst
Documentation/networking/devlink/ice.rst
Documentation/networking/dns_resolver.rst [new file with mode: 0644]
Documentation/networking/dns_resolver.txt [deleted file]
Documentation/networking/driver.rst [new file with mode: 0644]
Documentation/networking/driver.txt [deleted file]
Documentation/networking/eql.rst [new file with mode: 0644]
Documentation/networking/eql.txt [deleted file]
Documentation/networking/ethtool-netlink.rst
Documentation/networking/fib_trie.rst [new file with mode: 0644]
Documentation/networking/fib_trie.txt [deleted file]
Documentation/networking/filter.rst [new file with mode: 0644]
Documentation/networking/filter.txt [deleted file]
Documentation/networking/fore200e.rst [new file with mode: 0644]
Documentation/networking/fore200e.txt [deleted file]
Documentation/networking/framerelay.rst [new file with mode: 0644]
Documentation/networking/framerelay.txt [deleted file]
Documentation/networking/gen_stats.rst [new file with mode: 0644]
Documentation/networking/gen_stats.txt [deleted file]
Documentation/networking/generic-hdlc.rst [new file with mode: 0644]
Documentation/networking/generic-hdlc.txt [deleted file]
Documentation/networking/generic_netlink.rst [new file with mode: 0644]
Documentation/networking/generic_netlink.txt [deleted file]
Documentation/networking/gtp.rst [new file with mode: 0644]
Documentation/networking/gtp.txt [deleted file]
Documentation/networking/hinic.rst [new file with mode: 0644]
Documentation/networking/hinic.txt [deleted file]
Documentation/networking/ila.rst [new file with mode: 0644]
Documentation/networking/ila.txt [deleted file]
Documentation/networking/index.rst
Documentation/networking/ip-sysctl.rst [new file with mode: 0644]
Documentation/networking/ip-sysctl.txt [deleted file]
Documentation/networking/ip_dynaddr.rst [new file with mode: 0644]
Documentation/networking/ip_dynaddr.txt [deleted file]
Documentation/networking/ipddp.rst [new file with mode: 0644]
Documentation/networking/ipddp.txt [deleted file]
Documentation/networking/iphase.rst [new file with mode: 0644]
Documentation/networking/iphase.txt [deleted file]
Documentation/networking/ipsec.rst [new file with mode: 0644]
Documentation/networking/ipsec.txt [deleted file]
Documentation/networking/ipv6.rst [new file with mode: 0644]
Documentation/networking/ipv6.txt [deleted file]
Documentation/networking/ipvlan.rst [new file with mode: 0644]
Documentation/networking/ipvlan.txt [deleted file]
Documentation/networking/ipvs-sysctl.rst [new file with mode: 0644]
Documentation/networking/ipvs-sysctl.txt [deleted file]
Documentation/networking/kcm.rst [new file with mode: 0644]
Documentation/networking/kcm.txt [deleted file]
Documentation/networking/l2tp.rst [new file with mode: 0644]
Documentation/networking/l2tp.txt [deleted file]
Documentation/networking/lapb-module.rst [new file with mode: 0644]
Documentation/networking/lapb-module.txt [deleted file]
Documentation/networking/ltpc.rst [new file with mode: 0644]
Documentation/networking/ltpc.txt [deleted file]
Documentation/networking/mac80211-injection.rst [new file with mode: 0644]
Documentation/networking/mac80211-injection.txt [deleted file]
Documentation/networking/mpls-sysctl.rst [new file with mode: 0644]
Documentation/networking/mpls-sysctl.txt [deleted file]
Documentation/networking/multiqueue.rst [new file with mode: 0644]
Documentation/networking/multiqueue.txt [deleted file]
Documentation/networking/net_dim.rst [new file with mode: 0644]
Documentation/networking/net_dim.txt [deleted file]
Documentation/networking/netconsole.rst [new file with mode: 0644]
Documentation/networking/netconsole.txt [deleted file]
Documentation/networking/netdev-features.rst [new file with mode: 0644]
Documentation/networking/netdev-features.txt [deleted file]
Documentation/networking/netdevices.rst [new file with mode: 0644]
Documentation/networking/netdevices.txt [deleted file]
Documentation/networking/netfilter-sysctl.rst [new file with mode: 0644]
Documentation/networking/netfilter-sysctl.txt [deleted file]
Documentation/networking/netif-msg.rst [new file with mode: 0644]
Documentation/networking/netif-msg.txt [deleted file]
Documentation/networking/nf_conntrack-sysctl.rst [new file with mode: 0644]
Documentation/networking/nf_conntrack-sysctl.txt [deleted file]
Documentation/networking/nf_flowtable.rst [new file with mode: 0644]
Documentation/networking/nf_flowtable.txt [deleted file]
Documentation/networking/openvswitch.rst [new file with mode: 0644]
Documentation/networking/openvswitch.txt [deleted file]
Documentation/networking/operstates.rst [new file with mode: 0644]
Documentation/networking/operstates.txt [deleted file]
Documentation/networking/packet_mmap.rst [new file with mode: 0644]
Documentation/networking/packet_mmap.txt [deleted file]
Documentation/networking/phonet.rst [new file with mode: 0644]
Documentation/networking/phonet.txt [deleted file]
Documentation/networking/pktgen.rst [new file with mode: 0644]
Documentation/networking/pktgen.txt [deleted file]
Documentation/networking/plip.rst [new file with mode: 0644]
Documentation/networking/ppp_generic.rst [new file with mode: 0644]
Documentation/networking/ppp_generic.txt [deleted file]
Documentation/networking/proc_net_tcp.rst [new file with mode: 0644]
Documentation/networking/proc_net_tcp.txt [deleted file]
Documentation/networking/radiotap-headers.rst [new file with mode: 0644]
Documentation/networking/radiotap-headers.txt [deleted file]
Documentation/networking/ray_cs.rst [new file with mode: 0644]
Documentation/networking/ray_cs.txt [deleted file]
Documentation/networking/rds.rst [new file with mode: 0644]
Documentation/networking/rds.txt [deleted file]
Documentation/networking/regulatory.rst [new file with mode: 0644]
Documentation/networking/regulatory.txt [deleted file]
Documentation/networking/rxrpc.rst [new file with mode: 0644]
Documentation/networking/rxrpc.txt [deleted file]
Documentation/networking/sctp.rst [new file with mode: 0644]
Documentation/networking/sctp.txt [deleted file]
Documentation/networking/secid.rst [new file with mode: 0644]
Documentation/networking/secid.txt [deleted file]
Documentation/networking/seg6-sysctl.rst [new file with mode: 0644]
Documentation/networking/seg6-sysctl.txt [deleted file]
Documentation/networking/skfp.rst [new file with mode: 0644]
Documentation/networking/skfp.txt [deleted file]
Documentation/networking/snmp_counter.rst
Documentation/networking/strparser.rst [new file with mode: 0644]
Documentation/networking/strparser.txt [deleted file]
Documentation/networking/switchdev.rst [new file with mode: 0644]
Documentation/networking/switchdev.txt [deleted file]
Documentation/networking/tc-actions-env-rules.rst [new file with mode: 0644]
Documentation/networking/tc-actions-env-rules.txt [deleted file]
Documentation/networking/tcp-thin.rst [new file with mode: 0644]
Documentation/networking/tcp-thin.txt [deleted file]
Documentation/networking/team.rst [new file with mode: 0644]
Documentation/networking/team.txt [deleted file]
Documentation/networking/timestamping.rst [new file with mode: 0644]
Documentation/networking/timestamping.txt [deleted file]
Documentation/networking/tproxy.rst [new file with mode: 0644]
Documentation/networking/tproxy.txt [deleted file]
Documentation/networking/tuntap.rst [new file with mode: 0644]
Documentation/networking/tuntap.txt [deleted file]
Documentation/networking/udplite.rst [new file with mode: 0644]
Documentation/networking/udplite.txt [deleted file]
Documentation/networking/vrf.rst [new file with mode: 0644]
Documentation/networking/vrf.txt [deleted file]
Documentation/networking/vxlan.rst [new file with mode: 0644]
Documentation/networking/vxlan.txt [deleted file]
Documentation/networking/x25-iface.rst [new file with mode: 0644]
Documentation/networking/x25-iface.txt [deleted file]
Documentation/networking/x25.rst [new file with mode: 0644]
Documentation/networking/x25.txt [deleted file]
Documentation/networking/xfrm_device.rst [new file with mode: 0644]
Documentation/networking/xfrm_device.txt [deleted file]
Documentation/networking/xfrm_proc.rst [new file with mode: 0644]
Documentation/networking/xfrm_proc.txt [deleted file]
Documentation/networking/xfrm_sync.rst [new file with mode: 0644]
Documentation/networking/xfrm_sync.txt [deleted file]
Documentation/networking/xfrm_sysctl.rst [new file with mode: 0644]
Documentation/networking/xfrm_sysctl.txt [deleted file]
Documentation/networking/z8530drv.rst [new file with mode: 0644]
Documentation/networking/z8530drv.txt [deleted file]
Documentation/timers/timers-howto.rst
Documentation/x86/boot.rst
MAINTAINERS
Makefile
arch/arc/include/asm/module.h
arch/arc/include/asm/vermagic.h [new file with mode: 0644]
arch/arm/boot/compressed/head.S
arch/arm/boot/dts/bcm2835-rpi.dtsi
arch/arm/boot/dts/bcm283x.dtsi
arch/arm/boot/dts/imx6qdl.dtsi
arch/arm/boot/dts/imx6qp.dtsi
arch/arm/boot/dts/omap3-n950-n9.dtsi
arch/arm/boot/dts/qcom-ipq4019.dtsi
arch/arm/crypto/chacha-glue.c
arch/arm/crypto/nhpoly1305-neon-glue.c
arch/arm/crypto/poly1305-glue.c
arch/arm/include/asm/module.h
arch/arm/include/asm/vermagic.h [new file with mode: 0644]
arch/arm/mach-imx/Makefile
arch/arm/net/bpf_jit_32.c
arch/arm/xen/enlighten.c
arch/arm64/boot/dts/qcom/sdm845.dtsi
arch/arm64/boot/dts/ti/k3-am65-main.dtsi
arch/arm64/boot/dts/ti/k3-am65-mcu.dtsi
arch/arm64/boot/dts/ti/k3-j721e-main.dtsi
arch/arm64/boot/dts/ti/k3-j721e-mcu-wakeup.dtsi
arch/arm64/crypto/chacha-neon-glue.c
arch/arm64/crypto/nhpoly1305-neon-glue.c
arch/arm64/crypto/poly1305-glue.c
arch/arm64/include/asm/module.h
arch/arm64/include/asm/pointer_auth.h
arch/arm64/include/asm/sysreg.h
arch/arm64/include/asm/vermagic.h [new file with mode: 0644]
arch/arm64/kernel/armv8_deprecated.c
arch/arm64/kernel/fpsimd.c
arch/arm64/kernel/vdso.c
arch/arm64/kernel/vdso/Makefile
arch/h8300/kernel/.gitignore [new file with mode: 0644]
arch/hexagon/include/asm/module.h [deleted file]
arch/hexagon/include/asm/vermagic.h [new file with mode: 0644]
arch/ia64/include/asm/module.h
arch/ia64/include/asm/vermagic.h [new file with mode: 0644]
arch/m68k/include/asm/Kbuild
arch/mips/include/asm/module.h
arch/mips/include/asm/vermagic.h [new file with mode: 0644]
arch/mips/lasat/sysctl.c
arch/nds32/include/asm/module.h [deleted file]
arch/nds32/include/asm/vermagic.h [new file with mode: 0644]
arch/powerpc/include/asm/module.h
arch/powerpc/include/asm/vermagic.h [new file with mode: 0644]
arch/powerpc/kernel/entry_32.S
arch/powerpc/kernel/setup_64.c
arch/powerpc/kvm/book3s_64_mmu_hv.c
arch/powerpc/kvm/book3s_64_mmu_radix.c
arch/powerpc/mm/nohash/8xx.c
arch/powerpc/platforms/Kconfig.cputype
arch/riscv/Kconfig
arch/riscv/include/asm/module.h
arch/riscv/include/asm/vermagic.h [new file with mode: 0644]
arch/riscv/kernel/sbi.c
arch/riscv/kernel/stacktrace.c
arch/riscv/kernel/vdso/Makefile
arch/riscv/net/bpf_jit_comp32.c
arch/riscv/net/bpf_jit_comp64.c
arch/s390/appldata/appldata_base.c
arch/s390/boot/uv.c
arch/s390/kernel/debug.c
arch/s390/kernel/diag.c
arch/s390/kernel/smp.c
arch/s390/kernel/topology.c
arch/s390/kernel/trace.c
arch/s390/kernel/uv.c
arch/s390/kvm/interrupt.c
arch/s390/kvm/kvm-s390.c
arch/s390/lib/uaccess.c
arch/s390/mm/cmm.c
arch/s390/mm/pgalloc.c
arch/s390/pci/pci_irq.c
arch/sh/include/asm/module.h
arch/sh/include/asm/vermagic.h [new file with mode: 0644]
arch/sh/mm/init.c
arch/um/Makefile
arch/x86/Kconfig
arch/x86/crypto/blake2s-glue.c
arch/x86/crypto/chacha_glue.c
arch/x86/crypto/nhpoly1305-avx2-glue.c
arch/x86/crypto/nhpoly1305-sse2-glue.c
arch/x86/crypto/poly1305_glue.c
arch/x86/events/intel/cstate.c
arch/x86/hyperv/hv_init.c
arch/x86/include/asm/efi.h
arch/x86/include/asm/microcode_amd.h
arch/x86/include/asm/module.h
arch/x86/include/asm/mshyperv.h
arch/x86/include/asm/nospec-branch.h
arch/x86/include/asm/vermagic.h [new file with mode: 0644]
arch/x86/kernel/cpu/intel.c
arch/x86/kernel/cpu/mshyperv.c
arch/x86/kernel/cpu/resctrl/core.c
arch/x86/kernel/cpu/resctrl/internal.h
arch/x86/kernel/cpu/resctrl/rdtgroup.c
arch/x86/kernel/itmt.c
arch/x86/kernel/smpboot.c
arch/x86/kernel/umip.c
arch/x86/kvm/Makefile
arch/x86/kvm/svm/sev.c
arch/x86/kvm/svm/svm.c
arch/x86/kvm/svm/vmenter.S
arch/x86/kvm/vmx/nested.c
arch/x86/kvm/vmx/vmx.c
arch/x86/kvm/x86.c
arch/x86/net/bpf_jit_comp.c
arch/x86/net/bpf_jit_comp32.c
arch/x86/platform/efi/efi_64.c
arch/xtensa/include/asm/module.h [deleted file]
arch/xtensa/include/asm/vermagic.h [new file with mode: 0644]
block/blk-iocost.c
block/blk-mq.c
block/blk-wbt.c
block/partitions/core.c
drivers/acpi/button.c
drivers/acpi/device_pm.c
drivers/acpi/pci_link.c
drivers/ata/ahci.c
drivers/ata/sata_inic162x.c
drivers/atm/Kconfig
drivers/base/firmware_loader/fallback_table.c
drivers/base/power/main.c
drivers/block/null_blk.h
drivers/block/null_blk_main.c
drivers/block/null_blk_zoned.c
drivers/block/rbd.c
drivers/block/virtio_blk.c
drivers/bluetooth/btqca.c
drivers/bluetooth/btqca.h
drivers/bluetooth/btrtl.c
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_bcm.c
drivers/bluetooth/hci_qca.c
drivers/cdrom/cdrom.c
drivers/char/hw_random/virtio-rng.c
drivers/char/random.c
drivers/char/tpm/tpm-interface.c
drivers/char/tpm/tpm2-cmd.c
drivers/char/tpm/tpm_ibmvtpm.c
drivers/char/tpm/tpm_tis_core.c
drivers/clk/clk-asm9260.c
drivers/clk/mmp/clk-pll.c
drivers/clk/mmp/clk.c
drivers/clk/mmp/clk.h
drivers/clk/sprd/sc9863a-clk.c
drivers/counter/104-quad-8.c
drivers/cpufreq/intel_pstate.c
drivers/crypto/caam/caamalg.c
drivers/crypto/caam/caamhash.c
drivers/crypto/caam/caampkc.c
drivers/crypto/chelsio/chcr_algo.c
drivers/crypto/chelsio/chcr_crypto.h
drivers/crypto/chelsio/chcr_ipsec.c
drivers/crypto/chelsio/chcr_ktls.c
drivers/dma-buf/dma-buf.c
drivers/dma/Kconfig
drivers/dma/dmaengine.c
drivers/dma/dmatest.c
drivers/dma/mmp_tdma.c
drivers/dma/pch_dma.c
drivers/dma/tegra20-apb-dma.c
drivers/dma/ti/k3-psil.c
drivers/dma/xilinx/xilinx_dma.c
drivers/firmware/efi/cper.c
drivers/firmware/efi/libstub/efistub.h
drivers/firmware/efi/libstub/file.c
drivers/firmware/efi/libstub/x86-stub.c
drivers/firmware/imx/Kconfig
drivers/firmware/xilinx/zynqmp-debug.c
drivers/fpga/dfl-pci.c
drivers/fpga/zynq-fpga.c
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
drivers/gpu/drm/amd/amdgpu/cik.c
drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
drivers/gpu/drm/amd/amdgpu/navi10_sdma_pkt_open.h
drivers/gpu/drm/amd/amdgpu/nv.c
drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c
drivers/gpu/drm/amd/amdgpu/soc15.c
drivers/gpu/drm/amd/amdgpu/vi.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
drivers/gpu/drm/amd/display/dc/core/dc_stream.c
drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_init.c
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.c
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.h
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_init.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_mpc.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_mpc.h
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c
drivers/gpu/drm/amd/display/dc/dcn21/dcn21_init.c
drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c
drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h
drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h
drivers/gpu/drm/amd/powerplay/amd_powerplay.c
drivers/gpu/drm/amd/powerplay/hwmgr/processpptables.c
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
drivers/gpu/drm/amd/powerplay/renoir_ppt.c
drivers/gpu/drm/amd/powerplay/smu_v11_0.c
drivers/gpu/drm/bridge/analogix/analogix-anx6345.c
drivers/gpu/drm/drm_dp_mst_topology.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/i915/display/intel_ddi.c
drivers/gpu/drm/i915/display/intel_display_power.c
drivers/gpu/drm/i915/display/intel_dp.c
drivers/gpu/drm/i915/display/intel_dp_aux_backlight.c
drivers/gpu/drm/i915/display/intel_hdmi.c
drivers/gpu/drm/i915/display/intel_sprite.c
drivers/gpu/drm/i915/gem/i915_gem_tiling.c
drivers/gpu/drm/i915/gem/selftests/huge_pages.c
drivers/gpu/drm/i915/gt/intel_rps.c
drivers/gpu/drm/i915/gt/intel_timeline.c
drivers/gpu/drm/i915/gvt/kvmgt.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_perf.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_vma.c
drivers/gpu/drm/meson/meson_dw_hdmi.c
drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp108.c
drivers/gpu/drm/nouveau/nvkm/engine/sec2/tu102.c
drivers/gpu/drm/qxl/qxl_cmd.c
drivers/gpu/drm/qxl/qxl_display.c
drivers/gpu/drm/qxl/qxl_draw.c
drivers/gpu/drm/qxl/qxl_image.c
drivers/gpu/drm/qxl/qxl_ioctl.c
drivers/gpu/drm/scheduler/sched_main.c
drivers/gpu/drm/tidss/tidss_crtc.c
drivers/gpu/drm/tidss/tidss_encoder.c
drivers/gpu/drm/tidss/tidss_plane.c
drivers/gpu/drm/virtio/virtgpu_ioctl.c
drivers/gpu/drm/virtio/virtgpu_kms.c
drivers/hid/Kconfig
drivers/hid/hid-alps.c
drivers/hid/hid-ids.h
drivers/hid/hid-lg-g15.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-quirks.c
drivers/hid/i2c-hid/i2c-hid-core.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/usbhid.h
drivers/hid/wacom_sys.c
drivers/hid/wacom_wac.c
drivers/hv/channel_mgmt.c
drivers/hv/hv.c
drivers/hv/hv_debugfs.c
drivers/hv/hv_trace.h
drivers/hv/hyperv_vmbus.h
drivers/hv/vmbus_drv.c
drivers/hwmon/Kconfig
drivers/hwmon/drivetemp.c
drivers/hwmon/jc42.c
drivers/hwmon/k10temp.c
drivers/hwmon/pmbus/isl68137.c
drivers/i2c/busses/i2c-altera.c
drivers/i2c/busses/i2c-amd-mp2-pci.c
drivers/i2c/busses/i2c-aspeed.c
drivers/i2c/busses/i2c-bcm-iproc.c
drivers/i2c/busses/i2c-designware-platdrv.c
drivers/i2c/i2c-core-base.c
drivers/iio/adc/ad7192.c
drivers/iio/adc/ad7793.c
drivers/iio/adc/stm32-adc.c
drivers/iio/adc/ti-ads8344.c
drivers/iio/adc/xilinx-xadc-core.c
drivers/iio/common/st_sensors/st_sensors_core.c
drivers/iio/dac/ad5770r.c
drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
drivers/iio/industrialio-core.c
drivers/infiniband/core/cm.c
drivers/infiniband/core/rdma_core.c
drivers/infiniband/core/uverbs_main.c
drivers/infiniband/hw/i40iw/i40iw_ctrl.c
drivers/infiniband/hw/mlx4/main.c
drivers/infiniband/hw/mlx5/Makefile
drivers/infiniband/hw/mlx5/cmd.c
drivers/infiniband/hw/mlx5/cmd.h
drivers/infiniband/hw/mlx5/cq.c
drivers/infiniband/hw/mlx5/devx.c
drivers/infiniband/hw/mlx5/flow.c
drivers/infiniband/hw/mlx5/ib_virt.c
drivers/infiniband/hw/mlx5/mad.c
drivers/infiniband/hw/mlx5/main.c
drivers/infiniband/hw/mlx5/mlx5_ib.h
drivers/infiniband/hw/mlx5/odp.c
drivers/infiniband/hw/mlx5/qp.c
drivers/infiniband/hw/mlx5/qp.h [new file with mode: 0644]
drivers/infiniband/hw/mlx5/qpc.c [new file with mode: 0644]
drivers/infiniband/hw/mlx5/srq_cmd.c
drivers/infiniband/sw/rdmavt/cq.c
drivers/infiniband/sw/rdmavt/mmap.c
drivers/infiniband/sw/rdmavt/qp.c
drivers/infiniband/sw/rdmavt/srq.c
drivers/infiniband/sw/siw/siw_qp_tx.c
drivers/interconnect/qcom/bcm-voter.c
drivers/iommu/Kconfig
drivers/iommu/amd_iommu_init.c
drivers/iommu/intel-iommu.c
drivers/iommu/iommu.c
drivers/iommu/qcom_iommu.c
drivers/irqchip/irq-bcm7038-l1.c
drivers/irqchip/irq-gic-v3-its.c
drivers/irqchip/irq-gic-v3.c
drivers/irqchip/irq-mbigen.c
drivers/irqchip/irq-meson-gpio.c
drivers/irqchip/irq-mvebu-icu.c
drivers/irqchip/irq-sifive-plic.c
drivers/irqchip/irq-ti-sci-inta.c
drivers/isdn/hardware/mISDN/mISDNisar.c
drivers/macintosh/mac_hid.c
drivers/md/dm-mpath.c
drivers/md/dm-verity-fec.c
drivers/md/dm-writecache.c
drivers/media/rc/bpf-lirc.c
drivers/misc/mei/pci-me.c
drivers/misc/mic/Kconfig
drivers/mmc/core/mmc_ops.c
drivers/mmc/host/cqhci.c
drivers/mmc/host/meson-mx-sdio.c
drivers/mmc/host/sdhci-msm.c
drivers/mmc/host/sdhci-pci-core.c
drivers/mmc/host/sdhci-xenon.c
drivers/mtd/spi-nor/Makefile
drivers/net/Kconfig
drivers/net/appletalk/Kconfig
drivers/net/arcnet/Kconfig
drivers/net/bonding/bond_main.c
drivers/net/bonding/bonding_priv.h
drivers/net/caif/Kconfig
drivers/net/dsa/b53/b53_common.c
drivers/net/dsa/b53/b53_priv.h
drivers/net/dsa/b53/b53_regs.h
drivers/net/dsa/b53/b53_srab.c
drivers/net/dsa/mt7530.c
drivers/net/dsa/mt7530.h
drivers/net/dsa/mv88e6xxx/Kconfig
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/mv88e6xxx/serdes.c
drivers/net/dsa/mv88e6xxx/serdes.h
drivers/net/dsa/ocelot/felix.c
drivers/net/dsa/ocelot/felix.h
drivers/net/dsa/ocelot/felix_vsc9959.c
drivers/net/dsa/sja1105/Kconfig
drivers/net/dsa/sja1105/sja1105.h
drivers/net/dsa/sja1105/sja1105_clocking.c
drivers/net/dsa/sja1105/sja1105_ethtool.c
drivers/net/dsa/sja1105/sja1105_ptp.c
drivers/net/dsa/sja1105/sja1105_spi.c
drivers/net/ethernet/3com/3c509.c
drivers/net/ethernet/3com/3c515.c
drivers/net/ethernet/3com/3c59x.c
drivers/net/ethernet/3com/Kconfig
drivers/net/ethernet/adaptec/starfire.c
drivers/net/ethernet/agere/et131x.c
drivers/net/ethernet/allwinner/sun4i-emac.c
drivers/net/ethernet/altera/altera_tse_main.c
drivers/net/ethernet/amazon/ena/ena_admin_defs.h
drivers/net/ethernet/amazon/ena/ena_com.c
drivers/net/ethernet/amazon/ena/ena_com.h
drivers/net/ethernet/amazon/ena/ena_ethtool.c
drivers/net/ethernet/amazon/ena/ena_netdev.c
drivers/net/ethernet/amazon/ena/ena_netdev.h
drivers/net/ethernet/amd/7990.c
drivers/net/ethernet/amd/7990.h
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/aquantia/atlantic/Makefile
drivers/net/ethernet/aquantia/atlantic/aq_cfg.h
drivers/net/ethernet/aquantia/atlantic/aq_common.h
drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
drivers/net/ethernet/aquantia/atlantic/aq_hw.h
drivers/net/ethernet/aquantia/atlantic/aq_macsec.c
drivers/net/ethernet/aquantia/atlantic/aq_nic.c
drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_a0.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c [new file with mode: 0644]
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.h [new file with mode: 0644]
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h [new file with mode: 0644]
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.c [new file with mode: 0644]
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.h [new file with mode: 0644]
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh_internal.h [new file with mode: 0644]
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.c [new file with mode: 0644]
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.h [new file with mode: 0644]
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c [new file with mode: 0644]
drivers/net/ethernet/atheros/ag71xx.c
drivers/net/ethernet/atheros/atl1c/atl1c_main.c
drivers/net/ethernet/broadcom/bcmsysport.c
drivers/net/ethernet/broadcom/bgmac-platform.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
drivers/net/ethernet/broadcom/bnxt/bnxt.c
drivers/net/ethernet/broadcom/bnxt/bnxt.h
drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.h
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h
drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h
drivers/net/ethernet/broadcom/genet/bcmgenet.c
drivers/net/ethernet/broadcom/genet/bcmgenet.h
drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c
drivers/net/ethernet/cadence/Kconfig
drivers/net/ethernet/cadence/macb_main.c
drivers/net/ethernet/cavium/Kconfig
drivers/net/ethernet/cavium/liquidio/octeon_device.h
drivers/net/ethernet/chelsio/Kconfig
drivers/net/ethernet/chelsio/cxgb4/cudbg_lib.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c
drivers/net/ethernet/cirrus/Kconfig
drivers/net/ethernet/cortina/gemini.c
drivers/net/ethernet/dec/tulip/Kconfig
drivers/net/ethernet/dec/tulip/de4x5.c
drivers/net/ethernet/dec/tulip/tulip_core.c
drivers/net/ethernet/dlink/dl2k.c
drivers/net/ethernet/dnet.c
drivers/net/ethernet/faraday/ftmac100.c
drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth-debugfs.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
drivers/net/ethernet/freescale/enetc/enetc.c
drivers/net/ethernet/freescale/enetc/enetc.h
drivers/net/ethernet/freescale/enetc/enetc_hw.h
drivers/net/ethernet/freescale/enetc/enetc_pci_mdio.c
drivers/net/ethernet/freescale/enetc/enetc_pf.c
drivers/net/ethernet/freescale/enetc/enetc_qos.c
drivers/net/ethernet/freescale/fec.h
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h
drivers/net/ethernet/hisilicon/hns3/hnae3.h
drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c
drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
drivers/net/ethernet/hisilicon/hns3/hns3_enet.h
drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/Makefile
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_trace.h [new file with mode: 0644]
drivers/net/ethernet/hisilicon/hns3/hns3vf/Makefile
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_trace.h [new file with mode: 0644]
drivers/net/ethernet/huawei/hinic/Makefile
drivers/net/ethernet/huawei/hinic/hinic_dev.h
drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c
drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h
drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h
drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c
drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h
drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c
drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h
drivers/net/ethernet/huawei/hinic/hinic_hw_if.c
drivers/net/ethernet/huawei/hinic/hinic_hw_if.h
drivers/net/ethernet/huawei/hinic/hinic_hw_io.c
drivers/net/ethernet/huawei/hinic/hinic_hw_io.h
drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c [new file with mode: 0644]
drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.h [new file with mode: 0644]
drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c
drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h
drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c
drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h
drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c
drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h
drivers/net/ethernet/huawei/hinic/hinic_main.c
drivers/net/ethernet/huawei/hinic/hinic_port.c
drivers/net/ethernet/huawei/hinic/hinic_port.h
drivers/net/ethernet/huawei/hinic/hinic_rx.c
drivers/net/ethernet/huawei/hinic/hinic_sriov.c [new file with mode: 0644]
drivers/net/ethernet/huawei/hinic/hinic_sriov.h [new file with mode: 0644]
drivers/net/ethernet/huawei/hinic/hinic_tx.c
drivers/net/ethernet/ibm/ibmvnic.c
drivers/net/ethernet/intel/e1000/e1000_main.c
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/e1000e/regs.h
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/ice/ice_flex_pipe.c
drivers/net/ethernet/intel/igc/Makefile
drivers/net/ethernet/intel/igc/igc.h
drivers/net/ethernet/intel/igc/igc_base.c
drivers/net/ethernet/intel/igc/igc_defines.h
drivers/net/ethernet/intel/igc/igc_ethtool.c
drivers/net/ethernet/intel/igc/igc_hw.h
drivers/net/ethernet/intel/igc/igc_main.c
drivers/net/ethernet/intel/igc/igc_regs.h
drivers/net/ethernet/intel/igc/igc_tsn.c [new file with mode: 0644]
drivers/net/ethernet/intel/igc/igc_tsn.h [new file with mode: 0644]
drivers/net/ethernet/lantiq_xrx200.c
drivers/net/ethernet/marvell/mv643xx_eth.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
drivers/net/ethernet/mediatek/mtk_eth_soc.c
drivers/net/ethernet/mediatek/mtk_eth_soc.h
drivers/net/ethernet/mellanox/mlx4/crdump.c
drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx4/en_tx.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
drivers/net/ethernet/mellanox/mlx5/core/Kconfig
drivers/net/ethernet/mellanox/mlx5/core/Makefile
drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.c
drivers/net/ethernet/mellanox/mlx5/core/accel/ipsec.h
drivers/net/ethernet/mellanox/mlx5/core/cmd.c
drivers/net/ethernet/mellanox/mlx5/core/cq.c
drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
drivers/net/ethernet/mellanox/mlx5/core/devlink.c
drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
drivers/net/ethernet/mellanox/mlx5/core/ecpf.c
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en/health.c
drivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.c
drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c
drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h
drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h
drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c
drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_stats.c
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls.h
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_tx.c
drivers/net/ethernet/mellanox/mlx5/core/en_accel/tls_rxtx.c
drivers/net/ethernet/mellanox/mlx5/core/en_common.c
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
drivers/net/ethernet/mellanox/mlx5/core/eq.c
drivers/net/ethernet/mellanox/mlx5/core/esw/chains.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/fpga/cmd.c
drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c
drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.h
drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c
drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.h
drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
drivers/net/ethernet/mellanox/mlx5/core/fw.c
drivers/net/ethernet/mellanox/mlx5/core/health.c
drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h
drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c
drivers/net/ethernet/mellanox/mlx5/core/lag.c
drivers/net/ethernet/mellanox/mlx5/core/lib/dm.c
drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h
drivers/net/ethernet/mellanox/mlx5/core/lib/gid.c
drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c
drivers/net/ethernet/mellanox/mlx5/core/lib/port_tun.c
drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/ethernet/mellanox/mlx5/core/mcg.c
drivers/net/ethernet/mellanox/mlx5/core/mr.c
drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
drivers/net/ethernet/mellanox/mlx5/core/pd.c
drivers/net/ethernet/mellanox/mlx5/core/port.c
drivers/net/ethernet/mellanox/mlx5/core/qp.c [deleted file]
drivers/net/ethernet/mellanox/mlx5/core/rl.c
drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c
drivers/net/ethernet/mellanox/mlx5/core/steering/dr_icm_pool.c
drivers/net/ethernet/mellanox/mlx5/core/steering/dr_send.c
drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h
drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c
drivers/net/ethernet/mellanox/mlx5/core/transobj.c
drivers/net/ethernet/mellanox/mlx5/core/uar.c
drivers/net/ethernet/mellanox/mlx5/core/vport.c
drivers/net/ethernet/mellanox/mlxfw/mlxfw_fsm.c
drivers/net/ethernet/mellanox/mlxsw/Makefile
drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c
drivers/net/ethernet/mellanox/mlxsw/reg.h
drivers/net/ethernet/mellanox/mlxsw/spectrum.c
drivers/net/ethernet/mellanox/mlxsw/spectrum.h
drivers/net/ethernet/mellanox/mlxsw/spectrum2_acl_tcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum2_mr_tcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_actions.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_flow.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_matchall.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlxsw/spectrum_mr_tcam.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h
drivers/net/ethernet/microchip/encx24j600-regmap.c
drivers/net/ethernet/microchip/encx24j600.c
drivers/net/ethernet/moxa/moxart_ether.c
drivers/net/ethernet/mscc/Makefile
drivers/net/ethernet/mscc/ocelot.c
drivers/net/ethernet/mscc/ocelot.h
drivers/net/ethernet/mscc/ocelot_ace.c
drivers/net/ethernet/mscc/ocelot_ace.h
drivers/net/ethernet/mscc/ocelot_board.c
drivers/net/ethernet/mscc/ocelot_flower.c
drivers/net/ethernet/mscc/ocelot_ptp.c [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_ptp.h [deleted file]
drivers/net/ethernet/mscc/ocelot_regs.c
drivers/net/ethernet/myricom/myri10ge/myri10ge.c
drivers/net/ethernet/natsemi/jazzsonic.c
drivers/net/ethernet/neterion/Kconfig
drivers/net/ethernet/neterion/s2io.c
drivers/net/ethernet/netronome/nfp/abm/main.c
drivers/net/ethernet/netronome/nfp/nfp_main.c
drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
drivers/net/ethernet/netronome/nfp/nfp_net_repr.c
drivers/net/ethernet/ni/nixge.c
drivers/net/ethernet/nxp/lpc_eth.c
drivers/net/ethernet/pensando/ionic/ionic_debugfs.c
drivers/net/ethernet/pensando/ionic/ionic_lif.c
drivers/net/ethernet/pensando/ionic/ionic_main.c
drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c
drivers/net/ethernet/pensando/ionic/ionic_rx_filter.h
drivers/net/ethernet/qlogic/qed/qed_dev.c
drivers/net/ethernet/qlogic/qed/qed_ll2.c
drivers/net/ethernet/qlogic/qed/qed_main.c
drivers/net/ethernet/qlogic/qed/qed_roce.c
drivers/net/ethernet/qlogic/qede/qede.h
drivers/net/ethernet/qlogic/qede/qede_main.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c
drivers/net/ethernet/qualcomm/emac/emac-mac.c
drivers/net/ethernet/qualcomm/emac/emac-mac.h
drivers/net/ethernet/qualcomm/emac/emac.c
drivers/net/ethernet/realtek/r8169_main.c
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/sgi/meth.h
drivers/net/ethernet/smsc/Kconfig
drivers/net/ethernet/socionext/sni_ave.c
drivers/net/ethernet/stmicro/stmmac/Makefile
drivers/net/ethernet/stmicro/stmmac/common.h
drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h [new file with mode: 0644]
drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
drivers/net/ethernet/stmicro/stmmac/dwmac4.h
drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
drivers/net/ethernet/stmicro/stmmac/dwmac5.c
drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
drivers/net/ethernet/sun/cassini.c
drivers/net/ethernet/tehuti/tehuti.c
drivers/net/ethernet/ti/Kconfig
drivers/net/ethernet/ti/Makefile
drivers/net/ethernet/ti/am65-cpsw-ethtool.c
drivers/net/ethernet/ti/am65-cpsw-nuss.c
drivers/net/ethernet/ti/am65-cpsw-nuss.h
drivers/net/ethernet/ti/am65-cpts.c [new file with mode: 0644]
drivers/net/ethernet/ti/am65-cpts.h [new file with mode: 0644]
drivers/net/ethernet/ti/cpmac.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpsw_new.c
drivers/net/ethernet/ti/cpsw_priv.c
drivers/net/ethernet/ti/cpsw_priv.h
drivers/net/ethernet/ti/cpts.c
drivers/net/ethernet/ti/cpts.h
drivers/net/ethernet/ti/davinci_mdio.c
drivers/net/ethernet/ti/k3-cppi-desc-pool.c
drivers/net/ethernet/ti/netcp_ethss.c
drivers/net/ethernet/ti/tlan.c
drivers/net/ethernet/toshiba/ps3_gelic_net.c
drivers/net/ethernet/toshiba/spider_net.c
drivers/net/ethernet/toshiba/tc35815.c
drivers/net/ethernet/via/Kconfig
drivers/net/ethernet/xilinx/ll_temac_main.c
drivers/net/ethernet/xscale/ixp4xx_eth.c
drivers/net/fddi/Kconfig
drivers/net/geneve.c
drivers/net/gtp.c
drivers/net/hamradio/Kconfig
drivers/net/hamradio/bpqether.c
drivers/net/hamradio/scc.c
drivers/net/hyperv/netvsc_drv.c
drivers/net/ipa/gsi.c
drivers/net/ipa/gsi.h
drivers/net/ipa/gsi_reg.h
drivers/net/ipa/ipa.h
drivers/net/ipa/ipa_cmd.c
drivers/net/ipa/ipa_cmd.h
drivers/net/ipa/ipa_data-sc7180.c
drivers/net/ipa/ipa_data-sdm845.c
drivers/net/ipa/ipa_data.h
drivers/net/ipa/ipa_endpoint.c
drivers/net/ipa/ipa_endpoint.h
drivers/net/ipa/ipa_main.c
drivers/net/ipa/ipa_mem.c
drivers/net/ipa/ipa_mem.h
drivers/net/ipa/ipa_modem.c
drivers/net/ipvlan/ipvlan_main.c
drivers/net/macsec.c
drivers/net/macvlan.c
drivers/net/phy/Kconfig
drivers/net/phy/Makefile
drivers/net/phy/at803x.c
drivers/net/phy/bcm-phy-lib.c
drivers/net/phy/bcm-phy-lib.h
drivers/net/phy/bcm54140.c [new file with mode: 0644]
drivers/net/phy/bcm84881.c
drivers/net/phy/broadcom.c
drivers/net/phy/cortina.c
drivers/net/phy/dp83640.c
drivers/net/phy/dp83822.c
drivers/net/phy/dp83867.c
drivers/net/phy/dp83tc811.c
drivers/net/phy/marvell.c
drivers/net/phy/marvell10g.c
drivers/net/phy/mdio-bcm-iproc.c
drivers/net/phy/mdio-ipq4019.c [new file with mode: 0644]
drivers/net/phy/mdio_bus.c
drivers/net/phy/micrel.c
drivers/net/phy/microchip_t1.c
drivers/net/phy/mscc/mscc.h
drivers/net/phy/mscc/mscc_main.c
drivers/net/phy/nxp-tja11xx.c
drivers/net/phy/phy-c45.c
drivers/net/phy/phy.c
drivers/net/phy/phy_device.c
drivers/net/phy/phylink.c
drivers/net/phy/realtek.c
drivers/net/phy/teranetics.c
drivers/net/plip/Kconfig
drivers/net/ppp/ppp_generic.c
drivers/net/rionet.c
drivers/net/team/team.c
drivers/net/tun.c
drivers/net/usb/ax88179_178a.c
drivers/net/usb/qmi_wwan.c
drivers/net/usb/sierra_net.c
drivers/net/vrf.c
drivers/net/vxlan.c
drivers/net/wan/Kconfig
drivers/net/wimax/i2400m/usb-fw.c
drivers/net/wireguard/queueing.c
drivers/net/wireguard/receive.c
drivers/net/wireguard/selftest/ratelimiter.c
drivers/net/wireguard/send.c
drivers/net/wireguard/socket.c
drivers/net/wireless/Kconfig
drivers/net/wireless/ath/ath11k/thermal.h
drivers/net/wireless/broadcom/b43/xmit.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
drivers/net/wireless/intel/ipw2x00/Kconfig
drivers/net/wireless/intel/ipw2x00/ipw2100.c
drivers/net/wireless/intel/iwlegacy/3945-rs.c
drivers/net/wireless/intel/iwlegacy/4965-rs.c
drivers/net/wireless/intel/iwlwifi/dvm/rs.c
drivers/net/wireless/intel/iwlwifi/fw/acpi.c
drivers/net/wireless/intel/iwlwifi/fw/api/txq.h
drivers/net/wireless/intel/iwlwifi/iwl-drv.c
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/intel/iwlwifi/mvm/fw.c
drivers/net/wireless/intel/iwlwifi/mvm/rs.c
drivers/net/wireless/intel/iwlwifi/mvm/rx.c
drivers/net/wireless/intel/iwlwifi/mvm/sta.c
drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c
drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c
drivers/net/wireless/intersil/hostap/hostap_hw.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/realtek/rtlwifi/rc.c
drivers/net/wireless/realtek/rtw88/pci.c
drivers/nvme/host/core.c
drivers/of/of_mdio.c
drivers/of/overlay.c
drivers/of/unittest-data/overlay_bad_add_dup_prop.dts
drivers/of/unittest.c
drivers/opp/core.c
drivers/parport/procfs.c
drivers/pci/quirks.c
drivers/phy/tegra/Kconfig
drivers/platform/chrome/cros_ec_sensorhub.c
drivers/platform/chrome/cros_ec_sensorhub_ring.c
drivers/platform/x86/asus-nb-wmi.c
drivers/platform/x86/intel-uncore-frequency.c
drivers/platform/x86/intel_pmc_core.c
drivers/platform/x86/intel_pmc_core.h
drivers/platform/x86/surface3_power.c
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/xiaomi-wmi.c
drivers/power/supply/test_power.c
drivers/ptp/ptp_chardev.c
drivers/ptp/ptp_clock.c
drivers/ptp/ptp_clockmatrix.c
drivers/ptp/ptp_clockmatrix.h
drivers/ptp/ptp_idt82p33.c
drivers/ptp/ptp_ines.c
drivers/ptp/ptp_kvm.c
drivers/remoteproc/mtk_common.h
drivers/remoteproc/mtk_scp.c
drivers/remoteproc/qcom_q6v5_mss.c
drivers/remoteproc/remoteproc_sysfs.c
drivers/remoteproc/stm32_rproc.c
drivers/rpmsg/mtk_rpmsg.c
drivers/s390/block/Kconfig
drivers/s390/net/Kconfig
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_core_mpc.h
drivers/s390/net/qeth_core_sys.c
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3_main.c
drivers/scsi/Kconfig
drivers/scsi/hisi_sas/Kconfig
drivers/scsi/qla2xxx/qla_dbg.c
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/qla2xxx/qla_mbx.c
drivers/scsi/qla2xxx/qla_os.c
drivers/scsi/scsi_lib.c
drivers/scsi/sg.c
drivers/soc/fsl/dpio/dpio-service.c
drivers/soc/fsl/dpio/qbman-portal.c
drivers/soc/imx/Kconfig
drivers/soc/xilinx/Kconfig
drivers/staging/comedi/comedi_fops.c
drivers/staging/comedi/drivers/dt2815.c
drivers/staging/fsl-dpaa2/ethsw/README
drivers/staging/gasket/apex_driver.c
drivers/staging/gasket/gasket_sysfs.c
drivers/staging/gasket/gasket_sysfs.h
drivers/staging/vt6656/key.c
drivers/staging/vt6656/main_usb.c
drivers/staging/vt6656/usbpipe.c
drivers/target/target_core_fabric_lib.c
drivers/target/target_core_iblock.c
drivers/target/target_core_user.c
drivers/tty/hvc/Kconfig
drivers/tty/hvc/hvc_console.c
drivers/tty/rocket.c
drivers/tty/serial/Kconfig
drivers/tty/serial/bcm63xx_uart.c
drivers/tty/serial/owl-uart.c
drivers/tty/serial/sh-sci.c
drivers/tty/serial/sunhv.c
drivers/tty/serial/xilinx_uartps.c
drivers/tty/sysrq.c
drivers/tty/vt/vt.c
drivers/usb/class/cdc-acm.c
drivers/usb/class/cdc-acm.h
drivers/usb/core/hub.c
drivers/usb/core/message.c
drivers/usb/core/quirks.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/gadget.c
drivers/usb/early/xhci-dbc.c
drivers/usb/early/xhci-dbc.h
drivers/usb/gadget/function/f_fs.c
drivers/usb/gadget/legacy/raw_gadget.c
drivers/usb/gadget/udc/atmel_usba_udc.c
drivers/usb/gadget/udc/bdc/bdc_ep.c
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
drivers/usb/misc/sisusbvga/sisusb.c
drivers/usb/misc/sisusbvga/sisusb_init.h
drivers/usb/storage/uas.c
drivers/usb/storage/unusual_devs.h
drivers/usb/typec/bus.c
drivers/usb/typec/mux/pi3usb30532.c
drivers/usb/typec/tcpm/tcpm.c
drivers/vdpa/Kconfig
drivers/vdpa/ifcvf/ifcvf_base.c
drivers/vdpa/ifcvf/ifcvf_main.c
drivers/vdpa/vdpa.c
drivers/vdpa/vdpa_sim/vdpa_sim.c
drivers/vfio/vfio_iommu_type1.c
drivers/vhost/Kconfig
drivers/vhost/net.c
drivers/vhost/scsi.c
drivers/vhost/test.c
drivers/vhost/vdpa.c
drivers/vhost/vhost.h
drivers/vhost/vringh.c
drivers/vhost/vsock.c
drivers/virtio/Kconfig
drivers/virtio/virtio_balloon.c
drivers/virtio/virtio_input.c
drivers/watchdog/sp805_wdt.c
drivers/xen/xenbus/xenbus_client.c
fs/afs/cmservice.c
fs/afs/dir.c
fs/afs/dir_silly.c
fs/afs/fs_probe.c
fs/afs/fsclient.c
fs/afs/internal.h
fs/afs/rotate.c
fs/afs/server.c
fs/afs/vl_rotate.c
fs/afs/volume.c
fs/afs/yfsclient.c
fs/block_dev.c
fs/btrfs/backref.c
fs/btrfs/block-group.c
fs/btrfs/discard.h
fs/btrfs/disk-io.c
fs/btrfs/file.c
fs/btrfs/reflink.c
fs/btrfs/relocation.c
fs/btrfs/space-info.c
fs/btrfs/transaction.c
fs/btrfs/tree-log.c
fs/buffer.c
fs/ceph/dir.c
fs/ceph/file.c
fs/ceph/mds_client.h
fs/cifs/cifsglob.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/inode.c
fs/cifs/misc.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/cifs/smb2transport.c
fs/coredump.c
fs/dcache.c
fs/debugfs/file.c
fs/drop_caches.c
fs/exfat/balloc.c
fs/exfat/exfat_fs.h
fs/exfat/file.c
fs/exfat/misc.c
fs/exfat/namei.c
fs/exfat/super.c
fs/ext4/balloc.c
fs/ext4/ext4_jbd2.c
fs/ext4/extents.c
fs/ext4/ialloc.c
fs/ext4/inode.c
fs/ext4/mballoc.c
fs/ext4/super.c
fs/file_table.c
fs/fscache/main.c
fs/inode.c
fs/io_uring.c
fs/ioctl.c
fs/iomap/fiemap.c
fs/nfs/nfs3acl.c
fs/nfs/nfs4proc.c
fs/nfs/pnfs.c
fs/nfs/super.c
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4state.c
fs/ocfs2/dlmfs/dlmfs.c
fs/pnode.c
fs/proc/base.c
fs/proc/proc_sysctl.c
fs/proc/root.c
fs/proc/vmcore.c
fs/quota/dquot.c
fs/super.c
fs/xfs/xfs_icache.c
fs/xfs/xfs_ioctl.c
fs/xfs/xfs_mount.h
fs/xfs/xfs_reflink.c
fs/xfs/xfs_super.c
fs/xfs/xfs_sysctl.c
include/asm-generic/Kbuild
include/asm-generic/mshyperv.h
include/asm-generic/vermagic.h [new file with mode: 0644]
include/linux/backing-dev-defs.h
include/linux/bio.h
include/linux/blk-mq.h
include/linux/blk_types.h
include/linux/bpf-cgroup.h
include/linux/bpf.h
include/linux/bpf_types.h
include/linux/brcmphy.h
include/linux/buffer_head.h
include/linux/can/dev/peak_canfd.h
include/linux/compaction.h
include/linux/coredump.h
include/linux/cpu_rmap.h
include/linux/debugfs.h
include/linux/delay.h
include/linux/digsig.h
include/linux/dirent.h
include/linux/dma-buf.h
include/linux/dmaengine.h
include/linux/enclosure.h
include/linux/energy_model.h
include/linux/etherdevice.h
include/linux/ethtool.h
include/linux/file.h
include/linux/filter.h
include/linux/fs.h
include/linux/fsl/ptp_qoriq.h
include/linux/ftrace.h
include/linux/genalloc.h
include/linux/hugetlb.h
include/linux/i2c.h
include/linux/if_bridge.h
include/linux/igmp.h
include/linux/ihex.h
include/linux/iio/iio.h
include/linux/inet_diag.h
include/linux/irq.h
include/linux/irqchip/arm-gic-v3.h
include/linux/kprobes.h
include/linux/kvm_host.h
include/linux/latencytop.h
include/linux/list_lru.h
include/linux/memcontrol.h
include/linux/mlx5/accel.h
include/linux/mlx5/cmd.h [deleted file]
include/linux/mlx5/cq.h
include/linux/mlx5/device.h
include/linux/mlx5/driver.h
include/linux/mlx5/mlx5_ifc.h
include/linux/mlx5/qp.h
include/linux/mlx5/transobj.h
include/linux/mlx5/vport.h
include/linux/mm.h
include/linux/mmzone.h
include/linux/netdev_features.h
include/linux/netdevice.h
include/linux/nfs_xdr.h
include/linux/nmi.h
include/linux/of_mdio.h
include/linux/perf_event.h
include/linux/phy.h
include/linux/phylink.h
include/linux/pid.h
include/linux/platform_data/cros_ec_sensorhub.h
include/linux/platform_data/wilco-ec.h
include/linux/pnp.h
include/linux/posix_acl.h
include/linux/printk.h
include/linux/ptp_clock_kernel.h
include/linux/rio.h
include/linux/rslib.h
include/linux/sched/sysctl.h
include/linux/sched/topology.h
include/linux/security.h
include/linux/skbuff.h
include/linux/stmmac.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/svc_rdma.h
include/linux/swap.h
include/linux/sysctl.h
include/linux/tcp.h
include/linux/ti_wilink_st.h
include/linux/timer.h
include/linux/tpm_eventlog.h
include/linux/tracepoint.h
include/linux/tty.h
include/linux/vdpa.h
include/linux/vermagic.h
include/linux/virtio.h
include/linux/virtio_net.h
include/linux/virtio_vsock.h
include/linux/vmalloc.h
include/linux/vmstat.h
include/linux/vringh.h
include/linux/writeback.h
include/linux/xattr.h
include/net/addrconf.h
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/mgmt.h
include/net/bonding.h
include/net/cfg80211.h
include/net/erspan.h
include/net/flow_offload.h
include/net/if_inet6.h
include/net/inet_ecn.h
include/net/ip6_fib.h
include/net/ip6_route.h
include/net/ipv6.h
include/net/ipv6_stubs.h
include/net/mac80211.h
include/net/mptcp.h
include/net/ndisc.h
include/net/net_namespace.h
include/net/netfilter/nf_tables.h
include/net/netlink.h
include/net/netns/ipv4.h
include/net/sch_generic.h
include/net/sock.h
include/net/switchdev.h
include/net/tc_act/tc_gate.h [new file with mode: 0644]
include/net/tcp.h
include/net/xdp.h
include/soc/mscc/ocelot.h
include/soc/mscc/ocelot_ptp.h [new file with mode: 0644]
include/soc/tegra/pmc.h
include/sound/hda_codec.h
include/sound/soc-dai.h
include/sound/soc.h
include/trace/events/iocost.h
include/trace/events/qrtr.h [new file with mode: 0644]
include/trace/events/rpcrdma.h
include/trace/events/wbt.h
include/trace/events/writeback.h
include/uapi/drm/amdgpu_drm.h
include/uapi/linux/bpf.h
include/uapi/linux/btrfs.h
include/uapi/linux/dma-buf.h
include/uapi/linux/errqueue.h
include/uapi/linux/ethtool.h
include/uapi/linux/ethtool_netlink.h
include/uapi/linux/genetlink.h
include/uapi/linux/hyperv.h
include/uapi/linux/if.h
include/uapi/linux/if_arcnet.h
include/uapi/linux/if_bridge.h
include/uapi/linux/if_ether.h
include/uapi/linux/if_link.h
include/uapi/linux/if_x25.h
include/uapi/linux/inet_diag.h
include/uapi/linux/mii.h
include/uapi/linux/mmc/ioctl.h
include/uapi/linux/mrp_bridge.h [new file with mode: 0644]
include/uapi/linux/net_dropmon.h
include/uapi/linux/netfilter/nf_conntrack_common.h
include/uapi/linux/netfilter/nf_nat.h
include/uapi/linux/netfilter/nf_tables.h
include/uapi/linux/netfilter/xt_IDLETIMER.h
include/uapi/linux/netfilter_bridge/ebt_among.h
include/uapi/linux/netlink.h
include/uapi/linux/pkt_cls.h
include/uapi/linux/pkt_sched.h
include/uapi/linux/ptp_clock.h
include/uapi/linux/tc_act/tc_gate.h [new file with mode: 0644]
include/uapi/linux/virtio_balloon.h
include/uapi/scsi/scsi_bsg_fc.h
include/vdso/datapage.h
ipc/ipc_sysctl.c
ipc/mq_sysctl.c
kernel/audit.c
kernel/bpf/bpf_lru_list.h
kernel/bpf/btf.c
kernel/bpf/cgroup.c
kernel/bpf/core.c
kernel/bpf/cpumap.c
kernel/bpf/devmap.c
kernel/bpf/helpers.c
kernel/bpf/syscall.c
kernel/bpf/verifier.c
kernel/cgroup/cgroup.c
kernel/events/callchain.c
kernel/events/core.c
kernel/exit.c
kernel/fork.c
kernel/irq/manage.c
kernel/kprobes.c
kernel/latencytop.c
kernel/module.c
kernel/pid_namespace.c
kernel/power/hibernate.c
kernel/printk/printk.c
kernel/rcu/tree.c
kernel/sched/core.c
kernel/sched/cputime.c
kernel/sched/fair.c
kernel/sched/isolation.c
kernel/sched/rt.c
kernel/sched/topology.c
kernel/seccomp.c
kernel/signal.c
kernel/sysctl.c
kernel/time/namespace.c
kernel/time/timer.c
kernel/trace/bpf_trace.c
kernel/trace/ftrace.c
kernel/trace/trace.c
kernel/trace/trace_events_hist.c
kernel/trace/trace_events_trigger.c
kernel/trace/tracing_map.c
kernel/umh.c
kernel/utsname_sysctl.c
kernel/watchdog.c
lib/Kconfig.debug
lib/kunit/test.c
lib/mpi/longlong.h
lib/nlattr.c
mm/compaction.c
mm/gup.c
mm/hugetlb.c
mm/ksm.c
mm/madvise.c
mm/mremap.c
mm/page-writeback.c
mm/page_alloc.c
mm/shmem.c
mm/slub.c
mm/util.c
mm/vmalloc.c
mm/vmstat.c
net/8021q/vlan_dev.c
net/Kconfig
net/atm/Kconfig
net/atm/common.c
net/atm/lec.c
net/ax25/Kconfig
net/batman-adv/bat_iv_ogm.c
net/batman-adv/bat_v_elp.c
net/batman-adv/bat_v_ogm.c
net/batman-adv/distributed-arp-table.h
net/batman-adv/main.h
net/batman-adv/network-coding.c
net/batman-adv/soft-interface.c
net/batman-adv/sysfs.c
net/batman-adv/trace.h
net/batman-adv/types.h
net/bluetooth/6lowpan.c
net/bluetooth/Kconfig
net/bluetooth/Makefile
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_debugfs.c
net/bluetooth/hci_event.c
net/bluetooth/hci_request.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bluetooth/msft.c [new file with mode: 0644]
net/bluetooth/msft.h [new file with mode: 0644]
net/bluetooth/smp.c
net/bridge/Kconfig
net/bridge/Makefile
net/bridge/br_device.c
net/bridge/br_if.c
net/bridge/br_input.c
net/bridge/br_ioctl.c
net/bridge/br_mrp.c [new file with mode: 0644]
net/bridge/br_mrp_netlink.c [new file with mode: 0644]
net/bridge/br_mrp_switchdev.c [new file with mode: 0644]
net/bridge/br_netfilter_hooks.c
net/bridge/br_netlink.c
net/bridge/br_private.h
net/bridge/br_private_mrp.h [new file with mode: 0644]
net/bridge/br_stp.c
net/bridge/br_stp_if.c
net/bridge/br_sysfs_br.c
net/caif/caif_dev.c
net/caif/chnl_net.c
net/ceph/Kconfig
net/core/dev.c
net/core/devlink.c
net/core/drop_monitor.c
net/core/filter.c
net/core/gen_stats.c
net/core/link_watch.c
net/core/neighbour.c
net/core/net-sysfs.c
net/core/netclassid_cgroup.c
net/core/netpoll.c
net/core/pktgen.c
net/core/rtnetlink.c
net/core/skbuff.c
net/core/sock.c
net/core/sock_map.c
net/core/sysctl_net_core.c
net/dccp/dccp.h
net/decnet/Kconfig
net/decnet/dn_dev.c
net/decnet/sysctl_net_decnet.c
net/dns_resolver/Kconfig
net/dns_resolver/dns_key.c
net/dns_resolver/dns_query.c
net/dsa/Kconfig
net/dsa/dsa.c
net/dsa/dsa2.c
net/dsa/dsa_priv.h
net/dsa/master.c
net/dsa/port.c
net/dsa/slave.c
net/ethtool/ioctl.c
net/ethtool/linkmodes.c
net/hsr/hsr_device.c
net/hsr/hsr_main.c
net/hsr/hsr_main.h
net/hsr/hsr_netlink.c
net/hsr/hsr_slave.c
net/ieee802154/6lowpan/core.c
net/ipv4/Kconfig
net/ipv4/af_inet.c
net/ipv4/devinet.c
net/ipv4/fib_semantics.c
net/ipv4/icmp.c
net/ipv4/inet_diag.c
net/ipv4/ip_gre.c
net/ipv4/nexthop.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_timer.c
net/ipv4/xfrm4_output.c
net/ipv6/Kconfig
net/ipv6/addrconf.c
net/ipv6/addrconf_core.c
net/ipv6/anycast.c
net/ipv6/icmp.c
net/ipv6/ila/ila.h
net/ipv6/ila/ila_xlat.c
net/ipv6/ipv6_sockglue.c
net/ipv6/ndisc.c
net/ipv6/route.c
net/ipv6/rpl.c
net/ipv6/seg6.c
net/ipv6/sysctl_net_ipv6.c
net/ipv6/xfrm6_output.c
net/l2tp/l2tp_eth.c
net/l2tp/l2tp_netlink.c
net/lapb/Kconfig
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/rate.c
net/mac80211/rate.h
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/sta_info.c
net/mac80211/tx.c
net/mpls/af_mpls.c
net/mptcp/options.c
net/mptcp/pm_netlink.c
net/mptcp/protocol.c
net/mptcp/protocol.h
net/mptcp/subflow.c
net/netfilter/Kconfig
net/netfilter/ipset/ip_set_core.c
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/nf_conntrack_standalone.c
net/netfilter/nf_flow_table_core.c
net/netfilter/nf_flow_table_offload.c
net/netfilter/nf_log.c
net/netfilter/nf_nat_proto.c
net/netfilter/nf_tables_api.c
net/netfilter/nfnetlink_osf.c
net/netfilter/nft_lookup.c
net/netfilter/nft_nat.c
net/netfilter/nft_set_bitmap.c
net/netfilter/nft_set_rbtree.c
net/netfilter/xt_IDLETIMER.c
net/netlabel/Kconfig
net/netlink/Makefile
net/netlink/genetlink.c
net/netlink/policy.c [new file with mode: 0644]
net/netrom/af_netrom.c
net/netrom/nr_route.c
net/openvswitch/conntrack.c
net/openvswitch/datapath.c
net/openvswitch/datapath.h
net/openvswitch/meter.c
net/openvswitch/meter.h
net/phonet/sysctl.c
net/qrtr/Kconfig
net/qrtr/Makefile
net/qrtr/mhi.c [new file with mode: 0644]
net/qrtr/ns.c
net/qrtr/qrtr.c
net/rds/message.c
net/rds/rdma.c
net/rds/rds.h
net/rds/send.c
net/rds/tcp.c
net/rose/af_rose.c
net/rxrpc/Kconfig
net/rxrpc/local_object.c
net/rxrpc/output.c
net/rxrpc/sysctl.c
net/sched/Kconfig
net/sched/Makefile
net/sched/act_api.c
net/sched/act_ct.c
net/sched/act_gate.c [new file with mode: 0644]
net/sched/cls_api.c
net/sched/em_ipt.c
net/sched/sch_choke.c
net/sched/sch_etf.c
net/sched/sch_fq.c
net/sched/sch_fq_codel.c
net/sched/sch_generic.c
net/sched/sch_red.c
net/sched/sch_sfq.c
net/sched/sch_skbprio.c
net/sctp/sm_make_chunk.c
net/sctp/sm_statefuns.c
net/sctp/sysctl.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_ib.c
net/smc/smc_ib.h
net/smc/smc_ism.c
net/smc/smc_llc.c
net/smc/smc_llc.h
net/smc/smc_pnet.c
net/smc/smc_pnet.h
net/smc/smc_tx.c
net/smc/smc_wr.c
net/smc/smc_wr.h
net/sunrpc/cache.c
net/sunrpc/clnt.c
net/sunrpc/svc_xprt.c
net/sunrpc/svcsock.c
net/sunrpc/sysctl.c
net/sunrpc/xprtrdma/rpc_rdma.c
net/sunrpc/xprtrdma/svc_rdma.c
net/sunrpc/xprtrdma/svc_rdma_backchannel.c
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
net/sunrpc/xprtrdma/svc_rdma_rw.c
net/sunrpc/xprtrdma/svc_rdma_sendto.c
net/sunrpc/xprtrdma/svc_rdma_transport.c
net/sunrpc/xprtrdma/verbs.c
net/sunrpc/xprtsock.c
net/tipc/crypto.c
net/tipc/link.c
net/tipc/node.c
net/tipc/topsrv.c
net/tls/tls_main.c
net/tls/tls_sw.c
net/vmw_vsock/virtio_transport_common.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/pmsr.c
net/wireless/radiotap.c
net/x25/Kconfig
net/x25/x25_dev.c
net/x25/x25_subr.c
net/xdp/xdp_umem.c
net/xdp/xsk.c
samples/pktgen/README.rst
samples/vfio-mdev/mdpy.c
scripts/Makefile.lib
scripts/checkpatch.pl
scripts/config
scripts/documentation-file-ref-check
scripts/dtc/Makefile
scripts/gcc-plugins/Makefile
scripts/gcc-plugins/gcc-common.h
scripts/gcc-plugins/stackleak_plugin.c
scripts/kallsyms.c
scripts/kernel-doc
scripts/mod/modpost.c
security/apparmor/lsm.c
security/keys/proc.c
security/min_addr.c
security/selinux/hooks.c
security/selinux/ss/conditional.c
security/selinux/ss/policydb.c
security/yama/yama_lsm.c
sound/core/oss/pcm_plugin.c
sound/hda/Kconfig
sound/isa/opti9xx/miro.c
sound/isa/opti9xx/opti92x-ad1848.c
sound/pci/ctxfi/cthw20k1.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_intel.h
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/soc/amd/acp3x-rt5682-max9836.c
sound/soc/codecs/Kconfig
sound/soc/codecs/hdac_hdmi.c
sound/soc/codecs/madera.c
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/sgtl5000.h
sound/soc/codecs/tas571x.c
sound/soc/codecs/wm8960.c
sound/soc/codecs/wsa881x.c
sound/soc/intel/common/soc-acpi-intel-cml-match.c
sound/soc/intel/common/soc-acpi-intel-icl-match.c
sound/soc/meson/axg-card.c
sound/soc/meson/gx-card.c
sound/soc/qcom/apq8096.c
sound/soc/qcom/qdsp6/q6afe-dai.c
sound/soc/qcom/sdm845.c
sound/soc/samsung/s3c-i2s-v2.c
sound/soc/samsung/s3c2412-i2s.c
sound/soc/sh/rcar/ssi.c
sound/soc/sh/rcar/ssiu.c
sound/soc/soc-dai.c
sound/soc/soc-dapm.c
sound/soc/soc-pcm.c
sound/soc/soc-topology.c
sound/soc/sof/intel/bdw.c
sound/soc/sof/intel/byt.c
sound/soc/stm/stm32_sai_sub.c
sound/usb/format.c
sound/usb/line6/podhd.c
sound/usb/mixer.c
sound/usb/mixer.h
sound/usb/mixer_maps.c
sound/usb/mixer_quirks.c
sound/usb/quirks-table.h
sound/usb/quirks.c
sound/usb/usx2y/usbusx2yaudio.c
tools/arch/x86/include/asm/cpufeatures.h
tools/arch/x86/include/asm/msr-index.h
tools/bpf/bpf_asm.c
tools/bpf/bpf_dbg.c
tools/bpf/bpftool/Documentation/bpftool-feature.rst
tools/bpf/bpftool/Documentation/bpftool-link.rst [new file with mode: 0644]
tools/bpf/bpftool/Makefile
tools/bpf/bpftool/bash-completion/bpftool
tools/bpf/bpftool/btf.c
tools/bpf/bpftool/cgroup.c
tools/bpf/bpftool/common.c
tools/bpf/bpftool/feature.c
tools/bpf/bpftool/gen.c
tools/bpf/bpftool/jit_disasm.c
tools/bpf/bpftool/link.c [new file with mode: 0644]
tools/bpf/bpftool/main.c
tools/bpf/bpftool/main.h
tools/bpf/bpftool/struct_ops.c
tools/bpf/runqslower/Makefile
tools/build/feature/test-sync-compare-and-swap.c
tools/include/linux/bits.h
tools/include/linux/build_bug.h [new file with mode: 0644]
tools/include/linux/compiler.h
tools/include/linux/const.h
tools/include/linux/kernel.h
tools/include/uapi/drm/drm.h
tools/include/uapi/drm/i915_drm.h
tools/include/uapi/linux/bpf.h
tools/include/uapi/linux/fscrypt.h
tools/include/uapi/linux/if_link.h
tools/include/uapi/linux/kvm.h
tools/include/uapi/linux/mman.h
tools/include/uapi/linux/sched.h
tools/include/uapi/linux/vhost.h
tools/include/vdso/bits.h [new file with mode: 0644]
tools/include/vdso/const.h [new file with mode: 0644]
tools/lib/bpf/bpf.c
tools/lib/bpf/bpf.h
tools/lib/bpf/bpf_helpers.h
tools/lib/bpf/btf_dump.c
tools/lib/bpf/hashmap.c
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf.h
tools/lib/bpf/libbpf.map
tools/lib/bpf/netlink.c
tools/objtool/check.c
tools/objtool/elf.c
tools/objtool/elf.h
tools/objtool/orc_dump.c
tools/objtool/orc_gen.c
tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
tools/perf/check-headers.sh
tools/perf/trace/beauty/clone.c
tools/perf/trace/beauty/mmap.c
tools/perf/util/setup.py
tools/perf/util/stat-shadow.c
tools/power/pm-graph/Makefile
tools/power/pm-graph/README
tools/power/pm-graph/bootgraph.py
tools/power/pm-graph/sleepgraph.8
tools/power/pm-graph/sleepgraph.py
tools/testing/selftests/bpf/.gitignore
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/prog_tests/bpf_obj_id.c
tools/testing/selftests/bpf/prog_tests/btf_map_in_map.c [new file with mode: 0644]
tools/testing/selftests/bpf/prog_tests/cls_redirect.c [new file with mode: 0644]
tools/testing/selftests/bpf/prog_tests/core_reloc.c
tools/testing/selftests/bpf/prog_tests/enable_stats.c [new file with mode: 0644]
tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c
tools/testing/selftests/bpf/prog_tests/hashmap.c [new file with mode: 0644]
tools/testing/selftests/bpf/prog_tests/mmap.c
tools/testing/selftests/bpf/prog_tests/ns_current_pid_tgid.c
tools/testing/selftests/bpf/prog_tests/perf_buffer.c
tools/testing/selftests/bpf/prog_tests/section_names.c
tools/testing/selftests/bpf/prog_tests/sk_assign.c
tools/testing/selftests/bpf/prog_tests/test_lsm.c
tools/testing/selftests/bpf/prog_tests/xdp_attach.c
tools/testing/selftests/bpf/prog_tests/xdp_info.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/connect4_prog.c
tools/testing/selftests/bpf/progs/freplace_connect4.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/lsm.c
tools/testing/selftests/bpf/progs/test_btf_haskv.c
tools/testing/selftests/bpf/progs/test_btf_map_in_map.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_btf_newkv.c
tools/testing/selftests/bpf/progs/test_btf_nokv.c
tools/testing/selftests/bpf/progs/test_cls_redirect.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_cls_redirect.h [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_enable_stats.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_obj_id.c
tools/testing/selftests/bpf/progs/test_sk_assign.c
tools/testing/selftests/bpf/progs/test_sysctl_prog.c
tools/testing/selftests/bpf/test_btf.c
tools/testing/selftests/bpf/test_hashmap.c [deleted file]
tools/testing/selftests/bpf/test_progs.c
tools/testing/selftests/bpf/test_progs.h
tools/testing/selftests/bpf/test_verifier.c
tools/testing/selftests/bpf/verifier/bounds.c
tools/testing/selftests/bpf/verifier/event_output.c
tools/testing/selftests/bpf/verifier/prevent_map_lookup.c
tools/testing/selftests/bpf/verifier/sock.c
tools/testing/selftests/bpf/verifier/stack_ptr.c
tools/testing/selftests/bpf/verifier/value_illegal_alu.c
tools/testing/selftests/drivers/net/netdevsim/devlink.sh
tools/testing/selftests/ftrace/config
tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc
tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc
tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc
tools/testing/selftests/ftrace/test.d/ftrace/func-filter-notrace-pid.tc
tools/testing/selftests/ftrace/test.d/ftrace/func-filter-pid.tc
tools/testing/selftests/ftrace/test.d/ftrace/func-filter-stacktrace.tc
tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc
tools/testing/selftests/ftrace/test.d/ftrace/func_mod_trace.tc
tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc
tools/testing/selftests/ftrace/test.d/ftrace/func_set_ftrace_file.tc
tools/testing/selftests/ftrace/test.d/ftrace/func_stack_tracer.tc
tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc
tools/testing/selftests/ftrace/test.d/functions
tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_type.tc
tools/testing/selftests/ftrace/test.d/kprobe/kprobe_ftrace.tc
tools/testing/selftests/gpio/Makefile
tools/testing/selftests/intel_pstate/Makefile
tools/testing/selftests/ipc/msgque.c
tools/testing/selftests/kselftest/runner.sh
tools/testing/selftests/kselftest_deps.sh [new file with mode: 0755]
tools/testing/selftests/kselftest_harness.h
tools/testing/selftests/memfd/Makefile
tools/testing/selftests/net/Makefile
tools/testing/selftests/net/fib_nexthops.sh
tools/testing/selftests/net/fib_tests.sh
tools/testing/selftests/net/forwarding/pedit_dsfield.sh
tools/testing/selftests/net/forwarding/tc_actions.sh
tools/testing/selftests/net/pmtu.sh
tools/testing/selftests/net/tcp_mmap.c
tools/testing/selftests/net/tls.c
tools/testing/selftests/net/vrf-xfrm-tests.sh [new file with mode: 0755]
tools/testing/selftests/ptp/testptp.c
tools/testing/selftests/seccomp/seccomp_bpf.c
tools/testing/selftests/tc-testing/tc-tests/actions/pedit.json
tools/testing/selftests/tc-testing/tdc.py
tools/testing/selftests/tpm2/test_smoke.sh
tools/testing/selftests/tpm2/test_space.sh
tools/testing/selftests/vm/Makefile
tools/testing/selftests/vm/run_vmtests
tools/testing/selftests/wireguard/netns.sh
tools/testing/selftests/wireguard/qemu/arch/powerpc64le.config
tools/virtio/Makefile
tools/virtio/asm/barrier.h
tools/virtio/generated/autoconf.h [new file with mode: 0644]
tools/virtio/linux/compiler.h
tools/vm/Makefile

index 6ec5558..e92e6dd 100644 (file)
@@ -142,10 +142,13 @@ ForEachMacros:
   - 'for_each_card_auxs'
   - 'for_each_card_auxs_safe'
   - 'for_each_card_components'
+  - 'for_each_card_dapms'
   - 'for_each_card_pre_auxs'
   - 'for_each_card_prelinks'
   - 'for_each_card_rtds'
   - 'for_each_card_rtds_safe'
+  - 'for_each_card_widgets'
+  - 'for_each_card_widgets_safe'
   - 'for_each_cgroup_storage_type'
   - 'for_each_child_of_node'
   - 'for_each_clear_bit'
@@ -160,6 +163,7 @@ ForEachMacros:
   - 'for_each_cpu_and'
   - 'for_each_cpu_not'
   - 'for_each_cpu_wrap'
+  - 'for_each_dapm_widgets'
   - 'for_each_dev_addr'
   - 'for_each_dev_scope'
   - 'for_each_displayid_db'
@@ -170,7 +174,6 @@ ForEachMacros:
   - 'for_each_dpcm_fe'
   - 'for_each_drhd_unit'
   - 'for_each_dss_dev'
-  - 'for_each_efi_handle'
   - 'for_each_efi_memory_desc'
   - 'for_each_efi_memory_desc_in_map'
   - 'for_each_element'
@@ -191,6 +194,7 @@ ForEachMacros:
   - 'for_each_ip_tunnel_rcu'
   - 'for_each_irq_nr'
   - 'for_each_link_codecs'
+  - 'for_each_link_cpus'
   - 'for_each_link_platforms'
   - 'for_each_lru'
   - 'for_each_matching_node'
@@ -250,6 +254,7 @@ ForEachMacros:
   - 'for_each_pci_bridge'
   - 'for_each_pci_dev'
   - 'for_each_pci_msi_entry'
+  - 'for_each_pcm_streams'
   - 'for_each_populated_zone'
   - 'for_each_possible_cpu'
   - 'for_each_present_cpu'
@@ -260,9 +265,12 @@ ForEachMacros:
   - 'for_each_property_of_node'
   - 'for_each_registered_fb'
   - 'for_each_reserved_mem_region'
-  - 'for_each_rtd_codec_dai'
-  - 'for_each_rtd_codec_dai_rollback'
+  - 'for_each_rtd_codec_dais'
+  - 'for_each_rtd_codec_dais_rollback'
   - 'for_each_rtd_components'
+  - 'for_each_rtd_cpu_dais'
+  - 'for_each_rtd_cpu_dais_rollback'
+  - 'for_each_rtd_dais'
   - 'for_each_set_bit'
   - 'for_each_set_bit_from'
   - 'for_each_set_clump8'
@@ -334,6 +342,7 @@ ForEachMacros:
   - 'klp_for_each_object'
   - 'klp_for_each_object_safe'
   - 'klp_for_each_object_static'
+  - 'kunit_suite_for_each_test_case'
   - 'kvm_for_each_memslot'
   - 'kvm_for_each_vcpu'
   - 'list_for_each'
@@ -387,6 +396,7 @@ ForEachMacros:
   - 'of_property_for_each_string'
   - 'of_property_for_each_u32'
   - 'pci_bus_for_each_resource'
+  - 'pcm_for_each_format'
   - 'ping_portaddr_for_each_entry'
   - 'plist_for_each'
   - 'plist_for_each_continue'
@@ -482,7 +492,7 @@ KeepEmptyLinesAtTheStartOfBlocks: false
 MacroBlockBegin: ''
 MacroBlockEnd: ''
 MaxEmptyLinesToKeep: 1
-NamespaceIndentation: Inner
+NamespaceIndentation: None
 #ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
 ObjCBlockIndentWidth: 8
 ObjCSpaceAfterProperty: true
index 664a8f6..3b40457 100644 (file)
@@ -124,6 +124,19 @@ Description:
                authentication is performed (e.g: 802.1x). 'link_mode' attribute
                will also reflect the dormant state.
 
+What:          /sys/class/net/<iface>/testing
+Date:          April 2002
+KernelVersion: 5.8
+Contact:       netdev@vger.kernel.org
+Description:
+               Indicates whether the interface is under test. Possible
+               values are:
+               0: interface is not being tested
+               1: interface is being tested
+
+               When an interface is under test, it cannot be expected
+               to pass packets as normal.
+
 What:          /sys/clas/net/<iface>/duplex
 Date:          October 2009
 KernelVersion: 2.6.33
index c00f9f1..8439d2a 100644 (file)
@@ -182,12 +182,15 @@ fix_padding
        space-efficient. If this option is not present, large padding is
        used - that is for compatibility with older kernels.
 
-
-The journal mode (D/J), buffer_sectors, journal_watermark, commit_time can
-be changed when reloading the target (load an inactive table and swap the
-tables with suspend and resume). The other arguments should not be changed
-when reloading the target because the layout of disk data depend on them
-and the reloaded target would be non-functional.
+allow_discards
+       Allow block discard requests (a.k.a. TRIM) for the integrity device.
+       Discards are only allowed to devices using internal hash.
+
+The journal mode (D/J), buffer_sectors, journal_watermark, commit_time and
+allow_discards can be changed when reloading the target (load an inactive
+table and swap the tables with suspend and resume). The other arguments
+should not be changed when reloading the target because the layout of disk
+data depend on them and the reloaded target would be non-functional.
 
 
 The layout of the formatted block device:
index f2a93c8..a827ec8 100644 (file)
                              shot down by NMI
 
        autoconf=       [IPV6]
-                       See Documentation/networking/ipv6.txt.
+                       See Documentation/networking/ipv6.rst.
 
        show_lapic=     [APIC,X86] Advanced Programmable Interrupt Controller
                        Limit apic dumping. The parameter defines the maximal
 
                        See Documentation/admin-guide/serial-console.rst for more
                        information.  See
-                       Documentation/networking/netconsole.txt for an
+                       Documentation/networking/netconsole.rst for an
                        alternative.
 
                uart[8250],io,<addr>[,options]
 
        decnet.addr=    [HW,NET]
                        Format: <area>[,<node>]
-                       See also Documentation/networking/decnet.txt.
+                       See also Documentation/networking/decnet.rst.
 
        default_hugepagesz=
                        [same as hugepagesz=] The size of the default
                        miss to occur.
 
        disable=        [IPV6]
-                       See Documentation/networking/ipv6.txt.
+                       See Documentation/networking/ipv6.rst.
 
        hardened_usercopy=
                         [KNL] Under CONFIG_HARDENED_USERCOPY, whether
                        to workaround buggy firmware.
 
        disable_ipv6=   [IPV6]
-                       See Documentation/networking/ipv6.txt.
+                       See Documentation/networking/ipv6.rst.
 
        disable_mtrr_cleanup [X86]
                        The kernel tries to adjust MTRR layout from continuous
                        Set the number of tcp_metrics_hash slots.
                        Default value is 8192 or 16384 depending on total
                        ram pages. This is used to specify the TCP metrics
-                       cache size. See Documentation/networking/ip-sysctl.txt
+                       cache size. See Documentation/networking/ip-sysctl.rst
                        "tcp_no_metrics_save" section for more details.
 
        tdfx=           [HW,DRM]
 
        usbcore.old_scheme_first=
                        [USB] Start with the old device initialization
-                       scheme,  applies only to low and full-speed devices
-                        (default 0 = off).
+                       scheme (default 0 = off).
 
        usbcore.usbfs_memory_mb=
                        [USB] Memory limit (in MB) for buffers allocated by
index a8d1e36..58b3283 100644 (file)
@@ -54,7 +54,7 @@ You will need to create a new device to use ``/dev/console``. The official
 ``/dev/console`` is now character device 5,1.
 
 (You can also use a network device as a console.  See
-``Documentation/networking/netconsole.txt`` for information on that.)
+``Documentation/networking/netconsole.rst`` for information on that.)
 
 Here's an example that will use ``/dev/ttyS1`` (COM2) as the console.
 Replace the sample values as needed.
index 39c95c0..0d427fd 100644 (file)
@@ -390,9 +390,17 @@ When ``kptr_restrict`` is set to 2, kernel pointers printed using
 modprobe
 ========
 
-This gives the full path of the modprobe command which the kernel will
-use to load modules. This can be used to debug module loading
-requests::
+The full path to the usermode helper for autoloading kernel modules,
+by default "/sbin/modprobe".  This binary is executed when the kernel
+requests a module.  For example, if userspace passes an unknown
+filesystem type to mount(), then the kernel will automatically request
+the corresponding filesystem module by executing this usermode helper.
+This usermode helper should insert the needed module into the kernel.
+
+This sysctl only affects module autoloading.  It has no effect on the
+ability to explicitly insert modules.
+
+This sysctl can be used to debug module loading requests::
 
     echo '#! /bin/sh' > /tmp/modprobe
     echo 'echo "$@" >> /tmp/modprobe.log' >> /tmp/modprobe
@@ -400,10 +408,15 @@ requests::
     chmod a+x /tmp/modprobe
     echo /tmp/modprobe > /proc/sys/kernel/modprobe
 
-This only applies when the *kernel* is requesting that the module be
-loaded; it won't have any effect if the module is being loaded
-explicitly using ``modprobe`` from userspace.
+Alternatively, if this sysctl is set to the empty string, then module
+autoloading is completely disabled.  The kernel will not try to
+execute a usermode helper at all, nor will it call the
+kernel_module_request LSM hook.
 
+If CONFIG_STATIC_USERMODEHELPER=y is set in the kernel configuration,
+then the configured static usermode helper overrides this sysctl,
+except that the empty string is still accepted to completely disable
+module autoloading as described above.
 
 modules_disabled
 ================
@@ -446,28 +459,6 @@ Notes:
      successful IPC object allocation. If an IPC object allocation syscall
      fails, it is undefined if the value remains unmodified or is reset to -1.
 
-modprobe:
-=========
-
-The path to the usermode helper for autoloading kernel modules, by
-default "/sbin/modprobe".  This binary is executed when the kernel
-requests a module.  For example, if userspace passes an unknown
-filesystem type to mount(), then the kernel will automatically request
-the corresponding filesystem module by executing this usermode helper.
-This usermode helper should insert the needed module into the kernel.
-
-This sysctl only affects module autoloading.  It has no effect on the
-ability to explicitly insert modules.
-
-If this sysctl is set to the empty string, then module autoloading is
-completely disabled.  The kernel will not try to execute a usermode
-helper at all, nor will it call the kernel_module_request LSM hook.
-
-If CONFIG_STATIC_USERMODEHELPER=y is set in the kernel configuration,
-then the configured static usermode helper overrides this sysctl,
-except that the empty string is still accepted to completely disable
-module autoloading as described above.
-
 nmi_watchdog
 ============
 
index e043c92..2ad1b77 100644 (file)
@@ -353,8 +353,8 @@ socket's buffer. It will not take effect unless PF_UNIX flag is specified.
 
 3. /proc/sys/net/ipv4 - IPV4 settings
 -------------------------------------
-Please see: Documentation/networking/ip-sysctl.txt and ipvs-sysctl.txt for
-descriptions of these entries.
+Please see: Documentation/networking/ip-sysctl.rst and
+Documentation/admin-guide/sysctl/net.rst for descriptions of these entries.
 
 
 4. Appletalk
index 5057b11..036783e 100644 (file)
@@ -23,13 +23,14 @@ optional external memory-mapped interface.
 
 Version 1 of the Activity Monitors architecture implements a counter group
 of four fixed and architecturally defined 64-bit event counters.
-  - CPU cycle counter: increments at the frequency of the CPU.
-  - Constant counter: increments at the fixed frequency of the system
-    clock.
-  - Instructions retired: increments with every architecturally executed
-    instruction.
-  - Memory stall cycles: counts instruction dispatch stall cycles caused by
-    misses in the last level cache within the clock domain.
+
+- CPU cycle counter: increments at the frequency of the CPU.
+- Constant counter: increments at the fixed frequency of the system
+  clock.
+- Instructions retired: increments with every architecturally executed
+  instruction.
+- Memory stall cycles: counts instruction dispatch stall cycles caused by
+  misses in the last level cache within the clock domain.
 
 When in WFI or WFE these counters do not increment.
 
@@ -57,11 +58,12 @@ counters, only the presence of the extension.
 
 Firmware (code running at higher exception levels, e.g. arm-tf) support is
 needed to:
- - Enable access for lower exception levels (EL2 and EL1) to the AMU
-   registers.
- - Enable the counters. If not enabled these will read as 0.
- - Save/restore the counters before/after the CPU is being put/brought up
-   from the 'off' power state.
+
+- Enable access for lower exception levels (EL2 and EL1) to the AMU
+  registers.
+- Enable the counters. If not enabled these will read as 0.
+- Save/restore the counters before/after the CPU is being put/brought up
+  from the 'off' power state.
 
 When using kernels that have this feature enabled but boot with broken
 firmware the user may experience panics or lockups when accessing the
@@ -78,10 +80,11 @@ are not trapped in EL2/EL3.
 
 The fixed counters of AMUv1 are accessible though the following system
 register definitions:
- - SYS_AMEVCNTR0_CORE_EL0
- - SYS_AMEVCNTR0_CONST_EL0
- - SYS_AMEVCNTR0_INST_RET_EL0
- - SYS_AMEVCNTR0_MEM_STALL_EL0
+
+- SYS_AMEVCNTR0_CORE_EL0
+- SYS_AMEVCNTR0_CONST_EL0
+- SYS_AMEVCNTR0_INST_RET_EL0
+- SYS_AMEVCNTR0_MEM_STALL_EL0
 
 Auxiliary platform specific counters can be accessed using
 SYS_AMEVCNTR1_EL0(n), where n is a value between 0 and 15.
@@ -93,9 +96,10 @@ Userspace access
 ----------------
 
 Currently, access from userspace to the AMU registers is disabled due to:
- - Security reasons: they might expose information about code executed in
-   secure mode.
- - Purpose: AMU counters are intended for system management use.
+
+- Security reasons: they might expose information about code executed in
+  secure mode.
+- Purpose: AMU counters are intended for system management use.
 
 Also, the presence of the feature is not visible to userspace.
 
@@ -105,8 +109,9 @@ Virtualization
 
 Currently, access from userspace (EL0) and kernelspace (EL1) on the KVM
 guest side is disabled due to:
- - Security reasons: they might expose information about code executed
-   by other guests or the host.
+
+- Security reasons: they might expose information about code executed
+  by other guests or the host.
 
 Any attempt to access the AMU registers will result in an UNDEFINED
 exception being injected into the guest.
index f99677f..38b4db8 100644 (file)
@@ -7,7 +7,7 @@ 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
+`Documentation/networking/filter.rst`_, 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.
@@ -59,7 +59,7 @@ Testing and debugging BPF
 
 
 .. Links:
-.. _Documentation/networking/filter.txt: ../networking/filter.txt
+.. _Documentation/networking/filter.rst: ../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/
index c0ffa30..729e248 100644 (file)
@@ -154,9 +154,9 @@ architectures. These are the recommended replacements:
 
        Use ktime_get() or ktime_get_ts64() instead.
 
-.. c:function:: struct timeval do_gettimeofday( void )
-               struct timespec getnstimeofday( void )
-               struct timespec64 getnstimeofday64( void )
+.. c:function:: void do_gettimeofday( struct timeval * )
+               void getnstimeofday( struct timespec * )
+               void getnstimeofday64( struct timespec64 * )
                void ktime_get_real_ts( struct timespec * )
 
        ktime_get_real_ts64() is a direct replacement, but consider using
index 61ae13c..5d1f56f 100644 (file)
@@ -301,7 +301,8 @@ Helpers
 
 .. kernel-doc:: tools/testing/selftests/kselftest_harness.h
     :functions: TH_LOG TEST TEST_SIGNAL FIXTURE FIXTURE_DATA FIXTURE_SETUP
-                FIXTURE_TEARDOWN TEST_F TEST_HARNESS_MAIN
+                FIXTURE_TEARDOWN TEST_F TEST_HARNESS_MAIN FIXTURE_VARIANT
+                FIXTURE_VARIANT_ADD
 
 Operators
 ---------
index 1df680d..7782d99 100644 (file)
@@ -2,6 +2,7 @@
 DT_DOC_CHECKER ?= dt-doc-validate
 DT_EXTRACT_EX ?= dt-extract-example
 DT_MK_SCHEMA ?= dt-mk-schema
+DT_MK_SCHEMA_USERONLY_FLAG := $(if $(DT_SCHEMA_FILES), -u)
 
 quiet_cmd_chk_binding = CHKDT   $(patsubst $(srctree)/%,%,$<)
       cmd_chk_binding = $(DT_DOC_CHECKER) -u $(srctree)/$(src) $< ; \
@@ -13,16 +14,18 @@ $(obj)/%.example.dts: $(src)/%.yaml FORCE
 # Use full schemas when checking %.example.dts
 DT_TMP_SCHEMA := $(obj)/processed-schema-examples.yaml
 
+find_cmd = find $(srctree)/$(src) \( -name '*.yaml' ! \
+               -name 'processed-schema*' ! \
+               -name '*.example.dt.yaml' \)
+
 quiet_cmd_mk_schema = SCHEMA  $@
-      cmd_mk_schema = $(DT_MK_SCHEMA) $(DT_MK_SCHEMA_FLAGS) -o $@ $(real-prereqs)
+      cmd_mk_schema = rm -f $@ ; \
+                      $(if $(DT_MK_SCHEMA_FLAGS), \
+                           echo $(real-prereqs), \
+                           $(find_cmd)) | \
+                      xargs $(DT_MK_SCHEMA) $(DT_MK_SCHEMA_FLAGS) >> $@
 
-DT_DOCS = $(addprefix $(src)/, \
-       $(shell \
-       cd $(srctree)/$(src) && \
-       find * \( -name '*.yaml' ! \
-               -name 'processed-schema*' ! \
-               -name '*.example.dt.yaml' \) \
-       ))
+DT_DOCS = $(shell $(find_cmd) | sed -e 's|^$(srctree)/||')
 
 DT_SCHEMA_FILES ?= $(DT_DOCS)
 
@@ -37,7 +40,7 @@ override DTC_FLAGS := \
 $(obj)/processed-schema-examples.yaml: $(DT_DOCS) FORCE
        $(call if_changed,mk_schema)
 
-$(obj)/processed-schema.yaml: DT_MK_SCHEMA_FLAGS := -u
+$(obj)/processed-schema.yaml: DT_MK_SCHEMA_FLAGS := $(DT_MK_SCHEMA_USERONLY_FLAG)
 $(obj)/processed-schema.yaml: $(DT_SCHEMA_FILES) FORCE
        $(call if_changed,mk_schema)
 
index aa0738b..e713a6f 100644 (file)
@@ -42,6 +42,10 @@ properties:
     description:
       See section 2.3.9 of the DeviceTree Specification.
 
+  '#address-cells': true
+
+  '#size-cells': true
+
 required:
   - "#interconnect-cells"
   - compatible
@@ -59,6 +63,8 @@ examples:
         compatible = "allwinner,sun5i-a13-mbus";
         reg = <0x01c01000 0x1000>;
         clocks = <&ccu CLK_MBUS>;
+        #address-cells = <1>;
+        #size-cells = <1>;
         dma-ranges = <0x00000000 0x40000000 0x20000000>;
         #interconnect-cells = <1>;
     };
index de9a465..444aeea 100644 (file)
@@ -91,7 +91,7 @@ required:
 
 examples:
   - |
-    vco1: clock@00 {
+    vco1: clock {
       compatible = "arm,impd1-vco1";
       #clock-cells = <0>;
       lock-offset = <0x08>;
index a6b2b2b..d3c2a49 100644 (file)
@@ -1,5 +1,5 @@
-Analog Device ADV7123 Video DAC
--------------------------------
+Analog Devices ADV7123 Video DAC
+--------------------------------
 
 The ADV7123 is a digital-to-analog converter that outputs VGA signals from a
 parallel video input.
index e8ddec5..659523f 100644 (file)
@@ -1,5 +1,5 @@
-Analog Device ADV7511(W)/13/33/35 HDMI Encoders
------------------------------------------
+Analog Devices ADV7511(W)/13/33/35 HDMI Encoders
+------------------------------------------------
 
 The ADV7511, ADV7511W, ADV7513, ADV7533 and ADV7535 are HDMI audio and video
 transmitters compatible with HDMI 1.4 and DVI 1.0. They support color space
index fd931b2..b900973 100644 (file)
@@ -37,7 +37,6 @@ examples:
     dsi {
         #address-cells = <1>;
         #size-cells = <0>;
-        reg = <0xff450000 0x1000>;
 
         panel@0 {
             compatible = "leadtek,ltk500hd1829";
index d008330..946dd35 100644 (file)
@@ -96,12 +96,20 @@ properties:
       If set, reverse the bit order described in the data mappings below on all
       data lanes, transmitting bits for slots 6 to 0 instead of 0 to 6.
 
+  port: true
+  ports: true
+
 required:
   - compatible
   - data-mapping
   - width-mm
   - height-mm
   - panel-timing
-  - port
+
+oneOf:
+  - required:
+      - port
+  - required:
+      - ports
 
 ...
index d9fdb58..6913923 100644 (file)
@@ -37,7 +37,6 @@ examples:
     dsi {
         #address-cells = <1>;
         #size-cells = <0>;
-        reg = <0xff450000 0x1000>;
 
         panel@0 {
             compatible = "xinpeng,xpp055c272";
index b38ee73..cd17684 100644 (file)
@@ -1,4 +1,4 @@
-Analog Device AXI-DMAC DMA controller
+Analog Devices AXI-DMAC DMA controller
 
 Required properties:
  - compatible: Must be "adi,axi-dmac-1.00.a".
index 86cfb59..371f187 100644 (file)
@@ -22,9 +22,7 @@ properties:
     const: socionext,uniphier-xdmac
 
   reg:
-    items:
-      - description: XDMAC base register region (offset and length)
-      - description: XDMAC extension register region (offset and length)
+    maxItems: 1
 
   interrupts:
     maxItems: 1
@@ -49,12 +47,13 @@ required:
   - reg
   - interrupts
   - "#dma-cells"
+  - dma-channels
 
 examples:
   - |
     xdmac: dma-controller@5fc10000 {
         compatible = "socionext,uniphier-xdmac";
-        reg = <0x5fc10000 0x1000>, <0x5fc20000 0x800>;
+        reg = <0x5fc10000 0x5300>;
         interrupts = <0 188 4>;
         #dma-cells = <2>;
         dma-channels = <16>;
index 57a240d..7db7876 100644 (file)
@@ -2,7 +2,7 @@
 # Copyright 2019 Analog Devices Inc.
 %YAML 1.2
 ---
-$id: http://devicetree.org/schemas/bindings/hwmon/adi,axi-fan-control.yaml#
+$id: http://devicetree.org/schemas/hwmon/adi,axi-fan-control.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
 title: Analog Devices AXI FAN Control Device Tree Bindings
@@ -47,7 +47,7 @@ required:
 
 examples:
   - |
-    fpga_axi: fpga-axi@0 {
+    fpga_axi: fpga-axi {
             #address-cells = <0x2>;
             #size-cells = <0x1>;
 
index 7698503..46c4415 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
 %YAML 1.2
 ---
-$id: http://devicetree.org/schemas/adt7475.yaml#
+$id: http://devicetree.org/schemas/hwmon/adt7475.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
 title: ADT7475 hwmon sensor
index 933ba37..dd8eb15 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
 %YAML 1.2
 ---
-$id: "http://devicetree.org/schemas/bindings/iio/adc/st,stm32-adc.yaml#"
+$id: "http://devicetree.org/schemas/iio/adc/st,stm32-adc.yaml#"
 $schema: "http://devicetree.org/meta-schemas/core.yaml#"
 
 title: STMicroelectronics STM32 ADC bindings
index f0bbd7e..502e1e5 100644 (file)
@@ -1,4 +1,4 @@
-* Analog Device AD5755 IIO Multi-Channel DAC Linux Driver
+* Analog Devices AD5755 IIO Multi-Channel DAC Linux Driver
 
 Required properties:
  - compatible: Has to contain one of the following:
index d9c25cf..58d81ca 100644 (file)
@@ -2,7 +2,7 @@
 # Copyright 2020 Analog Devices Inc.
 %YAML 1.2
 ---
-$id: http://devicetree.org/schemas/bindings/iio/dac/adi,ad5770r.yaml#
+$id: http://devicetree.org/schemas/iio/dac/adi,ad5770r.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
 title: Analog Devices AD5770R DAC device driver
@@ -49,93 +49,86 @@ properties:
       asserted during driver probe.
     maxItems: 1
 
-  channel0:
+  channel@0:
     description: Represents an external channel which are
       connected to the DAC. Channel 0 can act both as a current
       source and sink.
     type: object
 
     properties:
-      num:
+      reg:
         description: This represents the channel number.
-        items:
-          const: 0
+        const: 0
 
       adi,range-microamp:
           description: Output range of the channel.
           oneOf:
-            - $ref: /schemas/types.yaml#/definitions/int32-array
             - items:
-                - enum: [0 300000]
-                - enum: [-60000 0]
-                - enum: [-60000 300000]
+                - const: 0
+                - const: 300000
+            - items:
+                - const: -60000
+                - const: 0
+            - items:
+                - const: -60000
+                - const: 300000
 
-  channel1:
+  channel@1:
     description: Represents an external channel which are
       connected to the DAC.
     type: object
 
     properties:
-      num:
+      reg:
         description: This represents the channel number.
-        items:
-          const: 1
+        const: 1
 
       adi,range-microamp:
           description: Output range of the channel.
-          oneOf:
-            - $ref: /schemas/types.yaml#/definitions/uint32-array
-            - items:
-                - enum: [0 140000]
-                - enum: [0 250000]
+          items:
+            - const: 0
+            - enum: [ 140000, 250000 ]
 
-  channel2:
+  channel@2:
     description: Represents an external channel which are
       connected to the DAC.
     type: object
 
     properties:
-      num:
+      reg:
         description: This represents the channel number.
-        items:
-          const: 2
+        const: 2
 
       adi,range-microamp:
           description: Output range of the channel.
-          oneOf:
-            - $ref: /schemas/types.yaml#/definitions/uint32-array
-            - items:
-                - enum: [0 140000]
-                - enum: [0 250000]
+          items:
+            - const: 0
+            - enum: [ 55000, 150000 ]
 
 patternProperties:
   "^channel@([3-5])$":
     type: object
     description: Represents the external channels which are connected to the DAC.
     properties:
-      num:
+      reg:
         description: This represents the channel number.
-        items:
-          minimum: 3
-          maximum: 5
+        minimum: 3
+        maximum: 5
 
       adi,range-microamp:
           description: Output range of the channel.
-          oneOf:
-            - $ref: /schemas/types.yaml#/definitions/uint32-array
-            - items:
-                - enum: [0 45000]
-                - enum: [0 100000]
+          items:
+            - const: 0
+            - enum: [ 45000, 100000 ]
 
 required:
 - reg
-- diff-channels
-- channel0
-- channel1
-- channel2
-- channel3
-- channel4
-- channel5
+- channel@0
+- channel@1
+- channel@2
+- channel@3
+- channel@4
+- channel@5
 
 examples:
   - |
@@ -144,40 +137,42 @@ examples:
                 #size-cells = <0>;
 
                 ad5770r@0 {
-                        compatible = "ad5770r";
+                        compatible = "adi,ad5770r";
                         reg = <0>;
                         spi-max-frequency = <1000000>;
                         vref-supply = <&vref>;
                         adi,external-resistor;
                         reset-gpios = <&gpio 22 0>;
+                        #address-cells = <1>;
+                        #size-cells = <0>;
 
                         channel@0 {
-                                num = <0>;
-                                adi,range-microamp = <(-60000) 300000>;
+                                reg = <0>;
+                                adi,range-microamp = <0 300000>;
                         };
 
                         channel@1 {
-                                num = <1>;
+                                reg = <1>;
                                 adi,range-microamp = <0 140000>;
                         };
 
                         channel@2 {
-                                num = <2>;
+                                reg = <2>;
                                 adi,range-microamp = <0 55000>;
                         };
 
                         channel@3 {
-                                num = <3>;
+                                reg = <3>;
                                 adi,range-microamp = <0 45000>;
                         };
 
                         channel@4 {
-                                num = <4>;
+                                reg = <4>;
                                 adi,range-microamp = <0 45000>;
                         };
 
                         channel@5 {
-                                num = <5>;
+                                reg = <5>;
                                 adi,range-microamp = <0 45000>;
                         };
                 };
index 8d58709..383d64a 100644 (file)
@@ -109,7 +109,7 @@ examples:
   - |
     #include <dt-bindings/gpio/gpio.h>
     #include <dt-bindings/interrupt-controller/arm-gic.h>
-    i2c@00000000 {
+    i2c {
       #address-cells = <1>;
       #size-cells = <0>;
       edt-ft5x06@38 {
index 9c6b91f..26f1fcf 100644 (file)
@@ -56,9 +56,8 @@ properties:
       cell with zero.
     allOf:
       - $ref: /schemas/types.yaml#/definitions/uint32-array
-      - items:
-          minItems: 4
-          maxItems: 4
+      - minItems: 4
+        maxItems: 4
 
 
 required:
index 12516bd..611bda3 100644 (file)
@@ -97,30 +97,35 @@ examples:
     #include <dt-bindings/clock/tegra186-clock.h>
     #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-    memory-controller@2c00000 {
-        compatible = "nvidia,tegra186-mc";
-        reg = <0x0 0x02c00000 0x0 0xb0000>;
-        interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_HIGH>;
-
+    bus {
         #address-cells = <2>;
         #size-cells = <2>;
 
-        ranges = <0x0 0x02c00000 0x02c00000 0x0 0xb0000>;
+        memory-controller@2c00000 {
+            compatible = "nvidia,tegra186-mc";
+            reg = <0x0 0x02c00000 0x0 0xb0000>;
+            interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_HIGH>;
+
+            #address-cells = <2>;
+            #size-cells = <2>;
+
+            ranges = <0x0 0x02c00000 0x0 0x02c00000 0x0 0xb0000>;
 
-        /*
-         * Memory clients have access to all 40 bits that the memory
-         * controller can address.
-         */
-        dma-ranges = <0x0 0x0 0x0 0x0 0x100 0x0>;
+            /*
+             * Memory clients have access to all 40 bits that the memory
+             * controller can address.
+             */
+            dma-ranges = <0x0 0x0 0x0 0x0 0x100 0x0>;
 
-        external-memory-controller@2c60000 {
-            compatible = "nvidia,tegra186-emc";
-            reg = <0x0 0x02c60000 0x0 0x50000>;
-            interrupts = <GIC_SPI 224 IRQ_TYPE_LEVEL_HIGH>;
-            clocks = <&bpmp TEGRA186_CLK_EMC>;
-            clock-names = "emc";
+            external-memory-controller@2c60000 {
+                compatible = "nvidia,tegra186-emc";
+                reg = <0x0 0x02c60000 0x0 0x50000>;
+                interrupts = <GIC_SPI 224 IRQ_TYPE_LEVEL_HIGH>;
+                clocks = <&bpmp TEGRA186_CLK_EMC>;
+                clock-names = "emc";
 
-            nvidia,bpmp = <&bpmp>;
+                nvidia,bpmp = <&bpmp>;
+            };
         };
     };
 
index aa922c5..65018a0 100644 (file)
@@ -123,7 +123,9 @@ examples:
     #include <dt-bindings/leds/common.h>
 
     i2c {
-      pmic: pmic@4b {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        pmic: pmic@4b {
             compatible = "rohm,bd71837";
             reg = <0x4b>;
             interrupt-parent = <&gpio1>;
index 402e40d..77bcca2 100644 (file)
@@ -128,7 +128,9 @@ examples:
     #include <dt-bindings/leds/common.h>
 
     i2c {
-      pmic: pmic@4b {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        pmic: pmic@4b {
             compatible = "rohm,bd71847";
             reg = <0x4b>;
             interrupt-parent = <&gpio1>;
index d9ad926..be7faa6 100644 (file)
@@ -259,8 +259,6 @@ properties:
 
     additionalProperties: false
 
-  additionalProperties: false
-
 additionalProperties: false
 
 required:
@@ -274,7 +272,7 @@ examples:
   - |
     #include <dt-bindings/mfd/st,stpmic1.h>
     #include <dt-bindings/interrupt-controller/arm-gic.h>
-    i2c@0 {
+    i2c {
       #address-cells = <1>;
       #size-cells = <0>;
       pmic@33 {
index 8927941..9b1f114 100644 (file)
@@ -43,6 +43,9 @@ properties:
           second group of digits is the Phy Identifier 2 register,
           this is the chip vendor OUI bits 19:24, followed by 10
           bits of a vendor specific ID.
+      - items:
+          - pattern: "^ethernet-phy-id[a-f0-9]{4}\\.[a-f0-9]{4}$"
+          - const: ethernet-phy-ieee802.3-c22
       - items:
           - pattern: "^ethernet-phy-id[a-f0-9]{4}\\.[a-f0-9]{4}$"
           - const: ethernet-phy-ieee802.3-c45
@@ -78,7 +81,8 @@ properties:
     $ref: /schemas/types.yaml#definitions/flag
     description:
       If set, indicates the PHY device does not correctly release
-      the turn around line low at the end of a MDIO transaction.
+      the turn around line low at end of the control phase of the
+      MDIO transaction.
 
   enet-phy-lane-swap:
     $ref: /schemas/types.yaml#definitions/flag
index 5b88fae..26c492a 100644 (file)
@@ -22,6 +22,8 @@ Optional properties:
 - fsl,err006687-workaround-present: If present indicates that the system has
   the hardware workaround for ERR006687 applied and does not need a software
   workaround.
+- gpr: phandle of SoC general purpose register mode. Required for wake on LAN
+  on some SoCs
  -interrupt-names:  names of the interrupts listed in interrupts property in
   the same order. The defaults if not specified are
   __Number of interrupts__   __Default__
@@ -80,6 +82,7 @@ ethernet@83fec000 {
        phy-supply = <&reg_fec_supply>;
        phy-handle = <&ethphy>;
        mdio {
+               clock-frequency = <5000000>;
                ethphy: ethernet-phy@6 {
                        compatible = "ethernet-phy-ieee802.3-c22";
                        reg = <6>;
index 50c3397..d6a3bf8 100644 (file)
@@ -31,13 +31,25 @@ properties:
     maxItems: 1
     description:
       The phandle and specifier for the GPIO that controls the RESET
-      lines of all PHYs on that MDIO bus.
+      lines of all devices on that MDIO bus.
 
   reset-delay-us:
     description:
-      RESET pulse width in microseconds. It applies to all PHY devices
-      and must therefore be appropriately determined based on all PHY
-      requirements (maximum value of all per-PHY RESET pulse widths).
+      RESET pulse width in microseconds. It applies to all MDIO devices
+      and must therefore be appropriately determined based on all devices
+      requirements (maximum value of all per-device RESET pulse widths).
+
+  clock-frequency:
+    description:
+      Desired MDIO bus clock frequency in Hz. Values greater than IEEE 802.3
+      defined 2.5MHz should only be used when all devices on the bus support
+      the given clock speed.
+
+  suppress-preamble:
+    description:
+      The 32 bit preamble should be suppressed. In order for this to
+      work, all devices on the bus must support suppressed preamble.
+    type: boolean
 
 patternProperties:
   "^ethernet-phy@[0-9a-f]+$":
@@ -48,7 +60,35 @@ patternProperties:
         minimum: 0
         maximum: 31
         description:
-          The ID number for the PHY.
+          The ID number for the device.
+
+      broken-turn-around:
+        $ref: /schemas/types.yaml#definitions/flag
+        description:
+          If set, indicates the MDIO device does not correctly release
+          the turn around line low at end of the control phase of the
+          MDIO transaction.
+
+      resets:
+        maxItems: 1
+
+      reset-names:
+        const: phy
+
+      reset-gpios:
+        maxItems: 1
+        description:
+          The GPIO phandle and specifier for the MDIO reset signal.
+
+      reset-assert-us:
+        description:
+          Delay after the reset was asserted in microseconds. If this
+          property is missing the delay will be skipped.
+
+      reset-deassert-us:
+        description:
+          Delay after the reset was deasserted in microseconds. If
+          this property is missing the delay will be skipped.
 
     required:
       - reg
diff --git a/Documentation/devicetree/bindings/net/nxp,tja11xx.yaml b/Documentation/devicetree/bindings/net/nxp,tja11xx.yaml
new file mode 100644 (file)
index 0000000..42be025
--- /dev/null
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: GPL-2.0+
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/nxp,tja11xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP TJA11xx PHY
+
+maintainers:
+  - Andrew Lunn <andrew@lunn.ch>
+  - Florian Fainelli <f.fainelli@gmail.com>
+  - Heiner Kallweit <hkallweit1@gmail.com>
+
+description:
+  Bindings for NXP TJA11xx automotive PHYs
+
+allOf:
+  - $ref: ethernet-phy.yaml#
+
+patternProperties:
+  "^ethernet-phy@[0-9a-f]+$":
+    type: object
+    description: |
+      Some packages have multiple PHYs. Secondary PHY should be defines as
+      subnode of the first (parent) PHY.
+
+    properties:
+      reg:
+        minimum: 0
+        maximum: 31
+        description:
+          The ID number for the child PHY. Should be +1 of parent PHY.
+
+    required:
+      - reg
+
+examples:
+  - |
+    mdio {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        tja1101_phy0: ethernet-phy@4 {
+            reg = <0x4>;
+        };
+    };
+  - |
+    mdio {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        tja1102_phy0: ethernet-phy@4 {
+            reg = <0x4>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            tja1102_phy1: ethernet-phy@5 {
+                reg = <0x5>;
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/net/qca,ar71xx.txt b/Documentation/devicetree/bindings/net/qca,ar71xx.txt
deleted file mode 100644 (file)
index 2a33e71..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-Required properties:
-- compatible:  Should be "qca,<soc>-eth". Currently support compatibles are:
-               qca,ar7100-eth - Atheros AR7100
-               qca,ar7240-eth - Atheros AR7240
-               qca,ar7241-eth - Atheros AR7241
-               qca,ar7242-eth - Atheros AR7242
-               qca,ar9130-eth - Atheros AR9130
-               qca,ar9330-eth - Atheros AR9330
-               qca,ar9340-eth - Atheros AR9340
-               qca,qca9530-eth - Qualcomm Atheros QCA9530
-               qca,qca9550-eth - Qualcomm Atheros QCA9550
-               qca,qca9560-eth - Qualcomm Atheros QCA9560
-
-- reg : Address and length of the register set for the device
-- interrupts : Should contain eth interrupt
-- phy-mode : See ethernet.txt file in the same directory
-- clocks: the clock used by the core
-- clock-names: the names of the clock listed in the clocks property. These are
-       "eth" and "mdio".
-- resets: Should contain phandles to the reset signals
-- reset-names: Should contain the names of reset signal listed in the resets
-               property. These are "mac" and "mdio"
-
-Optional properties:
-- phy-handle : phandle to the PHY device connected to this device.
-- fixed-link : Assume a fixed link. See fixed-link.txt in the same directory.
-  Use instead of phy-handle.
-
-Optional subnodes:
-- mdio : specifies the mdio bus, used as a container for phy nodes
-  according to phy.txt in the same directory
-
-Example:
-
-ethernet@1a000000 {
-       compatible = "qca,ar9330-eth";
-       reg = <0x1a000000 0x200>;
-       interrupts = <5>;
-       resets = <&rst 13>, <&rst 23>;
-       reset-names = "mac", "mdio";
-       clocks = <&pll ATH79_CLK_AHB>, <&pll ATH79_CLK_MDIO>;
-       clock-names = "eth", "mdio";
-
-       phy-mode = "gmii";
-};
diff --git a/Documentation/devicetree/bindings/net/qca,ar71xx.yaml b/Documentation/devicetree/bindings/net/qca,ar71xx.yaml
new file mode 100644 (file)
index 0000000..f99a5aa
--- /dev/null
@@ -0,0 +1,216 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/qca,ar71xx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: QCA AR71XX MAC
+
+allOf:
+  - $ref: ethernet-controller.yaml#
+
+maintainers:
+  - Oleksij Rempel <o.rempel@pengutronix.de>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - enum:
+              - qca,ar7100-eth   # Atheros AR7100
+              - qca,ar7240-eth   # Atheros AR7240
+              - qca,ar7241-eth   # Atheros AR7241
+              - qca,ar7242-eth   # Atheros AR7242
+              - qca,ar9130-eth   # Atheros AR9130
+              - qca,ar9330-eth   # Atheros AR9330
+              - qca,ar9340-eth   # Atheros AR9340
+              - qca,qca9530-eth  # Qualcomm Atheros QCA9530
+              - qca,qca9550-eth  # Qualcomm Atheros QCA9550
+              - qca,qca9560-eth  # Qualcomm Atheros QCA9560
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  '#address-cells':
+    description: number of address cells for the MDIO bus
+    const: 1
+
+  '#size-cells':
+    description: number of size cells on the MDIO bus
+    const: 0
+
+  clocks:
+    items:
+      - description: MAC main clock
+      - description: MDIO clock
+
+  clock-names:
+    items:
+      - const: eth
+      - const: mdio
+
+  resets:
+    items:
+      - description: MAC reset
+      - description: MDIO reset
+
+  reset-names:
+    items:
+      - const: mac
+      - const: mdio
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - phy-mode
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+
+examples:
+  # Lager board
+  - |
+    eth0: ethernet@19000000 {
+        compatible = "qca,ar9330-eth";
+        reg = <0x19000000 0x200>;
+        interrupts = <4>;
+        resets = <&rst 9>, <&rst 22>;
+        reset-names = "mac", "mdio";
+        clocks = <&pll 1>, <&pll 2>;
+        clock-names = "eth", "mdio";
+        qca,ethcfg = <&ethcfg>;
+        phy-mode = "mii";
+        phy-handle = <&phy_port4>;
+    };
+
+    eth1: ethernet@1a000000 {
+        compatible = "qca,ar9330-eth";
+        reg = <0x1a000000 0x200>;
+        interrupts = <5>;
+        resets = <&rst 13>, <&rst 23>;
+        reset-names = "mac", "mdio";
+        clocks = <&pll 1>, <&pll 2>;
+        clock-names = "eth", "mdio";
+
+        phy-mode = "gmii";
+
+        status = "disabled";
+
+        fixed-link {
+            speed = <1000>;
+            full-duplex;
+        };
+
+        mdio {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            switch10: switch@10 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                compatible = "qca,ar9331-switch";
+                reg = <0x10>;
+                resets = <&rst 8>;
+                reset-names = "switch";
+
+                interrupt-parent = <&miscintc>;
+                interrupts = <12>;
+
+                interrupt-controller;
+                #interrupt-cells = <1>;
+
+                ports {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+
+                    switch_port0: port@0 {
+                        reg = <0x0>;
+                        label = "cpu";
+                        ethernet = <&eth1>;
+
+                        phy-mode = "gmii";
+
+                        fixed-link {
+                            speed = <1000>;
+                            full-duplex;
+                        };
+                    };
+
+                    switch_port1: port@1 {
+                        reg = <0x1>;
+                        phy-handle = <&phy_port0>;
+                        phy-mode = "internal";
+
+                        status = "disabled";
+                    };
+
+                    switch_port2: port@2 {
+                        reg = <0x2>;
+                        phy-handle = <&phy_port1>;
+                        phy-mode = "internal";
+
+                        status = "disabled";
+                    };
+
+                    switch_port3: port@3 {
+                        reg = <0x3>;
+                        phy-handle = <&phy_port2>;
+                        phy-mode = "internal";
+
+                        status = "disabled";
+                    };
+
+                    switch_port4: port@4 {
+                        reg = <0x4>;
+                        phy-handle = <&phy_port3>;
+                        phy-mode = "internal";
+
+                        status = "disabled";
+                    };
+                };
+
+                mdio {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+
+                    interrupt-parent = <&switch10>;
+
+                    phy_port0: phy@0 {
+                        reg = <0x0>;
+                        interrupts = <0>;
+                        status = "disabled";
+                    };
+
+                    phy_port1: phy@1 {
+                        reg = <0x1>;
+                        interrupts = <0>;
+                        status = "disabled";
+                    };
+
+                    phy_port2: phy@2 {
+                        reg = <0x2>;
+                        interrupts = <0>;
+                        status = "disabled";
+                    };
+
+                    phy_port3: phy@3 {
+                        reg = <0x3>;
+                        interrupts = <0>;
+                        status = "disabled";
+                    };
+
+                    phy_port4: phy@4 {
+                        reg = <0x4>;
+                        interrupts = <0>;
+                        status = "disabled";
+                    };
+                };
+            };
+        };
+    };
index 140f152..7b749fc 100644 (file)
@@ -20,7 +20,10 @@ description:
   The GSI is an integral part of the IPA, but it is logically isolated
   and has a distinct interrupt and a separately-defined address space.
 
-  See also soc/qcom/qcom,smp2p.txt and interconnect/interconnect.txt.
+  See also soc/qcom/qcom,smp2p.txt and interconnect/interconnect.txt.  See
+  iommu/iommu.txt and iommu/arm,smmu.yaml for more information about SMMU
+  bindings.
+
 
   - |
     --------             ---------
@@ -54,6 +57,9 @@ properties:
       - const: ipa-shared
       - const: gsi
 
+  iommus:
+    maxItems: 1
+
   clocks:
     maxItems: 1
 
@@ -126,6 +132,7 @@ properties:
 
 required:
   - compatible
+  - iommus
   - reg
   - clocks
   - interrupts
@@ -164,6 +171,7 @@ examples:
                 modem-init;
                 modem-remoteproc = <&mss_pil>;
 
+                iommus = <&apps_smmu 0x720 0x3>;
                 reg = <0 0x1e40000 0 0x7000>,
                         <0 0x1e47000 0 0x2000>,
                         <0 0x1e04000 0 0x2c000>;
diff --git a/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml b/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml
new file mode 100644 (file)
index 0000000..13555a8
--- /dev/null
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/qcom,ipq4019-mdio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm IPQ40xx MDIO Controller Device Tree Bindings
+
+maintainers:
+  - Robert Marko <robert.marko@sartura.hr>
+
+allOf:
+  - $ref: "mdio.yaml#"
+
+properties:
+  compatible:
+    const: qcom,ipq4019-mdio
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - "#address-cells"
+  - "#size-cells"
+
+examples:
+  - |
+    mdio@90000 {
+      #address-cells = <1>;
+      #size-cells = <0>;
+      compatible = "qcom,ipq4019-mdio";
+      reg = <0x90000 0x64>;
+
+      ethphy0: ethernet-phy@0 {
+        reg = <0>;
+      };
+
+      ethphy1: ethernet-phy@1 {
+        reg = <1>;
+      };
+
+      ethphy2: ethernet-phy@2 {
+        reg = <2>;
+      };
+
+      ethphy3: ethernet-phy@3 {
+        reg = <3>;
+      };
+
+      ethphy4: ethernet-phy@4 {
+        reg = <4>;
+      };
+    };
index b9f9008..67df3fe 100644 (file)
@@ -48,6 +48,7 @@ examples:
 
         switch@10 {
             compatible = "qca,qca8337";
+            reg = <0x10>;
             /* ... */
         };
     };
index beca646..aad2632 100644 (file)
@@ -13,6 +13,7 @@ Required properties:
    * "qcom,wcn3990-bt"
    * "qcom,wcn3991-bt"
    * "qcom,wcn3998-bt"
+   * "qcom,qca6390-bt"
 
 Optional properties for compatible string qcom,qca6174-bt:
 
@@ -29,7 +30,7 @@ Required properties for compatible string qcom,wcn399x-bt:
 
 Optional properties for compatible string qcom,wcn399x-bt:
 
- - max-speed: see Documentation/devicetree/bindings/serial/slave-device.txt
+ - max-speed: see Documentation/devicetree/bindings/serial/serial.yaml
  - firmware-name: specify the name of nvm firmware to load
  - clocks: clock provided to the controller
 
index 78bf511..0c054a2 100644 (file)
@@ -144,6 +144,13 @@ patternProperties:
     description:
       CPSW MDIO bus.
 
+  "^cpts$":
+    type: object
+    allOf:
+      - $ref: "ti,k3-am654-cpts.yaml#"
+    description:
+      CPSW Common Platform Time Sync (CPTS) module.
+
 required:
   - compatible
   - reg
diff --git a/Documentation/devicetree/bindings/net/ti,k3-am654-cpts.yaml b/Documentation/devicetree/bindings/net/ti,k3-am654-cpts.yaml
new file mode 100644 (file)
index 0000000..df83c32
--- /dev/null
@@ -0,0 +1,152 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/ti,k3-am654-cpts.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: The TI AM654x/J721E Common Platform Time Sync (CPTS) module Device Tree Bindings
+
+maintainers:
+  - Grygorii Strashko <grygorii.strashko@ti.com>
+  - Sekhar Nori <nsekhar@ti.com>
+
+description: |+
+  The TI AM654x/J721E CPTS module is used to facilitate host control of time
+  sync operations.
+  Main features of CPTS module are
+  - selection of multiple external clock sources
+  - Software control of time sync events via interrupt or polling
+  - 64-bit timestamp mode in ns with PPM and nudge adjustment.
+  - hardware timestamp push inputs (HWx_TS_PUSH)
+  - timestamp counter compare output (TS_COMP)
+  - timestamp counter bit output (TS_SYNC)
+  - periodic Generator function outputs (TS_GENFx)
+  - Ethernet Enhanced Scheduled Traffic Operations (CPTS_ESTFn) (TSN)
+  - external hardware timestamp push inputs (HWx_TS_PUSH) timestamping
+
+   Depending on integration it enables compliance with the IEEE 1588-2008
+   standard for a precision clock synchronization protocol, Ethernet Enhanced
+   Scheduled Traffic Operations (CPTS_ESTFn) and PCIe Subsystem Precision Time
+   Measurement (PTM).
+
+  TI AM654x/J721E SoCs has several similar CPTS modules integrated into the
+  different parts of the system which could be synchronized with each other
+  - Main CPTS
+  - MCU CPSW CPTS with IEEE 1588-2008 support
+  - PCIe subsystem CPTS for PTM support
+
+  Depending on CPTS module integration and when CPTS is integral part of
+  another module (MCU CPSW for example) "compatible" and "reg" can
+  be omitted - parent module is fully responsible for CPTS enabling and
+  configuration.
+
+properties:
+  $nodename:
+    pattern: "^cpts(@.*|-[0-9a-f])*$"
+
+  compatible:
+    oneOf:
+      - const: ti,am65-cpts
+      - const: ti,j721e-cpts
+
+  reg:
+    maxItems: 1
+    description:
+       The physical base address and size of CPTS IO range
+
+  reg-names:
+    items:
+      - const: cpts
+
+  clocks:
+    description: CPTS reference clock
+
+  clock-names:
+    items:
+      - const: cpts
+
+  interrupts-extended:
+    items:
+      - description: CPTS events interrupt
+
+  interrupt-names:
+    items:
+      - const: "cpts"
+
+  ti,cpts-ext-ts-inputs:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+    maximum: 8
+    description:
+        Number of hardware timestamp push inputs (HWx_TS_PUSH)
+
+  ti,cpts-periodic-outputs:
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+    maximum: 8
+    description:
+         Number of timestamp Generator function outputs (TS_GENFx)
+
+  refclk-mux:
+    type: object
+    description: CPTS reference clock multiplexer clock
+    properties:
+      '#clock-cells':
+        const: 0
+
+      clocks:
+        maxItems: 8
+
+      assigned-clocks:
+        maxItems: 1
+
+      assigned-clocks-parents:
+        maxItems: 1
+
+    required:
+      - clocks
+
+required:
+  - clocks
+  - clock-names
+  - interrupts-extended
+  - interrupt-names
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    cpts@310d0000 {
+         compatible = "ti,am65-cpts";
+         reg = <0x0 0x310d0000 0x0 0x400>;
+         reg-names = "cpts";
+         clocks = <&main_cpts_mux>;
+         clock-names = "cpts";
+         interrupts-extended = <&k3_irq 163 0 IRQ_TYPE_LEVEL_HIGH>;
+         interrupt-names = "cpts";
+         ti,cpts-periodic-outputs = <6>;
+         ti,cpts-ext-ts-inputs = <8>;
+
+         main_cpts_mux: refclk-mux {
+               #clock-cells = <0>;
+               clocks = <&k3_clks 118 5>, <&k3_clks 118 11>,
+                        <&k3_clks 157 91>, <&k3_clks 157 77>,
+                        <&k3_clks 157 102>, <&k3_clks 157 80>,
+                        <&k3_clks 120 3>, <&k3_clks 121 3>;
+               assigned-clocks = <&main_cpts_mux>;
+               assigned-clock-parents = <&k3_clks 118 11>;
+         };
+    };
+  - |
+
+    cpts {
+             clocks = <&k3_clks 18 2>;
+             clock-names = "cpts";
+             interrupts-extended = <&gic500 GIC_SPI 858 IRQ_TYPE_LEVEL_HIGH>;
+             interrupt-names = "cpts";
+             ti,cpts-ext-ts-inputs = <4>;
+             ti,cpts-periodic-outputs = <2>;
+    };
index 144ae29..f8bd28f 100644 (file)
@@ -97,7 +97,7 @@ then:
         - $ref: /schemas/types.yaml#/definitions/uint32
         - minimum: 0
           maximum: 63
-          default: 0
+          default: 32
 
     qcom,charge-ctrl-value:
      description:
@@ -130,7 +130,7 @@ then:
         - $ref: /schemas/types.yaml#/definitions/uint32
         - minimum: 0
           maximum: 3
-          default: 2
+          default: 0
 
     qcom,preemphasis-width:
       description:
@@ -152,7 +152,7 @@ then:
         - $ref: /schemas/types.yaml#/definitions/uint32
         - minimum: 0
           maximum: 3
-          default: 0
+          default: 1
 
 required:
   - compatible
index fd1982c..3f913d6 100644 (file)
@@ -146,7 +146,7 @@ patternProperties:
       bindings specified in
       Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt
       Torrent SERDES should follow the bindings specified in
-      Documentation/devicetree/bindings/phy/phy-cadence-dp.txt
+      Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml
 
 required:
   - compatible
index 24c217b..41ece1d 100644 (file)
@@ -31,10 +31,17 @@ additionalProperties: false
 
 examples:
   - |
-    cros-ec@0 {
-        compatible = "google,cros-ec-spi";
-        cros_ec_pwm: ec-pwm {
-            compatible = "google,cros-ec-pwm";
-            #pwm-cells = <1>;
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        cros-ec@0 {
+            compatible = "google,cros-ec-spi";
+            reg = <0>;
+
+            cros_ec_pwm: ec-pwm {
+                compatible = "google,cros-ec-pwm";
+                #pwm-cells = <1>;
+            };
         };
     };
index f0acce2..3b019fa 100644 (file)
@@ -37,7 +37,6 @@ properties:
         type: object
 
     additionalProperties: false
-  additionalProperties: false
 
 required:
   - compatible
index a682af0..ae6e7ab 100644 (file)
@@ -75,7 +75,8 @@ properties:
             description: |
               disables over voltage protection of this buck
 
-      additionalProperties: false
+        unevaluatedProperties: false
+
     additionalProperties: false
 
 required:
index 71ce032..1e52daf 100644 (file)
@@ -35,6 +35,8 @@ patternProperties:
         description:
           should be "ldo1", ..., "ldo7"
 
+    unevaluatedProperties: false
+
   "^BUCK[1-7]$":
     type: object
     allOf:
@@ -103,5 +105,7 @@ patternProperties:
 
     required:
       - regulator-name
-  additionalProperties: false
+
+    unevaluatedProperties: false
+
 additionalProperties: false
index a323b16..543d4b5 100644 (file)
@@ -41,6 +41,8 @@ patternProperties:
         description:
           should be "ldo1", ..., "ldo7"
 
+    unevaluatedProperties: false
+
   "^BUCK[1-8]$":
     type: object
     allOf:
@@ -99,5 +101,7 @@ patternProperties:
 
     required:
       - regulator-name
-  additionalProperties: false
+
+    unevaluatedProperties: false
+
 additionalProperties: false
index 526fd00..d797cc2 100644 (file)
@@ -40,6 +40,8 @@ patternProperties:
         description:
           should be "ldo1", ..., "ldo6"
 
+    unevaluatedProperties: false
+
   "^BUCK[1-6]$":
     type: object
     allOf:
@@ -93,5 +95,7 @@ patternProperties:
 
     required:
       - regulator-name
-  additionalProperties: false
+
+    unevaluatedProperties: false
+
 additionalProperties: false
index 89ab67f..c147900 100644 (file)
@@ -39,7 +39,7 @@ additionalProperties: false
 
 examples:
   - |
-    rng {
+    rng@7e104000 {
         compatible = "brcm,bcm2835-rng";
         reg = <0x7e104000 0x10>;
         interrupts = <2 29>;
index 7cd0e27..a3ba218 100644 (file)
@@ -56,6 +56,9 @@ properties:
       - const: tx
       - const: rx
 
+  power-domains:
+    maxItems: 1
+
   rockchip,capture-channels:
     allOf:
       - $ref: /schemas/types.yaml#/definitions/uint32
diff --git a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt
deleted file mode 100644 (file)
index ec20c12..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-* Rockchip SPDIF transceiver
-
-The S/PDIF audio block is a stereo transceiver that allows the
-processor to receive and transmit digital audio via an coaxial cable or
-a fibre cable.
-
-Required properties:
-
-- compatible: should be one of the following:
-   - "rockchip,rk3066-spdif"
-   - "rockchip,rk3188-spdif"
-   - "rockchip,rk3228-spdif"
-   - "rockchip,rk3288-spdif"
-   - "rockchip,rk3328-spdif"
-   - "rockchip,rk3366-spdif"
-   - "rockchip,rk3368-spdif"
-   - "rockchip,rk3399-spdif"
-- reg: physical base address of the controller and length of memory mapped
-  region.
-- interrupts: should contain the SPDIF interrupt.
-- dmas: DMA specifiers for tx dma. See the DMA client binding,
-  Documentation/devicetree/bindings/dma/dma.txt
-- dma-names: should be "tx"
-- clocks: a list of phandle + clock-specifier pairs, one for each entry
-  in clock-names.
-- clock-names: should contain following:
-   - "hclk": clock for SPDIF controller
-   - "mclk" : clock for SPDIF bus
-
-Required properties on RK3288:
-  - rockchip,grf: the phandle of the syscon node for the general register
-                   file (GRF)
-
-Example for the rk3188 SPDIF controller:
-
-spdif: spdif@1011e000 {
-       compatible = "rockchip,rk3188-spdif", "rockchip,rk3066-spdif";
-       reg = <0x1011e000 0x2000>;
-       interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
-       dmas = <&dmac1_s 8>;
-       dma-names = "tx";
-       clock-names = "hclk", "mclk";
-       clocks = <&cru HCLK_SPDIF>, <&cru SCLK_SPDIF>;
-       #sound-dai-cells = <0>;
-};
diff --git a/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml b/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml
new file mode 100644 (file)
index 0000000..c467152
--- /dev/null
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/rockchip-spdif.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip SPDIF transceiver
+
+description:
+  The S/PDIF audio block is a stereo transceiver that allows the
+  processor to receive and transmit digital audio via a coaxial or
+  fibre cable.
+
+maintainers:
+  - Heiko Stuebner <heiko@sntech.de>
+
+properties:
+  compatible:
+    oneOf:
+      - const: rockchip,rk3066-spdif
+      - const: rockchip,rk3228-spdif
+      - const: rockchip,rk3328-spdif
+      - const: rockchip,rk3366-spdif
+      - const: rockchip,rk3368-spdif
+      - const: rockchip,rk3399-spdif
+      - items:
+          - enum:
+            - rockchip,rk3188-spdif
+            - rockchip,rk3288-spdif
+          - const: rockchip,rk3066-spdif
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: clock for SPDIF bus
+      - description: clock for SPDIF controller
+
+  clock-names:
+    items:
+      - const: mclk
+      - const: hclk
+
+  dmas:
+    maxItems: 1
+
+  dma-names:
+    const: tx
+
+  power-domains:
+    maxItems: 1
+
+  rockchip,grf:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      The phandle of the syscon node for the GRF register.
+      Required property on RK3288.
+
+  "#sound-dai-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - dmas
+  - dma-names
+  - "#sound-dai-cells"
+
+if:
+  properties:
+    compatible:
+      contains:
+        const: rockchip,rk3288-spdif
+
+then:
+  required:
+    - rockchip,grf
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/rk3188-cru.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    spdif: spdif@1011e000 {
+      compatible = "rockchip,rk3188-spdif", "rockchip,rk3066-spdif";
+      reg = <0x1011e000 0x2000>;
+      interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+      clocks = <&cru SCLK_SPDIF>, <&cru HCLK_SPDIF>;
+      clock-names = "mclk", "hclk";
+      dmas = <&dmac1_s 8>;
+      dma-names = "tx";
+      #sound-dai-cells = <0>;
+    };
index 0cf470e..5c16cf5 100644 (file)
@@ -61,7 +61,7 @@ examples:
     #include <dt-bindings/clock/qcom,gcc-sdm845.h>
     #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-    soc: soc@0 {
+    soc: soc {
         #address-cells = <2>;
         #size-cells = <2>;
 
index 1d68778..c2d2ee4 100644 (file)
@@ -56,7 +56,7 @@ additionalProperties: false
 examples:
   - |
     #include <dt-bindings/clock/jz4740-cgu.h>
-    usb_phy: usb-phy@0 {
+    usb_phy: usb-phy {
       compatible = "usb-nop-xceiv";
       #phy-cells = <0>;
     };
index cb695aa..fbdd017 100644 (file)
@@ -52,8 +52,8 @@ A child node must exist to represent the core DWC3 IP block. The name of
 the node is not important. The content of the node is defined in dwc3.txt.
 
 Phy documentation is provided in the following places:
-Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt   - USB3 QMP PHY
-Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt - USB2 QUSB2 PHY
+Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt    - USB3 QMP PHY
+Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml - USB2 QUSB2 PHY
 
 Example device nodes:
 
index 92d8631..031452a 100644 (file)
@@ -18,6 +18,7 @@ properties:
           - renesas,r8a774c0-usb3-peri # RZ/G2E
           - renesas,r8a7795-usb3-peri  # R-Car H3
           - renesas,r8a7796-usb3-peri  # R-Car M3-W
+          - renesas,r8a77961-usb3-peri # R-Car M3-W+
           - renesas,r8a77965-usb3-peri # R-Car M3-N
           - renesas,r8a77990-usb3-peri # R-Car E3
       - const: renesas,rcar-gen3-usb3-peri
index 469affa..a7ae955 100644 (file)
@@ -40,6 +40,7 @@ properties:
               - renesas,usbhs-r8a774c0 # RZ/G2E
               - renesas,usbhs-r8a7795  # R-Car H3
               - renesas,usbhs-r8a7796  # R-Car M3-W
+              - renesas,usbhs-r8a77961 # R-Car M3-W+
               - renesas,usbhs-r8a77965 # R-Car M3-N
               - renesas,usbhs-r8a77990 # R-Car E3
               - renesas,usbhs-r8a77995 # R-Car D3
index c8c4b00..9452049 100644 (file)
@@ -16,7 +16,7 @@ A child node must exist to represent the core DWC3 IP block. The name of
 the node is not important. The content of the node is defined in dwc3.txt.
 
 Phy documentation is provided in the following places:
-Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt - USB2.0 PHY
+Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.yaml - USB2.0 PHY
 Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt     - Type-C PHY
 
 Example device nodes:
index 3f37895..dc025f1 100644 (file)
@@ -16,7 +16,8 @@ Required properties:
     - "renesas,xhci-r8a7791" for r8a7791 SoC
     - "renesas,xhci-r8a7793" for r8a7793 SoC
     - "renesas,xhci-r8a7795" for r8a7795 SoC
-    - "renesas,xhci-r8a7796" for r8a7796 SoC
+    - "renesas,xhci-r8a7796" for r8a77960 SoC
+    - "renesas,xhci-r8a77961" for r8a77961 SoC
     - "renesas,xhci-r8a77965" for r8a77965 SoC
     - "renesas,xhci-r8a77990" for r8a77990 SoC
     - "renesas,rcar-gen2-xhci" for a generic R-Car Gen2 or RZ/G1 compatible
index c4ec39a..cada946 100644 (file)
@@ -70,7 +70,7 @@ list of volume location server IP addresses::
 The first module is the AF_RXRPC network protocol driver.  This provides the
 RxRPC remote operation protocol and may also be accessed from userspace.  See:
 
-       Documentation/networking/rxrpc.txt
+       Documentation/networking/rxrpc.rst
 
 The second module is the kerberos RxRPC security driver, and the third module
 is the actual filesystem driver for the AFS filesystem.
index db9ea08..6c032db 100644 (file)
@@ -79,8 +79,8 @@ created with any of::
                           struct dentry *parent, u8 *value);
     void debugfs_create_u16(const char *name, umode_t mode,
                            struct dentry *parent, u16 *value);
-    struct dentry *debugfs_create_u32(const char *name, umode_t mode,
-                                     struct dentry *parent, u32 *value);
+    void debugfs_create_u32(const char *name, umode_t mode,
+                           struct dentry *parent, u32 *value);
     void debugfs_create_u64(const char *name, umode_t mode,
                            struct dentry *parent, u64 *value);
 
diff --git a/Documentation/hwmon/bcm54140.rst b/Documentation/hwmon/bcm54140.rst
new file mode 100644 (file)
index 0000000..bc6ea4b
--- /dev/null
@@ -0,0 +1,45 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Broadcom BCM54140 Quad SGMII/QSGMII PHY
+=======================================
+
+Supported chips:
+
+   * Broadcom BCM54140
+
+     Datasheet: not public
+
+Author: Michael Walle <michael@walle.cc>
+
+Description
+-----------
+
+The Broadcom BCM54140 is a Quad SGMII/QSGMII PHY which supports monitoring
+its die temperature as well as two analog voltages.
+
+The AVDDL is a 1.0V analogue voltage, the AVDDH is a 3.3V analogue voltage.
+Both voltages and the temperature are measured in a round-robin fashion.
+
+Sysfs entries
+-------------
+
+The following attributes are supported.
+
+======================= ========================================================
+in0_label              "AVDDL"
+in0_input              Measured AVDDL voltage.
+in0_min                        Minimum AVDDL voltage.
+in0_max                        Maximum AVDDL voltage.
+in0_alarm              AVDDL voltage alarm.
+
+in1_label              "AVDDH"
+in1_input              Measured AVDDH voltage.
+in1_min                        Minimum AVDDH voltage.
+in1_max                        Maximum AVDDH voltage.
+in1_alarm              AVDDH voltage alarm.
+
+temp1_input            Die temperature.
+temp1_min              Minimum die temperature.
+temp1_max              Maximum die temperature.
+temp1_alarm            Die temperature alarm.
+======================= ========================================================
index 8ef62fd..1f0affb 100644 (file)
@@ -42,6 +42,7 @@ Hardware Monitoring Kernel Drivers
    asb100
    asc7621
    aspeed-pwm-tacho
+   bcm54140
    bel-pfe
    coretemp
    da9052
index cc4b614..0e71b22 100644 (file)
@@ -16,7 +16,7 @@ Supported chips:
 
   * Renesas ISL68220
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl68220'
 
     Addresses scanned: -
 
@@ -26,7 +26,7 @@ Supported chips:
 
   * Renesas ISL68221
 
-    Prefix: 'raa_dmpvr2_3rail'
+    Prefix: 'isl68221'
 
     Addresses scanned: -
 
@@ -36,7 +36,7 @@ Supported chips:
 
   * Renesas ISL68222
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl68222'
 
     Addresses scanned: -
 
@@ -46,7 +46,7 @@ Supported chips:
 
   * Renesas ISL68223
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl68223'
 
     Addresses scanned: -
 
@@ -56,7 +56,7 @@ Supported chips:
 
   * Renesas ISL68224
 
-    Prefix: 'raa_dmpvr2_3rail'
+    Prefix: 'isl68224'
 
     Addresses scanned: -
 
@@ -66,7 +66,7 @@ Supported chips:
 
   * Renesas ISL68225
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl68225'
 
     Addresses scanned: -
 
@@ -76,7 +76,7 @@ Supported chips:
 
   * Renesas ISL68226
 
-    Prefix: 'raa_dmpvr2_3rail'
+    Prefix: 'isl68226'
 
     Addresses scanned: -
 
@@ -86,7 +86,7 @@ Supported chips:
 
   * Renesas ISL68227
 
-    Prefix: 'raa_dmpvr2_1rail'
+    Prefix: 'isl68227'
 
     Addresses scanned: -
 
@@ -96,7 +96,7 @@ Supported chips:
 
   * Renesas ISL68229
 
-    Prefix: 'raa_dmpvr2_3rail'
+    Prefix: 'isl68229'
 
     Addresses scanned: -
 
@@ -106,7 +106,7 @@ Supported chips:
 
   * Renesas ISL68233
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl68233'
 
     Addresses scanned: -
 
@@ -116,7 +116,7 @@ Supported chips:
 
   * Renesas ISL68239
 
-    Prefix: 'raa_dmpvr2_3rail'
+    Prefix: 'isl68239'
 
     Addresses scanned: -
 
@@ -126,7 +126,7 @@ Supported chips:
 
   * Renesas ISL69222
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69222'
 
     Addresses scanned: -
 
@@ -136,7 +136,7 @@ Supported chips:
 
   * Renesas ISL69223
 
-    Prefix: 'raa_dmpvr2_3rail'
+    Prefix: 'isl69223'
 
     Addresses scanned: -
 
@@ -146,7 +146,7 @@ Supported chips:
 
   * Renesas ISL69224
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69224'
 
     Addresses scanned: -
 
@@ -156,7 +156,7 @@ Supported chips:
 
   * Renesas ISL69225
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69225'
 
     Addresses scanned: -
 
@@ -166,7 +166,7 @@ Supported chips:
 
   * Renesas ISL69227
 
-    Prefix: 'raa_dmpvr2_3rail'
+    Prefix: 'isl69227'
 
     Addresses scanned: -
 
@@ -176,7 +176,7 @@ Supported chips:
 
   * Renesas ISL69228
 
-    Prefix: 'raa_dmpvr2_3rail'
+    Prefix: 'isl69228'
 
     Addresses scanned: -
 
@@ -186,7 +186,7 @@ Supported chips:
 
   * Renesas ISL69234
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69234'
 
     Addresses scanned: -
 
@@ -196,7 +196,7 @@ Supported chips:
 
   * Renesas ISL69236
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69236'
 
     Addresses scanned: -
 
@@ -206,7 +206,7 @@ Supported chips:
 
   * Renesas ISL69239
 
-    Prefix: 'raa_dmpvr2_3rail'
+    Prefix: 'isl69239'
 
     Addresses scanned: -
 
@@ -216,7 +216,7 @@ Supported chips:
 
   * Renesas ISL69242
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69242'
 
     Addresses scanned: -
 
@@ -226,7 +226,7 @@ Supported chips:
 
   * Renesas ISL69243
 
-    Prefix: 'raa_dmpvr2_1rail'
+    Prefix: 'isl69243'
 
     Addresses scanned: -
 
@@ -236,7 +236,7 @@ Supported chips:
 
   * Renesas ISL69247
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69247'
 
     Addresses scanned: -
 
@@ -246,7 +246,7 @@ Supported chips:
 
   * Renesas ISL69248
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69248'
 
     Addresses scanned: -
 
@@ -256,7 +256,7 @@ Supported chips:
 
   * Renesas ISL69254
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69254'
 
     Addresses scanned: -
 
@@ -266,7 +266,7 @@ Supported chips:
 
   * Renesas ISL69255
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69255'
 
     Addresses scanned: -
 
@@ -276,7 +276,7 @@ Supported chips:
 
   * Renesas ISL69256
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69256'
 
     Addresses scanned: -
 
@@ -286,7 +286,7 @@ Supported chips:
 
   * Renesas ISL69259
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69259'
 
     Addresses scanned: -
 
@@ -296,7 +296,7 @@ Supported chips:
 
   * Renesas ISL69260
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69260'
 
     Addresses scanned: -
 
@@ -306,7 +306,7 @@ Supported chips:
 
   * Renesas ISL69268
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69268'
 
     Addresses scanned: -
 
@@ -316,7 +316,7 @@ Supported chips:
 
   * Renesas ISL69269
 
-    Prefix: 'raa_dmpvr2_3rail'
+    Prefix: 'isl69269'
 
     Addresses scanned: -
 
@@ -326,7 +326,7 @@ Supported chips:
 
   * Renesas ISL69298
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'isl69298'
 
     Addresses scanned: -
 
@@ -336,7 +336,7 @@ Supported chips:
 
   * Renesas RAA228000
 
-    Prefix: 'raa_dmpvr2_hv'
+    Prefix: 'raa228000'
 
     Addresses scanned: -
 
@@ -346,7 +346,7 @@ Supported chips:
 
   * Renesas RAA228004
 
-    Prefix: 'raa_dmpvr2_hv'
+    Prefix: 'raa228004'
 
     Addresses scanned: -
 
@@ -356,7 +356,7 @@ Supported chips:
 
   * Renesas RAA228006
 
-    Prefix: 'raa_dmpvr2_hv'
+    Prefix: 'raa228006'
 
     Addresses scanned: -
 
@@ -366,7 +366,7 @@ Supported chips:
 
   * Renesas RAA228228
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'raa228228'
 
     Addresses scanned: -
 
@@ -376,7 +376,7 @@ Supported chips:
 
   * Renesas RAA229001
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'raa229001'
 
     Addresses scanned: -
 
@@ -386,7 +386,7 @@ Supported chips:
 
   * Renesas RAA229004
 
-    Prefix: 'raa_dmpvr2_2rail'
+    Prefix: 'raa229004'
 
     Addresses scanned: -
 
index 04d5c01..b80257a 100644 (file)
@@ -1241,7 +1241,8 @@ When kbuild executes, the following steps are followed (roughly):
        will be displayed with "make KBUILD_VERBOSE=0".
 
 
---- 6.9 Preprocessing linker scripts
+6.9 Preprocessing linker scripts
+--------------------------------
 
        When the vmlinux image is built, the linker script
        arch/$(ARCH)/kernel/vmlinux.lds is used.
diff --git a/Documentation/networking/6pack.rst b/Documentation/networking/6pack.rst
new file mode 100644 (file)
index 0000000..bc5bf1f
--- /dev/null
@@ -0,0 +1,191 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============
+6pack Protocol
+==============
+
+This is the 6pack-mini-HOWTO, written by
+
+Andreas Könsgen DG3KQ
+
+:Internet: ajk@comnets.uni-bremen.de
+:AMPR-net: dg3kq@db0pra.ampr.org
+:AX.25:    dg3kq@db0ach.#nrw.deu.eu
+
+Last update: April 7, 1998
+
+1. What is 6pack, and what are the advantages to KISS?
+======================================================
+
+6pack is a transmission protocol for data exchange between the PC and
+the TNC over a serial line. It can be used as an alternative to KISS.
+
+6pack has two major advantages:
+
+- The PC is given full control over the radio
+  channel. Special control data is exchanged between the PC and the TNC so
+  that the PC knows at any time if the TNC is receiving data, if a TNC
+  buffer underrun or overrun has occurred, if the PTT is
+  set and so on. This control data is processed at a higher priority than
+  normal data, so a data stream can be interrupted at any time to issue an
+  important event. This helps to improve the channel access and timing
+  algorithms as everything is computed in the PC. It would even be possible
+  to experiment with something completely different from the known CSMA and
+  DAMA channel access methods.
+  This kind of real-time control is especially important to supply several
+  TNCs that are connected between each other and the PC by a daisy chain
+  (however, this feature is not supported yet by the Linux 6pack driver).
+
+- Each packet transferred over the serial line is supplied with a checksum,
+  so it is easy to detect errors due to problems on the serial line.
+  Received packets that are corrupt are not passed on to the AX.25 layer.
+  Damaged packets that the TNC has received from the PC are not transmitted.
+
+More details about 6pack are described in the file 6pack.ps that is located
+in the doc directory of the AX.25 utilities package.
+
+2. Who has developed the 6pack protocol?
+========================================
+
+The 6pack protocol has been developed by Ekki Plicht DF4OR, Henning Rech
+DF9IC and Gunter Jost DK7WJ. A driver for 6pack, written by Gunter Jost and
+Matthias Welwarsky DG2FEF, comes along with the PC version of FlexNet.
+They have also written a firmware for TNCs to perform the 6pack
+protocol (see section 4 below).
+
+3. Where can I get the latest version of 6pack for LinuX?
+=========================================================
+
+At the moment, the 6pack stuff can obtained via anonymous ftp from
+db0bm.automation.fh-aachen.de. In the directory /incoming/dg3kq,
+there is a file named 6pack.tgz.
+
+4. Preparing the TNC for 6pack operation
+========================================
+
+To be able to use 6pack, a special firmware for the TNC is needed. The EPROM
+of a newly bought TNC does not contain 6pack, so you will have to
+program an EPROM yourself. The image file for 6pack EPROMs should be
+available on any packet radio box where PC/FlexNet can be found. The name of
+the file is 6pack.bin. This file is copyrighted and maintained by the FlexNet
+team. It can be used under the terms of the license that comes along
+with PC/FlexNet. Please do not ask me about the internals of this file as I
+don't know anything about it. I used a textual description of the 6pack
+protocol to program the Linux driver.
+
+TNCs contain a 64kByte EPROM, the lower half of which is used for
+the firmware/KISS. The upper half is either empty or is sometimes
+programmed with software called TAPR. In the latter case, the TNC
+is supplied with a DIP switch so you can easily change between the
+two systems. When programming a new EPROM, one of the systems is replaced
+by 6pack. It is useful to replace TAPR, as this software is rarely used
+nowadays. If your TNC is not equipped with the switch mentioned above, you
+can build in one yourself that switches over the highest address pin
+of the EPROM between HIGH and LOW level. After having inserted the new EPROM
+and switched to 6pack, apply power to the TNC for a first test. The connect
+and the status LED are lit for about a second if the firmware initialises
+the TNC correctly.
+
+5. Building and installing the 6pack driver
+===========================================
+
+The driver has been tested with kernel version 2.1.90. Use with older
+kernels may lead to a compilation error because the interface to a kernel
+function has been changed in the 2.1.8x kernels.
+
+How to turn on 6pack support:
+=============================
+
+- In the linux kernel configuration program, select the code maturity level
+  options menu and turn on the prompting for development drivers.
+
+- Select the amateur radio support menu and turn on the serial port 6pack
+  driver.
+
+- Compile and install the kernel and the modules.
+
+To use the driver, the kissattach program delivered with the AX.25 utilities
+has to be modified.
+
+- Do a cd to the directory that holds the kissattach sources. Edit the
+  kissattach.c file. At the top, insert the following lines::
+
+    #ifndef N_6PACK
+    #define N_6PACK (N_AX25+1)
+    #endif
+
+  Then find the line:
+
+    int disc = N_AX25;
+
+  and replace N_AX25 by N_6PACK.
+
+- Recompile kissattach. Rename it to spattach to avoid confusions.
+
+Installing the driver:
+----------------------
+
+- Do an insmod 6pack. Look at your /var/log/messages file to check if the
+  module has printed its initialization message.
+
+- Do a spattach as you would launch kissattach when starting a KISS port.
+  Check if the kernel prints the message '6pack: TNC found'.
+
+- From here, everything should work as if you were setting up a KISS port.
+  The only difference is that the network device that represents
+  the 6pack port is called sp instead of sl or ax. So, sp0 would be the
+  first 6pack port.
+
+Although the driver has been tested on various platforms, I still declare it
+ALPHA. BE CAREFUL! Sync your disks before insmoding the 6pack module
+and spattaching. Watch out if your computer behaves strangely. Read section
+6 of this file about known problems.
+
+Note that the connect and status LEDs of the TNC are controlled in a
+different way than they are when the TNC is used with PC/FlexNet. When using
+FlexNet, the connect LED is on if there is a connection; the status LED is
+on if there is data in the buffer of the PC's AX.25 engine that has to be
+transmitted. Under Linux, the 6pack layer is beyond the AX.25 layer,
+so the 6pack driver doesn't know anything about connects or data that
+has not yet been transmitted. Therefore the LEDs are controlled
+as they are in KISS mode: The connect LED is turned on if data is transferred
+from the PC to the TNC over the serial line, the status LED if data is
+sent to the PC.
+
+6. Known problems
+=================
+
+When testing the driver with 2.0.3x kernels and
+operating with data rates on the radio channel of 9600 Baud or higher,
+the driver may, on certain systems, sometimes print the message '6pack:
+bad checksum', which is due to data loss if the other station sends two
+or more subsequent packets. I have been told that this is due to a problem
+with the serial driver of 2.0.3x kernels. I don't know yet if the problem
+still exists with 2.1.x kernels, as I have heard that the serial driver
+code has been changed with 2.1.x.
+
+When shutting down the sp interface with ifconfig, the kernel crashes if
+there is still an AX.25 connection left over which an IP connection was
+running, even if that IP connection is already closed. The problem does not
+occur when there is a bare AX.25 connection still running. I don't know if
+this is a problem of the 6pack driver or something else in the kernel.
+
+The driver has been tested as a module, not yet as a kernel-builtin driver.
+
+The 6pack protocol supports daisy-chaining of TNCs in a token ring, which is
+connected to one serial port of the PC. This feature is not implemented
+and at least at the moment I won't be able to do it because I do not have
+the opportunity to build a TNC daisy-chain and test it.
+
+Some of the comments in the source code are inaccurate. They are left from
+the SLIP/KISS driver, from which the 6pack driver has been derived.
+I haven't modified or removed them yet -- sorry! The code itself needs
+some cleaning and optimizing. This will be done in a later release.
+
+If you encounter a bug or if you have a question or suggestion concerning the
+driver, feel free to mail me, using the addresses given at the beginning of
+this file.
+
+Have fun!
+
+Andreas
diff --git a/Documentation/networking/6pack.txt b/Documentation/networking/6pack.txt
deleted file mode 100644 (file)
index 8f33942..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-This is the 6pack-mini-HOWTO, written by
-
-Andreas Könsgen DG3KQ
-Internet: ajk@comnets.uni-bremen.de
-AMPR-net: dg3kq@db0pra.ampr.org
-AX.25:    dg3kq@db0ach.#nrw.deu.eu
-
-Last update: April 7, 1998
-
-1. What is 6pack, and what are the advantages to KISS?
-
-6pack is a transmission protocol for data exchange between the PC and
-the TNC over a serial line. It can be used as an alternative to KISS.
-
-6pack has two major advantages:
-- The PC is given full control over the radio
-  channel. Special control data is exchanged between the PC and the TNC so
-  that the PC knows at any time if the TNC is receiving data, if a TNC
-  buffer underrun or overrun has occurred, if the PTT is
-  set and so on. This control data is processed at a higher priority than
-  normal data, so a data stream can be interrupted at any time to issue an
-  important event. This helps to improve the channel access and timing 
-  algorithms as everything is computed in the PC. It would even be possible 
-  to experiment with something completely different from the known CSMA and 
-  DAMA channel access methods.
-  This kind of real-time control is especially important to supply several
-  TNCs that are connected between each other and the PC by a daisy chain
-  (however, this feature is not supported yet by the Linux 6pack driver).
-
-- Each packet transferred over the serial line is supplied with a checksum,
-  so it is easy to detect errors due to problems on the serial line.
-  Received packets that are corrupt are not passed on to the AX.25 layer.
-  Damaged packets that the TNC has received from the PC are not transmitted.
-
-More details about 6pack are described in the file 6pack.ps that is located
-in the doc directory of the AX.25 utilities package.
-
-2. Who has developed the 6pack protocol?
-
-The 6pack protocol has been developed by Ekki Plicht DF4OR, Henning Rech
-DF9IC and Gunter Jost DK7WJ. A driver for 6pack, written by Gunter Jost and
-Matthias Welwarsky DG2FEF, comes along with the PC version of FlexNet.
-They have also written a firmware for TNCs to perform the 6pack
-protocol (see section 4 below).
-
-3. Where can I get the latest version of 6pack for LinuX?
-
-At the moment, the 6pack stuff can obtained via anonymous ftp from
-db0bm.automation.fh-aachen.de. In the directory /incoming/dg3kq,
-there is a file named 6pack.tgz.
-
-4. Preparing the TNC for 6pack operation
-
-To be able to use 6pack, a special firmware for the TNC is needed. The EPROM
-of a newly bought TNC does not contain 6pack, so you will have to
-program an EPROM yourself. The image file for 6pack EPROMs should be
-available on any packet radio box where PC/FlexNet can be found. The name of
-the file is 6pack.bin. This file is copyrighted and maintained by the FlexNet
-team. It can be used under the terms of the license that comes along
-with PC/FlexNet. Please do not ask me about the internals of this file as I
-don't know anything about it. I used a textual description of the 6pack
-protocol to program the Linux driver.
-
-TNCs contain a 64kByte EPROM, the lower half of which is used for
-the firmware/KISS. The upper half is either empty or is sometimes
-programmed with software called TAPR. In the latter case, the TNC
-is supplied with a DIP switch so you can easily change between the
-two systems. When programming a new EPROM, one of the systems is replaced
-by 6pack. It is useful to replace TAPR, as this software is rarely used
-nowadays. If your TNC is not equipped with the switch mentioned above, you
-can build in one yourself that switches over the highest address pin
-of the EPROM between HIGH and LOW level. After having inserted the new EPROM
-and switched to 6pack, apply power to the TNC for a first test. The connect
-and the status LED are lit for about a second if the firmware initialises
-the TNC correctly.
-
-5. Building and installing the 6pack driver
-
-The driver has been tested with kernel version 2.1.90. Use with older
-kernels may lead to a compilation error because the interface to a kernel
-function has been changed in the 2.1.8x kernels.
-
-How to turn on 6pack support:
-
-- In the linux kernel configuration program, select the code maturity level
-  options menu and turn on the prompting for development drivers.
-
-- Select the amateur radio support menu and turn on the serial port 6pack
-  driver.
-
-- Compile and install the kernel and the modules.
-
-To use the driver, the kissattach program delivered with the AX.25 utilities
-has to be modified.
-
-- Do a cd to the directory that holds the kissattach sources. Edit the
-  kissattach.c file. At the top, insert the following lines:
-
-  #ifndef N_6PACK
-  #define N_6PACK (N_AX25+1)
-  #endif
-
-  Then find the line
-   
-  int disc = N_AX25;
-
-  and replace N_AX25 by N_6PACK.
-
-- Recompile kissattach. Rename it to spattach to avoid confusions.
-
-Installing the driver:
-
-- Do an insmod 6pack. Look at your /var/log/messages file to check if the 
-  module has printed its initialization message.
-
-- Do a spattach as you would launch kissattach when starting a KISS port.
-  Check if the kernel prints the message '6pack: TNC found'. 
-
-- From here, everything should work as if you were setting up a KISS port.
-  The only difference is that the network device that represents
-  the 6pack port is called sp instead of sl or ax. So, sp0 would be the
-  first 6pack port.
-
-Although the driver has been tested on various platforms, I still declare it
-ALPHA. BE CAREFUL! Sync your disks before insmoding the 6pack module
-and spattaching. Watch out if your computer behaves strangely. Read section
-6 of this file about known problems.
-
-Note that the connect and status LEDs of the TNC are controlled in a
-different way than they are when the TNC is used with PC/FlexNet. When using
-FlexNet, the connect LED is on if there is a connection; the status LED is
-on if there is data in the buffer of the PC's AX.25 engine that has to be
-transmitted. Under Linux, the 6pack layer is beyond the AX.25 layer,
-so the 6pack driver doesn't know anything about connects or data that
-has not yet been transmitted. Therefore the LEDs are controlled
-as they are in KISS mode: The connect LED is turned on if data is transferred
-from the PC to the TNC over the serial line, the status LED if data is
-sent to the PC.
-
-6. Known problems
-
-When testing the driver with 2.0.3x kernels and
-operating with data rates on the radio channel of 9600 Baud or higher,
-the driver may, on certain systems, sometimes print the message '6pack:
-bad checksum', which is due to data loss if the other station sends two
-or more subsequent packets. I have been told that this is due to a problem
-with the serial driver of 2.0.3x kernels. I don't know yet if the problem
-still exists with 2.1.x kernels, as I have heard that the serial driver
-code has been changed with 2.1.x.
-
-When shutting down the sp interface with ifconfig, the kernel crashes if
-there is still an AX.25 connection left over which an IP connection was
-running, even if that IP connection is already closed. The problem does not
-occur when there is a bare AX.25 connection still running. I don't know if
-this is a problem of the 6pack driver or something else in the kernel.
-
-The driver has been tested as a module, not yet as a kernel-builtin driver.
-
-The 6pack protocol supports daisy-chaining of TNCs in a token ring, which is
-connected to one serial port of the PC. This feature is not implemented
-and at least at the moment I won't be able to do it because I do not have
-the opportunity to build a TNC daisy-chain and test it.
-
-Some of the comments in the source code are inaccurate. They are left from
-the SLIP/KISS driver, from which the 6pack driver has been derived.
-I haven't modified or removed them yet -- sorry! The code itself needs
-some cleaning and optimizing. This will be done in a later release.
-
-If you encounter a bug or if you have a question or suggestion concerning the
-driver, feel free to mail me, using the addresses given at the beginning of
-this file.
-
-Have fun!
-
-Andreas
diff --git a/Documentation/networking/PLIP.txt b/Documentation/networking/PLIP.txt
deleted file mode 100644 (file)
index ad7e3f7..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-PLIP: The Parallel Line Internet Protocol Device
-
-Donald Becker (becker@super.org)
-I.D.A. Supercomputing Research Center, Bowie MD 20715
-
-At some point T. Thorn will probably contribute text,
-Tommy Thorn (tthorn@daimi.aau.dk)
-
-PLIP Introduction
------------------
-
-This document describes the parallel port packet pusher for Net/LGX.
-This device interface allows a point-to-point connection between two
-parallel ports to appear as a IP network interface.
-
-What is PLIP?
-=============
-
-PLIP is Parallel Line IP, that is, the transportation of IP packages
-over a parallel port. In the case of a PC, the obvious choice is the
-printer port.  PLIP is a non-standard, but [can use] uses the standard
-LapLink null-printer cable [can also work in turbo mode, with a PLIP
-cable]. [The protocol used to pack IP packages, is a simple one
-initiated by Crynwr.]
-
-Advantages of PLIP
-==================
-
-It's cheap, it's available everywhere, and it's easy.
-
-The PLIP cable is all that's needed to connect two Linux boxes, and it
-can be built for very few bucks.
-
-Connecting two Linux boxes takes only a second's decision and a few
-minutes' work, no need to search for a [supported] netcard. This might
-even be especially important in the case of notebooks, where netcards
-are not easily available.
-
-Not requiring a netcard also means that apart from connecting the
-cables, everything else is software configuration [which in principle
-could be made very easy.]
-
-Disadvantages of PLIP
-=====================
-
-Doesn't work over a modem, like SLIP and PPP. Limited range, 15 m.
-Can only be used to connect three (?) Linux boxes. Doesn't connect to
-an existing Ethernet. Isn't standard (not even de facto standard, like
-SLIP).
-
-Performance
-===========
-
-PLIP easily outperforms Ethernet cards....(ups, I was dreaming, but
-it *is* getting late. EOB)
-
-PLIP driver details
--------------------
-
-The Linux PLIP driver is an implementation of the original Crynwr protocol,
-that uses the parallel port subsystem of the kernel in order to properly
-share parallel ports between PLIP and other services.
-
-IRQs and trigger timeouts
-=========================
-
-When a parallel port used for a PLIP driver has an IRQ configured to it, the
-PLIP driver is signaled whenever data is sent to it via the cable, such that
-when no data is available, the driver isn't being used.
-
-However, on some machines it is hard, if not impossible, to configure an IRQ
-to a certain parallel port, mainly because it is used by some other device.
-On these machines, the PLIP driver can be used in IRQ-less mode, where
-the PLIP driver would constantly poll the parallel port for data waiting,
-and if such data is available, process it. This mode is less efficient than
-the IRQ mode, because the driver has to check the parallel port many times
-per second, even when no data at all is sent. Some rough measurements
-indicate that there isn't a noticeable performance drop when using IRQ-less
-mode as compared to IRQ mode as far as the data transfer speed is involved.
-There is a performance drop on the machine hosting the driver.
-
-When the PLIP driver is used in IRQ mode, the timeout used for triggering a
-data transfer (the maximal time the PLIP driver would allow the other side
-before announcing a timeout, when trying to handshake a transfer of some
-data) is, by default, 500usec. As IRQ delivery is more or less immediate,
-this timeout is quite sufficient. 
-
-When in IRQ-less mode, the PLIP driver polls the parallel port HZ times
-per second (where HZ is typically 100 on most platforms, and 1024 on an
-Alpha, as of this writing). Between two such polls, there are 10^6/HZ usecs.
-On an i386, for example, 10^6/100 = 10000usec. It is easy to see that it is
-quite possible for the trigger timeout to expire between two such polls, as
-the timeout is only 500usec long. As a result, it is required to change the
-trigger timeout on the *other* side of a PLIP connection, to about
-10^6/HZ usecs. If both sides of a PLIP connection are used in IRQ-less mode,
-this timeout is required on both sides.
-
-It appears that in practice, the trigger timeout can be shorter than in the
-above calculation. It isn't an important issue, unless the wire is faulty,
-in which case a long timeout would stall the machine when, for whatever
-reason, bits are dropped.
-
-A utility that can perform this change in Linux is plipconfig, which is part
-of the net-tools package (its location can be found in the
-Documentation/Changes file). An example command would be
-'plipconfig plipX trigger 10000', where plipX is the appropriate
-PLIP device.
-
-PLIP hardware interconnection
------------------------------
-
-PLIP uses several different data transfer methods.  The first (and the
-only one implemented in the early version of the code) uses a standard
-printer "null" cable to transfer data four bits at a time using
-data bit outputs connected to status bit inputs.
-
-The second data transfer method relies on both machines having
-bi-directional parallel ports, rather than output-only ``printer''
-ports.  This allows byte-wide transfers and avoids reconstructing
-nibbles into bytes, leading to much faster transfers.
-
-Parallel Transfer Mode 0 Cable
-==============================
-
-The cable for the first transfer mode is a standard
-printer "null" cable which transfers data four bits at a time using
-data bit outputs of the first port (machine T) connected to the
-status bit inputs of the second port (machine R).  There are five
-status inputs, and they are used as four data inputs and a clock (data
-strobe) input, arranged so that the data input bits appear as contiguous
-bits with standard status register implementation.
-
-A cable that implements this protocol is available commercially as a
-"Null Printer" or "Turbo Laplink" cable.  It can be constructed with
-two DB-25 male connectors symmetrically connected as follows:
-
-    STROBE output      1*
-    D0->ERROR  2 - 15          15 - 2
-    D1->SLCT   3 - 13          13 - 3
-    D2->PAPOUT 4 - 12          12 - 4
-    D3->ACK    5 - 10          10 - 5
-    D4->BUSY   6 - 11          11 - 6
-    D5,D6,D7 are   7*, 8*, 9*
-    AUTOFD output 14*
-    INIT   output 16*
-    SLCTIN     17 - 17
-    extra grounds are 18*,19*,20*,21*,22*,23*,24*
-    GROUND     25 - 25
-* Do not connect these pins on either end
-
-If the cable you are using has a metallic shield it should be
-connected to the metallic DB-25 shell at one end only.
-
-Parallel Transfer Mode 1
-========================
-
-The second data transfer method relies on both machines having
-bi-directional parallel ports, rather than output-only ``printer''
-ports.  This allows byte-wide transfers, and avoids reconstructing
-nibbles into bytes.  This cable should not be used on unidirectional
-``printer'' (as opposed to ``parallel'') ports or when the machine
-isn't configured for PLIP, as it will result in output driver
-conflicts and the (unlikely) possibility of damage.
-
-The cable for this transfer mode should be constructed as follows:
-
-    STROBE->BUSY 1 - 11
-    D0->D0     2 - 2
-    D1->D1     3 - 3
-    D2->D2     4 - 4
-    D3->D3     5 - 5
-    D4->D4     6 - 6
-    D5->D5     7 - 7
-    D6->D6     8 - 8
-    D7->D7     9 - 9
-    INIT -> ACK  16 - 10
-    AUTOFD->PAPOUT 14 - 12
-    SLCT->SLCTIN 13 - 17
-    GND->ERROR 18 - 15
-    extra grounds are 19*,20*,21*,22*,23*,24*
-    GROUND     25 - 25
-* Do not connect these pins on either end
-
-Once again, if the cable you are using has a metallic shield it should
-be connected to the metallic DB-25 shell at one end only.
-
-PLIP Mode 0 transfer protocol
-=============================
-
-The PLIP driver is compatible with the "Crynwr" parallel port transfer
-standard in Mode 0.  That standard specifies the following protocol:
-
-   send header nibble '0x8'
-   count-low octet
-   count-high octet
-   ... data octets
-   checksum octet
-
-Each octet is sent as
-       <wait for rx. '0x1?'>   <send 0x10+(octet&0x0F)>
-       <wait for rx. '0x0?'>   <send 0x00+((octet>>4)&0x0F)>
-
-To start a transfer the transmitting machine outputs a nibble 0x08.
-That raises the ACK line, triggering an interrupt in the receiving
-machine.  The receiving machine disables interrupts and raises its own ACK
-line. 
-
-Restated:
-
-(OUT is bit 0-4, OUT.j is bit j from OUT. IN likewise)
-Send_Byte:
-   OUT := low nibble, OUT.4 := 1
-   WAIT FOR IN.4 = 1
-   OUT := high nibble, OUT.4 := 0
-   WAIT FOR IN.4 = 0
diff --git a/Documentation/networking/altera_tse.rst b/Documentation/networking/altera_tse.rst
new file mode 100644 (file)
index 0000000..7a70400
--- /dev/null
@@ -0,0 +1,286 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: <isonum.txt>
+
+=======================================
+Altera Triple-Speed Ethernet MAC driver
+=======================================
+
+Copyright |copy| 2008-2014 Altera Corporation
+
+This is the driver for the Altera Triple-Speed Ethernet (TSE) controllers
+using the SGDMA and MSGDMA soft DMA IP components. The driver uses the
+platform bus to obtain component resources. The designs used to test this
+driver were built for a Cyclone(R) V SOC FPGA board, a Cyclone(R) V FPGA board,
+and tested with ARM and NIOS processor hosts separately. The anticipated use
+cases are simple communications between an embedded system and an external peer
+for status and simple configuration of the embedded system.
+
+For more information visit www.altera.com and www.rocketboards.org. Support
+forums for the driver may be found on www.rocketboards.org, and a design used
+to test this driver may be found there as well. Support is also available from
+the maintainer of this driver, found in MAINTAINERS.
+
+The Triple-Speed Ethernet, SGDMA, and MSGDMA components are all soft IP
+components that can be assembled and built into an FPGA using the Altera
+Quartus toolchain. Quartus 13.1 and 14.0 were used to build the design that
+this driver was tested against. The sopc2dts tool is used to create the
+device tree for the driver, and may be found at rocketboards.org.
+
+The driver probe function examines the device tree and determines if the
+Triple-Speed Ethernet instance is using an SGDMA or MSGDMA component. The
+probe function then installs the appropriate set of DMA routines to
+initialize, setup transmits, receives, and interrupt handling primitives for
+the respective configurations.
+
+The SGDMA component is to be deprecated in the near future (over the next 1-2
+years as of this writing in early 2014) in favor of the MSGDMA component.
+SGDMA support is included for existing designs and reference in case a
+developer wishes to support their own soft DMA logic and driver support. Any
+new designs should not use the SGDMA.
+
+The SGDMA supports only a single transmit or receive operation at a time, and
+therefore will not perform as well compared to the MSGDMA soft IP. Please
+visit www.altera.com for known, documented SGDMA errata.
+
+Scatter-gather DMA is not supported by the SGDMA or MSGDMA at this time.
+Scatter-gather DMA will be added to a future maintenance update to this
+driver.
+
+Jumbo frames are not supported at this time.
+
+The driver limits PHY operations to 10/100Mbps, and has not yet been fully
+tested for 1Gbps. This support will be added in a future maintenance update.
+
+1. Kernel Configuration
+=======================
+
+The kernel configuration option is ALTERA_TSE:
+
+ Device Drivers ---> Network device support ---> Ethernet driver support --->
+ Altera Triple-Speed Ethernet MAC support (ALTERA_TSE)
+
+2. Driver parameters list
+=========================
+
+       - debug: message level (0: no output, 16: all);
+       - dma_rx_num: Number of descriptors in the RX list (default is 64);
+       - dma_tx_num: Number of descriptors in the TX list (default is 64).
+
+3. Command line options
+=======================
+
+Driver parameters can be also passed in command line by using::
+
+       altera_tse=dma_rx_num:128,dma_tx_num:512
+
+4. Driver information and notes
+===============================
+
+4.1. Transmit process
+---------------------
+When the driver's transmit routine is called by the kernel, it sets up a
+transmit descriptor by calling the underlying DMA transmit routine (SGDMA or
+MSGDMA), and initiates a transmit operation. Once the transmit is complete, an
+interrupt is driven by the transmit DMA logic. The driver handles the transmit
+completion in the context of the interrupt handling chain by recycling
+resource required to send and track the requested transmit operation.
+
+4.2. Receive process
+--------------------
+The driver will post receive buffers to the receive DMA logic during driver
+initialization. Receive buffers may or may not be queued depending upon the
+underlying DMA logic (MSGDMA is able queue receive buffers, SGDMA is not able
+to queue receive buffers to the SGDMA receive logic). When a packet is
+received, the DMA logic generates an interrupt. The driver handles a receive
+interrupt by obtaining the DMA receive logic status, reaping receive
+completions until no more receive completions are available.
+
+4.3. Interrupt Mitigation
+-------------------------
+The driver is able to mitigate the number of its DMA interrupts
+using NAPI for receive operations. Interrupt mitigation is not yet supported
+for transmit operations, but will be added in a future maintenance release.
+
+4.4) Ethtool support
+--------------------
+Ethtool is supported. Driver statistics and internal errors can be taken using:
+ethtool -S ethX command. It is possible to dump registers etc.
+
+4.5) PHY Support
+----------------
+The driver is compatible with PAL to work with PHY and GPHY devices.
+
+4.7) List of source files:
+--------------------------
+ - Kconfig
+ - Makefile
+ - altera_tse_main.c: main network device driver
+ - altera_tse_ethtool.c: ethtool support
+ - altera_tse.h: private driver structure and common definitions
+ - altera_msgdma.h: MSGDMA implementation function definitions
+ - altera_sgdma.h: SGDMA implementation function definitions
+ - altera_msgdma.c: MSGDMA implementation
+ - altera_sgdma.c: SGDMA implementation
+ - altera_sgdmahw.h: SGDMA register and descriptor definitions
+ - altera_msgdmahw.h: MSGDMA register and descriptor definitions
+ - altera_utils.c: Driver utility functions
+ - altera_utils.h: Driver utility function definitions
+
+5. Debug Information
+====================
+
+The driver exports debug information such as internal statistics,
+debug information, MAC and DMA registers etc.
+
+A user may use the ethtool support to get statistics:
+e.g. using: ethtool -S ethX (that shows the statistics counters)
+or sees the MAC registers: e.g. using: ethtool -d ethX
+
+The developer can also use the "debug" module parameter to get
+further debug information.
+
+6. Statistics Support
+=====================
+
+The controller and driver support a mix of IEEE standard defined statistics,
+RFC defined statistics, and driver or Altera defined statistics. The four
+specifications containing the standard definitions for these statistics are
+as follows:
+
+ - IEEE 802.3-2012 - IEEE Standard for Ethernet.
+ - RFC 2863 found at http://www.rfc-editor.org/rfc/rfc2863.txt.
+ - RFC 2819 found at http://www.rfc-editor.org/rfc/rfc2819.txt.
+ - Altera Triple Speed Ethernet User Guide, found at http://www.altera.com
+
+The statistics supported by the TSE and the device driver are as follows:
+
+"tx_packets" is equivalent to aFramesTransmittedOK defined in IEEE 802.3-2012,
+Section 5.2.2.1.2. This statistics is the count of frames that are successfully
+transmitted.
+
+"rx_packets" is equivalent to aFramesReceivedOK defined in IEEE 802.3-2012,
+Section 5.2.2.1.5. This statistic is the count of frames that are successfully
+received. This count does not include any error packets such as CRC errors,
+length errors, or alignment errors.
+
+"rx_crc_errors" is equivalent to aFrameCheckSequenceErrors defined in IEEE
+802.3-2012, Section 5.2.2.1.6. This statistic is the count of frames that are
+an integral number of bytes in length and do not pass the CRC test as the frame
+is received.
+
+"rx_align_errors" is equivalent to aAlignmentErrors defined in IEEE 802.3-2012,
+Section 5.2.2.1.7. This statistic is the count of frames that are not an
+integral number of bytes in length and do not pass the CRC test as the frame is
+received.
+
+"tx_bytes" is equivalent to aOctetsTransmittedOK defined in IEEE 802.3-2012,
+Section 5.2.2.1.8. This statistic is the count of data and pad bytes
+successfully transmitted from the interface.
+
+"rx_bytes" is equivalent to aOctetsReceivedOK defined in IEEE 802.3-2012,
+Section 5.2.2.1.14. This statistic is the count of data and pad bytes
+successfully received by the controller.
+
+"tx_pause" is equivalent to aPAUSEMACCtrlFramesTransmitted defined in IEEE
+802.3-2012, Section 30.3.4.2. This statistic is a count of PAUSE frames
+transmitted from the network controller.
+
+"rx_pause" is equivalent to aPAUSEMACCtrlFramesReceived defined in IEEE
+802.3-2012, Section 30.3.4.3. This statistic is a count of PAUSE frames
+received by the network controller.
+
+"rx_errors" is equivalent to ifInErrors defined in RFC 2863. This statistic is
+a count of the number of packets received containing errors that prevented the
+packet from being delivered to a higher level protocol.
+
+"tx_errors" is equivalent to ifOutErrors defined in RFC 2863. This statistic
+is a count of the number of packets that could not be transmitted due to errors.
+
+"rx_unicast" is equivalent to ifInUcastPkts defined in RFC 2863. This
+statistic is a count of the number of packets received that were not addressed
+to the broadcast address or a multicast group.
+
+"rx_multicast" is equivalent to ifInMulticastPkts defined in RFC 2863. This
+statistic is a count of the number of packets received that were addressed to
+a multicast address group.
+
+"rx_broadcast" is equivalent to ifInBroadcastPkts defined in RFC 2863. This
+statistic is a count of the number of packets received that were addressed to
+the broadcast address.
+
+"tx_discards" is equivalent to ifOutDiscards defined in RFC 2863. This
+statistic is the number of outbound packets not transmitted even though an
+error was not detected. An example of a reason this might occur is to free up
+internal buffer space.
+
+"tx_unicast" is equivalent to ifOutUcastPkts defined in RFC 2863. This
+statistic counts the number of packets transmitted that were not addressed to
+a multicast group or broadcast address.
+
+"tx_multicast" is equivalent to ifOutMulticastPkts defined in RFC 2863. This
+statistic counts the number of packets transmitted that were addressed to a
+multicast group.
+
+"tx_broadcast" is equivalent to ifOutBroadcastPkts defined in RFC 2863. This
+statistic counts the number of packets transmitted that were addressed to a
+broadcast address.
+
+"ether_drops" is equivalent to etherStatsDropEvents defined in RFC 2819.
+This statistic counts the number of packets dropped due to lack of internal
+controller resources.
+
+"rx_total_bytes" is equivalent to etherStatsOctets defined in RFC 2819.
+This statistic counts the total number of bytes received by the controller,
+including error and discarded packets.
+
+"rx_total_packets" is equivalent to etherStatsPkts defined in RFC 2819.
+This statistic counts the total number of packets received by the controller,
+including error, discarded, unicast, multicast, and broadcast packets.
+
+"rx_undersize" is equivalent to etherStatsUndersizePkts defined in RFC 2819.
+This statistic counts the number of correctly formed packets received less
+than 64 bytes long.
+
+"rx_oversize" is equivalent to etherStatsOversizePkts defined in RFC 2819.
+This statistic counts the number of correctly formed packets greater than 1518
+bytes long.
+
+"rx_64_bytes" is equivalent to etherStatsPkts64Octets defined in RFC 2819.
+This statistic counts the total number of packets received that were 64 octets
+in length.
+
+"rx_65_127_bytes" is equivalent to etherStatsPkts65to127Octets defined in RFC
+2819. This statistic counts the total number of packets received that were
+between 65 and 127 octets in length inclusive.
+
+"rx_128_255_bytes" is equivalent to etherStatsPkts128to255Octets defined in
+RFC 2819. This statistic is the total number of packets received that were
+between 128 and 255 octets in length inclusive.
+
+"rx_256_511_bytes" is equivalent to etherStatsPkts256to511Octets defined in
+RFC 2819. This statistic is the total number of packets received that were
+between 256 and 511 octets in length inclusive.
+
+"rx_512_1023_bytes" is equivalent to etherStatsPkts512to1023Octets defined in
+RFC 2819. This statistic is the total number of packets received that were
+between 512 and 1023 octets in length inclusive.
+
+"rx_1024_1518_bytes" is equivalent to etherStatsPkts1024to1518Octets define
+in RFC 2819. This statistic is the total number of packets received that were
+between 1024 and 1518 octets in length inclusive.
+
+"rx_gte_1519_bytes" is a statistic defined specific to the behavior of the
+Altera TSE. This statistics counts the number of received good and errored
+frames between the length of 1519 and the maximum frame length configured
+in the frm_length register. See the Altera TSE User Guide for More details.
+
+"rx_jabbers" is equivalent to etherStatsJabbers defined in RFC 2819. This
+statistic is the total number of packets received that were longer than 1518
+octets, and had either a bad CRC with an integral number of octets (CRC Error)
+or a bad CRC with a non-integral number of octets (Alignment Error).
+
+"rx_runts" is equivalent to etherStatsFragments defined in RFC 2819. This
+statistic is the total number of packets received that were less than 64 octets
+in length and had either a bad CRC with an integral number of octets (CRC
+error) or a bad CRC with a non-integral number of octets (Alignment Error).
diff --git a/Documentation/networking/altera_tse.txt b/Documentation/networking/altera_tse.txt
deleted file mode 100644 (file)
index 50b8589..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-       Altera Triple-Speed Ethernet MAC driver
-
-Copyright (C) 2008-2014 Altera Corporation
-
-This is the driver for the Altera Triple-Speed Ethernet (TSE) controllers
-using the SGDMA and MSGDMA soft DMA IP components. The driver uses the
-platform bus to obtain component resources. The designs used to test this
-driver were built for a Cyclone(R) V SOC FPGA board, a Cyclone(R) V FPGA board,
-and tested with ARM and NIOS processor hosts separately. The anticipated use
-cases are simple communications between an embedded system and an external peer
-for status and simple configuration of the embedded system.
-
-For more information visit www.altera.com and www.rocketboards.org. Support
-forums for the driver may be found on www.rocketboards.org, and a design used
-to test this driver may be found there as well. Support is also available from
-the maintainer of this driver, found in MAINTAINERS.
-
-The Triple-Speed Ethernet, SGDMA, and MSGDMA components are all soft IP
-components that can be assembled and built into an FPGA using the Altera
-Quartus toolchain. Quartus 13.1 and 14.0 were used to build the design that
-this driver was tested against. The sopc2dts tool is used to create the
-device tree for the driver, and may be found at rocketboards.org.
-
-The driver probe function examines the device tree and determines if the
-Triple-Speed Ethernet instance is using an SGDMA or MSGDMA component. The
-probe function then installs the appropriate set of DMA routines to
-initialize, setup transmits, receives, and interrupt handling primitives for
-the respective configurations.
-
-The SGDMA component is to be deprecated in the near future (over the next 1-2
-years as of this writing in early 2014) in favor of the MSGDMA component.
-SGDMA support is included for existing designs and reference in case a
-developer wishes to support their own soft DMA logic and driver support. Any
-new designs should not use the SGDMA.
-
-The SGDMA supports only a single transmit or receive operation at a time, and
-therefore will not perform as well compared to the MSGDMA soft IP. Please
-visit www.altera.com for known, documented SGDMA errata.
-
-Scatter-gather DMA is not supported by the SGDMA or MSGDMA at this time.
-Scatter-gather DMA will be added to a future maintenance update to this
-driver.
-
-Jumbo frames are not supported at this time.
-
-The driver limits PHY operations to 10/100Mbps, and has not yet been fully
-tested for 1Gbps. This support will be added in a future maintenance update.
-
-1) Kernel Configuration
-The kernel configuration option is ALTERA_TSE:
- Device Drivers ---> Network device support ---> Ethernet driver support --->
- Altera Triple-Speed Ethernet MAC support (ALTERA_TSE)
-
-2) Driver parameters list:
-       debug: message level (0: no output, 16: all);
-       dma_rx_num: Number of descriptors in the RX list (default is 64);
-       dma_tx_num: Number of descriptors in the TX list (default is 64).
-
-3) Command line options
-Driver parameters can be also passed in command line by using:
-       altera_tse=dma_rx_num:128,dma_tx_num:512
-
-4) Driver information and notes
-
-4.1) Transmit process
-When the driver's transmit routine is called by the kernel, it sets up a
-transmit descriptor by calling the underlying DMA transmit routine (SGDMA or
-MSGDMA), and initiates a transmit operation. Once the transmit is complete, an
-interrupt is driven by the transmit DMA logic. The driver handles the transmit
-completion in the context of the interrupt handling chain by recycling
-resource required to send and track the requested transmit operation.
-
-4.2) Receive process
-The driver will post receive buffers to the receive DMA logic during driver
-initialization. Receive buffers may or may not be queued depending upon the
-underlying DMA logic (MSGDMA is able queue receive buffers, SGDMA is not able
-to queue receive buffers to the SGDMA receive logic). When a packet is
-received, the DMA logic generates an interrupt. The driver handles a receive
-interrupt by obtaining the DMA receive logic status, reaping receive
-completions until no more receive completions are available.
-
-4.3) Interrupt Mitigation
-The driver is able to mitigate the number of its DMA interrupts
-using NAPI for receive operations. Interrupt mitigation is not yet supported
-for transmit operations, but will be added in a future maintenance release.
-
-4.4) Ethtool support
-Ethtool is supported. Driver statistics and internal errors can be taken using:
-ethtool -S ethX command. It is possible to dump registers etc.
-
-4.5) PHY Support
-The driver is compatible with PAL to work with PHY and GPHY devices.
-
-4.7) List of source files:
- o Kconfig
- o Makefile
- o altera_tse_main.c: main network device driver
- o altera_tse_ethtool.c: ethtool support
- o altera_tse.h: private driver structure and common definitions
- o altera_msgdma.h: MSGDMA implementation function definitions
- o altera_sgdma.h: SGDMA implementation function definitions
- o altera_msgdma.c: MSGDMA implementation
- o altera_sgdma.c: SGDMA implementation
- o altera_sgdmahw.h: SGDMA register and descriptor definitions
- o altera_msgdmahw.h: MSGDMA register and descriptor definitions
- o altera_utils.c: Driver utility functions
- o altera_utils.h: Driver utility function definitions
-
-5) Debug Information
-
-The driver exports debug information such as internal statistics,
-debug information, MAC and DMA registers etc.
-
-A user may use the ethtool support to get statistics:
-e.g. using: ethtool -S ethX (that shows the statistics counters)
-or sees the MAC registers: e.g. using: ethtool -d ethX
-
-The developer can also use the "debug" module parameter to get
-further debug information.
-
-6) Statistics Support
-
-The controller and driver support a mix of IEEE standard defined statistics,
-RFC defined statistics, and driver or Altera defined statistics. The four
-specifications containing the standard definitions for these statistics are
-as follows:
-
- o IEEE 802.3-2012 - IEEE Standard for Ethernet.
- o RFC 2863 found at http://www.rfc-editor.org/rfc/rfc2863.txt.
- o RFC 2819 found at http://www.rfc-editor.org/rfc/rfc2819.txt.
- o Altera Triple Speed Ethernet User Guide, found at http://www.altera.com
-
-The statistics supported by the TSE and the device driver are as follows:
-
-"tx_packets" is equivalent to aFramesTransmittedOK defined in IEEE 802.3-2012,
-Section 5.2.2.1.2. This statistics is the count of frames that are successfully
-transmitted.
-
-"rx_packets" is equivalent to aFramesReceivedOK defined in IEEE 802.3-2012,
-Section 5.2.2.1.5. This statistic is the count of frames that are successfully
-received. This count does not include any error packets such as CRC errors,
-length errors, or alignment errors.
-
-"rx_crc_errors" is equivalent to aFrameCheckSequenceErrors defined in IEEE
-802.3-2012, Section 5.2.2.1.6. This statistic is the count of frames that are
-an integral number of bytes in length and do not pass the CRC test as the frame
-is received.
-
-"rx_align_errors" is equivalent to aAlignmentErrors defined in IEEE 802.3-2012,
-Section 5.2.2.1.7. This statistic is the count of frames that are not an
-integral number of bytes in length and do not pass the CRC test as the frame is
-received.
-
-"tx_bytes" is equivalent to aOctetsTransmittedOK defined in IEEE 802.3-2012,
-Section 5.2.2.1.8. This statistic is the count of data and pad bytes
-successfully transmitted from the interface.
-
-"rx_bytes" is equivalent to aOctetsReceivedOK defined in IEEE 802.3-2012,
-Section 5.2.2.1.14. This statistic is the count of data and pad bytes
-successfully received by the controller.
-
-"tx_pause" is equivalent to aPAUSEMACCtrlFramesTransmitted defined in IEEE
-802.3-2012, Section 30.3.4.2. This statistic is a count of PAUSE frames
-transmitted from the network controller.
-
-"rx_pause" is equivalent to aPAUSEMACCtrlFramesReceived defined in IEEE
-802.3-2012, Section 30.3.4.3. This statistic is a count of PAUSE frames
-received by the network controller.
-
-"rx_errors" is equivalent to ifInErrors defined in RFC 2863. This statistic is
-a count of the number of packets received containing errors that prevented the
-packet from being delivered to a higher level protocol.
-
-"tx_errors" is equivalent to ifOutErrors defined in RFC 2863. This statistic
-is a count of the number of packets that could not be transmitted due to errors.
-
-"rx_unicast" is equivalent to ifInUcastPkts defined in RFC 2863. This
-statistic is a count of the number of packets received that were not addressed
-to the broadcast address or a multicast group.
-
-"rx_multicast" is equivalent to ifInMulticastPkts defined in RFC 2863. This
-statistic is a count of the number of packets received that were addressed to
-a multicast address group.
-
-"rx_broadcast" is equivalent to ifInBroadcastPkts defined in RFC 2863. This
-statistic is a count of the number of packets received that were addressed to
-the broadcast address.
-
-"tx_discards" is equivalent to ifOutDiscards defined in RFC 2863. This
-statistic is the number of outbound packets not transmitted even though an
-error was not detected. An example of a reason this might occur is to free up
-internal buffer space.
-
-"tx_unicast" is equivalent to ifOutUcastPkts defined in RFC 2863. This
-statistic counts the number of packets transmitted that were not addressed to
-a multicast group or broadcast address.
-
-"tx_multicast" is equivalent to ifOutMulticastPkts defined in RFC 2863. This
-statistic counts the number of packets transmitted that were addressed to a
-multicast group.
-
-"tx_broadcast" is equivalent to ifOutBroadcastPkts defined in RFC 2863. This
-statistic counts the number of packets transmitted that were addressed to a
-broadcast address.
-
-"ether_drops" is equivalent to etherStatsDropEvents defined in RFC 2819.
-This statistic counts the number of packets dropped due to lack of internal
-controller resources.
-
-"rx_total_bytes" is equivalent to etherStatsOctets defined in RFC 2819.
-This statistic counts the total number of bytes received by the controller,
-including error and discarded packets.
-
-"rx_total_packets" is equivalent to etherStatsPkts defined in RFC 2819.
-This statistic counts the total number of packets received by the controller,
-including error, discarded, unicast, multicast, and broadcast packets.
-
-"rx_undersize" is equivalent to etherStatsUndersizePkts defined in RFC 2819.
-This statistic counts the number of correctly formed packets received less
-than 64 bytes long.
-
-"rx_oversize" is equivalent to etherStatsOversizePkts defined in RFC 2819.
-This statistic counts the number of correctly formed packets greater than 1518
-bytes long.
-
-"rx_64_bytes" is equivalent to etherStatsPkts64Octets defined in RFC 2819.
-This statistic counts the total number of packets received that were 64 octets
-in length.
-
-"rx_65_127_bytes" is equivalent to etherStatsPkts65to127Octets defined in RFC
-2819. This statistic counts the total number of packets received that were
-between 65 and 127 octets in length inclusive.
-
-"rx_128_255_bytes" is equivalent to etherStatsPkts128to255Octets defined in
-RFC 2819. This statistic is the total number of packets received that were
-between 128 and 255 octets in length inclusive.
-
-"rx_256_511_bytes" is equivalent to etherStatsPkts256to511Octets defined in
-RFC 2819. This statistic is the total number of packets received that were
-between 256 and 511 octets in length inclusive.
-
-"rx_512_1023_bytes" is equivalent to etherStatsPkts512to1023Octets defined in
-RFC 2819. This statistic is the total number of packets received that were
-between 512 and 1023 octets in length inclusive.
-
-"rx_1024_1518_bytes" is equivalent to etherStatsPkts1024to1518Octets define
-in RFC 2819. This statistic is the total number of packets received that were
-between 1024 and 1518 octets in length inclusive.
-
-"rx_gte_1519_bytes" is a statistic defined specific to the behavior of the
-Altera TSE. This statistics counts the number of received good and errored
-frames between the length of 1519 and the maximum frame length configured
-in the frm_length register. See the Altera TSE User Guide for More details.
-
-"rx_jabbers" is equivalent to etherStatsJabbers defined in RFC 2819. This
-statistic is the total number of packets received that were longer than 1518
-octets, and had either a bad CRC with an integral number of octets (CRC Error)
-or a bad CRC with a non-integral number of octets (Alignment Error).
-
-"rx_runts" is equivalent to etherStatsFragments defined in RFC 2819. This
-statistic is the total number of packets received that were less than 64 octets
-in length and had either a bad CRC with an integral number of octets (CRC
-error) or a bad CRC with a non-integral number of octets (Alignment Error).
diff --git a/Documentation/networking/arcnet-hardware.rst b/Documentation/networking/arcnet-hardware.rst
new file mode 100644 (file)
index 0000000..ac249ac
--- /dev/null
@@ -0,0 +1,3234 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============
+ARCnet Hardware
+===============
+
+.. note::
+
+   1) This file is a supplement to arcnet.txt.  Please read that for general
+      driver configuration help.
+   2) This file is no longer Linux-specific.  It should probably be moved out
+      of the kernel sources.  Ideas?
+
+Because so many people (myself included) seem to have obtained ARCnet cards
+without manuals, this file contains a quick introduction to ARCnet hardware,
+some cabling tips, and a listing of all jumper settings I can find. Please
+e-mail apenwarr@worldvisions.ca with any settings for your particular card,
+or any other information you have!
+
+
+Introduction to ARCnet
+======================
+
+ARCnet is a network type which works in a way similar to popular Ethernet
+networks but which is also different in some very important ways.
+
+First of all, you can get ARCnet cards in at least two speeds: 2.5 Mbps
+(slower than Ethernet) and 100 Mbps (faster than normal Ethernet).  In fact,
+there are others as well, but these are less common.  The different hardware
+types, as far as I'm aware, are not compatible and so you cannot wire a
+100 Mbps card to a 2.5 Mbps card, and so on.  From what I hear, my driver does
+work with 100 Mbps cards, but I haven't been able to verify this myself,
+since I only have the 2.5 Mbps variety.  It is probably not going to saturate
+your 100 Mbps card.  Stop complaining. :)
+
+You also cannot connect an ARCnet card to any kind of Ethernet card and
+expect it to work.
+
+There are two "types" of ARCnet - STAR topology and BUS topology.  This
+refers to how the cards are meant to be wired together.  According to most
+available documentation, you can only connect STAR cards to STAR cards and
+BUS cards to BUS cards.  That makes sense, right?  Well, it's not quite
+true; see below under "Cabling."
+
+Once you get past these little stumbling blocks, ARCnet is actually quite a
+well-designed standard.  It uses something called "modified token passing"
+which makes it completely incompatible with so-called "Token Ring" cards,
+but which makes transfers much more reliable than Ethernet does.  In fact,
+ARCnet will guarantee that a packet arrives safely at the destination, and
+even if it can't possibly be delivered properly (ie. because of a cable
+break, or because the destination computer does not exist) it will at least
+tell the sender about it.
+
+Because of the carefully defined action of the "token", it will always make
+a pass around the "ring" within a maximum length of time.  This makes it
+useful for realtime networks.
+
+In addition, all known ARCnet cards have an (almost) identical programming
+interface.  This means that with one ARCnet driver you can support any
+card, whereas with Ethernet each manufacturer uses what is sometimes a
+completely different programming interface, leading to a lot of different,
+sometimes very similar, Ethernet drivers.  Of course, always using the same
+programming interface also means that when high-performance hardware
+facilities like PCI bus mastering DMA appear, it's hard to take advantage of
+them.  Let's not go into that.
+
+One thing that makes ARCnet cards difficult to program for, however, is the
+limit on their packet sizes; standard ARCnet can only send packets that are
+up to 508 bytes in length.  This is smaller than the Internet "bare minimum"
+of 576 bytes, let alone the Ethernet MTU of 1500.  To compensate, an extra
+level of encapsulation is defined by RFC1201, which I call "packet
+splitting," that allows "virtual packets" to grow as large as 64K each,
+although they are generally kept down to the Ethernet-style 1500 bytes.
+
+For more information on the advantages and disadvantages (mostly the
+advantages) of ARCnet networks, you might try the "ARCnet Trade Association"
+WWW page:
+
+       http://www.arcnet.com
+
+
+Cabling ARCnet Networks
+=======================
+
+This section was rewritten by
+
+       Vojtech Pavlik     <vojtech@suse.cz>
+
+using information from several people, including:
+
+       - Avery Pennraun     <apenwarr@worldvisions.ca>
+       - Stephen A. Wood    <saw@hallc1.cebaf.gov>
+       - John Paul Morrison <jmorriso@bogomips.ee.ubc.ca>
+       - Joachim Koenig     <jojo@repas.de>
+
+and Avery touched it up a bit, at Vojtech's request.
+
+ARCnet (the classic 2.5 Mbps version) can be connected by two different
+types of cabling: coax and twisted pair.  The other ARCnet-type networks
+(100 Mbps TCNS and 320 kbps - 32 Mbps ARCnet Plus) use different types of
+cabling (Type1, Fiber, C1, C4, C5).
+
+For a coax network, you "should" use 93 Ohm RG-62 cable.  But other cables
+also work fine, because ARCnet is a very stable network. I personally use 75
+Ohm TV antenna cable.
+
+Cards for coax cabling are shipped in two different variants: for BUS and
+STAR network topologies.  They are mostly the same.  The only difference
+lies in the hybrid chip installed.  BUS cards use high impedance output,
+while STAR use low impedance.  Low impedance card (STAR) is electrically
+equal to a high impedance one with a terminator installed.
+
+Usually, the ARCnet networks are built up from STAR cards and hubs.  There
+are two types of hubs - active and passive.  Passive hubs are small boxes
+with four BNC connectors containing four 47 Ohm resistors::
+
+          |         | wires
+          R         + junction
+       -R-+-R-      R 47 Ohm resistors
+          R
+          |
+
+The shielding is connected together.  Active hubs are much more complicated;
+they are powered and contain electronics to amplify the signal and send it
+to other segments of the net.  They usually have eight connectors.  Active
+hubs come in two variants - dumb and smart.  The dumb variant just
+amplifies, but the smart one decodes to digital and encodes back all packets
+coming through.  This is much better if you have several hubs in the net,
+since many dumb active hubs may worsen the signal quality.
+
+And now to the cabling.  What you can connect together:
+
+1. A card to a card.  This is the simplest way of creating a 2-computer
+   network.
+
+2. A card to a passive hub.  Remember that all unused connectors on the hub
+   must be properly terminated with 93 Ohm (or something else if you don't
+   have the right ones) terminators.
+
+       (Avery's note: oops, I didn't know that.  Mine (TV cable) works
+       anyway, though.)
+
+3. A card to an active hub.  Here is no need to terminate the unused
+   connectors except some kind of aesthetic feeling.  But, there may not be
+   more than eleven active hubs between any two computers.  That of course
+   doesn't limit the number of active hubs on the network.
+
+4. An active hub to another.
+
+5. An active hub to passive hub.
+
+Remember that you cannot connect two passive hubs together.  The power loss
+implied by such a connection is too high for the net to operate reliably.
+
+An example of a typical ARCnet network::
+
+          R                     S - STAR type card
+    S------H--------A-------S    R - Terminator
+          |        |            H - Hub
+          |        |            A - Active hub
+          |   S----H----S
+          S        |
+                   |
+                   S
+
+The BUS topology is very similar to the one used by Ethernet.  The only
+difference is in cable and terminators: they should be 93 Ohm.  Ethernet
+uses 50 Ohm impedance. You use T connectors to put the computers on a single
+line of cable, the bus. You have to put terminators at both ends of the
+cable. A typical BUS ARCnet network looks like::
+
+    RT----T------T------T------T------TR
+     B    B      B      B      B      B
+
+  B - BUS type card
+  R - Terminator
+  T - T connector
+
+But that is not all! The two types can be connected together.  According to
+the official documentation the only way of connecting them is using an active
+hub::
+
+        A------T------T------TR
+        |      B      B      B
+     S---H---S
+        |
+        S
+
+The official docs also state that you can use STAR cards at the ends of
+BUS network in place of a BUS card and a terminator::
+
+     S------T------T------S
+           B      B
+
+But, according to my own experiments, you can simply hang a BUS type card
+anywhere in middle of a cable in a STAR topology network.  And more - you
+can use the bus card in place of any star card if you use a terminator. Then
+you can build very complicated networks fulfilling all your needs!  An
+example::
+
+                                 S
+                                 |
+          RT------T-------T------H------S
+           B      B       B      |
+                                 |       R
+    S------A------T-------T-------A-------H------TR
+          |      B       B       |       |      B
+          |   S                 BT       |
+          |   |                  |  S----A-----S
+    S------H---A----S             |       |
+          |   |      S------T----H---S   |
+          S   S             B    R       S
+
+A basically different cabling scheme is used with Twisted Pair cabling. Each
+of the TP cards has two RJ (phone-cord style) connectors.  The cards are
+then daisy-chained together using a cable connecting every two neighboring
+cards.  The ends are terminated with RJ 93 Ohm terminators which plug into
+the empty connectors of cards on the ends of the chain.  An example::
+
+         ___________   ___________
+      _R_|_         _|_|_         _|_R_
+     |     |       |     |       |     |
+     |Card |       |Card |       |Card |
+     |_____|       |_____|       |_____|
+
+
+There are also hubs for the TP topology.  There is nothing difficult
+involved in using them; you just connect a TP chain to a hub on any end or
+even at both.  This way you can create almost any network configuration.
+The maximum of 11 hubs between any two computers on the net applies here as
+well.  An example::
+
+    RP-------P--------P--------H-----P------P-----PR
+                              |
+      RP-----H--------P--------H-----P------PR
+            |                 |
+            PR                PR
+
+    R - RJ Terminator
+    P - TP Card
+    H - TP Hub
+
+Like any network, ARCnet has a limited cable length.  These are the maximum
+cable lengths between two active ends (an active end being an active hub or
+a STAR card).
+
+               ========== ======= ===========
+               RG-62       93 Ohm up to 650 m
+               RG-59/U     75 Ohm up to 457 m
+               RG-11/U     75 Ohm up to 533 m
+               IBM Type 1 150 Ohm up to 200 m
+               IBM Type 3 100 Ohm up to 100 m
+               ========== ======= ===========
+
+The maximum length of all cables connected to a passive hub is limited to 65
+meters for RG-62 cabling; less for others.  You can see that using passive
+hubs in a large network is a bad idea. The maximum length of a single "BUS
+Trunk" is about 300 meters for RG-62. The maximum distance between the two
+most distant points of the net is limited to 3000 meters. The maximum length
+of a TP cable between two cards/hubs is 650 meters.
+
+
+Setting the Jumpers
+===================
+
+All ARCnet cards should have a total of four or five different settings:
+
+  - the I/O address:  this is the "port" your ARCnet card is on.  Probed
+    values in the Linux ARCnet driver are only from 0x200 through 0x3F0. (If
+    your card has additional ones, which is possible, please tell me.) This
+    should not be the same as any other device on your system.  According to
+    a doc I got from Novell, MS Windows prefers values of 0x300 or more,
+    eating net connections on my system (at least) otherwise.  My guess is
+    this may be because, if your card is at 0x2E0, probing for a serial port
+    at 0x2E8 will reset the card and probably mess things up royally.
+
+       - Avery's favourite: 0x300.
+
+  - the IRQ: on  8-bit cards, it might be 2 (9), 3, 4, 5, or 7.
+            on 16-bit cards, it might be 2 (9), 3, 4, 5, 7, or 10-15.
+
+    Make sure this is different from any other card on your system.  Note
+    that IRQ2 is the same as IRQ9, as far as Linux is concerned.  You can
+    "cat /proc/interrupts" for a somewhat complete list of which ones are in
+    use at any given time.  Here is a list of common usages from Vojtech
+    Pavlik <vojtech@suse.cz>:
+
+       ("Not on bus" means there is no way for a card to generate this
+       interrupt)
+
+       ======   =========================================================
+       IRQ  0   Timer 0 (Not on bus)
+       IRQ  1   Keyboard (Not on bus)
+       IRQ  2   IRQ Controller 2 (Not on bus, nor does interrupt the CPU)
+       IRQ  3   COM2
+       IRQ  4   COM1
+       IRQ  5   FREE (LPT2 if you have it; sometimes COM3; maybe PLIP)
+       IRQ  6   Floppy disk controller
+       IRQ  7   FREE (LPT1 if you don't use the polling driver; PLIP)
+       IRQ  8   Realtime Clock Interrupt (Not on bus)
+       IRQ  9   FREE (VGA vertical sync interrupt if enabled)
+       IRQ 10   FREE
+       IRQ 11   FREE
+       IRQ 12   FREE
+       IRQ 13   Numeric Coprocessor (Not on bus)
+       IRQ 14   Fixed Disk Controller
+       IRQ 15   FREE (Fixed Disk Controller 2 if you have it)
+       ======   =========================================================
+
+
+       .. note::
+
+          IRQ 9 is used on some video cards for the "vertical retrace"
+          interrupt.  This interrupt would have been handy for things like
+          video games, as it occurs exactly once per screen refresh, but
+          unfortunately IBM cancelled this feature starting with the original
+          VGA and thus many VGA/SVGA cards do not support it.  For this
+          reason, no modern software uses this interrupt and it can almost
+          always be safely disabled, if your video card supports it at all.
+
+       If your card for some reason CANNOT disable this IRQ (usually there
+       is a jumper), one solution would be to clip the printed circuit
+       contact on the board: it's the fourth contact from the left on the
+       back side.  I take no responsibility if you try this.
+
+       - Avery's favourite: IRQ2 (actually IRQ9).  Watch that VGA, though.
+
+  - the memory address:  Unlike most cards, ARCnets use "shared memory" for
+    copying buffers around.  Make SURE it doesn't conflict with any other
+    used memory in your system!
+
+    ::
+
+       A0000           - VGA graphics memory (ok if you don't have VGA)
+       B0000           - Monochrome text mode
+       C0000           \  One of these is your VGA BIOS - usually C0000.
+       E0000           /
+       F0000           - System BIOS
+
+    Anything less than 0xA0000 is, well, a BAD idea since it isn't above
+    640k.
+
+       - Avery's favourite: 0xD0000
+
+  - the station address:  Every ARCnet card has its own "unique" network
+    address from 0 to 255.  Unlike Ethernet, you can set this address
+    yourself with a jumper or switch (or on some cards, with special
+    software).  Since it's only 8 bits, you can only have 254 ARCnet cards
+    on a network.  DON'T use 0 or 255, since these are reserved (although
+    neat stuff will probably happen if you DO use them).  By the way, if you
+    haven't already guessed, don't set this the same as any other ARCnet on
+    your network!
+
+       - Avery's favourite:  3 and 4.  Not that it matters.
+
+  - There may be ETS1 and ETS2 settings.  These may or may not make a
+    difference on your card (many manuals call them "reserved"), but are
+    used to change the delays used when powering up a computer on the
+    network.  This is only necessary when wiring VERY long range ARCnet
+    networks, on the order of 4km or so; in any case, the only real
+    requirement here is that all cards on the network with ETS1 and ETS2
+    jumpers have them in the same position.  Chris Hindy <chrish@io.org>
+    sent in a chart with actual values for this:
+
+       ======= ======= =============== ====================
+       ET1     ET2     Response Time   Reconfiguration Time
+       ======= ======= =============== ====================
+       open    open    74.7us          840us
+       open    closed  283.4us         1680us
+       closed  open    561.8us         1680us
+       closed  closed  1118.6us        1680us
+       ======= ======= =============== ====================
+
+    Make sure you set ETS1 and ETS2 to the SAME VALUE for all cards on your
+    network.
+
+Also, on many cards (not mine, though) there are red and green LED's.
+Vojtech Pavlik <vojtech@suse.cz> tells me this is what they mean:
+
+       =============== =============== =====================================
+       GREEN           RED             Status
+       =============== =============== =====================================
+       OFF             OFF             Power off
+       OFF             Short flashes   Cabling problems (broken cable or not
+                                       terminated)
+       OFF (short)     ON              Card init
+       ON              ON              Normal state - everything OK, nothing
+                                       happens
+       ON              Long flashes    Data transfer
+       ON              OFF             Never happens (maybe when wrong ID)
+       =============== =============== =====================================
+
+
+The following is all the specific information people have sent me about
+their own particular ARCnet cards.  It is officially a mess, and contains
+huge amounts of duplicated information.  I have no time to fix it.  If you
+want to, PLEASE DO!  Just send me a 'diff -u' of all your changes.
+
+The model # is listed right above specifics for that card, so you should be
+able to use your text viewer's "search" function to find the entry you want.
+If you don't KNOW what kind of card you have, try looking through the
+various diagrams to see if you can tell.
+
+If your model isn't listed and/or has different settings, PLEASE PLEASE
+tell me.  I had to figure mine out without the manual, and it WASN'T FUN!
+
+Even if your ARCnet model isn't listed, but has the same jumpers as another
+model that is, please e-mail me to say so.
+
+Cards Listed in this file (in this order, mostly):
+
+       =============== ======================= ====
+       Manufacturer    Model #                 Bits
+       =============== ======================= ====
+       SMC             PC100                   8
+       SMC             PC110                   8
+       SMC             PC120                   8
+       SMC             PC130                   8
+       SMC             PC270E                  8
+       SMC             PC500                   16
+       SMC             PC500Longboard          16
+       SMC             PC550Longboard          16
+       SMC             PC600                   16
+       SMC             PC710                   8
+       SMC?            LCS-8830(-T)            8/16
+       Puredata        PDI507                  8
+       CNet Tech       CN120-Series            8
+       CNet Tech       CN160-Series            16
+       Lantech?        UM9065L chipset         8
+       Acer            5210-003                8
+       Datapoint?      LAN-ARC-8               8
+       Topware         TA-ARC/10               8
+       Thomas-Conrad   500-6242-0097 REV A     8
+       Waterloo?       (C)1985 Waterloo Micro. 8
+       No Name         --                      8/16
+       No Name         Taiwan R.O.C?           8
+       No Name         Model 9058              8
+       Tiara           Tiara Lancard?          8
+       =============== ======================= ====
+
+
+* SMC = Standard Microsystems Corp.
+* CNet Tech = CNet Technology, Inc.
+
+Unclassified Stuff
+==================
+
+  - Please send any other information you can find.
+
+  - And some other stuff (more info is welcome!)::
+
+     From: root@ultraworld.xs4all.nl (Timo Hilbrink)
+     To: apenwarr@foxnet.net (Avery Pennarun)
+     Date: Wed, 26 Oct 1994 02:10:32 +0000 (GMT)
+     Reply-To: timoh@xs4all.nl
+
+     [...parts deleted...]
+
+     About the jumpers: On my PC130 there is one more jumper, located near the
+     cable-connector and it's for changing to star or bus topology;
+     closed: star - open: bus
+     On the PC500 are some more jumper-pins, one block labeled with RX,PDN,TXI
+     and another with ALE,LA17,LA18,LA19 these are undocumented..
+
+     [...more parts deleted...]
+
+     --- CUT ---
+
+Standard Microsystems Corp (SMC)
+================================
+
+PC100, PC110, PC120, PC130 (8-bit cards) and PC500, PC600 (16-bit cards)
+------------------------------------------------------------------------
+
+  - mainly from Avery Pennarun <apenwarr@worldvisions.ca>.  Values depicted
+    are from Avery's setup.
+  - special thanks to Timo Hilbrink <timoh@xs4all.nl> for noting that PC120,
+    130, 500, and 600 all have the same switches as Avery's PC100.
+    PC500/600 have several extra, undocumented pins though. (?)
+  - PC110 settings were verified by Stephen A. Wood <saw@cebaf.gov>
+  - Also, the JP- and S-numbers probably don't match your card exactly.  Try
+    to find jumpers/switches with the same number of settings - it's
+    probably more reliable.
+
+::
+
+            JP5                       [|]    :    :    :    :
+       (IRQ Setting)                 IRQ2  IRQ3 IRQ4 IRQ5 IRQ7
+                       Put exactly one jumper on exactly one set of pins.
+
+
+                                 1  2   3  4  5  6   7  8  9 10
+            S1                /----------------------------------\
+       (I/O and Memory        |  1  1 * 0  0  0  0 * 1  1  0  1  |
+        addresses)            \----------------------------------/
+                                 |--|   |--------|   |--------|
+                                 (a)       (b)           (m)
+
+                       WARNING.  It's very important when setting these which way
+                       you're holding the card, and which way you think is '1'!
+
+                       If you suspect that your settings are not being made
+                       correctly, try reversing the direction or inverting the
+                       switch positions.
+
+                       a: The first digit of the I/O address.
+                               Setting         Value
+                               -------         -----
+                               00              0
+                               01              1
+                               10              2
+                               11              3
+
+                       b: The second digit of the I/O address.
+                               Setting         Value
+                               -------         -----
+                               0000            0
+                               0001            1
+                               0010            2
+                               ...             ...
+                               1110            E
+                               1111            F
+
+                       The I/O address is in the form ab0.  For example, if
+                       a is 0x2 and b is 0xE, the address will be 0x2E0.
+
+                       DO NOT SET THIS LESS THAN 0x200!!!!!
+
+
+                       m: The first digit of the memory address.
+                               Setting         Value
+                               -------         -----
+                               0000            0
+                               0001            1
+                               0010            2
+                               ...             ...
+                               1110            E
+                               1111            F
+
+                       The memory address is in the form m0000.  For example, if
+                       m is D, the address will be 0xD0000.
+
+                       DO NOT SET THIS TO C0000, F0000, OR LESS THAN A0000!
+
+                                 1  2  3  4  5  6  7  8
+            S2                /--------------------------\
+       (Station Address)      |  1  1  0  0  0  0  0  0  |
+                              \--------------------------/
+
+                               Setting         Value
+                               -------         -----
+                               00000000        00
+                               10000000        01
+                               01000000        02
+                               ...
+                               01111111        FE
+                               11111111        FF
+
+                       Note that this is binary with the digits reversed!
+
+                       DO NOT SET THIS TO 0 OR 255 (0xFF)!
+
+
+PC130E/PC270E (8-bit cards)
+---------------------------
+
+  - from Juergen Seifert <seifert@htwm.de>
+
+This description has been written by Juergen Seifert <seifert@htwm.de>
+using information from the following Original SMC Manual
+
+            "Configuration Guide for ARCNET(R)-PC130E/PC270 Network
+            Controller Boards Pub. # 900.044A June, 1989"
+
+ARCNET is a registered trademark of the Datapoint Corporation
+SMC is a registered trademark of the Standard Microsystems Corporation
+
+The PC130E is an enhanced version of the PC130 board, is equipped with a
+standard BNC female connector for connection to RG-62/U coax cable.
+Since this board is designed both for point-to-point connection in star
+networks and for connection to bus networks, it is downwardly compatible
+with all the other standard boards designed for coax networks (that is,
+the PC120, PC110 and PC100 star topology boards and the PC220, PC210 and
+PC200 bus topology boards).
+
+The PC270E is an enhanced version of the PC260 board, is equipped with two
+modular RJ11-type jacks for connection to twisted pair wiring.
+It can be used in a star or a daisy-chained network.
+
+::
+
+        8 7 6 5 4 3 2 1
+    ________________________________________________________________
+   |   |       S1        |                                          |
+   |   |_________________|                                          |
+   |    Offs|Base |I/O Addr                                         |
+   |     RAM Addr |                                              ___|
+   |         ___  ___                                       CR3 |___|
+   |        |   \/   |                                      CR4 |___|
+   |        |  PROM  |                                           ___|
+   |        |        |                                        N |   | 8
+   |        | SOCKET |                                        o |   | 7
+   |        |________|                                        d |   | 6
+   |                   ___________________                    e |   | 5
+   |                  |                   |                   A | S | 4
+   |       |oo| EXT2  |                   |                   d | 2 | 3
+   |       |oo| EXT1  |       SMC         |                   d |   | 2
+   |       |oo| ROM   |      90C63        |                   r |___| 1
+   |       |oo| IRQ7  |                   |               |o|  _____|
+   |       |oo| IRQ5  |                   |               |o| | J1  |
+   |       |oo| IRQ4  |                   |              STAR |_____|
+   |       |oo| IRQ3  |                   |                   | J2  |
+   |       |oo| IRQ2  |___________________|                   |_____|
+   |___                                               ______________|
+       |                                             |
+       |_____________________________________________|
+
+Legend::
+
+  SMC 90C63    ARCNET Controller / Transceiver /Logic
+  S1   1-3:    I/O Base Address Select
+       4-6:    Memory Base Address Select
+       7-8:    RAM Offset Select
+  S2   1-8:    Node ID Select
+  EXT          Extended Timeout Select
+  ROM          ROM Enable Select
+  STAR         Selected - Star Topology        (PC130E only)
+               Deselected - Bus Topology       (PC130E only)
+  CR3/CR4      Diagnostic LEDs
+  J1           BNC RG62/U Connector            (PC130E only)
+  J1           6-position Telephone Jack       (PC270E only)
+  J2           6-position Telephone Jack       (PC270E only)
+
+Setting one of the switches to Off/Open means "1", On/Closed means "0".
+
+
+Setting the Node ID
+^^^^^^^^^^^^^^^^^^^
+
+The eight switches in group S2 are used to set the node ID.
+These switches work in a way similar to the PC100-series cards; see that
+entry for more information.
+
+
+Setting the I/O Base Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The first three switches in switch group S1 are used to select one
+of eight possible I/O Base addresses using the following table::
+
+
+   Switch | Hex I/O
+   1 2 3  | Address
+   -------|--------
+   0 0 0  |  260
+   0 0 1  |  290
+   0 1 0  |  2E0  (Manufacturer's default)
+   0 1 1  |  2F0
+   1 0 0  |  300
+   1 0 1  |  350
+   1 1 0  |  380
+   1 1 1  |  3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The memory buffer requires 2K of a 16K block of RAM. The base of this
+16K block can be located in any of eight positions.
+Switches 4-6 of switch group S1 select the Base of the 16K block.
+Within that 16K address space, the buffer may be assigned any one of four
+positions, determined by the offset, switches 7 and 8 of group S1.
+
+::
+
+   Switch     | Hex RAM | Hex ROM
+   4 5 6  7 8 | Address | Address *)
+   -----------|---------|-----------
+   0 0 0  0 0 |  C0000  |  C2000
+   0 0 0  0 1 |  C0800  |  C2000
+   0 0 0  1 0 |  C1000  |  C2000
+   0 0 0  1 1 |  C1800  |  C2000
+             |         |
+   0 0 1  0 0 |  C4000  |  C6000
+   0 0 1  0 1 |  C4800  |  C6000
+   0 0 1  1 0 |  C5000  |  C6000
+   0 0 1  1 1 |  C5800  |  C6000
+             |         |
+   0 1 0  0 0 |  CC000  |  CE000
+   0 1 0  0 1 |  CC800  |  CE000
+   0 1 0  1 0 |  CD000  |  CE000
+   0 1 0  1 1 |  CD800  |  CE000
+             |         |
+   0 1 1  0 0 |  D0000  |  D2000  (Manufacturer's default)
+   0 1 1  0 1 |  D0800  |  D2000
+   0 1 1  1 0 |  D1000  |  D2000
+   0 1 1  1 1 |  D1800  |  D2000
+             |         |
+   1 0 0  0 0 |  D4000  |  D6000
+   1 0 0  0 1 |  D4800  |  D6000
+   1 0 0  1 0 |  D5000  |  D6000
+   1 0 0  1 1 |  D5800  |  D6000
+             |         |
+   1 0 1  0 0 |  D8000  |  DA000
+   1 0 1  0 1 |  D8800  |  DA000
+   1 0 1  1 0 |  D9000  |  DA000
+   1 0 1  1 1 |  D9800  |  DA000
+             |         |
+   1 1 0  0 0 |  DC000  |  DE000
+   1 1 0  0 1 |  DC800  |  DE000
+   1 1 0  1 0 |  DD000  |  DE000
+   1 1 0  1 1 |  DD800  |  DE000
+             |         |
+   1 1 1  0 0 |  E0000  |  E2000
+   1 1 1  0 1 |  E0800  |  E2000
+   1 1 1  1 0 |  E1000  |  E2000
+   1 1 1  1 1 |  E1800  |  E2000
+
+  *) To enable the 8K Boot PROM install the jumper ROM.
+     The default is jumper ROM not installed.
+
+
+Setting the Timeouts and Interrupt
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The jumpers labeled EXT1 and EXT2 are used to determine the timeout
+parameters. These two jumpers are normally left open.
+
+To select a hardware interrupt level set one (only one!) of the jumpers
+IRQ2, IRQ3, IRQ4, IRQ5, IRQ7. The Manufacturer's default is IRQ2.
+
+
+Configuring the PC130E for Star or Bus Topology
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The single jumper labeled STAR is used to configure the PC130E board for
+star or bus topology.
+When the jumper is installed, the board may be used in a star network, when
+it is removed, the board can be used in a bus topology.
+
+
+Diagnostic LEDs
+^^^^^^^^^^^^^^^
+
+Two diagnostic LEDs are visible on the rear bracket of the board.
+The green LED monitors the network activity: the red one shows the
+board activity::
+
+ Green  | Status               Red      | Status
+ -------|-------------------   ---------|-------------------
+  on    | normal activity      flash/on | data transfer
+  blink | reconfiguration      off      | no data transfer;
+  off   | defective board or            | incorrect memory or
+       | node ID is zero               | I/O address
+
+
+PC500/PC550 Longboard (16-bit cards)
+------------------------------------
+
+  - from Juergen Seifert <seifert@htwm.de>
+
+
+  .. note::
+
+      There is another Version of the PC500 called Short Version, which
+      is different in hard- and software! The most important differences
+      are:
+
+      - The long board has no Shared memory.
+      - On the long board the selection of the interrupt is done by binary
+       coded switch, on the short board directly by jumper.
+
+[Avery's note: pay special attention to that: the long board HAS NO SHARED
+MEMORY.  This means the current Linux-ARCnet driver can't use these cards.
+I have obtained a PC500Longboard and will be doing some experiments on it in
+the future, but don't hold your breath.  Thanks again to Juergen Seifert for
+his advice about this!]
+
+This description has been written by Juergen Seifert <seifert@htwm.de>
+using information from the following Original SMC Manual
+
+        "Configuration Guide for SMC ARCNET-PC500/PC550
+        Series Network Controller Boards Pub. # 900.033 Rev. A
+        November, 1989"
+
+ARCNET is a registered trademark of the Datapoint Corporation
+SMC is a registered trademark of the Standard Microsystems Corporation
+
+The PC500 is equipped with a standard BNC female connector for connection
+to RG-62/U coax cable.
+The board is designed both for point-to-point connection in star networks
+and for connection to bus networks.
+
+The PC550 is equipped with two modular RJ11-type jacks for connection
+to twisted pair wiring.
+It can be used in a star or a daisy-chained (BUS) network.
+
+::
+
+       1
+       0 9 8 7 6 5 4 3 2 1     6 5 4 3 2 1
+    ____________________________________________________________________
+   < |         SW1         | |     SW2     |                            |
+   > |_____________________| |_____________|                            |
+   <   IRQ    |I/O Addr                                                 |
+   >                                                                 ___|
+   <                                                            CR4 |___|
+   >                                                            CR3 |___|
+   <                                                                 ___|
+   >                                                              N |   | 8
+   <                                                              o |   | 7
+   >                                                              d | S | 6
+   <                                                              e | W | 5
+   >                                                              A | 3 | 4
+   <                                                              d |   | 3
+   >                                                              d |   | 2
+   <                                                              r |___| 1
+   >                                                        |o|    _____|
+   <                                                        |o|   | J1  |
+   >  3 1                                                   JP6   |_____|
+   < |o|o| JP2                                                    | J2  |
+   > |o|o|                                                        |_____|
+   <  4 2__                                               ______________|
+   >    |  |                                             |
+   <____|  |_____________________________________________|
+
+Legend::
+
+  SW1  1-6:    I/O Base Address Select
+       7-10:   Interrupt Select
+  SW2  1-6:    Reserved for Future Use
+  SW3  1-8:    Node ID Select
+  JP2  1-4:    Extended Timeout Select
+  JP6          Selected - Star Topology        (PC500 only)
+               Deselected - Bus Topology       (PC500 only)
+  CR3  Green   Monitors Network Activity
+  CR4  Red     Monitors Board Activity
+  J1           BNC RG62/U Connector            (PC500 only)
+  J1           6-position Telephone Jack       (PC550 only)
+  J2           6-position Telephone Jack       (PC550 only)
+
+Setting one of the switches to Off/Open means "1", On/Closed means "0".
+
+
+Setting the Node ID
+^^^^^^^^^^^^^^^^^^^
+
+The eight switches in group SW3 are used to set the node ID. Each node
+attached to the network must have an unique node ID which must be
+different from 0.
+Switch 1 serves as the least significant bit (LSB).
+
+The node ID is the sum of the values of all switches set to "1"
+These values are::
+
+    Switch | Value
+    -------|-------
+      1    |   1
+      2    |   2
+      3    |   4
+      4    |   8
+      5    |  16
+      6    |  32
+      7    |  64
+      8    | 128
+
+Some Examples::
+
+    Switch         | Hex     | Decimal
+   8 7 6 5 4 3 2 1 | Node ID | Node ID
+   ----------------|---------|---------
+   0 0 0 0 0 0 0 0 |    not allowed
+   0 0 0 0 0 0 0 1 |    1    |    1
+   0 0 0 0 0 0 1 0 |    2    |    2
+   0 0 0 0 0 0 1 1 |    3    |    3
+       . . .       |         |
+   0 1 0 1 0 1 0 1 |   55    |   85
+       . . .       |         |
+   1 0 1 0 1 0 1 0 |   AA    |  170
+       . . .       |         |
+   1 1 1 1 1 1 0 1 |   FD    |  253
+   1 1 1 1 1 1 1 0 |   FE    |  254
+   1 1 1 1 1 1 1 1 |   FF    |  255
+
+
+Setting the I/O Base Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The first six switches in switch group SW1 are used to select one
+of 32 possible I/O Base addresses using the following table::
+
+   Switch       | Hex I/O
+   6 5  4 3 2 1 | Address
+   -------------|--------
+   0 1  0 0 0 0 |  200
+   0 1  0 0 0 1 |  210
+   0 1  0 0 1 0 |  220
+   0 1  0 0 1 1 |  230
+   0 1  0 1 0 0 |  240
+   0 1  0 1 0 1 |  250
+   0 1  0 1 1 0 |  260
+   0 1  0 1 1 1 |  270
+   0 1  1 0 0 0 |  280
+   0 1  1 0 0 1 |  290
+   0 1  1 0 1 0 |  2A0
+   0 1  1 0 1 1 |  2B0
+   0 1  1 1 0 0 |  2C0
+   0 1  1 1 0 1 |  2D0
+   0 1  1 1 1 0 |  2E0 (Manufacturer's default)
+   0 1  1 1 1 1 |  2F0
+   1 1  0 0 0 0 |  300
+   1 1  0 0 0 1 |  310
+   1 1  0 0 1 0 |  320
+   1 1  0 0 1 1 |  330
+   1 1  0 1 0 0 |  340
+   1 1  0 1 0 1 |  350
+   1 1  0 1 1 0 |  360
+   1 1  0 1 1 1 |  370
+   1 1  1 0 0 0 |  380
+   1 1  1 0 0 1 |  390
+   1 1  1 0 1 0 |  3A0
+   1 1  1 0 1 1 |  3B0
+   1 1  1 1 0 0 |  3C0
+   1 1  1 1 0 1 |  3D0
+   1 1  1 1 1 0 |  3E0
+   1 1  1 1 1 1 |  3F0
+
+
+Setting the Interrupt
+^^^^^^^^^^^^^^^^^^^^^
+
+Switches seven through ten of switch group SW1 are used to select the
+interrupt level. The interrupt level is binary coded, so selections
+from 0 to 15 would be possible, but only the following eight values will
+be supported: 3, 4, 5, 7, 9, 10, 11, 12.
+
+::
+
+   Switch   | IRQ
+   10 9 8 7 |
+   ---------|--------
+    0 0 1 1 |  3
+    0 1 0 0 |  4
+    0 1 0 1 |  5
+    0 1 1 1 |  7
+    1 0 0 1 |  9 (=2) (default)
+    1 0 1 0 | 10
+    1 0 1 1 | 11
+    1 1 0 0 | 12
+
+
+Setting the Timeouts
+^^^^^^^^^^^^^^^^^^^^
+
+The two jumpers JP2 (1-4) are used to determine the timeout parameters.
+These two jumpers are normally left open.
+Refer to the COM9026 Data Sheet for alternate configurations.
+
+
+Configuring the PC500 for Star or Bus Topology
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The single jumper labeled JP6 is used to configure the PC500 board for
+star or bus topology.
+When the jumper is installed, the board may be used in a star network, when
+it is removed, the board can be used in a bus topology.
+
+
+Diagnostic LEDs
+^^^^^^^^^^^^^^^
+
+Two diagnostic LEDs are visible on the rear bracket of the board.
+The green LED monitors the network activity: the red one shows the
+board activity::
+
+ Green  | Status               Red      | Status
+ -------|-------------------   ---------|-------------------
+  on    | normal activity      flash/on | data transfer
+  blink | reconfiguration      off      | no data transfer;
+  off   | defective board or            | incorrect memory or
+       | node ID is zero               | I/O address
+
+
+PC710 (8-bit card)
+------------------
+
+  - from J.S. van Oosten <jvoosten@compiler.tdcnet.nl>
+
+Note: this data is gathered by experimenting and looking at info of other
+cards. However, I'm sure I got 99% of the settings right.
+
+The SMC710 card resembles the PC270 card, but is much more basic (i.e. no
+LEDs, RJ11 jacks, etc.) and 8 bit. Here's a little drawing::
+
+    _______________________________________
+   | +---------+  +---------+              |____
+   | |   S2    |  |   S1    |              |
+   | +---------+  +---------+              |
+   |                                       |
+   |  +===+    __                          |
+   |  | R |   |  | X-tal                 ###___
+   |  | O |   |__|                      ####__'|
+   |  | M |    ||                        ###
+   |  +===+                                |
+   |                                       |
+   |   .. JP1   +----------+               |
+   |   ..       | big chip |               |
+   |   ..       |  90C63   |               |
+   |   ..       |          |               |
+   |   ..       +----------+               |
+    -------                     -----------
+          |||||||||||||||||||||
+
+The row of jumpers at JP1 actually consists of 8 jumpers, (sometimes
+labelled) the same as on the PC270, from top to bottom: EXT2, EXT1, ROM,
+IRQ7, IRQ5, IRQ4, IRQ3, IRQ2 (gee, wonder what they would do? :-) )
+
+S1 and S2 perform the same function as on the PC270, only their numbers
+are swapped (S1 is the nodeaddress, S2 sets IO- and RAM-address).
+
+I know it works when connected to a PC110 type ARCnet board.
+
+
+*****************************************************************************
+
+Possibly SMC
+============
+
+LCS-8830(-T) (8 and 16-bit cards)
+---------------------------------
+
+  - from Mathias Katzer <mkatzer@HRZ.Uni-Bielefeld.DE>
+  - Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl> says the
+    LCS-8830 is slightly different from LCS-8830-T.  These are 8 bit, BUS
+    only (the JP0 jumper is hardwired), and BNC only.
+
+This is a LCS-8830-T made by SMC, I think ('SMC' only appears on one PLCC,
+nowhere else, not even on the few Xeroxed sheets from the manual).
+
+SMC ARCnet Board Type LCS-8830-T::
+
+     ------------------------------------
+    |                                    |
+    |              JP3 88  8 JP2         |
+    |       #####      | \               |
+    |       #####    ET1 ET2          ###|
+    |                              8  ###|
+    |  U3   SW 1                  JP0 ###|  Phone Jacks
+    |  --                             ###|
+    | |  |                               |
+    | |  |   SW2                         |
+    | |  |                               |
+    | |  |  #####                        |
+    |  --   #####                       ####  BNC Connector
+    |                                   ####
+    |   888888 JP1                       |
+    |   234567                           |
+     --                           -------
+       |||||||||||||||||||||||||||
+       --------------------------
+
+
+  SW1: DIP-Switches for Station Address
+  SW2: DIP-Switches for Memory Base and I/O Base addresses
+
+  JP0: If closed, internal termination on (default open)
+  JP1: IRQ Jumpers
+  JP2: Boot-ROM enabled if closed
+  JP3: Jumpers for response timeout
+
+  U3: Boot-ROM Socket
+
+
+  ET1 ET2     Response Time     Idle Time    Reconfiguration Time
+
+                78                86               840
+   X            285               316              1680
+       X        563               624              1680
+   X   X       1130              1237              1680
+
+  (X means closed jumper)
+
+  (DIP-Switch downwards means "0")
+
+The station address is binary-coded with SW1.
+
+The I/O base address is coded with DIP-Switches 6,7 and 8 of SW2:
+
+========       ========
+Switches        Base
+678             Address
+========       ========
+000            260-26f
+100            290-29f
+010            2e0-2ef
+110            2f0-2ff
+001            300-30f
+101            350-35f
+011            380-38f
+111            3e0-3ef
+========       ========
+
+
+DIP Switches 1-5 of SW2 encode the RAM and ROM Address Range:
+
+========        ============= ================
+Switches        RAM           ROM
+12345           Address Range  Address Range
+========        ============= ================
+00000          C:0000-C:07ff   C:2000-C:3fff
+10000          C:0800-C:0fff
+01000          C:1000-C:17ff
+11000          C:1800-C:1fff
+00100          C:4000-C:47ff   C:6000-C:7fff
+10100          C:4800-C:4fff
+01100          C:5000-C:57ff
+11100          C:5800-C:5fff
+00010          C:C000-C:C7ff   C:E000-C:ffff
+10010          C:C800-C:Cfff
+01010          C:D000-C:D7ff
+11010          C:D800-C:Dfff
+00110          D:0000-D:07ff   D:2000-D:3fff
+10110          D:0800-D:0fff
+01110          D:1000-D:17ff
+11110          D:1800-D:1fff
+00001          D:4000-D:47ff   D:6000-D:7fff
+10001          D:4800-D:4fff
+01001          D:5000-D:57ff
+11001          D:5800-D:5fff
+00101          D:8000-D:87ff   D:A000-D:bfff
+10101          D:8800-D:8fff
+01101          D:9000-D:97ff
+11101          D:9800-D:9fff
+00011          D:C000-D:c7ff   D:E000-D:ffff
+10011          D:C800-D:cfff
+01011          D:D000-D:d7ff
+11011          D:D800-D:dfff
+00111          E:0000-E:07ff   E:2000-E:3fff
+10111          E:0800-E:0fff
+01111          E:1000-E:17ff
+11111          E:1800-E:1fff
+========        ============= ================
+
+
+PureData Corp
+=============
+
+PDI507 (8-bit card)
+--------------------
+
+  - from Mark Rejhon <mdrejhon@magi.com> (slight modifications by Avery)
+  - Avery's note: I think PDI508 cards (but definitely NOT PDI508Plus cards)
+    are mostly the same as this.  PDI508Plus cards appear to be mainly
+    software-configured.
+
+Jumpers:
+
+       There is a jumper array at the bottom of the card, near the edge
+       connector.  This array is labelled J1.  They control the IRQs and
+       something else.  Put only one jumper on the IRQ pins.
+
+       ETS1, ETS2 are for timing on very long distance networks.  See the
+       more general information near the top of this file.
+
+       There is a J2 jumper on two pins.  A jumper should be put on them,
+       since it was already there when I got the card.  I don't know what
+       this jumper is for though.
+
+       There is a two-jumper array for J3.  I don't know what it is for,
+       but there were already two jumpers on it when I got the card.  It's
+       a six pin grid in a two-by-three fashion.  The jumpers were
+       configured as follows::
+
+          .-------.
+        o | o   o |
+          :-------:    ------> Accessible end of card with connectors
+        o | o   o |             in this direction ------->
+          `-------'
+
+Carl de Billy <CARL@carainfo.com> explains J3 and J4:
+
+   J3 Diagram::
+
+          .-------.
+        o | o   o |
+          :-------:    TWIST Technology
+        o | o   o |
+          `-------'
+          .-------.
+          | o   o | o
+          :-------:    COAX Technology
+          | o   o | o
+          `-------'
+
+  - If using coax cable in a bus topology the J4 jumper must be removed;
+    place it on one pin.
+
+  - If using bus topology with twisted pair wiring move the J3
+    jumpers so they connect the middle pin and the pins closest to the RJ11
+    Connectors.  Also the J4 jumper must be removed; place it on one pin of
+    J4 jumper for storage.
+
+  - If using  star topology with twisted pair wiring move the J3
+    jumpers so they connect the middle pin and the pins closest to the RJ11
+    connectors.
+
+
+DIP Switches:
+
+       The DIP switches accessible on the accessible end of the card while
+       it is installed, is used to set the ARCnet address.  There are 8
+       switches.  Use an address from 1 to 254
+
+       ==========      =========================
+       Switch No.      ARCnet address
+       12345678
+       ==========      =========================
+       00000000        FF      (Don't use this!)
+       00000001        FE
+       00000010        FD
+       ...
+       11111101        2
+       11111110        1
+       11111111        0       (Don't use this!)
+       ==========      =========================
+
+       There is another array of eight DIP switches at the top of the
+       card.  There are five labelled MS0-MS4 which seem to control the
+       memory address, and another three labelled IO0-IO2 which seem to
+       control the base I/O address of the card.
+
+       This was difficult to test by trial and error, and the I/O addresses
+       are in a weird order.  This was tested by setting the DIP switches,
+       rebooting the computer, and attempting to load ARCETHER at various
+       addresses (mostly between 0x200 and 0x400).  The address that caused
+       the red transmit LED to blink, is the one that I thought works.
+
+       Also, the address 0x3D0 seem to have a special meaning, since the
+       ARCETHER packet driver loaded fine, but without the red LED
+       blinking.  I don't know what 0x3D0 is for though.  I recommend using
+       an address of 0x300 since Windows may not like addresses below
+       0x300.
+
+       =============   ===========
+       IO Switch No.   I/O address
+       210
+       =============   ===========
+       111             0x260
+       110             0x290
+       101             0x2E0
+       100             0x2F0
+       011             0x300
+       010             0x350
+       001             0x380
+       000             0x3E0
+       =============   ===========
+
+       The memory switches set a reserved address space of 0x1000 bytes
+       (0x100 segment units, or 4k).  For example if I set an address of
+       0xD000, it will use up addresses 0xD000 to 0xD100.
+
+       The memory switches were tested by booting using QEMM386 stealth,
+       and using LOADHI to see what address automatically became excluded
+       from the upper memory regions, and then attempting to load ARCETHER
+       using these addresses.
+
+       I recommend using an ARCnet memory address of 0xD000, and putting
+       the EMS page frame at 0xC000 while using QEMM stealth mode.  That
+       way, you get contiguous high memory from 0xD100 almost all the way
+       the end of the megabyte.
+
+       Memory Switch 0 (MS0) didn't seem to work properly when set to OFF
+       on my card.  It could be malfunctioning on my card.  Experiment with
+       it ON first, and if it doesn't work, set it to OFF.  (It may be a
+       modifier for the 0x200 bit?)
+
+       =============   ============================================
+       MS Switch No.
+       43210           Memory address
+       =============   ============================================
+       00001           0xE100  (guessed - was not detected by QEMM)
+       00011           0xE000  (guessed - was not detected by QEMM)
+       00101           0xDD00
+       00111           0xDC00
+       01001           0xD900
+       01011           0xD800
+       01101           0xD500
+       01111           0xD400
+       10001           0xD100
+       10011           0xD000
+       10101           0xCD00
+       10111           0xCC00
+       11001           0xC900 (guessed - crashes tested system)
+       11011           0xC800 (guessed - crashes tested system)
+       11101           0xC500 (guessed - crashes tested system)
+       11111           0xC400 (guessed - crashes tested system)
+       =============   ============================================
+
+CNet Technology Inc. (8-bit cards)
+==================================
+
+120 Series (8-bit cards)
+------------------------
+  - from Juergen Seifert <seifert@htwm.de>
+
+This description has been written by Juergen Seifert <seifert@htwm.de>
+using information from the following Original CNet Manual
+
+             "ARCNET USER'S MANUAL for
+             CN120A
+             CN120AB
+             CN120TP
+             CN120ST
+             CN120SBT
+             P/N:12-01-0007
+             Revision 3.00"
+
+ARCNET is a registered trademark of the Datapoint Corporation
+
+- P/N 120A   ARCNET 8 bit XT/AT Star
+- P/N 120AB  ARCNET 8 bit XT/AT Bus
+- P/N 120TP  ARCNET 8 bit XT/AT Twisted Pair
+- P/N 120ST  ARCNET 8 bit XT/AT Star, Twisted Pair
+- P/N 120SBT ARCNET 8 bit XT/AT Star, Bus, Twisted Pair
+
+::
+
+    __________________________________________________________________
+   |                                                                  |
+   |                                                               ___|
+   |                                                          LED |___|
+   |                                                               ___|
+   |                                                            N |   | ID7
+   |                                                            o |   | ID6
+   |                                                            d | S | ID5
+   |                                                            e | W | ID4
+   |                     ___________________                    A | 2 | ID3
+   |                    |                   |                   d |   | ID2
+   |                    |                   |  1 2 3 4 5 6 7 8  d |   | ID1
+   |                    |                   | _________________ r |___| ID0
+   |                    |      90C65        ||       SW1       |  ____|
+   |  JP 8 7            |                   ||_________________| |    |
+   |    |o|o|  JP1      |                   |                    | J2 |
+   |    |o|o|  |oo|     |                   |         JP 1 1 1   |    |
+   |   ______________   |                   |            0 1 2   |____|
+   |  |  PROM        |  |___________________|           |o|o|o|  _____|
+   |  >  SOCKET      |  JP 6 5 4 3 2                    |o|o|o| | J1  |
+   |  |______________|    |o|o|o|o|o|                   |o|o|o| |_____|
+   |_____                 |o|o|o|o|o|                   ______________|
+        |                                             |
+        |_____________________________________________|
+
+Legend::
+
+  90C65       ARCNET Probe
+  S1  1-5:    Base Memory Address Select
+      6-8:    Base I/O Address Select
+  S2  1-8:    Node ID Select (ID0-ID7)
+  JP1     ROM Enable Select
+  JP2     IRQ2
+  JP3     IRQ3
+  JP4     IRQ4
+  JP5     IRQ5
+  JP6     IRQ7
+  JP7/JP8     ET1, ET2 Timeout Parameters
+  JP10/JP11   Coax / Twisted Pair Select  (CN120ST/SBT only)
+  JP12        Terminator Select       (CN120AB/ST/SBT only)
+  J1      BNC RG62/U Connector        (all except CN120TP)
+  J2      Two 6-position Telephone Jack   (CN120TP/ST/SBT only)
+
+Setting one of the switches to Off means "1", On means "0".
+
+
+Setting the Node ID
+^^^^^^^^^^^^^^^^^^^
+
+The eight switches in SW2 are used to set the node ID. Each node attached
+to the network must have an unique node ID which must be different from 0.
+Switch 1 (ID0) serves as the least significant bit (LSB).
+
+The node ID is the sum of the values of all switches set to "1"
+These values are:
+
+   =======  ======  =====
+   Switch   Label   Value
+   =======  ======  =====
+     1      ID0       1
+     2      ID1       2
+     3      ID2       4
+     4      ID3       8
+     5      ID4      16
+     6      ID5      32
+     7      ID6      64
+     8      ID7     128
+   =======  ======  =====
+
+Some Examples::
+
+    Switch         | Hex     | Decimal
+   8 7 6 5 4 3 2 1 | Node ID | Node ID
+   ----------------|---------|---------
+   0 0 0 0 0 0 0 0 |    not allowed
+   0 0 0 0 0 0 0 1 |    1    |    1
+   0 0 0 0 0 0 1 0 |    2    |    2
+   0 0 0 0 0 0 1 1 |    3    |    3
+       . . .       |         |
+   0 1 0 1 0 1 0 1 |   55    |   85
+       . . .       |         |
+   1 0 1 0 1 0 1 0 |   AA    |  170
+       . . .       |         |
+   1 1 1 1 1 1 0 1 |   FD    |  253
+   1 1 1 1 1 1 1 0 |   FE    |  254
+   1 1 1 1 1 1 1 1 |   FF    |  255
+
+
+Setting the I/O Base Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The last three switches in switch block SW1 are used to select one
+of eight possible I/O Base addresses using the following table::
+
+
+   Switch      | Hex I/O
+    6   7   8  | Address
+   ------------|--------
+   ON  ON  ON  |  260
+   OFF ON  ON  |  290
+   ON  OFF ON  |  2E0  (Manufacturer's default)
+   OFF OFF ON  |  2F0
+   ON  ON  OFF |  300
+   OFF ON  OFF |  350
+   ON  OFF OFF |  380
+   OFF OFF OFF |  3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The memory buffer (RAM) requires 2K. The base of this buffer can be
+located in any of eight positions. The address of the Boot Prom is
+memory base + 8K or memory base + 0x2000.
+Switches 1-5 of switch block SW1 select the Memory Base address.
+
+::
+
+   Switch              | Hex RAM | Hex ROM
+    1   2   3   4   5  | Address | Address *)
+   --------------------|---------|-----------
+   ON  ON  ON  ON  ON  |  C0000  |  C2000
+   ON  ON  OFF ON  ON  |  C4000  |  C6000
+   ON  ON  ON  OFF ON  |  CC000  |  CE000
+   ON  ON  OFF OFF ON  |  D0000  |  D2000  (Manufacturer's default)
+   ON  ON  ON  ON  OFF |  D4000  |  D6000
+   ON  ON  OFF ON  OFF |  D8000  |  DA000
+   ON  ON  ON  OFF OFF |  DC000  |  DE000
+   ON  ON  OFF OFF OFF |  E0000  |  E2000
+
+  *) To enable the Boot ROM install the jumper JP1
+
+.. note::
+
+      Since the switches 1 and 2 are always set to ON it may be possible
+      that they can be used to add an offset of 2K, 4K or 6K to the base
+      address, but this feature is not documented in the manual and I
+      haven't tested it yet.
+
+
+Setting the Interrupt Line
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To select a hardware interrupt level install one (only one!) of the jumpers
+JP2, JP3, JP4, JP5, JP6. JP2 is the default::
+
+   Jumper | IRQ
+   -------|-----
+     2    |  2
+     3    |  3
+     4    |  4
+     5    |  5
+     6    |  7
+
+
+Setting the Internal Terminator on CN120AB/TP/SBT
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The jumper JP12 is used to enable the internal terminator::
+
+                        -----
+       0                |  0  |
+     -----   ON         |     |  ON
+    |  0  |             |  0  |
+    |     |  OFF         -----   OFF
+    |  0  |                0
+     -----
+   Terminator          Terminator
+    disabled            enabled
+
+
+Selecting the Connector Type on CN120ST/SBT
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+     JP10    JP11        JP10    JP11
+                        -----   -----
+       0       0        |  0  | |  0  |
+     -----   -----      |     | |     |
+    |  0  | |  0  |     |  0  | |  0  |
+    |     | |     |      -----   -----
+    |  0  | |  0  |        0       0
+     -----   -----
+     Coaxial Cable       Twisted Pair Cable
+       (Default)
+
+
+Setting the Timeout Parameters
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The jumpers labeled EXT1 and EXT2 are used to determine the timeout
+parameters. These two jumpers are normally left open.
+
+
+CNet Technology Inc. (16-bit cards)
+===================================
+
+160 Series (16-bit cards)
+-------------------------
+  - from Juergen Seifert <seifert@htwm.de>
+
+This description has been written by Juergen Seifert <seifert@htwm.de>
+using information from the following Original CNet Manual
+
+             "ARCNET USER'S MANUAL for
+             CN160A CN160AB CN160TP
+             P/N:12-01-0006 Revision 3.00"
+
+ARCNET is a registered trademark of the Datapoint Corporation
+
+- P/N 160A   ARCNET 16 bit XT/AT Star
+- P/N 160AB  ARCNET 16 bit XT/AT Bus
+- P/N 160TP  ARCNET 16 bit XT/AT Twisted Pair
+
+::
+
+   ___________________________________________________________________
+  <                             _________________________          ___|
+  >               |oo| JP2     |                         |    LED |___|
+  <               |oo| JP1     |        9026             |    LED |___|
+  >                            |_________________________|         ___|
+  <                                                             N |   | ID7
+  >                                                      1      o |   | ID6
+  <                                    1 2 3 4 5 6 7 8 9 0      d | S | ID5
+  >         _______________           _____________________     e | W | ID4
+  <        |     PROM      |         |         SW1         |    A | 2 | ID3
+  >        >    SOCKET     |         |_____________________|    d |   | ID2
+  <        |_______________|          | IO-Base   | MEM   |     d |   | ID1
+  >                                                             r |___| ID0
+  <                                                               ____|
+  >                                                              |    |
+  <                                                              | J1 |
+  >                                                              |    |
+  <                                                              |____|
+  >                            1 1 1 1                                |
+  <  3 4 5 6 7      JP     8 9 0 1 2 3                                |
+  > |o|o|o|o|o|           |o|o|o|o|o|o|                               |
+  < |o|o|o|o|o| __        |o|o|o|o|o|o|                    ___________|
+  >            |  |                                       |
+  <____________|  |_______________________________________|
+
+Legend::
+
+  9026            ARCNET Probe
+  SW1 1-6:    Base I/O Address Select
+      7-10:   Base Memory Address Select
+  SW2 1-8:    Node ID Select (ID0-ID7)
+  JP1/JP2     ET1, ET2 Timeout Parameters
+  JP3-JP13    Interrupt Select
+  J1      BNC RG62/U Connector        (CN160A/AB only)
+  J1      Two 6-position Telephone Jack   (CN160TP only)
+  LED
+
+Setting one of the switches to Off means "1", On means "0".
+
+
+Setting the Node ID
+^^^^^^^^^^^^^^^^^^^
+
+The eight switches in SW2 are used to set the node ID. Each node attached
+to the network must have an unique node ID which must be different from 0.
+Switch 1 (ID0) serves as the least significant bit (LSB).
+
+The node ID is the sum of the values of all switches set to "1"
+These values are::
+
+   Switch | Label | Value
+   -------|-------|-------
+     1    | ID0   |   1
+     2    | ID1   |   2
+     3    | ID2   |   4
+     4    | ID3   |   8
+     5    | ID4   |  16
+     6    | ID5   |  32
+     7    | ID6   |  64
+     8    | ID7   | 128
+
+Some Examples::
+
+    Switch         | Hex     | Decimal
+   8 7 6 5 4 3 2 1 | Node ID | Node ID
+   ----------------|---------|---------
+   0 0 0 0 0 0 0 0 |    not allowed
+   0 0 0 0 0 0 0 1 |    1    |    1
+   0 0 0 0 0 0 1 0 |    2    |    2
+   0 0 0 0 0 0 1 1 |    3    |    3
+       . . .       |         |
+   0 1 0 1 0 1 0 1 |   55    |   85
+       . . .       |         |
+   1 0 1 0 1 0 1 0 |   AA    |  170
+       . . .       |         |
+   1 1 1 1 1 1 0 1 |   FD    |  253
+   1 1 1 1 1 1 1 0 |   FE    |  254
+   1 1 1 1 1 1 1 1 |   FF    |  255
+
+
+Setting the I/O Base Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The first six switches in switch block SW1 are used to select the I/O Base
+address using the following table::
+
+            Switch        | Hex I/O
+    1   2   3   4   5   6  | Address
+   ------------------------|--------
+   OFF ON  ON  OFF OFF ON  |  260
+   OFF ON  OFF ON  ON  OFF |  290
+   OFF ON  OFF OFF OFF ON  |  2E0  (Manufacturer's default)
+   OFF ON  OFF OFF OFF OFF |  2F0
+   OFF OFF ON  ON  ON  ON  |  300
+   OFF OFF ON  OFF ON  OFF |  350
+   OFF OFF OFF ON  ON  ON  |  380
+   OFF OFF OFF OFF OFF ON  |  3E0
+
+Note: Other IO-Base addresses seem to be selectable, but only the above
+      combinations are documented.
+
+
+Setting the Base Memory (RAM) buffer Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The switches 7-10 of switch block SW1 are used to select the Memory
+Base address of the RAM (2K) and the PROM::
+
+   Switch          | Hex RAM | Hex ROM
+    7   8   9  10  | Address | Address
+   ----------------|---------|-----------
+   OFF OFF ON  ON  |  C0000  |  C8000
+   OFF OFF ON  OFF |  D0000  |  D8000 (Default)
+   OFF OFF OFF ON  |  E0000  |  E8000
+
+.. note::
+
+      Other MEM-Base addresses seem to be selectable, but only the above
+      combinations are documented.
+
+
+Setting the Interrupt Line
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To select a hardware interrupt level install one (only one!) of the jumpers
+JP3 through JP13 using the following table::
+
+   Jumper | IRQ
+   -------|-----------------
+     3    |  14
+     4    |  15
+     5    |  12
+     6    |  11
+     7    |  10
+     8    |   3
+     9    |   4
+    10    |   5
+    11    |   6
+    12    |   7
+    13    |   2 (=9) Default!
+
+.. note::
+
+       - Do not use JP11=IRQ6, it may conflict with your Floppy Disk
+        Controller
+       - Use JP3=IRQ14 only, if you don't have an IDE-, MFM-, or RLL-
+        Hard Disk, it may conflict with their controllers
+
+
+Setting the Timeout Parameters
+------------------------------
+
+The jumpers labeled JP1 and JP2 are used to determine the timeout
+parameters. These two jumpers are normally left open.
+
+
+Lantech
+=======
+
+8-bit card, unknown model
+-------------------------
+  - from Vlad Lungu <vlungu@ugal.ro> - his e-mail address seemed broken at
+    the time I tried to reach him.  Sorry Vlad, if you didn't get my reply.
+
+::
+
+   ________________________________________________________________
+   |   1         8                                                 |
+   |   ___________                                               __|
+   |   |   SW1    |                                         LED |__|
+   |   |__________|                                                |
+   |                                                            ___|
+   |                _____________________                       |S | 8
+   |                |                   |                       |W |
+   |                |                   |                       |2 |
+   |                |                   |                       |__| 1
+   |                |      UM9065L      |     |o|  JP4         ____|____
+   |                |                   |     |o|              |  CN    |
+   |                |                   |                      |________|
+   |                |                   |                          |
+   |                |___________________|                          |
+   |                                                               |
+   |                                                               |
+   |      _____________                                            |
+   |      |            |                                           |
+   |      |    PROM    |        |ooooo|  JP6                       |
+   |      |____________|        |ooooo|                            |
+   |_____________                                             _   _|
+               |____________________________________________| |__|
+
+
+UM9065L : ARCnet Controller
+
+SW 1    : Shared Memory Address and I/O Base
+
+::
+
+       ON=0
+
+       12345|Memory Address
+       -----|--------------
+       00001|  D4000
+       00010|  CC000
+       00110|  D0000
+       01110|  D1000
+       01101|  D9000
+       10010|  CC800
+       10011|  DC800
+       11110|  D1800
+
+It seems that the bits are considered in reverse order.  Also, you must
+observe that some of those addresses are unusual and I didn't probe them; I
+used a memory dump in DOS to identify them.  For the 00000 configuration and
+some others that I didn't write here the card seems to conflict with the
+video card (an S3 GENDAC). I leave the full decoding of those addresses to
+you.
+
+::
+
+       678| I/O Address
+       ---|------------
+       000|    260
+       001|    failed probe
+       010|    2E0
+       011|    380
+       100|    290
+       101|    350
+       110|    failed probe
+       111|    3E0
+
+  SW 2  : Node ID (binary coded)
+
+  JP 4  : Boot PROM enable   CLOSE - enabled
+                            OPEN  - disabled
+
+  JP 6  : IRQ set (ONLY ONE jumper on 1-5 for IRQ 2-6)
+
+
+Acer
+====
+
+8-bit card, Model 5210-003
+--------------------------
+
+  - from Vojtech Pavlik <vojtech@suse.cz> using portions of the existing
+    arcnet-hardware file.
+
+This is a 90C26 based card.  Its configuration seems similar to the SMC
+PC100, but has some additional jumpers I don't know the meaning of.
+
+::
+
+              __
+             |  |
+   ___________|__|_________________________
+  |         |      |                       |
+  |         | BNC  |                       |
+  |         |______|                    ___|
+  |  _____________________             |___
+  | |                     |                |
+  | | Hybrid IC           |                |
+  | |                     |       o|o J1   |
+  | |_____________________|       8|8      |
+  |                               8|8 J5   |
+  |                               o|o      |
+  |                               8|8      |
+  |__                             8|8      |
+ (|__| LED                        o|o      |
+  |                               8|8      |
+  |                               8|8 J15  |
+  |                                        |
+  |                    _____               |
+  |                   |     |   _____      |
+  |                   |     |  |     |  ___|
+  |                   |     |  |     | |
+  |  _____            | ROM |  | UFS | |
+  | |     |           |     |  |     | |
+  | |     |     ___   |     |  |     | |
+  | |     |    |   |  |__.__|  |__.__| |
+  | | NCR |    |XTL|   _____    _____  |
+  | |     |    |___|  |     |  |     | |
+  | |90C26|           |     |  |     | |
+  | |     |           | RAM |  | UFS | |
+  | |     | J17 o|o   |     |  |     | |
+  | |     | J16 o|o   |     |  |     | |
+  | |__.__|           |__.__|  |__.__| |
+  |  ___                               |
+  | |   |8                             |
+  | |SW2|                              |
+  | |   |                              |
+  | |___|1                             |
+  |  ___                               |
+  | |   |10           J18 o|o          |
+  | |   |                 o|o          |
+  | |SW1|                 o|o          |
+  | |   |             J21 o|o          |
+  | |___|1                             |
+  |                                    |
+  |____________________________________|
+
+
+Legend::
+
+  90C26       ARCNET Chip
+  XTL         20 MHz Crystal
+  SW1 1-6     Base I/O Address Select
+      7-10    Memory Address Select
+  SW2 1-8     Node ID Select (ID0-ID7)
+  J1-J5       IRQ Select
+  J6-J21      Unknown (Probably extra timeouts & ROM enable ...)
+  LED1        Activity LED
+  BNC         Coax connector (STAR ARCnet)
+  RAM         2k of SRAM
+  ROM         Boot ROM socket
+  UFS         Unidentified Flying Sockets
+
+
+Setting the Node ID
+^^^^^^^^^^^^^^^^^^^
+
+The eight switches in SW2 are used to set the node ID. Each node attached
+to the network must have an unique node ID which must not be 0.
+Switch 1 (ID0) serves as the least significant bit (LSB).
+
+Setting one of the switches to OFF means "1", ON means "0".
+
+The node ID is the sum of the values of all switches set to "1"
+These values are::
+
+   Switch | Value
+   -------|-------
+     1    |   1
+     2    |   2
+     3    |   4
+     4    |   8
+     5    |  16
+     6    |  32
+     7    |  64
+     8    | 128
+
+Don't set this to 0 or 255; these values are reserved.
+
+
+Setting the I/O Base Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The switches 1 to 6 of switch block SW1 are used to select one
+of 32 possible I/O Base addresses using the following tables::
+
+         | Hex
+   Switch | Value
+   -------|-------
+     1    | 200
+     2    | 100
+     3    |  80
+     4    |  40
+     5    |  20
+     6    |  10
+
+The I/O address is sum of all switches set to "1". Remember that
+the I/O address space bellow 0x200 is RESERVED for mainboard, so
+switch 1 should be ALWAYS SET TO OFF.
+
+
+Setting the Base Memory (RAM) buffer Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The memory buffer (RAM) requires 2K. The base of this buffer can be
+located in any of sixteen positions. However, the addresses below
+A0000 are likely to cause system hang because there's main RAM.
+
+Jumpers 7-10 of switch block SW1 select the Memory Base address::
+
+   Switch          | Hex RAM
+    7   8   9  10  | Address
+   ----------------|---------
+   OFF OFF OFF OFF |  F0000 (conflicts with main BIOS)
+   OFF OFF OFF ON  |  E0000
+   OFF OFF ON  OFF |  D0000
+   OFF OFF ON  ON  |  C0000 (conflicts with video BIOS)
+   OFF ON  OFF OFF |  B0000 (conflicts with mono video)
+   OFF ON  OFF ON  |  A0000 (conflicts with graphics)
+
+
+Setting the Interrupt Line
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Jumpers 1-5 of the jumper block J1 control the IRQ level. ON means
+shorted, OFF means open::
+
+    Jumper              |  IRQ
+    1   2   3   4   5   |
+   ----------------------------
+    ON  OFF OFF OFF OFF |  7
+    OFF ON  OFF OFF OFF |  5
+    OFF OFF ON  OFF OFF |  4
+    OFF OFF OFF ON  OFF |  3
+    OFF OFF OFF OFF ON  |  2
+
+
+Unknown jumpers & sockets
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+I know nothing about these. I just guess that J16&J17 are timeout
+jumpers and maybe one of J18-J21 selects ROM. Also J6-J10 and
+J11-J15 are connecting IRQ2-7 to some pins on the UFSs. I can't
+guess the purpose.
+
+Datapoint?
+==========
+
+LAN-ARC-8, an 8-bit card
+------------------------
+
+  - from Vojtech Pavlik <vojtech@suse.cz>
+
+This is another SMC 90C65-based ARCnet card. I couldn't identify the
+manufacturer, but it might be DataPoint, because the card has the
+original arcNet logo in its upper right corner.
+
+::
+
+         _______________________________________________________
+        |                         _________                     |
+        |                        |   SW2   | ON      arcNet     |
+        |                        |_________| OFF             ___|
+        |  _____________         1 ______  8                |   | 8
+        | |             | SW1     | XTAL | ____________     | S |
+        | > RAM (2k)    |         |______||            |    | W |
+        | |_____________|                 |      H     |    | 3 |
+        |                        _________|_____ y     |    |___| 1
+        |  _________            |         |     |b     |        |
+        | |_________|           |         |     |r     |        |
+        |                       |     SMC |     |i     |        |
+        |                       |    90C65|     |d     |        |
+        |  _________            |         |     |      |        |
+        | |   SW1   | ON        |         |     |I     |        |
+        | |_________| OFF       |_________|_____/C     |   _____|
+        |  1       8                      |            |  |     |___
+        |  ______________                 |            |  | BNC |___|
+        | |              |                |____________|  |_____|
+        | > EPROM SOCKET |              _____________           |
+        | |______________|             |_____________|          |
+        |                                         ______________|
+        |                                        |
+        |________________________________________|
+
+Legend::
+
+  90C65       ARCNET Chip
+  SW1 1-5:    Base Memory Address Select
+      6-8:    Base I/O Address Select
+  SW2 1-8:    Node ID Select
+  SW3 1-5:    IRQ Select
+      6-7:    Extra Timeout
+      8  :    ROM Enable
+  BNC         Coax connector
+  XTAL        20 MHz Crystal
+
+
+Setting the Node ID
+^^^^^^^^^^^^^^^^^^^
+
+The eight switches in SW3 are used to set the node ID. Each node attached
+to the network must have an unique node ID which must not be 0.
+Switch 1 serves as the least significant bit (LSB).
+
+Setting one of the switches to Off means "1", On means "0".
+
+The node ID is the sum of the values of all switches set to "1"
+These values are::
+
+   Switch | Value
+   -------|-------
+     1    |   1
+     2    |   2
+     3    |   4
+     4    |   8
+     5    |  16
+     6    |  32
+     7    |  64
+     8    | 128
+
+
+Setting the I/O Base Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The last three switches in switch block SW1 are used to select one
+of eight possible I/O Base addresses using the following table::
+
+
+   Switch      | Hex I/O
+    6   7   8  | Address
+   ------------|--------
+   ON  ON  ON  |  260
+   OFF ON  ON  |  290
+   ON  OFF ON  |  2E0  (Manufacturer's default)
+   OFF OFF ON  |  2F0
+   ON  ON  OFF |  300
+   OFF ON  OFF |  350
+   ON  OFF OFF |  380
+   OFF OFF OFF |  3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The memory buffer (RAM) requires 2K. The base of this buffer can be
+located in any of eight positions. The address of the Boot Prom is
+memory base + 0x2000.
+
+Jumpers 3-5 of switch block SW1 select the Memory Base address.
+
+::
+
+   Switch              | Hex RAM | Hex ROM
+    1   2   3   4   5  | Address | Address *)
+   --------------------|---------|-----------
+   ON  ON  ON  ON  ON  |  C0000  |  C2000
+   ON  ON  OFF ON  ON  |  C4000  |  C6000
+   ON  ON  ON  OFF ON  |  CC000  |  CE000
+   ON  ON  OFF OFF ON  |  D0000  |  D2000  (Manufacturer's default)
+   ON  ON  ON  ON  OFF |  D4000  |  D6000
+   ON  ON  OFF ON  OFF |  D8000  |  DA000
+   ON  ON  ON  OFF OFF |  DC000  |  DE000
+   ON  ON  OFF OFF OFF |  E0000  |  E2000
+
+  *) To enable the Boot ROM set the switch 8 of switch block SW3 to position ON.
+
+The switches 1 and 2 probably add 0x0800 and 0x1000 to RAM base address.
+
+
+Setting the Interrupt Line
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Switches 1-5 of the switch block SW3 control the IRQ level::
+
+    Jumper              |  IRQ
+    1   2   3   4   5   |
+   ----------------------------
+    ON  OFF OFF OFF OFF |  3
+    OFF ON  OFF OFF OFF |  4
+    OFF OFF ON  OFF OFF |  5
+    OFF OFF OFF ON  OFF |  7
+    OFF OFF OFF OFF ON  |  2
+
+
+Setting the Timeout Parameters
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The switches 6-7 of the switch block SW3 are used to determine the timeout
+parameters.  These two switches are normally left in the OFF position.
+
+
+Topware
+=======
+
+8-bit card, TA-ARC/10
+---------------------
+
+  - from Vojtech Pavlik <vojtech@suse.cz>
+
+This is another very similar 90C65 card. Most of the switches and jumpers
+are the same as on other clones.
+
+::
+
+   _____________________________________________________________________
+  |  ___________   |                         |            ______        |
+  | |SW2 NODE ID|  |                         |           | XTAL |       |
+  | |___________|  |  Hybrid IC              |           |______|       |
+  |  ___________   |                         |                        __|
+  | |SW1 MEM+I/O|  |_________________________|                   LED1|__|)
+  | |___________|           1 2                                         |
+  |                     J3 |o|o| TIMEOUT                          ______|
+  |     ______________     |o|o|                                 |      |
+  |    |              |  ___________________                     | RJ   |
+  |    > EPROM SOCKET | |                   \                    |------|
+  |J2  |______________| |                    |                   |      |
+  ||o|                  |                    |                   |______|
+  ||o| ROM ENABLE       |        SMC         |    _________             |
+  |     _____________   |       90C65        |   |_________|       _____|
+  |    |             |  |                    |                    |     |___
+  |    > RAM (2k)    |  |                    |                    | BNC |___|
+  |    |_____________|  |                    |                    |_____|
+  |                     |____________________|                          |
+  | ________ IRQ 2 3 4 5 7                  ___________                 |
+  ||________|   |o|o|o|o|o|                |___________|                |
+  |________   J1|o|o|o|o|o|                               ______________|
+          |                                             |
+          |_____________________________________________|
+
+Legend::
+
+  90C65       ARCNET Chip
+  XTAL        20 MHz Crystal
+  SW1 1-5     Base Memory Address Select
+      6-8     Base I/O Address Select
+  SW2 1-8     Node ID Select (ID0-ID7)
+  J1          IRQ Select
+  J2          ROM Enable
+  J3          Extra Timeout
+  LED1        Activity LED
+  BNC         Coax connector (BUS ARCnet)
+  RJ          Twisted Pair Connector (daisy chain)
+
+
+Setting the Node ID
+^^^^^^^^^^^^^^^^^^^
+
+The eight switches in SW2 are used to set the node ID. Each node attached to
+the network must have an unique node ID which must not be 0.  Switch 1 (ID0)
+serves as the least significant bit (LSB).
+
+Setting one of the switches to Off means "1", On means "0".
+
+The node ID is the sum of the values of all switches set to "1"
+These values are::
+
+   Switch | Label | Value
+   -------|-------|-------
+     1    | ID0   |   1
+     2    | ID1   |   2
+     3    | ID2   |   4
+     4    | ID3   |   8
+     5    | ID4   |  16
+     6    | ID5   |  32
+     7    | ID6   |  64
+     8    | ID7   | 128
+
+Setting the I/O Base Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The last three switches in switch block SW1 are used to select one
+of eight possible I/O Base addresses using the following table::
+
+
+   Switch      | Hex I/O
+    6   7   8  | Address
+   ------------|--------
+   ON  ON  ON  |  260  (Manufacturer's default)
+   OFF ON  ON  |  290
+   ON  OFF ON  |  2E0
+   OFF OFF ON  |  2F0
+   ON  ON  OFF |  300
+   OFF ON  OFF |  350
+   ON  OFF OFF |  380
+   OFF OFF OFF |  3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The memory buffer (RAM) requires 2K. The base of this buffer can be
+located in any of eight positions. The address of the Boot Prom is
+memory base + 0x2000.
+
+Jumpers 3-5 of switch block SW1 select the Memory Base address.
+
+::
+
+   Switch              | Hex RAM | Hex ROM
+    1   2   3   4   5  | Address | Address *)
+   --------------------|---------|-----------
+   ON  ON  ON  ON  ON  |  C0000  |  C2000
+   ON  ON  OFF ON  ON  |  C4000  |  C6000  (Manufacturer's default)
+   ON  ON  ON  OFF ON  |  CC000  |  CE000
+   ON  ON  OFF OFF ON  |  D0000  |  D2000
+   ON  ON  ON  ON  OFF |  D4000  |  D6000
+   ON  ON  OFF ON  OFF |  D8000  |  DA000
+   ON  ON  ON  OFF OFF |  DC000  |  DE000
+   ON  ON  OFF OFF OFF |  E0000  |  E2000
+
+   *) To enable the Boot ROM short the jumper J2.
+
+The jumpers 1 and 2 probably add 0x0800 and 0x1000 to RAM address.
+
+
+Setting the Interrupt Line
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Jumpers 1-5 of the jumper block J1 control the IRQ level.  ON means
+shorted, OFF means open::
+
+    Jumper              |  IRQ
+    1   2   3   4   5   |
+   ----------------------------
+    ON  OFF OFF OFF OFF |  2
+    OFF ON  OFF OFF OFF |  3
+    OFF OFF ON  OFF OFF |  4
+    OFF OFF OFF ON  OFF |  5
+    OFF OFF OFF OFF ON  |  7
+
+
+Setting the Timeout Parameters
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The jumpers J3 are used to set the timeout parameters. These two
+jumpers are normally left open.
+
+Thomas-Conrad
+=============
+
+Model #500-6242-0097 REV A (8-bit card)
+---------------------------------------
+
+  - from Lars Karlsson <100617.3473@compuserve.com>
+
+::
+
+     ________________________________________________________
+   |          ________   ________                           |_____
+   |         |........| |........|                            |
+   |         |________| |________|                         ___|
+   |            SW 3       SW 1                           |   |
+   |         Base I/O   Base Addr.                Station |   |
+   |                                              address |   |
+   |    ______                                    switch  |   |
+   |   |      |                                           |   |
+   |   |      |                                           |___|
+   |   |      |                                 ______        |___._
+   |   |______|                                |______|         ____| BNC
+   |                                            Jumper-        _____| Connector
+   |   Main chip                                block  _    __|   '
+   |                                                  | |  |    RJ Connector
+   |                                                  |_|  |    with 110 Ohm
+   |                                                       |__  Terminator
+   |    ___________                                         __|
+   |   |...........|                                       |    RJ-jack
+   |   |...........|    _____                              |    (unused)
+   |   |___________|   |_____|                             |__
+   |  Boot PROM socket IRQ-jumpers                            |_  Diagnostic
+   |________                                       __          _| LED (red)
+           | | | | | | | | | | | | | | | | | | | |  |        |
+           | | | | | | | | | | | | | | | | | | | |  |________|
+                                                             |
+                                                             |
+
+And here are the settings for some of the switches and jumpers on the cards.
+
+::
+
+           I/O
+
+          1 2 3 4 5 6 7 8
+
+  2E0----- 0 0 0 1 0 0 0 1
+  2F0----- 0 0 0 1 0 0 0 0
+  300----- 0 0 0 0 1 1 1 1
+  350----- 0 0 0 0 1 1 1 0
+
+"0" in the above example means switch is off "1" means that it is on.
+
+::
+
+      ShMem address.
+
+       1 2 3 4 5 6 7 8
+
+  CX00--0 0 1 1 | |   |
+  DX00--0 0 1 0       |
+  X000--------- 1 1   |
+  X400--------- 1 0   |
+  X800--------- 0 1   |
+  XC00--------- 0 0
+  ENHANCED----------- 1
+  COMPATIBLE--------- 0
+
+::
+
+        IRQ
+
+
+     3 4 5 7 2
+     . . . . .
+     . . . . .
+
+
+There is a DIP-switch with 8 switches, used to set the shared memory address
+to be used. The first 6 switches set the address, the 7th doesn't have any
+function, and the 8th switch is used to select "compatible" or "enhanced".
+When I got my two cards, one of them had this switch set to "enhanced". That
+card didn't work at all, it wasn't even recognized by the driver. The other
+card had this switch set to "compatible" and it behaved absolutely normally. I
+guess that the switch on one of the cards, must have been changed accidentally
+when the card was taken out of its former host. The question remains
+unanswered, what is the purpose of the "enhanced" position?
+
+[Avery's note: "enhanced" probably either disables shared memory (use IO
+ports instead) or disables IO ports (use memory addresses instead).  This
+varies by the type of card involved.  I fail to see how either of these
+enhance anything.  Send me more detailed information about this mode, or
+just use "compatible" mode instead.]
+
+Waterloo Microsystems Inc. ??
+=============================
+
+8-bit card (C) 1985
+-------------------
+  - from Robert Michael Best <rmb117@cs.usask.ca>
+
+[Avery's note: these don't work with my driver for some reason.  These cards
+SEEM to have settings similar to the PDI508Plus, which is
+software-configured and doesn't work with my driver either.  The "Waterloo
+chip" is a boot PROM, probably designed specifically for the University of
+Waterloo.  If you have any further information about this card, please
+e-mail me.]
+
+The probe has not been able to detect the card on any of the J2 settings,
+and I tried them again with the "Waterloo" chip removed.
+
+::
+
+   _____________________________________________________________________
+  | \/  \/              ___  __ __                                      |
+  | C4  C4     |^|     | M ||  ^  ||^|                                  |
+  | --  --     |_|     | 5 ||     || | C3                               |
+  | \/  \/      C10    |___||     ||_|                                  |
+  | C4  C4             _  _ |     |                 ??                  |
+  | --  --            | \/ ||     |                                     |
+  |                   |    ||     |                                     |
+  |                   |    ||  C1 |                                     |
+  |                   |    ||     |  \/                            _____|
+  |                   | C6 ||     |  C9                           |     |___
+  |                   |    ||     |  --                           | BNC |___|
+  |                   |    ||     |          >C7|                 |_____|
+  |                   |    ||     |                                     |
+  | __ __             |____||_____|       1 2 3     6                   |
+  ||  ^  |     >C4|                      |o|o|o|o|o|o| J2    >C4|       |
+  ||     |                               |o|o|o|o|o|o|                  |
+  || C2  |     >C4|                                          >C4|       |
+  ||     |                                   >C8|                       |
+  ||     |       2 3 4 5 6 7  IRQ                            >C4|       |
+  ||_____|      |o|o|o|o|o|o| J3                                        |
+  |_______      |o|o|o|o|o|o|                            _______________|
+         |                                             |
+         |_____________________________________________|
+
+  C1 -- "COM9026
+        SMC 8638"
+       In a chip socket.
+
+  C2 -- "@Copyright
+        Waterloo Microsystems Inc.
+        1985"
+       In a chip Socket with info printed on a label covering a round window
+       showing the circuit inside. (The window indicates it is an EPROM chip.)
+
+  C3 -- "COM9032
+        SMC 8643"
+       In a chip socket.
+
+  C4 -- "74LS"
+       9 total no sockets.
+
+  M5 -- "50006-136
+        20.000000 MHZ
+        MTQ-T1-S3
+        0 M-TRON 86-40"
+       Metallic case with 4 pins, no socket.
+
+  C6 -- "MOSTEK@TC8643
+        MK6116N-20
+        MALAYSIA"
+       No socket.
+
+  C7 -- No stamp or label but in a 20 pin chip socket.
+
+  C8 -- "PAL10L8CN
+        8623"
+       In a 20 pin socket.
+
+  C9 -- "PAl16R4A-2CN
+        8641"
+       In a 20 pin socket.
+
+  C10 -- "M8640
+           NMC
+         9306N"
+        In an 8 pin socket.
+
+  ?? -- Some components on a smaller board and attached with 20 pins all
+       along the side closest to the BNC connector.  The are coated in a dark
+       resin.
+
+On the board there are two jumper banks labeled J2 and J3. The
+manufacturer didn't put a J1 on the board. The two boards I have both
+came with a jumper box for each bank.
+
+::
+
+  J2 -- Numbered 1 2 3 4 5 6.
+       4 and 5 are not stamped due to solder points.
+
+  J3 -- IRQ 2 3 4 5 6 7
+
+The board itself has a maple leaf stamped just above the irq jumpers
+and "-2 46-86" beside C2. Between C1 and C6 "ASS 'Y 300163" and "@1986
+CORMAN CUSTOM ELECTRONICS CORP." stamped just below the BNC connector.
+Below that "MADE IN CANADA"
+
+No Name
+=======
+
+8-bit cards, 16-bit cards
+-------------------------
+
+  - from Juergen Seifert <seifert@htwm.de>
+
+I have named this ARCnet card "NONAME", since there is no name of any
+manufacturer on the Installation manual nor on the shipping box. The only
+hint to the existence of a manufacturer at all is written in copper,
+it is "Made in Taiwan"
+
+This description has been written by Juergen Seifert <seifert@htwm.de>
+using information from the Original
+
+                   "ARCnet Installation Manual"
+
+::
+
+    ________________________________________________________________
+   | |STAR| BUS| T/P|                                               |
+   | |____|____|____|                                               |
+   |                            _____________________               |
+   |                           |                     |              |
+   |                           |                     |              |
+   |                           |                     |              |
+   |                           |        SMC          |              |
+   |                           |                     |              |
+   |                           |       COM90C65      |              |
+   |                           |                     |              |
+   |                           |                     |              |
+   |                           |__________-__________|              |
+   |                                                           _____|
+   |      _______________                                     |  CN |
+   |     | PROM          |                                    |_____|
+   |     > SOCKET        |                                          |
+   |     |_______________|         1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8 |
+   |                               _______________  _______________ |
+   |           |o|o|o|o|o|o|o|o|  |      SW1      ||      SW2      ||
+   |           |o|o|o|o|o|o|o|o|  |_______________||_______________||
+   |___         2 3 4 5 7 E E R        Node ID       IOB__|__MEM____|
+       |        \ IRQ   / T T O                      |
+       |__________________1_2_M______________________|
+
+Legend::
+
+  COM90C65:       ARCnet Probe
+  S1  1-8:    Node ID Select
+  S2  1-3:    I/O Base Address Select
+      4-6:    Memory Base Address Select
+      7-8:    RAM Offset Select
+  ET1, ET2    Extended Timeout Select
+  ROM     ROM Enable Select
+  CN              RG62 Coax Connector
+  STAR| BUS | T/P Three fields for placing a sign (colored circle)
+                 indicating the topology of the card
+
+Setting one of the switches to Off means "1", On means "0".
+
+
+Setting the Node ID
+^^^^^^^^^^^^^^^^^^^
+
+The eight switches in group SW1 are used to set the node ID.
+Each node attached to the network must have an unique node ID which
+must be different from 0.
+Switch 8 serves as the least significant bit (LSB).
+
+The node ID is the sum of the values of all switches set to "1"
+These values are::
+
+    Switch | Value
+    -------|-------
+      8    |   1
+      7    |   2
+      6    |   4
+      5    |   8
+      4    |  16
+      3    |  32
+      2    |  64
+      1    | 128
+
+Some Examples::
+
+    Switch         | Hex     | Decimal
+   1 2 3 4 5 6 7 8 | Node ID | Node ID
+   ----------------|---------|---------
+   0 0 0 0 0 0 0 0 |    not allowed
+   0 0 0 0 0 0 0 1 |    1    |    1
+   0 0 0 0 0 0 1 0 |    2    |    2
+   0 0 0 0 0 0 1 1 |    3    |    3
+       . . .       |         |
+   0 1 0 1 0 1 0 1 |   55    |   85
+       . . .       |         |
+   1 0 1 0 1 0 1 0 |   AA    |  170
+       . . .       |         |
+   1 1 1 1 1 1 0 1 |   FD    |  253
+   1 1 1 1 1 1 1 0 |   FE    |  254
+   1 1 1 1 1 1 1 1 |   FF    |  255
+
+
+Setting the I/O Base Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The first three switches in switch group SW2 are used to select one
+of eight possible I/O Base addresses using the following table::
+
+   Switch      | Hex I/O
+    1   2   3  | Address
+   ------------|--------
+   ON  ON  ON  |  260
+   ON  ON  OFF |  290
+   ON  OFF ON  |  2E0  (Manufacturer's default)
+   ON  OFF OFF |  2F0
+   OFF ON  ON  |  300
+   OFF ON  OFF |  350
+   OFF OFF ON  |  380
+   OFF OFF OFF |  3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The memory buffer requires 2K of a 16K block of RAM. The base of this
+16K block can be located in any of eight positions.
+Switches 4-6 of switch group SW2 select the Base of the 16K block.
+Within that 16K address space, the buffer may be assigned any one of four
+positions, determined by the offset, switches 7 and 8 of group SW2.
+
+::
+
+   Switch     | Hex RAM | Hex ROM
+   4 5 6  7 8 | Address | Address *)
+   -----------|---------|-----------
+   0 0 0  0 0 |  C0000  |  C2000
+   0 0 0  0 1 |  C0800  |  C2000
+   0 0 0  1 0 |  C1000  |  C2000
+   0 0 0  1 1 |  C1800  |  C2000
+             |         |
+   0 0 1  0 0 |  C4000  |  C6000
+   0 0 1  0 1 |  C4800  |  C6000
+   0 0 1  1 0 |  C5000  |  C6000
+   0 0 1  1 1 |  C5800  |  C6000
+             |         |
+   0 1 0  0 0 |  CC000  |  CE000
+   0 1 0  0 1 |  CC800  |  CE000
+   0 1 0  1 0 |  CD000  |  CE000
+   0 1 0  1 1 |  CD800  |  CE000
+             |         |
+   0 1 1  0 0 |  D0000  |  D2000  (Manufacturer's default)
+   0 1 1  0 1 |  D0800  |  D2000
+   0 1 1  1 0 |  D1000  |  D2000
+   0 1 1  1 1 |  D1800  |  D2000
+             |         |
+   1 0 0  0 0 |  D4000  |  D6000
+   1 0 0  0 1 |  D4800  |  D6000
+   1 0 0  1 0 |  D5000  |  D6000
+   1 0 0  1 1 |  D5800  |  D6000
+             |         |
+   1 0 1  0 0 |  D8000  |  DA000
+   1 0 1  0 1 |  D8800  |  DA000
+   1 0 1  1 0 |  D9000  |  DA000
+   1 0 1  1 1 |  D9800  |  DA000
+             |         |
+   1 1 0  0 0 |  DC000  |  DE000
+   1 1 0  0 1 |  DC800  |  DE000
+   1 1 0  1 0 |  DD000  |  DE000
+   1 1 0  1 1 |  DD800  |  DE000
+             |         |
+   1 1 1  0 0 |  E0000  |  E2000
+   1 1 1  0 1 |  E0800  |  E2000
+   1 1 1  1 0 |  E1000  |  E2000
+   1 1 1  1 1 |  E1800  |  E2000
+
+   *) To enable the 8K Boot PROM install the jumper ROM.
+      The default is jumper ROM not installed.
+
+
+Setting Interrupt Request Lines (IRQ)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To select a hardware interrupt level set one (only one!) of the jumpers
+IRQ2, IRQ3, IRQ4, IRQ5 or IRQ7. The manufacturer's default is IRQ2.
+
+
+Setting the Timeouts
+^^^^^^^^^^^^^^^^^^^^
+
+The two jumpers labeled ET1 and ET2 are used to determine the timeout
+parameters (response and reconfiguration time). Every node in a network
+must be set to the same timeout values.
+
+::
+
+   ET1 ET2 | Response Time (us) | Reconfiguration Time (ms)
+   --------|--------------------|--------------------------
+   Off Off |        78          |          840   (Default)
+   Off On  |       285          |         1680
+   On  Off |       563          |         1680
+   On  On  |      1130          |         1680
+
+On means jumper installed, Off means jumper not installed
+
+
+16-BIT ARCNET
+-------------
+
+The manual of my 8-Bit NONAME ARCnet Card contains another description
+of a 16-Bit Coax / Twisted Pair Card. This description is incomplete,
+because there are missing two pages in the manual booklet. (The table
+of contents reports pages ... 2-9, 2-11, 2-12, 3-1, ... but inside
+the booklet there is a different way of counting ... 2-9, 2-10, A-1,
+(empty page), 3-1, ..., 3-18, A-1 (again), A-2)
+Also the picture of the board layout is not as good as the picture of
+8-Bit card, because there isn't any letter like "SW1" written to the
+picture.
+
+Should somebody have such a board, please feel free to complete this
+description or to send a mail to me!
+
+This description has been written by Juergen Seifert <seifert@htwm.de>
+using information from the Original
+
+                   "ARCnet Installation Manual"
+
+::
+
+   ___________________________________________________________________
+  <                    _________________  _________________           |
+  >                   |       SW?       ||      SW?        |          |
+  <                   |_________________||_________________|          |
+  >                       ____________________                        |
+  <                      |                    |                       |
+  >                      |                    |                       |
+  <                      |                    |                       |
+  >                      |                    |                       |
+  <                      |                    |                       |
+  >                      |                    |                       |
+  <                      |                    |                       |
+  >                      |____________________|                       |
+  <                                                               ____|
+  >                       ____________________                   |    |
+  <                      |                    |                  | J1 |
+  >                      |                    <                  |    |
+  <                      |____________________|  ? ? ? ? ? ?     |____|
+  >                                             |o|o|o|o|o|o|         |
+  <                                             |o|o|o|o|o|o|         |
+  >                                                                   |
+  <             __                                         ___________|
+  >            |  |                                       |
+  <____________|  |_______________________________________|
+
+
+Setting one of the switches to Off means "1", On means "0".
+
+
+Setting the Node ID
+^^^^^^^^^^^^^^^^^^^
+
+The eight switches in group SW2 are used to set the node ID.
+Each node attached to the network must have an unique node ID which
+must be different from 0.
+Switch 8 serves as the least significant bit (LSB).
+
+The node ID is the sum of the values of all switches set to "1"
+These values are::
+
+    Switch | Value
+    -------|-------
+      8    |   1
+      7    |   2
+      6    |   4
+      5    |   8
+      4    |  16
+      3    |  32
+      2    |  64
+      1    | 128
+
+Some Examples::
+
+    Switch         | Hex     | Decimal
+   1 2 3 4 5 6 7 8 | Node ID | Node ID
+   ----------------|---------|---------
+   0 0 0 0 0 0 0 0 |    not allowed
+   0 0 0 0 0 0 0 1 |    1    |    1
+   0 0 0 0 0 0 1 0 |    2    |    2
+   0 0 0 0 0 0 1 1 |    3    |    3
+       . . .       |         |
+   0 1 0 1 0 1 0 1 |   55    |   85
+       . . .       |         |
+   1 0 1 0 1 0 1 0 |   AA    |  170
+       . . .       |         |
+   1 1 1 1 1 1 0 1 |   FD    |  253
+   1 1 1 1 1 1 1 0 |   FE    |  254
+   1 1 1 1 1 1 1 1 |   FF    |  255
+
+
+Setting the I/O Base Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The first three switches in switch group SW1 are used to select one
+of eight possible I/O Base addresses using the following table::
+
+   Switch      | Hex I/O
+    3   2   1  | Address
+   ------------|--------
+   ON  ON  ON  |  260
+   ON  ON  OFF |  290
+   ON  OFF ON  |  2E0  (Manufacturer's default)
+   ON  OFF OFF |  2F0
+   OFF ON  ON  |  300
+   OFF ON  OFF |  350
+   OFF OFF ON  |  380
+   OFF OFF OFF |  3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The memory buffer requires 2K of a 16K block of RAM. The base of this
+16K block can be located in any of eight positions.
+Switches 6-8 of switch group SW1 select the Base of the 16K block.
+Within that 16K address space, the buffer may be assigned any one of four
+positions, determined by the offset, switches 4 and 5 of group SW1::
+
+   Switch     | Hex RAM | Hex ROM
+   8 7 6  5 4 | Address | Address
+   -----------|---------|-----------
+   0 0 0  0 0 |  C0000  |  C2000
+   0 0 0  0 1 |  C0800  |  C2000
+   0 0 0  1 0 |  C1000  |  C2000
+   0 0 0  1 1 |  C1800  |  C2000
+             |         |
+   0 0 1  0 0 |  C4000  |  C6000
+   0 0 1  0 1 |  C4800  |  C6000
+   0 0 1  1 0 |  C5000  |  C6000
+   0 0 1  1 1 |  C5800  |  C6000
+             |         |
+   0 1 0  0 0 |  CC000  |  CE000
+   0 1 0  0 1 |  CC800  |  CE000
+   0 1 0  1 0 |  CD000  |  CE000
+   0 1 0  1 1 |  CD800  |  CE000
+             |         |
+   0 1 1  0 0 |  D0000  |  D2000  (Manufacturer's default)
+   0 1 1  0 1 |  D0800  |  D2000
+   0 1 1  1 0 |  D1000  |  D2000
+   0 1 1  1 1 |  D1800  |  D2000
+             |         |
+   1 0 0  0 0 |  D4000  |  D6000
+   1 0 0  0 1 |  D4800  |  D6000
+   1 0 0  1 0 |  D5000  |  D6000
+   1 0 0  1 1 |  D5800  |  D6000
+             |         |
+   1 0 1  0 0 |  D8000  |  DA000
+   1 0 1  0 1 |  D8800  |  DA000
+   1 0 1  1 0 |  D9000  |  DA000
+   1 0 1  1 1 |  D9800  |  DA000
+             |         |
+   1 1 0  0 0 |  DC000  |  DE000
+   1 1 0  0 1 |  DC800  |  DE000
+   1 1 0  1 0 |  DD000  |  DE000
+   1 1 0  1 1 |  DD800  |  DE000
+             |         |
+   1 1 1  0 0 |  E0000  |  E2000
+   1 1 1  0 1 |  E0800  |  E2000
+   1 1 1  1 0 |  E1000  |  E2000
+   1 1 1  1 1 |  E1800  |  E2000
+
+
+Setting Interrupt Request Lines (IRQ)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+??????????????????????????????????????
+
+
+Setting the Timeouts
+^^^^^^^^^^^^^^^^^^^^
+
+??????????????????????????????????????
+
+
+8-bit cards ("Made in Taiwan R.O.C.")
+-------------------------------------
+
+  - from Vojtech Pavlik <vojtech@suse.cz>
+
+I have named this ARCnet card "NONAME", since I got only the card with
+no manual at all and the only text identifying the manufacturer is
+"MADE IN TAIWAN R.O.C" printed on the card.
+
+::
+
+         ____________________________________________________________
+        |                 1 2 3 4 5 6 7 8                            |
+        | |o|o| JP1       o|o|o|o|o|o|o|o| ON                        |
+        |  +              o|o|o|o|o|o|o|o|                        ___|
+        |  _____________  o|o|o|o|o|o|o|o| OFF         _____     |   | ID7
+        | |             | SW1                         |     |    |   | ID6
+        | > RAM (2k)    |        ____________________ |  H  |    | S | ID5
+        | |_____________|       |                    ||  y  |    | W | ID4
+        |                       |                    ||  b  |    | 2 | ID3
+        |                       |                    ||  r  |    |   | ID2
+        |                       |                    ||  i  |    |   | ID1
+        |                       |       90C65        ||  d  |    |___| ID0
+        |      SW3              |                    ||     |        |
+        | |o|o|o|o|o|o|o|o| ON  |                    ||  I  |        |
+        | |o|o|o|o|o|o|o|o|     |                    ||  C  |        |
+        | |o|o|o|o|o|o|o|o| OFF |____________________||     |   _____|
+        |  1 2 3 4 5 6 7 8                            |     |  |     |___
+        |  ______________                             |     |  | BNC |___|
+        | |              |                            |_____|  |_____|
+        | > EPROM SOCKET |                                           |
+        | |______________|                                           |
+        |                                              ______________|
+        |                                             |
+        |_____________________________________________|
+
+Legend::
+
+  90C65       ARCNET Chip
+  SW1 1-5:    Base Memory Address Select
+      6-8:    Base I/O Address Select
+  SW2 1-8:    Node ID Select (ID0-ID7)
+  SW3 1-5:    IRQ Select
+      6-7:    Extra Timeout
+      8  :    ROM Enable
+  JP1         Led connector
+  BNC         Coax connector
+
+Although the jumpers SW1 and SW3 are marked SW, not JP, they are jumpers, not
+switches.
+
+Setting the jumpers to ON means connecting the upper two pins, off the bottom
+two - or - in case of IRQ setting, connecting none of them at all.
+
+Setting the Node ID
+^^^^^^^^^^^^^^^^^^^
+
+The eight switches in SW2 are used to set the node ID. Each node attached
+to the network must have an unique node ID which must not be 0.
+Switch 1 (ID0) serves as the least significant bit (LSB).
+
+Setting one of the switches to Off means "1", On means "0".
+
+The node ID is the sum of the values of all switches set to "1"
+These values are::
+
+   Switch | Label | Value
+   -------|-------|-------
+     1    | ID0   |   1
+     2    | ID1   |   2
+     3    | ID2   |   4
+     4    | ID3   |   8
+     5    | ID4   |  16
+     6    | ID5   |  32
+     7    | ID6   |  64
+     8    | ID7   | 128
+
+Some Examples::
+
+    Switch         | Hex     | Decimal
+   8 7 6 5 4 3 2 1 | Node ID | Node ID
+   ----------------|---------|---------
+   0 0 0 0 0 0 0 0 |    not allowed
+   0 0 0 0 0 0 0 1 |    1    |    1
+   0 0 0 0 0 0 1 0 |    2    |    2
+   0 0 0 0 0 0 1 1 |    3    |    3
+       . . .       |         |
+   0 1 0 1 0 1 0 1 |   55    |   85
+       . . .       |         |
+   1 0 1 0 1 0 1 0 |   AA    |  170
+       . . .       |         |
+   1 1 1 1 1 1 0 1 |   FD    |  253
+   1 1 1 1 1 1 1 0 |   FE    |  254
+   1 1 1 1 1 1 1 1 |   FF    |  255
+
+
+Setting the I/O Base Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The last three switches in switch block SW1 are used to select one
+of eight possible I/O Base addresses using the following table::
+
+
+   Switch      | Hex I/O
+    6   7   8  | Address
+   ------------|--------
+   ON  ON  ON  |  260
+   OFF ON  ON  |  290
+   ON  OFF ON  |  2E0  (Manufacturer's default)
+   OFF OFF ON  |  2F0
+   ON  ON  OFF |  300
+   OFF ON  OFF |  350
+   ON  OFF OFF |  380
+   OFF OFF OFF |  3E0
+
+
+Setting the Base Memory (RAM) buffer Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The memory buffer (RAM) requires 2K. The base of this buffer can be
+located in any of eight positions. The address of the Boot Prom is
+memory base + 0x2000.
+
+Jumpers 3-5 of jumper block SW1 select the Memory Base address.
+
+::
+
+   Switch              | Hex RAM | Hex ROM
+    1   2   3   4   5  | Address | Address *)
+   --------------------|---------|-----------
+   ON  ON  ON  ON  ON  |  C0000  |  C2000
+   ON  ON  OFF ON  ON  |  C4000  |  C6000
+   ON  ON  ON  OFF ON  |  CC000  |  CE000
+   ON  ON  OFF OFF ON  |  D0000  |  D2000  (Manufacturer's default)
+   ON  ON  ON  ON  OFF |  D4000  |  D6000
+   ON  ON  OFF ON  OFF |  D8000  |  DA000
+   ON  ON  ON  OFF OFF |  DC000  |  DE000
+   ON  ON  OFF OFF OFF |  E0000  |  E2000
+
+  *) To enable the Boot ROM set the jumper 8 of jumper block SW3 to position ON.
+
+The jumpers 1 and 2 probably add 0x0800, 0x1000 and 0x1800 to RAM adders.
+
+Setting the Interrupt Line
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Jumpers 1-5 of the jumper block SW3 control the IRQ level::
+
+    Jumper              |  IRQ
+    1   2   3   4   5   |
+   ----------------------------
+    ON  OFF OFF OFF OFF |  2
+    OFF ON  OFF OFF OFF |  3
+    OFF OFF ON  OFF OFF |  4
+    OFF OFF OFF ON  OFF |  5
+    OFF OFF OFF OFF ON  |  7
+
+
+Setting the Timeout Parameters
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The jumpers 6-7 of the jumper block SW3 are used to determine the timeout
+parameters. These two jumpers are normally left in the OFF position.
+
+
+
+(Generic Model 9058)
+--------------------
+  - from Andrew J. Kroll <ag784@freenet.buffalo.edu>
+  - Sorry this sat in my to-do box for so long, Andrew! (yikes - over a
+    year!)
+
+::
+
+                                                                     _____
+                                                                    |    <
+                                                                    | .---'
+    ________________________________________________________________ | |
+   |                           |     SW2     |                      |  |
+   |   ___________             |_____________|                      |  |
+   |  |           |              1 2 3 4 5 6                     ___|  |
+   |  >  6116 RAM |         _________                         8 |   |  |
+   |  |___________|        |20MHzXtal|                        7 |   |  |
+   |                       |_________|       __________       6 | S |  |
+   |    74LS373                             |          |-     5 | W |  |
+   |   _________                            |      E   |-     4 |   |  |
+   |   >_______|              ______________|..... P   |-     3 | 3 |  |
+   |                         |              |    : O   |-     2 |   |  |
+   |                         |              |    : X   |-     1 |___|  |
+   |   ________________      |              |    : Y   |-           |  |
+   |  |      SW1       |     |      SL90C65 |    :     |-           |  |
+   |  |________________|     |              |    : B   |-           |  |
+   |    1 2 3 4 5 6 7 8      |              |    : O   |-           |  |
+   |                         |_________o____|..../ A   |-    _______|  |
+   |    ____________________                |      R   |-   |       |------,
+   |   |                    |               |      D   |-   |  BNC  |   #  |
+   |   > 2764 PROM SOCKET   |               |__________|-   |_______|------'
+   |   |____________________|              _________                |  |
+   |                                       >________| <- 74LS245    |  |
+   |                                                                |  |
+   |___                                               ______________|  |
+       |H H H H H H H H H H H H H H H H H H H H H H H|               | |
+       |U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U|               | |
+                                                                     \|
+
+Legend::
+
+  SL90C65      ARCNET Controller / Transceiver /Logic
+  SW1  1-5:    IRQ Select
+         6:    ET1
+         7:    ET2
+         8:    ROM ENABLE
+  SW2  1-3:    Memory Buffer/PROM Address
+       3-6:    I/O Address Map
+  SW3  1-8:    Node ID Select
+  BNC          BNC RG62/U Connection
+               *I* have had success using RG59B/U with *NO* terminators!
+               What gives?!
+
+SW1: Timeouts, Interrupt and ROM
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To select a hardware interrupt level set one (only one!) of the dip switches
+up (on) SW1...(switches 1-5)
+IRQ3, IRQ4, IRQ5, IRQ7, IRQ2. The Manufacturer's default is IRQ2.
+
+The switches on SW1 labeled EXT1 (switch 6) and EXT2 (switch 7)
+are used to determine the timeout parameters. These two dip switches
+are normally left off (down).
+
+   To enable the 8K Boot PROM position SW1 switch 8 on (UP) labeled ROM.
+   The default is jumper ROM not installed.
+
+
+Setting the I/O Base Address
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The last three switches in switch group SW2 are used to select one
+of eight possible I/O Base addresses using the following table::
+
+
+   Switch | Hex I/O
+   4 5 6  | Address
+   -------|--------
+   0 0 0  |  260
+   0 0 1  |  290
+   0 1 0  |  2E0  (Manufacturer's default)
+   0 1 1  |  2F0
+   1 0 0  |  300
+   1 0 1  |  350
+   1 1 0  |  380
+   1 1 1  |  3E0
+
+
+Setting the Base Memory Address (RAM & ROM)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The memory buffer requires 2K of a 16K block of RAM. The base of this
+16K block can be located in any of eight positions.
+Switches 1-3 of switch group SW2 select the Base of the 16K block.
+(0 = DOWN, 1 = UP)
+I could, however, only verify two settings...
+
+
+::
+
+   Switch| Hex RAM | Hex ROM
+   1 2 3 | Address | Address
+   ------|---------|-----------
+   0 0 0 |  E0000  |  E2000
+   0 0 1 |  D0000  |  D2000  (Manufacturer's default)
+   0 1 0 |  ?????  |  ?????
+   0 1 1 |  ?????  |  ?????
+   1 0 0 |  ?????  |  ?????
+   1 0 1 |  ?????  |  ?????
+   1 1 0 |  ?????  |  ?????
+   1 1 1 |  ?????  |  ?????
+
+
+Setting the Node ID
+^^^^^^^^^^^^^^^^^^^
+
+The eight switches in group SW3 are used to set the node ID.
+Each node attached to the network must have an unique node ID which
+must be different from 0.
+Switch 1 serves as the least significant bit (LSB).
+switches in the DOWN position are OFF (0) and in the UP position are ON (1)
+
+The node ID is the sum of the values of all switches set to "1"
+These values are::
+
+    Switch | Value
+    -------|-------
+      1    |   1
+      2    |   2
+      3    |   4
+      4    |   8
+      5    |  16
+      6    |  32
+      7    |  64
+      8    | 128
+
+Some Examples::
+
+      Switch#     |   Hex   | Decimal
+  8 7 6 5 4 3 2 1 | Node ID | Node ID
+  ----------------|---------|---------
+  0 0 0 0 0 0 0 0 |    not allowed  <-.
+  0 0 0 0 0 0 0 1 |    1    |    1    |
+  0 0 0 0 0 0 1 0 |    2    |    2    |
+  0 0 0 0 0 0 1 1 |    3    |    3    |
+      . . .       |         |         |
+  0 1 0 1 0 1 0 1 |   55    |   85    |
+      . . .       |         |         + Don't use 0 or 255!
+  1 0 1 0 1 0 1 0 |   AA    |  170    |
+      . . .       |         |         |
+  1 1 1 1 1 1 0 1 |   FD    |  253    |
+  1 1 1 1 1 1 1 0 |   FE    |  254    |
+  1 1 1 1 1 1 1 1 |   FF    |  255  <-'
+
+
+Tiara
+=====
+
+(model unknown)
+---------------
+
+  - from Christoph Lameter <christoph@lameter.com>
+
+
+Here is information about my card as far as I could figure it out::
+
+
+  ----------------------------------------------- tiara
+  Tiara LanCard of Tiara Computer Systems.
+
+  +----------------------------------------------+
+  !           ! Transmitter Unit !               !
+  !           +------------------+             -------
+  !          MEM                              Coax Connector
+  !  ROM    7654321 <- I/O                     -------
+  !  :  :   +--------+                           !
+  !  :  :   ! 90C66LJ!                         +++
+  !  :  :   !        !                         !D  Switch to set
+  !  :  :   !        !                         !I  the Nodenumber
+  !  :  :   +--------+                         !P
+  !                                            !++
+  !         234567 <- IRQ                      !
+  +------------!!!!!!!!!!!!!!!!!!!!!!!!--------+
+              !!!!!!!!!!!!!!!!!!!!!!!!
+
+- 0 = Jumper Installed
+- 1 = Open
+
+Top Jumper line Bit 7 = ROM Enable 654=Memory location 321=I/O
+
+Settings for Memory Location (Top Jumper Line)
+
+===     ================
+456     Address selected
+===     ================
+000    C0000
+001     C4000
+010     CC000
+011     D0000
+100     D4000
+101     D8000
+110     DC000
+111     E0000
+===     ================
+
+Settings for I/O Address (Top Jumper Line)
+
+===     ====
+123     Port
+===     ====
+000    260
+001    290
+010    2E0
+011    2F0
+100    300
+101    350
+110    380
+111    3E0
+===     ====
+
+Settings for IRQ Selection (Lower Jumper Line)
+
+====== =====
+234567
+====== =====
+011111 IRQ 2
+101111 IRQ 3
+110111 IRQ 4
+111011 IRQ 5
+111110 IRQ 7
+====== =====
+
+Other Cards
+===========
+
+I have no information on other models of ARCnet cards at the moment.  Please
+send any and all info to:
+
+       apenwarr@worldvisions.ca
+
+Thanks.
diff --git a/Documentation/networking/arcnet-hardware.txt b/Documentation/networking/arcnet-hardware.txt
deleted file mode 100644 (file)
index 731de41..0000000
+++ /dev/null
@@ -1,3133 +0,0 @@
------------------------------------------------------------------------------
-1) This file is a supplement to arcnet.txt.  Please read that for general
-   driver configuration help.
------------------------------------------------------------------------------
-2) This file is no longer Linux-specific.  It should probably be moved out of
-   the kernel sources.  Ideas?
------------------------------------------------------------------------------
-
-Because so many people (myself included) seem to have obtained ARCnet cards
-without manuals, this file contains a quick introduction to ARCnet hardware,
-some cabling tips, and a listing of all jumper settings I can find. Please
-e-mail apenwarr@worldvisions.ca with any settings for your particular card,
-or any other information you have!
-
-
-INTRODUCTION TO ARCNET
-----------------------
-
-ARCnet is a network type which works in a way similar to popular Ethernet
-networks but which is also different in some very important ways.
-
-First of all, you can get ARCnet cards in at least two speeds: 2.5 Mbps
-(slower than Ethernet) and 100 Mbps (faster than normal Ethernet).  In fact,
-there are others as well, but these are less common.  The different hardware
-types, as far as I'm aware, are not compatible and so you cannot wire a
-100 Mbps card to a 2.5 Mbps card, and so on.  From what I hear, my driver does
-work with 100 Mbps cards, but I haven't been able to verify this myself,
-since I only have the 2.5 Mbps variety.  It is probably not going to saturate
-your 100 Mbps card.  Stop complaining. :)
-
-You also cannot connect an ARCnet card to any kind of Ethernet card and
-expect it to work.  
-
-There are two "types" of ARCnet - STAR topology and BUS topology.  This
-refers to how the cards are meant to be wired together.  According to most
-available documentation, you can only connect STAR cards to STAR cards and
-BUS cards to BUS cards.  That makes sense, right?  Well, it's not quite
-true; see below under "Cabling."
-
-Once you get past these little stumbling blocks, ARCnet is actually quite a
-well-designed standard.  It uses something called "modified token passing"
-which makes it completely incompatible with so-called "Token Ring" cards,
-but which makes transfers much more reliable than Ethernet does.  In fact,
-ARCnet will guarantee that a packet arrives safely at the destination, and
-even if it can't possibly be delivered properly (ie. because of a cable
-break, or because the destination computer does not exist) it will at least
-tell the sender about it.
-
-Because of the carefully defined action of the "token", it will always make
-a pass around the "ring" within a maximum length of time.  This makes it
-useful for realtime networks.
-
-In addition, all known ARCnet cards have an (almost) identical programming
-interface.  This means that with one ARCnet driver you can support any
-card, whereas with Ethernet each manufacturer uses what is sometimes a
-completely different programming interface, leading to a lot of different,
-sometimes very similar, Ethernet drivers.  Of course, always using the same
-programming interface also means that when high-performance hardware
-facilities like PCI bus mastering DMA appear, it's hard to take advantage of
-them.  Let's not go into that.
-
-One thing that makes ARCnet cards difficult to program for, however, is the
-limit on their packet sizes; standard ARCnet can only send packets that are
-up to 508 bytes in length.  This is smaller than the Internet "bare minimum"
-of 576 bytes, let alone the Ethernet MTU of 1500.  To compensate, an extra
-level of encapsulation is defined by RFC1201, which I call "packet
-splitting," that allows "virtual packets" to grow as large as 64K each,
-although they are generally kept down to the Ethernet-style 1500 bytes.
-
-For more information on the advantages and disadvantages (mostly the
-advantages) of ARCnet networks, you might try the "ARCnet Trade Association"
-WWW page:
-       http://www.arcnet.com
-
-
-CABLING ARCNET NETWORKS
------------------------
-
-This section was rewritten by 
-        Vojtech Pavlik     <vojtech@suse.cz>
-using information from several people, including:
-        Avery Pennraun     <apenwarr@worldvisions.ca>
-       Stephen A. Wood    <saw@hallc1.cebaf.gov>
-       John Paul Morrison <jmorriso@bogomips.ee.ubc.ca>
-       Joachim Koenig     <jojo@repas.de>
-and Avery touched it up a bit, at Vojtech's request.
-
-ARCnet (the classic 2.5 Mbps version) can be connected by two different
-types of cabling: coax and twisted pair.  The other ARCnet-type networks
-(100 Mbps TCNS and 320 kbps - 32 Mbps ARCnet Plus) use different types of
-cabling (Type1, Fiber, C1, C4, C5).
-
-For a coax network, you "should" use 93 Ohm RG-62 cable.  But other cables
-also work fine, because ARCnet is a very stable network. I personally use 75
-Ohm TV antenna cable.
-
-Cards for coax cabling are shipped in two different variants: for BUS and
-STAR network topologies.  They are mostly the same.  The only difference
-lies in the hybrid chip installed.  BUS cards use high impedance output,
-while STAR use low impedance.  Low impedance card (STAR) is electrically
-equal to a high impedance one with a terminator installed.
-
-Usually, the ARCnet networks are built up from STAR cards and hubs.  There
-are two types of hubs - active and passive.  Passive hubs are small boxes
-with four BNC connectors containing four 47 Ohm resistors:
-
-   |         | wires
-   R         + junction
--R-+-R-      R 47 Ohm resistors
-   R
-   |
-
-The shielding is connected together.  Active hubs are much more complicated;
-they are powered and contain electronics to amplify the signal and send it
-to other segments of the net.  They usually have eight connectors.  Active
-hubs come in two variants - dumb and smart.  The dumb variant just
-amplifies, but the smart one decodes to digital and encodes back all packets
-coming through.  This is much better if you have several hubs in the net,
-since many dumb active hubs may worsen the signal quality.
-
-And now to the cabling.  What you can connect together:
-
-1. A card to a card.  This is the simplest way of creating a 2-computer
-   network.
-
-2. A card to a passive hub.  Remember that all unused connectors on the hub
-   must be properly terminated with 93 Ohm (or something else if you don't
-   have the right ones) terminators.
-       (Avery's note: oops, I didn't know that.  Mine (TV cable) works
-       anyway, though.)
-
-3. A card to an active hub.  Here is no need to terminate the unused
-   connectors except some kind of aesthetic feeling.  But, there may not be
-   more than eleven active hubs between any two computers.  That of course
-   doesn't limit the number of active hubs on the network.
-   
-4. An active hub to another.
-
-5. An active hub to passive hub.
-
-Remember that you cannot connect two passive hubs together.  The power loss
-implied by such a connection is too high for the net to operate reliably.
-
-An example of a typical ARCnet network:
-
-           R                     S - STAR type card              
-    S------H--------A-------S    R - Terminator
-           |        |            H - Hub                         
-           |        |            A - Active hub                  
-           |   S----H----S                                       
-           S        |                                            
-                    |                                            
-                    S                                            
-                                                                          
-The BUS topology is very similar to the one used by Ethernet.  The only
-difference is in cable and terminators: they should be 93 Ohm.  Ethernet
-uses 50 Ohm impedance. You use T connectors to put the computers on a single
-line of cable, the bus. You have to put terminators at both ends of the
-cable. A typical BUS ARCnet network looks like:
-
-    RT----T------T------T------T------TR
-     B    B      B      B      B      B
-
-  B - BUS type card
-  R - Terminator
-  T - T connector
-
-But that is not all! The two types can be connected together.  According to
-the official documentation the only way of connecting them is using an active
-hub:
-
-         A------T------T------TR
-         |      B      B      B
-     S---H---S
-         |
-         S
-
-The official docs also state that you can use STAR cards at the ends of
-BUS network in place of a BUS card and a terminator:
-
-     S------T------T------S
-            B      B
-
-But, according to my own experiments, you can simply hang a BUS type card
-anywhere in middle of a cable in a STAR topology network.  And more - you
-can use the bus card in place of any star card if you use a terminator. Then
-you can build very complicated networks fulfilling all your needs!  An
-example:
-
-                                  S
-                                  |
-           RT------T-------T------H------S
-            B      B       B      |
-                                  |       R
-    S------A------T-------T-------A-------H------TR                    
-           |      B       B       |       |      B                         
-           |   S                 BT       |                                 
-           |   |                  |  S----A-----S
-    S------H---A----S             |       | 
-           |   |      S------T----H---S   |
-           S   S             B    R       S  
-                                                               
-A basically different cabling scheme is used with Twisted Pair cabling. Each
-of the TP cards has two RJ (phone-cord style) connectors.  The cards are
-then daisy-chained together using a cable connecting every two neighboring
-cards.  The ends are terminated with RJ 93 Ohm terminators which plug into
-the empty connectors of cards on the ends of the chain.  An example:
-
-          ___________   ___________
-      _R_|_         _|_|_         _|_R_  
-     |     |       |     |       |     |      
-     |Card |       |Card |       |Card |     
-     |_____|       |_____|       |_____|          
-
-
-There are also hubs for the TP topology.  There is nothing difficult
-involved in using them; you just connect a TP chain to a hub on any end or
-even at both.  This way you can create almost any network configuration. 
-The maximum of 11 hubs between any two computers on the net applies here as
-well.  An example:
-
-    RP-------P--------P--------H-----P------P-----PR
-                               |
-      RP-----H--------P--------H-----P------PR
-             |                 |
-             PR                PR
-
-    R - RJ Terminator
-    P - TP Card
-    H - TP Hub
-
-Like any network, ARCnet has a limited cable length.  These are the maximum
-cable lengths between two active ends (an active end being an active hub or
-a STAR card).
-
-               RG-62       93 Ohm up to 650 m
-               RG-59/U     75 Ohm up to 457 m
-               RG-11/U     75 Ohm up to 533 m
-               IBM Type 1 150 Ohm up to 200 m
-               IBM Type 3 100 Ohm up to 100 m
-
-The maximum length of all cables connected to a passive hub is limited to 65
-meters for RG-62 cabling; less for others.  You can see that using passive
-hubs in a large network is a bad idea. The maximum length of a single "BUS
-Trunk" is about 300 meters for RG-62. The maximum distance between the two
-most distant points of the net is limited to 3000 meters. The maximum length
-of a TP cable between two cards/hubs is 650 meters.
-
-
-SETTING THE JUMPERS
--------------------
-
-All ARCnet cards should have a total of four or five different settings:
-
-  - the I/O address:  this is the "port" your ARCnet card is on.  Probed
-    values in the Linux ARCnet driver are only from 0x200 through 0x3F0. (If
-    your card has additional ones, which is possible, please tell me.) This
-    should not be the same as any other device on your system.  According to
-    a doc I got from Novell, MS Windows prefers values of 0x300 or more,
-    eating net connections on my system (at least) otherwise.  My guess is
-    this may be because, if your card is at 0x2E0, probing for a serial port
-    at 0x2E8 will reset the card and probably mess things up royally.
-       - Avery's favourite: 0x300.
-
-  - the IRQ: on  8-bit cards, it might be 2 (9), 3, 4, 5, or 7.
-             on 16-bit cards, it might be 2 (9), 3, 4, 5, 7, or 10-15.
-             
-    Make sure this is different from any other card on your system.  Note
-    that IRQ2 is the same as IRQ9, as far as Linux is concerned.  You can
-    "cat /proc/interrupts" for a somewhat complete list of which ones are in
-    use at any given time.  Here is a list of common usages from Vojtech
-    Pavlik <vojtech@suse.cz>:
-       ("Not on bus" means there is no way for a card to generate this
-       interrupt)
-       IRQ  0 - Timer 0 (Not on bus)
-       IRQ  1 - Keyboard (Not on bus)
-       IRQ  2 - IRQ Controller 2 (Not on bus, nor does interrupt the CPU)
-       IRQ  3 - COM2
-       IRQ  4 - COM1
-       IRQ  5 - FREE (LPT2 if you have it; sometimes COM3; maybe PLIP)
-       IRQ  6 - Floppy disk controller
-       IRQ  7 - FREE (LPT1 if you don't use the polling driver; PLIP) 
-       IRQ  8 - Realtime Clock Interrupt (Not on bus)
-       IRQ  9 - FREE (VGA vertical sync interrupt if enabled)
-       IRQ 10 - FREE
-       IRQ 11 - FREE
-       IRQ 12 - FREE
-       IRQ 13 - Numeric Coprocessor (Not on bus)
-       IRQ 14 - Fixed Disk Controller
-       IRQ 15 - FREE (Fixed Disk Controller 2 if you have it) 
-       
-       Note: IRQ 9 is used on some video cards for the "vertical retrace"
-       interrupt.  This interrupt would have been handy for things like
-       video games, as it occurs exactly once per screen refresh, but
-       unfortunately IBM cancelled this feature starting with the original
-       VGA and thus many VGA/SVGA cards do not support it.  For this
-       reason, no modern software uses this interrupt and it can almost
-       always be safely disabled, if your video card supports it at all.
-       
-       If your card for some reason CANNOT disable this IRQ (usually there
-       is a jumper), one solution would be to clip the printed circuit
-       contact on the board: it's the fourth contact from the left on the
-       back side.  I take no responsibility if you try this.
-
-       - Avery's favourite: IRQ2 (actually IRQ9).  Watch that VGA, though.
-
-  - the memory address:  Unlike most cards, ARCnets use "shared memory" for
-    copying buffers around.  Make SURE it doesn't conflict with any other
-    used memory in your system!
-       A0000           - VGA graphics memory (ok if you don't have VGA)
-        B0000          - Monochrome text mode
-        C0000          \  One of these is your VGA BIOS - usually C0000.
-        E0000          /
-        F0000          - System BIOS
-
-    Anything less than 0xA0000 is, well, a BAD idea since it isn't above
-    640k.
-       - Avery's favourite: 0xD0000
-
-  - the station address:  Every ARCnet card has its own "unique" network
-    address from 0 to 255.  Unlike Ethernet, you can set this address
-    yourself with a jumper or switch (or on some cards, with special
-    software).  Since it's only 8 bits, you can only have 254 ARCnet cards
-    on a network.  DON'T use 0 or 255, since these are reserved (although
-    neat stuff will probably happen if you DO use them).  By the way, if you
-    haven't already guessed, don't set this the same as any other ARCnet on
-    your network!
-       - Avery's favourite:  3 and 4.  Not that it matters.
-
-  - There may be ETS1 and ETS2 settings.  These may or may not make a
-    difference on your card (many manuals call them "reserved"), but are
-    used to change the delays used when powering up a computer on the
-    network.  This is only necessary when wiring VERY long range ARCnet
-    networks, on the order of 4km or so; in any case, the only real
-    requirement here is that all cards on the network with ETS1 and ETS2
-    jumpers have them in the same position.  Chris Hindy <chrish@io.org>
-    sent in a chart with actual values for this:
-       ET1     ET2     Response Time   Reconfiguration Time
-       ---     ---     -------------   --------------------
-       open    open    74.7us          840us
-       open    closed  283.4us         1680us
-       closed  open    561.8us         1680us
-       closed  closed  1118.6us        1680us
-    
-    Make sure you set ETS1 and ETS2 to the SAME VALUE for all cards on your
-    network.
-    
-Also, on many cards (not mine, though) there are red and green LED's. 
-Vojtech Pavlik <vojtech@suse.cz> tells me this is what they mean:
-       GREEN           RED             Status
-       -----           ---             ------
-       OFF             OFF             Power off
-       OFF             Short flashes   Cabling problems (broken cable or not
-                                         terminated)
-       OFF (short)     ON              Card init
-       ON              ON              Normal state - everything OK, nothing
-                                         happens
-       ON              Long flashes    Data transfer
-       ON              OFF             Never happens (maybe when wrong ID)
-
-
-The following is all the specific information people have sent me about
-their own particular ARCnet cards.  It is officially a mess, and contains
-huge amounts of duplicated information.  I have no time to fix it.  If you
-want to, PLEASE DO!  Just send me a 'diff -u' of all your changes.
-
-The model # is listed right above specifics for that card, so you should be
-able to use your text viewer's "search" function to find the entry you want. 
-If you don't KNOW what kind of card you have, try looking through the
-various diagrams to see if you can tell.
-
-If your model isn't listed and/or has different settings, PLEASE PLEASE
-tell me.  I had to figure mine out without the manual, and it WASN'T FUN!
-
-Even if your ARCnet model isn't listed, but has the same jumpers as another
-model that is, please e-mail me to say so.
-
-Cards Listed in this file (in this order, mostly):
-
-       Manufacturer    Model #                 Bits
-       ------------    -------                 ----
-       SMC             PC100                   8
-       SMC             PC110                   8
-       SMC             PC120                   8
-       SMC             PC130                   8
-       SMC             PC270E                  8
-       SMC             PC500                   16
-       SMC             PC500Longboard          16
-       SMC             PC550Longboard          16
-       SMC             PC600                   16
-       SMC             PC710                   8
-       SMC?            LCS-8830(-T)            8/16
-       Puredata        PDI507                  8
-       CNet Tech       CN120-Series            8
-       CNet Tech       CN160-Series            16
-       Lantech?        UM9065L chipset         8
-       Acer            5210-003                8
-       Datapoint?      LAN-ARC-8               8
-       Topware         TA-ARC/10               8
-       Thomas-Conrad   500-6242-0097 REV A     8
-       Waterloo?       (C)1985 Waterloo Micro. 8
-       No Name         --                      8/16
-       No Name         Taiwan R.O.C?           8
-       No Name         Model 9058              8
-       Tiara           Tiara Lancard?          8
-       
-
-** SMC = Standard Microsystems Corp.
-** CNet Tech = CNet Technology, Inc.
-
-
-Unclassified Stuff
-------------------
-  - Please send any other information you can find.
-  
-  - And some other stuff (more info is welcome!):
-     From: root@ultraworld.xs4all.nl (Timo Hilbrink)
-     To: apenwarr@foxnet.net (Avery Pennarun)
-     Date: Wed, 26 Oct 1994 02:10:32 +0000 (GMT)
-     Reply-To: timoh@xs4all.nl
-
-     [...parts deleted...]
-
-     About the jumpers: On my PC130 there is one more jumper, located near the
-     cable-connector and it's for changing to star or bus topology; 
-     closed: star - open: bus
-     On the PC500 are some more jumper-pins, one block labeled with RX,PDN,TXI
-     and another with ALE,LA17,LA18,LA19 these are undocumented..
-
-     [...more parts deleted...]
-
-     --- CUT ---
-
-
-** Standard Microsystems Corp (SMC) **
-PC100, PC110, PC120, PC130 (8-bit cards)
-PC500, PC600 (16-bit cards)
----------------------------------
-  - mainly from Avery Pennarun <apenwarr@worldvisions.ca>.  Values depicted
-    are from Avery's setup.
-  - special thanks to Timo Hilbrink <timoh@xs4all.nl> for noting that PC120,
-    130, 500, and 600 all have the same switches as Avery's PC100. 
-    PC500/600 have several extra, undocumented pins though. (?)
-  - PC110 settings were verified by Stephen A. Wood <saw@cebaf.gov>
-  - Also, the JP- and S-numbers probably don't match your card exactly.  Try
-    to find jumpers/switches with the same number of settings - it's
-    probably more reliable.
-  
-
-     JP5                      [|]    :    :    :    :
-(IRQ Setting)                IRQ2  IRQ3 IRQ4 IRQ5 IRQ7
-               Put exactly one jumper on exactly one set of pins.
-
-
-                          1  2   3  4  5  6   7  8  9 10
-     S1                /----------------------------------\
-(I/O and Memory        |  1  1 * 0  0  0  0 * 1  1  0  1  |
- addresses)            \----------------------------------/
-                          |--|   |--------|   |--------|
-                          (a)       (b)           (m)
-                          
-                WARNING.  It's very important when setting these which way
-                you're holding the card, and which way you think is '1'!
-                
-                If you suspect that your settings are not being made
-               correctly, try reversing the direction or inverting the
-               switch positions.
-
-               a: The first digit of the I/O address.
-                       Setting         Value
-                       -------         -----
-                       00              0
-                       01              1
-                       10              2
-                       11              3
-
-               b: The second digit of the I/O address.
-                       Setting         Value
-                       -------         -----
-                       0000            0
-                       0001            1
-                       0010            2
-                       ...             ...
-                       1110            E
-                       1111            F
-
-               The I/O address is in the form ab0.  For example, if
-               a is 0x2 and b is 0xE, the address will be 0x2E0.
-
-               DO NOT SET THIS LESS THAN 0x200!!!!!
-
-
-               m: The first digit of the memory address.
-                       Setting         Value
-                       -------         -----
-                       0000            0
-                       0001            1
-                       0010            2
-                       ...             ...
-                       1110            E
-                       1111            F
-
-               The memory address is in the form m0000.  For example, if
-               m is D, the address will be 0xD0000.
-
-               DO NOT SET THIS TO C0000, F0000, OR LESS THAN A0000!
-
-                          1  2  3  4  5  6  7  8
-     S2                /--------------------------\
-(Station Address)      |  1  1  0  0  0  0  0  0  |
-                       \--------------------------/
-
-                       Setting         Value
-                       -------         -----
-                       00000000        00
-                       10000000        01
-                       01000000        02
-                       ...
-                       01111111        FE
-                       11111111        FF
-
-               Note that this is binary with the digits reversed!
-
-               DO NOT SET THIS TO 0 OR 255 (0xFF)!
-
-
-*****************************************************************************
-
-** Standard Microsystems Corp (SMC) **
-PC130E/PC270E (8-bit cards)
----------------------------
-  - from Juergen Seifert <seifert@htwm.de>
-
-
-STANDARD MICROSYSTEMS CORPORATION (SMC) ARCNET(R)-PC130E/PC270E
-===============================================================
-
-This description has been written by Juergen Seifert <seifert@htwm.de>
-using information from the following Original SMC Manual 
-
-             "Configuration Guide for
-             ARCNET(R)-PC130E/PC270
-            Network Controller Boards
-                Pub. # 900.044A
-                   June, 1989"
-
-ARCNET is a registered trademark of the Datapoint Corporation
-SMC is a registered trademark of the Standard Microsystems Corporation  
-
-The PC130E is an enhanced version of the PC130 board, is equipped with a 
-standard BNC female connector for connection to RG-62/U coax cable.
-Since this board is designed both for point-to-point connection in star
-networks and for connection to bus networks, it is downwardly compatible 
-with all the other standard boards designed for coax networks (that is,
-the PC120, PC110 and PC100 star topology boards and the PC220, PC210 and 
-PC200 bus topology boards).
-
-The PC270E is an enhanced version of the PC260 board, is equipped with two 
-modular RJ11-type jacks for connection to twisted pair wiring.
-It can be used in a star or a daisy-chained network.
-
-
-         8 7 6 5 4 3 2 1
-    ________________________________________________________________
-   |   |       S1        |                                          |
-   |   |_________________|                                          |
-   |    Offs|Base |I/O Addr                                         |
-   |     RAM Addr |                                              ___|
-   |         ___  ___                                       CR3 |___|
-   |        |   \/   |                                      CR4 |___|
-   |        |  PROM  |                                           ___|
-   |        |        |                                        N |   | 8
-   |        | SOCKET |                                        o |   | 7
-   |        |________|                                        d |   | 6
-   |                   ___________________                    e |   | 5
-   |                  |                   |                   A | S | 4
-   |       |oo| EXT2  |                   |                   d | 2 | 3
-   |       |oo| EXT1  |       SMC         |                   d |   | 2
-   |       |oo| ROM   |      90C63        |                   r |___| 1
-   |       |oo| IRQ7  |                   |               |o|  _____|
-   |       |oo| IRQ5  |                   |               |o| | J1  |
-   |       |oo| IRQ4  |                   |              STAR |_____|
-   |       |oo| IRQ3  |                   |                   | J2  |
-   |       |oo| IRQ2  |___________________|                   |_____|
-   |___                                               ______________|
-       |                                             |
-       |_____________________________________________|
-
-Legend:
-
-SMC 90C63      ARCNET Controller / Transceiver /Logic
-S1     1-3:    I/O Base Address Select
-       4-6:    Memory Base Address Select
-       7-8:    RAM Offset Select
-S2     1-8:    Node ID Select
-EXT            Extended Timeout Select
-ROM            ROM Enable Select
-STAR           Selected - Star Topology        (PC130E only)
-               Deselected - Bus Topology       (PC130E only)
-CR3/CR4                Diagnostic LEDs
-J1             BNC RG62/U Connector            (PC130E only)
-J1             6-position Telephone Jack       (PC270E only)
-J2             6-position Telephone Jack       (PC270E only)
-
-Setting one of the switches to Off/Open means "1", On/Closed means "0".
-
-
-Setting the Node ID
--------------------
-
-The eight switches in group S2 are used to set the node ID.
-These switches work in a way similar to the PC100-series cards; see that
-entry for more information.
-
-
-Setting the I/O Base Address
-----------------------------
-
-The first three switches in switch group S1 are used to select one
-of eight possible I/O Base addresses using the following table
-
-
-   Switch | Hex I/O
-   1 2 3  | Address
-   -------|--------
-   0 0 0  |  260
-   0 0 1  |  290
-   0 1 0  |  2E0  (Manufacturer's default)
-   0 1 1  |  2F0
-   1 0 0  |  300
-   1 0 1  |  350
-   1 1 0  |  380
-   1 1 1  |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
---------------------------------------------
-
-The memory buffer requires 2K of a 16K block of RAM. The base of this
-16K block can be located in any of eight positions.
-Switches 4-6 of switch group S1 select the Base of the 16K block.
-Within that 16K address space, the buffer may be assigned any one of four 
-positions, determined by the offset, switches 7 and 8 of group S1.
-
-   Switch     | Hex RAM | Hex ROM
-   4 5 6  7 8 | Address | Address *)
-   -----------|---------|-----------
-   0 0 0  0 0 |  C0000  |  C2000
-   0 0 0  0 1 |  C0800  |  C2000
-   0 0 0  1 0 |  C1000  |  C2000
-   0 0 0  1 1 |  C1800  |  C2000
-              |         |
-   0 0 1  0 0 |  C4000  |  C6000
-   0 0 1  0 1 |  C4800  |  C6000
-   0 0 1  1 0 |  C5000  |  C6000
-   0 0 1  1 1 |  C5800  |  C6000
-              |         |
-   0 1 0  0 0 |  CC000  |  CE000
-   0 1 0  0 1 |  CC800  |  CE000
-   0 1 0  1 0 |  CD000  |  CE000
-   0 1 0  1 1 |  CD800  |  CE000
-              |         |
-   0 1 1  0 0 |  D0000  |  D2000  (Manufacturer's default)
-   0 1 1  0 1 |  D0800  |  D2000
-   0 1 1  1 0 |  D1000  |  D2000
-   0 1 1  1 1 |  D1800  |  D2000
-              |         |
-   1 0 0  0 0 |  D4000  |  D6000
-   1 0 0  0 1 |  D4800  |  D6000
-   1 0 0  1 0 |  D5000  |  D6000
-   1 0 0  1 1 |  D5800  |  D6000
-              |         |
-   1 0 1  0 0 |  D8000  |  DA000
-   1 0 1  0 1 |  D8800  |  DA000
-   1 0 1  1 0 |  D9000  |  DA000
-   1 0 1  1 1 |  D9800  |  DA000
-              |         |
-   1 1 0  0 0 |  DC000  |  DE000
-   1 1 0  0 1 |  DC800  |  DE000
-   1 1 0  1 0 |  DD000  |  DE000
-   1 1 0  1 1 |  DD800  |  DE000
-              |         |
-   1 1 1  0 0 |  E0000  |  E2000
-   1 1 1  0 1 |  E0800  |  E2000
-   1 1 1  1 0 |  E1000  |  E2000
-   1 1 1  1 1 |  E1800  |  E2000
-  
-*) To enable the 8K Boot PROM install the jumper ROM.
-   The default is jumper ROM not installed.
-
-
-Setting the Timeouts and Interrupt
-----------------------------------
-
-The jumpers labeled EXT1 and EXT2 are used to determine the timeout 
-parameters. These two jumpers are normally left open.
-
-To select a hardware interrupt level set one (only one!) of the jumpers
-IRQ2, IRQ3, IRQ4, IRQ5, IRQ7. The Manufacturer's default is IRQ2.
-
-Configuring the PC130E for Star or Bus Topology
------------------------------------------------
-
-The single jumper labeled STAR is used to configure the PC130E board for 
-star or bus topology.
-When the jumper is installed, the board may be used in a star network, when 
-it is removed, the board can be used in a bus topology.
-
-
-Diagnostic LEDs
----------------
-
-Two diagnostic LEDs are visible on the rear bracket of the board.
-The green LED monitors the network activity: the red one shows the
-board activity:
-
- Green  | Status               Red      | Status
- -------|-------------------   ---------|-------------------
-  on    | normal activity      flash/on | data transfer
-  blink | reconfiguration      off      | no data transfer;
-  off   | defective board or            | incorrect memory or
-        | node ID is zero               | I/O address
-
-
-*****************************************************************************
-
-** Standard Microsystems Corp (SMC) **
-PC500/PC550 Longboard (16-bit cards)
--------------------------------------
-  - from Juergen Seifert <seifert@htwm.de>
-
-
-STANDARD MICROSYSTEMS CORPORATION (SMC) ARCNET-PC500/PC550 Long Board
-=====================================================================
-
-Note: There is another Version of the PC500 called Short Version, which 
-      is different in hard- and software! The most important differences
-      are:
-      - The long board has no Shared memory.
-      - On the long board the selection of the interrupt is done by binary
-        coded switch, on the short board directly by jumper.
-        
-[Avery's note: pay special attention to that: the long board HAS NO SHARED
-MEMORY.  This means the current Linux-ARCnet driver can't use these cards. 
-I have obtained a PC500Longboard and will be doing some experiments on it in
-the future, but don't hold your breath.  Thanks again to Juergen Seifert for
-his advice about this!]
-
-This description has been written by Juergen Seifert <seifert@htwm.de>
-using information from the following Original SMC Manual 
-
-             "Configuration Guide for
-             SMC ARCNET-PC500/PC550
-         Series Network Controller Boards
-             Pub. # 900.033 Rev. A
-                November, 1989"
-
-ARCNET is a registered trademark of the Datapoint Corporation
-SMC is a registered trademark of the Standard Microsystems Corporation  
-
-The PC500 is equipped with a standard BNC female connector for connection
-to RG-62/U coax cable.
-The board is designed both for point-to-point connection in star networks
-and for connection to bus networks.
-
-The PC550 is equipped with two modular RJ11-type jacks for connection
-to twisted pair wiring.
-It can be used in a star or a daisy-chained (BUS) network.
-
-       1 
-       0 9 8 7 6 5 4 3 2 1     6 5 4 3 2 1
-    ____________________________________________________________________
-   < |         SW1         | |     SW2     |                            |
-   > |_____________________| |_____________|                            |
-   <   IRQ    |I/O Addr                                                 |
-   >                                                                 ___|
-   <                                                            CR4 |___|
-   >                                                            CR3 |___|
-   <                                                                 ___|
-   >                                                              N |   | 8
-   <                                                              o |   | 7
-   >                                                              d | S | 6
-   <                                                              e | W | 5
-   >                                                              A | 3 | 4
-   <                                                              d |   | 3
-   >                                                              d |   | 2
-   <                                                              r |___| 1
-   >                                                        |o|    _____|
-   <                                                        |o|   | J1  |
-   >  3 1                                                   JP6   |_____|
-   < |o|o| JP2                                                    | J2  |
-   > |o|o|                                                        |_____|
-   <  4 2__                                               ______________|
-   >    |  |                                             |
-   <____|  |_____________________________________________|
-
-Legend:
-
-SW1    1-6:    I/O Base Address Select
-       7-10:   Interrupt Select
-SW2    1-6:    Reserved for Future Use
-SW3    1-8:    Node ID Select
-JP2    1-4:    Extended Timeout Select
-JP6            Selected - Star Topology        (PC500 only)
-               Deselected - Bus Topology       (PC500 only)
-CR3    Green   Monitors Network Activity
-CR4    Red     Monitors Board Activity
-J1             BNC RG62/U Connector            (PC500 only)
-J1             6-position Telephone Jack       (PC550 only)
-J2             6-position Telephone Jack       (PC550 only)
-
-Setting one of the switches to Off/Open means "1", On/Closed means "0".
-
-
-Setting the Node ID
--------------------
-
-The eight switches in group SW3 are used to set the node ID. Each node
-attached to the network must have an unique node ID which must be 
-different from 0.
-Switch 1 serves as the least significant bit (LSB).
-
-The node ID is the sum of the values of all switches set to "1"  
-These values are:
-
-    Switch | Value
-    -------|-------
-      1    |   1
-      2    |   2
-      3    |   4
-      4    |   8
-      5    |  16
-      6    |  32
-      7    |  64
-      8    | 128
-
-Some Examples:
-
-    Switch         | Hex     | Decimal 
-   8 7 6 5 4 3 2 1 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1 
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |  
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255 
-
-
-Setting the I/O Base Address
-----------------------------
-
-The first six switches in switch group SW1 are used to select one
-of 32 possible I/O Base addresses using the following table
-
-   Switch       | Hex I/O
-   6 5  4 3 2 1 | Address
-   -------------|--------
-   0 1  0 0 0 0 |  200
-   0 1  0 0 0 1 |  210
-   0 1  0 0 1 0 |  220
-   0 1  0 0 1 1 |  230
-   0 1  0 1 0 0 |  240
-   0 1  0 1 0 1 |  250
-   0 1  0 1 1 0 |  260
-   0 1  0 1 1 1 |  270
-   0 1  1 0 0 0 |  280
-   0 1  1 0 0 1 |  290
-   0 1  1 0 1 0 |  2A0
-   0 1  1 0 1 1 |  2B0
-   0 1  1 1 0 0 |  2C0
-   0 1  1 1 0 1 |  2D0
-   0 1  1 1 1 0 |  2E0 (Manufacturer's default)
-   0 1  1 1 1 1 |  2F0
-   1 1  0 0 0 0 |  300
-   1 1  0 0 0 1 |  310
-   1 1  0 0 1 0 |  320
-   1 1  0 0 1 1 |  330
-   1 1  0 1 0 0 |  340
-   1 1  0 1 0 1 |  350
-   1 1  0 1 1 0 |  360
-   1 1  0 1 1 1 |  370
-   1 1  1 0 0 0 |  380
-   1 1  1 0 0 1 |  390
-   1 1  1 0 1 0 |  3A0
-   1 1  1 0 1 1 |  3B0
-   1 1  1 1 0 0 |  3C0
-   1 1  1 1 0 1 |  3D0
-   1 1  1 1 1 0 |  3E0
-   1 1  1 1 1 1 |  3F0
-
-
-Setting the Interrupt
----------------------
-
-Switches seven through ten of switch group SW1 are used to select the 
-interrupt level. The interrupt level is binary coded, so selections 
-from 0 to 15 would be possible, but only the following eight values will
-be supported: 3, 4, 5, 7, 9, 10, 11, 12.
-
-   Switch   | IRQ
-   10 9 8 7 | 
-   ---------|-------- 
-    0 0 1 1 |  3
-    0 1 0 0 |  4
-    0 1 0 1 |  5
-    0 1 1 1 |  7
-    1 0 0 1 |  9 (=2) (default)
-    1 0 1 0 | 10
-    1 0 1 1 | 11
-    1 1 0 0 | 12
-
-
-Setting the Timeouts 
---------------------
-
-The two jumpers JP2 (1-4) are used to determine the timeout parameters. 
-These two jumpers are normally left open.
-Refer to the COM9026 Data Sheet for alternate configurations.
-
-
-Configuring the PC500 for Star or Bus Topology
-----------------------------------------------
-
-The single jumper labeled JP6 is used to configure the PC500 board for 
-star or bus topology.
-When the jumper is installed, the board may be used in a star network, when 
-it is removed, the board can be used in a bus topology.
-
-
-Diagnostic LEDs
----------------
-
-Two diagnostic LEDs are visible on the rear bracket of the board.
-The green LED monitors the network activity: the red one shows the
-board activity:
-
- Green  | Status               Red      | Status
- -------|-------------------   ---------|-------------------
-  on    | normal activity      flash/on | data transfer
-  blink | reconfiguration      off      | no data transfer;
-  off   | defective board or            | incorrect memory or
-        | node ID is zero               | I/O address
-
-
-*****************************************************************************
-
-** SMC **
-PC710 (8-bit card)
-------------------
-  - from J.S. van Oosten <jvoosten@compiler.tdcnet.nl>
-  
-Note: this data is gathered by experimenting and looking at info of other
-cards. However, I'm sure I got 99% of the settings right.
-
-The SMC710 card resembles the PC270 card, but is much more basic (i.e. no
-LEDs, RJ11 jacks, etc.) and 8 bit. Here's a little drawing:
-
-    _______________________________________   
-   | +---------+  +---------+              |____
-   | |   S2    |  |   S1    |              |
-   | +---------+  +---------+              |
-   |                                       |
-   |  +===+    __                          |
-   |  | R |   |  | X-tal                 ###___
-   |  | O |   |__|                      ####__'|
-   |  | M |    ||                        ###
-   |  +===+                                |
-   |                                       |
-   |   .. JP1   +----------+               |
-   |   ..       | big chip |               |   
-   |   ..       |  90C63   |               |
-   |   ..       |          |               |
-   |   ..       +----------+               |
-    -------                     -----------
-           |||||||||||||||||||||
-
-The row of jumpers at JP1 actually consists of 8 jumpers, (sometimes
-labelled) the same as on the PC270, from top to bottom: EXT2, EXT1, ROM,
-IRQ7, IRQ5, IRQ4, IRQ3, IRQ2 (gee, wonder what they would do? :-) )
-
-S1 and S2 perform the same function as on the PC270, only their numbers
-are swapped (S1 is the nodeaddress, S2 sets IO- and RAM-address).
-
-I know it works when connected to a PC110 type ARCnet board.
-
-       
-*****************************************************************************
-
-** Possibly SMC **
-LCS-8830(-T) (8 and 16-bit cards)
----------------------------------
-  - from Mathias Katzer <mkatzer@HRZ.Uni-Bielefeld.DE>
-  - Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl> says the
-    LCS-8830 is slightly different from LCS-8830-T.  These are 8 bit, BUS
-    only (the JP0 jumper is hardwired), and BNC only.
-       
-This is a LCS-8830-T made by SMC, I think ('SMC' only appears on one PLCC,
-nowhere else, not even on the few Xeroxed sheets from the manual).
-
-SMC ARCnet Board Type LCS-8830-T
-
-   ------------------------------------
-  |                                    |
-  |              JP3 88  8 JP2         |
-  |       #####      | \               |
-  |       #####    ET1 ET2          ###|
-  |                              8  ###|
-  |  U3   SW 1                  JP0 ###|  Phone Jacks
-  |  --                             ###|
-  | |  |                               |
-  | |  |   SW2                         |
-  | |  |                               |
-  | |  |  #####                        |
-  |  --   #####                       ####  BNC Connector 
-  |                                   ####
-  |   888888 JP1                       |
-  |   234567                           |
-   --                           -------
-     |||||||||||||||||||||||||||
-      --------------------------
-
-
-SW1: DIP-Switches for Station Address
-SW2: DIP-Switches for Memory Base and I/O Base addresses
-
-JP0: If closed, internal termination on (default open)
-JP1: IRQ Jumpers
-JP2: Boot-ROM enabled if closed
-JP3: Jumpers for response timeout
-U3: Boot-ROM Socket          
-
-
-ET1 ET2     Response Time     Idle Time    Reconfiguration Time
-
-               78                86               840
- X            285               316              1680
-     X        563               624              1680
- X   X       1130              1237              1680
-
-(X means closed jumper)
-
-(DIP-Switch downwards means "0")
-
-The station address is binary-coded with SW1.
-
-The I/O base address is coded with DIP-Switches 6,7 and 8 of SW2:
-
-Switches        Base
-678             Address
-000            260-26f
-100            290-29f
-010            2e0-2ef
-110            2f0-2ff
-001            300-30f
-101            350-35f
-011            380-38f
-111            3e0-3ef
-
-
-DIP Switches 1-5 of SW2 encode the RAM and ROM Address Range:
-
-Switches        RAM           ROM
-12345           Address Range  Address Range
-00000          C:0000-C:07ff   C:2000-C:3fff
-10000          C:0800-C:0fff
-01000          C:1000-C:17ff
-11000          C:1800-C:1fff
-00100          C:4000-C:47ff   C:6000-C:7fff
-10100          C:4800-C:4fff
-01100          C:5000-C:57ff 
-11100          C:5800-C:5fff
-00010          C:C000-C:C7ff   C:E000-C:ffff
-10010          C:C800-C:Cfff
-01010          C:D000-C:D7ff
-11010          C:D800-C:Dfff
-00110          D:0000-D:07ff   D:2000-D:3fff
-10110          D:0800-D:0fff
-01110          D:1000-D:17ff
-11110          D:1800-D:1fff
-00001          D:4000-D:47ff   D:6000-D:7fff
-10001          D:4800-D:4fff
-01001          D:5000-D:57ff
-11001          D:5800-D:5fff
-00101          D:8000-D:87ff   D:A000-D:bfff
-10101          D:8800-D:8fff
-01101          D:9000-D:97ff
-11101          D:9800-D:9fff 
-00011          D:C000-D:c7ff   D:E000-D:ffff
-10011          D:C800-D:cfff
-01011          D:D000-D:d7ff
-11011          D:D800-D:dfff
-00111          E:0000-E:07ff   E:2000-E:3fff
-10111          E:0800-E:0fff
-01111          E:1000-E:17ff
-11111          E:1800-E:1fff
-
-
-*****************************************************************************
-
-** PureData Corp **
-PDI507 (8-bit card)
---------------------
-  - from Mark Rejhon <mdrejhon@magi.com> (slight modifications by Avery)
-  - Avery's note: I think PDI508 cards (but definitely NOT PDI508Plus cards)
-    are mostly the same as this.  PDI508Plus cards appear to be mainly
-    software-configured.
-
-Jumpers:
-       There is a jumper array at the bottom of the card, near the edge
-        connector.  This array is labelled J1.  They control the IRQs and
-        something else.  Put only one jumper on the IRQ pins.
-
-       ETS1, ETS2 are for timing on very long distance networks.  See the
-       more general information near the top of this file.
-
-       There is a J2 jumper on two pins.  A jumper should be put on them,
-        since it was already there when I got the card.  I don't know what
-        this jumper is for though.
-
-       There is a two-jumper array for J3.  I don't know what it is for,
-        but there were already two jumpers on it when I got the card.  It's
-        a six pin grid in a two-by-three fashion.  The jumpers were
-        configured as follows:
-
-          .-------.
-        o | o   o |
-          :-------:    ------> Accessible end of card with connectors
-        o | o   o |             in this direction ------->
-          `-------'
-
-Carl de Billy <CARL@carainfo.com> explains J3 and J4:
-
-       J3 Diagram:
-
-           .-------.
-         o | o   o |
-           :-------:    TWIST Technology
-         o | o   o |
-           `-------'
-           .-------.
-           | o   o | o
-           :-------:    COAX Technology
-           | o   o | o
-           `-------'
-
-  - If using coax cable in a bus topology the J4 jumper must be removed;
-    place it on one pin.
-
-  - If using bus topology with twisted pair wiring move the J3 
-    jumpers so they connect the middle pin and the pins closest to the RJ11
-    Connectors.  Also the J4 jumper must be removed; place it on one pin of
-    J4 jumper for storage.
-
-  - If using  star topology with twisted pair wiring move the J3 
-    jumpers so they connect the middle pin and the pins closest to the RJ11
-    connectors.
-
-
-DIP Switches:
-
-       The DIP switches accessible on the accessible end of the card while
-        it is installed, is used to set the ARCnet address.  There are 8
-        switches.  Use an address from 1 to 254.
-
-       Switch No.
-       12345678        ARCnet address
-       -----------------------------------------
-       00000000        FF      (Don't use this!)
-       00000001        FE
-       00000010        FD
-       ....
-       11111101        2       
-       11111110        1
-       11111111        0       (Don't use this!)
-
-       There is another array of eight DIP switches at the top of the
-        card.  There are five labelled MS0-MS4 which seem to control the
-        memory address, and another three labelled IO0-IO2 which seem to
-        control the base I/O address of the card.
-
-       This was difficult to test by trial and error, and the I/O addresses
-        are in a weird order.  This was tested by setting the DIP switches,
-        rebooting the computer, and attempting to load ARCETHER at various
-        addresses (mostly between 0x200 and 0x400).  The address that caused
-        the red transmit LED to blink, is the one that I thought works.
-
-       Also, the address 0x3D0 seem to have a special meaning, since the
-        ARCETHER packet driver loaded fine, but without the red LED
-        blinking.  I don't know what 0x3D0 is for though.  I recommend using
-        an address of 0x300 since Windows may not like addresses below
-        0x300.
-
-       IO Switch No.
-       210             I/O address
-       -------------------------------
-       111             0x260
-       110             0x290
-       101             0x2E0
-       100             0x2F0
-       011             0x300
-       010             0x350
-       001             0x380
-       000             0x3E0
-
-       The memory switches set a reserved address space of 0x1000 bytes
-        (0x100 segment units, or 4k).  For example if I set an address of
-        0xD000, it will use up addresses 0xD000 to 0xD100.
-
-       The memory switches were tested by booting using QEMM386 stealth,
-        and using LOADHI to see what address automatically became excluded
-        from the upper memory regions, and then attempting to load ARCETHER
-        using these addresses.
-
-       I recommend using an ARCnet memory address of 0xD000, and putting
-        the EMS page frame at 0xC000 while using QEMM stealth mode.  That
-        way, you get contiguous high memory from 0xD100 almost all the way
-        the end of the megabyte.
-
-       Memory Switch 0 (MS0) didn't seem to work properly when set to OFF
-        on my card.  It could be malfunctioning on my card.  Experiment with
-        it ON first, and if it doesn't work, set it to OFF.  (It may be a
-        modifier for the 0x200 bit?)
-
-       MS Switch No.
-       43210           Memory address
-       --------------------------------
-       00001           0xE100  (guessed - was not detected by QEMM)
-       00011           0xE000  (guessed - was not detected by QEMM)
-       00101           0xDD00
-       00111           0xDC00
-       01001           0xD900
-       01011           0xD800
-       01101           0xD500
-       01111           0xD400
-       10001           0xD100
-       10011           0xD000
-       10101           0xCD00
-       10111           0xCC00
-       11001           0xC900 (guessed - crashes tested system)
-       11011           0xC800 (guessed - crashes tested system)
-       11101           0xC500 (guessed - crashes tested system)
-       11111           0xC400 (guessed - crashes tested system)
-       
-       
-*****************************************************************************
-
-** CNet Technology Inc. **
-120 Series (8-bit cards)
-------------------------
-  - from Juergen Seifert <seifert@htwm.de>
-
-
-CNET TECHNOLOGY INC. (CNet) ARCNET 120A SERIES
-==============================================
-
-This description has been written by Juergen Seifert <seifert@htwm.de>
-using information from the following Original CNet Manual 
-
-              "ARCNET
-            USER'S MANUAL 
-                for
-               CN120A
-               CN120AB
-               CN120TP
-               CN120ST
-               CN120SBT
-             P/N:12-01-0007
-             Revision 3.00"
-
-ARCNET is a registered trademark of the Datapoint Corporation
-
-P/N 120A   ARCNET 8 bit XT/AT Star
-P/N 120AB  ARCNET 8 bit XT/AT Bus
-P/N 120TP  ARCNET 8 bit XT/AT Twisted Pair
-P/N 120ST  ARCNET 8 bit XT/AT Star, Twisted Pair
-P/N 120SBT ARCNET 8 bit XT/AT Star, Bus, Twisted Pair
-
-    __________________________________________________________________
-   |                                                                  |
-   |                                                               ___|
-   |                                                          LED |___|
-   |                                                               ___|
-   |                                                            N |   | ID7
-   |                                                            o |   | ID6
-   |                                                            d | S | ID5
-   |                                                            e | W | ID4
-   |                     ___________________                    A | 2 | ID3
-   |                    |                   |                   d |   | ID2
-   |                    |                   |  1 2 3 4 5 6 7 8  d |   | ID1
-   |                    |                   | _________________ r |___| ID0
-   |                    |      90C65        ||       SW1       |  ____|
-   |  JP 8 7            |                   ||_________________| |    |
-   |    |o|o|  JP1      |                   |                    | J2 |
-   |    |o|o|  |oo|     |                   |         JP 1 1 1   |    |
-   |   ______________   |                   |            0 1 2   |____|
-   |  |  PROM        |  |___________________|           |o|o|o|  _____|
-   |  >  SOCKET      |  JP 6 5 4 3 2                    |o|o|o| | J1  |
-   |  |______________|    |o|o|o|o|o|                   |o|o|o| |_____|
-   |_____                 |o|o|o|o|o|                   ______________|
-         |                                             |
-         |_____________________________________________|
-
-Legend:
-
-90C65       ARCNET Probe
-S1  1-5:    Base Memory Address Select
-    6-8:    Base I/O Address Select
-S2  1-8:    Node ID Select (ID0-ID7)
-JP1     ROM Enable Select
-JP2     IRQ2
-JP3     IRQ3
-JP4     IRQ4
-JP5     IRQ5
-JP6     IRQ7
-JP7/JP8     ET1, ET2 Timeout Parameters
-JP10/JP11   Coax / Twisted Pair Select  (CN120ST/SBT only)
-JP12        Terminator Select       (CN120AB/ST/SBT only)
-J1      BNC RG62/U Connector        (all except CN120TP)
-J2      Two 6-position Telephone Jack   (CN120TP/ST/SBT only)
-
-Setting one of the switches to Off means "1", On means "0".
-
-
-Setting the Node ID
--------------------
-
-The eight switches in SW2 are used to set the node ID. Each node attached
-to the network must have an unique node ID which must be different from 0.
-Switch 1 (ID0) serves as the least significant bit (LSB).
-
-The node ID is the sum of the values of all switches set to "1"  
-These values are:
-
-   Switch | Label | Value
-   -------|-------|-------
-     1    | ID0   |   1
-     2    | ID1   |   2
-     3    | ID2   |   4
-     4    | ID3   |   8
-     5    | ID4   |  16
-     6    | ID5   |  32
-     7    | ID6   |  64
-     8    | ID7   | 128
-
-Some Examples:
-
-    Switch         | Hex     | Decimal 
-   8 7 6 5 4 3 2 1 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1 
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |  
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
-
-
-Setting the I/O Base Address
-----------------------------
-
-The last three switches in switch block SW1 are used to select one
-of eight possible I/O Base addresses using the following table
-
-
-   Switch      | Hex I/O
-    6   7   8  | Address
-   ------------|--------
-   ON  ON  ON  |  260
-   OFF ON  ON  |  290
-   ON  OFF ON  |  2E0  (Manufacturer's default)
-   OFF OFF ON  |  2F0
-   ON  ON  OFF |  300
-   OFF ON  OFF |  350
-   ON  OFF OFF |  380
-   OFF OFF OFF |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
---------------------------------------------
-
-The memory buffer (RAM) requires 2K. The base of this buffer can be 
-located in any of eight positions. The address of the Boot Prom is
-memory base + 8K or memory base + 0x2000.
-Switches 1-5 of switch block SW1 select the Memory Base address.
-
-   Switch              | Hex RAM | Hex ROM
-    1   2   3   4   5  | Address | Address *)
-   --------------------|---------|-----------
-   ON  ON  ON  ON  ON  |  C0000  |  C2000
-   ON  ON  OFF ON  ON  |  C4000  |  C6000
-   ON  ON  ON  OFF ON  |  CC000  |  CE000
-   ON  ON  OFF OFF ON  |  D0000  |  D2000  (Manufacturer's default)
-   ON  ON  ON  ON  OFF |  D4000  |  D6000
-   ON  ON  OFF ON  OFF |  D8000  |  DA000
-   ON  ON  ON  OFF OFF |  DC000  |  DE000
-   ON  ON  OFF OFF OFF |  E0000  |  E2000
-  
-*) To enable the Boot ROM install the jumper JP1
-
-Note: Since the switches 1 and 2 are always set to ON it may be possible
-      that they can be used to add an offset of 2K, 4K or 6K to the base
-      address, but this feature is not documented in the manual and I
-      haven't tested it yet.
-
-
-Setting the Interrupt Line
---------------------------
-
-To select a hardware interrupt level install one (only one!) of the jumpers
-JP2, JP3, JP4, JP5, JP6. JP2 is the default.
-
-   Jumper | IRQ     
-   -------|-----
-     2    |  2
-     3    |  3
-     4    |  4
-     5    |  5
-     6    |  7
-
-
-Setting the Internal Terminator on CN120AB/TP/SBT
---------------------------------------------------
-
-The jumper JP12 is used to enable the internal terminator. 
-
-                         -----
-       0                |  0  |     
-     -----   ON         |     |  ON
-    |  0  |             |  0  |
-    |     |  OFF         -----   OFF
-    |  0  |                0
-     -----
-   Terminator          Terminator 
-    disabled            enabled
-  
-
-Selecting the Connector Type on CN120ST/SBT
--------------------------------------------
-
-     JP10    JP11        JP10    JP11
-                         -----   -----
-       0       0        |  0  | |  0  |       
-     -----   -----      |     | |     |
-    |  0  | |  0  |     |  0  | |  0  |
-    |     | |     |      -----   -----
-    |  0  | |  0  |        0       0 
-     -----   -----
-     Coaxial Cable       Twisted Pair Cable 
-       (Default)
-
-
-Setting the Timeout Parameters
-------------------------------
-
-The jumpers labeled EXT1 and EXT2 are used to determine the timeout 
-parameters. These two jumpers are normally left open.
-
-
-
-*****************************************************************************
-
-** CNet Technology Inc. **
-160 Series (16-bit cards)
--------------------------
-  - from Juergen Seifert <seifert@htwm.de>
-
-CNET TECHNOLOGY INC. (CNet) ARCNET 160A SERIES
-==============================================
-
-This description has been written by Juergen Seifert <seifert@htwm.de>
-using information from the following Original CNet Manual 
-
-              "ARCNET
-            USER'S MANUAL 
-                for
-               CN160A
-               CN160AB
-               CN160TP
-             P/N:12-01-0006
-             Revision 3.00"
-
-ARCNET is a registered trademark of the Datapoint Corporation
-
-P/N 160A   ARCNET 16 bit XT/AT Star
-P/N 160AB  ARCNET 16 bit XT/AT Bus
-P/N 160TP  ARCNET 16 bit XT/AT Twisted Pair
-
-   ___________________________________________________________________
-  <                             _________________________          ___|
-  >               |oo| JP2     |                         |    LED |___|
-  <               |oo| JP1     |        9026             |    LED |___|
-  >                            |_________________________|         ___|
-  <                                                             N |   | ID7
-  >                                                      1      o |   | ID6
-  <                                    1 2 3 4 5 6 7 8 9 0      d | S | ID5
-  >         _______________           _____________________     e | W | ID4
-  <        |     PROM      |         |         SW1         |    A | 2 | ID3
-  >        >    SOCKET     |         |_____________________|    d |   | ID2
-  <        |_______________|          | IO-Base   | MEM   |     d |   | ID1
-  >                                                             r |___| ID0
-  <                                                               ____|
-  >                                                              |    |
-  <                                                              | J1 |
-  >                                                              |    |
-  <                                                              |____|
-  >                            1 1 1 1                                |
-  <  3 4 5 6 7      JP     8 9 0 1 2 3                                |
-  > |o|o|o|o|o|           |o|o|o|o|o|o|                               |
-  < |o|o|o|o|o| __        |o|o|o|o|o|o|                    ___________|
-  >            |  |                                       |
-  <____________|  |_______________________________________|
-
-Legend:
-
-9026            ARCNET Probe
-SW1 1-6:    Base I/O Address Select
-    7-10:   Base Memory Address Select
-SW2 1-8:    Node ID Select (ID0-ID7)
-JP1/JP2     ET1, ET2 Timeout Parameters
-JP3-JP13    Interrupt Select
-J1      BNC RG62/U Connector        (CN160A/AB only)
-J1      Two 6-position Telephone Jack   (CN160TP only)
-LED
-
-Setting one of the switches to Off means "1", On means "0".
-
-
-Setting the Node ID
--------------------
-
-The eight switches in SW2 are used to set the node ID. Each node attached
-to the network must have an unique node ID which must be different from 0.
-Switch 1 (ID0) serves as the least significant bit (LSB).
-
-The node ID is the sum of the values of all switches set to "1"  
-These values are:
-
-   Switch | Label | Value
-   -------|-------|-------
-     1    | ID0   |   1
-     2    | ID1   |   2
-     3    | ID2   |   4
-     4    | ID3   |   8
-     5    | ID4   |  16
-     6    | ID5   |  32
-     7    | ID6   |  64
-     8    | ID7   | 128
-
-Some Examples:
-
-    Switch         | Hex     | Decimal 
-   8 7 6 5 4 3 2 1 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1 
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |  
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
-
-
-Setting the I/O Base Address
-----------------------------
-
-The first six switches in switch block SW1 are used to select the I/O Base
-address using the following table:
-
-             Switch        | Hex I/O
-    1   2   3   4   5   6  | Address
-   ------------------------|--------
-   OFF ON  ON  OFF OFF ON  |  260
-   OFF ON  OFF ON  ON  OFF |  290
-   OFF ON  OFF OFF OFF ON  |  2E0  (Manufacturer's default)
-   OFF ON  OFF OFF OFF OFF |  2F0
-   OFF OFF ON  ON  ON  ON  |  300
-   OFF OFF ON  OFF ON  OFF |  350
-   OFF OFF OFF ON  ON  ON  |  380
-   OFF OFF OFF OFF OFF ON  |  3E0
-
-Note: Other IO-Base addresses seem to be selectable, but only the above
-      combinations are documented.
-
-
-Setting the Base Memory (RAM) buffer Address
---------------------------------------------
-
-The switches 7-10 of switch block SW1 are used to select the Memory
-Base address of the RAM (2K) and the PROM.
-
-   Switch          | Hex RAM | Hex ROM
-    7   8   9  10  | Address | Address
-   ----------------|---------|-----------
-   OFF OFF ON  ON  |  C0000  |  C8000
-   OFF OFF ON  OFF |  D0000  |  D8000 (Default)
-   OFF OFF OFF ON  |  E0000  |  E8000
-
-Note: Other MEM-Base addresses seem to be selectable, but only the above
-      combinations are documented.
-
-
-Setting the Interrupt Line
---------------------------
-
-To select a hardware interrupt level install one (only one!) of the jumpers
-JP3 through JP13 using the following table:
-
-   Jumper | IRQ     
-   -------|-----------------
-     3    |  14
-     4    |  15
-     5    |  12
-     6    |  11
-     7    |  10
-     8    |   3
-     9    |   4
-    10    |   5
-    11    |   6
-    12    |   7
-    13    |   2 (=9) Default!
-
-Note:  - Do not use JP11=IRQ6, it may conflict with your Floppy Disk
-         Controller
-       - Use JP3=IRQ14 only, if you don't have an IDE-, MFM-, or RLL-
-         Hard Disk, it may conflict with their controllers
-
-
-Setting the Timeout Parameters
-------------------------------
-
-The jumpers labeled JP1 and JP2 are used to determine the timeout
-parameters. These two jumpers are normally left open.
-
-
-*****************************************************************************
-
-** Lantech **
-8-bit card, unknown model
--------------------------
-  - from Vlad Lungu <vlungu@ugal.ro> - his e-mail address seemed broken at
-    the time I tried to reach him.  Sorry Vlad, if you didn't get my reply.
-
-   ________________________________________________________________
-   |   1         8                                                 |
-   |   ___________                                               __|
-   |   |   SW1    |                                         LED |__|
-   |   |__________|                                                |
-   |                                                            ___|
-   |                _____________________                       |S | 8
-   |                |                   |                       |W |
-   |                |                   |                       |2 |
-   |                |                   |                       |__| 1
-   |                |      UM9065L      |     |o|  JP4         ____|____
-   |                |                   |     |o|              |  CN    |
-   |                |                   |                      |________|
-   |                |                   |                          |
-   |                |___________________|                          |
-   |                                                               |
-   |                                                               |
-   |      _____________                                            |
-   |      |            |                                           |
-   |      |    PROM    |        |ooooo|  JP6                       |
-   |      |____________|        |ooooo|                            |
-   |_____________                                             _   _|
-                |____________________________________________| |__|
-
-
-UM9065L : ARCnet Controller
-
-SW 1    : Shared Memory Address and I/O Base
-
-        ON=0
-
-        12345|Memory Address
-        -----|--------------
-        00001|  D4000
-        00010|  CC000
-        00110|  D0000
-        01110|  D1000
-        01101|  D9000
-        10010|  CC800
-        10011|  DC800
-        11110|  D1800
-
-It seems that the bits are considered in reverse order.  Also, you must
-observe that some of those addresses are unusual and I didn't probe them; I
-used a memory dump in DOS to identify them.  For the 00000 configuration and
-some others that I didn't write here the card seems to conflict with the
-video card (an S3 GENDAC). I leave the full decoding of those addresses to
-you.
-
-        678| I/O Address
-        ---|------------
-        000|    260
-        001|    failed probe
-        010|    2E0
-        011|    380
-        100|    290
-        101|    350
-        110|    failed probe
-        111|    3E0
-
-SW 2  : Node ID (binary coded)
-
-JP 4  : Boot PROM enable   CLOSE - enabled
-                           OPEN  - disabled
-
-JP 6  : IRQ set (ONLY ONE jumper on 1-5 for IRQ 2-6)
-
-
-*****************************************************************************
-
-** Acer **
-8-bit card, Model 5210-003
---------------------------
-  - from Vojtech Pavlik <vojtech@suse.cz> using portions of the existing
-    arcnet-hardware file.
-
-This is a 90C26 based card.  Its configuration seems similar to the SMC
-PC100, but has some additional jumpers I don't know the meaning of.
-
-               __
-              |  |
-   ___________|__|_________________________
-  |         |      |                       |
-  |         | BNC  |                       |
-  |         |______|                    ___|
-  |  _____________________             |___  
-  | |                     |                |
-  | | Hybrid IC           |                |
-  | |                     |       o|o J1   |
-  | |_____________________|       8|8      |
-  |                               8|8 J5   |
-  |                               o|o      |
-  |                               8|8      |
-  |__                             8|8      |
- (|__| LED                        o|o      |
-  |                               8|8      |
-  |                               8|8 J15  |
-  |                                        |
-  |                    _____               |
-  |                   |     |   _____      |
-  |                   |     |  |     |  ___|
-  |                   |     |  |     | |    
-  |  _____            | ROM |  | UFS | |    
-  | |     |           |     |  |     | |   
-  | |     |     ___   |     |  |     | |   
-  | |     |    |   |  |__.__|  |__.__| |   
-  | | NCR |    |XTL|   _____    _____  |   
-  | |     |    |___|  |     |  |     | |   
-  | |90C26|           |     |  |     | |   
-  | |     |           | RAM |  | UFS | |   
-  | |     | J17 o|o   |     |  |     | |   
-  | |     | J16 o|o   |     |  |     | |   
-  | |__.__|           |__.__|  |__.__| |   
-  |  ___                               |   
-  | |   |8                             |   
-  | |SW2|                              |   
-  | |   |                              |   
-  | |___|1                             |   
-  |  ___                               |   
-  | |   |10           J18 o|o          |   
-  | |   |                 o|o          |   
-  | |SW1|                 o|o          |   
-  | |   |             J21 o|o          |   
-  | |___|1                             |   
-  |                                    |   
-  |____________________________________|   
-
-
-Legend:
-
-90C26       ARCNET Chip
-XTL         20 MHz Crystal
-SW1 1-6     Base I/O Address Select
-    7-10    Memory Address Select
-SW2 1-8     Node ID Select (ID0-ID7)
-J1-J5       IRQ Select
-J6-J21      Unknown (Probably extra timeouts & ROM enable ...)
-LED1        Activity LED 
-BNC         Coax connector (STAR ARCnet)
-RAM         2k of SRAM
-ROM         Boot ROM socket
-UFS         Unidentified Flying Sockets
-
-
-Setting the Node ID
--------------------
-
-The eight switches in SW2 are used to set the node ID. Each node attached
-to the network must have an unique node ID which must not be 0.
-Switch 1 (ID0) serves as the least significant bit (LSB).
-
-Setting one of the switches to OFF means "1", ON means "0".
-
-The node ID is the sum of the values of all switches set to "1"
-These values are:
-
-   Switch | Value
-   -------|-------
-     1    |   1
-     2    |   2
-     3    |   4
-     4    |   8
-     5    |  16
-     6    |  32
-     7    |  64
-     8    | 128
-
-Don't set this to 0 or 255; these values are reserved.
-
-
-Setting the I/O Base Address
-----------------------------
-
-The switches 1 to 6 of switch block SW1 are used to select one
-of 32 possible I/O Base addresses using the following tables
-   
-          | Hex
-   Switch | Value
-   -------|-------
-     1    | 200  
-     2    | 100  
-     3    |  80  
-     4    |  40  
-     5    |  20  
-     6    |  10 
-
-The I/O address is sum of all switches set to "1". Remember that
-the I/O address space bellow 0x200 is RESERVED for mainboard, so
-switch 1 should be ALWAYS SET TO OFF. 
-
-
-Setting the Base Memory (RAM) buffer Address
---------------------------------------------
-
-The memory buffer (RAM) requires 2K. The base of this buffer can be
-located in any of sixteen positions. However, the addresses below
-A0000 are likely to cause system hang because there's main RAM.
-
-Jumpers 7-10 of switch block SW1 select the Memory Base address.
-
-   Switch          | Hex RAM
-    7   8   9  10  | Address
-   ----------------|---------
-   OFF OFF OFF OFF |  F0000 (conflicts with main BIOS)
-   OFF OFF OFF ON  |  E0000 
-   OFF OFF ON  OFF |  D0000
-   OFF OFF ON  ON  |  C0000 (conflicts with video BIOS)
-   OFF ON  OFF OFF |  B0000 (conflicts with mono video)
-   OFF ON  OFF ON  |  A0000 (conflicts with graphics)
-
-
-Setting the Interrupt Line
---------------------------
-
-Jumpers 1-5 of the jumper block J1 control the IRQ level. ON means 
-shorted, OFF means open.
-
-    Jumper              |  IRQ
-    1   2   3   4   5   |
-   ----------------------------
-    ON  OFF OFF OFF OFF |  7
-    OFF ON  OFF OFF OFF |  5
-    OFF OFF ON  OFF OFF |  4
-    OFF OFF OFF ON  OFF |  3
-    OFF OFF OFF OFF ON  |  2
-
-
-Unknown jumpers & sockets
--------------------------
-
-I know nothing about these. I just guess that J16&J17 are timeout
-jumpers and maybe one of J18-J21 selects ROM. Also J6-J10 and
-J11-J15 are connecting IRQ2-7 to some pins on the UFSs. I can't
-guess the purpose.
-
-
-*****************************************************************************
-
-** Datapoint? **
-LAN-ARC-8, an 8-bit card
-------------------------
-  - from Vojtech Pavlik <vojtech@suse.cz>
-
-This is another SMC 90C65-based ARCnet card. I couldn't identify the
-manufacturer, but it might be DataPoint, because the card has the
-original arcNet logo in its upper right corner.
-
-          _______________________________________________________
-         |                         _________                     |
-         |                        |   SW2   | ON      arcNet     |
-         |                        |_________| OFF             ___|
-         |  _____________         1 ______  8                |   | 8  
-         | |             | SW1     | XTAL | ____________     | S |    
-         | > RAM (2k)    |         |______||            |    | W |    
-         | |_____________|                 |      H     |    | 3 |    
-         |                        _________|_____ y     |    |___| 1  
-         |  _________            |         |     |b     |        |    
-         | |_________|           |         |     |r     |        |    
-         |                       |     SMC |     |i     |        |    
-         |                       |    90C65|     |d     |        |      
-         |  _________            |         |     |      |        |
-         | |   SW1   | ON        |         |     |I     |        |
-         | |_________| OFF       |_________|_____/C     |   _____|
-         |  1       8                      |            |  |     |___
-         |  ______________                 |            |  | BNC |___|
-         | |              |                |____________|  |_____|
-         | > EPROM SOCKET |              _____________           |
-         | |______________|             |_____________|          |
-         |                                         ______________|
-         |                                        | 
-         |________________________________________|
-
-Legend:
-
-90C65       ARCNET Chip 
-SW1 1-5:    Base Memory Address Select
-    6-8:    Base I/O Address Select
-SW2 1-8:    Node ID Select
-SW3 1-5:    IRQ Select   
-    6-7:    Extra Timeout
-    8  :    ROM Enable   
-BNC         Coax connector
-XTAL        20 MHz Crystal
-
-
-Setting the Node ID
--------------------
-
-The eight switches in SW3 are used to set the node ID. Each node attached
-to the network must have an unique node ID which must not be 0.
-Switch 1 serves as the least significant bit (LSB).
-
-Setting one of the switches to Off means "1", On means "0".
-
-The node ID is the sum of the values of all switches set to "1"  
-These values are:
-
-   Switch | Value
-   -------|-------
-     1    |   1
-     2    |   2
-     3    |   4
-     4    |   8
-     5    |  16
-     6    |  32
-     7    |  64
-     8    | 128
-
-
-Setting the I/O Base Address
-----------------------------
-
-The last three switches in switch block SW1 are used to select one
-of eight possible I/O Base addresses using the following table
-
-
-   Switch      | Hex I/O
-    6   7   8  | Address
-   ------------|--------
-   ON  ON  ON  |  260
-   OFF ON  ON  |  290
-   ON  OFF ON  |  2E0  (Manufacturer's default)
-   OFF OFF ON  |  2F0
-   ON  ON  OFF |  300
-   OFF ON  OFF |  350
-   ON  OFF OFF |  380
-   OFF OFF OFF |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
---------------------------------------------
-
-The memory buffer (RAM) requires 2K. The base of this buffer can be 
-located in any of eight positions. The address of the Boot Prom is
-memory base + 0x2000.
-Jumpers 3-5 of switch block SW1 select the Memory Base address.
-
-   Switch              | Hex RAM | Hex ROM
-    1   2   3   4   5  | Address | Address *)
-   --------------------|---------|-----------
-   ON  ON  ON  ON  ON  |  C0000  |  C2000
-   ON  ON  OFF ON  ON  |  C4000  |  C6000
-   ON  ON  ON  OFF ON  |  CC000  |  CE000
-   ON  ON  OFF OFF ON  |  D0000  |  D2000  (Manufacturer's default)
-   ON  ON  ON  ON  OFF |  D4000  |  D6000
-   ON  ON  OFF ON  OFF |  D8000  |  DA000
-   ON  ON  ON  OFF OFF |  DC000  |  DE000
-   ON  ON  OFF OFF OFF |  E0000  |  E2000
-  
-*) To enable the Boot ROM set the switch 8 of switch block SW3 to position ON.
-
-The switches 1 and 2 probably add 0x0800 and 0x1000 to RAM base address.
-
-
-Setting the Interrupt Line
---------------------------
-
-Switches 1-5 of the switch block SW3 control the IRQ level.
-
-    Jumper              |  IRQ
-    1   2   3   4   5   |
-   ----------------------------
-    ON  OFF OFF OFF OFF |  3
-    OFF ON  OFF OFF OFF |  4
-    OFF OFF ON  OFF OFF |  5
-    OFF OFF OFF ON  OFF |  7
-    OFF OFF OFF OFF ON  |  2
-
-
-Setting the Timeout Parameters
-------------------------------
-
-The switches 6-7 of the switch block SW3 are used to determine the timeout
-parameters.  These two switches are normally left in the OFF position.
-
-
-*****************************************************************************
-
-** Topware **
-8-bit card, TA-ARC/10
--------------------------
-  - from Vojtech Pavlik <vojtech@suse.cz>
-
-This is another very similar 90C65 card. Most of the switches and jumpers
-are the same as on other clones.
-
- _____________________________________________________________________
-|  ___________   |                         |            ______        |
-| |SW2 NODE ID|  |                         |           | XTAL |       |
-| |___________|  |  Hybrid IC              |           |______|       |
-|  ___________   |                         |                        __|    
-| |SW1 MEM+I/O|  |_________________________|                   LED1|__|)   
-| |___________|           1 2                                         |     
-|                     J3 |o|o| TIMEOUT                          ______|    
-|     ______________     |o|o|                                 |      |    
-|    |              |  ___________________                     | RJ   |    
-|    > EPROM SOCKET | |                   \                    |------|     
-|J2  |______________| |                    |                   |      |    
-||o|                  |                    |                   |______|
-||o| ROM ENABLE       |        SMC         |    _________             |
-|     _____________   |       90C65        |   |_________|       _____|    
-|    |             |  |                    |                    |     |___ 
-|    > RAM (2k)    |  |                    |                    | BNC |___|
-|    |_____________|  |                    |                    |_____|    
-|                     |____________________|                          |    
-| ________ IRQ 2 3 4 5 7                  ___________                 |
-||________|   |o|o|o|o|o|                |___________|                |
-|________   J1|o|o|o|o|o|                               ______________|
-         |                                             |
-         |_____________________________________________|
-
-Legend:
-
-90C65       ARCNET Chip
-XTAL        20 MHz Crystal
-SW1 1-5     Base Memory Address Select
-    6-8     Base I/O Address Select
-SW2 1-8     Node ID Select (ID0-ID7)
-J1          IRQ Select
-J2          ROM Enable
-J3          Extra Timeout
-LED1        Activity LED 
-BNC         Coax connector (BUS ARCnet)
-RJ          Twisted Pair Connector (daisy chain)
-
-
-Setting the Node ID
--------------------
-
-The eight switches in SW2 are used to set the node ID. Each node attached to
-the network must have an unique node ID which must not be 0.  Switch 1 (ID0)
-serves as the least significant bit (LSB).
-
-Setting one of the switches to Off means "1", On means "0".
-
-The node ID is the sum of the values of all switches set to "1"
-These values are:
-
-   Switch | Label | Value
-   -------|-------|-------
-     1    | ID0   |   1
-     2    | ID1   |   2
-     3    | ID2   |   4
-     4    | ID3   |   8
-     5    | ID4   |  16
-     6    | ID5   |  32
-     7    | ID6   |  64
-     8    | ID7   | 128
-
-Setting the I/O Base Address
-----------------------------
-
-The last three switches in switch block SW1 are used to select one
-of eight possible I/O Base addresses using the following table:
-
-
-   Switch      | Hex I/O
-    6   7   8  | Address
-   ------------|--------
-   ON  ON  ON  |  260  (Manufacturer's default)
-   OFF ON  ON  |  290
-   ON  OFF ON  |  2E0                         
-   OFF OFF ON  |  2F0
-   ON  ON  OFF |  300
-   OFF ON  OFF |  350
-   ON  OFF OFF |  380
-   OFF OFF OFF |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
---------------------------------------------
-
-The memory buffer (RAM) requires 2K. The base of this buffer can be
-located in any of eight positions. The address of the Boot Prom is
-memory base + 0x2000.
-Jumpers 3-5 of switch block SW1 select the Memory Base address.
-
-   Switch              | Hex RAM | Hex ROM
-    1   2   3   4   5  | Address | Address *)
-   --------------------|---------|-----------
-   ON  ON  ON  ON  ON  |  C0000  |  C2000
-   ON  ON  OFF ON  ON  |  C4000  |  C6000  (Manufacturer's default) 
-   ON  ON  ON  OFF ON  |  CC000  |  CE000
-   ON  ON  OFF OFF ON  |  D0000  |  D2000  
-   ON  ON  ON  ON  OFF |  D4000  |  D6000
-   ON  ON  OFF ON  OFF |  D8000  |  DA000
-   ON  ON  ON  OFF OFF |  DC000  |  DE000
-   ON  ON  OFF OFF OFF |  E0000  |  E2000
-
-*) To enable the Boot ROM short the jumper J2.
-
-The jumpers 1 and 2 probably add 0x0800 and 0x1000 to RAM address.
-
-
-Setting the Interrupt Line
---------------------------
-
-Jumpers 1-5 of the jumper block J1 control the IRQ level.  ON means
-shorted, OFF means open.
-
-    Jumper              |  IRQ
-    1   2   3   4   5   |
-   ----------------------------
-    ON  OFF OFF OFF OFF |  2
-    OFF ON  OFF OFF OFF |  3
-    OFF OFF ON  OFF OFF |  4
-    OFF OFF OFF ON  OFF |  5
-    OFF OFF OFF OFF ON  |  7
-
-
-Setting the Timeout Parameters
-------------------------------
-
-The jumpers J3 are used to set the timeout parameters. These two 
-jumpers are normally left open.
-
-  
-*****************************************************************************
-
-** Thomas-Conrad **
-Model #500-6242-0097 REV A (8-bit card)
----------------------------------------
-  - from Lars Karlsson <100617.3473@compuserve.com>
-
-     ________________________________________________________
-   |          ________   ________                           |_____
-   |         |........| |........|                            |
-   |         |________| |________|                         ___|
-   |            SW 3       SW 1                           |   |
-   |         Base I/O   Base Addr.                Station |   |
-   |                                              address |   |
-   |    ______                                    switch  |   |
-   |   |      |                                           |   |
-   |   |      |                                           |___|    
-   |   |      |                                 ______        |___._
-   |   |______|                                |______|         ____| BNC
-   |                                            Jumper-        _____| Connector
-   |   Main chip                                block  _    __|   '  
-   |                                                  | |  |    RJ Connector
-   |                                                  |_|  |    with 110 Ohm
-   |                                                       |__  Terminator
-   |    ___________                                         __|
-   |   |...........|                                       |    RJ-jack
-   |   |...........|    _____                              |    (unused)
-   |   |___________|   |_____|                             |__
-   |  Boot PROM socket IRQ-jumpers                            |_  Diagnostic
-   |________                                       __          _| LED (red)
-            | | | | | | | | | | | | | | | | | | | |  |        |
-            | | | | | | | | | | | | | | | | | | | |  |________|
-                                                              |
-                                                              |
-
-And here are the settings for some of the switches and jumpers on the cards.
-
-
-          I/O
-
-         1 2 3 4 5 6 7 8
-
-2E0----- 0 0 0 1 0 0 0 1
-2F0----- 0 0 0 1 0 0 0 0
-300----- 0 0 0 0 1 1 1 1
-350----- 0 0 0 0 1 1 1 0
-
-"0" in the above example means switch is off "1" means that it is on.
-
-
-    ShMem address.
-
-      1 2 3 4 5 6 7 8
-
-CX00--0 0 1 1 | |   |
-DX00--0 0 1 0       |
-X000--------- 1 1   |
-X400--------- 1 0   |
-X800--------- 0 1   |
-XC00--------- 0 0   
-ENHANCED----------- 1
-COMPATIBLE--------- 0
-
-
-       IRQ
-
-
-   3 4 5 7 2
-   . . . . .
-   . . . . .
-
-
-There is a DIP-switch with 8 switches, used to set the shared memory address
-to be used. The first 6 switches set the address, the 7th doesn't have any
-function, and the 8th switch is used to select "compatible" or "enhanced".
-When I got my two cards, one of them had this switch set to "enhanced". That
-card didn't work at all, it wasn't even recognized by the driver. The other
-card had this switch set to "compatible" and it behaved absolutely normally. I
-guess that the switch on one of the cards, must have been changed accidentally
-when the card was taken out of its former host. The question remains
-unanswered, what is the purpose of the "enhanced" position?
-
-[Avery's note: "enhanced" probably either disables shared memory (use IO
-ports instead) or disables IO ports (use memory addresses instead).  This
-varies by the type of card involved.  I fail to see how either of these
-enhance anything.  Send me more detailed information about this mode, or
-just use "compatible" mode instead.]
-
-
-*****************************************************************************
-
-** Waterloo Microsystems Inc. ?? **
-8-bit card (C) 1985
--------------------
-  - from Robert Michael Best <rmb117@cs.usask.ca>
-
-[Avery's note: these don't work with my driver for some reason.  These cards
-SEEM to have settings similar to the PDI508Plus, which is
-software-configured and doesn't work with my driver either.  The "Waterloo
-chip" is a boot PROM, probably designed specifically for the University of
-Waterloo.  If you have any further information about this card, please
-e-mail me.]
-
-The probe has not been able to detect the card on any of the J2 settings,
-and I tried them again with the "Waterloo" chip removed.
- _____________________________________________________________________
-| \/  \/              ___  __ __                                      |
-| C4  C4     |^|     | M ||  ^  ||^|                                  |
-| --  --     |_|     | 5 ||     || | C3                               |
-| \/  \/      C10    |___||     ||_|                                  | 
-| C4  C4             _  _ |     |                 ??                  | 
-| --  --            | \/ ||     |                                     | 
-|                   |    ||     |                                     | 
-|                   |    ||  C1 |                                     | 
-|                   |    ||     |  \/                            _____|    
-|                   | C6 ||     |  C9                           |     |___ 
-|                   |    ||     |  --                           | BNC |___| 
-|                   |    ||     |          >C7|                 |_____|
-|                   |    ||     |                                     |
-| __ __             |____||_____|       1 2 3     6                   |
-||  ^  |     >C4|                      |o|o|o|o|o|o| J2    >C4|       |
-||     |                               |o|o|o|o|o|o|                  |
-|| C2  |     >C4|                                          >C4|       |
-||     |                                   >C8|                       |
-||     |       2 3 4 5 6 7  IRQ                            >C4|       |
-||_____|      |o|o|o|o|o|o| J3                                        |
-|_______      |o|o|o|o|o|o|                            _______________|
-        |                                             |
-        |_____________________________________________|
-
-C1 -- "COM9026
-       SMC 8638"
-      In a chip socket.
-
-C2 -- "@Copyright
-       Waterloo Microsystems Inc.
-       1985"
-      In a chip Socket with info printed on a label covering a round window
-      showing the circuit inside. (The window indicates it is an EPROM chip.)
-
-C3 -- "COM9032
-       SMC 8643"
-      In a chip socket.
-
-C4 -- "74LS"
-      9 total no sockets.
-
-M5 -- "50006-136
-       20.000000 MHZ
-       MTQ-T1-S3
-       0 M-TRON 86-40"
-      Metallic case with 4 pins, no socket.
-
-C6 -- "MOSTEK@TC8643
-       MK6116N-20
-       MALAYSIA"
-      No socket.
-
-C7 -- No stamp or label but in a 20 pin chip socket.
-
-C8 -- "PAL10L8CN
-       8623"
-      In a 20 pin socket.
-
-C9 -- "PAl16R4A-2CN
-       8641"
-      In a 20 pin socket.
-
-C10 -- "M8640
-          NMC
-        9306N"
-       In an 8 pin socket.
-
-?? -- Some components on a smaller board and attached with 20 pins all 
-      along the side closest to the BNC connector.  The are coated in a dark 
-      resin.
-
-On the board there are two jumper banks labeled J2 and J3. The 
-manufacturer didn't put a J1 on the board. The two boards I have both 
-came with a jumper box for each bank.
-
-J2 -- Numbered 1 2 3 4 5 6. 
-      4 and 5 are not stamped due to solder points.
-       
-J3 -- IRQ 2 3 4 5 6 7
-
-The board itself has a maple leaf stamped just above the irq jumpers 
-and "-2 46-86" beside C2. Between C1 and C6 "ASS 'Y 300163" and "@1986 
-CORMAN CUSTOM ELECTRONICS CORP." stamped just below the BNC connector.
-Below that "MADE IN CANADA"
-
-  
-*****************************************************************************
-
-** No Name **
-8-bit cards, 16-bit cards
--------------------------
-  - from Juergen Seifert <seifert@htwm.de>
-  
-NONAME 8-BIT ARCNET
-===================
-
-I have named this ARCnet card "NONAME", since there is no name of any
-manufacturer on the Installation manual nor on the shipping box. The only
-hint to the existence of a manufacturer at all is written in copper,
-it is "Made in Taiwan"
-
-This description has been written by Juergen Seifert <seifert@htwm.de>
-using information from the Original
-                    "ARCnet Installation Manual"
-
-
-    ________________________________________________________________
-   | |STAR| BUS| T/P|                                               |
-   | |____|____|____|                                               |
-   |                            _____________________               |
-   |                           |                     |              |
-   |                           |                     |              |
-   |                           |                     |              |
-   |                           |        SMC          |              |
-   |                           |                     |              |
-   |                           |       COM90C65      |              |
-   |                           |                     |              |
-   |                           |                     |              |
-   |                           |__________-__________|              |
-   |                                                           _____|
-   |      _______________                                     |  CN |
-   |     | PROM          |                                    |_____|
-   |     > SOCKET        |                                          |
-   |     |_______________|         1 2 3 4 5 6 7 8  1 2 3 4 5 6 7 8 |
-   |                               _______________  _______________ |
-   |           |o|o|o|o|o|o|o|o|  |      SW1      ||      SW2      ||
-   |           |o|o|o|o|o|o|o|o|  |_______________||_______________||
-   |___         2 3 4 5 7 E E R        Node ID       IOB__|__MEM____|
-       |        \ IRQ   / T T O                      |
-       |__________________1_2_M______________________|
-
-Legend:
-
-COM90C65:       ARCnet Probe
-S1  1-8:    Node ID Select
-S2  1-3:    I/O Base Address Select
-    4-6:    Memory Base Address Select
-    7-8:    RAM Offset Select
-ET1, ET2    Extended Timeout Select
-ROM     ROM Enable Select
-CN              RG62 Coax Connector
-STAR| BUS | T/P Three fields for placing a sign (colored circle)
-                indicating the topology of the card
-
-Setting one of the switches to Off means "1", On means "0".
-
-
-Setting the Node ID
--------------------
-
-The eight switches in group SW1 are used to set the node ID.
-Each node attached to the network must have an unique node ID which
-must be different from 0.
-Switch 8 serves as the least significant bit (LSB).
-
-The node ID is the sum of the values of all switches set to "1"  
-These values are:
-
-    Switch | Value
-    -------|-------
-      8    |   1
-      7    |   2
-      6    |   4
-      5    |   8
-      4    |  16
-      3    |  32
-      2    |  64
-      1    | 128
-
-Some Examples:
-
-    Switch         | Hex     | Decimal 
-   1 2 3 4 5 6 7 8 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1 
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |  
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
-
-
-Setting the I/O Base Address
-----------------------------
-
-The first three switches in switch group SW2 are used to select one
-of eight possible I/O Base addresses using the following table
-
-   Switch      | Hex I/O
-    1   2   3  | Address
-   ------------|--------
-   ON  ON  ON  |  260
-   ON  ON  OFF |  290
-   ON  OFF ON  |  2E0  (Manufacturer's default)
-   ON  OFF OFF |  2F0
-   OFF ON  ON  |  300
-   OFF ON  OFF |  350
-   OFF OFF ON  |  380
-   OFF OFF OFF |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
---------------------------------------------
-
-The memory buffer requires 2K of a 16K block of RAM. The base of this
-16K block can be located in any of eight positions.
-Switches 4-6 of switch group SW2 select the Base of the 16K block.
-Within that 16K address space, the buffer may be assigned any one of four
-positions, determined by the offset, switches 7 and 8 of group SW2.
-
-   Switch     | Hex RAM | Hex ROM
-   4 5 6  7 8 | Address | Address *)
-   -----------|---------|-----------
-   0 0 0  0 0 |  C0000  |  C2000
-   0 0 0  0 1 |  C0800  |  C2000
-   0 0 0  1 0 |  C1000  |  C2000
-   0 0 0  1 1 |  C1800  |  C2000
-              |         |
-   0 0 1  0 0 |  C4000  |  C6000
-   0 0 1  0 1 |  C4800  |  C6000
-   0 0 1  1 0 |  C5000  |  C6000
-   0 0 1  1 1 |  C5800  |  C6000
-              |         |
-   0 1 0  0 0 |  CC000  |  CE000
-   0 1 0  0 1 |  CC800  |  CE000
-   0 1 0  1 0 |  CD000  |  CE000
-   0 1 0  1 1 |  CD800  |  CE000
-              |         |
-   0 1 1  0 0 |  D0000  |  D2000  (Manufacturer's default)
-   0 1 1  0 1 |  D0800  |  D2000
-   0 1 1  1 0 |  D1000  |  D2000
-   0 1 1  1 1 |  D1800  |  D2000
-              |         |
-   1 0 0  0 0 |  D4000  |  D6000
-   1 0 0  0 1 |  D4800  |  D6000
-   1 0 0  1 0 |  D5000  |  D6000
-   1 0 0  1 1 |  D5800  |  D6000
-              |         |
-   1 0 1  0 0 |  D8000  |  DA000
-   1 0 1  0 1 |  D8800  |  DA000
-   1 0 1  1 0 |  D9000  |  DA000
-   1 0 1  1 1 |  D9800  |  DA000
-              |         |
-   1 1 0  0 0 |  DC000  |  DE000
-   1 1 0  0 1 |  DC800  |  DE000
-   1 1 0  1 0 |  DD000  |  DE000
-   1 1 0  1 1 |  DD800  |  DE000
-              |         |
-   1 1 1  0 0 |  E0000  |  E2000
-   1 1 1  0 1 |  E0800  |  E2000
-   1 1 1  1 0 |  E1000  |  E2000
-   1 1 1  1 1 |  E1800  |  E2000
-  
-*) To enable the 8K Boot PROM install the jumper ROM.
-   The default is jumper ROM not installed.
-
-
-Setting Interrupt Request Lines (IRQ)
--------------------------------------
-
-To select a hardware interrupt level set one (only one!) of the jumpers
-IRQ2, IRQ3, IRQ4, IRQ5 or IRQ7. The manufacturer's default is IRQ2.
-
-Setting the Timeouts
---------------------
-
-The two jumpers labeled ET1 and ET2 are used to determine the timeout
-parameters (response and reconfiguration time). Every node in a network
-must be set to the same timeout values.
-
-   ET1 ET2 | Response Time (us) | Reconfiguration Time (ms)
-   --------|--------------------|--------------------------
-   Off Off |        78          |          840   (Default)
-   Off On  |       285          |         1680
-   On  Off |       563          |         1680
-   On  On  |      1130          |         1680
-
-On means jumper installed, Off means jumper not installed
-
-
-NONAME 16-BIT ARCNET
-====================
-
-The manual of my 8-Bit NONAME ARCnet Card contains another description
-of a 16-Bit Coax / Twisted Pair Card. This description is incomplete,
-because there are missing two pages in the manual booklet. (The table
-of contents reports pages ... 2-9, 2-11, 2-12, 3-1, ... but inside
-the booklet there is a different way of counting ... 2-9, 2-10, A-1,
-(empty page), 3-1, ..., 3-18, A-1 (again), A-2)
-Also the picture of the board layout is not as good as the picture of
-8-Bit card, because there isn't any letter like "SW1" written to the
-picture.
-Should somebody have such a board, please feel free to complete this
-description or to send a mail to me!
-
-This description has been written by Juergen Seifert <seifert@htwm.de>
-using information from the Original
-                    "ARCnet Installation Manual"
-
-
-   ___________________________________________________________________
-  <                    _________________  _________________           |
-  >                   |       SW?       ||      SW?        |          |
-  <                   |_________________||_________________|          |
-  >                       ____________________                        |
-  <                      |                    |                       |
-  >                      |                    |                       |
-  <                      |                    |                       |
-  >                      |                    |                       |
-  <                      |                    |                       |
-  >                      |                    |                       |
-  <                      |                    |                       |
-  >                      |____________________|                       |
-  <                                                               ____|
-  >                       ____________________                   |    |
-  <                      |                    |                  | J1 |
-  >                      |                    <                  |    |
-  <                      |____________________|  ? ? ? ? ? ?     |____|
-  >                                             |o|o|o|o|o|o|         |
-  <                                             |o|o|o|o|o|o|         |
-  >                                                                   |
-  <             __                                         ___________|
-  >            |  |                                       |
-  <____________|  |_______________________________________|
-
-
-Setting one of the switches to Off means "1", On means "0".
-
-
-Setting the Node ID
--------------------
-
-The eight switches in group SW2 are used to set the node ID.
-Each node attached to the network must have an unique node ID which
-must be different from 0.
-Switch 8 serves as the least significant bit (LSB).
-
-The node ID is the sum of the values of all switches set to "1"  
-These values are:
-
-    Switch | Value
-    -------|-------
-      8    |   1
-      7    |   2
-      6    |   4
-      5    |   8
-      4    |  16
-      3    |  32
-      2    |  64
-      1    | 128
-
-Some Examples:
-
-    Switch         | Hex     | Decimal 
-   1 2 3 4 5 6 7 8 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1 
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |  
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
-
-
-Setting the I/O Base Address
-----------------------------
-
-The first three switches in switch group SW1 are used to select one
-of eight possible I/O Base addresses using the following table
-
-   Switch      | Hex I/O
-    3   2   1  | Address
-   ------------|--------
-   ON  ON  ON  |  260
-   ON  ON  OFF |  290
-   ON  OFF ON  |  2E0  (Manufacturer's default)
-   ON  OFF OFF |  2F0
-   OFF ON  ON  |  300
-   OFF ON  OFF |  350
-   OFF OFF ON  |  380
-   OFF OFF OFF |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
---------------------------------------------
-
-The memory buffer requires 2K of a 16K block of RAM. The base of this
-16K block can be located in any of eight positions.
-Switches 6-8 of switch group SW1 select the Base of the 16K block.
-Within that 16K address space, the buffer may be assigned any one of four
-positions, determined by the offset, switches 4 and 5 of group SW1.
-
-   Switch     | Hex RAM | Hex ROM
-   8 7 6  5 4 | Address | Address
-   -----------|---------|-----------
-   0 0 0  0 0 |  C0000  |  C2000
-   0 0 0  0 1 |  C0800  |  C2000
-   0 0 0  1 0 |  C1000  |  C2000
-   0 0 0  1 1 |  C1800  |  C2000
-              |         |
-   0 0 1  0 0 |  C4000  |  C6000
-   0 0 1  0 1 |  C4800  |  C6000
-   0 0 1  1 0 |  C5000  |  C6000
-   0 0 1  1 1 |  C5800  |  C6000
-              |         |
-   0 1 0  0 0 |  CC000  |  CE000
-   0 1 0  0 1 |  CC800  |  CE000
-   0 1 0  1 0 |  CD000  |  CE000
-   0 1 0  1 1 |  CD800  |  CE000
-              |         |
-   0 1 1  0 0 |  D0000  |  D2000  (Manufacturer's default)
-   0 1 1  0 1 |  D0800  |  D2000
-   0 1 1  1 0 |  D1000  |  D2000
-   0 1 1  1 1 |  D1800  |  D2000
-              |         |
-   1 0 0  0 0 |  D4000  |  D6000
-   1 0 0  0 1 |  D4800  |  D6000
-   1 0 0  1 0 |  D5000  |  D6000
-   1 0 0  1 1 |  D5800  |  D6000
-              |         |
-   1 0 1  0 0 |  D8000  |  DA000
-   1 0 1  0 1 |  D8800  |  DA000
-   1 0 1  1 0 |  D9000  |  DA000
-   1 0 1  1 1 |  D9800  |  DA000
-              |         |
-   1 1 0  0 0 |  DC000  |  DE000
-   1 1 0  0 1 |  DC800  |  DE000
-   1 1 0  1 0 |  DD000  |  DE000
-   1 1 0  1 1 |  DD800  |  DE000
-              |         |
-   1 1 1  0 0 |  E0000  |  E2000
-   1 1 1  0 1 |  E0800  |  E2000
-   1 1 1  1 0 |  E1000  |  E2000
-   1 1 1  1 1 |  E1800  |  E2000
-  
-
-Setting Interrupt Request Lines (IRQ)
--------------------------------------
-
-??????????????????????????????????????
-
-
-Setting the Timeouts
---------------------
-
-??????????????????????????????????????
-
-
-*****************************************************************************
-
-** No Name **
-8-bit cards ("Made in Taiwan R.O.C.")
------------
-  - from Vojtech Pavlik <vojtech@suse.cz>
-
-I have named this ARCnet card "NONAME", since I got only the card with
-no manual at all and the only text identifying the manufacturer is 
-"MADE IN TAIWAN R.O.C" printed on the card.
-
-          ____________________________________________________________
-         |                 1 2 3 4 5 6 7 8                            |
-         | |o|o| JP1       o|o|o|o|o|o|o|o| ON                        |
-         |  +              o|o|o|o|o|o|o|o|                        ___|
-         |  _____________  o|o|o|o|o|o|o|o| OFF         _____     |   | ID7
-         | |             | SW1                         |     |    |   | ID6
-         | > RAM (2k)    |        ____________________ |  H  |    | S | ID5
-         | |_____________|       |                    ||  y  |    | W | ID4
-         |                       |                    ||  b  |    | 2 | ID3
-         |                       |                    ||  r  |    |   | ID2
-         |                       |                    ||  i  |    |   | ID1
-         |                       |       90C65        ||  d  |    |___| ID0
-         |      SW3              |                    ||     |        |      
-         | |o|o|o|o|o|o|o|o| ON  |                    ||  I  |        |
-         | |o|o|o|o|o|o|o|o|     |                    ||  C  |        |
-         | |o|o|o|o|o|o|o|o| OFF |____________________||     |   _____|
-         |  1 2 3 4 5 6 7 8                            |     |  |     |___
-         |  ______________                             |     |  | BNC |___|
-         | |              |                            |_____|  |_____|
-         | > EPROM SOCKET |                                           |
-         | |______________|                                           |
-         |                                              ______________|
-         |                                             |
-         |_____________________________________________|
-
-Legend:
-
-90C65       ARCNET Chip 
-SW1 1-5:    Base Memory Address Select
-    6-8:    Base I/O Address Select
-SW2 1-8:    Node ID Select (ID0-ID7)
-SW3 1-5:    IRQ Select   
-    6-7:    Extra Timeout
-    8  :    ROM Enable   
-JP1         Led connector
-BNC         Coax connector
-
-Although the jumpers SW1 and SW3 are marked SW, not JP, they are jumpers, not 
-switches.
-
-Setting the jumpers to ON means connecting the upper two pins, off the bottom 
-two - or - in case of IRQ setting, connecting none of them at all.
-
-Setting the Node ID
--------------------
-
-The eight switches in SW2 are used to set the node ID. Each node attached
-to the network must have an unique node ID which must not be 0.
-Switch 1 (ID0) serves as the least significant bit (LSB).
-
-Setting one of the switches to Off means "1", On means "0".
-
-The node ID is the sum of the values of all switches set to "1"  
-These values are:
-
-   Switch | Label | Value
-   -------|-------|-------
-     1    | ID0   |   1
-     2    | ID1   |   2
-     3    | ID2   |   4
-     4    | ID3   |   8
-     5    | ID4   |  16
-     6    | ID5   |  32
-     7    | ID6   |  64
-     8    | ID7   | 128
-
-Some Examples:
-
-    Switch         | Hex     | Decimal 
-   8 7 6 5 4 3 2 1 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1 
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |  
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
-
-
-Setting the I/O Base Address
-----------------------------
-
-The last three switches in switch block SW1 are used to select one
-of eight possible I/O Base addresses using the following table
-
-
-   Switch      | Hex I/O
-    6   7   8  | Address
-   ------------|--------
-   ON  ON  ON  |  260
-   OFF ON  ON  |  290
-   ON  OFF ON  |  2E0  (Manufacturer's default)
-   OFF OFF ON  |  2F0
-   ON  ON  OFF |  300
-   OFF ON  OFF |  350
-   ON  OFF OFF |  380
-   OFF OFF OFF |  3E0
-
-
-Setting the Base Memory (RAM) buffer Address
---------------------------------------------
-
-The memory buffer (RAM) requires 2K. The base of this buffer can be 
-located in any of eight positions. The address of the Boot Prom is
-memory base + 0x2000.
-Jumpers 3-5 of jumper block SW1 select the Memory Base address.
-
-   Switch              | Hex RAM | Hex ROM
-    1   2   3   4   5  | Address | Address *)
-   --------------------|---------|-----------
-   ON  ON  ON  ON  ON  |  C0000  |  C2000
-   ON  ON  OFF ON  ON  |  C4000  |  C6000
-   ON  ON  ON  OFF ON  |  CC000  |  CE000
-   ON  ON  OFF OFF ON  |  D0000  |  D2000  (Manufacturer's default)
-   ON  ON  ON  ON  OFF |  D4000  |  D6000
-   ON  ON  OFF ON  OFF |  D8000  |  DA000
-   ON  ON  ON  OFF OFF |  DC000  |  DE000
-   ON  ON  OFF OFF OFF |  E0000  |  E2000
-  
-*) To enable the Boot ROM set the jumper 8 of jumper block SW3 to position ON.
-
-The jumpers 1 and 2 probably add 0x0800, 0x1000 and 0x1800 to RAM adders.
-
-Setting the Interrupt Line
---------------------------
-
-Jumpers 1-5 of the jumper block SW3 control the IRQ level.
-
-    Jumper              |  IRQ
-    1   2   3   4   5   |
-   ----------------------------
-    ON  OFF OFF OFF OFF |  2
-    OFF ON  OFF OFF OFF |  3
-    OFF OFF ON  OFF OFF |  4
-    OFF OFF OFF ON  OFF |  5
-    OFF OFF OFF OFF ON  |  7
-
-
-Setting the Timeout Parameters
-------------------------------
-
-The jumpers 6-7 of the jumper block SW3 are used to determine the timeout 
-parameters. These two jumpers are normally left in the OFF position.
-
-
-*****************************************************************************
-
-** No Name **
-(Generic Model 9058)
---------------------
-  - from Andrew J. Kroll <ag784@freenet.buffalo.edu>
-  - Sorry this sat in my to-do box for so long, Andrew! (yikes - over a
-    year!)
-                                                                      _____
-                                                                     |    <
-                                                                     | .---'
-    ________________________________________________________________ | |
-   |                           |     SW2     |                      |  |
-   |   ___________             |_____________|                      |  |
-   |  |           |              1 2 3 4 5 6                     ___|  |
-   |  >  6116 RAM |         _________                         8 |   |  |
-   |  |___________|        |20MHzXtal|                        7 |   |  |
-   |                       |_________|       __________       6 | S |  |
-   |    74LS373                             |          |-     5 | W |  |
-   |   _________                            |      E   |-     4 |   |  |
-   |   >_______|              ______________|..... P   |-     3 | 3 |  |
-   |                         |              |    : O   |-     2 |   |  |
-   |                         |              |    : X   |-     1 |___|  |
-   |   ________________      |              |    : Y   |-           |  |
-   |  |      SW1       |     |      SL90C65 |    :     |-           |  |
-   |  |________________|     |              |    : B   |-           |  |
-   |    1 2 3 4 5 6 7 8      |              |    : O   |-           |  |
-   |                         |_________o____|..../ A   |-    _______|  |
-   |    ____________________                |      R   |-   |       |------,   
-   |   |                    |               |      D   |-   |  BNC  |   #  |
-   |   > 2764 PROM SOCKET   |               |__________|-   |_______|------'
-   |   |____________________|              _________                |  |
-   |                                       >________| <- 74LS245    |  |
-   |                                                                |  |
-   |___                                               ______________|  |
-       |H H H H H H H H H H H H H H H H H H H H H H H|               | |
-       |U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U_U|               | |
-                                                                      \|
-Legend:
-
-SL90C65        ARCNET Controller / Transceiver /Logic
-SW1    1-5:    IRQ Select
-         6:    ET1
-         7:    ET2
-         8:    ROM ENABLE 
-SW2    1-3:    Memory Buffer/PROM Address
-       3-6:    I/O Address Map
-SW3    1-8:    Node ID Select
-BNC            BNC RG62/U Connection 
-               *I* have had success using RG59B/U with *NO* terminators!
-               What gives?!
-
-SW1: Timeouts, Interrupt and ROM
----------------------------------
-
-To select a hardware interrupt level set one (only one!) of the dip switches
-up (on) SW1...(switches 1-5)
-IRQ3, IRQ4, IRQ5, IRQ7, IRQ2. The Manufacturer's default is IRQ2.
-
-The switches on SW1 labeled EXT1 (switch 6) and EXT2 (switch 7)
-are used to determine the timeout parameters. These two dip switches
-are normally left off (down).
-
-   To enable the 8K Boot PROM position SW1 switch 8 on (UP) labeled ROM.
-   The default is jumper ROM not installed.
-
-
-Setting the I/O Base Address
-----------------------------
-
-The last three switches in switch group SW2 are used to select one
-of eight possible I/O Base addresses using the following table
-
-
-   Switch | Hex I/O
-   4 5 6  | Address
-   -------|--------
-   0 0 0  |  260
-   0 0 1  |  290
-   0 1 0  |  2E0  (Manufacturer's default)
-   0 1 1  |  2F0
-   1 0 0  |  300
-   1 0 1  |  350
-   1 1 0  |  380
-   1 1 1  |  3E0
-
-
-Setting the Base Memory Address (RAM & ROM)
--------------------------------------------
-
-The memory buffer requires 2K of a 16K block of RAM. The base of this
-16K block can be located in any of eight positions.
-Switches 1-3 of switch group SW2 select the Base of the 16K block.
-(0 = DOWN, 1 = UP)
-I could, however, only verify two settings...
-
-   Switch| Hex RAM | Hex ROM
-   1 2 3 | Address | Address
-   ------|---------|-----------
-   0 0 0 |  E0000  |  E2000
-   0 0 1 |  D0000  |  D2000  (Manufacturer's default)
-   0 1 0 |  ?????  |  ?????
-   0 1 1 |  ?????  |  ?????  
-   1 0 0 |  ?????  |  ?????
-   1 0 1 |  ?????  |  ?????
-   1 1 0 |  ?????  |  ?????
-   1 1 1 |  ?????  |  ?????
-
-
-Setting the Node ID
--------------------
-
-The eight switches in group SW3 are used to set the node ID.
-Each node attached to the network must have an unique node ID which
-must be different from 0.
-Switch 1 serves as the least significant bit (LSB).
-switches in the DOWN position are OFF (0) and in the UP position are ON (1)
-
-The node ID is the sum of the values of all switches set to "1"  
-These values are:
-    Switch | Value
-    -------|-------
-      1    |   1
-      2    |   2
-      3    |   4
-      4    |   8
-      5    |  16
-      6    |  32
-      7    |  64
-      8    | 128
-
-Some Examples:
-
-    Switch#     |   Hex   | Decimal 
-8 7 6 5 4 3 2 1 | Node ID | Node ID
-----------------|---------|---------
-0 0 0 0 0 0 0 0 |    not allowed  <-.
-0 0 0 0 0 0 0 1 |    1    |    1    | 
-0 0 0 0 0 0 1 0 |    2    |    2    |
-0 0 0 0 0 0 1 1 |    3    |    3    |
-    . . .       |         |         |
-0 1 0 1 0 1 0 1 |   55    |   85    |
-    . . .       |         |         + Don't use 0 or 255!
-1 0 1 0 1 0 1 0 |   AA    |  170    |
-    . . .       |         |         |
-1 1 1 1 1 1 0 1 |   FD    |  253    |
-1 1 1 1 1 1 1 0 |   FE    |  254    |
-1 1 1 1 1 1 1 1 |   FF    |  255  <-'
-  
-
-*****************************************************************************
-
-** Tiara **
-(model unknown)
--------------------------
-  - from Christoph Lameter <christoph@lameter.com>
-  
-
-Here is information about my card as far as I could figure it out:
------------------------------------------------ tiara
-Tiara LanCard of Tiara Computer Systems.
-
-+----------------------------------------------+
-!           ! Transmitter Unit !               !
-!           +------------------+             -------
-!          MEM                              Coax Connector
-!  ROM    7654321 <- I/O                     -------
-!  :  :   +--------+                           !
-!  :  :   ! 90C66LJ!                         +++
-!  :  :   !        !                         !D  Switch to set
-!  :  :   !        !                         !I  the Nodenumber
-!  :  :   +--------+                         !P
-!                                            !++
-!         234567 <- IRQ                      !
-+------------!!!!!!!!!!!!!!!!!!!!!!!!--------+
-             !!!!!!!!!!!!!!!!!!!!!!!!
-
-0 = Jumper Installed
-1 = Open
-
-Top Jumper line Bit 7 = ROM Enable 654=Memory location 321=I/O
-
-Settings for Memory Location (Top Jumper Line)
-456     Address selected
-000    C0000
-001     C4000
-010     CC000
-011     D0000
-100     D4000
-101     D8000
-110     DC000     
-111     E0000
-
-Settings for I/O Address (Top Jumper Line)
-123     Port
-000    260
-001    290
-010    2E0
-011    2F0
-100    300
-101    350
-110    380
-111    3E0
-
-Settings for IRQ Selection (Lower Jumper Line)
-234567
-011111 IRQ 2
-101111 IRQ 3
-110111 IRQ 4
-111011 IRQ 5
-111110 IRQ 7
-
-*****************************************************************************
-
-
-Other Cards
------------
-
-I have no information on other models of ARCnet cards at the moment.  Please
-send any and all info to:
-       apenwarr@worldvisions.ca
-
-Thanks.
diff --git a/Documentation/networking/arcnet.rst b/Documentation/networking/arcnet.rst
new file mode 100644 (file)
index 0000000..e93d982
--- /dev/null
@@ -0,0 +1,594 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======
+ARCnet
+======
+
+.. note::
+
+   See also arcnet-hardware.txt in this directory for jumper-setting
+   and cabling information if you're like many of us and didn't happen to get a
+   manual with your ARCnet card.
+
+Since no one seems to listen to me otherwise, perhaps a poem will get your
+attention::
+
+               This driver's getting fat and beefy,
+               But my cat is still named Fifi.
+
+Hmm, I think I'm allowed to call that a poem, even though it's only two
+lines.  Hey, I'm in Computer Science, not English.  Give me a break.
+
+The point is:  I REALLY REALLY REALLY REALLY REALLY want to hear from you if
+you test this and get it working.  Or if you don't.  Or anything.
+
+ARCnet 0.32 ALPHA first made it into the Linux kernel 1.1.80 - this was
+nice, but after that even FEWER people started writing to me because they
+didn't even have to install the patch.  <sigh>
+
+Come on, be a sport!  Send me a success report!
+
+(hey, that was even better than my original poem... this is getting bad!)
+
+
+.. warning::
+
+   If you don't e-mail me about your success/failure soon, I may be forced to
+   start SINGING.  And we don't want that, do we?
+
+   (You know, it might be argued that I'm pushing this point a little too much.
+   If you think so, why not flame me in a quick little e-mail?  Please also
+   include the type of card(s) you're using, software, size of network, and
+   whether it's working or not.)
+
+   My e-mail address is: apenwarr@worldvisions.ca
+
+These are the ARCnet drivers for Linux.
+
+This new release (2.91) has been put together by David Woodhouse
+<dwmw2@infradead.org>, in an attempt to tidy up the driver after adding support
+for yet another chipset. Now the generic support has been separated from the
+individual chipset drivers, and the source files aren't quite so packed with
+#ifdefs! I've changed this file a bit, but kept it in the first person from
+Avery, because I didn't want to completely rewrite it.
+
+The previous release resulted from many months of on-and-off effort from me
+(Avery Pennarun), many bug reports/fixes and suggestions from others, and in
+particular a lot of input and coding from Tomasz Motylewski.  Starting with
+ARCnet 2.10 ALPHA, Tomasz's all-new-and-improved RFC1051 support has been
+included and seems to be working fine!
+
+
+Where do I discuss these drivers?
+---------------------------------
+
+Tomasz has been so kind as to set up a new and improved mailing list.
+Subscribe by sending a message with the BODY "subscribe linux-arcnet YOUR
+REAL NAME" to listserv@tichy.ch.uj.edu.pl.  Then, to submit messages to the
+list, mail to linux-arcnet@tichy.ch.uj.edu.pl.
+
+There are archives of the mailing list at:
+
+       http://epistolary.org/mailman/listinfo.cgi/arcnet
+
+The people on linux-net@vger.kernel.org (now defunct, replaced by
+netdev@vger.kernel.org) have also been known to be very helpful, especially
+when we're talking about ALPHA Linux kernels that may or may not work right
+in the first place.
+
+
+Other Drivers and Info
+----------------------
+
+You can try my ARCNET page on the World Wide Web at:
+
+       http://www.qis.net/~jschmitz/arcnet/
+
+Also, SMC (one of the companies that makes ARCnet cards) has a WWW site you
+might be interested in, which includes several drivers for various cards
+including ARCnet.  Try:
+
+       http://www.smc.com/
+
+Performance Technologies makes various network software that supports
+ARCnet:
+
+       http://www.perftech.com/ or ftp to ftp.perftech.com.
+
+Novell makes a networking stack for DOS which includes ARCnet drivers.  Try
+FTPing to ftp.novell.com.
+
+You can get the Crynwr packet driver collection (including arcether.com, the
+one you'll want to use with ARCnet cards) from
+oak.oakland.edu:/simtel/msdos/pktdrvr. It won't work perfectly on a 386+
+without patches, though, and also doesn't like several cards.  Fixed
+versions are available on my WWW page, or via e-mail if you don't have WWW
+access.
+
+
+Installing the Driver
+---------------------
+
+All you will need to do in order to install the driver is::
+
+       make config
+               (be sure to choose ARCnet in the network devices
+               and at least one chipset driver.)
+       make clean
+       make zImage
+
+If you obtained this ARCnet package as an upgrade to the ARCnet driver in
+your current kernel, you will need to first copy arcnet.c over the one in
+the linux/drivers/net directory.
+
+You will know the driver is installed properly if you get some ARCnet
+messages when you reboot into the new Linux kernel.
+
+There are four chipset options:
+
+ 1. Standard ARCnet COM90xx chipset.
+
+This is the normal ARCnet card, which you've probably got. This is the only
+chipset driver which will autoprobe if not told where the card is.
+It following options on the command line::
+
+ com90xx=[<io>[,<irq>[,<shmem>]]][,<name>] | <name>
+
+If you load the chipset support as a module, the options are::
+
+ io=<io> irq=<irq> shmem=<shmem> device=<name>
+
+To disable the autoprobe, just specify "com90xx=" on the kernel command line.
+To specify the name alone, but allow autoprobe, just put "com90xx=<name>"
+
+ 2. ARCnet COM20020 chipset.
+
+This is the new chipset from SMC with support for promiscuous mode (packet
+sniffing), extra diagnostic information, etc. Unfortunately, there is no
+sensible method of autoprobing for these cards. You must specify the I/O
+address on the kernel command line.
+
+The command line options are::
+
+ com20020=<io>[,<irq>[,<node_ID>[,backplane[,CKP[,timeout]]]]][,name]
+
+If you load the chipset support as a module, the options are::
+
+ io=<io> irq=<irq> node=<node_ID> backplane=<backplane> clock=<CKP>
+ timeout=<timeout> device=<name>
+
+The COM20020 chipset allows you to set the node ID in software, overriding the
+default which is still set in DIP switches on the card. If you don't have the
+COM20020 data sheets, and you don't know what the other three options refer
+to, then they won't interest you - forget them.
+
+ 3. ARCnet COM90xx chipset in IO-mapped mode.
+
+This will also work with the normal ARCnet cards, but doesn't use the shared
+memory. It performs less well than the above driver, but is provided in case
+you have a card which doesn't support shared memory, or (strangely) in case
+you have so many ARCnet cards in your machine that you run out of shmem slots.
+If you don't give the IO address on the kernel command line, then the driver
+will not find the card.
+
+The command line options are::
+
+ com90io=<io>[,<irq>][,<name>]
+
+If you load the chipset support as a module, the options are:
+ io=<io> irq=<irq> device=<name>
+
+ 4. ARCnet RIM I cards.
+
+These are COM90xx chips which are _completely_ memory mapped. The support for
+these is not tested. If you have one, please mail the author with a success
+report. All options must be specified, except the device name.
+Command line options::
+
+ arcrimi=<shmem>,<irq>,<node_ID>[,<name>]
+
+If you load the chipset support as a module, the options are::
+
+ shmem=<shmem> irq=<irq> node=<node_ID> device=<name>
+
+
+Loadable Module Support
+-----------------------
+
+Configure and rebuild Linux.  When asked, answer 'm' to "Generic ARCnet
+support" and to support for your ARCnet chipset if you want to use the
+loadable module. You can also say 'y' to "Generic ARCnet support" and 'm'
+to the chipset support if you wish.
+
+::
+
+       make config
+       make clean
+       make zImage
+       make modules
+
+If you're using a loadable module, you need to use insmod to load it, and
+you can specify various characteristics of your card on the command
+line.  (In recent versions of the driver, autoprobing is much more reliable
+and works as a module, so most of this is now unnecessary.)
+
+For example::
+
+       cd /usr/src/linux/modules
+       insmod arcnet.o
+       insmod com90xx.o
+       insmod com20020.o io=0x2e0 device=eth1
+
+
+Using the Driver
+----------------
+
+If you build your kernel with ARCnet COM90xx support included, it should
+probe for your card automatically when you boot. If you use a different
+chipset driver complied into the kernel, you must give the necessary options
+on the kernel command line, as detailed above.
+
+Go read the NET-2-HOWTO and ETHERNET-HOWTO for Linux; they should be
+available where you picked up this driver.  Think of your ARCnet as a
+souped-up (or down, as the case may be) Ethernet card.
+
+By the way, be sure to change all references from "eth0" to "arc0" in the
+HOWTOs.  Remember that ARCnet isn't a "true" Ethernet, and the device name
+is DIFFERENT.
+
+
+Multiple Cards in One Computer
+------------------------------
+
+Linux has pretty good support for this now, but since I've been busy, the
+ARCnet driver has somewhat suffered in this respect. COM90xx support, if
+compiled into the kernel, will (try to) autodetect all the installed cards.
+
+If you have other cards, with support compiled into the kernel, then you can
+just repeat the options on the kernel command line, e.g.::
+
+       LILO: linux com20020=0x2e0 com20020=0x380 com90io=0x260
+
+If you have the chipset support built as a loadable module, then you need to
+do something like this::
+
+       insmod -o arc0 com90xx
+       insmod -o arc1 com20020 io=0x2e0
+       insmod -o arc2 com90xx
+
+The ARCnet drivers will now sort out their names automatically.
+
+
+How do I get it to work with...?
+--------------------------------
+
+NFS:
+       Should be fine linux->linux, just pretend you're using Ethernet cards.
+       oak.oakland.edu:/simtel/msdos/nfs has some nice DOS clients.  There
+       is also a DOS-based NFS server called SOSS.  It doesn't multitask
+       quite the way Linux does (actually, it doesn't multitask AT ALL) but
+       you never know what you might need.
+
+       With AmiTCP (and possibly others), you may need to set the following
+       options in your Amiga nfstab:  MD 1024 MR 1024 MW 1024
+       (Thanks to Christian Gottschling <ferksy@indigo.tng.oche.de>
+       for this.)
+
+       Probably these refer to maximum NFS data/read/write block sizes.  I
+       don't know why the defaults on the Amiga didn't work; write to me if
+       you know more.
+
+DOS:
+       If you're using the freeware arcether.com, you might want to install
+       the driver patch from my web page.  It helps with PC/TCP, and also
+       can get arcether to load if it timed out too quickly during
+       initialization.  In fact, if you use it on a 386+ you REALLY need
+       the patch, really.
+
+Windows:
+       See DOS :)  Trumpet Winsock works fine with either the Novell or
+       Arcether client, assuming you remember to load winpkt of course.
+
+LAN Manager and Windows for Workgroups:
+       These programs use protocols that
+       are incompatible with the Internet standard.  They try to pretend
+       the cards are Ethernet, and confuse everyone else on the network.
+
+       However, v2.00 and higher of the Linux ARCnet driver supports this
+       protocol via the 'arc0e' device.  See the section on "Multiprotocol
+       Support" for more information.
+
+       Using the freeware Samba server and clients for Linux, you can now
+       interface quite nicely with TCP/IP-based WfWg or Lan Manager
+       networks.
+
+Windows 95:
+       Tools are included with Win95 that let you use either the LANMAN
+       style network drivers (NDIS) or Novell drivers (ODI) to handle your
+       ARCnet packets.  If you use ODI, you'll need to use the 'arc0'
+       device with Linux.  If you use NDIS, then try the 'arc0e' device.
+       See the "Multiprotocol Support" section below if you need arc0e,
+       you're completely insane, and/or you need to build some kind of
+       hybrid network that uses both encapsulation types.
+
+OS/2:
+       I've been told it works under Warp Connect with an ARCnet driver from
+       SMC.  You need to use the 'arc0e' interface for this.  If you get
+       the SMC driver to work with the TCP/IP stuff included in the
+       "normal" Warp Bonus Pack, let me know.
+
+       ftp.microsoft.com also has a freeware "Lan Manager for OS/2" client
+       which should use the same protocol as WfWg does.  I had no luck
+       installing it under Warp, however.  Please mail me with any results.
+
+NetBSD/AmiTCP:
+       These use an old version of the Internet standard ARCnet
+       protocol (RFC1051) which is compatible with the Linux driver v2.10
+       ALPHA and above using the arc0s device. (See "Multiprotocol ARCnet"
+       below.)  ** Newer versions of NetBSD apparently support RFC1201.
+
+
+Using Multiprotocol ARCnet
+--------------------------
+
+The ARCnet driver v2.10 ALPHA supports three protocols, each on its own
+"virtual network device":
+
+       ======  ===============================================================
+       arc0    RFC1201 protocol, the official Internet standard which just
+               happens to be 100% compatible with Novell's TRXNET driver.
+               Version 1.00 of the ARCnet driver supported _only_ this
+               protocol.  arc0 is the fastest of the three protocols (for
+               whatever reason), and allows larger packets to be used
+               because it supports RFC1201 "packet splitting" operations.
+               Unless you have a specific need to use a different protocol,
+               I strongly suggest that you stick with this one.
+
+       arc0e   "Ethernet-Encapsulation" which sends packets over ARCnet
+               that are actually a lot like Ethernet packets, including the
+               6-byte hardware addresses.  This protocol is compatible with
+               Microsoft's NDIS ARCnet driver, like the one in WfWg and
+               LANMAN.  Because the MTU of 493 is actually smaller than the
+               one "required" by TCP/IP (576), there is a chance that some
+               network operations will not function properly.  The Linux
+               TCP/IP layer can compensate in most cases, however, by
+               automatically fragmenting the TCP/IP packets to make them
+               fit.  arc0e also works slightly more slowly than arc0, for
+               reasons yet to be determined.  (Probably it's the smaller
+               MTU that does it.)
+
+       arc0s   The "[s]imple" RFC1051 protocol is the "previous" Internet
+               standard that is completely incompatible with the new
+               standard.  Some software today, however, continues to
+               support the old standard (and only the old standard)
+               including NetBSD and AmiTCP.  RFC1051 also does not support
+               RFC1201's packet splitting, and the MTU of 507 is still
+               smaller than the Internet "requirement," so it's quite
+               possible that you may run into problems.  It's also slower
+               than RFC1201 by about 25%, for the same reason as arc0e.
+
+               The arc0s support was contributed by Tomasz Motylewski
+               and modified somewhat by me.  Bugs are probably my fault.
+       ======  ===============================================================
+
+You can choose not to compile arc0e and arc0s into the driver if you want -
+this will save you a bit of memory and avoid confusion when eg. trying to
+use the "NFS-root" stuff in recent Linux kernels.
+
+The arc0e and arc0s devices are created automatically when you first
+ifconfig the arc0 device.  To actually use them, though, you need to also
+ifconfig the other virtual devices you need.  There are a number of ways you
+can set up your network then:
+
+
+1. Single Protocol.
+
+   This is the simplest way to configure your network: use just one of the
+   two available protocols.  As mentioned above, it's a good idea to use
+   only arc0 unless you have a good reason (like some other software, ie.
+   WfWg, that only works with arc0e).
+
+   If you need only arc0, then the following commands should get you going::
+
+       ifconfig arc0 MY.IP.ADD.RESS
+       route add MY.IP.ADD.RESS arc0
+       route add -net SUB.NET.ADD.RESS arc0
+       [add other local routes here]
+
+   If you need arc0e (and only arc0e), it's a little different::
+
+       ifconfig arc0 MY.IP.ADD.RESS
+       ifconfig arc0e MY.IP.ADD.RESS
+       route add MY.IP.ADD.RESS arc0e
+       route add -net SUB.NET.ADD.RESS arc0e
+
+   arc0s works much the same way as arc0e.
+
+
+2. More than one protocol on the same wire.
+
+   Now things start getting confusing.  To even try it, you may need to be
+   partly crazy.  Here's what *I* did. :) Note that I don't include arc0s in
+   my home network; I don't have any NetBSD or AmiTCP computers, so I only
+   use arc0s during limited testing.
+
+   I have three computers on my home network; two Linux boxes (which prefer
+   RFC1201 protocol, for reasons listed above), and one XT that can't run
+   Linux but runs the free Microsoft LANMAN Client instead.
+
+   Worse, one of the Linux computers (freedom) also has a modem and acts as
+   a router to my Internet provider.  The other Linux box (insight) also has
+   its own IP address and needs to use freedom as its default gateway.  The
+   XT (patience), however, does not have its own Internet IP address and so
+   I assigned it one on a "private subnet" (as defined by RFC1597).
+
+   To start with, take a simple network with just insight and freedom.
+   Insight needs to:
+
+       - talk to freedom via RFC1201 (arc0) protocol, because I like it
+         more and it's faster.
+       - use freedom as its Internet gateway.
+
+   That's pretty easy to do.  Set up insight like this::
+
+       ifconfig arc0 insight
+       route add insight arc0
+       route add freedom arc0  /* I would use the subnet here (like I said
+                                       to to in "single protocol" above),
+                                       but the rest of the subnet
+                                       unfortunately lies across the PPP
+                                       link on freedom, which confuses
+                                       things. */
+       route add default gw freedom
+
+   And freedom gets configured like so::
+
+       ifconfig arc0 freedom
+       route add freedom arc0
+       route add insight arc0
+       /* and default gateway is configured by pppd */
+
+   Great, now insight talks to freedom directly on arc0, and sends packets
+   to the Internet through freedom.  If you didn't know how to do the above,
+   you should probably stop reading this section now because it only gets
+   worse.
+
+   Now, how do I add patience into the network?  It will be using LANMAN
+   Client, which means I need the arc0e device.  It needs to be able to talk
+   to both insight and freedom, and also use freedom as a gateway to the
+   Internet.  (Recall that patience has a "private IP address" which won't
+   work on the Internet; that's okay, I configured Linux IP masquerading on
+   freedom for this subnet).
+
+   So patience (necessarily; I don't have another IP number from my
+   provider) has an IP address on a different subnet than freedom and
+   insight, but needs to use freedom as an Internet gateway.  Worse, most
+   DOS networking programs, including LANMAN, have braindead networking
+   schemes that rely completely on the netmask and a 'default gateway' to
+   determine how to route packets.  This means that to get to freedom or
+   insight, patience WILL send through its default gateway, regardless of
+   the fact that both freedom and insight (courtesy of the arc0e device)
+   could understand a direct transmission.
+
+   I compensate by giving freedom an extra IP address - aliased 'gatekeeper' -
+   that is on my private subnet, the same subnet that patience is on.  I
+   then define gatekeeper to be the default gateway for patience.
+
+   To configure freedom (in addition to the commands above)::
+
+       ifconfig arc0e gatekeeper
+       route add gatekeeper arc0e
+       route add patience arc0e
+
+   This way, freedom will send all packets for patience through arc0e,
+   giving its IP address as gatekeeper (on the private subnet).  When it
+   talks to insight or the Internet, it will use its "freedom" Internet IP
+   address.
+
+   You will notice that we haven't configured the arc0e device on insight.
+   This would work, but is not really necessary, and would require me to
+   assign insight another special IP number from my private subnet.  Since
+   both insight and patience are using freedom as their default gateway, the
+   two can already talk to each other.
+
+   It's quite fortunate that I set things up like this the first time (cough
+   cough) because it's really handy when I boot insight into DOS.  There, it
+   runs the Novell ODI protocol stack, which only works with RFC1201 ARCnet.
+   In this mode it would be impossible for insight to communicate directly
+   with patience, since the Novell stack is incompatible with Microsoft's
+   Ethernet-Encap.  Without changing any settings on freedom or patience, I
+   simply set freedom as the default gateway for insight (now in DOS,
+   remember) and all the forwarding happens "automagically" between the two
+   hosts that would normally not be able to communicate at all.
+
+   For those who like diagrams, I have created two "virtual subnets" on the
+   same physical ARCnet wire.  You can picture it like this::
+
+
+         [RFC1201 NETWORK]                   [ETHER-ENCAP NETWORK]
+      (registered Internet subnet)           (RFC1597 private subnet)
+
+                            (IP Masquerade)
+         /---------------\         *            /---------------\
+         |               |         *            |               |
+         |               +-Freedom-*-Gatekeeper-+               |
+         |               |    |    *            |               |
+         \-------+-------/    |    *            \-------+-------/
+                 |            |                         |
+              Insight         |                      Patience
+                          (Internet)
+
+
+
+It works: what now?
+-------------------
+
+Send mail describing your setup, preferably including driver version, kernel
+version, ARCnet card model, CPU type, number of systems on your network, and
+list of software in use to me at the following address:
+
+       apenwarr@worldvisions.ca
+
+I do send (sometimes automated) replies to all messages I receive.  My email
+can be weird (and also usually gets forwarded all over the place along the
+way to me), so if you don't get a reply within a reasonable time, please
+resend.
+
+
+It doesn't work: what now?
+--------------------------
+
+Do the same as above, but also include the output of the ifconfig and route
+commands, as well as any pertinent log entries (ie. anything that starts
+with "arcnet:" and has shown up since the last reboot) in your mail.
+
+If you want to try fixing it yourself (I strongly recommend that you mail me
+about the problem first, since it might already have been solved) you may
+want to try some of the debug levels available.  For heavy testing on
+D_DURING or more, it would be a REALLY good idea to kill your klogd daemon
+first!  D_DURING displays 4-5 lines for each packet sent or received.  D_TX,
+D_RX, and D_SKB actually DISPLAY each packet as it is sent or received,
+which is obviously quite big.
+
+Starting with v2.40 ALPHA, the autoprobe routines have changed
+significantly.  In particular, they won't tell you why the card was not
+found unless you turn on the D_INIT_REASONS debugging flag.
+
+Once the driver is running, you can run the arcdump shell script (available
+from me or in the full ARCnet package, if you have it) as root to list the
+contents of the arcnet buffers at any time.  To make any sense at all out of
+this, you should grab the pertinent RFCs. (some are listed near the top of
+arcnet.c).  arcdump assumes your card is at 0xD0000.  If it isn't, edit the
+script.
+
+Buffers 0 and 1 are used for receiving, and Buffers 2 and 3 are for sending.
+Ping-pong buffers are implemented both ways.
+
+If your debug level includes D_DURING and you did NOT define SLOW_XMIT_COPY,
+the buffers are cleared to a constant value of 0x42 every time the card is
+reset (which should only happen when you do an ifconfig up, or when Linux
+decides that the driver is broken).  During a transmit, unused parts of the
+buffer will be cleared to 0x42 as well.  This is to make it easier to figure
+out which bytes are being used by a packet.
+
+You can change the debug level without recompiling the kernel by typing::
+
+       ifconfig arc0 down metric 1xxx
+       /etc/rc.d/rc.inet1
+
+where "xxx" is the debug level you want.  For example, "metric 1015" would put
+you at debug level 15.  Debug level 7 is currently the default.
+
+Note that the debug level is (starting with v1.90 ALPHA) a binary
+combination of different debug flags; so debug level 7 is really 1+2+4 or
+D_NORMAL+D_EXTRA+D_INIT.  To include D_DURING, you would add 16 to this,
+resulting in debug level 23.
+
+If you don't understand that, you probably don't want to know anyway.
+E-mail me about your problem.
+
+
+I want to send money: what now?
+-------------------------------
+
+Go take a nap or something.  You'll feel better in the morning.
diff --git a/Documentation/networking/arcnet.txt b/Documentation/networking/arcnet.txt
deleted file mode 100644 (file)
index aff97f4..0000000
+++ /dev/null
@@ -1,556 +0,0 @@
-----------------------------------------------------------------------------
-NOTE:  See also arcnet-hardware.txt in this directory for jumper-setting
-and cabling information if you're like many of us and didn't happen to get a
-manual with your ARCnet card.
-----------------------------------------------------------------------------
-
-Since no one seems to listen to me otherwise, perhaps a poem will get your
-attention:
-               This driver's getting fat and beefy,
-               But my cat is still named Fifi.
-
-Hmm, I think I'm allowed to call that a poem, even though it's only two
-lines.  Hey, I'm in Computer Science, not English.  Give me a break.
-
-The point is:  I REALLY REALLY REALLY REALLY REALLY want to hear from you if
-you test this and get it working.  Or if you don't.  Or anything.
-
-ARCnet 0.32 ALPHA first made it into the Linux kernel 1.1.80 - this was
-nice, but after that even FEWER people started writing to me because they
-didn't even have to install the patch.  <sigh>
-
-Come on, be a sport!  Send me a success report!
-
-(hey, that was even better than my original poem... this is getting bad!)
-
-
---------
-WARNING:
---------
-
-If you don't e-mail me about your success/failure soon, I may be forced to
-start SINGING.  And we don't want that, do we?
-
-(You know, it might be argued that I'm pushing this point a little too much. 
-If you think so, why not flame me in a quick little e-mail?  Please also
-include the type of card(s) you're using, software, size of network, and
-whether it's working or not.)
-
-My e-mail address is: apenwarr@worldvisions.ca
-
-
----------------------------------------------------------------------------
-
-                       
-These are the ARCnet drivers for Linux.
-
-
-This new release (2.91) has been put together by David Woodhouse 
-<dwmw2@infradead.org>, in an attempt to tidy up the driver after adding support
-for yet another chipset. Now the generic support has been separated from the
-individual chipset drivers, and the source files aren't quite so packed with
-#ifdefs! I've changed this file a bit, but kept it in the first person from
-Avery, because I didn't want to completely rewrite it.
-
-The previous release resulted from many months of on-and-off effort from me
-(Avery Pennarun), many bug reports/fixes and suggestions from others, and in
-particular a lot of input and coding from Tomasz Motylewski.  Starting with
-ARCnet 2.10 ALPHA, Tomasz's all-new-and-improved RFC1051 support has been
-included and seems to be working fine!
-
-
-Where do I discuss these drivers?
----------------------------------
-
-Tomasz has been so kind as to set up a new and improved mailing list. 
-Subscribe by sending a message with the BODY "subscribe linux-arcnet YOUR
-REAL NAME" to listserv@tichy.ch.uj.edu.pl.  Then, to submit messages to the
-list, mail to linux-arcnet@tichy.ch.uj.edu.pl.
-
-There are archives of the mailing list at:
-       http://epistolary.org/mailman/listinfo.cgi/arcnet
-
-The people on linux-net@vger.kernel.org (now defunct, replaced by
-netdev@vger.kernel.org) have also been known to be very helpful, especially
-when we're talking about ALPHA Linux kernels that may or may not work right
-in the first place.
-
-
-Other Drivers and Info
-----------------------
-
-You can try my ARCNET page on the World Wide Web at:
-       http://www.qis.net/~jschmitz/arcnet/    
-
-Also, SMC (one of the companies that makes ARCnet cards) has a WWW site you
-might be interested in, which includes several drivers for various cards
-including ARCnet.  Try:
-       http://www.smc.com/
-       
-Performance Technologies makes various network software that supports
-ARCnet:
-       http://www.perftech.com/ or ftp to ftp.perftech.com.
-       
-Novell makes a networking stack for DOS which includes ARCnet drivers.  Try
-FTPing to ftp.novell.com.
-
-You can get the Crynwr packet driver collection (including arcether.com, the
-one you'll want to use with ARCnet cards) from
-oak.oakland.edu:/simtel/msdos/pktdrvr. It won't work perfectly on a 386+
-without patches, though, and also doesn't like several cards.  Fixed
-versions are available on my WWW page, or via e-mail if you don't have WWW
-access. 
-
-
-Installing the Driver
----------------------
-
-All you will need to do in order to install the driver is:
-       make config
-               (be sure to choose ARCnet in the network devices 
-               and at least one chipset driver.)
-       make clean
-       make zImage
-       
-If you obtained this ARCnet package as an upgrade to the ARCnet driver in
-your current kernel, you will need to first copy arcnet.c over the one in
-the linux/drivers/net directory.
-
-You will know the driver is installed properly if you get some ARCnet
-messages when you reboot into the new Linux kernel.
-
-There are four chipset options:
-
- 1. Standard ARCnet COM90xx chipset.
-
-This is the normal ARCnet card, which you've probably got. This is the only
-chipset driver which will autoprobe if not told where the card is.
-It following options on the command line:
- com90xx=[<io>[,<irq>[,<shmem>]]][,<name>] | <name>
-
-If you load the chipset support as a module, the options are:
- io=<io> irq=<irq> shmem=<shmem> device=<name>
-
-To disable the autoprobe, just specify "com90xx=" on the kernel command line.
-To specify the name alone, but allow autoprobe, just put "com90xx=<name>"
-
- 2. ARCnet COM20020 chipset.
-
-This is the new chipset from SMC with support for promiscuous mode (packet 
-sniffing), extra diagnostic information, etc. Unfortunately, there is no
-sensible method of autoprobing for these cards. You must specify the I/O
-address on the kernel command line.
-The command line options are:
- com20020=<io>[,<irq>[,<node_ID>[,backplane[,CKP[,timeout]]]]][,name]
-
-If you load the chipset support as a module, the options are:
- io=<io> irq=<irq> node=<node_ID> backplane=<backplane> clock=<CKP>
- timeout=<timeout> device=<name>
-
-The COM20020 chipset allows you to set the node ID in software, overriding the
-default which is still set in DIP switches on the card. If you don't have the
-COM20020 data sheets, and you don't know what the other three options refer
-to, then they won't interest you - forget them.
-
- 3. ARCnet COM90xx chipset in IO-mapped mode.
-
-This will also work with the normal ARCnet cards, but doesn't use the shared
-memory. It performs less well than the above driver, but is provided in case
-you have a card which doesn't support shared memory, or (strangely) in case
-you have so many ARCnet cards in your machine that you run out of shmem slots.
-If you don't give the IO address on the kernel command line, then the driver
-will not find the card.
-The command line options are:
- com90io=<io>[,<irq>][,<name>] 
-
-If you load the chipset support as a module, the options are:
- io=<io> irq=<irq> device=<name>
-
- 4. ARCnet RIM I cards.
-
-These are COM90xx chips which are _completely_ memory mapped. The support for
-these is not tested. If you have one, please mail the author with a success 
-report. All options must be specified, except the device name.
-Command line options:
- arcrimi=<shmem>,<irq>,<node_ID>[,<name>]
-
-If you load the chipset support as a module, the options are:
- shmem=<shmem> irq=<irq> node=<node_ID> device=<name>
-
-
-Loadable Module Support
------------------------
-
-Configure and rebuild Linux.  When asked, answer 'm' to "Generic ARCnet 
-support" and to support for your ARCnet chipset if you want to use the
-loadable module. You can also say 'y' to "Generic ARCnet support" and 'm' 
-to the chipset support if you wish.
-
-       make config
-       make clean      
-       make zImage
-       make modules
-       
-If you're using a loadable module, you need to use insmod to load it, and
-you can specify various characteristics of your card on the command
-line.  (In recent versions of the driver, autoprobing is much more reliable
-and works as a module, so most of this is now unnecessary.)
-
-For example:
-       cd /usr/src/linux/modules
-       insmod arcnet.o
-       insmod com90xx.o
-       insmod com20020.o io=0x2e0 device=eth1
-       
-
-Using the Driver
-----------------
-
-If you build your kernel with ARCnet COM90xx support included, it should 
-probe for your card automatically when you boot. If you use a different
-chipset driver complied into the kernel, you must give the necessary options
-on the kernel command line, as detailed above.
-
-Go read the NET-2-HOWTO and ETHERNET-HOWTO for Linux; they should be
-available where you picked up this driver.  Think of your ARCnet as a
-souped-up (or down, as the case may be) Ethernet card.
-
-By the way, be sure to change all references from "eth0" to "arc0" in the
-HOWTOs.  Remember that ARCnet isn't a "true" Ethernet, and the device name
-is DIFFERENT.
-
-
-Multiple Cards in One Computer
-------------------------------
-
-Linux has pretty good support for this now, but since I've been busy, the
-ARCnet driver has somewhat suffered in this respect. COM90xx support, if 
-compiled into the kernel, will (try to) autodetect all the installed cards. 
-
-If you have other cards, with support compiled into the kernel, then you can 
-just repeat the options on the kernel command line, e.g.:
-LILO: linux com20020=0x2e0 com20020=0x380 com90io=0x260
-
-If you have the chipset support built as a loadable module, then you need to 
-do something like this:
-       insmod -o arc0 com90xx
-       insmod -o arc1 com20020 io=0x2e0
-       insmod -o arc2 com90xx
-The ARCnet drivers will now sort out their names automatically.
-
-
-How do I get it to work with...?
---------------------------------
-
-NFS: Should be fine linux->linux, just pretend you're using Ethernet cards. 
-        oak.oakland.edu:/simtel/msdos/nfs has some nice DOS clients.  There
-        is also a DOS-based NFS server called SOSS.  It doesn't multitask
-        quite the way Linux does (actually, it doesn't multitask AT ALL) but
-        you never know what you might need.
-        
-        With AmiTCP (and possibly others), you may need to set the following
-        options in your Amiga nfstab:  MD 1024 MR 1024 MW 1024
-        (Thanks to Christian Gottschling <ferksy@indigo.tng.oche.de>
-       for this.)
-       
-       Probably these refer to maximum NFS data/read/write block sizes.  I
-       don't know why the defaults on the Amiga didn't work; write to me if
-       you know more.
-
-DOS: If you're using the freeware arcether.com, you might want to install
-        the driver patch from my web page.  It helps with PC/TCP, and also
-        can get arcether to load if it timed out too quickly during
-        initialization.  In fact, if you use it on a 386+ you REALLY need
-        the patch, really.
-       
-Windows:  See DOS :)  Trumpet Winsock works fine with either the Novell or
-       Arcether client, assuming you remember to load winpkt of course.
-
-LAN Manager and Windows for Workgroups: These programs use protocols that
-        are incompatible with the Internet standard.  They try to pretend
-        the cards are Ethernet, and confuse everyone else on the network. 
-        
-        However, v2.00 and higher of the Linux ARCnet driver supports this
-        protocol via the 'arc0e' device.  See the section on "Multiprotocol
-        Support" for more information.
-
-       Using the freeware Samba server and clients for Linux, you can now
-       interface quite nicely with TCP/IP-based WfWg or Lan Manager
-       networks.
-       
-Windows 95: Tools are included with Win95 that let you use either the LANMAN
-       style network drivers (NDIS) or Novell drivers (ODI) to handle your
-       ARCnet packets.  If you use ODI, you'll need to use the 'arc0'
-       device with Linux.  If you use NDIS, then try the 'arc0e' device. 
-       See the "Multiprotocol Support" section below if you need arc0e,
-       you're completely insane, and/or you need to build some kind of
-       hybrid network that uses both encapsulation types.
-
-OS/2: I've been told it works under Warp Connect with an ARCnet driver from
-       SMC.  You need to use the 'arc0e' interface for this.  If you get
-       the SMC driver to work with the TCP/IP stuff included in the
-       "normal" Warp Bonus Pack, let me know.
-
-       ftp.microsoft.com also has a freeware "Lan Manager for OS/2" client
-       which should use the same protocol as WfWg does.  I had no luck
-       installing it under Warp, however.  Please mail me with any results.
-
-NetBSD/AmiTCP: These use an old version of the Internet standard ARCnet
-       protocol (RFC1051) which is compatible with the Linux driver v2.10
-       ALPHA and above using the arc0s device. (See "Multiprotocol ARCnet"
-       below.)  ** Newer versions of NetBSD apparently support RFC1201.
-
-
-Using Multiprotocol ARCnet
---------------------------
-
-The ARCnet driver v2.10 ALPHA supports three protocols, each on its own
-"virtual network device":
-
-       arc0  - RFC1201 protocol, the official Internet standard which just
-               happens to be 100% compatible with Novell's TRXNET driver. 
-               Version 1.00 of the ARCnet driver supported _only_ this
-               protocol.  arc0 is the fastest of the three protocols (for
-               whatever reason), and allows larger packets to be used
-               because it supports RFC1201 "packet splitting" operations. 
-               Unless you have a specific need to use a different protocol,
-               I strongly suggest that you stick with this one.
-               
-       arc0e - "Ethernet-Encapsulation" which sends packets over ARCnet
-               that are actually a lot like Ethernet packets, including the
-               6-byte hardware addresses.  This protocol is compatible with
-               Microsoft's NDIS ARCnet driver, like the one in WfWg and
-               LANMAN.  Because the MTU of 493 is actually smaller than the
-               one "required" by TCP/IP (576), there is a chance that some
-               network operations will not function properly.  The Linux
-               TCP/IP layer can compensate in most cases, however, by
-               automatically fragmenting the TCP/IP packets to make them
-               fit.  arc0e also works slightly more slowly than arc0, for
-               reasons yet to be determined.  (Probably it's the smaller
-               MTU that does it.)
-               
-       arc0s - The "[s]imple" RFC1051 protocol is the "previous" Internet
-               standard that is completely incompatible with the new
-               standard.  Some software today, however, continues to
-               support the old standard (and only the old standard)
-               including NetBSD and AmiTCP.  RFC1051 also does not support
-               RFC1201's packet splitting, and the MTU of 507 is still
-               smaller than the Internet "requirement," so it's quite
-               possible that you may run into problems.  It's also slower
-               than RFC1201 by about 25%, for the same reason as arc0e.
-               
-               The arc0s support was contributed by Tomasz Motylewski
-               and modified somewhat by me.  Bugs are probably my fault.
-
-You can choose not to compile arc0e and arc0s into the driver if you want -
-this will save you a bit of memory and avoid confusion when eg. trying to
-use the "NFS-root" stuff in recent Linux kernels.
-
-The arc0e and arc0s devices are created automatically when you first
-ifconfig the arc0 device.  To actually use them, though, you need to also
-ifconfig the other virtual devices you need.  There are a number of ways you
-can set up your network then:
-
-
-1. Single Protocol.
-
-   This is the simplest way to configure your network: use just one of the
-   two available protocols.  As mentioned above, it's a good idea to use
-   only arc0 unless you have a good reason (like some other software, ie.
-   WfWg, that only works with arc0e).
-   
-   If you need only arc0, then the following commands should get you going:
-       ifconfig arc0 MY.IP.ADD.RESS
-       route add MY.IP.ADD.RESS arc0
-       route add -net SUB.NET.ADD.RESS arc0
-       [add other local routes here]
-       
-   If you need arc0e (and only arc0e), it's a little different:
-       ifconfig arc0 MY.IP.ADD.RESS
-       ifconfig arc0e MY.IP.ADD.RESS
-       route add MY.IP.ADD.RESS arc0e
-       route add -net SUB.NET.ADD.RESS arc0e
-   
-   arc0s works much the same way as arc0e.
-
-
-2. More than one protocol on the same wire.
-
-   Now things start getting confusing.  To even try it, you may need to be
-   partly crazy.  Here's what *I* did. :) Note that I don't include arc0s in
-   my home network; I don't have any NetBSD or AmiTCP computers, so I only
-   use arc0s during limited testing.
-
-   I have three computers on my home network; two Linux boxes (which prefer
-   RFC1201 protocol, for reasons listed above), and one XT that can't run
-   Linux but runs the free Microsoft LANMAN Client instead.
-
-   Worse, one of the Linux computers (freedom) also has a modem and acts as
-   a router to my Internet provider.  The other Linux box (insight) also has
-   its own IP address and needs to use freedom as its default gateway.  The
-   XT (patience), however, does not have its own Internet IP address and so
-   I assigned it one on a "private subnet" (as defined by RFC1597).
-
-   To start with, take a simple network with just insight and freedom. 
-   Insight needs to:
-       - talk to freedom via RFC1201 (arc0) protocol, because I like it
-         more and it's faster.
-       - use freedom as its Internet gateway.
-       
-   That's pretty easy to do.  Set up insight like this:
-       ifconfig arc0 insight
-       route add insight arc0
-       route add freedom arc0  /* I would use the subnet here (like I said
-                                       to to in "single protocol" above),
-                                       but the rest of the subnet
-                                       unfortunately lies across the PPP
-                                       link on freedom, which confuses
-                                       things. */
-       route add default gw freedom
-       
-   And freedom gets configured like so:
-       ifconfig arc0 freedom
-       route add freedom arc0
-       route add insight arc0
-       /* and default gateway is configured by pppd */
-       
-   Great, now insight talks to freedom directly on arc0, and sends packets
-   to the Internet through freedom.  If you didn't know how to do the above,
-   you should probably stop reading this section now because it only gets
-   worse.
-
-   Now, how do I add patience into the network?  It will be using LANMAN
-   Client, which means I need the arc0e device.  It needs to be able to talk
-   to both insight and freedom, and also use freedom as a gateway to the
-   Internet.  (Recall that patience has a "private IP address" which won't
-   work on the Internet; that's okay, I configured Linux IP masquerading on
-   freedom for this subnet).
-   
-   So patience (necessarily; I don't have another IP number from my
-   provider) has an IP address on a different subnet than freedom and
-   insight, but needs to use freedom as an Internet gateway.  Worse, most
-   DOS networking programs, including LANMAN, have braindead networking
-   schemes that rely completely on the netmask and a 'default gateway' to
-   determine how to route packets.  This means that to get to freedom or
-   insight, patience WILL send through its default gateway, regardless of
-   the fact that both freedom and insight (courtesy of the arc0e device)
-   could understand a direct transmission.
-   
-   I compensate by giving freedom an extra IP address - aliased 'gatekeeper'
-   - that is on my private subnet, the same subnet that patience is on.  I
-   then define gatekeeper to be the default gateway for patience.
-   
-   To configure freedom (in addition to the commands above):
-       ifconfig arc0e gatekeeper
-       route add gatekeeper arc0e
-       route add patience arc0e
-   
-   This way, freedom will send all packets for patience through arc0e,
-   giving its IP address as gatekeeper (on the private subnet).  When it
-   talks to insight or the Internet, it will use its "freedom" Internet IP
-   address.
-   
-   You will notice that we haven't configured the arc0e device on insight. 
-   This would work, but is not really necessary, and would require me to
-   assign insight another special IP number from my private subnet.  Since
-   both insight and patience are using freedom as their default gateway, the
-   two can already talk to each other.
-   
-   It's quite fortunate that I set things up like this the first time (cough
-   cough) because it's really handy when I boot insight into DOS.  There, it
-   runs the Novell ODI protocol stack, which only works with RFC1201 ARCnet. 
-   In this mode it would be impossible for insight to communicate directly
-   with patience, since the Novell stack is incompatible with Microsoft's
-   Ethernet-Encap.  Without changing any settings on freedom or patience, I
-   simply set freedom as the default gateway for insight (now in DOS,
-   remember) and all the forwarding happens "automagically" between the two
-   hosts that would normally not be able to communicate at all.
-   
-   For those who like diagrams, I have created two "virtual subnets" on the
-   same physical ARCnet wire.  You can picture it like this:
-   
-                                                    
-          [RFC1201 NETWORK]                   [ETHER-ENCAP NETWORK]
-      (registered Internet subnet)           (RFC1597 private subnet)
-  
-                             (IP Masquerade)
-          /---------------\         *            /---------------\
-          |               |         *            |               |
-          |               +-Freedom-*-Gatekeeper-+               |
-          |               |    |    *            |               |
-          \-------+-------/    |    *            \-------+-------/
-                  |            |                         |
-               Insight         |                      Patience
-                           (Internet)
-
-
-
-It works: what now?
--------------------
-
-Send mail describing your setup, preferably including driver version, kernel
-version, ARCnet card model, CPU type, number of systems on your network, and
-list of software in use to me at the following address:
-       apenwarr@worldvisions.ca
-
-I do send (sometimes automated) replies to all messages I receive.  My email
-can be weird (and also usually gets forwarded all over the place along the
-way to me), so if you don't get a reply within a reasonable time, please
-resend.
-
-
-It doesn't work: what now?
---------------------------
-
-Do the same as above, but also include the output of the ifconfig and route
-commands, as well as any pertinent log entries (ie. anything that starts
-with "arcnet:" and has shown up since the last reboot) in your mail.
-
-If you want to try fixing it yourself (I strongly recommend that you mail me
-about the problem first, since it might already have been solved) you may
-want to try some of the debug levels available.  For heavy testing on
-D_DURING or more, it would be a REALLY good idea to kill your klogd daemon
-first!  D_DURING displays 4-5 lines for each packet sent or received.  D_TX,
-D_RX, and D_SKB actually DISPLAY each packet as it is sent or received,
-which is obviously quite big.
-
-Starting with v2.40 ALPHA, the autoprobe routines have changed
-significantly.  In particular, they won't tell you why the card was not
-found unless you turn on the D_INIT_REASONS debugging flag.
-
-Once the driver is running, you can run the arcdump shell script (available
-from me or in the full ARCnet package, if you have it) as root to list the
-contents of the arcnet buffers at any time.  To make any sense at all out of
-this, you should grab the pertinent RFCs. (some are listed near the top of
-arcnet.c).  arcdump assumes your card is at 0xD0000.  If it isn't, edit the
-script.
-
-Buffers 0 and 1 are used for receiving, and Buffers 2 and 3 are for sending. 
-Ping-pong buffers are implemented both ways.
-
-If your debug level includes D_DURING and you did NOT define SLOW_XMIT_COPY,
-the buffers are cleared to a constant value of 0x42 every time the card is
-reset (which should only happen when you do an ifconfig up, or when Linux
-decides that the driver is broken).  During a transmit, unused parts of the
-buffer will be cleared to 0x42 as well.  This is to make it easier to figure
-out which bytes are being used by a packet.
-
-You can change the debug level without recompiling the kernel by typing:
-       ifconfig arc0 down metric 1xxx
-       /etc/rc.d/rc.inet1
-where "xxx" is the debug level you want.  For example, "metric 1015" would put
-you at debug level 15.  Debug level 7 is currently the default.
-
-Note that the debug level is (starting with v1.90 ALPHA) a binary
-combination of different debug flags; so debug level 7 is really 1+2+4 or
-D_NORMAL+D_EXTRA+D_INIT.  To include D_DURING, you would add 16 to this,
-resulting in debug level 23.
-
-If you don't understand that, you probably don't want to know anyway. 
-E-mail me about your problem.
-
-
-I want to send money: what now?
--------------------------------
-
-Go take a nap or something.  You'll feel better in the morning.
diff --git a/Documentation/networking/atm.rst b/Documentation/networking/atm.rst
new file mode 100644 (file)
index 0000000..c1df8c0
--- /dev/null
@@ -0,0 +1,14 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===
+ATM
+===
+
+In order to use anything but the most primitive functions of ATM,
+several user-mode programs are required to assist the kernel. These
+programs and related material can be found via the ATM on Linux Web
+page at http://linux-atm.sourceforge.net/
+
+If you encounter problems with ATM, please report them on the ATM
+on Linux mailing list. Subscription information, archives, etc.,
+can be found on http://linux-atm.sourceforge.net/
diff --git a/Documentation/networking/atm.txt b/Documentation/networking/atm.txt
deleted file mode 100644 (file)
index 82921ce..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-In order to use anything but the most primitive functions of ATM,
-several user-mode programs are required to assist the kernel. These
-programs and related material can be found via the ATM on Linux Web
-page at http://linux-atm.sourceforge.net/
-
-If you encounter problems with ATM, please report them on the ATM
-on Linux mailing list. Subscription information, archives, etc.,
-can be found on http://linux-atm.sourceforge.net/
diff --git a/Documentation/networking/ax25.rst b/Documentation/networking/ax25.rst
new file mode 100644 (file)
index 0000000..824afd7
--- /dev/null
@@ -0,0 +1,16 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====
+AX.25
+=====
+
+To use the amateur radio protocols within Linux you will need to get a
+suitable copy of the AX.25 Utilities. More detailed information about
+AX.25, NET/ROM and ROSE, associated programs and and utilities can be
+found on http://www.linux-ax25.org.
+
+There is an active mailing list for discussing Linux amateur radio matters
+called linux-hams@vger.kernel.org. To subscribe to it, send a message to
+majordomo@vger.kernel.org with the words "subscribe linux-hams" in the body
+of the message, the subject field is ignored.  You don't need to be
+subscribed to post but of course that means you might miss an answer.
diff --git a/Documentation/networking/ax25.txt b/Documentation/networking/ax25.txt
deleted file mode 100644 (file)
index 8257dbf..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-To use the amateur radio protocols within Linux you will need to get a
-suitable copy of the AX.25 Utilities. More detailed information about
-AX.25, NET/ROM and ROSE, associated programs and and utilities can be
-found on http://www.linux-ax25.org.
-
-There is an active mailing list for discussing Linux amateur radio matters
-called linux-hams@vger.kernel.org. To subscribe to it, send a message to
-majordomo@vger.kernel.org with the words "subscribe linux-hams" in the body
-of the message, the subject field is ignored.  You don't need to be
-subscribed to post but of course that means you might miss an answer.
diff --git a/Documentation/networking/baycom.rst b/Documentation/networking/baycom.rst
new file mode 100644 (file)
index 0000000..fe2d010
--- /dev/null
@@ -0,0 +1,174 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============================
+Linux Drivers for Baycom Modems
+===============================
+
+Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
+
+The drivers for the baycom modems have been split into
+separate drivers as they did not share any code, and the driver
+and device names have changed.
+
+This document describes the Linux Kernel Drivers for simple Baycom style
+amateur radio modems.
+
+The following drivers are available:
+====================================
+
+baycom_ser_fdx:
+  This driver supports the SER12 modems either full or half duplex.
+  Its baud rate may be changed via the ``baud`` module parameter,
+  therefore it supports just about every bit bang modem on a
+  serial port. Its devices are called bcsf0 through bcsf3.
+  This is the recommended driver for SER12 type modems,
+  however if you have a broken UART clone that does not have working
+  delta status bits, you may try baycom_ser_hdx.
+
+baycom_ser_hdx:
+  This is an alternative driver for SER12 type modems.
+  It only supports half duplex, and only 1200 baud. Its devices
+  are called bcsh0 through bcsh3. Use this driver only if baycom_ser_fdx
+  does not work with your UART.
+
+baycom_par:
+  This driver supports the par96 and picpar modems.
+  Its devices are called bcp0 through bcp3.
+
+baycom_epp:
+  This driver supports the EPP modem.
+  Its devices are called bce0 through bce3.
+  This driver is work-in-progress.
+
+The following modems are supported:
+
+======= ========================================================================
+ser12   This is a very simple 1200 baud AFSK modem. The modem consists only
+       of a modulator/demodulator chip, usually a TI TCM3105. The computer
+       is responsible for regenerating the receiver bit clock, as well as
+       for handling the HDLC protocol. The modem connects to a serial port,
+       hence the name. Since the serial port is not used as an async serial
+       port, the kernel driver for serial ports cannot be used, and this
+       driver only supports standard serial hardware (8250, 16450, 16550)
+
+par96   This is a modem for 9600 baud FSK compatible to the G3RUH standard.
+       The modem does all the filtering and regenerates the receiver clock.
+       Data is transferred from and to the PC via a shift register.
+       The shift register is filled with 16 bits and an interrupt is signalled.
+       The PC then empties the shift register in a burst. This modem connects
+       to the parallel port, hence the name. The modem leaves the
+       implementation of the HDLC protocol and the scrambler polynomial to
+       the PC.
+
+picpar  This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem
+       is protocol compatible to par96, but uses only three low power ICs
+       and can therefore be fed from the parallel port and does not require
+       an additional power supply. Furthermore, it incorporates a carrier
+       detect circuitry.
+
+EPP     This is a high-speed modem adaptor that connects to an enhanced parallel
+       port.
+
+       Its target audience is users working over a high speed hub (76.8kbit/s).
+
+eppfpga This is a redesign of the EPP adaptor.
+======= ========================================================================
+
+All of the above modems only support half duplex communications. However,
+the driver supports the KISS (see below) fullduplex command. It then simply
+starts to send as soon as there's a packet to transmit and does not care
+about DCD, i.e. it starts to send even if there's someone else on the channel.
+This command is required by some implementations of the DAMA channel
+access protocol.
+
+
+The Interface of the drivers
+============================
+
+Unlike previous drivers, these drivers are no longer character devices,
+but they are now true kernel network interfaces. Installation is therefore
+simple. Once installed, four interfaces named bc{sf,sh,p,e}[0-3] are available.
+sethdlc from the ax25 utilities may be used to set driver states etc.
+Users of userland AX.25 stacks may use the net2kiss utility (also available
+in the ax25 utilities package) to convert packets of a network interface
+to a KISS stream on a pseudo tty. There's also a patch available from
+me for WAMPES which allows attaching a kernel network interface directly.
+
+
+Configuring the driver
+======================
+
+Every time a driver is inserted into the kernel, it has to know which
+modems it should access at which ports. This can be done with the setbaycom
+utility. If you are only using one modem, you can also configure the
+driver from the insmod command line (or by means of an option line in
+``/etc/modprobe.d/*.conf``).
+
+Examples::
+
+  modprobe baycom_ser_fdx mode="ser12*" iobase=0x3f8 irq=4
+  sethdlc -i bcsf0 -p mode "ser12*" io 0x3f8 irq 4
+
+Both lines configure the first port to drive a ser12 modem at the first
+serial port (COM1 under DOS). The * in the mode parameter instructs the driver
+to use the software DCD algorithm (see below)::
+
+  insmod baycom_par mode="picpar" iobase=0x378
+  sethdlc -i bcp0 -p mode "picpar" io 0x378
+
+Both lines configure the first port to drive a picpar modem at the
+first parallel port (LPT1 under DOS). (Note: picpar implies
+hardware DCD, par96 implies software DCD).
+
+The channel access parameters can be set with sethdlc -a or kissparms.
+Note that both utilities interpret the values slightly differently.
+
+
+Hardware DCD versus Software DCD
+================================
+
+To avoid collisions on the air, the driver must know when the channel is
+busy. This is the task of the DCD circuitry/software. The driver may either
+utilise a software DCD algorithm (options=1) or use a DCD signal from
+the hardware (options=0).
+
+======= =================================================================
+ser12   if software DCD is utilised, the radio's squelch should always be
+       open. It is highly recommended to use the software DCD algorithm,
+       as it is much faster than most hardware squelch circuitry. The
+       disadvantage is a slightly higher load on the system.
+
+par96   the software DCD algorithm for this type of modem is rather poor.
+       The modem simply does not provide enough information to implement
+       a reasonable DCD algorithm in software. Therefore, if your radio
+       feeds the DCD input of the PAR96 modem, the use of the hardware
+       DCD circuitry is recommended.
+
+picpar  the picpar modem features a builtin DCD hardware, which is highly
+       recommended.
+======= =================================================================
+
+
+
+Compatibility with the rest of the Linux kernel
+===============================================
+
+The serial driver and the baycom serial drivers compete
+for the same hardware resources. Of course only one driver can access a given
+interface at a time. The serial driver grabs all interfaces it can find at
+startup time. Therefore the baycom drivers subsequently won't be able to
+access a serial port. You might therefore find it necessary to release
+a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where
+# is the number of the interface. The baycom drivers do not reserve any
+ports at startup, unless one is specified on the 'insmod' command line. Another
+method to solve the problem is to compile all drivers as modules and
+leave it to kmod to load the correct driver depending on the application.
+
+The parallel port drivers (baycom_par, baycom_epp) now use the parport subsystem
+to arbitrate the ports between different client drivers.
+
+vy 73s de
+
+Tom Sailer, sailer@ife.ee.ethz.ch
+
+hb9jnx @ hb9w.ampr.org
diff --git a/Documentation/networking/baycom.txt b/Documentation/networking/baycom.txt
deleted file mode 100644 (file)
index 688f18f..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-                   LINUX DRIVERS FOR BAYCOM MODEMS
-
-       Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
-
-!!NEW!! (04/98) The drivers for the baycom modems have been split into
-separate drivers as they did not share any code, and the driver
-and device names have changed.
-
-This document describes the Linux Kernel Drivers for simple Baycom style
-amateur radio modems. 
-
-The following drivers are available:
-
-baycom_ser_fdx:
-  This driver supports the SER12 modems either full or half duplex.
-  Its baud rate may be changed via the `baud' module parameter,
-  therefore it supports just about every bit bang modem on a
-  serial port. Its devices are called bcsf0 through bcsf3.
-  This is the recommended driver for SER12 type modems,
-  however if you have a broken UART clone that does not have working
-  delta status bits, you may try baycom_ser_hdx. 
-
-baycom_ser_hdx: 
-  This is an alternative driver for SER12 type modems.
-  It only supports half duplex, and only 1200 baud. Its devices
-  are called bcsh0 through bcsh3. Use this driver only if baycom_ser_fdx
-  does not work with your UART.
-
-baycom_par:
-  This driver supports the par96 and picpar modems.
-  Its devices are called bcp0 through bcp3.
-
-baycom_epp:
-  This driver supports the EPP modem.
-  Its devices are called bce0 through bce3.
-  This driver is work-in-progress.
-
-The following modems are supported:
-
-ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
-        of a modulator/demodulator chip, usually a TI TCM3105. The computer
-        is responsible for regenerating the receiver bit clock, as well as
-        for handling the HDLC protocol. The modem connects to a serial port,
-        hence the name. Since the serial port is not used as an async serial
-        port, the kernel driver for serial ports cannot be used, and this
-        driver only supports standard serial hardware (8250, 16450, 16550)
-
-par96:  This is a modem for 9600 baud FSK compatible to the G3RUH standard.
-        The modem does all the filtering and regenerates the receiver clock.
-        Data is transferred from and to the PC via a shift register.
-        The shift register is filled with 16 bits and an interrupt is signalled.
-        The PC then empties the shift register in a burst. This modem connects
-        to the parallel port, hence the name. The modem leaves the 
-        implementation of the HDLC protocol and the scrambler polynomial to
-        the PC.
-
-picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The modem
-        is protocol compatible to par96, but uses only three low power ICs
-        and can therefore be fed from the parallel port and does not require
-        an additional power supply. Furthermore, it incorporates a carrier
-        detect circuitry.
-
-EPP:    This is a high-speed modem adaptor that connects to an enhanced parallel port.
-        Its target audience is users working over a high speed hub (76.8kbit/s).
-
-eppfpga: This is a redesign of the EPP adaptor.
-
-
-
-All of the above modems only support half duplex communications. However,
-the driver supports the KISS (see below) fullduplex command. It then simply
-starts to send as soon as there's a packet to transmit and does not care
-about DCD, i.e. it starts to send even if there's someone else on the channel.
-This command is required by some implementations of the DAMA channel 
-access protocol.
-
-
-The Interface of the drivers
-
-Unlike previous drivers, these drivers are no longer character devices,
-but they are now true kernel network interfaces. Installation is therefore
-simple. Once installed, four interfaces named bc{sf,sh,p,e}[0-3] are available.
-sethdlc from the ax25 utilities may be used to set driver states etc.
-Users of userland AX.25 stacks may use the net2kiss utility (also available
-in the ax25 utilities package) to convert packets of a network interface
-to a KISS stream on a pseudo tty. There's also a patch available from
-me for WAMPES which allows attaching a kernel network interface directly.
-
-
-Configuring the driver
-
-Every time a driver is inserted into the kernel, it has to know which
-modems it should access at which ports. This can be done with the setbaycom
-utility. If you are only using one modem, you can also configure the
-driver from the insmod command line (or by means of an option line in
-/etc/modprobe.d/*.conf).
-
-Examples:
-  modprobe baycom_ser_fdx mode="ser12*" iobase=0x3f8 irq=4
-  sethdlc -i bcsf0 -p mode "ser12*" io 0x3f8 irq 4
-
-Both lines configure the first port to drive a ser12 modem at the first
-serial port (COM1 under DOS). The * in the mode parameter instructs the driver to use
-the software DCD algorithm (see below).
-
-  insmod baycom_par mode="picpar" iobase=0x378
-  sethdlc -i bcp0 -p mode "picpar" io 0x378
-
-Both lines configure the first port to drive a picpar modem at the
-first parallel port (LPT1 under DOS). (Note: picpar implies
-hardware DCD, par96 implies software DCD).
-
-The channel access parameters can be set with sethdlc -a or kissparms.
-Note that both utilities interpret the values slightly differently.
-
-
-Hardware DCD versus Software DCD
-
-To avoid collisions on the air, the driver must know when the channel is
-busy. This is the task of the DCD circuitry/software. The driver may either
-utilise a software DCD algorithm (options=1) or use a DCD signal from
-the hardware (options=0).
-
-ser12:  if software DCD is utilised, the radio's squelch should always be
-        open. It is highly recommended to use the software DCD algorithm,
-        as it is much faster than most hardware squelch circuitry. The
-        disadvantage is a slightly higher load on the system.
-
-par96:  the software DCD algorithm for this type of modem is rather poor.
-        The modem simply does not provide enough information to implement
-        a reasonable DCD algorithm in software. Therefore, if your radio
-        feeds the DCD input of the PAR96 modem, the use of the hardware
-        DCD circuitry is recommended.
-
-picpar: the picpar modem features a builtin DCD hardware, which is highly
-        recommended.
-
-
-
-Compatibility with the rest of the Linux kernel
-
-The serial driver and the baycom serial drivers compete
-for the same hardware resources. Of course only one driver can access a given
-interface at a time. The serial driver grabs all interfaces it can find at
-startup time. Therefore the baycom drivers subsequently won't be able to
-access a serial port. You might therefore find it necessary to release
-a port owned by the serial driver with 'setserial /dev/ttyS# uart none', where
-# is the number of the interface. The baycom drivers do not reserve any
-ports at startup, unless one is specified on the 'insmod' command line. Another
-method to solve the problem is to compile all drivers as modules and
-leave it to kmod to load the correct driver depending on the application.
-
-The parallel port drivers (baycom_par, baycom_epp) now use the parport subsystem
-to arbitrate the ports between different client drivers.
-
-vy 73s de
-Tom Sailer, sailer@ife.ee.ethz.ch
-hb9jnx @ hb9w.ampr.org
diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst
new file mode 100644 (file)
index 0000000..24168b0
--- /dev/null
@@ -0,0 +1,2890 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================================
+Linux Ethernet Bonding Driver HOWTO
+===================================
+
+Latest update: 27 April 2011
+
+Initial release: Thomas Davis <tadavis at lbl.gov>
+
+Corrections, HA extensions: 2000/10/03-15:
+
+  - Willy Tarreau <willy at meta-x.org>
+  - Constantine Gavrilov <const-g at xpert.com>
+  - Chad N. Tindel <ctindel at ieee dot org>
+  - Janice Girouard <girouard at us dot ibm dot com>
+  - Jay Vosburgh <fubar at us dot ibm dot com>
+
+Reorganized and updated Feb 2005 by Jay Vosburgh
+Added Sysfs information: 2006/04/24
+
+  - Mitch Williams <mitch.a.williams at intel.com>
+
+Introduction
+============
+
+The Linux bonding driver provides a method for aggregating
+multiple network interfaces into a single logical "bonded" interface.
+The behavior of the bonded interfaces depends upon the mode; generally
+speaking, modes provide either hot standby or load balancing services.
+Additionally, link integrity monitoring may be performed.
+
+The bonding driver originally came from Donald Becker's
+beowulf patches for kernel 2.0. It has changed quite a bit since, and
+the original tools from extreme-linux and beowulf sites will not work
+with this version of the driver.
+
+For new versions of the driver, updated userspace tools, and
+who to ask for help, please follow the links at the end of this file.
+
+.. Table of Contents
+
+   1. Bonding Driver Installation
+
+   2. Bonding Driver Options
+
+   3. Configuring Bonding Devices
+   3.1 Configuration with Sysconfig Support
+   3.1.1               Using DHCP with Sysconfig
+   3.1.2               Configuring Multiple Bonds with Sysconfig
+   3.2 Configuration with Initscripts Support
+   3.2.1               Using DHCP with Initscripts
+   3.2.2               Configuring Multiple Bonds with Initscripts
+   3.3 Configuring Bonding Manually with Ifenslave
+   3.3.1               Configuring Multiple Bonds Manually
+   3.4 Configuring Bonding Manually via Sysfs
+   3.5 Configuration with Interfaces Support
+   3.6 Overriding Configuration for Special Cases
+   3.7 Configuring LACP for 802.3ad mode in a more secure way
+
+   4. Querying Bonding Configuration
+   4.1 Bonding Configuration
+   4.2 Network Configuration
+
+   5. Switch Configuration
+
+   6. 802.1q VLAN Support
+
+   7. Link Monitoring
+   7.1 ARP Monitor Operation
+   7.2 Configuring Multiple ARP Targets
+   7.3 MII Monitor Operation
+
+   8. Potential Trouble Sources
+   8.1 Adventures in Routing
+   8.2 Ethernet Device Renaming
+   8.3 Painfully Slow Or No Failed Link Detection By Miimon
+
+   9. SNMP agents
+
+   10. Promiscuous mode
+
+   11. Configuring Bonding for High Availability
+   11.1        High Availability in a Single Switch Topology
+   11.2        High Availability in a Multiple Switch Topology
+   11.2.1              HA Bonding Mode Selection for Multiple Switch Topology
+   11.2.2              HA Link Monitoring for Multiple Switch Topology
+
+   12. Configuring Bonding for Maximum Throughput
+   12.1        Maximum Throughput in a Single Switch Topology
+   12.1.1              MT Bonding Mode Selection for Single Switch Topology
+   12.1.2              MT Link Monitoring for Single Switch Topology
+   12.2        Maximum Throughput in a Multiple Switch Topology
+   12.2.1              MT Bonding Mode Selection for Multiple Switch Topology
+   12.2.2              MT Link Monitoring for Multiple Switch Topology
+
+   13. Switch Behavior Issues
+   13.1        Link Establishment and Failover Delays
+   13.2        Duplicated Incoming Packets
+
+   14. Hardware Specific Considerations
+   14.1        IBM BladeCenter
+
+   15. Frequently Asked Questions
+
+   16. Resources and Links
+
+
+1. Bonding Driver Installation
+==============================
+
+Most popular distro kernels ship with the bonding driver
+already available as a module. If your distro does not, or you
+have need to compile bonding from source (e.g., configuring and
+installing a mainline kernel from kernel.org), you'll need to perform
+the following steps:
+
+1.1 Configure and build the kernel with bonding
+-----------------------------------------------
+
+The current version of the bonding driver is available in the
+drivers/net/bonding subdirectory of the most recent kernel source
+(which is available on http://kernel.org).  Most users "rolling their
+own" will want to use the most recent kernel from kernel.org.
+
+Configure kernel with "make menuconfig" (or "make xconfig" or
+"make config"), then select "Bonding driver support" in the "Network
+device support" section.  It is recommended that you configure the
+driver as module since it is currently the only way to pass parameters
+to the driver or configure more than one bonding device.
+
+Build and install the new kernel and modules.
+
+1.2 Bonding Control Utility
+---------------------------
+
+It is recommended to configure bonding via iproute2 (netlink)
+or sysfs, the old ifenslave control utility is obsolete.
+
+2. Bonding Driver Options
+=========================
+
+Options for the bonding driver are supplied as parameters to the
+bonding module at load time, or are specified via sysfs.
+
+Module options may be given as command line arguments to the
+insmod or modprobe command, but are usually specified in either the
+``/etc/modprobe.d/*.conf`` configuration files, or in a distro-specific
+configuration file (some of which are detailed in the next section).
+
+Details on bonding support for sysfs is provided in the
+"Configuring Bonding Manually via Sysfs" section, below.
+
+The available bonding driver parameters are listed below. If a
+parameter is not specified the default value is used.  When initially
+configuring a bond, it is recommended "tail -f /var/log/messages" be
+run in a separate window to watch for bonding driver error messages.
+
+It is critical that either the miimon or arp_interval and
+arp_ip_target parameters be specified, otherwise serious network
+degradation will occur during link failures.  Very few devices do not
+support at least miimon, so there is really no reason not to use it.
+
+Options with textual values will accept either the text name
+or, for backwards compatibility, the option value.  E.g.,
+"mode=802.3ad" and "mode=4" set the same mode.
+
+The parameters are as follows:
+
+active_slave
+
+       Specifies the new active slave for modes that support it
+       (active-backup, balance-alb and balance-tlb).  Possible values
+       are the name of any currently enslaved interface, or an empty
+       string.  If a name is given, the slave and its link must be up in order
+       to be selected as the new active slave.  If an empty string is
+       specified, the current active slave is cleared, and a new active
+       slave is selected automatically.
+
+       Note that this is only available through the sysfs interface. No module
+       parameter by this name exists.
+
+       The normal value of this option is the name of the currently
+       active slave, or the empty string if there is no active slave or
+       the current mode does not use an active slave.
+
+ad_actor_sys_prio
+
+       In an AD system, this specifies the system priority. The allowed range
+       is 1 - 65535. If the value is not specified, it takes 65535 as the
+       default value.
+
+       This parameter has effect only in 802.3ad mode and is available through
+       SysFs interface.
+
+ad_actor_system
+
+       In an AD system, this specifies the mac-address for the actor in
+       protocol packet exchanges (LACPDUs). The value cannot be NULL or
+       multicast. It is preferred to have the local-admin bit set for this
+       mac but driver does not enforce it. If the value is not given then
+       system defaults to using the masters' mac address as actors' system
+       address.
+
+       This parameter has effect only in 802.3ad mode and is available through
+       SysFs interface.
+
+ad_select
+
+       Specifies the 802.3ad aggregation selection logic to use.  The
+       possible values and their effects are:
+
+       stable or 0
+
+               The active aggregator is chosen by largest aggregate
+               bandwidth.
+
+               Reselection of the active aggregator occurs only when all
+               slaves of the active aggregator are down or the active
+               aggregator has no slaves.
+
+               This is the default value.
+
+       bandwidth or 1
+
+               The active aggregator is chosen by largest aggregate
+               bandwidth.  Reselection occurs if:
+
+               - A slave is added to or removed from the bond
+
+               - Any slave's link state changes
+
+               - Any slave's 802.3ad association state changes
+
+               - The bond's administrative state changes to up
+
+       count or 2
+
+               The active aggregator is chosen by the largest number of
+               ports (slaves).  Reselection occurs as described under the
+               "bandwidth" setting, above.
+
+       The bandwidth and count selection policies permit failover of
+       802.3ad aggregations when partial failure of the active aggregator
+       occurs.  This keeps the aggregator with the highest availability
+       (either in bandwidth or in number of ports) active at all times.
+
+       This option was added in bonding version 3.4.0.
+
+ad_user_port_key
+
+       In an AD system, the port-key has three parts as shown below -
+
+          =====  ============
+          Bits   Use
+          =====  ============
+          00     Duplex
+          01-05  Speed
+          06-15  User-defined
+          =====  ============
+
+       This defines the upper 10 bits of the port key. The values can be
+       from 0 - 1023. If not given, the system defaults to 0.
+
+       This parameter has effect only in 802.3ad mode and is available through
+       SysFs interface.
+
+all_slaves_active
+
+       Specifies that duplicate frames (received on inactive ports) should be
+       dropped (0) or delivered (1).
+
+       Normally, bonding will drop duplicate frames (received on inactive
+       ports), which is desirable for most users. But there are some times
+       it is nice to allow duplicate frames to be delivered.
+
+       The default value is 0 (drop duplicate frames received on inactive
+       ports).
+
+arp_interval
+
+       Specifies the ARP link monitoring frequency in milliseconds.
+
+       The ARP monitor works by periodically checking the slave
+       devices to determine whether they have sent or received
+       traffic recently (the precise criteria depends upon the
+       bonding mode, and the state of the slave).  Regular traffic is
+       generated via ARP probes issued for the addresses specified by
+       the arp_ip_target option.
+
+       This behavior can be modified by the arp_validate option,
+       below.
+
+       If ARP monitoring is used in an etherchannel compatible mode
+       (modes 0 and 2), the switch should be configured in a mode
+       that evenly distributes packets across all links. If the
+       switch is configured to distribute the packets in an XOR
+       fashion, all replies from the ARP targets will be received on
+       the same link which could cause the other team members to
+       fail.  ARP monitoring should not be used in conjunction with
+       miimon.  A value of 0 disables ARP monitoring.  The default
+       value is 0.
+
+arp_ip_target
+
+       Specifies the IP addresses to use as ARP monitoring peers when
+       arp_interval is > 0.  These are the targets of the ARP request
+       sent to determine the health of the link to the targets.
+       Specify these values in ddd.ddd.ddd.ddd format.  Multiple IP
+       addresses must be separated by a comma.  At least one IP
+       address must be given for ARP monitoring to function.  The
+       maximum number of targets that can be specified is 16.  The
+       default value is no IP addresses.
+
+arp_validate
+
+       Specifies whether or not ARP probes and replies should be
+       validated in any mode that supports arp monitoring, or whether
+       non-ARP traffic should be filtered (disregarded) for link
+       monitoring purposes.
+
+       Possible values are:
+
+       none or 0
+
+               No validation or filtering is performed.
+
+       active or 1
+
+               Validation is performed only for the active slave.
+
+       backup or 2
+
+               Validation is performed only for backup slaves.
+
+       all or 3
+
+               Validation is performed for all slaves.
+
+       filter or 4
+
+               Filtering is applied to all slaves. No validation is
+               performed.
+
+       filter_active or 5
+
+               Filtering is applied to all slaves, validation is performed
+               only for the active slave.
+
+       filter_backup or 6
+
+               Filtering is applied to all slaves, validation is performed
+               only for backup slaves.
+
+       Validation:
+
+       Enabling validation causes the ARP monitor to examine the incoming
+       ARP requests and replies, and only consider a slave to be up if it
+       is receiving the appropriate ARP traffic.
+
+       For an active slave, the validation checks ARP replies to confirm
+       that they were generated by an arp_ip_target.  Since backup slaves
+       do not typically receive these replies, the validation performed
+       for backup slaves is on the broadcast ARP request sent out via the
+       active slave.  It is possible that some switch or network
+       configurations may result in situations wherein the backup slaves
+       do not receive the ARP requests; in such a situation, validation
+       of backup slaves must be disabled.
+
+       The validation of ARP requests on backup slaves is mainly helping
+       bonding to decide which slaves are more likely to work in case of
+       the active slave failure, it doesn't really guarantee that the
+       backup slave will work if it's selected as the next active slave.
+
+       Validation is useful in network configurations in which multiple
+       bonding hosts are concurrently issuing ARPs to one or more targets
+       beyond a common switch.  Should the link between the switch and
+       target fail (but not the switch itself), the probe traffic
+       generated by the multiple bonding instances will fool the standard
+       ARP monitor into considering the links as still up.  Use of
+       validation can resolve this, as the ARP monitor will only consider
+       ARP requests and replies associated with its own instance of
+       bonding.
+
+       Filtering:
+
+       Enabling filtering causes the ARP monitor to only use incoming ARP
+       packets for link availability purposes.  Arriving packets that are
+       not ARPs are delivered normally, but do not count when determining
+       if a slave is available.
+
+       Filtering operates by only considering the reception of ARP
+       packets (any ARP packet, regardless of source or destination) when
+       determining if a slave has received traffic for link availability
+       purposes.
+
+       Filtering is useful in network configurations in which significant
+       levels of third party broadcast traffic would fool the standard
+       ARP monitor into considering the links as still up.  Use of
+       filtering can resolve this, as only ARP traffic is considered for
+       link availability purposes.
+
+       This option was added in bonding version 3.1.0.
+
+arp_all_targets
+
+       Specifies the quantity of arp_ip_targets that must be reachable
+       in order for the ARP monitor to consider a slave as being up.
+       This option affects only active-backup mode for slaves with
+       arp_validation enabled.
+
+       Possible values are:
+
+       any or 0
+
+               consider the slave up only when any of the arp_ip_targets
+               is reachable
+
+       all or 1
+
+               consider the slave up only when all of the arp_ip_targets
+               are reachable
+
+downdelay
+
+       Specifies the time, in milliseconds, to wait before disabling
+       a slave after a link failure has been detected.  This option
+       is only valid for the miimon link monitor.  The downdelay
+       value should be a multiple of the miimon value; if not, it
+       will be rounded down to the nearest multiple.  The default
+       value is 0.
+
+fail_over_mac
+
+       Specifies whether active-backup mode should set all slaves to
+       the same MAC address at enslavement (the traditional
+       behavior), or, when enabled, perform special handling of the
+       bond's MAC address in accordance with the selected policy.
+
+       Possible values are:
+
+       none or 0
+
+               This setting disables fail_over_mac, and causes
+               bonding to set all slaves of an active-backup bond to
+               the same MAC address at enslavement time.  This is the
+               default.
+
+       active or 1
+
+               The "active" fail_over_mac policy indicates that the
+               MAC address of the bond should always be the MAC
+               address of the currently active slave.  The MAC
+               address of the slaves is not changed; instead, the MAC
+               address of the bond changes during a failover.
+
+               This policy is useful for devices that cannot ever
+               alter their MAC address, or for devices that refuse
+               incoming broadcasts with their own source MAC (which
+               interferes with the ARP monitor).
+
+               The down side of this policy is that every device on
+               the network must be updated via gratuitous ARP,
+               vs. just updating a switch or set of switches (which
+               often takes place for any traffic, not just ARP
+               traffic, if the switch snoops incoming traffic to
+               update its tables) for the traditional method.  If the
+               gratuitous ARP is lost, communication may be
+               disrupted.
+
+               When this policy is used in conjunction with the mii
+               monitor, devices which assert link up prior to being
+               able to actually transmit and receive are particularly
+               susceptible to loss of the gratuitous ARP, and an
+               appropriate updelay setting may be required.
+
+       follow or 2
+
+               The "follow" fail_over_mac policy causes the MAC
+               address of the bond to be selected normally (normally
+               the MAC address of the first slave added to the bond).
+               However, the second and subsequent slaves are not set
+               to this MAC address while they are in a backup role; a
+               slave is programmed with the bond's MAC address at
+               failover time (and the formerly active slave receives
+               the newly active slave's MAC address).
+
+               This policy is useful for multiport devices that
+               either become confused or incur a performance penalty
+               when multiple ports are programmed with the same MAC
+               address.
+
+
+       The default policy is none, unless the first slave cannot
+       change its MAC address, in which case the active policy is
+       selected by default.
+
+       This option may be modified via sysfs only when no slaves are
+       present in the bond.
+
+       This option was added in bonding version 3.2.0.  The "follow"
+       policy was added in bonding version 3.3.0.
+
+lacp_rate
+
+       Option specifying the rate in which we'll ask our link partner
+       to transmit LACPDU packets in 802.3ad mode.  Possible values
+       are:
+
+       slow or 0
+               Request partner to transmit LACPDUs every 30 seconds
+
+       fast or 1
+               Request partner to transmit LACPDUs every 1 second
+
+       The default is slow.
+
+max_bonds
+
+       Specifies the number of bonding devices to create for this
+       instance of the bonding driver.  E.g., if max_bonds is 3, and
+       the bonding driver is not already loaded, then bond0, bond1
+       and bond2 will be created.  The default value is 1.  Specifying
+       a value of 0 will load bonding, but will not create any devices.
+
+miimon
+
+       Specifies the MII link monitoring frequency in milliseconds.
+       This determines how often the link state of each slave is
+       inspected for link failures.  A value of zero disables MII
+       link monitoring.  A value of 100 is a good starting point.
+       The use_carrier option, below, affects how the link state is
+       determined.  See the High Availability section for additional
+       information.  The default value is 0.
+
+min_links
+
+       Specifies the minimum number of links that must be active before
+       asserting carrier. It is similar to the Cisco EtherChannel min-links
+       feature. This allows setting the minimum number of member ports that
+       must be up (link-up state) before marking the bond device as up
+       (carrier on). This is useful for situations where higher level services
+       such as clustering want to ensure a minimum number of low bandwidth
+       links are active before switchover. This option only affect 802.3ad
+       mode.
+
+       The default value is 0. This will cause carrier to be asserted (for
+       802.3ad mode) whenever there is an active aggregator, regardless of the
+       number of available links in that aggregator. Note that, because an
+       aggregator cannot be active without at least one available link,
+       setting this option to 0 or to 1 has the exact same effect.
+
+mode
+
+       Specifies one of the bonding policies. The default is
+       balance-rr (round robin).  Possible values are:
+
+       balance-rr or 0
+
+               Round-robin policy: Transmit packets in sequential
+               order from the first available slave through the
+               last.  This mode provides load balancing and fault
+               tolerance.
+
+       active-backup or 1
+
+               Active-backup policy: Only one slave in the bond is
+               active.  A different slave becomes active if, and only
+               if, the active slave fails.  The bond's MAC address is
+               externally visible on only one port (network adapter)
+               to avoid confusing the switch.
+
+               In bonding version 2.6.2 or later, when a failover
+               occurs in active-backup mode, bonding will issue one
+               or more gratuitous ARPs on the newly active slave.
+               One gratuitous ARP is issued for the bonding master
+               interface and each VLAN interfaces configured above
+               it, provided that the interface has at least one IP
+               address configured.  Gratuitous ARPs issued for VLAN
+               interfaces are tagged with the appropriate VLAN id.
+
+               This mode provides fault tolerance.  The primary
+               option, documented below, affects the behavior of this
+               mode.
+
+       balance-xor or 2
+
+               XOR policy: Transmit based on the selected transmit
+               hash policy.  The default policy is a simple [(source
+               MAC address XOR'd with destination MAC address XOR
+               packet type ID) modulo slave count].  Alternate transmit
+               policies may be selected via the xmit_hash_policy option,
+               described below.
+
+               This mode provides load balancing and fault tolerance.
+
+       broadcast or 3
+
+               Broadcast policy: transmits everything on all slave
+               interfaces.  This mode provides fault tolerance.
+
+       802.3ad or 4
+
+               IEEE 802.3ad Dynamic link aggregation.  Creates
+               aggregation groups that share the same speed and
+               duplex settings.  Utilizes all slaves in the active
+               aggregator according to the 802.3ad specification.
+
+               Slave selection for outgoing traffic is done according
+               to the transmit hash policy, which may be changed from
+               the default simple XOR policy via the xmit_hash_policy
+               option, documented below.  Note that not all transmit
+               policies may be 802.3ad compliant, particularly in
+               regards to the packet mis-ordering requirements of
+               section 43.2.4 of the 802.3ad standard.  Differing
+               peer implementations will have varying tolerances for
+               noncompliance.
+
+               Prerequisites:
+
+               1. Ethtool support in the base drivers for retrieving
+               the speed and duplex of each slave.
+
+               2. A switch that supports IEEE 802.3ad Dynamic link
+               aggregation.
+
+               Most switches will require some type of configuration
+               to enable 802.3ad mode.
+
+       balance-tlb or 5
+
+               Adaptive transmit load balancing: channel bonding that
+               does not require any special switch support.
+
+               In tlb_dynamic_lb=1 mode; the outgoing traffic is
+               distributed according to the current load (computed
+               relative to the speed) on each slave.
+
+               In tlb_dynamic_lb=0 mode; the load balancing based on
+               current load is disabled and the load is distributed
+               only using the hash distribution.
+
+               Incoming traffic is received by the current slave.
+               If the receiving slave fails, another slave takes over
+               the MAC address of the failed receiving slave.
+
+               Prerequisite:
+
+               Ethtool support in the base drivers for retrieving the
+               speed of each slave.
+
+       balance-alb or 6
+
+               Adaptive load balancing: includes balance-tlb plus
+               receive load balancing (rlb) for IPV4 traffic, and
+               does not require any special switch support.  The
+               receive load balancing is achieved by ARP negotiation.
+               The bonding driver intercepts the ARP Replies sent by
+               the local system on their way out and overwrites the
+               source hardware address with the unique hardware
+               address of one of the slaves in the bond such that
+               different peers use different hardware addresses for
+               the server.
+
+               Receive traffic from connections created by the server
+               is also balanced.  When the local system sends an ARP
+               Request the bonding driver copies and saves the peer's
+               IP information from the ARP packet.  When the ARP
+               Reply arrives from the peer, its hardware address is
+               retrieved and the bonding driver initiates an ARP
+               reply to this peer assigning it to one of the slaves
+               in the bond.  A problematic outcome of using ARP
+               negotiation for balancing is that each time that an
+               ARP request is broadcast it uses the hardware address
+               of the bond.  Hence, peers learn the hardware address
+               of the bond and the balancing of receive traffic
+               collapses to the current slave.  This is handled by
+               sending updates (ARP Replies) to all the peers with
+               their individually assigned hardware address such that
+               the traffic is redistributed.  Receive traffic is also
+               redistributed when a new slave is added to the bond
+               and when an inactive slave is re-activated.  The
+               receive load is distributed sequentially (round robin)
+               among the group of highest speed slaves in the bond.
+
+               When a link is reconnected or a new slave joins the
+               bond the receive traffic is redistributed among all
+               active slaves in the bond by initiating ARP Replies
+               with the selected MAC address to each of the
+               clients. The updelay parameter (detailed below) must
+               be set to a value equal or greater than the switch's
+               forwarding delay so that the ARP Replies sent to the
+               peers will not be blocked by the switch.
+
+               Prerequisites:
+
+               1. Ethtool support in the base drivers for retrieving
+               the speed of each slave.
+
+               2. Base driver support for setting the hardware
+               address of a device while it is open.  This is
+               required so that there will always be one slave in the
+               team using the bond hardware address (the
+               curr_active_slave) while having a unique hardware
+               address for each slave in the bond.  If the
+               curr_active_slave fails its hardware address is
+               swapped with the new curr_active_slave that was
+               chosen.
+
+num_grat_arp,
+num_unsol_na
+
+       Specify the number of peer notifications (gratuitous ARPs and
+       unsolicited IPv6 Neighbor Advertisements) to be issued after a
+       failover event.  As soon as the link is up on the new slave
+       (possibly immediately) a peer notification is sent on the
+       bonding device and each VLAN sub-device. This is repeated at
+       the rate specified by peer_notif_delay if the number is
+       greater than 1.
+
+       The valid range is 0 - 255; the default value is 1.  These options
+       affect only the active-backup mode.  These options were added for
+       bonding versions 3.3.0 and 3.4.0 respectively.
+
+       From Linux 3.0 and bonding version 3.7.1, these notifications
+       are generated by the ipv4 and ipv6 code and the numbers of
+       repetitions cannot be set independently.
+
+packets_per_slave
+
+       Specify the number of packets to transmit through a slave before
+       moving to the next one. When set to 0 then a slave is chosen at
+       random.
+
+       The valid range is 0 - 65535; the default value is 1. This option
+       has effect only in balance-rr mode.
+
+peer_notif_delay
+
+       Specify the delay, in milliseconds, between each peer
+       notification (gratuitous ARP and unsolicited IPv6 Neighbor
+       Advertisement) when they are issued after a failover event.
+       This delay should be a multiple of the link monitor interval
+       (arp_interval or miimon, whichever is active). The default
+       value is 0 which means to match the value of the link monitor
+       interval.
+
+primary
+
+       A string (eth0, eth2, etc) specifying which slave is the
+       primary device.  The specified device will always be the
+       active slave while it is available.  Only when the primary is
+       off-line will alternate devices be used.  This is useful when
+       one slave is preferred over another, e.g., when one slave has
+       higher throughput than another.
+
+       The primary option is only valid for active-backup(1),
+       balance-tlb (5) and balance-alb (6) mode.
+
+primary_reselect
+
+       Specifies the reselection policy for the primary slave.  This
+       affects how the primary slave is chosen to become the active slave
+       when failure of the active slave or recovery of the primary slave
+       occurs.  This option is designed to prevent flip-flopping between
+       the primary slave and other slaves.  Possible values are:
+
+       always or 0 (default)
+
+               The primary slave becomes the active slave whenever it
+               comes back up.
+
+       better or 1
+
+               The primary slave becomes the active slave when it comes
+               back up, if the speed and duplex of the primary slave is
+               better than the speed and duplex of the current active
+               slave.
+
+       failure or 2
+
+               The primary slave becomes the active slave only if the
+               current active slave fails and the primary slave is up.
+
+       The primary_reselect setting is ignored in two cases:
+
+               If no slaves are active, the first slave to recover is
+               made the active slave.
+
+               When initially enslaved, the primary slave is always made
+               the active slave.
+
+       Changing the primary_reselect policy via sysfs will cause an
+       immediate selection of the best active slave according to the new
+       policy.  This may or may not result in a change of the active
+       slave, depending upon the circumstances.
+
+       This option was added for bonding version 3.6.0.
+
+tlb_dynamic_lb
+
+       Specifies if dynamic shuffling of flows is enabled in tlb
+       mode. The value has no effect on any other modes.
+
+       The default behavior of tlb mode is to shuffle active flows across
+       slaves based on the load in that interval. This gives nice lb
+       characteristics but can cause packet reordering. If re-ordering is
+       a concern use this variable to disable flow shuffling and rely on
+       load balancing provided solely by the hash distribution.
+       xmit-hash-policy can be used to select the appropriate hashing for
+       the setup.
+
+       The sysfs entry can be used to change the setting per bond device
+       and the initial value is derived from the module parameter. The
+       sysfs entry is allowed to be changed only if the bond device is
+       down.
+
+       The default value is "1" that enables flow shuffling while value "0"
+       disables it. This option was added in bonding driver 3.7.1
+
+
+updelay
+
+       Specifies the time, in milliseconds, to wait before enabling a
+       slave after a link recovery has been detected.  This option is
+       only valid for the miimon link monitor.  The updelay value
+       should be a multiple of the miimon value; if not, it will be
+       rounded down to the nearest multiple.  The default value is 0.
+
+use_carrier
+
+       Specifies whether or not miimon should use MII or ETHTOOL
+       ioctls vs. netif_carrier_ok() to determine the link
+       status. The MII or ETHTOOL ioctls are less efficient and
+       utilize a deprecated calling sequence within the kernel.  The
+       netif_carrier_ok() relies on the device driver to maintain its
+       state with netif_carrier_on/off; at this writing, most, but
+       not all, device drivers support this facility.
+
+       If bonding insists that the link is up when it should not be,
+       it may be that your network device driver does not support
+       netif_carrier_on/off.  The default state for netif_carrier is
+       "carrier on," so if a driver does not support netif_carrier,
+       it will appear as if the link is always up.  In this case,
+       setting use_carrier to 0 will cause bonding to revert to the
+       MII / ETHTOOL ioctl method to determine the link state.
+
+       A value of 1 enables the use of netif_carrier_ok(), a value of
+       0 will use the deprecated MII / ETHTOOL ioctls.  The default
+       value is 1.
+
+xmit_hash_policy
+
+       Selects the transmit hash policy to use for slave selection in
+       balance-xor, 802.3ad, and tlb modes.  Possible values are:
+
+       layer2
+
+               Uses XOR of hardware MAC addresses and packet type ID
+               field to generate the hash. The formula is
+
+               hash = source MAC XOR destination MAC XOR packet type ID
+               slave number = hash modulo slave count
+
+               This algorithm will place all traffic to a particular
+               network peer on the same slave.
+
+               This algorithm is 802.3ad compliant.
+
+       layer2+3
+
+               This policy uses a combination of layer2 and layer3
+               protocol information to generate the hash.
+
+               Uses XOR of hardware MAC addresses and IP addresses to
+               generate the hash.  The formula is
+
+               hash = source MAC XOR destination MAC XOR packet type ID
+               hash = hash XOR source IP XOR destination IP
+               hash = hash XOR (hash RSHIFT 16)
+               hash = hash XOR (hash RSHIFT 8)
+               And then hash is reduced modulo slave count.
+
+               If the protocol is IPv6 then the source and destination
+               addresses are first hashed using ipv6_addr_hash.
+
+               This algorithm will place all traffic to a particular
+               network peer on the same slave.  For non-IP traffic,
+               the formula is the same as for the layer2 transmit
+               hash policy.
+
+               This policy is intended to provide a more balanced
+               distribution of traffic than layer2 alone, especially
+               in environments where a layer3 gateway device is
+               required to reach most destinations.
+
+               This algorithm is 802.3ad compliant.
+
+       layer3+4
+
+               This policy uses upper layer protocol information,
+               when available, to generate the hash.  This allows for
+               traffic to a particular network peer to span multiple
+               slaves, although a single connection will not span
+               multiple slaves.
+
+               The formula for unfragmented TCP and UDP packets is
+
+               hash = source port, destination port (as in the header)
+               hash = hash XOR source IP XOR destination IP
+               hash = hash XOR (hash RSHIFT 16)
+               hash = hash XOR (hash RSHIFT 8)
+               And then hash is reduced modulo slave count.
+
+               If the protocol is IPv6 then the source and destination
+               addresses are first hashed using ipv6_addr_hash.
+
+               For fragmented TCP or UDP packets and all other IPv4 and
+               IPv6 protocol traffic, the source and destination port
+               information is omitted.  For non-IP traffic, the
+               formula is the same as for the layer2 transmit hash
+               policy.
+
+               This algorithm is not fully 802.3ad compliant.  A
+               single TCP or UDP conversation containing both
+               fragmented and unfragmented packets will see packets
+               striped across two interfaces.  This may result in out
+               of order delivery.  Most traffic types will not meet
+               this criteria, as TCP rarely fragments traffic, and
+               most UDP traffic is not involved in extended
+               conversations.  Other implementations of 802.3ad may
+               or may not tolerate this noncompliance.
+
+       encap2+3
+
+               This policy uses the same formula as layer2+3 but it
+               relies on skb_flow_dissect to obtain the header fields
+               which might result in the use of inner headers if an
+               encapsulation protocol is used. For example this will
+               improve the performance for tunnel users because the
+               packets will be distributed according to the encapsulated
+               flows.
+
+       encap3+4
+
+               This policy uses the same formula as layer3+4 but it
+               relies on skb_flow_dissect to obtain the header fields
+               which might result in the use of inner headers if an
+               encapsulation protocol is used. For example this will
+               improve the performance for tunnel users because the
+               packets will be distributed according to the encapsulated
+               flows.
+
+       The default value is layer2.  This option was added in bonding
+       version 2.6.3.  In earlier versions of bonding, this parameter
+       does not exist, and the layer2 policy is the only policy.  The
+       layer2+3 value was added for bonding version 3.2.2.
+
+resend_igmp
+
+       Specifies the number of IGMP membership reports to be issued after
+       a failover event. One membership report is issued immediately after
+       the failover, subsequent packets are sent in each 200ms interval.
+
+       The valid range is 0 - 255; the default value is 1. A value of 0
+       prevents the IGMP membership report from being issued in response
+       to the failover event.
+
+       This option is useful for bonding modes balance-rr (0), active-backup
+       (1), balance-tlb (5) and balance-alb (6), in which a failover can
+       switch the IGMP traffic from one slave to another.  Therefore a fresh
+       IGMP report must be issued to cause the switch to forward the incoming
+       IGMP traffic over the newly selected slave.
+
+       This option was added for bonding version 3.7.0.
+
+lp_interval
+
+       Specifies the number of seconds between instances where the bonding
+       driver sends learning packets to each slaves peer switch.
+
+       The valid range is 1 - 0x7fffffff; the default value is 1. This Option
+       has effect only in balance-tlb and balance-alb modes.
+
+3. Configuring Bonding Devices
+==============================
+
+You can configure bonding using either your distro's network
+initialization scripts, or manually using either iproute2 or the
+sysfs interface.  Distros generally use one of three packages for the
+network initialization scripts: initscripts, sysconfig or interfaces.
+Recent versions of these packages have support for bonding, while older
+versions do not.
+
+We will first describe the options for configuring bonding for
+distros using versions of initscripts, sysconfig and interfaces with full
+or partial support for bonding, then provide information on enabling
+bonding without support from the network initialization scripts (i.e.,
+older versions of initscripts or sysconfig).
+
+If you're unsure whether your distro uses sysconfig,
+initscripts or interfaces, or don't know if it's new enough, have no fear.
+Determining this is fairly straightforward.
+
+First, look for a file called interfaces in /etc/network directory.
+If this file is present in your system, then your system use interfaces. See
+Configuration with Interfaces Support.
+
+Else, issue the command::
+
+       $ rpm -qf /sbin/ifup
+
+It will respond with a line of text starting with either
+"initscripts" or "sysconfig," followed by some numbers.  This is the
+package that provides your network initialization scripts.
+
+Next, to determine if your installation supports bonding,
+issue the command::
+
+    $ grep ifenslave /sbin/ifup
+
+If this returns any matches, then your initscripts or
+sysconfig has support for bonding.
+
+3.1 Configuration with Sysconfig Support
+----------------------------------------
+
+This section applies to distros using a version of sysconfig
+with bonding support, for example, SuSE Linux Enterprise Server 9.
+
+SuSE SLES 9's networking configuration system does support
+bonding, however, at this writing, the YaST system configuration
+front end does not provide any means to work with bonding devices.
+Bonding devices can be managed by hand, however, as follows.
+
+First, if they have not already been configured, configure the
+slave devices.  On SLES 9, this is most easily done by running the
+yast2 sysconfig configuration utility.  The goal is for to create an
+ifcfg-id file for each slave device.  The simplest way to accomplish
+this is to configure the devices for DHCP (this is only to get the
+file ifcfg-id file created; see below for some issues with DHCP).  The
+name of the configuration file for each device will be of the form::
+
+    ifcfg-id-xx:xx:xx:xx:xx:xx
+
+Where the "xx" portion will be replaced with the digits from
+the device's permanent MAC address.
+
+Once the set of ifcfg-id-xx:xx:xx:xx:xx:xx files has been
+created, it is necessary to edit the configuration files for the slave
+devices (the MAC addresses correspond to those of the slave devices).
+Before editing, the file will contain multiple lines, and will look
+something like this::
+
+       BOOTPROTO='dhcp'
+       STARTMODE='on'
+       USERCTL='no'
+       UNIQUE='XNzu.WeZGOGF+4wE'
+       _nm_name='bus-pci-0001:61:01.0'
+
+Change the BOOTPROTO and STARTMODE lines to the following::
+
+       BOOTPROTO='none'
+       STARTMODE='off'
+
+Do not alter the UNIQUE or _nm_name lines.  Remove any other
+lines (USERCTL, etc).
+
+Once the ifcfg-id-xx:xx:xx:xx:xx:xx files have been modified,
+it's time to create the configuration file for the bonding device
+itself.  This file is named ifcfg-bondX, where X is the number of the
+bonding device to create, starting at 0.  The first such file is
+ifcfg-bond0, the second is ifcfg-bond1, and so on.  The sysconfig
+network configuration system will correctly start multiple instances
+of bonding.
+
+The contents of the ifcfg-bondX file is as follows::
+
+       BOOTPROTO="static"
+       BROADCAST="10.0.2.255"
+       IPADDR="10.0.2.10"
+       NETMASK="255.255.0.0"
+       NETWORK="10.0.2.0"
+       REMOTE_IPADDR=""
+       STARTMODE="onboot"
+       BONDING_MASTER="yes"
+       BONDING_MODULE_OPTS="mode=active-backup miimon=100"
+       BONDING_SLAVE0="eth0"
+       BONDING_SLAVE1="bus-pci-0000:06:08.1"
+
+Replace the sample BROADCAST, IPADDR, NETMASK and NETWORK
+values with the appropriate values for your network.
+
+The STARTMODE specifies when the device is brought online.
+The possible values are:
+
+       ======== ======================================================
+       onboot   The device is started at boot time.  If you're not
+                sure, this is probably what you want.
+
+       manual   The device is started only when ifup is called
+                manually.  Bonding devices may be configured this
+                way if you do not wish them to start automatically
+                at boot for some reason.
+
+       hotplug  The device is started by a hotplug event.  This is not
+                a valid choice for a bonding device.
+
+       off or   The device configuration is ignored.
+       ignore
+       ======== ======================================================
+
+The line BONDING_MASTER='yes' indicates that the device is a
+bonding master device.  The only useful value is "yes."
+
+The contents of BONDING_MODULE_OPTS are supplied to the
+instance of the bonding module for this device.  Specify the options
+for the bonding mode, link monitoring, and so on here.  Do not include
+the max_bonds bonding parameter; this will confuse the configuration
+system if you have multiple bonding devices.
+
+Finally, supply one BONDING_SLAVEn="slave device" for each
+slave.  where "n" is an increasing value, one for each slave.  The
+"slave device" is either an interface name, e.g., "eth0", or a device
+specifier for the network device.  The interface name is easier to
+find, but the ethN names are subject to change at boot time if, e.g.,
+a device early in the sequence has failed.  The device specifiers
+(bus-pci-0000:06:08.1 in the example above) specify the physical
+network device, and will not change unless the device's bus location
+changes (for example, it is moved from one PCI slot to another).  The
+example above uses one of each type for demonstration purposes; most
+configurations will choose one or the other for all slave devices.
+
+When all configuration files have been modified or created,
+networking must be restarted for the configuration changes to take
+effect.  This can be accomplished via the following::
+
+       # /etc/init.d/network restart
+
+Note that the network control script (/sbin/ifdown) will
+remove the bonding module as part of the network shutdown processing,
+so it is not necessary to remove the module by hand if, e.g., the
+module parameters have changed.
+
+Also, at this writing, YaST/YaST2 will not manage bonding
+devices (they do not show bonding interfaces on its list of network
+devices).  It is necessary to edit the configuration file by hand to
+change the bonding configuration.
+
+Additional general options and details of the ifcfg file
+format can be found in an example ifcfg template file::
+
+       /etc/sysconfig/network/ifcfg.template
+
+Note that the template does not document the various ``BONDING_*``
+settings described above, but does describe many of the other options.
+
+3.1.1 Using DHCP with Sysconfig
+-------------------------------
+
+Under sysconfig, configuring a device with BOOTPROTO='dhcp'
+will cause it to query DHCP for its IP address information.  At this
+writing, this does not function for bonding devices; the scripts
+attempt to obtain the device address from DHCP prior to adding any of
+the slave devices.  Without active slaves, the DHCP requests are not
+sent to the network.
+
+3.1.2 Configuring Multiple Bonds with Sysconfig
+-----------------------------------------------
+
+The sysconfig network initialization system is capable of
+handling multiple bonding devices.  All that is necessary is for each
+bonding instance to have an appropriately configured ifcfg-bondX file
+(as described above).  Do not specify the "max_bonds" parameter to any
+instance of bonding, as this will confuse sysconfig.  If you require
+multiple bonding devices with identical parameters, create multiple
+ifcfg-bondX files.
+
+Because the sysconfig scripts supply the bonding module
+options in the ifcfg-bondX file, it is not necessary to add them to
+the system ``/etc/modules.d/*.conf`` configuration files.
+
+3.2 Configuration with Initscripts Support
+------------------------------------------
+
+This section applies to distros using a recent version of
+initscripts with bonding support, for example, Red Hat Enterprise Linux
+version 3 or later, Fedora, etc.  On these systems, the network
+initialization scripts have knowledge of bonding, and can be configured to
+control bonding devices.  Note that older versions of the initscripts
+package have lower levels of support for bonding; this will be noted where
+applicable.
+
+These distros will not automatically load the network adapter
+driver unless the ethX device is configured with an IP address.
+Because of this constraint, users must manually configure a
+network-script file for all physical adapters that will be members of
+a bondX link.  Network script files are located in the directory:
+
+/etc/sysconfig/network-scripts
+
+The file name must be prefixed with "ifcfg-eth" and suffixed
+with the adapter's physical adapter number.  For example, the script
+for eth0 would be named /etc/sysconfig/network-scripts/ifcfg-eth0.
+Place the following text in the file::
+
+       DEVICE=eth0
+       USERCTL=no
+       ONBOOT=yes
+       MASTER=bond0
+       SLAVE=yes
+       BOOTPROTO=none
+
+The DEVICE= line will be different for every ethX device and
+must correspond with the name of the file, i.e., ifcfg-eth1 must have
+a device line of DEVICE=eth1.  The setting of the MASTER= line will
+also depend on the final bonding interface name chosen for your bond.
+As with other network devices, these typically start at 0, and go up
+one for each device, i.e., the first bonding instance is bond0, the
+second is bond1, and so on.
+
+Next, create a bond network script.  The file name for this
+script will be /etc/sysconfig/network-scripts/ifcfg-bondX where X is
+the number of the bond.  For bond0 the file is named "ifcfg-bond0",
+for bond1 it is named "ifcfg-bond1", and so on.  Within that file,
+place the following text::
+
+       DEVICE=bond0
+       IPADDR=192.168.1.1
+       NETMASK=255.255.255.0
+       NETWORK=192.168.1.0
+       BROADCAST=192.168.1.255
+       ONBOOT=yes
+       BOOTPROTO=none
+       USERCTL=no
+
+Be sure to change the networking specific lines (IPADDR,
+NETMASK, NETWORK and BROADCAST) to match your network configuration.
+
+For later versions of initscripts, such as that found with Fedora
+7 (or later) and Red Hat Enterprise Linux version 5 (or later), it is possible,
+and, indeed, preferable, to specify the bonding options in the ifcfg-bond0
+file, e.g. a line of the format::
+
+  BONDING_OPTS="mode=active-backup arp_interval=60 arp_ip_target=192.168.1.254"
+
+will configure the bond with the specified options.  The options
+specified in BONDING_OPTS are identical to the bonding module parameters
+except for the arp_ip_target field when using versions of initscripts older
+than and 8.57 (Fedora 8) and 8.45.19 (Red Hat Enterprise Linux 5.2).  When
+using older versions each target should be included as a separate option and
+should be preceded by a '+' to indicate it should be added to the list of
+queried targets, e.g.,::
+
+    arp_ip_target=+192.168.1.1 arp_ip_target=+192.168.1.2
+
+is the proper syntax to specify multiple targets.  When specifying
+options via BONDING_OPTS, it is not necessary to edit
+``/etc/modprobe.d/*.conf``.
+
+For even older versions of initscripts that do not support
+BONDING_OPTS, it is necessary to edit /etc/modprobe.d/*.conf, depending upon
+your distro) to load the bonding module with your desired options when the
+bond0 interface is brought up.  The following lines in /etc/modprobe.d/*.conf
+will load the bonding module, and select its options:
+
+       alias bond0 bonding
+       options bond0 mode=balance-alb miimon=100
+
+Replace the sample parameters with the appropriate set of
+options for your configuration.
+
+Finally run "/etc/rc.d/init.d/network restart" as root.  This
+will restart the networking subsystem and your bond link should be now
+up and running.
+
+3.2.1 Using DHCP with Initscripts
+---------------------------------
+
+Recent versions of initscripts (the versions supplied with Fedora
+Core 3 and Red Hat Enterprise Linux 4, or later versions, are reported to
+work) have support for assigning IP information to bonding devices via
+DHCP.
+
+To configure bonding for DHCP, configure it as described
+above, except replace the line "BOOTPROTO=none" with "BOOTPROTO=dhcp"
+and add a line consisting of "TYPE=Bonding".  Note that the TYPE value
+is case sensitive.
+
+3.2.2 Configuring Multiple Bonds with Initscripts
+-------------------------------------------------
+
+Initscripts packages that are included with Fedora 7 and Red Hat
+Enterprise Linux 5 support multiple bonding interfaces by simply
+specifying the appropriate BONDING_OPTS= in ifcfg-bondX where X is the
+number of the bond.  This support requires sysfs support in the kernel,
+and a bonding driver of version 3.0.0 or later.  Other configurations may
+not support this method for specifying multiple bonding interfaces; for
+those instances, see the "Configuring Multiple Bonds Manually" section,
+below.
+
+3.3 Configuring Bonding Manually with iproute2
+-----------------------------------------------
+
+This section applies to distros whose network initialization
+scripts (the sysconfig or initscripts package) do not have specific
+knowledge of bonding.  One such distro is SuSE Linux Enterprise Server
+version 8.
+
+The general method for these systems is to place the bonding
+module parameters into a config file in /etc/modprobe.d/ (as
+appropriate for the installed distro), then add modprobe and/or
+`ip link` commands to the system's global init script.  The name of
+the global init script differs; for sysconfig, it is
+/etc/init.d/boot.local and for initscripts it is /etc/rc.d/rc.local.
+
+For example, if you wanted to make a simple bond of two e100
+devices (presumed to be eth0 and eth1), and have it persist across
+reboots, edit the appropriate file (/etc/init.d/boot.local or
+/etc/rc.d/rc.local), and add the following::
+
+       modprobe bonding mode=balance-alb miimon=100
+       modprobe e100
+       ifconfig bond0 192.168.1.1 netmask 255.255.255.0 up
+       ip link set eth0 master bond0
+       ip link set eth1 master bond0
+
+Replace the example bonding module parameters and bond0
+network configuration (IP address, netmask, etc) with the appropriate
+values for your configuration.
+
+Unfortunately, this method will not provide support for the
+ifup and ifdown scripts on the bond devices.  To reload the bonding
+configuration, it is necessary to run the initialization script, e.g.,::
+
+       # /etc/init.d/boot.local
+
+or::
+
+       # /etc/rc.d/rc.local
+
+It may be desirable in such a case to create a separate script
+which only initializes the bonding configuration, then call that
+separate script from within boot.local.  This allows for bonding to be
+enabled without re-running the entire global init script.
+
+To shut down the bonding devices, it is necessary to first
+mark the bonding device itself as being down, then remove the
+appropriate device driver modules.  For our example above, you can do
+the following::
+
+       # ifconfig bond0 down
+       # rmmod bonding
+       # rmmod e100
+
+Again, for convenience, it may be desirable to create a script
+with these commands.
+
+
+3.3.1 Configuring Multiple Bonds Manually
+-----------------------------------------
+
+This section contains information on configuring multiple
+bonding devices with differing options for those systems whose network
+initialization scripts lack support for configuring multiple bonds.
+
+If you require multiple bonding devices, but all with the same
+options, you may wish to use the "max_bonds" module parameter,
+documented above.
+
+To create multiple bonding devices with differing options, it is
+preferable to use bonding parameters exported by sysfs, documented in the
+section below.
+
+For versions of bonding without sysfs support, the only means to
+provide multiple instances of bonding with differing options is to load
+the bonding driver multiple times.  Note that current versions of the
+sysconfig network initialization scripts handle this automatically; if
+your distro uses these scripts, no special action is needed.  See the
+section Configuring Bonding Devices, above, if you're not sure about your
+network initialization scripts.
+
+To load multiple instances of the module, it is necessary to
+specify a different name for each instance (the module loading system
+requires that every loaded module, even multiple instances of the same
+module, have a unique name).  This is accomplished by supplying multiple
+sets of bonding options in ``/etc/modprobe.d/*.conf``, for example::
+
+       alias bond0 bonding
+       options bond0 -o bond0 mode=balance-rr miimon=100
+
+       alias bond1 bonding
+       options bond1 -o bond1 mode=balance-alb miimon=50
+
+will load the bonding module two times.  The first instance is
+named "bond0" and creates the bond0 device in balance-rr mode with an
+miimon of 100.  The second instance is named "bond1" and creates the
+bond1 device in balance-alb mode with an miimon of 50.
+
+In some circumstances (typically with older distributions),
+the above does not work, and the second bonding instance never sees
+its options.  In that case, the second options line can be substituted
+as follows::
+
+       install bond1 /sbin/modprobe --ignore-install bonding -o bond1 \
+                                    mode=balance-alb miimon=50
+
+This may be repeated any number of times, specifying a new and
+unique name in place of bond1 for each subsequent instance.
+
+It has been observed that some Red Hat supplied kernels are unable
+to rename modules at load time (the "-o bond1" part).  Attempts to pass
+that option to modprobe will produce an "Operation not permitted" error.
+This has been reported on some Fedora Core kernels, and has been seen on
+RHEL 4 as well.  On kernels exhibiting this problem, it will be impossible
+to configure multiple bonds with differing parameters (as they are older
+kernels, and also lack sysfs support).
+
+3.4 Configuring Bonding Manually via Sysfs
+------------------------------------------
+
+Starting with version 3.0.0, Channel Bonding may be configured
+via the sysfs interface.  This interface allows dynamic configuration
+of all bonds in the system without unloading the module.  It also
+allows for adding and removing bonds at runtime.  Ifenslave is no
+longer required, though it is still supported.
+
+Use of the sysfs interface allows you to use multiple bonds
+with different configurations without having to reload the module.
+It also allows you to use multiple, differently configured bonds when
+bonding is compiled into the kernel.
+
+You must have the sysfs filesystem mounted to configure
+bonding this way.  The examples in this document assume that you
+are using the standard mount point for sysfs, e.g. /sys.  If your
+sysfs filesystem is mounted elsewhere, you will need to adjust the
+example paths accordingly.
+
+Creating and Destroying Bonds
+-----------------------------
+To add a new bond foo::
+
+       # echo +foo > /sys/class/net/bonding_masters
+
+To remove an existing bond bar::
+
+       # echo -bar > /sys/class/net/bonding_masters
+
+To show all existing bonds::
+
+       # cat /sys/class/net/bonding_masters
+
+.. note::
+
+   due to 4K size limitation of sysfs files, this list may be
+   truncated if you have more than a few hundred bonds.  This is unlikely
+   to occur under normal operating conditions.
+
+Adding and Removing Slaves
+--------------------------
+Interfaces may be enslaved to a bond using the file
+/sys/class/net/<bond>/bonding/slaves.  The semantics for this file
+are the same as for the bonding_masters file.
+
+To enslave interface eth0 to bond bond0::
+
+       # ifconfig bond0 up
+       # echo +eth0 > /sys/class/net/bond0/bonding/slaves
+
+To free slave eth0 from bond bond0::
+
+       # echo -eth0 > /sys/class/net/bond0/bonding/slaves
+
+When an interface is enslaved to a bond, symlinks between the
+two are created in the sysfs filesystem.  In this case, you would get
+/sys/class/net/bond0/slave_eth0 pointing to /sys/class/net/eth0, and
+/sys/class/net/eth0/master pointing to /sys/class/net/bond0.
+
+This means that you can tell quickly whether or not an
+interface is enslaved by looking for the master symlink.  Thus:
+# echo -eth0 > /sys/class/net/eth0/master/bonding/slaves
+will free eth0 from whatever bond it is enslaved to, regardless of
+the name of the bond interface.
+
+Changing a Bond's Configuration
+-------------------------------
+Each bond may be configured individually by manipulating the
+files located in /sys/class/net/<bond name>/bonding
+
+The names of these files correspond directly with the command-
+line parameters described elsewhere in this file, and, with the
+exception of arp_ip_target, they accept the same values.  To see the
+current setting, simply cat the appropriate file.
+
+A few examples will be given here; for specific usage
+guidelines for each parameter, see the appropriate section in this
+document.
+
+To configure bond0 for balance-alb mode::
+
+       # ifconfig bond0 down
+       # echo 6 > /sys/class/net/bond0/bonding/mode
+       - or -
+       # echo balance-alb > /sys/class/net/bond0/bonding/mode
+
+.. note::
+
+   The bond interface must be down before the mode can be changed.
+
+To enable MII monitoring on bond0 with a 1 second interval::
+
+       # echo 1000 > /sys/class/net/bond0/bonding/miimon
+
+.. note::
+
+   If ARP monitoring is enabled, it will disabled when MII
+   monitoring is enabled, and vice-versa.
+
+To add ARP targets::
+
+       # echo +192.168.0.100 > /sys/class/net/bond0/bonding/arp_ip_target
+       # echo +192.168.0.101 > /sys/class/net/bond0/bonding/arp_ip_target
+
+.. note::
+
+   up to 16 target addresses may be specified.
+
+To remove an ARP target::
+
+       # echo -192.168.0.100 > /sys/class/net/bond0/bonding/arp_ip_target
+
+To configure the interval between learning packet transmits::
+
+       # echo 12 > /sys/class/net/bond0/bonding/lp_interval
+
+.. note::
+
+   the lp_interval is the number of seconds between instances where
+   the bonding driver sends learning packets to each slaves peer switch.  The
+   default interval is 1 second.
+
+Example Configuration
+---------------------
+We begin with the same example that is shown in section 3.3,
+executed with sysfs, and without using ifenslave.
+
+To make a simple bond of two e100 devices (presumed to be eth0
+and eth1), and have it persist across reboots, edit the appropriate
+file (/etc/init.d/boot.local or /etc/rc.d/rc.local), and add the
+following::
+
+       modprobe bonding
+       modprobe e100
+       echo balance-alb > /sys/class/net/bond0/bonding/mode
+       ifconfig bond0 192.168.1.1 netmask 255.255.255.0 up
+       echo 100 > /sys/class/net/bond0/bonding/miimon
+       echo +eth0 > /sys/class/net/bond0/bonding/slaves
+       echo +eth1 > /sys/class/net/bond0/bonding/slaves
+
+To add a second bond, with two e1000 interfaces in
+active-backup mode, using ARP monitoring, add the following lines to
+your init script::
+
+       modprobe e1000
+       echo +bond1 > /sys/class/net/bonding_masters
+       echo active-backup > /sys/class/net/bond1/bonding/mode
+       ifconfig bond1 192.168.2.1 netmask 255.255.255.0 up
+       echo +192.168.2.100 /sys/class/net/bond1/bonding/arp_ip_target
+       echo 2000 > /sys/class/net/bond1/bonding/arp_interval
+       echo +eth2 > /sys/class/net/bond1/bonding/slaves
+       echo +eth3 > /sys/class/net/bond1/bonding/slaves
+
+3.5 Configuration with Interfaces Support
+-----------------------------------------
+
+This section applies to distros which use /etc/network/interfaces file
+to describe network interface configuration, most notably Debian and it's
+derivatives.
+
+The ifup and ifdown commands on Debian don't support bonding out of
+the box. The ifenslave-2.6 package should be installed to provide bonding
+support.  Once installed, this package will provide ``bond-*`` options
+to be used into /etc/network/interfaces.
+
+Note that ifenslave-2.6 package will load the bonding module and use
+the ifenslave command when appropriate.
+
+Example Configurations
+----------------------
+
+In /etc/network/interfaces, the following stanza will configure bond0, in
+active-backup mode, with eth0 and eth1 as slaves::
+
+       auto bond0
+       iface bond0 inet dhcp
+               bond-slaves eth0 eth1
+               bond-mode active-backup
+               bond-miimon 100
+               bond-primary eth0 eth1
+
+If the above configuration doesn't work, you might have a system using
+upstart for system startup. This is most notably true for recent
+Ubuntu versions. The following stanza in /etc/network/interfaces will
+produce the same result on those systems::
+
+       auto bond0
+       iface bond0 inet dhcp
+               bond-slaves none
+               bond-mode active-backup
+               bond-miimon 100
+
+       auto eth0
+       iface eth0 inet manual
+               bond-master bond0
+               bond-primary eth0 eth1
+
+       auto eth1
+       iface eth1 inet manual
+               bond-master bond0
+               bond-primary eth0 eth1
+
+For a full list of ``bond-*`` supported options in /etc/network/interfaces and
+some more advanced examples tailored to you particular distros, see the files in
+/usr/share/doc/ifenslave-2.6.
+
+3.6 Overriding Configuration for Special Cases
+----------------------------------------------
+
+When using the bonding driver, the physical port which transmits a frame is
+typically selected by the bonding driver, and is not relevant to the user or
+system administrator.  The output port is simply selected using the policies of
+the selected bonding mode.  On occasion however, it is helpful to direct certain
+classes of traffic to certain physical interfaces on output to implement
+slightly more complex policies.  For example, to reach a web server over a
+bonded interface in which eth0 connects to a private network, while eth1
+connects via a public network, it may be desirous to bias the bond to send said
+traffic over eth0 first, using eth1 only as a fall back, while all other traffic
+can safely be sent over either interface.  Such configurations may be achieved
+using the traffic control utilities inherent in linux.
+
+By default the bonding driver is multiqueue aware and 16 queues are created
+when the driver initializes (see Documentation/networking/multiqueue.rst
+for details).  If more or less queues are desired the module parameter
+tx_queues can be used to change this value.  There is no sysfs parameter
+available as the allocation is done at module init time.
+
+The output of the file /proc/net/bonding/bondX has changed so the output Queue
+ID is now printed for each slave::
+
+       Bonding Mode: fault-tolerance (active-backup)
+       Primary Slave: None
+       Currently Active Slave: eth0
+       MII Status: up
+       MII Polling Interval (ms): 0
+       Up Delay (ms): 0
+       Down Delay (ms): 0
+
+       Slave Interface: eth0
+       MII Status: up
+       Link Failure Count: 0
+       Permanent HW addr: 00:1a:a0:12:8f:cb
+       Slave queue ID: 0
+
+       Slave Interface: eth1
+       MII Status: up
+       Link Failure Count: 0
+       Permanent HW addr: 00:1a:a0:12:8f:cc
+       Slave queue ID: 2
+
+The queue_id for a slave can be set using the command::
+
+       # echo "eth1:2" > /sys/class/net/bond0/bonding/queue_id
+
+Any interface that needs a queue_id set should set it with multiple calls
+like the one above until proper priorities are set for all interfaces.  On
+distributions that allow configuration via initscripts, multiple 'queue_id'
+arguments can be added to BONDING_OPTS to set all needed slave queues.
+
+These queue id's can be used in conjunction with the tc utility to configure
+a multiqueue qdisc and filters to bias certain traffic to transmit on certain
+slave devices.  For instance, say we wanted, in the above configuration to
+force all traffic bound to 192.168.1.100 to use eth1 in the bond as its output
+device. The following commands would accomplish this::
+
+       # tc qdisc add dev bond0 handle 1 root multiq
+
+       # tc filter add dev bond0 protocol ip parent 1: prio 1 u32 match ip \
+               dst 192.168.1.100 action skbedit queue_mapping 2
+
+These commands tell the kernel to attach a multiqueue queue discipline to the
+bond0 interface and filter traffic enqueued to it, such that packets with a dst
+ip of 192.168.1.100 have their output queue mapping value overwritten to 2.
+This value is then passed into the driver, causing the normal output path
+selection policy to be overridden, selecting instead qid 2, which maps to eth1.
+
+Note that qid values begin at 1.  Qid 0 is reserved to initiate to the driver
+that normal output policy selection should take place.  One benefit to simply
+leaving the qid for a slave to 0 is the multiqueue awareness in the bonding
+driver that is now present.  This awareness allows tc filters to be placed on
+slave devices as well as bond devices and the bonding driver will simply act as
+a pass-through for selecting output queues on the slave device rather than
+output port selection.
+
+This feature first appeared in bonding driver version 3.7.0 and support for
+output slave selection was limited to round-robin and active-backup modes.
+
+3.7 Configuring LACP for 802.3ad mode in a more secure way
+----------------------------------------------------------
+
+When using 802.3ad bonding mode, the Actor (host) and Partner (switch)
+exchange LACPDUs.  These LACPDUs cannot be sniffed, because they are
+destined to link local mac addresses (which switches/bridges are not
+supposed to forward).  However, most of the values are easily predictable
+or are simply the machine's MAC address (which is trivially known to all
+other hosts in the same L2).  This implies that other machines in the L2
+domain can spoof LACPDU packets from other hosts to the switch and potentially
+cause mayhem by joining (from the point of view of the switch) another
+machine's aggregate, thus receiving a portion of that hosts incoming
+traffic and / or spoofing traffic from that machine themselves (potentially
+even successfully terminating some portion of flows). Though this is not
+a likely scenario, one could avoid this possibility by simply configuring
+few bonding parameters:
+
+   (a) ad_actor_system : You can set a random mac-address that can be used for
+       these LACPDU exchanges. The value can not be either NULL or Multicast.
+       Also it's preferable to set the local-admin bit. Following shell code
+       generates a random mac-address as described above::
+
+             # sys_mac_addr=$(printf '%02x:%02x:%02x:%02x:%02x:%02x' \
+                                      $(( (RANDOM & 0xFE) | 0x02 )) \
+                                      $(( RANDOM & 0xFF )) \
+                                      $(( RANDOM & 0xFF )) \
+                                      $(( RANDOM & 0xFF )) \
+                                      $(( RANDOM & 0xFF )) \
+                                      $(( RANDOM & 0xFF )))
+             # echo $sys_mac_addr > /sys/class/net/bond0/bonding/ad_actor_system
+
+   (b) ad_actor_sys_prio : Randomize the system priority. The default value
+       is 65535, but system can take the value from 1 - 65535. Following shell
+       code generates random priority and sets it::
+
+           # sys_prio=$(( 1 + RANDOM + RANDOM ))
+           # echo $sys_prio > /sys/class/net/bond0/bonding/ad_actor_sys_prio
+
+   (c) ad_user_port_key : Use the user portion of the port-key. The default
+       keeps this empty. These are the upper 10 bits of the port-key and value
+       ranges from 0 - 1023. Following shell code generates these 10 bits and
+       sets it::
+
+           # usr_port_key=$(( RANDOM & 0x3FF ))
+           # echo $usr_port_key > /sys/class/net/bond0/bonding/ad_user_port_key
+
+
+4 Querying Bonding Configuration
+=================================
+
+4.1 Bonding Configuration
+-------------------------
+
+Each bonding device has a read-only file residing in the
+/proc/net/bonding directory.  The file contents include information
+about the bonding configuration, options and state of each slave.
+
+For example, the contents of /proc/net/bonding/bond0 after the
+driver is loaded with parameters of mode=0 and miimon=1000 is
+generally as follows::
+
+       Ethernet Channel Bonding Driver: 2.6.1 (October 29, 2004)
+       Bonding Mode: load balancing (round-robin)
+       Currently Active Slave: eth0
+       MII Status: up
+       MII Polling Interval (ms): 1000
+       Up Delay (ms): 0
+       Down Delay (ms): 0
+
+       Slave Interface: eth1
+       MII Status: up
+       Link Failure Count: 1
+
+       Slave Interface: eth0
+       MII Status: up
+       Link Failure Count: 1
+
+The precise format and contents will change depending upon the
+bonding configuration, state, and version of the bonding driver.
+
+4.2 Network configuration
+-------------------------
+
+The network configuration can be inspected using the ifconfig
+command.  Bonding devices will have the MASTER flag set; Bonding slave
+devices will have the SLAVE flag set.  The ifconfig output does not
+contain information on which slaves are associated with which masters.
+
+In the example below, the bond0 interface is the master
+(MASTER) while eth0 and eth1 are slaves (SLAVE). Notice all slaves of
+bond0 have the same MAC address (HWaddr) as bond0 for all modes except
+TLB and ALB that require a unique MAC address for each slave::
+
+  # /sbin/ifconfig
+  bond0     Link encap:Ethernet  HWaddr 00:C0:F0:1F:37:B4
+           inet addr:XXX.XXX.XXX.YYY  Bcast:XXX.XXX.XXX.255  Mask:255.255.252.0
+           UP BROADCAST RUNNING MASTER MULTICAST  MTU:1500  Metric:1
+           RX packets:7224794 errors:0 dropped:0 overruns:0 frame:0
+           TX packets:3286647 errors:1 dropped:0 overruns:1 carrier:0
+           collisions:0 txqueuelen:0
+
+  eth0      Link encap:Ethernet  HWaddr 00:C0:F0:1F:37:B4
+           UP BROADCAST RUNNING SLAVE MULTICAST  MTU:1500  Metric:1
+           RX packets:3573025 errors:0 dropped:0 overruns:0 frame:0
+           TX packets:1643167 errors:1 dropped:0 overruns:1 carrier:0
+           collisions:0 txqueuelen:100
+           Interrupt:10 Base address:0x1080
+
+  eth1      Link encap:Ethernet  HWaddr 00:C0:F0:1F:37:B4
+           UP BROADCAST RUNNING SLAVE MULTICAST  MTU:1500  Metric:1
+           RX packets:3651769 errors:0 dropped:0 overruns:0 frame:0
+           TX packets:1643480 errors:0 dropped:0 overruns:0 carrier:0
+           collisions:0 txqueuelen:100
+           Interrupt:9 Base address:0x1400
+
+5. Switch Configuration
+=======================
+
+For this section, "switch" refers to whatever system the
+bonded devices are directly connected to (i.e., where the other end of
+the cable plugs into).  This may be an actual dedicated switch device,
+or it may be another regular system (e.g., another computer running
+Linux),
+
+The active-backup, balance-tlb and balance-alb modes do not
+require any specific configuration of the switch.
+
+The 802.3ad mode requires that the switch have the appropriate
+ports configured as an 802.3ad aggregation.  The precise method used
+to configure this varies from switch to switch, but, for example, a
+Cisco 3550 series switch requires that the appropriate ports first be
+grouped together in a single etherchannel instance, then that
+etherchannel is set to mode "lacp" to enable 802.3ad (instead of
+standard EtherChannel).
+
+The balance-rr, balance-xor and broadcast modes generally
+require that the switch have the appropriate ports grouped together.
+The nomenclature for such a group differs between switches, it may be
+called an "etherchannel" (as in the Cisco example, above), a "trunk
+group" or some other similar variation.  For these modes, each switch
+will also have its own configuration options for the switch's transmit
+policy to the bond.  Typical choices include XOR of either the MAC or
+IP addresses.  The transmit policy of the two peers does not need to
+match.  For these three modes, the bonding mode really selects a
+transmit policy for an EtherChannel group; all three will interoperate
+with another EtherChannel group.
+
+
+6. 802.1q VLAN Support
+======================
+
+It is possible to configure VLAN devices over a bond interface
+using the 8021q driver.  However, only packets coming from the 8021q
+driver and passing through bonding will be tagged by default.  Self
+generated packets, for example, bonding's learning packets or ARP
+packets generated by either ALB mode or the ARP monitor mechanism, are
+tagged internally by bonding itself.  As a result, bonding must
+"learn" the VLAN IDs configured above it, and use those IDs to tag
+self generated packets.
+
+For reasons of simplicity, and to support the use of adapters
+that can do VLAN hardware acceleration offloading, the bonding
+interface declares itself as fully hardware offloading capable, it gets
+the add_vid/kill_vid notifications to gather the necessary
+information, and it propagates those actions to the slaves.  In case
+of mixed adapter types, hardware accelerated tagged packets that
+should go through an adapter that is not offloading capable are
+"un-accelerated" by the bonding driver so the VLAN tag sits in the
+regular location.
+
+VLAN interfaces *must* be added on top of a bonding interface
+only after enslaving at least one slave.  The bonding interface has a
+hardware address of 00:00:00:00:00:00 until the first slave is added.
+If the VLAN interface is created prior to the first enslavement, it
+would pick up the all-zeroes hardware address.  Once the first slave
+is attached to the bond, the bond device itself will pick up the
+slave's hardware address, which is then available for the VLAN device.
+
+Also, be aware that a similar problem can occur if all slaves
+are released from a bond that still has one or more VLAN interfaces on
+top of it.  When a new slave is added, the bonding interface will
+obtain its hardware address from the first slave, which might not
+match the hardware address of the VLAN interfaces (which was
+ultimately copied from an earlier slave).
+
+There are two methods to insure that the VLAN device operates
+with the correct hardware address if all slaves are removed from a
+bond interface:
+
+1. Remove all VLAN interfaces then recreate them
+
+2. Set the bonding interface's hardware address so that it
+matches the hardware address of the VLAN interfaces.
+
+Note that changing a VLAN interface's HW address would set the
+underlying device -- i.e. the bonding interface -- to promiscuous
+mode, which might not be what you want.
+
+
+7. Link Monitoring
+==================
+
+The bonding driver at present supports two schemes for
+monitoring a slave device's link state: the ARP monitor and the MII
+monitor.
+
+At the present time, due to implementation restrictions in the
+bonding driver itself, it is not possible to enable both ARP and MII
+monitoring simultaneously.
+
+7.1 ARP Monitor Operation
+-------------------------
+
+The ARP monitor operates as its name suggests: it sends ARP
+queries to one or more designated peer systems on the network, and
+uses the response as an indication that the link is operating.  This
+gives some assurance that traffic is actually flowing to and from one
+or more peers on the local network.
+
+The ARP monitor relies on the device driver itself to verify
+that traffic is flowing.  In particular, the driver must keep up to
+date the last receive time, dev->last_rx.  Drivers that use NETIF_F_LLTX
+flag must also update netdev_queue->trans_start.  If they do not, then the
+ARP monitor will immediately fail any slaves using that driver, and
+those slaves will stay down.  If networking monitoring (tcpdump, etc)
+shows the ARP requests and replies on the network, then it may be that
+your device driver is not updating last_rx and trans_start.
+
+7.2 Configuring Multiple ARP Targets
+------------------------------------
+
+While ARP monitoring can be done with just one target, it can
+be useful in a High Availability setup to have several targets to
+monitor.  In the case of just one target, the target itself may go
+down or have a problem making it unresponsive to ARP requests.  Having
+an additional target (or several) increases the reliability of the ARP
+monitoring.
+
+Multiple ARP targets must be separated by commas as follows::
+
+ # example options for ARP monitoring with three targets
+ alias bond0 bonding
+ options bond0 arp_interval=60 arp_ip_target=192.168.0.1,192.168.0.3,192.168.0.9
+
+For just a single target the options would resemble::
+
+    # example options for ARP monitoring with one target
+    alias bond0 bonding
+    options bond0 arp_interval=60 arp_ip_target=192.168.0.100
+
+
+7.3 MII Monitor Operation
+-------------------------
+
+The MII monitor monitors only the carrier state of the local
+network interface.  It accomplishes this in one of three ways: by
+depending upon the device driver to maintain its carrier state, by
+querying the device's MII registers, or by making an ethtool query to
+the device.
+
+If the use_carrier module parameter is 1 (the default value),
+then the MII monitor will rely on the driver for carrier state
+information (via the netif_carrier subsystem).  As explained in the
+use_carrier parameter information, above, if the MII monitor fails to
+detect carrier loss on the device (e.g., when the cable is physically
+disconnected), it may be that the driver does not support
+netif_carrier.
+
+If use_carrier is 0, then the MII monitor will first query the
+device's (via ioctl) MII registers and check the link state.  If that
+request fails (not just that it returns carrier down), then the MII
+monitor will make an ethtool ETHOOL_GLINK request to attempt to obtain
+the same information.  If both methods fail (i.e., the driver either
+does not support or had some error in processing both the MII register
+and ethtool requests), then the MII monitor will assume the link is
+up.
+
+8. Potential Sources of Trouble
+===============================
+
+8.1 Adventures in Routing
+-------------------------
+
+When bonding is configured, it is important that the slave
+devices not have routes that supersede routes of the master (or,
+generally, not have routes at all).  For example, suppose the bonding
+device bond0 has two slaves, eth0 and eth1, and the routing table is
+as follows::
+
+  Kernel IP routing table
+  Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
+  10.0.0.0        0.0.0.0         255.255.0.0     U        40 0          0 eth0
+  10.0.0.0        0.0.0.0         255.255.0.0     U        40 0          0 eth1
+  10.0.0.0        0.0.0.0         255.255.0.0     U        40 0          0 bond0
+  127.0.0.0       0.0.0.0         255.0.0.0       U        40 0          0 lo
+
+This routing configuration will likely still update the
+receive/transmit times in the driver (needed by the ARP monitor), but
+may bypass the bonding driver (because outgoing traffic to, in this
+case, another host on network 10 would use eth0 or eth1 before bond0).
+
+The ARP monitor (and ARP itself) may become confused by this
+configuration, because ARP requests (generated by the ARP monitor)
+will be sent on one interface (bond0), but the corresponding reply
+will arrive on a different interface (eth0).  This reply looks to ARP
+as an unsolicited ARP reply (because ARP matches replies on an
+interface basis), and is discarded.  The MII monitor is not affected
+by the state of the routing table.
+
+The solution here is simply to insure that slaves do not have
+routes of their own, and if for some reason they must, those routes do
+not supersede routes of their master.  This should generally be the
+case, but unusual configurations or errant manual or automatic static
+route additions may cause trouble.
+
+8.2 Ethernet Device Renaming
+----------------------------
+
+On systems with network configuration scripts that do not
+associate physical devices directly with network interface names (so
+that the same physical device always has the same "ethX" name), it may
+be necessary to add some special logic to config files in
+/etc/modprobe.d/.
+
+For example, given a modules.conf containing the following::
+
+       alias bond0 bonding
+       options bond0 mode=some-mode miimon=50
+       alias eth0 tg3
+       alias eth1 tg3
+       alias eth2 e1000
+       alias eth3 e1000
+
+If neither eth0 and eth1 are slaves to bond0, then when the
+bond0 interface comes up, the devices may end up reordered.  This
+happens because bonding is loaded first, then its slave device's
+drivers are loaded next.  Since no other drivers have been loaded,
+when the e1000 driver loads, it will receive eth0 and eth1 for its
+devices, but the bonding configuration tries to enslave eth2 and eth3
+(which may later be assigned to the tg3 devices).
+
+Adding the following::
+
+       add above bonding e1000 tg3
+
+causes modprobe to load e1000 then tg3, in that order, when
+bonding is loaded.  This command is fully documented in the
+modules.conf manual page.
+
+On systems utilizing modprobe an equivalent problem can occur.
+In this case, the following can be added to config files in
+/etc/modprobe.d/ as::
+
+       softdep bonding pre: tg3 e1000
+
+This will load tg3 and e1000 modules before loading the bonding one.
+Full documentation on this can be found in the modprobe.d and modprobe
+manual pages.
+
+8.3. Painfully Slow Or No Failed Link Detection By Miimon
+---------------------------------------------------------
+
+By default, bonding enables the use_carrier option, which
+instructs bonding to trust the driver to maintain carrier state.
+
+As discussed in the options section, above, some drivers do
+not support the netif_carrier_on/_off link state tracking system.
+With use_carrier enabled, bonding will always see these links as up,
+regardless of their actual state.
+
+Additionally, other drivers do support netif_carrier, but do
+not maintain it in real time, e.g., only polling the link state at
+some fixed interval.  In this case, miimon will detect failures, but
+only after some long period of time has expired.  If it appears that
+miimon is very slow in detecting link failures, try specifying
+use_carrier=0 to see if that improves the failure detection time.  If
+it does, then it may be that the driver checks the carrier state at a
+fixed interval, but does not cache the MII register values (so the
+use_carrier=0 method of querying the registers directly works).  If
+use_carrier=0 does not improve the failover, then the driver may cache
+the registers, or the problem may be elsewhere.
+
+Also, remember that miimon only checks for the device's
+carrier state.  It has no way to determine the state of devices on or
+beyond other ports of a switch, or if a switch is refusing to pass
+traffic while still maintaining carrier on.
+
+9. SNMP agents
+===============
+
+If running SNMP agents, the bonding driver should be loaded
+before any network drivers participating in a bond.  This requirement
+is due to the interface index (ipAdEntIfIndex) being associated to
+the first interface found with a given IP address.  That is, there is
+only one ipAdEntIfIndex for each IP address.  For example, if eth0 and
+eth1 are slaves of bond0 and the driver for eth0 is loaded before the
+bonding driver, the interface for the IP address will be associated
+with the eth0 interface.  This configuration is shown below, the IP
+address 192.168.1.1 has an interface index of 2 which indexes to eth0
+in the ifDescr table (ifDescr.2).
+
+::
+
+     interfaces.ifTable.ifEntry.ifDescr.1 = lo
+     interfaces.ifTable.ifEntry.ifDescr.2 = eth0
+     interfaces.ifTable.ifEntry.ifDescr.3 = eth1
+     interfaces.ifTable.ifEntry.ifDescr.4 = eth2
+     interfaces.ifTable.ifEntry.ifDescr.5 = eth3
+     interfaces.ifTable.ifEntry.ifDescr.6 = bond0
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.10.10.10 = 5
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.192.168.1.1 = 2
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.74.20.94 = 4
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.127.0.0.1 = 1
+
+This problem is avoided by loading the bonding driver before
+any network drivers participating in a bond.  Below is an example of
+loading the bonding driver first, the IP address 192.168.1.1 is
+correctly associated with ifDescr.2.
+
+     interfaces.ifTable.ifEntry.ifDescr.1 = lo
+     interfaces.ifTable.ifEntry.ifDescr.2 = bond0
+     interfaces.ifTable.ifEntry.ifDescr.3 = eth0
+     interfaces.ifTable.ifEntry.ifDescr.4 = eth1
+     interfaces.ifTable.ifEntry.ifDescr.5 = eth2
+     interfaces.ifTable.ifEntry.ifDescr.6 = eth3
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.10.10.10 = 6
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.192.168.1.1 = 2
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.74.20.94 = 5
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.127.0.0.1 = 1
+
+While some distributions may not report the interface name in
+ifDescr, the association between the IP address and IfIndex remains
+and SNMP functions such as Interface_Scan_Next will report that
+association.
+
+10. Promiscuous mode
+====================
+
+When running network monitoring tools, e.g., tcpdump, it is
+common to enable promiscuous mode on the device, so that all traffic
+is seen (instead of seeing only traffic destined for the local host).
+The bonding driver handles promiscuous mode changes to the bonding
+master device (e.g., bond0), and propagates the setting to the slave
+devices.
+
+For the balance-rr, balance-xor, broadcast, and 802.3ad modes,
+the promiscuous mode setting is propagated to all slaves.
+
+For the active-backup, balance-tlb and balance-alb modes, the
+promiscuous mode setting is propagated only to the active slave.
+
+For balance-tlb mode, the active slave is the slave currently
+receiving inbound traffic.
+
+For balance-alb mode, the active slave is the slave used as a
+"primary."  This slave is used for mode-specific control traffic, for
+sending to peers that are unassigned or if the load is unbalanced.
+
+For the active-backup, balance-tlb and balance-alb modes, when
+the active slave changes (e.g., due to a link failure), the
+promiscuous setting will be propagated to the new active slave.
+
+11. Configuring Bonding for High Availability
+=============================================
+
+High Availability refers to configurations that provide
+maximum network availability by having redundant or backup devices,
+links or switches between the host and the rest of the world.  The
+goal is to provide the maximum availability of network connectivity
+(i.e., the network always works), even though other configurations
+could provide higher throughput.
+
+11.1 High Availability in a Single Switch Topology
+--------------------------------------------------
+
+If two hosts (or a host and a single switch) are directly
+connected via multiple physical links, then there is no availability
+penalty to optimizing for maximum bandwidth.  In this case, there is
+only one switch (or peer), so if it fails, there is no alternative
+access to fail over to.  Additionally, the bonding load balance modes
+support link monitoring of their members, so if individual links fail,
+the load will be rebalanced across the remaining devices.
+
+See Section 12, "Configuring Bonding for Maximum Throughput"
+for information on configuring bonding with one peer device.
+
+11.2 High Availability in a Multiple Switch Topology
+----------------------------------------------------
+
+With multiple switches, the configuration of bonding and the
+network changes dramatically.  In multiple switch topologies, there is
+a trade off between network availability and usable bandwidth.
+
+Below is a sample network, configured to maximize the
+availability of the network::
+
+               |                                     |
+               |port3                           port3|
+         +-----+----+                          +-----+----+
+         |          |port2       ISL      port2|          |
+         | switch A +--------------------------+ switch B |
+         |          |                          |          |
+         +-----+----+                          +-----++---+
+               |port1                           port1|
+               |             +-------+               |
+               +-------------+ host1 +---------------+
+                        eth0 +-------+ eth1
+
+In this configuration, there is a link between the two
+switches (ISL, or inter switch link), and multiple ports connecting to
+the outside world ("port3" on each switch).  There is no technical
+reason that this could not be extended to a third switch.
+
+11.2.1 HA Bonding Mode Selection for Multiple Switch Topology
+-------------------------------------------------------------
+
+In a topology such as the example above, the active-backup and
+broadcast modes are the only useful bonding modes when optimizing for
+availability; the other modes require all links to terminate on the
+same peer for them to behave rationally.
+
+active-backup:
+       This is generally the preferred mode, particularly if
+       the switches have an ISL and play together well.  If the
+       network configuration is such that one switch is specifically
+       a backup switch (e.g., has lower capacity, higher cost, etc),
+       then the primary option can be used to insure that the
+       preferred link is always used when it is available.
+
+broadcast:
+       This mode is really a special purpose mode, and is suitable
+       only for very specific needs.  For example, if the two
+       switches are not connected (no ISL), and the networks beyond
+       them are totally independent.  In this case, if it is
+       necessary for some specific one-way traffic to reach both
+       independent networks, then the broadcast mode may be suitable.
+
+11.2.2 HA Link Monitoring Selection for Multiple Switch Topology
+----------------------------------------------------------------
+
+The choice of link monitoring ultimately depends upon your
+switch.  If the switch can reliably fail ports in response to other
+failures, then either the MII or ARP monitors should work.  For
+example, in the above example, if the "port3" link fails at the remote
+end, the MII monitor has no direct means to detect this.  The ARP
+monitor could be configured with a target at the remote end of port3,
+thus detecting that failure without switch support.
+
+In general, however, in a multiple switch topology, the ARP
+monitor can provide a higher level of reliability in detecting end to
+end connectivity failures (which may be caused by the failure of any
+individual component to pass traffic for any reason).  Additionally,
+the ARP monitor should be configured with multiple targets (at least
+one for each switch in the network).  This will insure that,
+regardless of which switch is active, the ARP monitor has a suitable
+target to query.
+
+Note, also, that of late many switches now support a functionality
+generally referred to as "trunk failover."  This is a feature of the
+switch that causes the link state of a particular switch port to be set
+down (or up) when the state of another switch port goes down (or up).
+Its purpose is to propagate link failures from logically "exterior" ports
+to the logically "interior" ports that bonding is able to monitor via
+miimon.  Availability and configuration for trunk failover varies by
+switch, but this can be a viable alternative to the ARP monitor when using
+suitable switches.
+
+12. Configuring Bonding for Maximum Throughput
+==============================================
+
+12.1 Maximizing Throughput in a Single Switch Topology
+------------------------------------------------------
+
+In a single switch configuration, the best method to maximize
+throughput depends upon the application and network environment.  The
+various load balancing modes each have strengths and weaknesses in
+different environments, as detailed below.
+
+For this discussion, we will break down the topologies into
+two categories.  Depending upon the destination of most traffic, we
+categorize them into either "gatewayed" or "local" configurations.
+
+In a gatewayed configuration, the "switch" is acting primarily
+as a router, and the majority of traffic passes through this router to
+other networks.  An example would be the following::
+
+
+     +----------+                     +----------+
+     |          |eth0            port1|          | to other networks
+     | Host A   +---------------------+ router   +------------------->
+     |          +---------------------+          | Hosts B and C are out
+     |          |eth1            port2|          | here somewhere
+     +----------+                     +----------+
+
+The router may be a dedicated router device, or another host
+acting as a gateway.  For our discussion, the important point is that
+the majority of traffic from Host A will pass through the router to
+some other network before reaching its final destination.
+
+In a gatewayed network configuration, although Host A may
+communicate with many other systems, all of its traffic will be sent
+and received via one other peer on the local network, the router.
+
+Note that the case of two systems connected directly via
+multiple physical links is, for purposes of configuring bonding, the
+same as a gatewayed configuration.  In that case, it happens that all
+traffic is destined for the "gateway" itself, not some other network
+beyond the gateway.
+
+In a local configuration, the "switch" is acting primarily as
+a switch, and the majority of traffic passes through this switch to
+reach other stations on the same network.  An example would be the
+following::
+
+    +----------+            +----------+       +--------+
+    |          |eth0   port1|          +-------+ Host B |
+    |  Host A  +------------+  switch  |port3  +--------+
+    |          +------------+          |                  +--------+
+    |          |eth1   port2|          +------------------+ Host C |
+    +----------+            +----------+port4             +--------+
+
+
+Again, the switch may be a dedicated switch device, or another
+host acting as a gateway.  For our discussion, the important point is
+that the majority of traffic from Host A is destined for other hosts
+on the same local network (Hosts B and C in the above example).
+
+In summary, in a gatewayed configuration, traffic to and from
+the bonded device will be to the same MAC level peer on the network
+(the gateway itself, i.e., the router), regardless of its final
+destination.  In a local configuration, traffic flows directly to and
+from the final destinations, thus, each destination (Host B, Host C)
+will be addressed directly by their individual MAC addresses.
+
+This distinction between a gatewayed and a local network
+configuration is important because many of the load balancing modes
+available use the MAC addresses of the local network source and
+destination to make load balancing decisions.  The behavior of each
+mode is described below.
+
+
+12.1.1 MT Bonding Mode Selection for Single Switch Topology
+-----------------------------------------------------------
+
+This configuration is the easiest to set up and to understand,
+although you will have to decide which bonding mode best suits your
+needs.  The trade offs for each mode are detailed below:
+
+balance-rr:
+       This mode is the only mode that will permit a single
+       TCP/IP connection to stripe traffic across multiple
+       interfaces. It is therefore the only mode that will allow a
+       single TCP/IP stream to utilize more than one interface's
+       worth of throughput.  This comes at a cost, however: the
+       striping generally results in peer systems receiving packets out
+       of order, causing TCP/IP's congestion control system to kick
+       in, often by retransmitting segments.
+
+       It is possible to adjust TCP/IP's congestion limits by
+       altering the net.ipv4.tcp_reordering sysctl parameter.  The
+       usual default value is 3. But keep in mind TCP stack is able
+       to automatically increase this when it detects reorders.
+
+       Note that the fraction of packets that will be delivered out of
+       order is highly variable, and is unlikely to be zero.  The level
+       of reordering depends upon a variety of factors, including the
+       networking interfaces, the switch, and the topology of the
+       configuration.  Speaking in general terms, higher speed network
+       cards produce more reordering (due to factors such as packet
+       coalescing), and a "many to many" topology will reorder at a
+       higher rate than a "many slow to one fast" configuration.
+
+       Many switches do not support any modes that stripe traffic
+       (instead choosing a port based upon IP or MAC level addresses);
+       for those devices, traffic for a particular connection flowing
+       through the switch to a balance-rr bond will not utilize greater
+       than one interface's worth of bandwidth.
+
+       If you are utilizing protocols other than TCP/IP, UDP for
+       example, and your application can tolerate out of order
+       delivery, then this mode can allow for single stream datagram
+       performance that scales near linearly as interfaces are added
+       to the bond.
+
+       This mode requires the switch to have the appropriate ports
+       configured for "etherchannel" or "trunking."
+
+active-backup:
+       There is not much advantage in this network topology to
+       the active-backup mode, as the inactive backup devices are all
+       connected to the same peer as the primary.  In this case, a
+       load balancing mode (with link monitoring) will provide the
+       same level of network availability, but with increased
+       available bandwidth.  On the plus side, active-backup mode
+       does not require any configuration of the switch, so it may
+       have value if the hardware available does not support any of
+       the load balance modes.
+
+balance-xor:
+       This mode will limit traffic such that packets destined
+       for specific peers will always be sent over the same
+       interface.  Since the destination is determined by the MAC
+       addresses involved, this mode works best in a "local" network
+       configuration (as described above), with destinations all on
+       the same local network.  This mode is likely to be suboptimal
+       if all your traffic is passed through a single router (i.e., a
+       "gatewayed" network configuration, as described above).
+
+       As with balance-rr, the switch ports need to be configured for
+       "etherchannel" or "trunking."
+
+broadcast:
+       Like active-backup, there is not much advantage to this
+       mode in this type of network topology.
+
+802.3ad:
+       This mode can be a good choice for this type of network
+       topology.  The 802.3ad mode is an IEEE standard, so all peers
+       that implement 802.3ad should interoperate well.  The 802.3ad
+       protocol includes automatic configuration of the aggregates,
+       so minimal manual configuration of the switch is needed
+       (typically only to designate that some set of devices is
+       available for 802.3ad).  The 802.3ad standard also mandates
+       that frames be delivered in order (within certain limits), so
+       in general single connections will not see misordering of
+       packets.  The 802.3ad mode does have some drawbacks: the
+       standard mandates that all devices in the aggregate operate at
+       the same speed and duplex.  Also, as with all bonding load
+       balance modes other than balance-rr, no single connection will
+       be able to utilize more than a single interface's worth of
+       bandwidth.
+
+       Additionally, the linux bonding 802.3ad implementation
+       distributes traffic by peer (using an XOR of MAC addresses
+       and packet type ID), so in a "gatewayed" configuration, all
+       outgoing traffic will generally use the same device.  Incoming
+       traffic may also end up on a single device, but that is
+       dependent upon the balancing policy of the peer's 802.3ad
+       implementation.  In a "local" configuration, traffic will be
+       distributed across the devices in the bond.
+
+       Finally, the 802.3ad mode mandates the use of the MII monitor,
+       therefore, the ARP monitor is not available in this mode.
+
+balance-tlb:
+       The balance-tlb mode balances outgoing traffic by peer.
+       Since the balancing is done according to MAC address, in a
+       "gatewayed" configuration (as described above), this mode will
+       send all traffic across a single device.  However, in a
+       "local" network configuration, this mode balances multiple
+       local network peers across devices in a vaguely intelligent
+       manner (not a simple XOR as in balance-xor or 802.3ad mode),
+       so that mathematically unlucky MAC addresses (i.e., ones that
+       XOR to the same value) will not all "bunch up" on a single
+       interface.
+
+       Unlike 802.3ad, interfaces may be of differing speeds, and no
+       special switch configuration is required.  On the down side,
+       in this mode all incoming traffic arrives over a single
+       interface, this mode requires certain ethtool support in the
+       network device driver of the slave interfaces, and the ARP
+       monitor is not available.
+
+balance-alb:
+       This mode is everything that balance-tlb is, and more.
+       It has all of the features (and restrictions) of balance-tlb,
+       and will also balance incoming traffic from local network
+       peers (as described in the Bonding Module Options section,
+       above).
+
+       The only additional down side to this mode is that the network
+       device driver must support changing the hardware address while
+       the device is open.
+
+12.1.2 MT Link Monitoring for Single Switch Topology
+----------------------------------------------------
+
+The choice of link monitoring may largely depend upon which
+mode you choose to use.  The more advanced load balancing modes do not
+support the use of the ARP monitor, and are thus restricted to using
+the MII monitor (which does not provide as high a level of end to end
+assurance as the ARP monitor).
+
+12.2 Maximum Throughput in a Multiple Switch Topology
+-----------------------------------------------------
+
+Multiple switches may be utilized to optimize for throughput
+when they are configured in parallel as part of an isolated network
+between two or more systems, for example::
+
+                      +-----------+
+                      |  Host A   |
+                      +-+---+---+-+
+                        |   |   |
+               +--------+   |   +---------+
+               |            |             |
+        +------+---+  +-----+----+  +-----+----+
+        | Switch A |  | Switch B |  | Switch C |
+        +------+---+  +-----+----+  +-----+----+
+               |            |             |
+               +--------+   |   +---------+
+                        |   |   |
+                      +-+---+---+-+
+                      |  Host B   |
+                      +-----------+
+
+In this configuration, the switches are isolated from one
+another.  One reason to employ a topology such as this is for an
+isolated network with many hosts (a cluster configured for high
+performance, for example), using multiple smaller switches can be more
+cost effective than a single larger switch, e.g., on a network with 24
+hosts, three 24 port switches can be significantly less expensive than
+a single 72 port switch.
+
+If access beyond the network is required, an individual host
+can be equipped with an additional network device connected to an
+external network; this host then additionally acts as a gateway.
+
+12.2.1 MT Bonding Mode Selection for Multiple Switch Topology
+-------------------------------------------------------------
+
+In actual practice, the bonding mode typically employed in
+configurations of this type is balance-rr.  Historically, in this
+network configuration, the usual caveats about out of order packet
+delivery are mitigated by the use of network adapters that do not do
+any kind of packet coalescing (via the use of NAPI, or because the
+device itself does not generate interrupts until some number of
+packets has arrived).  When employed in this fashion, the balance-rr
+mode allows individual connections between two hosts to effectively
+utilize greater than one interface's bandwidth.
+
+12.2.2 MT Link Monitoring for Multiple Switch Topology
+------------------------------------------------------
+
+Again, in actual practice, the MII monitor is most often used
+in this configuration, as performance is given preference over
+availability.  The ARP monitor will function in this topology, but its
+advantages over the MII monitor are mitigated by the volume of probes
+needed as the number of systems involved grows (remember that each
+host in the network is configured with bonding).
+
+13. Switch Behavior Issues
+==========================
+
+13.1 Link Establishment and Failover Delays
+-------------------------------------------
+
+Some switches exhibit undesirable behavior with regard to the
+timing of link up and down reporting by the switch.
+
+First, when a link comes up, some switches may indicate that
+the link is up (carrier available), but not pass traffic over the
+interface for some period of time.  This delay is typically due to
+some type of autonegotiation or routing protocol, but may also occur
+during switch initialization (e.g., during recovery after a switch
+failure).  If you find this to be a problem, specify an appropriate
+value to the updelay bonding module option to delay the use of the
+relevant interface(s).
+
+Second, some switches may "bounce" the link state one or more
+times while a link is changing state.  This occurs most commonly while
+the switch is initializing.  Again, an appropriate updelay value may
+help.
+
+Note that when a bonding interface has no active links, the
+driver will immediately reuse the first link that goes up, even if the
+updelay parameter has been specified (the updelay is ignored in this
+case).  If there are slave interfaces waiting for the updelay timeout
+to expire, the interface that first went into that state will be
+immediately reused.  This reduces down time of the network if the
+value of updelay has been overestimated, and since this occurs only in
+cases with no connectivity, there is no additional penalty for
+ignoring the updelay.
+
+In addition to the concerns about switch timings, if your
+switches take a long time to go into backup mode, it may be desirable
+to not activate a backup interface immediately after a link goes down.
+Failover may be delayed via the downdelay bonding module option.
+
+13.2 Duplicated Incoming Packets
+--------------------------------
+
+NOTE: Starting with version 3.0.2, the bonding driver has logic to
+suppress duplicate packets, which should largely eliminate this problem.
+The following description is kept for reference.
+
+It is not uncommon to observe a short burst of duplicated
+traffic when the bonding device is first used, or after it has been
+idle for some period of time.  This is most easily observed by issuing
+a "ping" to some other host on the network, and noticing that the
+output from ping flags duplicates (typically one per slave).
+
+For example, on a bond in active-backup mode with five slaves
+all connected to one switch, the output may appear as follows::
+
+       # ping -n 10.0.4.2
+       PING 10.0.4.2 (10.0.4.2) from 10.0.3.10 : 56(84) bytes of data.
+       64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.7 ms
+       64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.8 ms (DUP!)
+       64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.8 ms (DUP!)
+       64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.8 ms (DUP!)
+       64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.8 ms (DUP!)
+       64 bytes from 10.0.4.2: icmp_seq=2 ttl=64 time=0.216 ms
+       64 bytes from 10.0.4.2: icmp_seq=3 ttl=64 time=0.267 ms
+       64 bytes from 10.0.4.2: icmp_seq=4 ttl=64 time=0.222 ms
+
+This is not due to an error in the bonding driver, rather, it
+is a side effect of how many switches update their MAC forwarding
+tables.  Initially, the switch does not associate the MAC address in
+the packet with a particular switch port, and so it may send the
+traffic to all ports until its MAC forwarding table is updated.  Since
+the interfaces attached to the bond may occupy multiple ports on a
+single switch, when the switch (temporarily) floods the traffic to all
+ports, the bond device receives multiple copies of the same packet
+(one per slave device).
+
+The duplicated packet behavior is switch dependent, some
+switches exhibit this, and some do not.  On switches that display this
+behavior, it can be induced by clearing the MAC forwarding table (on
+most Cisco switches, the privileged command "clear mac address-table
+dynamic" will accomplish this).
+
+14. Hardware Specific Considerations
+====================================
+
+This section contains additional information for configuring
+bonding on specific hardware platforms, or for interfacing bonding
+with particular switches or other devices.
+
+14.1 IBM BladeCenter
+--------------------
+
+This applies to the JS20 and similar systems.
+
+On the JS20 blades, the bonding driver supports only
+balance-rr, active-backup, balance-tlb and balance-alb modes.  This is
+largely due to the network topology inside the BladeCenter, detailed
+below.
+
+JS20 network adapter information
+--------------------------------
+
+All JS20s come with two Broadcom Gigabit Ethernet ports
+integrated on the planar (that's "motherboard" in IBM-speak).  In the
+BladeCenter chassis, the eth0 port of all JS20 blades is hard wired to
+I/O Module #1; similarly, all eth1 ports are wired to I/O Module #2.
+An add-on Broadcom daughter card can be installed on a JS20 to provide
+two more Gigabit Ethernet ports.  These ports, eth2 and eth3, are
+wired to I/O Modules 3 and 4, respectively.
+
+Each I/O Module may contain either a switch or a passthrough
+module (which allows ports to be directly connected to an external
+switch).  Some bonding modes require a specific BladeCenter internal
+network topology in order to function; these are detailed below.
+
+Additional BladeCenter-specific networking information can be
+found in two IBM Redbooks (www.ibm.com/redbooks):
+
+- "IBM eServer BladeCenter Networking Options"
+- "IBM eServer BladeCenter Layer 2-7 Network Switching"
+
+BladeCenter networking configuration
+------------------------------------
+
+Because a BladeCenter can be configured in a very large number
+of ways, this discussion will be confined to describing basic
+configurations.
+
+Normally, Ethernet Switch Modules (ESMs) are used in I/O
+modules 1 and 2.  In this configuration, the eth0 and eth1 ports of a
+JS20 will be connected to different internal switches (in the
+respective I/O modules).
+
+A passthrough module (OPM or CPM, optical or copper,
+passthrough module) connects the I/O module directly to an external
+switch.  By using PMs in I/O module #1 and #2, the eth0 and eth1
+interfaces of a JS20 can be redirected to the outside world and
+connected to a common external switch.
+
+Depending upon the mix of ESMs and PMs, the network will
+appear to bonding as either a single switch topology (all PMs) or as a
+multiple switch topology (one or more ESMs, zero or more PMs).  It is
+also possible to connect ESMs together, resulting in a configuration
+much like the example in "High Availability in a Multiple Switch
+Topology," above.
+
+Requirements for specific modes
+-------------------------------
+
+The balance-rr mode requires the use of passthrough modules
+for devices in the bond, all connected to an common external switch.
+That switch must be configured for "etherchannel" or "trunking" on the
+appropriate ports, as is usual for balance-rr.
+
+The balance-alb and balance-tlb modes will function with
+either switch modules or passthrough modules (or a mix).  The only
+specific requirement for these modes is that all network interfaces
+must be able to reach all destinations for traffic sent over the
+bonding device (i.e., the network must converge at some point outside
+the BladeCenter).
+
+The active-backup mode has no additional requirements.
+
+Link monitoring issues
+----------------------
+
+When an Ethernet Switch Module is in place, only the ARP
+monitor will reliably detect link loss to an external switch.  This is
+nothing unusual, but examination of the BladeCenter cabinet would
+suggest that the "external" network ports are the ethernet ports for
+the system, when it fact there is a switch between these "external"
+ports and the devices on the JS20 system itself.  The MII monitor is
+only able to detect link failures between the ESM and the JS20 system.
+
+When a passthrough module is in place, the MII monitor does
+detect failures to the "external" port, which is then directly
+connected to the JS20 system.
+
+Other concerns
+--------------
+
+The Serial Over LAN (SoL) link is established over the primary
+ethernet (eth0) only, therefore, any loss of link to eth0 will result
+in losing your SoL connection.  It will not fail over with other
+network traffic, as the SoL system is beyond the control of the
+bonding driver.
+
+It may be desirable to disable spanning tree on the switch
+(either the internal Ethernet Switch Module, or an external switch) to
+avoid fail-over delay issues when using bonding.
+
+
+15. Frequently Asked Questions
+==============================
+
+1.  Is it SMP safe?
+-------------------
+
+Yes. The old 2.0.xx channel bonding patch was not SMP safe.
+The new driver was designed to be SMP safe from the start.
+
+2.  What type of cards will work with it?
+-----------------------------------------
+
+Any Ethernet type cards (you can even mix cards - a Intel
+EtherExpress PRO/100 and a 3com 3c905b, for example).  For most modes,
+devices need not be of the same speed.
+
+Starting with version 3.2.1, bonding also supports Infiniband
+slaves in active-backup mode.
+
+3.  How many bonding devices can I have?
+----------------------------------------
+
+There is no limit.
+
+4.  How many slaves can a bonding device have?
+----------------------------------------------
+
+This is limited only by the number of network interfaces Linux
+supports and/or the number of network cards you can place in your
+system.
+
+5.  What happens when a slave link dies?
+----------------------------------------
+
+If link monitoring is enabled, then the failing device will be
+disabled.  The active-backup mode will fail over to a backup link, and
+other modes will ignore the failed link.  The link will continue to be
+monitored, and should it recover, it will rejoin the bond (in whatever
+manner is appropriate for the mode). See the sections on High
+Availability and the documentation for each mode for additional
+information.
+
+Link monitoring can be enabled via either the miimon or
+arp_interval parameters (described in the module parameters section,
+above).  In general, miimon monitors the carrier state as sensed by
+the underlying network device, and the arp monitor (arp_interval)
+monitors connectivity to another host on the local network.
+
+If no link monitoring is configured, the bonding driver will
+be unable to detect link failures, and will assume that all links are
+always available.  This will likely result in lost packets, and a
+resulting degradation of performance.  The precise performance loss
+depends upon the bonding mode and network configuration.
+
+6.  Can bonding be used for High Availability?
+----------------------------------------------
+
+Yes.  See the section on High Availability for details.
+
+7.  Which switches/systems does it work with?
+---------------------------------------------
+
+The full answer to this depends upon the desired mode.
+
+In the basic balance modes (balance-rr and balance-xor), it
+works with any system that supports etherchannel (also called
+trunking).  Most managed switches currently available have such
+support, and many unmanaged switches as well.
+
+The advanced balance modes (balance-tlb and balance-alb) do
+not have special switch requirements, but do need device drivers that
+support specific features (described in the appropriate section under
+module parameters, above).
+
+In 802.3ad mode, it works with systems that support IEEE
+802.3ad Dynamic Link Aggregation.  Most managed and many unmanaged
+switches currently available support 802.3ad.
+
+The active-backup mode should work with any Layer-II switch.
+
+8.  Where does a bonding device get its MAC address from?
+---------------------------------------------------------
+
+When using slave devices that have fixed MAC addresses, or when
+the fail_over_mac option is enabled, the bonding device's MAC address is
+the MAC address of the active slave.
+
+For other configurations, if not explicitly configured (with
+ifconfig or ip link), the MAC address of the bonding device is taken from
+its first slave device.  This MAC address is then passed to all following
+slaves and remains persistent (even if the first slave is removed) until
+the bonding device is brought down or reconfigured.
+
+If you wish to change the MAC address, you can set it with
+ifconfig or ip link::
+
+       # ifconfig bond0 hw ether 00:11:22:33:44:55
+
+       # ip link set bond0 address 66:77:88:99:aa:bb
+
+The MAC address can be also changed by bringing down/up the
+device and then changing its slaves (or their order)::
+
+       # ifconfig bond0 down ; modprobe -r bonding
+       # ifconfig bond0 .... up
+       # ifenslave bond0 eth...
+
+This method will automatically take the address from the next
+slave that is added.
+
+To restore your slaves' MAC addresses, you need to detach them
+from the bond (``ifenslave -d bond0 eth0``). The bonding driver will
+then restore the MAC addresses that the slaves had before they were
+enslaved.
+
+16. Resources and Links
+=======================
+
+The latest version of the bonding driver can be found in the latest
+version of the linux kernel, found on http://kernel.org
+
+The latest version of this document can be found in the latest kernel
+source (named Documentation/networking/bonding.rst).
+
+Discussions regarding the usage of the bonding driver take place on the
+bonding-devel mailing list, hosted at sourceforge.net. If you have questions or
+problems, post them to the list.  The list address is:
+
+bonding-devel@lists.sourceforge.net
+
+The administrative interface (to subscribe or unsubscribe) can
+be found at:
+
+https://lists.sourceforge.net/lists/listinfo/bonding-devel
+
+Discussions regarding the development of the bonding driver take place
+on the main Linux network mailing list, hosted at vger.kernel.org. The list
+address is:
+
+netdev@vger.kernel.org
+
+The administrative interface (to subscribe or unsubscribe) can
+be found at:
+
+http://vger.kernel.org/vger-lists.html#netdev
+
+Donald Becker's Ethernet Drivers and diag programs may be found at :
+
+ - http://web.archive.org/web/%2E/http://www.scyld.com/network/
+
+You will also find a lot of information regarding Ethernet, NWay, MII,
+etc. at www.scyld.com.
diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt
deleted file mode 100644 (file)
index e3abfbd..0000000
+++ /dev/null
@@ -1,2837 +0,0 @@
-
-               Linux Ethernet Bonding Driver HOWTO
-
-               Latest update: 27 April 2011
-
-Initial release : Thomas Davis <tadavis at lbl.gov>
-Corrections, HA extensions : 2000/10/03-15 :
-  - Willy Tarreau <willy at meta-x.org>
-  - Constantine Gavrilov <const-g at xpert.com>
-  - Chad N. Tindel <ctindel at ieee dot org>
-  - Janice Girouard <girouard at us dot ibm dot com>
-  - Jay Vosburgh <fubar at us dot ibm dot com>
-
-Reorganized and updated Feb 2005 by Jay Vosburgh
-Added Sysfs information: 2006/04/24
-  - Mitch Williams <mitch.a.williams at intel.com>
-
-Introduction
-============
-
-       The Linux bonding driver provides a method for aggregating
-multiple network interfaces into a single logical "bonded" interface.
-The behavior of the bonded interfaces depends upon the mode; generally
-speaking, modes provide either hot standby or load balancing services.
-Additionally, link integrity monitoring may be performed.
-       
-       The bonding driver originally came from Donald Becker's
-beowulf patches for kernel 2.0. It has changed quite a bit since, and
-the original tools from extreme-linux and beowulf sites will not work
-with this version of the driver.
-
-       For new versions of the driver, updated userspace tools, and
-who to ask for help, please follow the links at the end of this file.
-
-Table of Contents
-=================
-
-1. Bonding Driver Installation
-
-2. Bonding Driver Options
-
-3. Configuring Bonding Devices
-3.1    Configuration with Sysconfig Support
-3.1.1          Using DHCP with Sysconfig
-3.1.2          Configuring Multiple Bonds with Sysconfig
-3.2    Configuration with Initscripts Support
-3.2.1          Using DHCP with Initscripts
-3.2.2          Configuring Multiple Bonds with Initscripts
-3.3    Configuring Bonding Manually with Ifenslave
-3.3.1          Configuring Multiple Bonds Manually
-3.4    Configuring Bonding Manually via Sysfs
-3.5    Configuration with Interfaces Support
-3.6    Overriding Configuration for Special Cases
-3.7 Configuring LACP for 802.3ad mode in a more secure way
-
-4. Querying Bonding Configuration
-4.1    Bonding Configuration
-4.2    Network Configuration
-
-5. Switch Configuration
-
-6. 802.1q VLAN Support
-
-7. Link Monitoring
-7.1    ARP Monitor Operation
-7.2    Configuring Multiple ARP Targets
-7.3    MII Monitor Operation
-
-8. Potential Trouble Sources
-8.1    Adventures in Routing
-8.2    Ethernet Device Renaming
-8.3    Painfully Slow Or No Failed Link Detection By Miimon
-
-9. SNMP agents
-
-10. Promiscuous mode
-
-11. Configuring Bonding for High Availability
-11.1   High Availability in a Single Switch Topology
-11.2   High Availability in a Multiple Switch Topology
-11.2.1         HA Bonding Mode Selection for Multiple Switch Topology
-11.2.2         HA Link Monitoring for Multiple Switch Topology
-
-12. Configuring Bonding for Maximum Throughput
-12.1   Maximum Throughput in a Single Switch Topology
-12.1.1         MT Bonding Mode Selection for Single Switch Topology
-12.1.2         MT Link Monitoring for Single Switch Topology
-12.2   Maximum Throughput in a Multiple Switch Topology
-12.2.1         MT Bonding Mode Selection for Multiple Switch Topology
-12.2.2         MT Link Monitoring for Multiple Switch Topology
-
-13. Switch Behavior Issues
-13.1   Link Establishment and Failover Delays
-13.2   Duplicated Incoming Packets
-
-14. Hardware Specific Considerations
-14.1   IBM BladeCenter
-
-15. Frequently Asked Questions
-
-16. Resources and Links
-
-
-1. Bonding Driver Installation
-==============================
-
-       Most popular distro kernels ship with the bonding driver
-already available as a module. If your distro does not, or you
-have need to compile bonding from source (e.g., configuring and
-installing a mainline kernel from kernel.org), you'll need to perform
-the following steps:
-
-1.1 Configure and build the kernel with bonding
------------------------------------------------
-
-       The current version of the bonding driver is available in the
-drivers/net/bonding subdirectory of the most recent kernel source
-(which is available on http://kernel.org).  Most users "rolling their
-own" will want to use the most recent kernel from kernel.org.
-
-       Configure kernel with "make menuconfig" (or "make xconfig" or
-"make config"), then select "Bonding driver support" in the "Network
-device support" section.  It is recommended that you configure the
-driver as module since it is currently the only way to pass parameters
-to the driver or configure more than one bonding device.
-
-       Build and install the new kernel and modules.
-
-1.2 Bonding Control Utility
--------------------------------------
-
-        It is recommended to configure bonding via iproute2 (netlink)
-or sysfs, the old ifenslave control utility is obsolete.
-
-2. Bonding Driver Options
-=========================
-
-       Options for the bonding driver are supplied as parameters to the
-bonding module at load time, or are specified via sysfs.
-
-       Module options may be given as command line arguments to the
-insmod or modprobe command, but are usually specified in either the
-/etc/modprobe.d/*.conf configuration files, or in a distro-specific
-configuration file (some of which are detailed in the next section).
-
-       Details on bonding support for sysfs is provided in the
-"Configuring Bonding Manually via Sysfs" section, below.
-
-       The available bonding driver parameters are listed below. If a
-parameter is not specified the default value is used.  When initially
-configuring a bond, it is recommended "tail -f /var/log/messages" be
-run in a separate window to watch for bonding driver error messages.
-
-       It is critical that either the miimon or arp_interval and
-arp_ip_target parameters be specified, otherwise serious network
-degradation will occur during link failures.  Very few devices do not
-support at least miimon, so there is really no reason not to use it.
-
-       Options with textual values will accept either the text name
-or, for backwards compatibility, the option value.  E.g.,
-"mode=802.3ad" and "mode=4" set the same mode.
-
-       The parameters are as follows:
-
-active_slave
-
-       Specifies the new active slave for modes that support it
-       (active-backup, balance-alb and balance-tlb).  Possible values
-       are the name of any currently enslaved interface, or an empty
-       string.  If a name is given, the slave and its link must be up in order
-       to be selected as the new active slave.  If an empty string is
-       specified, the current active slave is cleared, and a new active
-       slave is selected automatically.
-
-       Note that this is only available through the sysfs interface. No module
-       parameter by this name exists.
-
-       The normal value of this option is the name of the currently
-       active slave, or the empty string if there is no active slave or
-       the current mode does not use an active slave.
-
-ad_actor_sys_prio
-
-       In an AD system, this specifies the system priority. The allowed range
-       is 1 - 65535. If the value is not specified, it takes 65535 as the
-       default value.
-
-       This parameter has effect only in 802.3ad mode and is available through
-       SysFs interface.
-
-ad_actor_system
-
-       In an AD system, this specifies the mac-address for the actor in
-       protocol packet exchanges (LACPDUs). The value cannot be NULL or
-       multicast. It is preferred to have the local-admin bit set for this
-       mac but driver does not enforce it. If the value is not given then
-       system defaults to using the masters' mac address as actors' system
-       address.
-
-       This parameter has effect only in 802.3ad mode and is available through
-       SysFs interface.
-
-ad_select
-
-       Specifies the 802.3ad aggregation selection logic to use.  The
-       possible values and their effects are:
-
-       stable or 0
-
-               The active aggregator is chosen by largest aggregate
-               bandwidth.
-
-               Reselection of the active aggregator occurs only when all
-               slaves of the active aggregator are down or the active
-               aggregator has no slaves.
-
-               This is the default value.
-
-       bandwidth or 1
-
-               The active aggregator is chosen by largest aggregate
-               bandwidth.  Reselection occurs if:
-
-               - A slave is added to or removed from the bond
-
-               - Any slave's link state changes
-
-               - Any slave's 802.3ad association state changes
-
-               - The bond's administrative state changes to up
-
-       count or 2
-
-               The active aggregator is chosen by the largest number of
-               ports (slaves).  Reselection occurs as described under the
-               "bandwidth" setting, above.
-
-       The bandwidth and count selection policies permit failover of
-       802.3ad aggregations when partial failure of the active aggregator
-       occurs.  This keeps the aggregator with the highest availability
-       (either in bandwidth or in number of ports) active at all times.
-
-       This option was added in bonding version 3.4.0.
-
-ad_user_port_key
-
-       In an AD system, the port-key has three parts as shown below -
-
-          Bits   Use
-          00     Duplex
-          01-05  Speed
-          06-15  User-defined
-
-       This defines the upper 10 bits of the port key. The values can be
-       from 0 - 1023. If not given, the system defaults to 0.
-
-       This parameter has effect only in 802.3ad mode and is available through
-       SysFs interface.
-
-all_slaves_active
-
-       Specifies that duplicate frames (received on inactive ports) should be
-       dropped (0) or delivered (1).
-
-       Normally, bonding will drop duplicate frames (received on inactive
-       ports), which is desirable for most users. But there are some times
-       it is nice to allow duplicate frames to be delivered.
-
-       The default value is 0 (drop duplicate frames received on inactive
-       ports).
-
-arp_interval
-
-       Specifies the ARP link monitoring frequency in milliseconds.
-
-       The ARP monitor works by periodically checking the slave
-       devices to determine whether they have sent or received
-       traffic recently (the precise criteria depends upon the
-       bonding mode, and the state of the slave).  Regular traffic is
-       generated via ARP probes issued for the addresses specified by
-       the arp_ip_target option.
-
-       This behavior can be modified by the arp_validate option,
-       below.
-
-       If ARP monitoring is used in an etherchannel compatible mode
-       (modes 0 and 2), the switch should be configured in a mode
-       that evenly distributes packets across all links. If the
-       switch is configured to distribute the packets in an XOR
-       fashion, all replies from the ARP targets will be received on
-       the same link which could cause the other team members to
-       fail.  ARP monitoring should not be used in conjunction with
-       miimon.  A value of 0 disables ARP monitoring.  The default
-       value is 0.
-
-arp_ip_target
-
-       Specifies the IP addresses to use as ARP monitoring peers when
-       arp_interval is > 0.  These are the targets of the ARP request
-       sent to determine the health of the link to the targets.
-       Specify these values in ddd.ddd.ddd.ddd format.  Multiple IP
-       addresses must be separated by a comma.  At least one IP
-       address must be given for ARP monitoring to function.  The
-       maximum number of targets that can be specified is 16.  The
-       default value is no IP addresses.
-
-arp_validate
-
-       Specifies whether or not ARP probes and replies should be
-       validated in any mode that supports arp monitoring, or whether
-       non-ARP traffic should be filtered (disregarded) for link
-       monitoring purposes.
-
-       Possible values are:
-
-       none or 0
-
-               No validation or filtering is performed.
-
-       active or 1
-
-               Validation is performed only for the active slave.
-
-       backup or 2
-
-               Validation is performed only for backup slaves.
-
-       all or 3
-
-               Validation is performed for all slaves.
-
-       filter or 4
-
-               Filtering is applied to all slaves. No validation is
-               performed.
-
-       filter_active or 5
-
-               Filtering is applied to all slaves, validation is performed
-               only for the active slave.
-
-       filter_backup or 6
-
-               Filtering is applied to all slaves, validation is performed
-               only for backup slaves.
-
-       Validation:
-
-       Enabling validation causes the ARP monitor to examine the incoming
-       ARP requests and replies, and only consider a slave to be up if it
-       is receiving the appropriate ARP traffic.
-
-       For an active slave, the validation checks ARP replies to confirm
-       that they were generated by an arp_ip_target.  Since backup slaves
-       do not typically receive these replies, the validation performed
-       for backup slaves is on the broadcast ARP request sent out via the
-       active slave.  It is possible that some switch or network
-       configurations may result in situations wherein the backup slaves
-       do not receive the ARP requests; in such a situation, validation
-       of backup slaves must be disabled.
-
-       The validation of ARP requests on backup slaves is mainly helping
-       bonding to decide which slaves are more likely to work in case of
-       the active slave failure, it doesn't really guarantee that the
-       backup slave will work if it's selected as the next active slave.
-
-       Validation is useful in network configurations in which multiple
-       bonding hosts are concurrently issuing ARPs to one or more targets
-       beyond a common switch.  Should the link between the switch and
-       target fail (but not the switch itself), the probe traffic
-       generated by the multiple bonding instances will fool the standard
-       ARP monitor into considering the links as still up.  Use of
-       validation can resolve this, as the ARP monitor will only consider
-       ARP requests and replies associated with its own instance of
-       bonding.
-
-       Filtering:
-
-       Enabling filtering causes the ARP monitor to only use incoming ARP
-       packets for link availability purposes.  Arriving packets that are
-       not ARPs are delivered normally, but do not count when determining
-       if a slave is available.
-
-       Filtering operates by only considering the reception of ARP
-       packets (any ARP packet, regardless of source or destination) when
-       determining if a slave has received traffic for link availability
-       purposes.
-
-       Filtering is useful in network configurations in which significant
-       levels of third party broadcast traffic would fool the standard
-       ARP monitor into considering the links as still up.  Use of
-       filtering can resolve this, as only ARP traffic is considered for
-       link availability purposes.
-
-       This option was added in bonding version 3.1.0.
-
-arp_all_targets
-
-       Specifies the quantity of arp_ip_targets that must be reachable
-       in order for the ARP monitor to consider a slave as being up.
-       This option affects only active-backup mode for slaves with
-       arp_validation enabled.
-
-       Possible values are:
-
-       any or 0
-
-               consider the slave up only when any of the arp_ip_targets
-               is reachable
-
-       all or 1
-
-               consider the slave up only when all of the arp_ip_targets
-               are reachable
-
-downdelay
-
-       Specifies the time, in milliseconds, to wait before disabling
-       a slave after a link failure has been detected.  This option
-       is only valid for the miimon link monitor.  The downdelay
-       value should be a multiple of the miimon value; if not, it
-       will be rounded down to the nearest multiple.  The default
-       value is 0.
-
-fail_over_mac
-
-       Specifies whether active-backup mode should set all slaves to
-       the same MAC address at enslavement (the traditional
-       behavior), or, when enabled, perform special handling of the
-       bond's MAC address in accordance with the selected policy.
-
-       Possible values are:
-
-       none or 0
-
-               This setting disables fail_over_mac, and causes
-               bonding to set all slaves of an active-backup bond to
-               the same MAC address at enslavement time.  This is the
-               default.
-
-       active or 1
-
-               The "active" fail_over_mac policy indicates that the
-               MAC address of the bond should always be the MAC
-               address of the currently active slave.  The MAC
-               address of the slaves is not changed; instead, the MAC
-               address of the bond changes during a failover.
-
-               This policy is useful for devices that cannot ever
-               alter their MAC address, or for devices that refuse
-               incoming broadcasts with their own source MAC (which
-               interferes with the ARP monitor).
-
-               The down side of this policy is that every device on
-               the network must be updated via gratuitous ARP,
-               vs. just updating a switch or set of switches (which
-               often takes place for any traffic, not just ARP
-               traffic, if the switch snoops incoming traffic to
-               update its tables) for the traditional method.  If the
-               gratuitous ARP is lost, communication may be
-               disrupted.
-
-               When this policy is used in conjunction with the mii
-               monitor, devices which assert link up prior to being
-               able to actually transmit and receive are particularly
-               susceptible to loss of the gratuitous ARP, and an
-               appropriate updelay setting may be required.
-
-       follow or 2
-
-               The "follow" fail_over_mac policy causes the MAC
-               address of the bond to be selected normally (normally
-               the MAC address of the first slave added to the bond).
-               However, the second and subsequent slaves are not set
-               to this MAC address while they are in a backup role; a
-               slave is programmed with the bond's MAC address at
-               failover time (and the formerly active slave receives
-               the newly active slave's MAC address).
-
-               This policy is useful for multiport devices that
-               either become confused or incur a performance penalty
-               when multiple ports are programmed with the same MAC
-               address.
-
-
-       The default policy is none, unless the first slave cannot
-       change its MAC address, in which case the active policy is
-       selected by default.
-
-       This option may be modified via sysfs only when no slaves are
-       present in the bond.
-
-       This option was added in bonding version 3.2.0.  The "follow"
-       policy was added in bonding version 3.3.0.
-
-lacp_rate
-
-       Option specifying the rate in which we'll ask our link partner
-       to transmit LACPDU packets in 802.3ad mode.  Possible values
-       are:
-
-       slow or 0
-               Request partner to transmit LACPDUs every 30 seconds
-
-       fast or 1
-               Request partner to transmit LACPDUs every 1 second
-
-       The default is slow.
-
-max_bonds
-
-       Specifies the number of bonding devices to create for this
-       instance of the bonding driver.  E.g., if max_bonds is 3, and
-       the bonding driver is not already loaded, then bond0, bond1
-       and bond2 will be created.  The default value is 1.  Specifying
-       a value of 0 will load bonding, but will not create any devices.
-
-miimon
-
-       Specifies the MII link monitoring frequency in milliseconds.
-       This determines how often the link state of each slave is
-       inspected for link failures.  A value of zero disables MII
-       link monitoring.  A value of 100 is a good starting point.
-       The use_carrier option, below, affects how the link state is
-       determined.  See the High Availability section for additional
-       information.  The default value is 0.
-
-min_links
-
-       Specifies the minimum number of links that must be active before
-       asserting carrier. It is similar to the Cisco EtherChannel min-links
-       feature. This allows setting the minimum number of member ports that
-       must be up (link-up state) before marking the bond device as up
-       (carrier on). This is useful for situations where higher level services
-       such as clustering want to ensure a minimum number of low bandwidth
-       links are active before switchover. This option only affect 802.3ad
-       mode.
-
-       The default value is 0. This will cause carrier to be asserted (for
-       802.3ad mode) whenever there is an active aggregator, regardless of the
-       number of available links in that aggregator. Note that, because an
-       aggregator cannot be active without at least one available link,
-       setting this option to 0 or to 1 has the exact same effect.
-
-mode
-
-       Specifies one of the bonding policies. The default is
-       balance-rr (round robin).  Possible values are:
-
-       balance-rr or 0
-
-               Round-robin policy: Transmit packets in sequential
-               order from the first available slave through the
-               last.  This mode provides load balancing and fault
-               tolerance.
-
-       active-backup or 1
-
-               Active-backup policy: Only one slave in the bond is
-               active.  A different slave becomes active if, and only
-               if, the active slave fails.  The bond's MAC address is
-               externally visible on only one port (network adapter)
-               to avoid confusing the switch.
-
-               In bonding version 2.6.2 or later, when a failover
-               occurs in active-backup mode, bonding will issue one
-               or more gratuitous ARPs on the newly active slave.
-               One gratuitous ARP is issued for the bonding master
-               interface and each VLAN interfaces configured above
-               it, provided that the interface has at least one IP
-               address configured.  Gratuitous ARPs issued for VLAN
-               interfaces are tagged with the appropriate VLAN id.
-
-               This mode provides fault tolerance.  The primary
-               option, documented below, affects the behavior of this
-               mode.
-
-       balance-xor or 2
-
-               XOR policy: Transmit based on the selected transmit
-               hash policy.  The default policy is a simple [(source
-               MAC address XOR'd with destination MAC address XOR
-               packet type ID) modulo slave count].  Alternate transmit
-               policies may be selected via the xmit_hash_policy option,
-               described below.
-
-               This mode provides load balancing and fault tolerance.
-
-       broadcast or 3
-
-               Broadcast policy: transmits everything on all slave
-               interfaces.  This mode provides fault tolerance.
-
-       802.3ad or 4
-
-               IEEE 802.3ad Dynamic link aggregation.  Creates
-               aggregation groups that share the same speed and
-               duplex settings.  Utilizes all slaves in the active
-               aggregator according to the 802.3ad specification.
-
-               Slave selection for outgoing traffic is done according
-               to the transmit hash policy, which may be changed from
-               the default simple XOR policy via the xmit_hash_policy
-               option, documented below.  Note that not all transmit
-               policies may be 802.3ad compliant, particularly in
-               regards to the packet mis-ordering requirements of
-               section 43.2.4 of the 802.3ad standard.  Differing
-               peer implementations will have varying tolerances for
-               noncompliance.
-
-               Prerequisites:
-
-               1. Ethtool support in the base drivers for retrieving
-               the speed and duplex of each slave.
-
-               2. A switch that supports IEEE 802.3ad Dynamic link
-               aggregation.
-
-               Most switches will require some type of configuration
-               to enable 802.3ad mode.
-
-       balance-tlb or 5
-
-               Adaptive transmit load balancing: channel bonding that
-               does not require any special switch support.
-
-               In tlb_dynamic_lb=1 mode; the outgoing traffic is
-               distributed according to the current load (computed
-               relative to the speed) on each slave.
-
-               In tlb_dynamic_lb=0 mode; the load balancing based on
-               current load is disabled and the load is distributed
-               only using the hash distribution.
-
-               Incoming traffic is received by the current slave.
-               If the receiving slave fails, another slave takes over
-               the MAC address of the failed receiving slave.
-
-               Prerequisite:
-
-               Ethtool support in the base drivers for retrieving the
-               speed of each slave.
-
-       balance-alb or 6
-
-               Adaptive load balancing: includes balance-tlb plus
-               receive load balancing (rlb) for IPV4 traffic, and
-               does not require any special switch support.  The
-               receive load balancing is achieved by ARP negotiation.
-               The bonding driver intercepts the ARP Replies sent by
-               the local system on their way out and overwrites the
-               source hardware address with the unique hardware
-               address of one of the slaves in the bond such that
-               different peers use different hardware addresses for
-               the server.
-
-               Receive traffic from connections created by the server
-               is also balanced.  When the local system sends an ARP
-               Request the bonding driver copies and saves the peer's
-               IP information from the ARP packet.  When the ARP
-               Reply arrives from the peer, its hardware address is
-               retrieved and the bonding driver initiates an ARP
-               reply to this peer assigning it to one of the slaves
-               in the bond.  A problematic outcome of using ARP
-               negotiation for balancing is that each time that an
-               ARP request is broadcast it uses the hardware address
-               of the bond.  Hence, peers learn the hardware address
-               of the bond and the balancing of receive traffic
-               collapses to the current slave.  This is handled by
-               sending updates (ARP Replies) to all the peers with
-               their individually assigned hardware address such that
-               the traffic is redistributed.  Receive traffic is also
-               redistributed when a new slave is added to the bond
-               and when an inactive slave is re-activated.  The
-               receive load is distributed sequentially (round robin)
-               among the group of highest speed slaves in the bond.
-
-               When a link is reconnected or a new slave joins the
-               bond the receive traffic is redistributed among all
-               active slaves in the bond by initiating ARP Replies
-               with the selected MAC address to each of the
-               clients. The updelay parameter (detailed below) must
-               be set to a value equal or greater than the switch's
-               forwarding delay so that the ARP Replies sent to the
-               peers will not be blocked by the switch.
-
-               Prerequisites:
-
-               1. Ethtool support in the base drivers for retrieving
-               the speed of each slave.
-
-               2. Base driver support for setting the hardware
-               address of a device while it is open.  This is
-               required so that there will always be one slave in the
-               team using the bond hardware address (the
-               curr_active_slave) while having a unique hardware
-               address for each slave in the bond.  If the
-               curr_active_slave fails its hardware address is
-               swapped with the new curr_active_slave that was
-               chosen.
-
-num_grat_arp
-num_unsol_na
-
-       Specify the number of peer notifications (gratuitous ARPs and
-       unsolicited IPv6 Neighbor Advertisements) to be issued after a
-       failover event.  As soon as the link is up on the new slave
-       (possibly immediately) a peer notification is sent on the
-       bonding device and each VLAN sub-device. This is repeated at
-       the rate specified by peer_notif_delay if the number is
-       greater than 1.
-
-       The valid range is 0 - 255; the default value is 1.  These options
-       affect only the active-backup mode.  These options were added for
-       bonding versions 3.3.0 and 3.4.0 respectively.
-
-       From Linux 3.0 and bonding version 3.7.1, these notifications
-       are generated by the ipv4 and ipv6 code and the numbers of
-       repetitions cannot be set independently.
-
-packets_per_slave
-
-       Specify the number of packets to transmit through a slave before
-       moving to the next one. When set to 0 then a slave is chosen at
-       random.
-
-       The valid range is 0 - 65535; the default value is 1. This option
-       has effect only in balance-rr mode.
-
-peer_notif_delay
-
-        Specify the delay, in milliseconds, between each peer
-        notification (gratuitous ARP and unsolicited IPv6 Neighbor
-        Advertisement) when they are issued after a failover event.
-        This delay should be a multiple of the link monitor interval
-        (arp_interval or miimon, whichever is active). The default
-        value is 0 which means to match the value of the link monitor
-        interval.
-
-primary
-
-       A string (eth0, eth2, etc) specifying which slave is the
-       primary device.  The specified device will always be the
-       active slave while it is available.  Only when the primary is
-       off-line will alternate devices be used.  This is useful when
-       one slave is preferred over another, e.g., when one slave has
-       higher throughput than another.
-
-       The primary option is only valid for active-backup(1),
-       balance-tlb (5) and balance-alb (6) mode.
-
-primary_reselect
-
-       Specifies the reselection policy for the primary slave.  This
-       affects how the primary slave is chosen to become the active slave
-       when failure of the active slave or recovery of the primary slave
-       occurs.  This option is designed to prevent flip-flopping between
-       the primary slave and other slaves.  Possible values are:
-
-       always or 0 (default)
-
-               The primary slave becomes the active slave whenever it
-               comes back up.
-
-       better or 1
-
-               The primary slave becomes the active slave when it comes
-               back up, if the speed and duplex of the primary slave is
-               better than the speed and duplex of the current active
-               slave.
-
-       failure or 2
-
-               The primary slave becomes the active slave only if the
-               current active slave fails and the primary slave is up.
-
-       The primary_reselect setting is ignored in two cases:
-
-               If no slaves are active, the first slave to recover is
-               made the active slave.
-
-               When initially enslaved, the primary slave is always made
-               the active slave.
-
-       Changing the primary_reselect policy via sysfs will cause an
-       immediate selection of the best active slave according to the new
-       policy.  This may or may not result in a change of the active
-       slave, depending upon the circumstances.
-
-       This option was added for bonding version 3.6.0.
-
-tlb_dynamic_lb
-
-       Specifies if dynamic shuffling of flows is enabled in tlb
-       mode. The value has no effect on any other modes.
-
-       The default behavior of tlb mode is to shuffle active flows across
-       slaves based on the load in that interval. This gives nice lb
-       characteristics but can cause packet reordering. If re-ordering is
-       a concern use this variable to disable flow shuffling and rely on
-       load balancing provided solely by the hash distribution.
-       xmit-hash-policy can be used to select the appropriate hashing for
-       the setup.
-
-       The sysfs entry can be used to change the setting per bond device
-       and the initial value is derived from the module parameter. The
-       sysfs entry is allowed to be changed only if the bond device is
-       down.
-
-       The default value is "1" that enables flow shuffling while value "0"
-       disables it. This option was added in bonding driver 3.7.1
-
-
-updelay
-
-       Specifies the time, in milliseconds, to wait before enabling a
-       slave after a link recovery has been detected.  This option is
-       only valid for the miimon link monitor.  The updelay value
-       should be a multiple of the miimon value; if not, it will be
-       rounded down to the nearest multiple.  The default value is 0.
-
-use_carrier
-
-       Specifies whether or not miimon should use MII or ETHTOOL
-       ioctls vs. netif_carrier_ok() to determine the link
-       status. The MII or ETHTOOL ioctls are less efficient and
-       utilize a deprecated calling sequence within the kernel.  The
-       netif_carrier_ok() relies on the device driver to maintain its
-       state with netif_carrier_on/off; at this writing, most, but
-       not all, device drivers support this facility.
-
-       If bonding insists that the link is up when it should not be,
-       it may be that your network device driver does not support
-       netif_carrier_on/off.  The default state for netif_carrier is
-       "carrier on," so if a driver does not support netif_carrier,
-       it will appear as if the link is always up.  In this case,
-       setting use_carrier to 0 will cause bonding to revert to the
-       MII / ETHTOOL ioctl method to determine the link state.
-
-       A value of 1 enables the use of netif_carrier_ok(), a value of
-       0 will use the deprecated MII / ETHTOOL ioctls.  The default
-       value is 1.
-
-xmit_hash_policy
-
-       Selects the transmit hash policy to use for slave selection in
-       balance-xor, 802.3ad, and tlb modes.  Possible values are:
-
-       layer2
-
-               Uses XOR of hardware MAC addresses and packet type ID
-               field to generate the hash. The formula is
-
-               hash = source MAC XOR destination MAC XOR packet type ID
-               slave number = hash modulo slave count
-
-               This algorithm will place all traffic to a particular
-               network peer on the same slave.
-
-               This algorithm is 802.3ad compliant.
-
-       layer2+3
-
-               This policy uses a combination of layer2 and layer3
-               protocol information to generate the hash.
-
-               Uses XOR of hardware MAC addresses and IP addresses to
-               generate the hash.  The formula is
-
-               hash = source MAC XOR destination MAC XOR packet type ID
-               hash = hash XOR source IP XOR destination IP
-               hash = hash XOR (hash RSHIFT 16)
-               hash = hash XOR (hash RSHIFT 8)
-               And then hash is reduced modulo slave count.
-
-               If the protocol is IPv6 then the source and destination
-               addresses are first hashed using ipv6_addr_hash.
-
-               This algorithm will place all traffic to a particular
-               network peer on the same slave.  For non-IP traffic,
-               the formula is the same as for the layer2 transmit
-               hash policy.
-
-               This policy is intended to provide a more balanced
-               distribution of traffic than layer2 alone, especially
-               in environments where a layer3 gateway device is
-               required to reach most destinations.
-
-               This algorithm is 802.3ad compliant.
-
-       layer3+4
-
-               This policy uses upper layer protocol information,
-               when available, to generate the hash.  This allows for
-               traffic to a particular network peer to span multiple
-               slaves, although a single connection will not span
-               multiple slaves.
-
-               The formula for unfragmented TCP and UDP packets is
-
-               hash = source port, destination port (as in the header)
-               hash = hash XOR source IP XOR destination IP
-               hash = hash XOR (hash RSHIFT 16)
-               hash = hash XOR (hash RSHIFT 8)
-               And then hash is reduced modulo slave count.
-
-               If the protocol is IPv6 then the source and destination
-               addresses are first hashed using ipv6_addr_hash.
-
-               For fragmented TCP or UDP packets and all other IPv4 and
-               IPv6 protocol traffic, the source and destination port
-               information is omitted.  For non-IP traffic, the
-               formula is the same as for the layer2 transmit hash
-               policy.
-
-               This algorithm is not fully 802.3ad compliant.  A
-               single TCP or UDP conversation containing both
-               fragmented and unfragmented packets will see packets
-               striped across two interfaces.  This may result in out
-               of order delivery.  Most traffic types will not meet
-               this criteria, as TCP rarely fragments traffic, and
-               most UDP traffic is not involved in extended
-               conversations.  Other implementations of 802.3ad may
-               or may not tolerate this noncompliance.
-
-       encap2+3
-
-               This policy uses the same formula as layer2+3 but it
-               relies on skb_flow_dissect to obtain the header fields
-               which might result in the use of inner headers if an
-               encapsulation protocol is used. For example this will
-               improve the performance for tunnel users because the
-               packets will be distributed according to the encapsulated
-               flows.
-
-       encap3+4
-
-               This policy uses the same formula as layer3+4 but it
-               relies on skb_flow_dissect to obtain the header fields
-               which might result in the use of inner headers if an
-               encapsulation protocol is used. For example this will
-               improve the performance for tunnel users because the
-               packets will be distributed according to the encapsulated
-               flows.
-
-       The default value is layer2.  This option was added in bonding
-       version 2.6.3.  In earlier versions of bonding, this parameter
-       does not exist, and the layer2 policy is the only policy.  The
-       layer2+3 value was added for bonding version 3.2.2.
-
-resend_igmp
-
-       Specifies the number of IGMP membership reports to be issued after
-       a failover event. One membership report is issued immediately after
-       the failover, subsequent packets are sent in each 200ms interval.
-
-       The valid range is 0 - 255; the default value is 1. A value of 0
-       prevents the IGMP membership report from being issued in response
-       to the failover event.
-
-       This option is useful for bonding modes balance-rr (0), active-backup
-       (1), balance-tlb (5) and balance-alb (6), in which a failover can
-       switch the IGMP traffic from one slave to another.  Therefore a fresh
-       IGMP report must be issued to cause the switch to forward the incoming
-       IGMP traffic over the newly selected slave.
-
-       This option was added for bonding version 3.7.0.
-
-lp_interval
-
-       Specifies the number of seconds between instances where the bonding
-       driver sends learning packets to each slaves peer switch.
-
-       The valid range is 1 - 0x7fffffff; the default value is 1. This Option
-       has effect only in balance-tlb and balance-alb modes.
-
-3. Configuring Bonding Devices
-==============================
-
-       You can configure bonding using either your distro's network
-initialization scripts, or manually using either iproute2 or the
-sysfs interface.  Distros generally use one of three packages for the
-network initialization scripts: initscripts, sysconfig or interfaces.
-Recent versions of these packages have support for bonding, while older
-versions do not.
-
-       We will first describe the options for configuring bonding for
-distros using versions of initscripts, sysconfig and interfaces with full
-or partial support for bonding, then provide information on enabling
-bonding without support from the network initialization scripts (i.e.,
-older versions of initscripts or sysconfig).
-
-       If you're unsure whether your distro uses sysconfig,
-initscripts or interfaces, or don't know if it's new enough, have no fear.
-Determining this is fairly straightforward.
-
-       First, look for a file called interfaces in /etc/network directory.
-If this file is present in your system, then your system use interfaces. See
-Configuration with Interfaces Support.
-
-       Else, issue the command:
-
-$ rpm -qf /sbin/ifup
-
-       It will respond with a line of text starting with either
-"initscripts" or "sysconfig," followed by some numbers.  This is the
-package that provides your network initialization scripts.
-
-       Next, to determine if your installation supports bonding,
-issue the command:
-
-$ grep ifenslave /sbin/ifup
-
-       If this returns any matches, then your initscripts or
-sysconfig has support for bonding.
-
-3.1 Configuration with Sysconfig Support
-----------------------------------------
-
-       This section applies to distros using a version of sysconfig
-with bonding support, for example, SuSE Linux Enterprise Server 9.
-
-       SuSE SLES 9's networking configuration system does support
-bonding, however, at this writing, the YaST system configuration
-front end does not provide any means to work with bonding devices.
-Bonding devices can be managed by hand, however, as follows.
-
-       First, if they have not already been configured, configure the
-slave devices.  On SLES 9, this is most easily done by running the
-yast2 sysconfig configuration utility.  The goal is for to create an
-ifcfg-id file for each slave device.  The simplest way to accomplish
-this is to configure the devices for DHCP (this is only to get the
-file ifcfg-id file created; see below for some issues with DHCP).  The
-name of the configuration file for each device will be of the form:
-
-ifcfg-id-xx:xx:xx:xx:xx:xx
-
-       Where the "xx" portion will be replaced with the digits from
-the device's permanent MAC address.
-
-       Once the set of ifcfg-id-xx:xx:xx:xx:xx:xx files has been
-created, it is necessary to edit the configuration files for the slave
-devices (the MAC addresses correspond to those of the slave devices).
-Before editing, the file will contain multiple lines, and will look
-something like this:
-
-BOOTPROTO='dhcp'
-STARTMODE='on'
-USERCTL='no'
-UNIQUE='XNzu.WeZGOGF+4wE'
-_nm_name='bus-pci-0001:61:01.0'
-
-       Change the BOOTPROTO and STARTMODE lines to the following:
-
-BOOTPROTO='none'
-STARTMODE='off'
-
-       Do not alter the UNIQUE or _nm_name lines.  Remove any other
-lines (USERCTL, etc).
-
-       Once the ifcfg-id-xx:xx:xx:xx:xx:xx files have been modified,
-it's time to create the configuration file for the bonding device
-itself.  This file is named ifcfg-bondX, where X is the number of the
-bonding device to create, starting at 0.  The first such file is
-ifcfg-bond0, the second is ifcfg-bond1, and so on.  The sysconfig
-network configuration system will correctly start multiple instances
-of bonding.
-
-       The contents of the ifcfg-bondX file is as follows:
-
-BOOTPROTO="static"
-BROADCAST="10.0.2.255"
-IPADDR="10.0.2.10"
-NETMASK="255.255.0.0"
-NETWORK="10.0.2.0"
-REMOTE_IPADDR=""
-STARTMODE="onboot"
-BONDING_MASTER="yes"
-BONDING_MODULE_OPTS="mode=active-backup miimon=100"
-BONDING_SLAVE0="eth0"
-BONDING_SLAVE1="bus-pci-0000:06:08.1"
-
-       Replace the sample BROADCAST, IPADDR, NETMASK and NETWORK
-values with the appropriate values for your network.
-
-       The STARTMODE specifies when the device is brought online.
-The possible values are:
-
-       onboot:  The device is started at boot time.  If you're not
-                sure, this is probably what you want.
-
-       manual:  The device is started only when ifup is called
-                manually.  Bonding devices may be configured this
-                way if you do not wish them to start automatically
-                at boot for some reason.
-
-       hotplug: The device is started by a hotplug event.  This is not
-                a valid choice for a bonding device.
-
-       off or ignore: The device configuration is ignored.
-
-       The line BONDING_MASTER='yes' indicates that the device is a
-bonding master device.  The only useful value is "yes."
-
-       The contents of BONDING_MODULE_OPTS are supplied to the
-instance of the bonding module for this device.  Specify the options
-for the bonding mode, link monitoring, and so on here.  Do not include
-the max_bonds bonding parameter; this will confuse the configuration
-system if you have multiple bonding devices.
-
-       Finally, supply one BONDING_SLAVEn="slave device" for each
-slave.  where "n" is an increasing value, one for each slave.  The
-"slave device" is either an interface name, e.g., "eth0", or a device
-specifier for the network device.  The interface name is easier to
-find, but the ethN names are subject to change at boot time if, e.g.,
-a device early in the sequence has failed.  The device specifiers
-(bus-pci-0000:06:08.1 in the example above) specify the physical
-network device, and will not change unless the device's bus location
-changes (for example, it is moved from one PCI slot to another).  The
-example above uses one of each type for demonstration purposes; most
-configurations will choose one or the other for all slave devices.
-
-       When all configuration files have been modified or created,
-networking must be restarted for the configuration changes to take
-effect.  This can be accomplished via the following:
-
-# /etc/init.d/network restart
-
-       Note that the network control script (/sbin/ifdown) will
-remove the bonding module as part of the network shutdown processing,
-so it is not necessary to remove the module by hand if, e.g., the
-module parameters have changed.
-
-       Also, at this writing, YaST/YaST2 will not manage bonding
-devices (they do not show bonding interfaces on its list of network
-devices).  It is necessary to edit the configuration file by hand to
-change the bonding configuration.
-
-       Additional general options and details of the ifcfg file
-format can be found in an example ifcfg template file:
-
-/etc/sysconfig/network/ifcfg.template
-
-       Note that the template does not document the various BONDING_
-settings described above, but does describe many of the other options.
-
-3.1.1 Using DHCP with Sysconfig
--------------------------------
-
-       Under sysconfig, configuring a device with BOOTPROTO='dhcp'
-will cause it to query DHCP for its IP address information.  At this
-writing, this does not function for bonding devices; the scripts
-attempt to obtain the device address from DHCP prior to adding any of
-the slave devices.  Without active slaves, the DHCP requests are not
-sent to the network.
-
-3.1.2 Configuring Multiple Bonds with Sysconfig
------------------------------------------------
-
-       The sysconfig network initialization system is capable of
-handling multiple bonding devices.  All that is necessary is for each
-bonding instance to have an appropriately configured ifcfg-bondX file
-(as described above).  Do not specify the "max_bonds" parameter to any
-instance of bonding, as this will confuse sysconfig.  If you require
-multiple bonding devices with identical parameters, create multiple
-ifcfg-bondX files.
-
-       Because the sysconfig scripts supply the bonding module
-options in the ifcfg-bondX file, it is not necessary to add them to
-the system /etc/modules.d/*.conf configuration files.
-
-3.2 Configuration with Initscripts Support
-------------------------------------------
-
-       This section applies to distros using a recent version of
-initscripts with bonding support, for example, Red Hat Enterprise Linux
-version 3 or later, Fedora, etc.  On these systems, the network
-initialization scripts have knowledge of bonding, and can be configured to
-control bonding devices.  Note that older versions of the initscripts
-package have lower levels of support for bonding; this will be noted where
-applicable.
-
-       These distros will not automatically load the network adapter
-driver unless the ethX device is configured with an IP address.
-Because of this constraint, users must manually configure a
-network-script file for all physical adapters that will be members of
-a bondX link.  Network script files are located in the directory:
-
-/etc/sysconfig/network-scripts
-
-       The file name must be prefixed with "ifcfg-eth" and suffixed
-with the adapter's physical adapter number.  For example, the script
-for eth0 would be named /etc/sysconfig/network-scripts/ifcfg-eth0.
-Place the following text in the file:
-
-DEVICE=eth0
-USERCTL=no
-ONBOOT=yes
-MASTER=bond0
-SLAVE=yes
-BOOTPROTO=none
-
-       The DEVICE= line will be different for every ethX device and
-must correspond with the name of the file, i.e., ifcfg-eth1 must have
-a device line of DEVICE=eth1.  The setting of the MASTER= line will
-also depend on the final bonding interface name chosen for your bond.
-As with other network devices, these typically start at 0, and go up
-one for each device, i.e., the first bonding instance is bond0, the
-second is bond1, and so on.
-
-       Next, create a bond network script.  The file name for this
-script will be /etc/sysconfig/network-scripts/ifcfg-bondX where X is
-the number of the bond.  For bond0 the file is named "ifcfg-bond0",
-for bond1 it is named "ifcfg-bond1", and so on.  Within that file,
-place the following text:
-
-DEVICE=bond0
-IPADDR=192.168.1.1
-NETMASK=255.255.255.0
-NETWORK=192.168.1.0
-BROADCAST=192.168.1.255
-ONBOOT=yes
-BOOTPROTO=none
-USERCTL=no
-
-       Be sure to change the networking specific lines (IPADDR,
-NETMASK, NETWORK and BROADCAST) to match your network configuration.
-
-       For later versions of initscripts, such as that found with Fedora
-7 (or later) and Red Hat Enterprise Linux version 5 (or later), it is possible,
-and, indeed, preferable, to specify the bonding options in the ifcfg-bond0
-file, e.g. a line of the format:
-
-BONDING_OPTS="mode=active-backup arp_interval=60 arp_ip_target=192.168.1.254"
-
-       will configure the bond with the specified options.  The options
-specified in BONDING_OPTS are identical to the bonding module parameters
-except for the arp_ip_target field when using versions of initscripts older
-than and 8.57 (Fedora 8) and 8.45.19 (Red Hat Enterprise Linux 5.2).  When
-using older versions each target should be included as a separate option and
-should be preceded by a '+' to indicate it should be added to the list of
-queried targets, e.g.,
-
-       arp_ip_target=+192.168.1.1 arp_ip_target=+192.168.1.2
-
-       is the proper syntax to specify multiple targets.  When specifying
-options via BONDING_OPTS, it is not necessary to edit /etc/modprobe.d/*.conf.
-
-       For even older versions of initscripts that do not support
-BONDING_OPTS, it is necessary to edit /etc/modprobe.d/*.conf, depending upon
-your distro) to load the bonding module with your desired options when the
-bond0 interface is brought up.  The following lines in /etc/modprobe.d/*.conf
-will load the bonding module, and select its options:
-
-alias bond0 bonding
-options bond0 mode=balance-alb miimon=100
-
-       Replace the sample parameters with the appropriate set of
-options for your configuration.
-
-       Finally run "/etc/rc.d/init.d/network restart" as root.  This
-will restart the networking subsystem and your bond link should be now
-up and running.
-
-3.2.1 Using DHCP with Initscripts
----------------------------------
-
-       Recent versions of initscripts (the versions supplied with Fedora
-Core 3 and Red Hat Enterprise Linux 4, or later versions, are reported to
-work) have support for assigning IP information to bonding devices via
-DHCP.
-
-       To configure bonding for DHCP, configure it as described
-above, except replace the line "BOOTPROTO=none" with "BOOTPROTO=dhcp"
-and add a line consisting of "TYPE=Bonding".  Note that the TYPE value
-is case sensitive.
-
-3.2.2 Configuring Multiple Bonds with Initscripts
--------------------------------------------------
-
-       Initscripts packages that are included with Fedora 7 and Red Hat
-Enterprise Linux 5 support multiple bonding interfaces by simply
-specifying the appropriate BONDING_OPTS= in ifcfg-bondX where X is the
-number of the bond.  This support requires sysfs support in the kernel,
-and a bonding driver of version 3.0.0 or later.  Other configurations may
-not support this method for specifying multiple bonding interfaces; for
-those instances, see the "Configuring Multiple Bonds Manually" section,
-below.
-
-3.3 Configuring Bonding Manually with iproute2
------------------------------------------------
-
-       This section applies to distros whose network initialization
-scripts (the sysconfig or initscripts package) do not have specific
-knowledge of bonding.  One such distro is SuSE Linux Enterprise Server
-version 8.
-
-       The general method for these systems is to place the bonding
-module parameters into a config file in /etc/modprobe.d/ (as
-appropriate for the installed distro), then add modprobe and/or
-`ip link` commands to the system's global init script.  The name of
-the global init script differs; for sysconfig, it is
-/etc/init.d/boot.local and for initscripts it is /etc/rc.d/rc.local.
-
-       For example, if you wanted to make a simple bond of two e100
-devices (presumed to be eth0 and eth1), and have it persist across
-reboots, edit the appropriate file (/etc/init.d/boot.local or
-/etc/rc.d/rc.local), and add the following:
-
-modprobe bonding mode=balance-alb miimon=100
-modprobe e100
-ifconfig bond0 192.168.1.1 netmask 255.255.255.0 up
-ip link set eth0 master bond0
-ip link set eth1 master bond0
-
-       Replace the example bonding module parameters and bond0
-network configuration (IP address, netmask, etc) with the appropriate
-values for your configuration.
-
-       Unfortunately, this method will not provide support for the
-ifup and ifdown scripts on the bond devices.  To reload the bonding
-configuration, it is necessary to run the initialization script, e.g.,
-
-# /etc/init.d/boot.local
-
-       or
-
-# /etc/rc.d/rc.local
-
-       It may be desirable in such a case to create a separate script
-which only initializes the bonding configuration, then call that
-separate script from within boot.local.  This allows for bonding to be
-enabled without re-running the entire global init script.
-
-       To shut down the bonding devices, it is necessary to first
-mark the bonding device itself as being down, then remove the
-appropriate device driver modules.  For our example above, you can do
-the following:
-
-# ifconfig bond0 down
-# rmmod bonding
-# rmmod e100
-
-       Again, for convenience, it may be desirable to create a script
-with these commands.
-
-
-3.3.1 Configuring Multiple Bonds Manually
------------------------------------------
-
-       This section contains information on configuring multiple
-bonding devices with differing options for those systems whose network
-initialization scripts lack support for configuring multiple bonds.
-
-       If you require multiple bonding devices, but all with the same
-options, you may wish to use the "max_bonds" module parameter,
-documented above.
-
-       To create multiple bonding devices with differing options, it is
-preferable to use bonding parameters exported by sysfs, documented in the
-section below.
-
-       For versions of bonding without sysfs support, the only means to
-provide multiple instances of bonding with differing options is to load
-the bonding driver multiple times.  Note that current versions of the
-sysconfig network initialization scripts handle this automatically; if
-your distro uses these scripts, no special action is needed.  See the
-section Configuring Bonding Devices, above, if you're not sure about your
-network initialization scripts.
-
-       To load multiple instances of the module, it is necessary to
-specify a different name for each instance (the module loading system
-requires that every loaded module, even multiple instances of the same
-module, have a unique name).  This is accomplished by supplying multiple
-sets of bonding options in /etc/modprobe.d/*.conf, for example:
-
-alias bond0 bonding
-options bond0 -o bond0 mode=balance-rr miimon=100
-
-alias bond1 bonding
-options bond1 -o bond1 mode=balance-alb miimon=50
-
-       will load the bonding module two times.  The first instance is
-named "bond0" and creates the bond0 device in balance-rr mode with an
-miimon of 100.  The second instance is named "bond1" and creates the
-bond1 device in balance-alb mode with an miimon of 50.
-
-       In some circumstances (typically with older distributions),
-the above does not work, and the second bonding instance never sees
-its options.  In that case, the second options line can be substituted
-as follows:
-
-install bond1 /sbin/modprobe --ignore-install bonding -o bond1 \
-       mode=balance-alb miimon=50
-
-       This may be repeated any number of times, specifying a new and
-unique name in place of bond1 for each subsequent instance.
-
-       It has been observed that some Red Hat supplied kernels are unable
-to rename modules at load time (the "-o bond1" part).  Attempts to pass
-that option to modprobe will produce an "Operation not permitted" error.
-This has been reported on some Fedora Core kernels, and has been seen on
-RHEL 4 as well.  On kernels exhibiting this problem, it will be impossible
-to configure multiple bonds with differing parameters (as they are older
-kernels, and also lack sysfs support).
-
-3.4 Configuring Bonding Manually via Sysfs
-------------------------------------------
-
-       Starting with version 3.0.0, Channel Bonding may be configured
-via the sysfs interface.  This interface allows dynamic configuration
-of all bonds in the system without unloading the module.  It also
-allows for adding and removing bonds at runtime.  Ifenslave is no
-longer required, though it is still supported.
-
-       Use of the sysfs interface allows you to use multiple bonds
-with different configurations without having to reload the module.
-It also allows you to use multiple, differently configured bonds when
-bonding is compiled into the kernel.
-
-       You must have the sysfs filesystem mounted to configure
-bonding this way.  The examples in this document assume that you
-are using the standard mount point for sysfs, e.g. /sys.  If your
-sysfs filesystem is mounted elsewhere, you will need to adjust the
-example paths accordingly.
-
-Creating and Destroying Bonds
------------------------------
-To add a new bond foo:
-# echo +foo > /sys/class/net/bonding_masters
-
-To remove an existing bond bar:
-# echo -bar > /sys/class/net/bonding_masters
-
-To show all existing bonds:
-# cat /sys/class/net/bonding_masters
-
-NOTE: due to 4K size limitation of sysfs files, this list may be
-truncated if you have more than a few hundred bonds.  This is unlikely
-to occur under normal operating conditions.
-
-Adding and Removing Slaves
---------------------------
-       Interfaces may be enslaved to a bond using the file
-/sys/class/net/<bond>/bonding/slaves.  The semantics for this file
-are the same as for the bonding_masters file.
-
-To enslave interface eth0 to bond bond0:
-# ifconfig bond0 up
-# echo +eth0 > /sys/class/net/bond0/bonding/slaves
-
-To free slave eth0 from bond bond0:
-# echo -eth0 > /sys/class/net/bond0/bonding/slaves
-
-       When an interface is enslaved to a bond, symlinks between the
-two are created in the sysfs filesystem.  In this case, you would get
-/sys/class/net/bond0/slave_eth0 pointing to /sys/class/net/eth0, and
-/sys/class/net/eth0/master pointing to /sys/class/net/bond0.
-
-       This means that you can tell quickly whether or not an
-interface is enslaved by looking for the master symlink.  Thus:
-# echo -eth0 > /sys/class/net/eth0/master/bonding/slaves
-will free eth0 from whatever bond it is enslaved to, regardless of
-the name of the bond interface.
-
-Changing a Bond's Configuration
--------------------------------
-       Each bond may be configured individually by manipulating the
-files located in /sys/class/net/<bond name>/bonding
-
-       The names of these files correspond directly with the command-
-line parameters described elsewhere in this file, and, with the
-exception of arp_ip_target, they accept the same values.  To see the
-current setting, simply cat the appropriate file.
-
-       A few examples will be given here; for specific usage
-guidelines for each parameter, see the appropriate section in this
-document.
-
-To configure bond0 for balance-alb mode:
-# ifconfig bond0 down
-# echo 6 > /sys/class/net/bond0/bonding/mode
- - or -
-# echo balance-alb > /sys/class/net/bond0/bonding/mode
-       NOTE: The bond interface must be down before the mode can be
-changed.
-
-To enable MII monitoring on bond0 with a 1 second interval:
-# echo 1000 > /sys/class/net/bond0/bonding/miimon
-       NOTE: If ARP monitoring is enabled, it will disabled when MII
-monitoring is enabled, and vice-versa.
-
-To add ARP targets:
-# echo +192.168.0.100 > /sys/class/net/bond0/bonding/arp_ip_target
-# echo +192.168.0.101 > /sys/class/net/bond0/bonding/arp_ip_target
-       NOTE:  up to 16 target addresses may be specified.
-
-To remove an ARP target:
-# echo -192.168.0.100 > /sys/class/net/bond0/bonding/arp_ip_target
-
-To configure the interval between learning packet transmits:
-# echo 12 > /sys/class/net/bond0/bonding/lp_interval
-       NOTE: the lp_interval is the number of seconds between instances where
-the bonding driver sends learning packets to each slaves peer switch.  The
-default interval is 1 second.
-
-Example Configuration
----------------------
-       We begin with the same example that is shown in section 3.3,
-executed with sysfs, and without using ifenslave.
-
-       To make a simple bond of two e100 devices (presumed to be eth0
-and eth1), and have it persist across reboots, edit the appropriate
-file (/etc/init.d/boot.local or /etc/rc.d/rc.local), and add the
-following:
-
-modprobe bonding
-modprobe e100
-echo balance-alb > /sys/class/net/bond0/bonding/mode
-ifconfig bond0 192.168.1.1 netmask 255.255.255.0 up
-echo 100 > /sys/class/net/bond0/bonding/miimon
-echo +eth0 > /sys/class/net/bond0/bonding/slaves
-echo +eth1 > /sys/class/net/bond0/bonding/slaves
-
-       To add a second bond, with two e1000 interfaces in
-active-backup mode, using ARP monitoring, add the following lines to
-your init script:
-
-modprobe e1000
-echo +bond1 > /sys/class/net/bonding_masters
-echo active-backup > /sys/class/net/bond1/bonding/mode
-ifconfig bond1 192.168.2.1 netmask 255.255.255.0 up
-echo +192.168.2.100 /sys/class/net/bond1/bonding/arp_ip_target
-echo 2000 > /sys/class/net/bond1/bonding/arp_interval
-echo +eth2 > /sys/class/net/bond1/bonding/slaves
-echo +eth3 > /sys/class/net/bond1/bonding/slaves
-
-3.5 Configuration with Interfaces Support
------------------------------------------
-
-        This section applies to distros which use /etc/network/interfaces file
-to describe network interface configuration, most notably Debian and it's
-derivatives.
-
-       The ifup and ifdown commands on Debian don't support bonding out of
-the box. The ifenslave-2.6 package should be installed to provide bonding
-support.  Once installed, this package will provide bond-* options to be used
-into /etc/network/interfaces.
-
-       Note that ifenslave-2.6 package will load the bonding module and use
-the ifenslave command when appropriate.
-
-Example Configurations
-----------------------
-
-In /etc/network/interfaces, the following stanza will configure bond0, in
-active-backup mode, with eth0 and eth1 as slaves.
-
-auto bond0
-iface bond0 inet dhcp
-       bond-slaves eth0 eth1
-       bond-mode active-backup
-       bond-miimon 100
-       bond-primary eth0 eth1
-
-If the above configuration doesn't work, you might have a system using
-upstart for system startup. This is most notably true for recent
-Ubuntu versions. The following stanza in /etc/network/interfaces will
-produce the same result on those systems.
-
-auto bond0
-iface bond0 inet dhcp
-       bond-slaves none
-       bond-mode active-backup
-       bond-miimon 100
-
-auto eth0
-iface eth0 inet manual
-       bond-master bond0
-       bond-primary eth0 eth1
-
-auto eth1
-iface eth1 inet manual
-       bond-master bond0
-       bond-primary eth0 eth1
-
-For a full list of bond-* supported options in /etc/network/interfaces and some
-more advanced examples tailored to you particular distros, see the files in
-/usr/share/doc/ifenslave-2.6.
-
-3.6 Overriding Configuration for Special Cases
-----------------------------------------------
-
-When using the bonding driver, the physical port which transmits a frame is
-typically selected by the bonding driver, and is not relevant to the user or
-system administrator.  The output port is simply selected using the policies of
-the selected bonding mode.  On occasion however, it is helpful to direct certain
-classes of traffic to certain physical interfaces on output to implement
-slightly more complex policies.  For example, to reach a web server over a
-bonded interface in which eth0 connects to a private network, while eth1
-connects via a public network, it may be desirous to bias the bond to send said
-traffic over eth0 first, using eth1 only as a fall back, while all other traffic
-can safely be sent over either interface.  Such configurations may be achieved
-using the traffic control utilities inherent in linux.
-
-By default the bonding driver is multiqueue aware and 16 queues are created
-when the driver initializes (see Documentation/networking/multiqueue.txt
-for details).  If more or less queues are desired the module parameter
-tx_queues can be used to change this value.  There is no sysfs parameter
-available as the allocation is done at module init time.
-
-The output of the file /proc/net/bonding/bondX has changed so the output Queue
-ID is now printed for each slave:
-
-Bonding Mode: fault-tolerance (active-backup)
-Primary Slave: None
-Currently Active Slave: eth0
-MII Status: up
-MII Polling Interval (ms): 0
-Up Delay (ms): 0
-Down Delay (ms): 0
-
-Slave Interface: eth0
-MII Status: up
-Link Failure Count: 0
-Permanent HW addr: 00:1a:a0:12:8f:cb
-Slave queue ID: 0
-
-Slave Interface: eth1
-MII Status: up
-Link Failure Count: 0
-Permanent HW addr: 00:1a:a0:12:8f:cc
-Slave queue ID: 2
-
-The queue_id for a slave can be set using the command:
-
-# echo "eth1:2" > /sys/class/net/bond0/bonding/queue_id
-
-Any interface that needs a queue_id set should set it with multiple calls
-like the one above until proper priorities are set for all interfaces.  On
-distributions that allow configuration via initscripts, multiple 'queue_id'
-arguments can be added to BONDING_OPTS to set all needed slave queues.
-
-These queue id's can be used in conjunction with the tc utility to configure
-a multiqueue qdisc and filters to bias certain traffic to transmit on certain
-slave devices.  For instance, say we wanted, in the above configuration to
-force all traffic bound to 192.168.1.100 to use eth1 in the bond as its output
-device. The following commands would accomplish this:
-
-# tc qdisc add dev bond0 handle 1 root multiq
-
-# tc filter add dev bond0 protocol ip parent 1: prio 1 u32 match ip dst \
-       192.168.1.100 action skbedit queue_mapping 2
-
-These commands tell the kernel to attach a multiqueue queue discipline to the
-bond0 interface and filter traffic enqueued to it, such that packets with a dst
-ip of 192.168.1.100 have their output queue mapping value overwritten to 2.
-This value is then passed into the driver, causing the normal output path
-selection policy to be overridden, selecting instead qid 2, which maps to eth1.
-
-Note that qid values begin at 1.  Qid 0 is reserved to initiate to the driver
-that normal output policy selection should take place.  One benefit to simply
-leaving the qid for a slave to 0 is the multiqueue awareness in the bonding
-driver that is now present.  This awareness allows tc filters to be placed on
-slave devices as well as bond devices and the bonding driver will simply act as
-a pass-through for selecting output queues on the slave device rather than 
-output port selection.
-
-This feature first appeared in bonding driver version 3.7.0 and support for
-output slave selection was limited to round-robin and active-backup modes.
-
-3.7 Configuring LACP for 802.3ad mode in a more secure way
-----------------------------------------------------------
-
-When using 802.3ad bonding mode, the Actor (host) and Partner (switch)
-exchange LACPDUs.  These LACPDUs cannot be sniffed, because they are
-destined to link local mac addresses (which switches/bridges are not
-supposed to forward).  However, most of the values are easily predictable
-or are simply the machine's MAC address (which is trivially known to all
-other hosts in the same L2).  This implies that other machines in the L2
-domain can spoof LACPDU packets from other hosts to the switch and potentially
-cause mayhem by joining (from the point of view of the switch) another
-machine's aggregate, thus receiving a portion of that hosts incoming
-traffic and / or spoofing traffic from that machine themselves (potentially
-even successfully terminating some portion of flows). Though this is not
-a likely scenario, one could avoid this possibility by simply configuring
-few bonding parameters:
-
-   (a) ad_actor_system : You can set a random mac-address that can be used for
-       these LACPDU exchanges. The value can not be either NULL or Multicast.
-       Also it's preferable to set the local-admin bit. Following shell code
-       generates a random mac-address as described above.
-
-       # sys_mac_addr=$(printf '%02x:%02x:%02x:%02x:%02x:%02x' \
-                                $(( (RANDOM & 0xFE) | 0x02 )) \
-                                $(( RANDOM & 0xFF )) \
-                                $(( RANDOM & 0xFF )) \
-                                $(( RANDOM & 0xFF )) \
-                                $(( RANDOM & 0xFF )) \
-                                $(( RANDOM & 0xFF )))
-       # echo $sys_mac_addr > /sys/class/net/bond0/bonding/ad_actor_system
-
-   (b) ad_actor_sys_prio : Randomize the system priority. The default value
-       is 65535, but system can take the value from 1 - 65535. Following shell
-       code generates random priority and sets it.
-
-       # sys_prio=$(( 1 + RANDOM + RANDOM ))
-       # echo $sys_prio > /sys/class/net/bond0/bonding/ad_actor_sys_prio
-
-   (c) ad_user_port_key : Use the user portion of the port-key. The default
-       keeps this empty. These are the upper 10 bits of the port-key and value
-       ranges from 0 - 1023. Following shell code generates these 10 bits and
-       sets it.
-
-       # usr_port_key=$(( RANDOM & 0x3FF ))
-       # echo $usr_port_key > /sys/class/net/bond0/bonding/ad_user_port_key
-
-
-4 Querying Bonding Configuration
-=================================
-
-4.1 Bonding Configuration
--------------------------
-
-       Each bonding device has a read-only file residing in the
-/proc/net/bonding directory.  The file contents include information
-about the bonding configuration, options and state of each slave.
-
-       For example, the contents of /proc/net/bonding/bond0 after the
-driver is loaded with parameters of mode=0 and miimon=1000 is
-generally as follows:
-
-       Ethernet Channel Bonding Driver: 2.6.1 (October 29, 2004)
-        Bonding Mode: load balancing (round-robin)
-        Currently Active Slave: eth0
-        MII Status: up
-        MII Polling Interval (ms): 1000
-        Up Delay (ms): 0
-        Down Delay (ms): 0
-
-        Slave Interface: eth1
-        MII Status: up
-        Link Failure Count: 1
-
-        Slave Interface: eth0
-        MII Status: up
-        Link Failure Count: 1
-
-       The precise format and contents will change depending upon the
-bonding configuration, state, and version of the bonding driver.
-
-4.2 Network configuration
--------------------------
-
-       The network configuration can be inspected using the ifconfig
-command.  Bonding devices will have the MASTER flag set; Bonding slave
-devices will have the SLAVE flag set.  The ifconfig output does not
-contain information on which slaves are associated with which masters.
-
-       In the example below, the bond0 interface is the master
-(MASTER) while eth0 and eth1 are slaves (SLAVE). Notice all slaves of
-bond0 have the same MAC address (HWaddr) as bond0 for all modes except
-TLB and ALB that require a unique MAC address for each slave.
-
-# /sbin/ifconfig
-bond0     Link encap:Ethernet  HWaddr 00:C0:F0:1F:37:B4
-          inet addr:XXX.XXX.XXX.YYY  Bcast:XXX.XXX.XXX.255  Mask:255.255.252.0
-          UP BROADCAST RUNNING MASTER MULTICAST  MTU:1500  Metric:1
-          RX packets:7224794 errors:0 dropped:0 overruns:0 frame:0
-          TX packets:3286647 errors:1 dropped:0 overruns:1 carrier:0
-          collisions:0 txqueuelen:0
-
-eth0      Link encap:Ethernet  HWaddr 00:C0:F0:1F:37:B4
-          UP BROADCAST RUNNING SLAVE MULTICAST  MTU:1500  Metric:1
-          RX packets:3573025 errors:0 dropped:0 overruns:0 frame:0
-          TX packets:1643167 errors:1 dropped:0 overruns:1 carrier:0
-          collisions:0 txqueuelen:100
-          Interrupt:10 Base address:0x1080
-
-eth1      Link encap:Ethernet  HWaddr 00:C0:F0:1F:37:B4
-          UP BROADCAST RUNNING SLAVE MULTICAST  MTU:1500  Metric:1
-          RX packets:3651769 errors:0 dropped:0 overruns:0 frame:0
-          TX packets:1643480 errors:0 dropped:0 overruns:0 carrier:0
-          collisions:0 txqueuelen:100
-          Interrupt:9 Base address:0x1400
-
-5. Switch Configuration
-=======================
-
-       For this section, "switch" refers to whatever system the
-bonded devices are directly connected to (i.e., where the other end of
-the cable plugs into).  This may be an actual dedicated switch device,
-or it may be another regular system (e.g., another computer running
-Linux),
-
-       The active-backup, balance-tlb and balance-alb modes do not
-require any specific configuration of the switch.
-
-       The 802.3ad mode requires that the switch have the appropriate
-ports configured as an 802.3ad aggregation.  The precise method used
-to configure this varies from switch to switch, but, for example, a
-Cisco 3550 series switch requires that the appropriate ports first be
-grouped together in a single etherchannel instance, then that
-etherchannel is set to mode "lacp" to enable 802.3ad (instead of
-standard EtherChannel).
-
-       The balance-rr, balance-xor and broadcast modes generally
-require that the switch have the appropriate ports grouped together.
-The nomenclature for such a group differs between switches, it may be
-called an "etherchannel" (as in the Cisco example, above), a "trunk
-group" or some other similar variation.  For these modes, each switch
-will also have its own configuration options for the switch's transmit
-policy to the bond.  Typical choices include XOR of either the MAC or
-IP addresses.  The transmit policy of the two peers does not need to
-match.  For these three modes, the bonding mode really selects a
-transmit policy for an EtherChannel group; all three will interoperate
-with another EtherChannel group.
-
-
-6. 802.1q VLAN Support
-======================
-
-       It is possible to configure VLAN devices over a bond interface
-using the 8021q driver.  However, only packets coming from the 8021q
-driver and passing through bonding will be tagged by default.  Self
-generated packets, for example, bonding's learning packets or ARP
-packets generated by either ALB mode or the ARP monitor mechanism, are
-tagged internally by bonding itself.  As a result, bonding must
-"learn" the VLAN IDs configured above it, and use those IDs to tag
-self generated packets.
-
-       For reasons of simplicity, and to support the use of adapters
-that can do VLAN hardware acceleration offloading, the bonding
-interface declares itself as fully hardware offloading capable, it gets
-the add_vid/kill_vid notifications to gather the necessary
-information, and it propagates those actions to the slaves.  In case
-of mixed adapter types, hardware accelerated tagged packets that
-should go through an adapter that is not offloading capable are
-"un-accelerated" by the bonding driver so the VLAN tag sits in the
-regular location.
-
-       VLAN interfaces *must* be added on top of a bonding interface
-only after enslaving at least one slave.  The bonding interface has a
-hardware address of 00:00:00:00:00:00 until the first slave is added.
-If the VLAN interface is created prior to the first enslavement, it
-would pick up the all-zeroes hardware address.  Once the first slave
-is attached to the bond, the bond device itself will pick up the
-slave's hardware address, which is then available for the VLAN device.
-
-       Also, be aware that a similar problem can occur if all slaves
-are released from a bond that still has one or more VLAN interfaces on
-top of it.  When a new slave is added, the bonding interface will
-obtain its hardware address from the first slave, which might not
-match the hardware address of the VLAN interfaces (which was
-ultimately copied from an earlier slave).
-
-       There are two methods to insure that the VLAN device operates
-with the correct hardware address if all slaves are removed from a
-bond interface:
-
-       1. Remove all VLAN interfaces then recreate them
-
-       2. Set the bonding interface's hardware address so that it
-matches the hardware address of the VLAN interfaces.
-
-       Note that changing a VLAN interface's HW address would set the
-underlying device -- i.e. the bonding interface -- to promiscuous
-mode, which might not be what you want.
-
-
-7. Link Monitoring
-==================
-
-       The bonding driver at present supports two schemes for
-monitoring a slave device's link state: the ARP monitor and the MII
-monitor.
-
-       At the present time, due to implementation restrictions in the
-bonding driver itself, it is not possible to enable both ARP and MII
-monitoring simultaneously.
-
-7.1 ARP Monitor Operation
--------------------------
-
-       The ARP monitor operates as its name suggests: it sends ARP
-queries to one or more designated peer systems on the network, and
-uses the response as an indication that the link is operating.  This
-gives some assurance that traffic is actually flowing to and from one
-or more peers on the local network.
-
-       The ARP monitor relies on the device driver itself to verify
-that traffic is flowing.  In particular, the driver must keep up to
-date the last receive time, dev->last_rx.  Drivers that use NETIF_F_LLTX
-flag must also update netdev_queue->trans_start.  If they do not, then the
-ARP monitor will immediately fail any slaves using that driver, and
-those slaves will stay down.  If networking monitoring (tcpdump, etc)
-shows the ARP requests and replies on the network, then it may be that
-your device driver is not updating last_rx and trans_start.
-
-7.2 Configuring Multiple ARP Targets
-------------------------------------
-
-       While ARP monitoring can be done with just one target, it can
-be useful in a High Availability setup to have several targets to
-monitor.  In the case of just one target, the target itself may go
-down or have a problem making it unresponsive to ARP requests.  Having
-an additional target (or several) increases the reliability of the ARP
-monitoring.
-
-       Multiple ARP targets must be separated by commas as follows:
-
-# example options for ARP monitoring with three targets
-alias bond0 bonding
-options bond0 arp_interval=60 arp_ip_target=192.168.0.1,192.168.0.3,192.168.0.9
-
-       For just a single target the options would resemble:
-
-# example options for ARP monitoring with one target
-alias bond0 bonding
-options bond0 arp_interval=60 arp_ip_target=192.168.0.100
-
-
-7.3 MII Monitor Operation
--------------------------
-
-       The MII monitor monitors only the carrier state of the local
-network interface.  It accomplishes this in one of three ways: by
-depending upon the device driver to maintain its carrier state, by
-querying the device's MII registers, or by making an ethtool query to
-the device.
-
-       If the use_carrier module parameter is 1 (the default value),
-then the MII monitor will rely on the driver for carrier state
-information (via the netif_carrier subsystem).  As explained in the
-use_carrier parameter information, above, if the MII monitor fails to
-detect carrier loss on the device (e.g., when the cable is physically
-disconnected), it may be that the driver does not support
-netif_carrier.
-
-       If use_carrier is 0, then the MII monitor will first query the
-device's (via ioctl) MII registers and check the link state.  If that
-request fails (not just that it returns carrier down), then the MII
-monitor will make an ethtool ETHOOL_GLINK request to attempt to obtain
-the same information.  If both methods fail (i.e., the driver either
-does not support or had some error in processing both the MII register
-and ethtool requests), then the MII monitor will assume the link is
-up.
-
-8. Potential Sources of Trouble
-===============================
-
-8.1 Adventures in Routing
--------------------------
-
-       When bonding is configured, it is important that the slave
-devices not have routes that supersede routes of the master (or,
-generally, not have routes at all).  For example, suppose the bonding
-device bond0 has two slaves, eth0 and eth1, and the routing table is
-as follows:
-
-Kernel IP routing table
-Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
-10.0.0.0        0.0.0.0         255.255.0.0     U        40 0          0 eth0
-10.0.0.0        0.0.0.0         255.255.0.0     U        40 0          0 eth1
-10.0.0.0        0.0.0.0         255.255.0.0     U        40 0          0 bond0
-127.0.0.0       0.0.0.0         255.0.0.0       U        40 0          0 lo
-
-       This routing configuration will likely still update the
-receive/transmit times in the driver (needed by the ARP monitor), but
-may bypass the bonding driver (because outgoing traffic to, in this
-case, another host on network 10 would use eth0 or eth1 before bond0).
-
-       The ARP monitor (and ARP itself) may become confused by this
-configuration, because ARP requests (generated by the ARP monitor)
-will be sent on one interface (bond0), but the corresponding reply
-will arrive on a different interface (eth0).  This reply looks to ARP
-as an unsolicited ARP reply (because ARP matches replies on an
-interface basis), and is discarded.  The MII monitor is not affected
-by the state of the routing table.
-
-       The solution here is simply to insure that slaves do not have
-routes of their own, and if for some reason they must, those routes do
-not supersede routes of their master.  This should generally be the
-case, but unusual configurations or errant manual or automatic static
-route additions may cause trouble.
-
-8.2 Ethernet Device Renaming
-----------------------------
-
-       On systems with network configuration scripts that do not
-associate physical devices directly with network interface names (so
-that the same physical device always has the same "ethX" name), it may
-be necessary to add some special logic to config files in
-/etc/modprobe.d/.
-
-       For example, given a modules.conf containing the following:
-
-alias bond0 bonding
-options bond0 mode=some-mode miimon=50
-alias eth0 tg3
-alias eth1 tg3
-alias eth2 e1000
-alias eth3 e1000
-
-       If neither eth0 and eth1 are slaves to bond0, then when the
-bond0 interface comes up, the devices may end up reordered.  This
-happens because bonding is loaded first, then its slave device's
-drivers are loaded next.  Since no other drivers have been loaded,
-when the e1000 driver loads, it will receive eth0 and eth1 for its
-devices, but the bonding configuration tries to enslave eth2 and eth3
-(which may later be assigned to the tg3 devices).
-
-       Adding the following:
-
-add above bonding e1000 tg3
-
-       causes modprobe to load e1000 then tg3, in that order, when
-bonding is loaded.  This command is fully documented in the
-modules.conf manual page.
-
-       On systems utilizing modprobe an equivalent problem can occur.
-In this case, the following can be added to config files in
-/etc/modprobe.d/ as:
-
-softdep bonding pre: tg3 e1000
-
-       This will load tg3 and e1000 modules before loading the bonding one.
-Full documentation on this can be found in the modprobe.d and modprobe
-manual pages.
-
-8.3. Painfully Slow Or No Failed Link Detection By Miimon
----------------------------------------------------------
-
-       By default, bonding enables the use_carrier option, which
-instructs bonding to trust the driver to maintain carrier state.
-
-       As discussed in the options section, above, some drivers do
-not support the netif_carrier_on/_off link state tracking system.
-With use_carrier enabled, bonding will always see these links as up,
-regardless of their actual state.
-
-       Additionally, other drivers do support netif_carrier, but do
-not maintain it in real time, e.g., only polling the link state at
-some fixed interval.  In this case, miimon will detect failures, but
-only after some long period of time has expired.  If it appears that
-miimon is very slow in detecting link failures, try specifying
-use_carrier=0 to see if that improves the failure detection time.  If
-it does, then it may be that the driver checks the carrier state at a
-fixed interval, but does not cache the MII register values (so the
-use_carrier=0 method of querying the registers directly works).  If
-use_carrier=0 does not improve the failover, then the driver may cache
-the registers, or the problem may be elsewhere.
-
-       Also, remember that miimon only checks for the device's
-carrier state.  It has no way to determine the state of devices on or
-beyond other ports of a switch, or if a switch is refusing to pass
-traffic while still maintaining carrier on.
-
-9. SNMP agents
-===============
-
-       If running SNMP agents, the bonding driver should be loaded
-before any network drivers participating in a bond.  This requirement
-is due to the interface index (ipAdEntIfIndex) being associated to
-the first interface found with a given IP address.  That is, there is
-only one ipAdEntIfIndex for each IP address.  For example, if eth0 and
-eth1 are slaves of bond0 and the driver for eth0 is loaded before the
-bonding driver, the interface for the IP address will be associated
-with the eth0 interface.  This configuration is shown below, the IP
-address 192.168.1.1 has an interface index of 2 which indexes to eth0
-in the ifDescr table (ifDescr.2).
-
-     interfaces.ifTable.ifEntry.ifDescr.1 = lo
-     interfaces.ifTable.ifEntry.ifDescr.2 = eth0
-     interfaces.ifTable.ifEntry.ifDescr.3 = eth1
-     interfaces.ifTable.ifEntry.ifDescr.4 = eth2
-     interfaces.ifTable.ifEntry.ifDescr.5 = eth3
-     interfaces.ifTable.ifEntry.ifDescr.6 = bond0
-     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.10.10.10 = 5
-     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.192.168.1.1 = 2
-     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.74.20.94 = 4
-     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.127.0.0.1 = 1
-
-       This problem is avoided by loading the bonding driver before
-any network drivers participating in a bond.  Below is an example of
-loading the bonding driver first, the IP address 192.168.1.1 is
-correctly associated with ifDescr.2.
-
-     interfaces.ifTable.ifEntry.ifDescr.1 = lo
-     interfaces.ifTable.ifEntry.ifDescr.2 = bond0
-     interfaces.ifTable.ifEntry.ifDescr.3 = eth0
-     interfaces.ifTable.ifEntry.ifDescr.4 = eth1
-     interfaces.ifTable.ifEntry.ifDescr.5 = eth2
-     interfaces.ifTable.ifEntry.ifDescr.6 = eth3
-     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.10.10.10 = 6
-     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.192.168.1.1 = 2
-     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.74.20.94 = 5
-     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.127.0.0.1 = 1
-
-       While some distributions may not report the interface name in
-ifDescr, the association between the IP address and IfIndex remains
-and SNMP functions such as Interface_Scan_Next will report that
-association.
-
-10. Promiscuous mode
-====================
-
-       When running network monitoring tools, e.g., tcpdump, it is
-common to enable promiscuous mode on the device, so that all traffic
-is seen (instead of seeing only traffic destined for the local host).
-The bonding driver handles promiscuous mode changes to the bonding
-master device (e.g., bond0), and propagates the setting to the slave
-devices.
-
-       For the balance-rr, balance-xor, broadcast, and 802.3ad modes,
-the promiscuous mode setting is propagated to all slaves.
-
-       For the active-backup, balance-tlb and balance-alb modes, the
-promiscuous mode setting is propagated only to the active slave.
-
-       For balance-tlb mode, the active slave is the slave currently
-receiving inbound traffic.
-
-       For balance-alb mode, the active slave is the slave used as a
-"primary."  This slave is used for mode-specific control traffic, for
-sending to peers that are unassigned or if the load is unbalanced.
-
-       For the active-backup, balance-tlb and balance-alb modes, when
-the active slave changes (e.g., due to a link failure), the
-promiscuous setting will be propagated to the new active slave.
-
-11. Configuring Bonding for High Availability
-=============================================
-
-       High Availability refers to configurations that provide
-maximum network availability by having redundant or backup devices,
-links or switches between the host and the rest of the world.  The
-goal is to provide the maximum availability of network connectivity
-(i.e., the network always works), even though other configurations
-could provide higher throughput.
-
-11.1 High Availability in a Single Switch Topology
---------------------------------------------------
-
-       If two hosts (or a host and a single switch) are directly
-connected via multiple physical links, then there is no availability
-penalty to optimizing for maximum bandwidth.  In this case, there is
-only one switch (or peer), so if it fails, there is no alternative
-access to fail over to.  Additionally, the bonding load balance modes
-support link monitoring of their members, so if individual links fail,
-the load will be rebalanced across the remaining devices.
-
-       See Section 12, "Configuring Bonding for Maximum Throughput"
-for information on configuring bonding with one peer device.
-
-11.2 High Availability in a Multiple Switch Topology
-----------------------------------------------------
-
-       With multiple switches, the configuration of bonding and the
-network changes dramatically.  In multiple switch topologies, there is
-a trade off between network availability and usable bandwidth.
-
-       Below is a sample network, configured to maximize the
-availability of the network:
-
-                |                                     |
-                |port3                           port3|
-          +-----+----+                          +-----+----+
-          |          |port2       ISL      port2|          |
-          | switch A +--------------------------+ switch B |
-          |          |                          |          |
-          +-----+----+                          +-----++---+
-                |port1                           port1|
-                |             +-------+               |
-                +-------------+ host1 +---------------+
-                         eth0 +-------+ eth1
-
-       In this configuration, there is a link between the two
-switches (ISL, or inter switch link), and multiple ports connecting to
-the outside world ("port3" on each switch).  There is no technical
-reason that this could not be extended to a third switch.
-
-11.2.1 HA Bonding Mode Selection for Multiple Switch Topology
--------------------------------------------------------------
-
-       In a topology such as the example above, the active-backup and
-broadcast modes are the only useful bonding modes when optimizing for
-availability; the other modes require all links to terminate on the
-same peer for them to behave rationally.
-
-active-backup: This is generally the preferred mode, particularly if
-       the switches have an ISL and play together well.  If the
-       network configuration is such that one switch is specifically
-       a backup switch (e.g., has lower capacity, higher cost, etc),
-       then the primary option can be used to insure that the
-       preferred link is always used when it is available.
-
-broadcast: This mode is really a special purpose mode, and is suitable
-       only for very specific needs.  For example, if the two
-       switches are not connected (no ISL), and the networks beyond
-       them are totally independent.  In this case, if it is
-       necessary for some specific one-way traffic to reach both
-       independent networks, then the broadcast mode may be suitable.
-
-11.2.2 HA Link Monitoring Selection for Multiple Switch Topology
-----------------------------------------------------------------
-
-       The choice of link monitoring ultimately depends upon your
-switch.  If the switch can reliably fail ports in response to other
-failures, then either the MII or ARP monitors should work.  For
-example, in the above example, if the "port3" link fails at the remote
-end, the MII monitor has no direct means to detect this.  The ARP
-monitor could be configured with a target at the remote end of port3,
-thus detecting that failure without switch support.
-
-       In general, however, in a multiple switch topology, the ARP
-monitor can provide a higher level of reliability in detecting end to
-end connectivity failures (which may be caused by the failure of any
-individual component to pass traffic for any reason).  Additionally,
-the ARP monitor should be configured with multiple targets (at least
-one for each switch in the network).  This will insure that,
-regardless of which switch is active, the ARP monitor has a suitable
-target to query.
-
-       Note, also, that of late many switches now support a functionality
-generally referred to as "trunk failover."  This is a feature of the
-switch that causes the link state of a particular switch port to be set
-down (or up) when the state of another switch port goes down (or up).
-Its purpose is to propagate link failures from logically "exterior" ports
-to the logically "interior" ports that bonding is able to monitor via
-miimon.  Availability and configuration for trunk failover varies by
-switch, but this can be a viable alternative to the ARP monitor when using
-suitable switches.
-
-12. Configuring Bonding for Maximum Throughput
-==============================================
-
-12.1 Maximizing Throughput in a Single Switch Topology
-------------------------------------------------------
-
-       In a single switch configuration, the best method to maximize
-throughput depends upon the application and network environment.  The
-various load balancing modes each have strengths and weaknesses in
-different environments, as detailed below.
-
-       For this discussion, we will break down the topologies into
-two categories.  Depending upon the destination of most traffic, we
-categorize them into either "gatewayed" or "local" configurations.
-
-       In a gatewayed configuration, the "switch" is acting primarily
-as a router, and the majority of traffic passes through this router to
-other networks.  An example would be the following:
-
-
-     +----------+                     +----------+
-     |          |eth0            port1|          | to other networks
-     | Host A   +---------------------+ router   +------------------->
-     |          +---------------------+          | Hosts B and C are out
-     |          |eth1            port2|          | here somewhere
-     +----------+                     +----------+
-
-       The router may be a dedicated router device, or another host
-acting as a gateway.  For our discussion, the important point is that
-the majority of traffic from Host A will pass through the router to
-some other network before reaching its final destination.
-
-       In a gatewayed network configuration, although Host A may
-communicate with many other systems, all of its traffic will be sent
-and received via one other peer on the local network, the router.
-
-       Note that the case of two systems connected directly via
-multiple physical links is, for purposes of configuring bonding, the
-same as a gatewayed configuration.  In that case, it happens that all
-traffic is destined for the "gateway" itself, not some other network
-beyond the gateway.
-
-       In a local configuration, the "switch" is acting primarily as
-a switch, and the majority of traffic passes through this switch to
-reach other stations on the same network.  An example would be the
-following:
-
-    +----------+            +----------+       +--------+
-    |          |eth0   port1|          +-------+ Host B |
-    |  Host A  +------------+  switch  |port3  +--------+
-    |          +------------+          |                  +--------+
-    |          |eth1   port2|          +------------------+ Host C |
-    +----------+            +----------+port4             +--------+
-
-
-       Again, the switch may be a dedicated switch device, or another
-host acting as a gateway.  For our discussion, the important point is
-that the majority of traffic from Host A is destined for other hosts
-on the same local network (Hosts B and C in the above example).
-
-       In summary, in a gatewayed configuration, traffic to and from
-the bonded device will be to the same MAC level peer on the network
-(the gateway itself, i.e., the router), regardless of its final
-destination.  In a local configuration, traffic flows directly to and
-from the final destinations, thus, each destination (Host B, Host C)
-will be addressed directly by their individual MAC addresses.
-
-       This distinction between a gatewayed and a local network
-configuration is important because many of the load balancing modes
-available use the MAC addresses of the local network source and
-destination to make load balancing decisions.  The behavior of each
-mode is described below.
-
-
-12.1.1 MT Bonding Mode Selection for Single Switch Topology
------------------------------------------------------------
-
-       This configuration is the easiest to set up and to understand,
-although you will have to decide which bonding mode best suits your
-needs.  The trade offs for each mode are detailed below:
-
-balance-rr: This mode is the only mode that will permit a single
-       TCP/IP connection to stripe traffic across multiple
-       interfaces. It is therefore the only mode that will allow a
-       single TCP/IP stream to utilize more than one interface's
-       worth of throughput.  This comes at a cost, however: the
-       striping generally results in peer systems receiving packets out
-       of order, causing TCP/IP's congestion control system to kick
-       in, often by retransmitting segments.
-
-       It is possible to adjust TCP/IP's congestion limits by
-       altering the net.ipv4.tcp_reordering sysctl parameter.  The
-       usual default value is 3. But keep in mind TCP stack is able
-       to automatically increase this when it detects reorders.
-
-       Note that the fraction of packets that will be delivered out of
-       order is highly variable, and is unlikely to be zero.  The level
-       of reordering depends upon a variety of factors, including the
-       networking interfaces, the switch, and the topology of the
-       configuration.  Speaking in general terms, higher speed network
-       cards produce more reordering (due to factors such as packet
-       coalescing), and a "many to many" topology will reorder at a
-       higher rate than a "many slow to one fast" configuration.
-
-       Many switches do not support any modes that stripe traffic
-       (instead choosing a port based upon IP or MAC level addresses);
-       for those devices, traffic for a particular connection flowing
-       through the switch to a balance-rr bond will not utilize greater
-       than one interface's worth of bandwidth.
-
-       If you are utilizing protocols other than TCP/IP, UDP for
-       example, and your application can tolerate out of order
-       delivery, then this mode can allow for single stream datagram
-       performance that scales near linearly as interfaces are added
-       to the bond.
-
-       This mode requires the switch to have the appropriate ports
-       configured for "etherchannel" or "trunking."
-
-active-backup: There is not much advantage in this network topology to
-       the active-backup mode, as the inactive backup devices are all
-       connected to the same peer as the primary.  In this case, a
-       load balancing mode (with link monitoring) will provide the
-       same level of network availability, but with increased
-       available bandwidth.  On the plus side, active-backup mode
-       does not require any configuration of the switch, so it may
-       have value if the hardware available does not support any of
-       the load balance modes.
-
-balance-xor: This mode will limit traffic such that packets destined
-       for specific peers will always be sent over the same
-       interface.  Since the destination is determined by the MAC
-       addresses involved, this mode works best in a "local" network
-       configuration (as described above), with destinations all on
-       the same local network.  This mode is likely to be suboptimal
-       if all your traffic is passed through a single router (i.e., a
-       "gatewayed" network configuration, as described above).
-
-       As with balance-rr, the switch ports need to be configured for
-       "etherchannel" or "trunking."
-
-broadcast: Like active-backup, there is not much advantage to this
-       mode in this type of network topology.
-
-802.3ad: This mode can be a good choice for this type of network
-       topology.  The 802.3ad mode is an IEEE standard, so all peers
-       that implement 802.3ad should interoperate well.  The 802.3ad
-       protocol includes automatic configuration of the aggregates,
-       so minimal manual configuration of the switch is needed
-       (typically only to designate that some set of devices is
-       available for 802.3ad).  The 802.3ad standard also mandates
-       that frames be delivered in order (within certain limits), so
-       in general single connections will not see misordering of
-       packets.  The 802.3ad mode does have some drawbacks: the
-       standard mandates that all devices in the aggregate operate at
-       the same speed and duplex.  Also, as with all bonding load
-       balance modes other than balance-rr, no single connection will
-       be able to utilize more than a single interface's worth of
-       bandwidth.  
-
-       Additionally, the linux bonding 802.3ad implementation
-       distributes traffic by peer (using an XOR of MAC addresses
-       and packet type ID), so in a "gatewayed" configuration, all
-       outgoing traffic will generally use the same device.  Incoming
-       traffic may also end up on a single device, but that is
-       dependent upon the balancing policy of the peer's 802.3ad
-       implementation.  In a "local" configuration, traffic will be
-       distributed across the devices in the bond.
-
-       Finally, the 802.3ad mode mandates the use of the MII monitor,
-       therefore, the ARP monitor is not available in this mode.
-
-balance-tlb: The balance-tlb mode balances outgoing traffic by peer.
-       Since the balancing is done according to MAC address, in a
-       "gatewayed" configuration (as described above), this mode will
-       send all traffic across a single device.  However, in a
-       "local" network configuration, this mode balances multiple
-       local network peers across devices in a vaguely intelligent
-       manner (not a simple XOR as in balance-xor or 802.3ad mode),
-       so that mathematically unlucky MAC addresses (i.e., ones that
-       XOR to the same value) will not all "bunch up" on a single
-       interface.
-
-       Unlike 802.3ad, interfaces may be of differing speeds, and no
-       special switch configuration is required.  On the down side,
-       in this mode all incoming traffic arrives over a single
-       interface, this mode requires certain ethtool support in the
-       network device driver of the slave interfaces, and the ARP
-       monitor is not available.
-
-balance-alb: This mode is everything that balance-tlb is, and more.
-       It has all of the features (and restrictions) of balance-tlb,
-       and will also balance incoming traffic from local network
-       peers (as described in the Bonding Module Options section,
-       above).
-
-       The only additional down side to this mode is that the network
-       device driver must support changing the hardware address while
-       the device is open.
-
-12.1.2 MT Link Monitoring for Single Switch Topology
-----------------------------------------------------
-
-       The choice of link monitoring may largely depend upon which
-mode you choose to use.  The more advanced load balancing modes do not
-support the use of the ARP monitor, and are thus restricted to using
-the MII monitor (which does not provide as high a level of end to end
-assurance as the ARP monitor).
-
-12.2 Maximum Throughput in a Multiple Switch Topology
------------------------------------------------------
-
-       Multiple switches may be utilized to optimize for throughput
-when they are configured in parallel as part of an isolated network
-between two or more systems, for example:
-
-                       +-----------+
-                       |  Host A   | 
-                       +-+---+---+-+
-                         |   |   |
-                +--------+   |   +---------+
-                |            |             |
-         +------+---+  +-----+----+  +-----+----+
-         | Switch A |  | Switch B |  | Switch C |
-         +------+---+  +-----+----+  +-----+----+
-                |            |             |
-                +--------+   |   +---------+
-                         |   |   |
-                       +-+---+---+-+
-                       |  Host B   | 
-                       +-----------+
-
-       In this configuration, the switches are isolated from one
-another.  One reason to employ a topology such as this is for an
-isolated network with many hosts (a cluster configured for high
-performance, for example), using multiple smaller switches can be more
-cost effective than a single larger switch, e.g., on a network with 24
-hosts, three 24 port switches can be significantly less expensive than
-a single 72 port switch.
-
-       If access beyond the network is required, an individual host
-can be equipped with an additional network device connected to an
-external network; this host then additionally acts as a gateway.
-
-12.2.1 MT Bonding Mode Selection for Multiple Switch Topology
--------------------------------------------------------------
-
-       In actual practice, the bonding mode typically employed in
-configurations of this type is balance-rr.  Historically, in this
-network configuration, the usual caveats about out of order packet
-delivery are mitigated by the use of network adapters that do not do
-any kind of packet coalescing (via the use of NAPI, or because the
-device itself does not generate interrupts until some number of
-packets has arrived).  When employed in this fashion, the balance-rr
-mode allows individual connections between two hosts to effectively
-utilize greater than one interface's bandwidth.
-
-12.2.2 MT Link Monitoring for Multiple Switch Topology
-------------------------------------------------------
-
-       Again, in actual practice, the MII monitor is most often used
-in this configuration, as performance is given preference over
-availability.  The ARP monitor will function in this topology, but its
-advantages over the MII monitor are mitigated by the volume of probes
-needed as the number of systems involved grows (remember that each
-host in the network is configured with bonding).
-
-13. Switch Behavior Issues
-==========================
-
-13.1 Link Establishment and Failover Delays
--------------------------------------------
-
-       Some switches exhibit undesirable behavior with regard to the
-timing of link up and down reporting by the switch.
-
-       First, when a link comes up, some switches may indicate that
-the link is up (carrier available), but not pass traffic over the
-interface for some period of time.  This delay is typically due to
-some type of autonegotiation or routing protocol, but may also occur
-during switch initialization (e.g., during recovery after a switch
-failure).  If you find this to be a problem, specify an appropriate
-value to the updelay bonding module option to delay the use of the
-relevant interface(s).
-
-       Second, some switches may "bounce" the link state one or more
-times while a link is changing state.  This occurs most commonly while
-the switch is initializing.  Again, an appropriate updelay value may
-help.
-
-       Note that when a bonding interface has no active links, the
-driver will immediately reuse the first link that goes up, even if the
-updelay parameter has been specified (the updelay is ignored in this
-case).  If there are slave interfaces waiting for the updelay timeout
-to expire, the interface that first went into that state will be
-immediately reused.  This reduces down time of the network if the
-value of updelay has been overestimated, and since this occurs only in
-cases with no connectivity, there is no additional penalty for
-ignoring the updelay.
-
-       In addition to the concerns about switch timings, if your
-switches take a long time to go into backup mode, it may be desirable
-to not activate a backup interface immediately after a link goes down.
-Failover may be delayed via the downdelay bonding module option.
-
-13.2 Duplicated Incoming Packets
---------------------------------
-
-       NOTE: Starting with version 3.0.2, the bonding driver has logic to
-suppress duplicate packets, which should largely eliminate this problem.
-The following description is kept for reference.
-
-       It is not uncommon to observe a short burst of duplicated
-traffic when the bonding device is first used, or after it has been
-idle for some period of time.  This is most easily observed by issuing
-a "ping" to some other host on the network, and noticing that the
-output from ping flags duplicates (typically one per slave).
-
-       For example, on a bond in active-backup mode with five slaves
-all connected to one switch, the output may appear as follows:
-
-# ping -n 10.0.4.2
-PING 10.0.4.2 (10.0.4.2) from 10.0.3.10 : 56(84) bytes of data.
-64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.7 ms
-64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.8 ms (DUP!)
-64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.8 ms (DUP!)
-64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.8 ms (DUP!)
-64 bytes from 10.0.4.2: icmp_seq=1 ttl=64 time=13.8 ms (DUP!)
-64 bytes from 10.0.4.2: icmp_seq=2 ttl=64 time=0.216 ms
-64 bytes from 10.0.4.2: icmp_seq=3 ttl=64 time=0.267 ms
-64 bytes from 10.0.4.2: icmp_seq=4 ttl=64 time=0.222 ms
-
-       This is not due to an error in the bonding driver, rather, it
-is a side effect of how many switches update their MAC forwarding
-tables.  Initially, the switch does not associate the MAC address in
-the packet with a particular switch port, and so it may send the
-traffic to all ports until its MAC forwarding table is updated.  Since
-the interfaces attached to the bond may occupy multiple ports on a
-single switch, when the switch (temporarily) floods the traffic to all
-ports, the bond device receives multiple copies of the same packet
-(one per slave device).
-
-       The duplicated packet behavior is switch dependent, some
-switches exhibit this, and some do not.  On switches that display this
-behavior, it can be induced by clearing the MAC forwarding table (on
-most Cisco switches, the privileged command "clear mac address-table
-dynamic" will accomplish this).
-
-14. Hardware Specific Considerations
-====================================
-
-       This section contains additional information for configuring
-bonding on specific hardware platforms, or for interfacing bonding
-with particular switches or other devices.
-
-14.1 IBM BladeCenter
---------------------
-
-       This applies to the JS20 and similar systems.
-
-       On the JS20 blades, the bonding driver supports only
-balance-rr, active-backup, balance-tlb and balance-alb modes.  This is
-largely due to the network topology inside the BladeCenter, detailed
-below.
-
-JS20 network adapter information
---------------------------------
-
-       All JS20s come with two Broadcom Gigabit Ethernet ports
-integrated on the planar (that's "motherboard" in IBM-speak).  In the
-BladeCenter chassis, the eth0 port of all JS20 blades is hard wired to
-I/O Module #1; similarly, all eth1 ports are wired to I/O Module #2.
-An add-on Broadcom daughter card can be installed on a JS20 to provide
-two more Gigabit Ethernet ports.  These ports, eth2 and eth3, are
-wired to I/O Modules 3 and 4, respectively.
-
-       Each I/O Module may contain either a switch or a passthrough
-module (which allows ports to be directly connected to an external
-switch).  Some bonding modes require a specific BladeCenter internal
-network topology in order to function; these are detailed below.
-
-       Additional BladeCenter-specific networking information can be
-found in two IBM Redbooks (www.ibm.com/redbooks):
-
-"IBM eServer BladeCenter Networking Options"
-"IBM eServer BladeCenter Layer 2-7 Network Switching"
-
-BladeCenter networking configuration
-------------------------------------
-
-       Because a BladeCenter can be configured in a very large number
-of ways, this discussion will be confined to describing basic
-configurations.
-
-       Normally, Ethernet Switch Modules (ESMs) are used in I/O
-modules 1 and 2.  In this configuration, the eth0 and eth1 ports of a
-JS20 will be connected to different internal switches (in the
-respective I/O modules).
-
-       A passthrough module (OPM or CPM, optical or copper,
-passthrough module) connects the I/O module directly to an external
-switch.  By using PMs in I/O module #1 and #2, the eth0 and eth1
-interfaces of a JS20 can be redirected to the outside world and
-connected to a common external switch.
-
-       Depending upon the mix of ESMs and PMs, the network will
-appear to bonding as either a single switch topology (all PMs) or as a
-multiple switch topology (one or more ESMs, zero or more PMs).  It is
-also possible to connect ESMs together, resulting in a configuration
-much like the example in "High Availability in a Multiple Switch
-Topology," above.
-
-Requirements for specific modes
--------------------------------
-
-       The balance-rr mode requires the use of passthrough modules
-for devices in the bond, all connected to an common external switch.
-That switch must be configured for "etherchannel" or "trunking" on the
-appropriate ports, as is usual for balance-rr.
-
-       The balance-alb and balance-tlb modes will function with
-either switch modules or passthrough modules (or a mix).  The only
-specific requirement for these modes is that all network interfaces
-must be able to reach all destinations for traffic sent over the
-bonding device (i.e., the network must converge at some point outside
-the BladeCenter).
-
-       The active-backup mode has no additional requirements.
-
-Link monitoring issues
-----------------------
-
-       When an Ethernet Switch Module is in place, only the ARP
-monitor will reliably detect link loss to an external switch.  This is
-nothing unusual, but examination of the BladeCenter cabinet would
-suggest that the "external" network ports are the ethernet ports for
-the system, when it fact there is a switch between these "external"
-ports and the devices on the JS20 system itself.  The MII monitor is
-only able to detect link failures between the ESM and the JS20 system.
-
-       When a passthrough module is in place, the MII monitor does
-detect failures to the "external" port, which is then directly
-connected to the JS20 system.
-
-Other concerns
---------------
-
-       The Serial Over LAN (SoL) link is established over the primary
-ethernet (eth0) only, therefore, any loss of link to eth0 will result
-in losing your SoL connection.  It will not fail over with other
-network traffic, as the SoL system is beyond the control of the
-bonding driver.
-
-       It may be desirable to disable spanning tree on the switch
-(either the internal Ethernet Switch Module, or an external switch) to
-avoid fail-over delay issues when using bonding.
-
-       
-15. Frequently Asked Questions
-==============================
-
-1.  Is it SMP safe?
-
-       Yes. The old 2.0.xx channel bonding patch was not SMP safe.
-The new driver was designed to be SMP safe from the start.
-
-2.  What type of cards will work with it?
-
-       Any Ethernet type cards (you can even mix cards - a Intel
-EtherExpress PRO/100 and a 3com 3c905b, for example).  For most modes,
-devices need not be of the same speed.
-
-       Starting with version 3.2.1, bonding also supports Infiniband
-slaves in active-backup mode.
-
-3.  How many bonding devices can I have?
-
-       There is no limit.
-
-4.  How many slaves can a bonding device have?
-
-       This is limited only by the number of network interfaces Linux
-supports and/or the number of network cards you can place in your
-system.
-
-5.  What happens when a slave link dies?
-
-       If link monitoring is enabled, then the failing device will be
-disabled.  The active-backup mode will fail over to a backup link, and
-other modes will ignore the failed link.  The link will continue to be
-monitored, and should it recover, it will rejoin the bond (in whatever
-manner is appropriate for the mode). See the sections on High
-Availability and the documentation for each mode for additional
-information.
-       
-       Link monitoring can be enabled via either the miimon or
-arp_interval parameters (described in the module parameters section,
-above).  In general, miimon monitors the carrier state as sensed by
-the underlying network device, and the arp monitor (arp_interval)
-monitors connectivity to another host on the local network.
-
-       If no link monitoring is configured, the bonding driver will
-be unable to detect link failures, and will assume that all links are
-always available.  This will likely result in lost packets, and a
-resulting degradation of performance.  The precise performance loss
-depends upon the bonding mode and network configuration.
-
-6.  Can bonding be used for High Availability?
-
-       Yes.  See the section on High Availability for details.
-
-7.  Which switches/systems does it work with?
-
-       The full answer to this depends upon the desired mode.
-
-       In the basic balance modes (balance-rr and balance-xor), it
-works with any system that supports etherchannel (also called
-trunking).  Most managed switches currently available have such
-support, and many unmanaged switches as well.
-
-       The advanced balance modes (balance-tlb and balance-alb) do
-not have special switch requirements, but do need device drivers that
-support specific features (described in the appropriate section under
-module parameters, above).
-
-       In 802.3ad mode, it works with systems that support IEEE
-802.3ad Dynamic Link Aggregation.  Most managed and many unmanaged
-switches currently available support 802.3ad.
-
-        The active-backup mode should work with any Layer-II switch.
-
-8.  Where does a bonding device get its MAC address from?
-
-       When using slave devices that have fixed MAC addresses, or when
-the fail_over_mac option is enabled, the bonding device's MAC address is
-the MAC address of the active slave.
-
-       For other configurations, if not explicitly configured (with
-ifconfig or ip link), the MAC address of the bonding device is taken from
-its first slave device.  This MAC address is then passed to all following
-slaves and remains persistent (even if the first slave is removed) until
-the bonding device is brought down or reconfigured.
-
-       If you wish to change the MAC address, you can set it with
-ifconfig or ip link:
-
-# ifconfig bond0 hw ether 00:11:22:33:44:55
-
-# ip link set bond0 address 66:77:88:99:aa:bb
-
-       The MAC address can be also changed by bringing down/up the
-device and then changing its slaves (or their order):
-
-# ifconfig bond0 down ; modprobe -r bonding
-# ifconfig bond0 .... up
-# ifenslave bond0 eth...
-
-       This method will automatically take the address from the next
-slave that is added.
-
-       To restore your slaves' MAC addresses, you need to detach them
-from the bond (`ifenslave -d bond0 eth0'). The bonding driver will
-then restore the MAC addresses that the slaves had before they were
-enslaved.
-
-16. Resources and Links
-=======================
-
-       The latest version of the bonding driver can be found in the latest
-version of the linux kernel, found on http://kernel.org
-
-       The latest version of this document can be found in the latest kernel
-source (named Documentation/networking/bonding.txt).
-
-       Discussions regarding the usage of the bonding driver take place on the
-bonding-devel mailing list, hosted at sourceforge.net. If you have questions or
-problems, post them to the list.  The list address is:
-
-bonding-devel@lists.sourceforge.net
-
-       The administrative interface (to subscribe or unsubscribe) can
-be found at:
-
-https://lists.sourceforge.net/lists/listinfo/bonding-devel
-
-       Discussions regarding the development of the bonding driver take place
-on the main Linux network mailing list, hosted at vger.kernel.org. The list
-address is:
-
-netdev@vger.kernel.org
-
-       The administrative interface (to subscribe or unsubscribe) can
-be found at:
-
-http://vger.kernel.org/vger-lists.html#netdev
-
-Donald Becker's Ethernet Drivers and diag programs may be found at :
- - http://web.archive.org/web/*/http://www.scyld.com/network/ 
-
-You will also find a lot of information regarding Ethernet, NWay, MII,
-etc. at www.scyld.com.
-
--- END --
diff --git a/Documentation/networking/caif/Linux-CAIF.txt b/Documentation/networking/caif/Linux-CAIF.txt
deleted file mode 100644 (file)
index 0aa4bd3..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-Linux CAIF
-===========
-copyright (C) ST-Ericsson AB 2010
-Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
-License terms: GNU General Public License (GPL) version 2
-
-
-Introduction
-------------
-CAIF is a MUX protocol used by ST-Ericsson cellular modems for
-communication between Modem and host. The host processes can open virtual AT
-channels, initiate GPRS Data connections, Video channels and Utility Channels.
-The Utility Channels are general purpose pipes between modem and host.
-
-ST-Ericsson modems support a number of transports between modem
-and host. Currently, UART and Loopback are available for Linux.
-
-
-Architecture:
-------------
-The implementation of CAIF is divided into:
-* CAIF Socket Layer and GPRS IP Interface.
-* CAIF Core Protocol Implementation
-* CAIF Link Layer, implemented as NET devices.
-
-
-  RTNL
-   !
-   !         +------+   +------+
-   !        +------+!  +------+!
-   !        !  IP  !!  !Socket!!
-   +-------> !interf!+ ! API  !+       <- CAIF Client APIs
-   !        +------+   +------!
-   !           !           !
-   !           +-----------+
-   !                 !
-   !              +------+             <- CAIF Core Protocol
-   !              ! CAIF !
-   !              ! Core !
-   !              +------+
-   !      +----------!---------+
-   !      !          !         !
-   !   +------+   +-----+   +------+
-   +--> ! HSI  !   ! TTY !   ! USB  !  <- Link Layer (Net Devices)
-       +------+   +-----+   +------+
-
-
-
-I M P L E M E N T A T I O N
-===========================
-
-
-CAIF Core Protocol Layer
-=========================================
-
-CAIF Core layer implements the CAIF protocol as defined by ST-Ericsson.
-It implements the CAIF protocol stack in a layered approach, where
-each layer described in the specification is implemented as a separate layer.
-The architecture is inspired by the design patterns "Protocol Layer" and
-"Protocol Packet".
-
-== CAIF structure ==
-The Core CAIF implementation contains:
-      -        Simple implementation of CAIF.
-      -        Layered architecture (a la Streams), each layer in the CAIF
-       specification is implemented in a separate c-file.
-      -        Clients must call configuration function to add PHY layer.
-      -        Clients must implement CAIF layer to consume/produce
-       CAIF payload with receive and transmit functions.
-      -        Clients must call configuration function to add and connect the
-       Client layer.
-      - When receiving / transmitting CAIF Packets (cfpkt), ownership is passed
-       to the called function (except for framing layers' receive function)
-
-Layered Architecture
---------------------
-The CAIF protocol can be divided into two parts: Support functions and Protocol
-Implementation. The support functions include:
-
-      - CFPKT CAIF Packet. Implementation of CAIF Protocol Packet. The
-       CAIF Packet has functions for creating, destroying and adding content
-       and for adding/extracting header and trailers to protocol packets.
-
-The CAIF Protocol implementation contains:
-
-      - CFCNFG CAIF Configuration layer. Configures the CAIF Protocol
-       Stack and provides a Client interface for adding Link-Layer and
-       Driver interfaces on top of the CAIF Stack.
-
-      - CFCTRL CAIF Control layer. Encodes and Decodes control messages
-       such as enumeration and channel setup. Also matches request and
-       response messages.
-
-      - CFSERVL General CAIF Service Layer functionality; handles flow
-       control and remote shutdown requests.
-
-      - CFVEI CAIF VEI layer. Handles CAIF AT Channels on VEI (Virtual
-       External Interface). This layer encodes/decodes VEI frames.
-
-      - CFDGML CAIF Datagram layer. Handles CAIF Datagram layer (IP
-       traffic), encodes/decodes Datagram frames.
-
-      - CFMUX CAIF Mux layer. Handles multiplexing between multiple
-       physical bearers and multiple channels such as VEI, Datagram, etc.
-       The MUX keeps track of the existing CAIF Channels and
-       Physical Instances and selects the appropriate instance based
-       on Channel-Id and Physical-ID.
-
-      - CFFRML CAIF Framing layer. Handles Framing i.e. Frame length
-       and frame checksum.
-
-      - CFSERL CAIF Serial layer. Handles concatenation/split of frames
-       into CAIF Frames with correct length.
-
-
-
-                   +---------+
-                   | Config  |
-                   | CFCNFG  |
-                   +---------+
-                        !
-    +---------+            +---------+     +---------+
-    |  AT    |     | Control |     | Datagram|
-    | CFVEIL  |            | CFCTRL  |     | CFDGML  |
-    +---------+            +---------+     +---------+
-          \_____________!______________/
-                        !
-                   +---------+
-                   |   MUX   |
-                   |         |
-                   +---------+
-                   _____!_____
-                  /           \
-           +---------+     +---------+
-           | CFFRML  |     | CFFRML  |
-           | Framing |     | Framing |
-           +---------+     +---------+
-                !              !
-           +---------+     +---------+
-           |         |     | Serial  |
-           |         |     | CFSERL  |
-           +---------+     +---------+
-
-
-In this layered approach the following "rules" apply.
-      - All layers embed the same structure "struct cflayer"
-      - A layer does not depend on any other layer's private data.
-      - Layers are stacked by setting the pointers
-                 layer->up , layer->dn
-      -        In order to send data upwards, each layer should do
-                layer->up->receive(layer->up, packet);
-      - In order to send data downwards, each layer should do
-                layer->dn->transmit(layer->dn, packet);
-
-
-CAIF Socket and IP interface
-===========================
-
-The IP interface and CAIF socket API are implemented on top of the
-CAIF Core protocol. The IP Interface and CAIF socket have an instance of
-'struct cflayer', just like the CAIF Core protocol stack.
-Net device and Socket implement the 'receive()' function defined by
-'struct cflayer', just like the rest of the CAIF stack. In this way, transmit and
-receive of packets is handled as by the rest of the layers: the 'dn->transmit()'
-function is called in order to transmit data.
-
-Configuration of Link Layer
----------------------------
-The Link Layer is implemented as Linux network devices (struct net_device).
-Payload handling and registration is done using standard Linux mechanisms.
-
-The CAIF Protocol relies on a loss-less link layer without implementing
-retransmission. This implies that packet drops must not happen.
-Therefore a flow-control mechanism is implemented where the physical
-interface can initiate flow stop for all CAIF Channels.
index 07afc80..a072130 100644 (file)
@@ -1,5 +1,3 @@
-:orphan:
-
 .. SPDX-License-Identifier: GPL-2.0
 .. include:: <isonum.txt>
 
diff --git a/Documentation/networking/caif/index.rst b/Documentation/networking/caif/index.rst
new file mode 100644 (file)
index 0000000..86e5b78
--- /dev/null
@@ -0,0 +1,13 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+CAIF
+====
+
+Contents:
+
+.. toctree::
+   :maxdepth: 2
+
+   linux_caif
+   caif
+   spi_porting
diff --git a/Documentation/networking/caif/linux_caif.rst b/Documentation/networking/caif/linux_caif.rst
new file mode 100644 (file)
index 0000000..a048086
--- /dev/null
@@ -0,0 +1,195 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+==========
+Linux CAIF
+==========
+
+Copyright |copy| ST-Ericsson AB 2010
+
+:Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
+:License terms: GNU General Public License (GPL) version 2
+
+
+Introduction
+============
+
+CAIF is a MUX protocol used by ST-Ericsson cellular modems for
+communication between Modem and host. The host processes can open virtual AT
+channels, initiate GPRS Data connections, Video channels and Utility Channels.
+The Utility Channels are general purpose pipes between modem and host.
+
+ST-Ericsson modems support a number of transports between modem
+and host. Currently, UART and Loopback are available for Linux.
+
+
+Architecture
+============
+
+The implementation of CAIF is divided into:
+
+* CAIF Socket Layer and GPRS IP Interface.
+* CAIF Core Protocol Implementation
+* CAIF Link Layer, implemented as NET devices.
+
+::
+
+  RTNL
+   !
+   !         +------+   +------+
+   !        +------+!  +------+!
+   !        !  IP  !!  !Socket!!
+   +-------> !interf!+ ! API  !+       <- CAIF Client APIs
+   !        +------+   +------!
+   !           !           !
+   !           +-----------+
+   !                 !
+   !              +------+             <- CAIF Core Protocol
+   !              ! CAIF !
+   !              ! Core !
+   !              +------+
+   !      +----------!---------+
+   !      !          !         !
+   !   +------+   +-----+   +------+
+   +--> ! HSI  !   ! TTY !   ! USB  !  <- Link Layer (Net Devices)
+       +------+   +-----+   +------+
+
+
+
+Implementation
+==============
+
+
+CAIF Core Protocol Layer
+------------------------
+
+CAIF Core layer implements the CAIF protocol as defined by ST-Ericsson.
+It implements the CAIF protocol stack in a layered approach, where
+each layer described in the specification is implemented as a separate layer.
+The architecture is inspired by the design patterns "Protocol Layer" and
+"Protocol Packet".
+
+CAIF structure
+^^^^^^^^^^^^^^
+
+The Core CAIF implementation contains:
+
+      -        Simple implementation of CAIF.
+      -        Layered architecture (a la Streams), each layer in the CAIF
+       specification is implemented in a separate c-file.
+      -        Clients must call configuration function to add PHY layer.
+      -        Clients must implement CAIF layer to consume/produce
+       CAIF payload with receive and transmit functions.
+      -        Clients must call configuration function to add and connect the
+       Client layer.
+      - When receiving / transmitting CAIF Packets (cfpkt), ownership is passed
+       to the called function (except for framing layers' receive function)
+
+Layered Architecture
+====================
+
+The CAIF protocol can be divided into two parts: Support functions and Protocol
+Implementation. The support functions include:
+
+      - CFPKT CAIF Packet. Implementation of CAIF Protocol Packet. The
+       CAIF Packet has functions for creating, destroying and adding content
+       and for adding/extracting header and trailers to protocol packets.
+
+The CAIF Protocol implementation contains:
+
+      - CFCNFG CAIF Configuration layer. Configures the CAIF Protocol
+       Stack and provides a Client interface for adding Link-Layer and
+       Driver interfaces on top of the CAIF Stack.
+
+      - CFCTRL CAIF Control layer. Encodes and Decodes control messages
+       such as enumeration and channel setup. Also matches request and
+       response messages.
+
+      - CFSERVL General CAIF Service Layer functionality; handles flow
+       control and remote shutdown requests.
+
+      - CFVEI CAIF VEI layer. Handles CAIF AT Channels on VEI (Virtual
+       External Interface). This layer encodes/decodes VEI frames.
+
+      - CFDGML CAIF Datagram layer. Handles CAIF Datagram layer (IP
+       traffic), encodes/decodes Datagram frames.
+
+      - CFMUX CAIF Mux layer. Handles multiplexing between multiple
+       physical bearers and multiple channels such as VEI, Datagram, etc.
+       The MUX keeps track of the existing CAIF Channels and
+       Physical Instances and selects the appropriate instance based
+       on Channel-Id and Physical-ID.
+
+      - CFFRML CAIF Framing layer. Handles Framing i.e. Frame length
+       and frame checksum.
+
+      - CFSERL CAIF Serial layer. Handles concatenation/split of frames
+       into CAIF Frames with correct length.
+
+::
+
+                   +---------+
+                   | Config  |
+                   | CFCNFG  |
+                   +---------+
+                        !
+    +---------+            +---------+     +---------+
+    |  AT    |     | Control |     | Datagram|
+    | CFVEIL  |            | CFCTRL  |     | CFDGML  |
+    +---------+            +---------+     +---------+
+          \_____________!______________/
+                        !
+                   +---------+
+                   |   MUX   |
+                   |         |
+                   +---------+
+                   _____!_____
+                  /           \
+           +---------+     +---------+
+           | CFFRML  |     | CFFRML  |
+           | Framing |     | Framing |
+           +---------+     +---------+
+                !              !
+           +---------+     +---------+
+           |         |     | Serial  |
+           |         |     | CFSERL  |
+           +---------+     +---------+
+
+
+In this layered approach the following "rules" apply.
+
+      - All layers embed the same structure "struct cflayer"
+      - A layer does not depend on any other layer's private data.
+      - Layers are stacked by setting the pointers::
+
+                 layer->up , layer->dn
+
+      -        In order to send data upwards, each layer should do::
+
+                layer->up->receive(layer->up, packet);
+
+      - In order to send data downwards, each layer should do::
+
+                layer->dn->transmit(layer->dn, packet);
+
+
+CAIF Socket and IP interface
+============================
+
+The IP interface and CAIF socket API are implemented on top of the
+CAIF Core protocol. The IP Interface and CAIF socket have an instance of
+'struct cflayer', just like the CAIF Core protocol stack.
+Net device and Socket implement the 'receive()' function defined by
+'struct cflayer', just like the rest of the CAIF stack. In this way, transmit and
+receive of packets is handled as by the rest of the layers: the 'dn->transmit()'
+function is called in order to transmit data.
+
+Configuration of Link Layer
+---------------------------
+The Link Layer is implemented as Linux network devices (struct net_device).
+Payload handling and registration is done using standard Linux mechanisms.
+
+The CAIF Protocol relies on a loss-less link layer without implementing
+retransmission. This implies that packet drops must not happen.
+Therefore a flow-control mechanism is implemented where the physical
+interface can initiate flow stop for all CAIF Channels.
diff --git a/Documentation/networking/caif/spi_porting.rst b/Documentation/networking/caif/spi_porting.rst
new file mode 100644 (file)
index 0000000..d49f874
--- /dev/null
@@ -0,0 +1,229 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+================
+CAIF SPI porting
+================
+
+CAIF SPI basics
+===============
+
+Running CAIF over SPI needs some extra setup, owing to the nature of SPI.
+Two extra GPIOs have been added in order to negotiate the transfers
+between the master and the slave. The minimum requirement for running
+CAIF over SPI is a SPI slave chip and two GPIOs (more details below).
+Please note that running as a slave implies that you need to keep up
+with the master clock. An overrun or underrun event is fatal.
+
+CAIF SPI framework
+==================
+
+To make porting as easy as possible, the CAIF SPI has been divided in
+two parts. The first part (called the interface part) deals with all
+generic functionality such as length framing, SPI frame negotiation
+and SPI frame delivery and transmission. The other part is the CAIF
+SPI slave device part, which is the module that you have to write if
+you want to run SPI CAIF on a new hardware. This part takes care of
+the physical hardware, both with regard to SPI and to GPIOs.
+
+- Implementing a CAIF SPI device:
+
+       - Functionality provided by the CAIF SPI slave device:
+
+       In order to implement a SPI device you will, as a minimum,
+       need to implement the following
+       functions:
+
+       ::
+
+           int (*init_xfer) (struct cfspi_xfer * xfer, struct cfspi_dev *dev):
+
+       This function is called by the CAIF SPI interface to give
+       you a chance to set up your hardware to be ready to receive
+       a stream of data from the master. The xfer structure contains
+       both physical and logical addresses, as well as the total length
+       of the transfer in both directions.The dev parameter can be used
+       to map to different CAIF SPI slave devices.
+
+       ::
+
+           void (*sig_xfer) (bool xfer, struct cfspi_dev *dev):
+
+       This function is called by the CAIF SPI interface when the output
+       (SPI_INT) GPIO needs to change state. The boolean value of the xfer
+       variable indicates whether the GPIO should be asserted (HIGH) or
+       deasserted (LOW). The dev parameter can be used to map to different CAIF
+       SPI slave devices.
+
+       - Functionality provided by the CAIF SPI interface:
+
+       ::
+
+           void (*ss_cb) (bool assert, struct cfspi_ifc *ifc);
+
+       This function is called by the CAIF SPI slave device in order to
+       signal a change of state of the input GPIO (SS) to the interface.
+       Only active edges are mandatory to be reported.
+       This function can be called from IRQ context (recommended in order
+       not to introduce latency). The ifc parameter should be the pointer
+       returned from the platform probe function in the SPI device structure.
+
+       ::
+
+           void (*xfer_done_cb) (struct cfspi_ifc *ifc);
+
+       This function is called by the CAIF SPI slave device in order to
+       report that a transfer is completed. This function should only be
+       called once both the transmission and the reception are completed.
+       This function can be called from IRQ context (recommended in order
+       not to introduce latency). The ifc parameter should be the pointer
+       returned from the platform probe function in the SPI device structure.
+
+       - Connecting the bits and pieces:
+
+               - Filling in the SPI slave device structure:
+
+                 Connect the necessary callback functions.
+
+                 Indicate clock speed (used to calculate toggle delays).
+
+                 Chose a suitable name (helps debugging if you use several CAIF
+                 SPI slave devices).
+
+                 Assign your private data (can be used to map to your
+                 structure).
+
+               - Filling in the SPI slave platform device structure:
+
+                 Add name of driver to connect to ("cfspi_sspi").
+
+                 Assign the SPI slave device structure as platform data.
+
+Padding
+=======
+
+In order to optimize throughput, a number of SPI padding options are provided.
+Padding can be enabled independently for uplink and downlink transfers.
+Padding can be enabled for the head, the tail and for the total frame size.
+The padding needs to be correctly configured on both sides of the link.
+The padding can be changed via module parameters in cfspi_sspi.c or via
+the sysfs directory of the cfspi_sspi driver (before device registration).
+
+- CAIF SPI device template::
+
+    /*
+    *  Copyright (C) ST-Ericsson AB 2010
+    *  Author: Daniel Martensson / Daniel.Martensson@stericsson.com
+    *  License terms: GNU General Public License (GPL), version 2.
+    *
+    */
+
+    #include <linux/init.h>
+    #include <linux/module.h>
+    #include <linux/device.h>
+    #include <linux/wait.h>
+    #include <linux/interrupt.h>
+    #include <linux/dma-mapping.h>
+    #include <net/caif/caif_spi.h>
+
+    MODULE_LICENSE("GPL");
+
+    struct sspi_struct {
+           struct cfspi_dev sdev;
+           struct cfspi_xfer *xfer;
+    };
+
+    static struct sspi_struct slave;
+    static struct platform_device slave_device;
+
+    static irqreturn_t sspi_irq(int irq, void *arg)
+    {
+           /* You only need to trigger on an edge to the active state of the
+           * SS signal. Once a edge is detected, the ss_cb() function should be
+           * called with the parameter assert set to true. It is OK
+           * (and even advised) to call the ss_cb() function in IRQ context in
+           * order not to add any delay. */
+
+           return IRQ_HANDLED;
+    }
+
+    static void sspi_complete(void *context)
+    {
+           /* Normally the DMA or the SPI framework will call you back
+           * in something similar to this. The only thing you need to
+           * do is to call the xfer_done_cb() function, providing the pointer
+           * to the CAIF SPI interface. It is OK to call this function
+           * from IRQ context. */
+    }
+
+    static int sspi_init_xfer(struct cfspi_xfer *xfer, struct cfspi_dev *dev)
+    {
+           /* Store transfer info. For a normal implementation you should
+           * set up your DMA here and make sure that you are ready to
+           * receive the data from the master SPI. */
+
+           struct sspi_struct *sspi = (struct sspi_struct *)dev->priv;
+
+           sspi->xfer = xfer;
+
+           return 0;
+    }
+
+    void sspi_sig_xfer(bool xfer, struct cfspi_dev *dev)
+    {
+           /* If xfer is true then you should assert the SPI_INT to indicate to
+           * the master that you are ready to receive the data from the master
+           * SPI. If xfer is false then you should de-assert SPI_INT to indicate
+           * that the transfer is done.
+           */
+
+           struct sspi_struct *sspi = (struct sspi_struct *)dev->priv;
+    }
+
+    static void sspi_release(struct device *dev)
+    {
+           /*
+           * Here you should release your SPI device resources.
+           */
+    }
+
+    static int __init sspi_init(void)
+    {
+           /* Here you should initialize your SPI device by providing the
+           * necessary functions, clock speed, name and private data. Once
+           * done, you can register your device with the
+           * platform_device_register() function. This function will return
+           * with the CAIF SPI interface initialized. This is probably also
+           * the place where you should set up your GPIOs, interrupts and SPI
+           * resources. */
+
+           int res = 0;
+
+           /* Initialize slave device. */
+           slave.sdev.init_xfer = sspi_init_xfer;
+           slave.sdev.sig_xfer = sspi_sig_xfer;
+           slave.sdev.clk_mhz = 13;
+           slave.sdev.priv = &slave;
+           slave.sdev.name = "spi_sspi";
+           slave_device.dev.release = sspi_release;
+
+           /* Initialize platform device. */
+           slave_device.name = "cfspi_sspi";
+           slave_device.dev.platform_data = &slave.sdev;
+
+           /* Register platform device. */
+           res = platform_device_register(&slave_device);
+           if (res) {
+                   printk(KERN_WARNING "sspi_init: failed to register dev.\n");
+                   return -ENODEV;
+           }
+
+           return res;
+    }
+
+    static void __exit sspi_exit(void)
+    {
+           platform_device_del(&slave_device);
+    }
+
+    module_init(sspi_init);
+    module_exit(sspi_exit);
diff --git a/Documentation/networking/caif/spi_porting.txt b/Documentation/networking/caif/spi_porting.txt
deleted file mode 100644 (file)
index 9efd068..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-- CAIF SPI porting -
-
-- CAIF SPI basics:
-
-Running CAIF over SPI needs some extra setup, owing to the nature of SPI.
-Two extra GPIOs have been added in order to negotiate the transfers
- between the master and the slave. The minimum requirement for running
-CAIF over SPI is a SPI slave chip and two GPIOs (more details below).
-Please note that running as a slave implies that you need to keep up
-with the master clock. An overrun or underrun event is fatal.
-
-- CAIF SPI framework:
-
-To make porting as easy as possible, the CAIF SPI has been divided in
-two parts. The first part (called the interface part) deals with all
-generic functionality such as length framing, SPI frame negotiation
-and SPI frame delivery and transmission. The other part is the CAIF
-SPI slave device part, which is the module that you have to write if
-you want to run SPI CAIF on a new hardware. This part takes care of
-the physical hardware, both with regard to SPI and to GPIOs.
-
-- Implementing a CAIF SPI device:
-
-       - Functionality provided by the CAIF SPI slave device:
-
-       In order to implement a SPI device you will, as a minimum,
-       need to implement the following
-       functions:
-
-       int (*init_xfer) (struct cfspi_xfer * xfer, struct cfspi_dev *dev):
-
-       This function is called by the CAIF SPI interface to give
-       you a chance to set up your hardware to be ready to receive
-       a stream of data from the master. The xfer structure contains
-       both physical and logical addresses, as well as the total length
-       of the transfer in both directions.The dev parameter can be used
-       to map to different CAIF SPI slave devices.
-
-       void (*sig_xfer) (bool xfer, struct cfspi_dev *dev):
-
-       This function is called by the CAIF SPI interface when the output
-       (SPI_INT) GPIO needs to change state. The boolean value of the xfer
-       variable indicates whether the GPIO should be asserted (HIGH) or
-       deasserted (LOW). The dev parameter can be used to map to different CAIF
-       SPI slave devices.
-
-       - Functionality provided by the CAIF SPI interface:
-
-       void (*ss_cb) (bool assert, struct cfspi_ifc *ifc);
-
-       This function is called by the CAIF SPI slave device in order to
-       signal a change of state of the input GPIO (SS) to the interface.
-       Only active edges are mandatory to be reported.
-       This function can be called from IRQ context (recommended in order
-       not to introduce latency). The ifc parameter should be the pointer
-       returned from the platform probe function in the SPI device structure.
-
-       void (*xfer_done_cb) (struct cfspi_ifc *ifc);
-
-       This function is called by the CAIF SPI slave device in order to
-       report that a transfer is completed. This function should only be
-       called once both the transmission and the reception are completed.
-       This function can be called from IRQ context (recommended in order
-       not to introduce latency). The ifc parameter should be the pointer
-       returned from the platform probe function in the SPI device structure.
-
-       - Connecting the bits and pieces:
-
-               - Filling in the SPI slave device structure:
-
-               Connect the necessary callback functions.
-               Indicate clock speed (used to calculate toggle delays).
-               Chose a suitable name (helps debugging if you use several CAIF
-               SPI slave devices).
-               Assign your private data (can be used to map to your structure).
-
-               - Filling in the SPI slave platform device structure:
-               Add name of driver to connect to ("cfspi_sspi").
-               Assign the SPI slave device structure as platform data.
-
-- Padding:
-
-In order to optimize throughput, a number of SPI padding options are provided.
-Padding can be enabled independently for uplink and downlink transfers.
-Padding can be enabled for the head, the tail and for the total frame size.
-The padding needs to be correctly configured on both sides of the link.
-The padding can be changed via module parameters in cfspi_sspi.c or via
-the sysfs directory of the cfspi_sspi driver (before device registration).
-
-- CAIF SPI device template:
-
-/*
- *     Copyright (C) ST-Ericsson AB 2010
- *     Author: Daniel Martensson / Daniel.Martensson@stericsson.com
- *     License terms: GNU General Public License (GPL), version 2.
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/wait.h>
-#include <linux/interrupt.h>
-#include <linux/dma-mapping.h>
-#include <net/caif/caif_spi.h>
-
-MODULE_LICENSE("GPL");
-
-struct sspi_struct {
-       struct cfspi_dev sdev;
-       struct cfspi_xfer *xfer;
-};
-
-static struct sspi_struct slave;
-static struct platform_device slave_device;
-
-static irqreturn_t sspi_irq(int irq, void *arg)
-{
-       /* You only need to trigger on an edge to the active state of the
-        * SS signal. Once a edge is detected, the ss_cb() function should be
-        * called with the parameter assert set to true. It is OK
-        * (and even advised) to call the ss_cb() function in IRQ context in
-        * order not to add any delay. */
-
-       return IRQ_HANDLED;
-}
-
-static void sspi_complete(void *context)
-{
-       /* Normally the DMA or the SPI framework will call you back
-        * in something similar to this. The only thing you need to
-        * do is to call the xfer_done_cb() function, providing the pointer
-        * to the CAIF SPI interface. It is OK to call this function
-        * from IRQ context. */
-}
-
-static int sspi_init_xfer(struct cfspi_xfer *xfer, struct cfspi_dev *dev)
-{
-       /* Store transfer info. For a normal implementation you should
-        * set up your DMA here and make sure that you are ready to
-        * receive the data from the master SPI. */
-
-       struct sspi_struct *sspi = (struct sspi_struct *)dev->priv;
-
-       sspi->xfer = xfer;
-
-       return 0;
-}
-
-void sspi_sig_xfer(bool xfer, struct cfspi_dev *dev)
-{
-       /* If xfer is true then you should assert the SPI_INT to indicate to
-        * the master that you are ready to receive the data from the master
-        * SPI. If xfer is false then you should de-assert SPI_INT to indicate
-        * that the transfer is done.
-        */
-
-       struct sspi_struct *sspi = (struct sspi_struct *)dev->priv;
-}
-
-static void sspi_release(struct device *dev)
-{
-       /*
-        * Here you should release your SPI device resources.
-        */
-}
-
-static int __init sspi_init(void)
-{
-       /* Here you should initialize your SPI device by providing the
-        * necessary functions, clock speed, name and private data. Once
-        * done, you can register your device with the
-        * platform_device_register() function. This function will return
-        * with the CAIF SPI interface initialized. This is probably also
-        * the place where you should set up your GPIOs, interrupts and SPI
-        * resources. */
-
-       int res = 0;
-
-       /* Initialize slave device. */
-       slave.sdev.init_xfer = sspi_init_xfer;
-       slave.sdev.sig_xfer = sspi_sig_xfer;
-       slave.sdev.clk_mhz = 13;
-       slave.sdev.priv = &slave;
-       slave.sdev.name = "spi_sspi";
-       slave_device.dev.release = sspi_release;
-
-       /* Initialize platform device. */
-       slave_device.name = "cfspi_sspi";
-       slave_device.dev.platform_data = &slave.sdev;
-
-       /* Register platform device. */
-       res = platform_device_register(&slave_device);
-       if (res) {
-               printk(KERN_WARNING "sspi_init: failed to register dev.\n");
-               return -ENODEV;
-       }
-
-       return res;
-}
-
-static void __exit sspi_exit(void)
-{
-       platform_device_del(&slave_device);
-}
-
-module_init(sspi_init);
-module_exit(sspi_exit);
index 2fd0b51..ff05cbd 100644 (file)
@@ -1058,7 +1058,7 @@ drivers you mainly have to deal with:
 - TX: Put the CAN frame from the socket buffer to the CAN controller.
 - RX: Put the CAN frame from the CAN controller to the socket buffer.
 
-See e.g. at Documentation/networking/netdevices.txt . The differences
+See e.g. at Documentation/networking/netdevices.rst . The differences
 for writing CAN network device driver are described below:
 
 
diff --git a/Documentation/networking/cdc_mbim.rst b/Documentation/networking/cdc_mbim.rst
new file mode 100644 (file)
index 0000000..0048409
--- /dev/null
@@ -0,0 +1,355 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================================
+cdc_mbim - Driver for CDC MBIM Mobile Broadband modems
+======================================================
+
+The cdc_mbim driver supports USB devices conforming to the "Universal
+Serial Bus Communications Class Subclass Specification for Mobile
+Broadband Interface Model" [1], which is a further development of
+"Universal Serial Bus Communications Class Subclass Specifications for
+Network Control Model Devices" [2] optimized for Mobile Broadband
+devices, aka "3G/LTE modems".
+
+
+Command Line Parameters
+=======================
+
+The cdc_mbim driver has no parameters of its own.  But the probing
+behaviour for NCM 1.0 backwards compatible MBIM functions (an
+"NCM/MBIM function" as defined in section 3.2 of [1]) is affected
+by a cdc_ncm driver parameter:
+
+prefer_mbim
+-----------
+:Type:          Boolean
+:Valid Range:   N/Y (0-1)
+:Default Value: Y (MBIM is preferred)
+
+This parameter sets the system policy for NCM/MBIM functions.  Such
+functions will be handled by either the cdc_ncm driver or the cdc_mbim
+driver depending on the prefer_mbim setting.  Setting prefer_mbim=N
+makes the cdc_mbim driver ignore these functions and lets the cdc_ncm
+driver handle them instead.
+
+The parameter is writable, and can be changed at any time. A manual
+unbind/bind is required to make the change effective for NCM/MBIM
+functions bound to the "wrong" driver
+
+
+Basic usage
+===========
+
+MBIM functions are inactive when unmanaged. The cdc_mbim driver only
+provides a userspace interface to the MBIM control channel, and will
+not participate in the management of the function. This implies that a
+userspace MBIM management application always is required to enable a
+MBIM function.
+
+Such userspace applications includes, but are not limited to:
+
+ - mbimcli (included with the libmbim [3] library), and
+ - ModemManager [4]
+
+Establishing a MBIM IP session reequires at least these actions by the
+management application:
+
+ - open the control channel
+ - configure network connection settings
+ - connect to network
+ - configure IP interface
+
+Management application development
+----------------------------------
+The driver <-> userspace interfaces are described below.  The MBIM
+control channel protocol is described in [1].
+
+
+MBIM control channel userspace ABI
+==================================
+
+/dev/cdc-wdmX character device
+------------------------------
+The driver creates a two-way pipe to the MBIM function control channel
+using the cdc-wdm driver as a subdriver.  The userspace end of the
+control channel pipe is a /dev/cdc-wdmX character device.
+
+The cdc_mbim driver does not process or police messages on the control
+channel.  The channel is fully delegated to the userspace management
+application.  It is therefore up to this application to ensure that it
+complies with all the control channel requirements in [1].
+
+The cdc-wdmX device is created as a child of the MBIM control
+interface USB device.  The character device associated with a specific
+MBIM function can be looked up using sysfs.  For example::
+
+ bjorn@nemi:~$ ls /sys/bus/usb/drivers/cdc_mbim/2-4:2.12/usbmisc
+ cdc-wdm0
+
+ bjorn@nemi:~$ grep . /sys/bus/usb/drivers/cdc_mbim/2-4:2.12/usbmisc/cdc-wdm0/dev
+ 180:0
+
+
+USB configuration descriptors
+-----------------------------
+The wMaxControlMessage field of the CDC MBIM functional descriptor
+limits the maximum control message size. The managament application is
+responsible for negotiating a control message size complying with the
+requirements in section 9.3.1 of [1], taking this descriptor field
+into consideration.
+
+The userspace application can access the CDC MBIM functional
+descriptor of a MBIM function using either of the two USB
+configuration descriptor kernel interfaces described in [6] or [7].
+
+See also the ioctl documentation below.
+
+
+Fragmentation
+-------------
+The userspace application is responsible for all control message
+fragmentation and defragmentaion, as described in section 9.5 of [1].
+
+
+/dev/cdc-wdmX write()
+---------------------
+The MBIM control messages from the management application *must not*
+exceed the negotiated control message size.
+
+
+/dev/cdc-wdmX read()
+--------------------
+The management application *must* accept control messages of up the
+negotiated control message size.
+
+
+/dev/cdc-wdmX ioctl()
+---------------------
+IOCTL_WDM_MAX_COMMAND: Get Maximum Command Size
+This ioctl returns the wMaxControlMessage field of the CDC MBIM
+functional descriptor for MBIM devices.  This is intended as a
+convenience, eliminating the need to parse the USB descriptors from
+userspace.
+
+::
+
+       #include <stdio.h>
+       #include <fcntl.h>
+       #include <sys/ioctl.h>
+       #include <linux/types.h>
+       #include <linux/usb/cdc-wdm.h>
+       int main()
+       {
+               __u16 max;
+               int fd = open("/dev/cdc-wdm0", O_RDWR);
+               if (!ioctl(fd, IOCTL_WDM_MAX_COMMAND, &max))
+                       printf("wMaxControlMessage is %d\n", max);
+       }
+
+
+Custom device services
+----------------------
+The MBIM specification allows vendors to freely define additional
+services.  This is fully supported by the cdc_mbim driver.
+
+Support for new MBIM services, including vendor specified services, is
+implemented entirely in userspace, like the rest of the MBIM control
+protocol
+
+New services should be registered in the MBIM Registry [5].
+
+
+
+MBIM data channel userspace ABI
+===============================
+
+wwanY network device
+--------------------
+The cdc_mbim driver represents the MBIM data channel as a single
+network device of the "wwan" type. This network device is initially
+mapped to MBIM IP session 0.
+
+
+Multiplexed IP sessions (IPS)
+-----------------------------
+MBIM allows multiplexing up to 256 IP sessions over a single USB data
+channel.  The cdc_mbim driver models such IP sessions as 802.1q VLAN
+subdevices of the master wwanY device, mapping MBIM IP session Z to
+VLAN ID Z for all values of Z greater than 0.
+
+The device maximum Z is given in the MBIM_DEVICE_CAPS_INFO structure
+described in section 10.5.1 of [1].
+
+The userspace management application is responsible for adding new
+VLAN links prior to establishing MBIM IP sessions where the SessionId
+is greater than 0. These links can be added by using the normal VLAN
+kernel interfaces, either ioctl or netlink.
+
+For example, adding a link for a MBIM IP session with SessionId 3::
+
+  ip link add link wwan0 name wwan0.3 type vlan id 3
+
+The driver will automatically map the "wwan0.3" network device to MBIM
+IP session 3.
+
+
+Device Service Streams (DSS)
+----------------------------
+MBIM also allows up to 256 non-IP data streams to be multiplexed over
+the same shared USB data channel.  The cdc_mbim driver models these
+sessions as another set of 802.1q VLAN subdevices of the master wwanY
+device, mapping MBIM DSS session A to VLAN ID (256 + A) for all values
+of A.
+
+The device maximum A is given in the MBIM_DEVICE_SERVICES_INFO
+structure described in section 10.5.29 of [1].
+
+The DSS VLAN subdevices are used as a practical interface between the
+shared MBIM data channel and a MBIM DSS aware userspace application.
+It is not intended to be presented as-is to an end user. The
+assumption is that a userspace application initiating a DSS session
+also takes care of the necessary framing of the DSS data, presenting
+the stream to the end user in an appropriate way for the stream type.
+
+The network device ABI requires a dummy ethernet header for every DSS
+data frame being transported.  The contents of this header is
+arbitrary, with the following exceptions:
+
+ - TX frames using an IP protocol (0x0800 or 0x86dd) will be dropped
+ - RX frames will have the protocol field set to ETH_P_802_3 (but will
+   not be properly formatted 802.3 frames)
+ - RX frames will have the destination address set to the hardware
+   address of the master device
+
+The DSS supporting userspace management application is responsible for
+adding the dummy ethernet header on TX and stripping it on RX.
+
+This is a simple example using tools commonly available, exporting
+DssSessionId 5 as a pty character device pointed to by a /dev/nmea
+symlink::
+
+  ip link add link wwan0 name wwan0.dss5 type vlan id 261
+  ip link set dev wwan0.dss5 up
+  socat INTERFACE:wwan0.dss5,type=2 PTY:,echo=0,link=/dev/nmea
+
+This is only an example, most suitable for testing out a DSS
+service. Userspace applications supporting specific MBIM DSS services
+are expected to use the tools and programming interfaces required by
+that service.
+
+Note that adding VLAN links for DSS sessions is entirely optional.  A
+management application may instead choose to bind a packet socket
+directly to the master network device, using the received VLAN tags to
+map frames to the correct DSS session and adding 18 byte VLAN ethernet
+headers with the appropriate tag on TX.  In this case using a socket
+filter is recommended, matching only the DSS VLAN subset. This avoid
+unnecessary copying of unrelated IP session data to userspace.  For
+example::
+
+  static struct sock_filter dssfilter[] = {
+       /* use special negative offsets to get VLAN tag */
+       BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT),
+       BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 1, 0, 6), /* true */
+
+       /* verify DSS VLAN range */
+       BPF_STMT(BPF_LD|BPF_H|BPF_ABS, SKF_AD_OFF + SKF_AD_VLAN_TAG),
+       BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 256, 0, 4),     /* 256 is first DSS VLAN */
+       BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 512, 3, 0),     /* 511 is last DSS VLAN */
+
+       /* verify ethertype */
+       BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 2 * ETH_ALEN),
+       BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETH_P_802_3, 0, 1),
+
+       BPF_STMT(BPF_RET|BPF_K, (u_int)-1),     /* accept */
+       BPF_STMT(BPF_RET|BPF_K, 0),             /* ignore */
+  };
+
+
+
+Tagged IP session 0 VLAN
+------------------------
+As described above, MBIM IP session 0 is treated as special by the
+driver.  It is initially mapped to untagged frames on the wwanY
+network device.
+
+This mapping implies a few restrictions on multiplexed IPS and DSS
+sessions, which may not always be practical:
+
+ - no IPS or DSS session can use a frame size greater than the MTU on
+   IP session 0
+ - no IPS or DSS session can be in the up state unless the network
+   device representing IP session 0 also is up
+
+These problems can be avoided by optionally making the driver map IP
+session 0 to a VLAN subdevice, similar to all other IP sessions.  This
+behaviour is triggered by adding a VLAN link for the magic VLAN ID
+4094.  The driver will then immediately start mapping MBIM IP session
+0 to this VLAN, and will drop untagged frames on the master wwanY
+device.
+
+Tip: It might be less confusing to the end user to name this VLAN
+subdevice after the MBIM SessionID instead of the VLAN ID.  For
+example::
+
+  ip link add link wwan0 name wwan0.0 type vlan id 4094
+
+
+VLAN mapping
+------------
+
+Summarizing the cdc_mbim driver mapping described above, we have this
+relationship between VLAN tags on the wwanY network device and MBIM
+sessions on the shared USB data channel::
+
+  VLAN ID       MBIM type   MBIM SessionID           Notes
+  ---------------------------------------------------------
+  untagged      IPS         0                        a)
+  1 - 255       IPS         1 - 255 <VLANID>
+  256 - 511     DSS         0 - 255 <VLANID - 256>
+  512 - 4093                                         b)
+  4094          IPS         0                        c)
+
+    a) if no VLAN ID 4094 link exists, else dropped
+    b) unsupported VLAN range, unconditionally dropped
+    c) if a VLAN ID 4094 link exists, else dropped
+
+
+
+
+References
+==========
+
+ 1) USB Implementers Forum, Inc. - "Universal Serial Bus
+    Communications Class Subclass Specification for Mobile Broadband
+    Interface Model", Revision 1.0 (Errata 1), May 1, 2013
+
+      - http://www.usb.org/developers/docs/devclass_docs/
+
+ 2) USB Implementers Forum, Inc. - "Universal Serial Bus
+    Communications Class Subclass Specifications for Network Control
+    Model Devices", Revision 1.0 (Errata 1), November 24, 2010
+
+      - http://www.usb.org/developers/docs/devclass_docs/
+
+ 3) libmbim - "a glib-based library for talking to WWAN modems and
+    devices which speak the Mobile Interface Broadband Model (MBIM)
+    protocol"
+
+      - http://www.freedesktop.org/wiki/Software/libmbim/
+
+ 4) ModemManager - "a DBus-activated daemon which controls mobile
+    broadband (2G/3G/4G) devices and connections"
+
+      - http://www.freedesktop.org/wiki/Software/ModemManager/
+
+ 5) "MBIM (Mobile Broadband Interface Model) Registry"
+
+       - http://compliance.usb.org/mbim/
+
+ 6) "/sys/kernel/debug/usb/devices output format"
+
+       - Documentation/driver-api/usb/usb.rst
+
+ 7) "/sys/bus/usb/devices/.../descriptors"
+
+       - Documentation/ABI/stable/sysfs-bus-usb
diff --git a/Documentation/networking/cdc_mbim.txt b/Documentation/networking/cdc_mbim.txt
deleted file mode 100644 (file)
index 4e68f0b..0000000
+++ /dev/null
@@ -1,339 +0,0 @@
-     cdc_mbim - Driver for CDC MBIM Mobile Broadband modems
-    ========================================================
-
-The cdc_mbim driver supports USB devices conforming to the "Universal
-Serial Bus Communications Class Subclass Specification for Mobile
-Broadband Interface Model" [1], which is a further development of
-"Universal Serial Bus Communications Class Subclass Specifications for
-Network Control Model Devices" [2] optimized for Mobile Broadband
-devices, aka "3G/LTE modems".
-
-
-Command Line Parameters
-=======================
-
-The cdc_mbim driver has no parameters of its own.  But the probing
-behaviour for NCM 1.0 backwards compatible MBIM functions (an
-"NCM/MBIM function" as defined in section 3.2 of [1]) is affected
-by a cdc_ncm driver parameter:
-
-prefer_mbim
------------
-Type:          Boolean
-Valid Range:   N/Y (0-1)
-Default Value: Y (MBIM is preferred)
-
-This parameter sets the system policy for NCM/MBIM functions.  Such
-functions will be handled by either the cdc_ncm driver or the cdc_mbim
-driver depending on the prefer_mbim setting.  Setting prefer_mbim=N
-makes the cdc_mbim driver ignore these functions and lets the cdc_ncm
-driver handle them instead.
-
-The parameter is writable, and can be changed at any time. A manual
-unbind/bind is required to make the change effective for NCM/MBIM
-functions bound to the "wrong" driver
-
-
-Basic usage
-===========
-
-MBIM functions are inactive when unmanaged. The cdc_mbim driver only
-provides a userspace interface to the MBIM control channel, and will
-not participate in the management of the function. This implies that a
-userspace MBIM management application always is required to enable a
-MBIM function.
-
-Such userspace applications includes, but are not limited to:
- - mbimcli (included with the libmbim [3] library), and
- - ModemManager [4]
-
-Establishing a MBIM IP session reequires at least these actions by the
-management application:
- - open the control channel
- - configure network connection settings
- - connect to network
- - configure IP interface
-
-Management application development
-----------------------------------
-The driver <-> userspace interfaces are described below.  The MBIM
-control channel protocol is described in [1].
-
-
-MBIM control channel userspace ABI
-==================================
-
-/dev/cdc-wdmX character device
-------------------------------
-The driver creates a two-way pipe to the MBIM function control channel
-using the cdc-wdm driver as a subdriver.  The userspace end of the
-control channel pipe is a /dev/cdc-wdmX character device.
-
-The cdc_mbim driver does not process or police messages on the control
-channel.  The channel is fully delegated to the userspace management
-application.  It is therefore up to this application to ensure that it
-complies with all the control channel requirements in [1].
-
-The cdc-wdmX device is created as a child of the MBIM control
-interface USB device.  The character device associated with a specific
-MBIM function can be looked up using sysfs.  For example:
-
- bjorn@nemi:~$ ls /sys/bus/usb/drivers/cdc_mbim/2-4:2.12/usbmisc
- cdc-wdm0
-
- bjorn@nemi:~$ grep . /sys/bus/usb/drivers/cdc_mbim/2-4:2.12/usbmisc/cdc-wdm0/dev
- 180:0
-
-
-USB configuration descriptors
------------------------------
-The wMaxControlMessage field of the CDC MBIM functional descriptor
-limits the maximum control message size. The managament application is
-responsible for negotiating a control message size complying with the
-requirements in section 9.3.1 of [1], taking this descriptor field
-into consideration.
-
-The userspace application can access the CDC MBIM functional
-descriptor of a MBIM function using either of the two USB
-configuration descriptor kernel interfaces described in [6] or [7].
-
-See also the ioctl documentation below.
-
-
-Fragmentation
--------------
-The userspace application is responsible for all control message
-fragmentation and defragmentaion, as described in section 9.5 of [1].
-
-
-/dev/cdc-wdmX write()
----------------------
-The MBIM control messages from the management application *must not*
-exceed the negotiated control message size.
-
-
-/dev/cdc-wdmX read()
---------------------
-The management application *must* accept control messages of up the
-negotiated control message size.
-
-
-/dev/cdc-wdmX ioctl()
---------------------
-IOCTL_WDM_MAX_COMMAND: Get Maximum Command Size
-This ioctl returns the wMaxControlMessage field of the CDC MBIM
-functional descriptor for MBIM devices.  This is intended as a
-convenience, eliminating the need to parse the USB descriptors from
-userspace.
-
-       #include <stdio.h>
-       #include <fcntl.h>
-       #include <sys/ioctl.h>
-       #include <linux/types.h>
-       #include <linux/usb/cdc-wdm.h>
-       int main()
-       {
-               __u16 max;
-               int fd = open("/dev/cdc-wdm0", O_RDWR);
-               if (!ioctl(fd, IOCTL_WDM_MAX_COMMAND, &max))
-                       printf("wMaxControlMessage is %d\n", max);
-       }
-
-
-Custom device services
-----------------------
-The MBIM specification allows vendors to freely define additional
-services.  This is fully supported by the cdc_mbim driver.
-
-Support for new MBIM services, including vendor specified services, is
-implemented entirely in userspace, like the rest of the MBIM control
-protocol
-
-New services should be registered in the MBIM Registry [5].
-
-
-
-MBIM data channel userspace ABI
-===============================
-
-wwanY network device
---------------------
-The cdc_mbim driver represents the MBIM data channel as a single
-network device of the "wwan" type. This network device is initially
-mapped to MBIM IP session 0.
-
-
-Multiplexed IP sessions (IPS)
------------------------------
-MBIM allows multiplexing up to 256 IP sessions over a single USB data
-channel.  The cdc_mbim driver models such IP sessions as 802.1q VLAN
-subdevices of the master wwanY device, mapping MBIM IP session Z to
-VLAN ID Z for all values of Z greater than 0.
-
-The device maximum Z is given in the MBIM_DEVICE_CAPS_INFO structure
-described in section 10.5.1 of [1].
-
-The userspace management application is responsible for adding new
-VLAN links prior to establishing MBIM IP sessions where the SessionId
-is greater than 0. These links can be added by using the normal VLAN
-kernel interfaces, either ioctl or netlink.
-
-For example, adding a link for a MBIM IP session with SessionId 3:
-
-  ip link add link wwan0 name wwan0.3 type vlan id 3
-
-The driver will automatically map the "wwan0.3" network device to MBIM
-IP session 3.
-
-
-Device Service Streams (DSS)
-----------------------------
-MBIM also allows up to 256 non-IP data streams to be multiplexed over
-the same shared USB data channel.  The cdc_mbim driver models these
-sessions as another set of 802.1q VLAN subdevices of the master wwanY
-device, mapping MBIM DSS session A to VLAN ID (256 + A) for all values
-of A.
-
-The device maximum A is given in the MBIM_DEVICE_SERVICES_INFO
-structure described in section 10.5.29 of [1].
-
-The DSS VLAN subdevices are used as a practical interface between the
-shared MBIM data channel and a MBIM DSS aware userspace application.
-It is not intended to be presented as-is to an end user. The
-assumption is that a userspace application initiating a DSS session
-also takes care of the necessary framing of the DSS data, presenting
-the stream to the end user in an appropriate way for the stream type.
-
-The network device ABI requires a dummy ethernet header for every DSS
-data frame being transported.  The contents of this header is
-arbitrary, with the following exceptions:
- - TX frames using an IP protocol (0x0800 or 0x86dd) will be dropped
- - RX frames will have the protocol field set to ETH_P_802_3 (but will
-   not be properly formatted 802.3 frames)
- - RX frames will have the destination address set to the hardware
-   address of the master device
-
-The DSS supporting userspace management application is responsible for
-adding the dummy ethernet header on TX and stripping it on RX.
-
-This is a simple example using tools commonly available, exporting
-DssSessionId 5 as a pty character device pointed to by a /dev/nmea
-symlink:
-
-  ip link add link wwan0 name wwan0.dss5 type vlan id 261
-  ip link set dev wwan0.dss5 up
-  socat INTERFACE:wwan0.dss5,type=2 PTY:,echo=0,link=/dev/nmea
-
-This is only an example, most suitable for testing out a DSS
-service. Userspace applications supporting specific MBIM DSS services
-are expected to use the tools and programming interfaces required by
-that service.
-
-Note that adding VLAN links for DSS sessions is entirely optional.  A
-management application may instead choose to bind a packet socket
-directly to the master network device, using the received VLAN tags to
-map frames to the correct DSS session and adding 18 byte VLAN ethernet
-headers with the appropriate tag on TX.  In this case using a socket
-filter is recommended, matching only the DSS VLAN subset. This avoid
-unnecessary copying of unrelated IP session data to userspace.  For
-example:
-
-  static struct sock_filter dssfilter[] = {
-       /* use special negative offsets to get VLAN tag */
-       BPF_STMT(BPF_LD|BPF_B|BPF_ABS, SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT),
-       BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 1, 0, 6), /* true */
-
-       /* verify DSS VLAN range */
-       BPF_STMT(BPF_LD|BPF_H|BPF_ABS, SKF_AD_OFF + SKF_AD_VLAN_TAG),
-       BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 256, 0, 4),     /* 256 is first DSS VLAN */
-       BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 512, 3, 0),     /* 511 is last DSS VLAN */
-
-       /* verify ethertype */
-        BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 2 * ETH_ALEN),
-        BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETH_P_802_3, 0, 1),
-
-        BPF_STMT(BPF_RET|BPF_K, (u_int)-1),    /* accept */
-        BPF_STMT(BPF_RET|BPF_K, 0),            /* ignore */
-  };
-
-
-
-Tagged IP session 0 VLAN
-------------------------
-As described above, MBIM IP session 0 is treated as special by the
-driver.  It is initially mapped to untagged frames on the wwanY
-network device.
-
-This mapping implies a few restrictions on multiplexed IPS and DSS
-sessions, which may not always be practical:
- - no IPS or DSS session can use a frame size greater than the MTU on
-   IP session 0
- - no IPS or DSS session can be in the up state unless the network
-   device representing IP session 0 also is up
-
-These problems can be avoided by optionally making the driver map IP
-session 0 to a VLAN subdevice, similar to all other IP sessions.  This
-behaviour is triggered by adding a VLAN link for the magic VLAN ID
-4094.  The driver will then immediately start mapping MBIM IP session
-0 to this VLAN, and will drop untagged frames on the master wwanY
-device.
-
-Tip: It might be less confusing to the end user to name this VLAN
-subdevice after the MBIM SessionID instead of the VLAN ID.  For
-example:
-
-  ip link add link wwan0 name wwan0.0 type vlan id 4094
-
-
-VLAN mapping
-------------
-
-Summarizing the cdc_mbim driver mapping described above, we have this
-relationship between VLAN tags on the wwanY network device and MBIM
-sessions on the shared USB data channel:
-
-  VLAN ID       MBIM type   MBIM SessionID           Notes
-  ---------------------------------------------------------
-  untagged      IPS         0                        a)
-  1 - 255       IPS         1 - 255 <VLANID>
-  256 - 511     DSS         0 - 255 <VLANID - 256>
-  512 - 4093                                         b)
-  4094          IPS         0                        c)
-
-    a) if no VLAN ID 4094 link exists, else dropped
-    b) unsupported VLAN range, unconditionally dropped
-    c) if a VLAN ID 4094 link exists, else dropped
-
-
-
-
-References
-==========
-
-[1] USB Implementers Forum, Inc. - "Universal Serial Bus
-      Communications Class Subclass Specification for Mobile Broadband
-      Interface Model", Revision 1.0 (Errata 1), May 1, 2013
-      - http://www.usb.org/developers/docs/devclass_docs/
-
-[2] USB Implementers Forum, Inc. - "Universal Serial Bus
-      Communications Class Subclass Specifications for Network Control
-      Model Devices", Revision 1.0 (Errata 1), November 24, 2010
-      - http://www.usb.org/developers/docs/devclass_docs/
-
-[3] libmbim - "a glib-based library for talking to WWAN modems and
-      devices which speak the Mobile Interface Broadband Model (MBIM)
-      protocol"
-      - http://www.freedesktop.org/wiki/Software/libmbim/
-
-[4] ModemManager - "a DBus-activated daemon which controls mobile
-      broadband (2G/3G/4G) devices and connections"
-      - http://www.freedesktop.org/wiki/Software/ModemManager/
-
-[5] "MBIM (Mobile Broadband Interface Model) Registry"
-       - http://compliance.usb.org/mbim/
-
-[6] "/sys/kernel/debug/usb/devices output format"
-       - Documentation/driver-api/usb/usb.rst
-
-[7] "/sys/bus/usb/devices/.../descriptors"
-       - Documentation/ABI/stable/sysfs-bus-usb
index 905c8a8..69b23cf 100644 (file)
@@ -59,7 +59,7 @@ recomputed for each resulting segment.  See the skbuff.h comment (section 'E')
 for more details.
 
 A driver declares its offload capabilities in netdev->hw_features; see
-Documentation/networking/netdev-features.txt for more.  Note that a device
+Documentation/networking/netdev-features.rst for more.  Note that a device
 which only advertises NETIF_F_IP[V6]_CSUM must still obey the csum_start and
 csum_offset given in the SKB; if it tries to deduce these itself in hardware
 (as some NICs do) the driver should check that the values in the SKB match
diff --git a/Documentation/networking/cops.rst b/Documentation/networking/cops.rst
new file mode 100644 (file)
index 0000000..964ba80
--- /dev/null
@@ -0,0 +1,80 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+========================================
+The COPS LocalTalk Linux driver (cops.c)
+========================================
+
+By Jay Schulist <jschlst@samba.org>
+
+This driver has two modes and they are: Dayna mode and Tangent mode.
+Each mode corresponds with the type of card. It has been found
+that there are 2 main types of cards and all other cards are
+the same and just have different names or only have minor differences
+such as more IO ports. As this driver is tested it will
+become more clear exactly what cards are supported.
+
+Right now these cards are known to work with the COPS driver. The
+LT-200 cards work in a somewhat more limited capacity than the
+DL200 cards, which work very well and are in use by many people.
+
+TANGENT driver mode:
+       - Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200
+
+DAYNA driver mode:
+       - Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95,
+       - Farallon PhoneNET PC III, Farallon PhoneNET PC II
+
+Other cards possibly supported mode unknown though:
+       - Dayna DL2000 (Full length)
+
+The COPS driver defaults to using Dayna mode. To change the driver's
+mode if you built a driver with dual support use board_type=1 or
+board_type=2 for Dayna or Tangent with insmod.
+
+Operation/loading of the driver
+===============================
+
+Use modprobe like this:        /sbin/modprobe cops.o (IO #) (IRQ #)
+If you do not specify any options the driver will try and use the IO = 0x240,
+IRQ = 5. As of right now I would only use IRQ 5 for the card, if autoprobing.
+
+To load multiple COPS driver Localtalk cards you can do one of the following::
+
+       insmod cops io=0x240 irq=5
+       insmod -o cops2 cops io=0x260 irq=3
+
+Or in lilo.conf put something like this::
+
+       append="ether=5,0x240,lt0 ether=3,0x260,lt1"
+
+Then bring up the interface with ifconfig. It will look something like this::
+
+  lt0       Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-F7-00-00-00-00-00-00-00-00
+           inet addr:192.168.1.2  Bcast:192.168.1.255  Mask:255.255.255.0
+           UP BROADCAST RUNNING NOARP MULTICAST  MTU:600  Metric:1
+           RX packets:0 errors:0 dropped:0 overruns:0 frame:0
+           TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 coll:0
+
+Netatalk Configuration
+======================
+
+You will need to configure atalkd with something like the following to make
+it work with the cops.c driver.
+
+* For single LTalk card use::
+
+    dummy -seed -phase 2 -net 2000 -addr 2000.10 -zone "1033"
+    lt0 -seed -phase 1 -net 1000 -addr 1000.50 -zone "1033"
+
+* For multiple cards, Ethernet and LocalTalk::
+
+    eth0 -seed -phase 2 -net 3000 -addr 3000.20 -zone "1033"
+    lt0 -seed -phase 1 -net 1000 -addr 1000.50 -zone "1033"
+
+* For multiple LocalTalk cards, and an Ethernet card.
+
+* Order seems to matter here, Ethernet last::
+
+    lt0 -seed -phase 1 -net 1000 -addr 1000.10 -zone "LocalTalk1"
+    lt1 -seed -phase 1 -net 2000 -addr 2000.20 -zone "LocalTalk2"
+    eth0 -seed -phase 2 -net 3000 -addr 3000.30 -zone "EtherTalk"
diff --git a/Documentation/networking/cops.txt b/Documentation/networking/cops.txt
deleted file mode 100644 (file)
index 3e344b4..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-Text File for the COPS LocalTalk Linux driver (cops.c).
-       By Jay Schulist <jschlst@samba.org>
-
-This driver has two modes and they are: Dayna mode and Tangent mode.
-Each mode corresponds with the type of card. It has been found
-that there are 2 main types of cards and all other cards are
-the same and just have different names or only have minor differences
-such as more IO ports. As this driver is tested it will
-become more clear exactly what cards are supported. 
-
-Right now these cards are known to work with the COPS driver. The
-LT-200 cards work in a somewhat more limited capacity than the
-DL200 cards, which work very well and are in use by many people.
-
-TANGENT driver mode:
-       Tangent ATB-II, Novell NL-1000, Daystar Digital LT-200
-DAYNA driver mode:
-       Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95,
-       Farallon PhoneNET PC III, Farallon PhoneNET PC II
-Other cards possibly supported mode unknown though:
-       Dayna DL2000 (Full length)
-
-The COPS driver defaults to using Dayna mode. To change the driver's 
-mode if you built a driver with dual support use board_type=1 or
-board_type=2 for Dayna or Tangent with insmod.
-
-** Operation/loading of the driver.
-Use modprobe like this:        /sbin/modprobe cops.o (IO #) (IRQ #)
-If you do not specify any options the driver will try and use the IO = 0x240,
-IRQ = 5. As of right now I would only use IRQ 5 for the card, if autoprobing.
-
-To load multiple COPS driver Localtalk cards you can do one of the following.
-
-insmod cops io=0x240 irq=5
-insmod -o cops2 cops io=0x260 irq=3
-
-Or in lilo.conf put something like this:
-       append="ether=5,0x240,lt0 ether=3,0x260,lt1"
-
-Then bring up the interface with ifconfig. It will look something like this:
-lt0       Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-F7-00-00-00-00-00-00-00-00
-          inet addr:192.168.1.2  Bcast:192.168.1.255  Mask:255.255.255.0
-          UP BROADCAST RUNNING NOARP MULTICAST  MTU:600  Metric:1
-          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
-          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 coll:0
-
-** Netatalk Configuration
-You will need to configure atalkd with something like the following to make
-it work with the cops.c driver.
-
-* For single LTalk card use.
-dummy -seed -phase 2 -net 2000 -addr 2000.10 -zone "1033"
-lt0 -seed -phase 1 -net 1000 -addr 1000.50 -zone "1033"
-
-* For multiple cards, Ethernet and LocalTalk.
-eth0 -seed -phase 2 -net 3000 -addr 3000.20 -zone "1033"
-lt0 -seed -phase 1 -net 1000 -addr 1000.50 -zone "1033"
-
-* For multiple LocalTalk cards, and an Ethernet card.
-* Order seems to matter here, Ethernet last.
-lt0 -seed -phase 1 -net 1000 -addr 1000.10 -zone "LocalTalk1"
-lt1 -seed -phase 1 -net 2000 -addr 2000.20 -zone "LocalTalk2"
-eth0 -seed -phase 2 -net 3000 -addr 3000.30 -zone "EtherTalk"
diff --git a/Documentation/networking/cxacru.rst b/Documentation/networking/cxacru.rst
new file mode 100644 (file)
index 0000000..6088af2
--- /dev/null
@@ -0,0 +1,120 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+========================
+ATM cxacru device driver
+========================
+
+Firmware is required for this device: http://accessrunner.sourceforge.net/
+
+While it is capable of managing/maintaining the ADSL connection without the
+module loaded, the device will sometimes stop responding after unloading the
+driver and it is necessary to unplug/remove power to the device to fix this.
+
+Note: support for cxacru-cf.bin has been removed. It was not loaded correctly
+so it had no effect on the device configuration. Fixing it could have stopped
+existing devices working when an invalid configuration is supplied.
+
+There is a script cxacru-cf.py to convert an existing file to the sysfs form.
+
+Detected devices will appear as ATM devices named "cxacru". In /sys/class/atm/
+these are directories named cxacruN where N is the device number. A symlink
+named device points to the USB interface device's directory which contains
+several sysfs attribute files for retrieving device statistics:
+
+* adsl_controller_version
+
+* adsl_headend
+* adsl_headend_environment
+
+       - Information about the remote headend.
+
+* adsl_config
+
+       - Configuration writing interface.
+       - Write parameters in hexadecimal format <index>=<value>,
+         separated by whitespace, e.g.:
+
+               "1=0 a=5"
+
+       - Up to 7 parameters at a time will be sent and the modem will restart
+         the ADSL connection when any value is set. These are logged for future
+         reference.
+
+* downstream_attenuation (dB)
+* downstream_bits_per_frame
+* downstream_rate (kbps)
+* downstream_snr_margin (dB)
+
+       - Downstream stats.
+
+* upstream_attenuation (dB)
+* upstream_bits_per_frame
+* upstream_rate (kbps)
+* upstream_snr_margin (dB)
+* transmitter_power (dBm/Hz)
+
+       - Upstream stats.
+
+* downstream_crc_errors
+* downstream_fec_errors
+* downstream_hec_errors
+* upstream_crc_errors
+* upstream_fec_errors
+* upstream_hec_errors
+
+       - Error counts.
+
+* line_startable
+
+       - Indicates that ADSL support on the device
+         is/can be enabled, see adsl_start.
+
+* line_status
+
+        - "initialising"
+        - "down"
+        - "attempting to activate"
+        - "training"
+        - "channel analysis"
+        - "exchange"
+        - "waiting"
+        - "up"
+
+       Changes between "down" and "attempting to activate"
+       if there is no signal.
+
+* link_status
+
+        - "not connected"
+        - "connected"
+        - "lost"
+
+* mac_address
+
+* modulation
+
+        - "" (when not connected)
+        - "ANSI T1.413"
+        - "ITU-T G.992.1 (G.DMT)"
+        - "ITU-T G.992.2 (G.LITE)"
+
+* startup_attempts
+
+       - Count of total attempts to initialise ADSL.
+
+To enable/disable ADSL, the following can be written to the adsl_state file:
+
+        - "start"
+        - "stop
+        - "restart" (stops, waits 1.5s, then starts)
+        - "poll" (used to resume status polling if it was disabled due to failure)
+
+Changes in adsl/line state are reported via kernel log messages::
+
+       [4942145.150704] ATM dev 0: ADSL state: running
+       [4942243.663766] ATM dev 0: ADSL line: down
+       [4942249.665075] ATM dev 0: ADSL line: attempting to activate
+       [4942253.654954] ATM dev 0: ADSL line: training
+       [4942255.666387] ATM dev 0: ADSL line: channel analysis
+       [4942259.656262] ATM dev 0: ADSL line: exchange
+       [2635357.696901] ATM dev 0: ADSL line: up (8128 kb/s down | 832 kb/s up)
diff --git a/Documentation/networking/cxacru.txt b/Documentation/networking/cxacru.txt
deleted file mode 100644 (file)
index 2cce044..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-Firmware is required for this device: http://accessrunner.sourceforge.net/
-
-While it is capable of managing/maintaining the ADSL connection without the
-module loaded, the device will sometimes stop responding after unloading the
-driver and it is necessary to unplug/remove power to the device to fix this.
-
-Note: support for cxacru-cf.bin has been removed. It was not loaded correctly
-so it had no effect on the device configuration. Fixing it could have stopped
-existing devices working when an invalid configuration is supplied.
-
-There is a script cxacru-cf.py to convert an existing file to the sysfs form.
-
-Detected devices will appear as ATM devices named "cxacru". In /sys/class/atm/
-these are directories named cxacruN where N is the device number. A symlink
-named device points to the USB interface device's directory which contains
-several sysfs attribute files for retrieving device statistics:
-
-* adsl_controller_version
-
-* adsl_headend
-* adsl_headend_environment
-       Information about the remote headend.
-
-* adsl_config
-       Configuration writing interface.
-       Write parameters in hexadecimal format <index>=<value>,
-       separated by whitespace, e.g.:
-               "1=0 a=5"
-       Up to 7 parameters at a time will be sent and the modem will restart
-       the ADSL connection when any value is set. These are logged for future
-       reference.
-
-* downstream_attenuation (dB)
-* downstream_bits_per_frame
-* downstream_rate (kbps)
-* downstream_snr_margin (dB)
-       Downstream stats.
-
-* upstream_attenuation (dB)
-* upstream_bits_per_frame
-* upstream_rate (kbps)
-* upstream_snr_margin (dB)
-* transmitter_power (dBm/Hz)
-       Upstream stats.
-
-* downstream_crc_errors
-* downstream_fec_errors
-* downstream_hec_errors
-* upstream_crc_errors
-* upstream_fec_errors
-* upstream_hec_errors
-       Error counts.
-
-* line_startable
-       Indicates that ADSL support on the device
-       is/can be enabled, see adsl_start.
-
-* line_status
-       "initialising"
-       "down"
-       "attempting to activate"
-       "training"
-       "channel analysis"
-       "exchange"
-       "waiting"
-       "up"
-
-       Changes between "down" and "attempting to activate"
-       if there is no signal.
-
-* link_status
-       "not connected"
-       "connected"
-       "lost"
-
-* mac_address
-
-* modulation
-       "" (when not connected)
-       "ANSI T1.413"
-       "ITU-T G.992.1 (G.DMT)"
-       "ITU-T G.992.2 (G.LITE)"
-
-* startup_attempts
-       Count of total attempts to initialise ADSL.
-
-To enable/disable ADSL, the following can be written to the adsl_state file:
-       "start"
-       "stop
-       "restart" (stops, waits 1.5s, then starts)
-       "poll" (used to resume status polling if it was disabled due to failure)
-
-Changes in adsl/line state are reported via kernel log messages:
-       [4942145.150704] ATM dev 0: ADSL state: running
-       [4942243.663766] ATM dev 0: ADSL line: down
-       [4942249.665075] ATM dev 0: ADSL line: attempting to activate
-       [4942253.654954] ATM dev 0: ADSL line: training
-       [4942255.666387] ATM dev 0: ADSL line: channel analysis
-       [4942259.656262] ATM dev 0: ADSL line: exchange
-       [2635357.696901] ATM dev 0: ADSL line: up (8128 kb/s down | 832 kb/s up)
diff --git a/Documentation/networking/dccp.rst b/Documentation/networking/dccp.rst
new file mode 100644 (file)
index 0000000..dde16be
--- /dev/null
@@ -0,0 +1,216 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============
+DCCP protocol
+=============
+
+
+.. Contents
+   - Introduction
+   - Missing features
+   - Socket options
+   - Sysctl variables
+   - IOCTLs
+   - Other tunables
+   - Notes
+
+
+Introduction
+============
+Datagram Congestion Control Protocol (DCCP) is an unreliable, connection
+oriented protocol designed to solve issues present in UDP and TCP, particularly
+for real-time and multimedia (streaming) traffic.
+It divides into a base protocol (RFC 4340) and pluggable congestion control
+modules called CCIDs. Like pluggable TCP congestion control, at least one CCID
+needs to be enabled in order for the protocol to function properly. In the Linux
+implementation, this is the TCP-like CCID2 (RFC 4341). Additional CCIDs, such as
+the TCP-friendly CCID3 (RFC 4342), are optional.
+For a brief introduction to CCIDs and suggestions for choosing a CCID to match
+given applications, see section 10 of RFC 4340.
+
+It has a base protocol and pluggable congestion control IDs (CCIDs).
+
+DCCP is a Proposed Standard (RFC 2026), and the homepage for DCCP as a protocol
+is at http://www.ietf.org/html.charters/dccp-charter.html
+
+
+Missing features
+================
+The Linux DCCP implementation does not currently support all the features that are
+specified in RFCs 4340...42.
+
+The known bugs are at:
+
+       http://www.linuxfoundation.org/collaborate/workgroups/networking/todo#DCCP
+
+For more up-to-date versions of the DCCP implementation, please consider using
+the experimental DCCP test tree; instructions for checking this out are on:
+http://www.linuxfoundation.org/collaborate/workgroups/networking/dccp_testing#Experimental_DCCP_source_tree
+
+
+Socket options
+==============
+DCCP_SOCKOPT_QPOLICY_ID sets the dequeuing policy for outgoing packets. It takes
+a policy ID as argument and can only be set before the connection (i.e. changes
+during an established connection are not supported). Currently, two policies are
+defined: the "simple" policy (DCCPQ_POLICY_SIMPLE), which does nothing special,
+and a priority-based variant (DCCPQ_POLICY_PRIO). The latter allows to pass an
+u32 priority value as ancillary data to sendmsg(), where higher numbers indicate
+a higher packet priority (similar to SO_PRIORITY). This ancillary data needs to
+be formatted using a cmsg(3) message header filled in as follows::
+
+       cmsg->cmsg_level = SOL_DCCP;
+       cmsg->cmsg_type  = DCCP_SCM_PRIORITY;
+       cmsg->cmsg_len   = CMSG_LEN(sizeof(uint32_t));  /* or CMSG_LEN(4) */
+
+DCCP_SOCKOPT_QPOLICY_TXQLEN sets the maximum length of the output queue. A zero
+value is always interpreted as unbounded queue length. If different from zero,
+the interpretation of this parameter depends on the current dequeuing policy
+(see above): the "simple" policy will enforce a fixed queue size by returning
+EAGAIN, whereas the "prio" policy enforces a fixed queue length by dropping the
+lowest-priority packet first. The default value for this parameter is
+initialised from /proc/sys/net/dccp/default/tx_qlen.
+
+DCCP_SOCKOPT_SERVICE sets the service. The specification mandates use of
+service codes (RFC 4340, sec. 8.1.2); if this socket option is not set,
+the socket will fall back to 0 (which means that no meaningful service code
+is present). On active sockets this is set before connect(); specifying more
+than one code has no effect (all subsequent service codes are ignored). The
+case is different for passive sockets, where multiple service codes (up to 32)
+can be set before calling bind().
+
+DCCP_SOCKOPT_GET_CUR_MPS is read-only and retrieves the current maximum packet
+size (application payload size) in bytes, see RFC 4340, section 14.
+
+DCCP_SOCKOPT_AVAILABLE_CCIDS is also read-only and returns the list of CCIDs
+supported by the endpoint. The option value is an array of type uint8_t whose
+size is passed as option length. The minimum array size is 4 elements, the
+value returned in the optlen argument always reflects the true number of
+built-in CCIDs.
+
+DCCP_SOCKOPT_CCID is write-only and sets both the TX and RX CCIDs at the same
+time, combining the operation of the next two socket options. This option is
+preferable over the latter two, since often applications will use the same
+type of CCID for both directions; and mixed use of CCIDs is not currently well
+understood. This socket option takes as argument at least one uint8_t value, or
+an array of uint8_t values, which must match available CCIDS (see above). CCIDs
+must be registered on the socket before calling connect() or listen().
+
+DCCP_SOCKOPT_TX_CCID is read/write. It returns the current CCID (if set) or sets
+the preference list for the TX CCID, using the same format as DCCP_SOCKOPT_CCID.
+Please note that the getsockopt argument type here is ``int``, not uint8_t.
+
+DCCP_SOCKOPT_RX_CCID is analogous to DCCP_SOCKOPT_TX_CCID, but for the RX CCID.
+
+DCCP_SOCKOPT_SERVER_TIMEWAIT enables the server (listening socket) to hold
+timewait state when closing the connection (RFC 4340, 8.3). The usual case is
+that the closing server sends a CloseReq, whereupon the client holds timewait
+state. When this boolean socket option is on, the server sends a Close instead
+and will enter TIMEWAIT. This option must be set after accept() returns.
+
+DCCP_SOCKOPT_SEND_CSCOV and DCCP_SOCKOPT_RECV_CSCOV are used for setting the
+partial checksum coverage (RFC 4340, sec. 9.2). The default is that checksums
+always cover the entire packet and that only fully covered application data is
+accepted by the receiver. Hence, when using this feature on the sender, it must
+be enabled at the receiver, too with suitable choice of CsCov.
+
+DCCP_SOCKOPT_SEND_CSCOV sets the sender checksum coverage. Values in the
+       range 0..15 are acceptable. The default setting is 0 (full coverage),
+       values between 1..15 indicate partial coverage.
+
+DCCP_SOCKOPT_RECV_CSCOV is for the receiver and has a different meaning: it
+       sets a threshold, where again values 0..15 are acceptable. The default
+       of 0 means that all packets with a partial coverage will be discarded.
+       Values in the range 1..15 indicate that packets with minimally such a
+       coverage value are also acceptable. The higher the number, the more
+       restrictive this setting (see [RFC 4340, sec. 9.2.1]). Partial coverage
+       settings are inherited to the child socket after accept().
+
+The following two options apply to CCID 3 exclusively and are getsockopt()-only.
+In either case, a TFRC info struct (defined in <linux/tfrc.h>) is returned.
+
+DCCP_SOCKOPT_CCID_RX_INFO
+       Returns a ``struct tfrc_rx_info`` in optval; the buffer for optval and
+       optlen must be set to at least sizeof(struct tfrc_rx_info).
+
+DCCP_SOCKOPT_CCID_TX_INFO
+       Returns a ``struct tfrc_tx_info`` in optval; the buffer for optval and
+       optlen must be set to at least sizeof(struct tfrc_tx_info).
+
+On unidirectional connections it is useful to close the unused half-connection
+via shutdown (SHUT_WR or SHUT_RD): this will reduce per-packet processing costs.
+
+
+Sysctl variables
+================
+Several DCCP default parameters can be managed by the following sysctls
+(sysctl net.dccp.default or /proc/sys/net/dccp/default):
+
+request_retries
+       The number of active connection initiation retries (the number of
+       Requests minus one) before timing out. In addition, it also governs
+       the behaviour of the other, passive side: this variable also sets
+       the number of times DCCP repeats sending a Response when the initial
+       handshake does not progress from RESPOND to OPEN (i.e. when no Ack
+       is received after the initial Request).  This value should be greater
+       than 0, suggested is less than 10. Analogue of tcp_syn_retries.
+
+retries1
+       How often a DCCP Response is retransmitted until the listening DCCP
+       side considers its connecting peer dead. Analogue of tcp_retries1.
+
+retries2
+       The number of times a general DCCP packet is retransmitted. This has
+       importance for retransmitted acknowledgments and feature negotiation,
+       data packets are never retransmitted. Analogue of tcp_retries2.
+
+tx_ccid = 2
+       Default CCID for the sender-receiver half-connection. Depending on the
+       choice of CCID, the Send Ack Vector feature is enabled automatically.
+
+rx_ccid = 2
+       Default CCID for the receiver-sender half-connection; see tx_ccid.
+
+seq_window = 100
+       The initial sequence window (sec. 7.5.2) of the sender. This influences
+       the local ackno validity and the remote seqno validity windows (7.5.1).
+       Values in the range Wmin = 32 (RFC 4340, 7.5.2) up to 2^32-1 can be set.
+
+tx_qlen = 5
+       The size of the transmit buffer in packets. A value of 0 corresponds
+       to an unbounded transmit buffer.
+
+sync_ratelimit = 125 ms
+       The timeout between subsequent DCCP-Sync packets sent in response to
+       sequence-invalid packets on the same socket (RFC 4340, 7.5.4). The unit
+       of this parameter is milliseconds; a value of 0 disables rate-limiting.
+
+
+IOCTLS
+======
+FIONREAD
+       Works as in udp(7): returns in the ``int`` argument pointer the size of
+       the next pending datagram in bytes, or 0 when no datagram is pending.
+
+
+Other tunables
+==============
+Per-route rto_min support
+       CCID-2 supports the RTAX_RTO_MIN per-route setting for the minimum value
+       of the RTO timer. This setting can be modified via the 'rto_min' option
+       of iproute2; for example::
+
+               > ip route change 10.0.0.0/24   rto_min 250j dev wlan0
+               > ip route add    10.0.0.254/32 rto_min 800j dev wlan0
+               > ip route show dev wlan0
+
+       CCID-3 also supports the rto_min setting: it is used to define the lower
+       bound for the expiry of the nofeedback timer. This can be useful on LANs
+       with very low RTTs (e.g., loopback, Gbit ethernet).
+
+
+Notes
+=====
+DCCP does not travel through NAT successfully at present on many boxes. This is
+because the checksum covers the pseudo-header as per TCP and UDP. Linux NAT
+support for DCCP has been added.
diff --git a/Documentation/networking/dccp.txt b/Documentation/networking/dccp.txt
deleted file mode 100644 (file)
index 55c575f..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-DCCP protocol
-=============
-
-
-Contents
-========
-- Introduction
-- Missing features
-- Socket options
-- Sysctl variables
-- IOCTLs
-- Other tunables
-- Notes
-
-
-Introduction
-============
-Datagram Congestion Control Protocol (DCCP) is an unreliable, connection
-oriented protocol designed to solve issues present in UDP and TCP, particularly
-for real-time and multimedia (streaming) traffic.
-It divides into a base protocol (RFC 4340) and pluggable congestion control
-modules called CCIDs. Like pluggable TCP congestion control, at least one CCID
-needs to be enabled in order for the protocol to function properly. In the Linux
-implementation, this is the TCP-like CCID2 (RFC 4341). Additional CCIDs, such as
-the TCP-friendly CCID3 (RFC 4342), are optional.
-For a brief introduction to CCIDs and suggestions for choosing a CCID to match
-given applications, see section 10 of RFC 4340.
-
-It has a base protocol and pluggable congestion control IDs (CCIDs).
-
-DCCP is a Proposed Standard (RFC 2026), and the homepage for DCCP as a protocol
-is at http://www.ietf.org/html.charters/dccp-charter.html
-
-
-Missing features
-================
-The Linux DCCP implementation does not currently support all the features that are
-specified in RFCs 4340...42.
-
-The known bugs are at:
-       http://www.linuxfoundation.org/collaborate/workgroups/networking/todo#DCCP
-
-For more up-to-date versions of the DCCP implementation, please consider using
-the experimental DCCP test tree; instructions for checking this out are on:
-http://www.linuxfoundation.org/collaborate/workgroups/networking/dccp_testing#Experimental_DCCP_source_tree
-
-
-Socket options
-==============
-DCCP_SOCKOPT_QPOLICY_ID sets the dequeuing policy for outgoing packets. It takes
-a policy ID as argument and can only be set before the connection (i.e. changes
-during an established connection are not supported). Currently, two policies are
-defined: the "simple" policy (DCCPQ_POLICY_SIMPLE), which does nothing special,
-and a priority-based variant (DCCPQ_POLICY_PRIO). The latter allows to pass an
-u32 priority value as ancillary data to sendmsg(), where higher numbers indicate
-a higher packet priority (similar to SO_PRIORITY). This ancillary data needs to
-be formatted using a cmsg(3) message header filled in as follows:
-       cmsg->cmsg_level = SOL_DCCP;
-       cmsg->cmsg_type  = DCCP_SCM_PRIORITY;
-       cmsg->cmsg_len   = CMSG_LEN(sizeof(uint32_t));  /* or CMSG_LEN(4) */
-
-DCCP_SOCKOPT_QPOLICY_TXQLEN sets the maximum length of the output queue. A zero
-value is always interpreted as unbounded queue length. If different from zero,
-the interpretation of this parameter depends on the current dequeuing policy
-(see above): the "simple" policy will enforce a fixed queue size by returning
-EAGAIN, whereas the "prio" policy enforces a fixed queue length by dropping the
-lowest-priority packet first. The default value for this parameter is
-initialised from /proc/sys/net/dccp/default/tx_qlen.
-
-DCCP_SOCKOPT_SERVICE sets the service. The specification mandates use of
-service codes (RFC 4340, sec. 8.1.2); if this socket option is not set,
-the socket will fall back to 0 (which means that no meaningful service code
-is present). On active sockets this is set before connect(); specifying more
-than one code has no effect (all subsequent service codes are ignored). The
-case is different for passive sockets, where multiple service codes (up to 32)
-can be set before calling bind().
-
-DCCP_SOCKOPT_GET_CUR_MPS is read-only and retrieves the current maximum packet
-size (application payload size) in bytes, see RFC 4340, section 14.
-
-DCCP_SOCKOPT_AVAILABLE_CCIDS is also read-only and returns the list of CCIDs
-supported by the endpoint. The option value is an array of type uint8_t whose
-size is passed as option length. The minimum array size is 4 elements, the
-value returned in the optlen argument always reflects the true number of
-built-in CCIDs.
-
-DCCP_SOCKOPT_CCID is write-only and sets both the TX and RX CCIDs at the same
-time, combining the operation of the next two socket options. This option is
-preferable over the latter two, since often applications will use the same
-type of CCID for both directions; and mixed use of CCIDs is not currently well
-understood. This socket option takes as argument at least one uint8_t value, or
-an array of uint8_t values, which must match available CCIDS (see above). CCIDs
-must be registered on the socket before calling connect() or listen().
-
-DCCP_SOCKOPT_TX_CCID is read/write. It returns the current CCID (if set) or sets
-the preference list for the TX CCID, using the same format as DCCP_SOCKOPT_CCID.
-Please note that the getsockopt argument type here is `int', not uint8_t.
-
-DCCP_SOCKOPT_RX_CCID is analogous to DCCP_SOCKOPT_TX_CCID, but for the RX CCID.
-
-DCCP_SOCKOPT_SERVER_TIMEWAIT enables the server (listening socket) to hold
-timewait state when closing the connection (RFC 4340, 8.3). The usual case is
-that the closing server sends a CloseReq, whereupon the client holds timewait
-state. When this boolean socket option is on, the server sends a Close instead
-and will enter TIMEWAIT. This option must be set after accept() returns.
-
-DCCP_SOCKOPT_SEND_CSCOV and DCCP_SOCKOPT_RECV_CSCOV are used for setting the
-partial checksum coverage (RFC 4340, sec. 9.2). The default is that checksums
-always cover the entire packet and that only fully covered application data is
-accepted by the receiver. Hence, when using this feature on the sender, it must
-be enabled at the receiver, too with suitable choice of CsCov.
-
-DCCP_SOCKOPT_SEND_CSCOV sets the sender checksum coverage. Values in the
-       range 0..15 are acceptable. The default setting is 0 (full coverage),
-       values between 1..15 indicate partial coverage.
-DCCP_SOCKOPT_RECV_CSCOV is for the receiver and has a different meaning: it
-       sets a threshold, where again values 0..15 are acceptable. The default
-       of 0 means that all packets with a partial coverage will be discarded.
-       Values in the range 1..15 indicate that packets with minimally such a
-       coverage value are also acceptable. The higher the number, the more
-       restrictive this setting (see [RFC 4340, sec. 9.2.1]). Partial coverage
-       settings are inherited to the child socket after accept().
-
-The following two options apply to CCID 3 exclusively and are getsockopt()-only.
-In either case, a TFRC info struct (defined in <linux/tfrc.h>) is returned.
-DCCP_SOCKOPT_CCID_RX_INFO
-       Returns a `struct tfrc_rx_info' in optval; the buffer for optval and
-       optlen must be set to at least sizeof(struct tfrc_rx_info).
-DCCP_SOCKOPT_CCID_TX_INFO
-       Returns a `struct tfrc_tx_info' in optval; the buffer for optval and
-       optlen must be set to at least sizeof(struct tfrc_tx_info).
-
-On unidirectional connections it is useful to close the unused half-connection
-via shutdown (SHUT_WR or SHUT_RD): this will reduce per-packet processing costs.
-
-
-Sysctl variables
-================
-Several DCCP default parameters can be managed by the following sysctls
-(sysctl net.dccp.default or /proc/sys/net/dccp/default):
-
-request_retries
-       The number of active connection initiation retries (the number of
-       Requests minus one) before timing out. In addition, it also governs
-       the behaviour of the other, passive side: this variable also sets
-       the number of times DCCP repeats sending a Response when the initial
-       handshake does not progress from RESPOND to OPEN (i.e. when no Ack
-       is received after the initial Request).  This value should be greater
-       than 0, suggested is less than 10. Analogue of tcp_syn_retries.
-
-retries1
-       How often a DCCP Response is retransmitted until the listening DCCP
-       side considers its connecting peer dead. Analogue of tcp_retries1.
-
-retries2
-       The number of times a general DCCP packet is retransmitted. This has
-       importance for retransmitted acknowledgments and feature negotiation,
-       data packets are never retransmitted. Analogue of tcp_retries2.
-
-tx_ccid = 2
-       Default CCID for the sender-receiver half-connection. Depending on the
-       choice of CCID, the Send Ack Vector feature is enabled automatically.
-
-rx_ccid = 2
-       Default CCID for the receiver-sender half-connection; see tx_ccid.
-
-seq_window = 100
-       The initial sequence window (sec. 7.5.2) of the sender. This influences
-       the local ackno validity and the remote seqno validity windows (7.5.1).
-       Values in the range Wmin = 32 (RFC 4340, 7.5.2) up to 2^32-1 can be set.
-
-tx_qlen = 5
-       The size of the transmit buffer in packets. A value of 0 corresponds
-       to an unbounded transmit buffer.
-
-sync_ratelimit = 125 ms
-       The timeout between subsequent DCCP-Sync packets sent in response to
-       sequence-invalid packets on the same socket (RFC 4340, 7.5.4). The unit
-       of this parameter is milliseconds; a value of 0 disables rate-limiting.
-
-
-IOCTLS
-======
-FIONREAD
-       Works as in udp(7): returns in the `int' argument pointer the size of
-       the next pending datagram in bytes, or 0 when no datagram is pending.
-
-
-Other tunables
-==============
-Per-route rto_min support
-       CCID-2 supports the RTAX_RTO_MIN per-route setting for the minimum value
-       of the RTO timer. This setting can be modified via the 'rto_min' option
-       of iproute2; for example:
-               > ip route change 10.0.0.0/24   rto_min 250j dev wlan0
-               > ip route add    10.0.0.254/32 rto_min 800j dev wlan0
-               > ip route show dev wlan0
-       CCID-3 also supports the rto_min setting: it is used to define the lower
-       bound for the expiry of the nofeedback timer. This can be useful on LANs
-       with very low RTTs (e.g., loopback, Gbit ethernet).
-
-
-Notes
-=====
-DCCP does not travel through NAT successfully at present on many boxes. This is
-because the checksum covers the pseudo-header as per TCP and UDP. Linux NAT
-support for DCCP has been added.
diff --git a/Documentation/networking/dctcp.rst b/Documentation/networking/dctcp.rst
new file mode 100644 (file)
index 0000000..4cc8bb2
--- /dev/null
@@ -0,0 +1,52 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================
+DCTCP (DataCenter TCP)
+======================
+
+DCTCP is an enhancement to the TCP congestion control algorithm for data
+center networks and leverages Explicit Congestion Notification (ECN) in
+the data center network to provide multi-bit feedback to the end hosts.
+
+To enable it on end hosts::
+
+  sysctl -w net.ipv4.tcp_congestion_control=dctcp
+  sysctl -w net.ipv4.tcp_ecn_fallback=0 (optional)
+
+All switches in the data center network running DCTCP must support ECN
+marking and be configured for marking when reaching defined switch buffer
+thresholds. The default ECN marking threshold heuristic for DCTCP on
+switches is 20 packets (30KB) at 1Gbps, and 65 packets (~100KB) at 10Gbps,
+but might need further careful tweaking.
+
+For more details, see below documents:
+
+Paper:
+
+The algorithm is further described in detail in the following two
+SIGCOMM/SIGMETRICS papers:
+
+ i) Mohammad Alizadeh, Albert Greenberg, David A. Maltz, Jitendra Padhye,
+    Parveen Patel, Balaji Prabhakar, Sudipta Sengupta, and Murari Sridharan:
+
+      "Data Center TCP (DCTCP)", Data Center Networks session"
+
+      Proc. ACM SIGCOMM, New Delhi, 2010.
+
+    http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp-final.pdf
+    http://www.sigcomm.org/ccr/papers/2010/October/1851275.1851192
+
+ii) Mohammad Alizadeh, Adel Javanmard, and Balaji Prabhakar:
+
+      "Analysis of DCTCP: Stability, Convergence, and Fairness"
+      Proc. ACM SIGMETRICS, San Jose, 2011.
+
+    http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp_analysis-full.pdf
+
+IETF informational draft:
+
+  http://tools.ietf.org/html/draft-bensley-tcpm-dctcp-00
+
+DCTCP site:
+
+  http://simula.stanford.edu/~alizade/Site/DCTCP.html
diff --git a/Documentation/networking/dctcp.txt b/Documentation/networking/dctcp.txt
deleted file mode 100644 (file)
index 13a8577..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-DCTCP (DataCenter TCP)
-----------------------
-
-DCTCP is an enhancement to the TCP congestion control algorithm for data
-center networks and leverages Explicit Congestion Notification (ECN) in
-the data center network to provide multi-bit feedback to the end hosts.
-
-To enable it on end hosts:
-
-  sysctl -w net.ipv4.tcp_congestion_control=dctcp
-  sysctl -w net.ipv4.tcp_ecn_fallback=0 (optional)
-
-All switches in the data center network running DCTCP must support ECN
-marking and be configured for marking when reaching defined switch buffer
-thresholds. The default ECN marking threshold heuristic for DCTCP on
-switches is 20 packets (30KB) at 1Gbps, and 65 packets (~100KB) at 10Gbps,
-but might need further careful tweaking.
-
-For more details, see below documents:
-
-Paper:
-
-The algorithm is further described in detail in the following two
-SIGCOMM/SIGMETRICS papers:
-
- i) Mohammad Alizadeh, Albert Greenberg, David A. Maltz, Jitendra Padhye,
-    Parveen Patel, Balaji Prabhakar, Sudipta Sengupta, and Murari Sridharan:
-      "Data Center TCP (DCTCP)", Data Center Networks session
-      Proc. ACM SIGCOMM, New Delhi, 2010.
-    http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp-final.pdf
-    http://www.sigcomm.org/ccr/papers/2010/October/1851275.1851192
-
-ii) Mohammad Alizadeh, Adel Javanmard, and Balaji Prabhakar:
-      "Analysis of DCTCP: Stability, Convergence, and Fairness"
-      Proc. ACM SIGMETRICS, San Jose, 2011.
-    http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp_analysis-full.pdf
-
-IETF informational draft:
-
-  http://tools.ietf.org/html/draft-bensley-tcpm-dctcp-00
-
-DCTCP site:
-
-  http://simula.stanford.edu/~alizade/Site/DCTCP.html
diff --git a/Documentation/networking/decnet.rst b/Documentation/networking/decnet.rst
new file mode 100644 (file)
index 0000000..b8bc11f
--- /dev/null
@@ -0,0 +1,243 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================================
+Linux DECnet Networking Layer Information
+=========================================
+
+1. Other documentation....
+==========================
+
+   - Project Home Pages
+     - http://www.chygwyn.com/                            - Kernel info
+     - http://linux-decnet.sourceforge.net/                - Userland tools
+     - http://www.sourceforge.net/projects/linux-decnet/   - Status page
+
+2. Configuring the kernel
+=========================
+
+Be sure to turn on the following options:
+
+    - CONFIG_DECNET (obviously)
+    - CONFIG_PROC_FS (to see what's going on)
+    - CONFIG_SYSCTL (for easy configuration)
+
+if you want to try out router support (not properly debugged yet)
+you'll need the following options as well...
+
+    - CONFIG_DECNET_ROUTER (to be able to add/delete routes)
+    - CONFIG_NETFILTER (will be required for the DECnet routing daemon)
+
+Don't turn on SIOCGIFCONF support for DECnet unless you are really sure
+that you need it, in general you won't and it can cause ifconfig to
+malfunction.
+
+Run time configuration has changed slightly from the 2.4 system. If you
+want to configure an endnode, then the simplified procedure is as follows:
+
+ - Set the MAC address on your ethernet card before starting _any_ other
+   network protocols.
+
+As soon as your network card is brought into the UP state, DECnet should
+start working. If you need something more complicated or are unsure how
+to set the MAC address, see the next section. Also all configurations which
+worked with 2.4 will work under 2.5 with no change.
+
+3. Command line options
+=======================
+
+You can set a DECnet address on the kernel command line for compatibility
+with the 2.4 configuration procedure, but in general it's not needed any more.
+If you do st a DECnet address on the command line, it has only one purpose
+which is that its added to the addresses on the loopback device.
+
+With 2.4 kernels, DECnet would only recognise addresses as local if they
+were added to the loopback device. In 2.5, any local interface address
+can be used to loop back to the local machine. Of course this does not
+prevent you adding further addresses to the loopback device if you
+want to.
+
+N.B. Since the address list of an interface determines the addresses for
+which "hello" messages are sent, if you don't set an address on the loopback
+interface then you won't see any entries in /proc/net/neigh for the local
+host until such time as you start a connection. This doesn't affect the
+operation of the local communications in any other way though.
+
+The kernel command line takes options looking like the following::
+
+    decnet.addr=1,2
+
+the two numbers are the node address 1,2 = 1.2 For 2.2.xx kernels
+and early 2.3.xx kernels, you must use a comma when specifying the
+DECnet address like this. For more recent 2.3.xx kernels, you may
+use almost any character except space, although a `.` would be the most
+obvious choice :-)
+
+There used to be a third number specifying the node type. This option
+has gone away in favour of a per interface node type. This is now set
+using /proc/sys/net/decnet/conf/<dev>/forwarding. This file can be
+set with a single digit, 0=EndNode, 1=L1 Router and  2=L2 Router.
+
+There are also equivalent options for modules. The node address can
+also be set through the /proc/sys/net/decnet/ files, as can other system
+parameters.
+
+Currently the only supported devices are ethernet and ip_gre. The
+ethernet address of your ethernet card has to be set according to the DECnet
+address of the node in order for it to be autoconfigured (and then appear in
+/proc/net/decnet_dev). There is a utility available at the above
+FTP sites called dn2ethaddr which can compute the correct ethernet
+address to use. The address can be set by ifconfig either before or
+at the time the device is brought up. If you are using RedHat you can
+add the line::
+
+    MACADDR=AA:00:04:00:03:04
+
+or something similar, to /etc/sysconfig/network-scripts/ifcfg-eth0 or
+wherever your network card's configuration lives. Setting the MAC address
+of your ethernet card to an address starting with "hi-ord" will cause a
+DECnet address which matches to be added to the interface (which you can
+verify with iproute2).
+
+The default device for routing can be set through the /proc filesystem
+by setting /proc/sys/net/decnet/default_device to the
+device you want DECnet to route packets out of when no specific route
+is available. Usually this will be eth0, for example::
+
+    echo -n "eth0" >/proc/sys/net/decnet/default_device
+
+If you don't set the default device, then it will default to the first
+ethernet card which has been autoconfigured as described above. You can
+confirm that by looking in the default_device file of course.
+
+There is a list of what the other files under /proc/sys/net/decnet/ do
+on the kernel patch web site (shown above).
+
+4. Run time kernel configuration
+================================
+
+
+This is either done through the sysctl/proc interface (see the kernel web
+pages for details on what the various options do) or through the iproute2
+package in the same way as IPv4/6 configuration is performed.
+
+Documentation for iproute2 is included with the package, although there is
+as yet no specific section on DECnet, most of the features apply to both
+IP and DECnet, albeit with DECnet addresses instead of IP addresses and
+a reduced functionality.
+
+If you want to configure a DECnet router you'll need the iproute2 package
+since its the _only_ way to add and delete routes currently. Eventually
+there will be a routing daemon to send and receive routing messages for
+each interface and update the kernel routing tables accordingly. The
+routing daemon will use netfilter to listen to routing packets, and
+rtnetlink to update the kernels routing tables.
+
+The DECnet raw socket layer has been removed since it was there purely
+for use by the routing daemon which will now use netfilter (a much cleaner
+and more generic solution) instead.
+
+5. How can I tell if its working?
+=================================
+
+Here is a quick guide of what to look for in order to know if your DECnet
+kernel subsystem is working.
+
+   - Is the node address set (see /proc/sys/net/decnet/node_address)
+   - Is the node of the correct type
+     (see /proc/sys/net/decnet/conf/<dev>/forwarding)
+   - Is the Ethernet MAC address of each Ethernet card set to match
+     the DECnet address. If in doubt use the dn2ethaddr utility available
+     at the ftp archive.
+   - If the previous two steps are satisfied, and the Ethernet card is up,
+     you should find that it is listed in /proc/net/decnet_dev and also
+     that it appears as a directory in /proc/sys/net/decnet/conf/. The
+     loopback device (lo) should also appear and is required to communicate
+     within a node.
+   - If you have any DECnet routers on your network, they should appear
+     in /proc/net/decnet_neigh, otherwise this file will only contain the
+     entry for the node itself (if it doesn't check to see if lo is up).
+   - If you want to send to any node which is not listed in the
+     /proc/net/decnet_neigh file, you'll need to set the default device
+     to point to an Ethernet card with connection to a router. This is
+     again done with the /proc/sys/net/decnet/default_device file.
+   - Try starting a simple server and client, like the dnping/dnmirror
+     over the loopback interface. With luck they should communicate.
+     For this step and those after, you'll need the DECnet library
+     which can be obtained from the above ftp sites as well as the
+     actual utilities themselves.
+   - If this seems to work, then try talking to a node on your local
+     network, and see if you can obtain the same results.
+   - At this point you are on your own... :-)
+
+6. How to send a bug report
+===========================
+
+If you've found a bug and want to report it, then there are several things
+you can do to help me work out exactly what it is that is wrong. Useful
+information (_most_ of which _is_ _essential_) includes:
+
+ - What kernel version are you running ?
+ - What version of the patch are you running ?
+ - How far though the above set of tests can you get ?
+ - What is in the /proc/decnet* files and /proc/sys/net/decnet/* files ?
+ - Which services are you running ?
+ - Which client caused the problem ?
+ - How much data was being transferred ?
+ - Was the network congested ?
+ - How can the problem be reproduced ?
+ - Can you use tcpdump to get a trace ? (N.B. Most (all?) versions of
+   tcpdump don't understand how to dump DECnet properly, so including
+   the hex listing of the packet contents is _essential_, usually the -x flag.
+   You may also need to increase the length grabbed with the -s flag. The
+   -e flag also provides very useful information (ethernet MAC addresses))
+
+7. MAC FAQ
+==========
+
+A quick FAQ on ethernet MAC addresses to explain how Linux and DECnet
+interact and how to get the best performance from your hardware.
+
+Ethernet cards are designed to normally only pass received network frames
+to a host computer when they are addressed to it, or to the broadcast address.
+
+Linux has an interface which allows the setting of extra addresses for
+an ethernet card to listen to. If the ethernet card supports it, the
+filtering operation will be done in hardware, if not the extra unwanted packets
+received will be discarded by the host computer. In the latter case,
+significant processor time and bus bandwidth can be used up on a busy
+network (see the NAPI documentation for a longer explanation of these
+effects).
+
+DECnet makes use of this interface to allow running DECnet on an ethernet
+card which has already been configured using TCP/IP (presumably using the
+built in MAC address of the card, as usual) and/or to allow multiple DECnet
+addresses on each physical interface. If you do this, be aware that if your
+ethernet card doesn't support perfect hashing in its MAC address filter
+then your computer will be doing more work than required. Some cards
+will simply set themselves into promiscuous mode in order to receive
+packets from the DECnet specified addresses. So if you have one of these
+cards its better to set the MAC address of the card as described above
+to gain the best efficiency. Better still is to use a card which supports
+NAPI as well.
+
+
+8. Mailing list
+===============
+
+If you are keen to get involved in development, or want to ask questions
+about configuration, or even just report bugs, then there is a mailing
+list that you can join, details are at:
+
+http://sourceforge.net/mail/?group_id=4993
+
+9. Legal Info
+=============
+
+The Linux DECnet project team have placed their code under the GPL. The
+software is provided "as is" and without warranty express or implied.
+DECnet is a trademark of Compaq. This software is not a product of
+Compaq. We acknowledge the help of people at Compaq in providing extra
+documentation above and beyond what was previously publicly available.
+
+Steve Whitehouse <SteveW@ACM.org>
+
diff --git a/Documentation/networking/decnet.txt b/Documentation/networking/decnet.txt
deleted file mode 100644 (file)
index d192f8b..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-                    Linux DECnet Networking Layer Information
-                   ===========================================
-
-1) Other documentation....
-
-   o Project Home Pages
-       http://www.chygwyn.com/                             - Kernel info
-       http://linux-decnet.sourceforge.net/                - Userland tools
-       http://www.sourceforge.net/projects/linux-decnet/   - Status page
-
-2) Configuring the kernel
-
-Be sure to turn on the following options:
-
-    CONFIG_DECNET (obviously)
-    CONFIG_PROC_FS (to see what's going on)
-    CONFIG_SYSCTL (for easy configuration)
-
-if you want to try out router support (not properly debugged yet)
-you'll need the following options as well...
-
-    CONFIG_DECNET_ROUTER (to be able to add/delete routes)
-    CONFIG_NETFILTER (will be required for the DECnet routing daemon)
-
-Don't turn on SIOCGIFCONF support for DECnet unless you are really sure
-that you need it, in general you won't and it can cause ifconfig to
-malfunction.
-
-Run time configuration has changed slightly from the 2.4 system. If you
-want to configure an endnode, then the simplified procedure is as follows:
-
- o Set the MAC address on your ethernet card before starting _any_ other
-   network protocols.
-
-As soon as your network card is brought into the UP state, DECnet should
-start working. If you need something more complicated or are unsure how
-to set the MAC address, see the next section. Also all configurations which
-worked with 2.4 will work under 2.5 with no change.
-
-3) Command line options
-
-You can set a DECnet address on the kernel command line for compatibility
-with the 2.4 configuration procedure, but in general it's not needed any more.
-If you do st a DECnet address on the command line, it has only one purpose
-which is that its added to the addresses on the loopback device.
-
-With 2.4 kernels, DECnet would only recognise addresses as local if they
-were added to the loopback device. In 2.5, any local interface address
-can be used to loop back to the local machine. Of course this does not
-prevent you adding further addresses to the loopback device if you
-want to.
-
-N.B. Since the address list of an interface determines the addresses for
-which "hello" messages are sent, if you don't set an address on the loopback
-interface then you won't see any entries in /proc/net/neigh for the local
-host until such time as you start a connection. This doesn't affect the
-operation of the local communications in any other way though.
-
-The kernel command line takes options looking like the following:
-
-    decnet.addr=1,2
-
-the two numbers are the node address 1,2 = 1.2 For 2.2.xx kernels
-and early 2.3.xx kernels, you must use a comma when specifying the
-DECnet address like this. For more recent 2.3.xx kernels, you may
-use almost any character except space, although a `.` would be the most
-obvious choice :-)
-
-There used to be a third number specifying the node type. This option
-has gone away in favour of a per interface node type. This is now set
-using /proc/sys/net/decnet/conf/<dev>/forwarding. This file can be
-set with a single digit, 0=EndNode, 1=L1 Router and  2=L2 Router.
-
-There are also equivalent options for modules. The node address can
-also be set through the /proc/sys/net/decnet/ files, as can other system
-parameters.
-
-Currently the only supported devices are ethernet and ip_gre. The
-ethernet address of your ethernet card has to be set according to the DECnet
-address of the node in order for it to be autoconfigured (and then appear in
-/proc/net/decnet_dev). There is a utility available at the above
-FTP sites called dn2ethaddr which can compute the correct ethernet
-address to use. The address can be set by ifconfig either before or
-at the time the device is brought up. If you are using RedHat you can
-add the line:
-
-    MACADDR=AA:00:04:00:03:04
-
-or something similar, to /etc/sysconfig/network-scripts/ifcfg-eth0 or
-wherever your network card's configuration lives. Setting the MAC address
-of your ethernet card to an address starting with "hi-ord" will cause a
-DECnet address which matches to be added to the interface (which you can
-verify with iproute2).
-
-The default device for routing can be set through the /proc filesystem
-by setting /proc/sys/net/decnet/default_device to the
-device you want DECnet to route packets out of when no specific route
-is available. Usually this will be eth0, for example:
-
-    echo -n "eth0" >/proc/sys/net/decnet/default_device
-
-If you don't set the default device, then it will default to the first
-ethernet card which has been autoconfigured as described above. You can
-confirm that by looking in the default_device file of course.
-
-There is a list of what the other files under /proc/sys/net/decnet/ do
-on the kernel patch web site (shown above).
-
-4) Run time kernel configuration
-
-This is either done through the sysctl/proc interface (see the kernel web
-pages for details on what the various options do) or through the iproute2
-package in the same way as IPv4/6 configuration is performed.
-
-Documentation for iproute2 is included with the package, although there is
-as yet no specific section on DECnet, most of the features apply to both
-IP and DECnet, albeit with DECnet addresses instead of IP addresses and
-a reduced functionality.
-
-If you want to configure a DECnet router you'll need the iproute2 package
-since its the _only_ way to add and delete routes currently. Eventually
-there will be a routing daemon to send and receive routing messages for
-each interface and update the kernel routing tables accordingly. The
-routing daemon will use netfilter to listen to routing packets, and
-rtnetlink to update the kernels routing tables. 
-
-The DECnet raw socket layer has been removed since it was there purely
-for use by the routing daemon which will now use netfilter (a much cleaner
-and more generic solution) instead.
-
-5) How can I tell if its working ?
-
-Here is a quick guide of what to look for in order to know if your DECnet
-kernel subsystem is working.
-
-   - Is the node address set (see /proc/sys/net/decnet/node_address)
-   - Is the node of the correct type 
-                             (see /proc/sys/net/decnet/conf/<dev>/forwarding)
-   - Is the Ethernet MAC address of each Ethernet card set to match
-     the DECnet address. If in doubt use the dn2ethaddr utility available
-     at the ftp archive.
-   - If the previous two steps are satisfied, and the Ethernet card is up,
-     you should find that it is listed in /proc/net/decnet_dev and also
-     that it appears as a directory in /proc/sys/net/decnet/conf/. The
-     loopback device (lo) should also appear and is required to communicate
-     within a node.
-   - If you have any DECnet routers on your network, they should appear
-     in /proc/net/decnet_neigh, otherwise this file will only contain the
-     entry for the node itself (if it doesn't check to see if lo is up).
-   - If you want to send to any node which is not listed in the
-     /proc/net/decnet_neigh file, you'll need to set the default device
-     to point to an Ethernet card with connection to a router. This is
-     again done with the /proc/sys/net/decnet/default_device file.
-   - Try starting a simple server and client, like the dnping/dnmirror
-     over the loopback interface. With luck they should communicate.
-     For this step and those after, you'll need the DECnet library
-     which can be obtained from the above ftp sites as well as the
-     actual utilities themselves.
-   - If this seems to work, then try talking to a node on your local
-     network, and see if you can obtain the same results.
-   - At this point you are on your own... :-)
-
-6) How to send a bug report
-
-If you've found a bug and want to report it, then there are several things
-you can do to help me work out exactly what it is that is wrong. Useful
-information (_most_ of which _is_ _essential_) includes:
-
- - What kernel version are you running ?
- - What version of the patch are you running ?
- - How far though the above set of tests can you get ?
- - What is in the /proc/decnet* files and /proc/sys/net/decnet/* files ?
- - Which services are you running ?
- - Which client caused the problem ?
- - How much data was being transferred ?
- - Was the network congested ?
- - How can the problem be reproduced ?
- - Can you use tcpdump to get a trace ? (N.B. Most (all?) versions of 
-   tcpdump don't understand how to dump DECnet properly, so including
-   the hex listing of the packet contents is _essential_, usually the -x flag.
-   You may also need to increase the length grabbed with the -s flag. The
-   -e flag also provides very useful information (ethernet MAC addresses))
-
-7) MAC FAQ
-
-A quick FAQ on ethernet MAC addresses to explain how Linux and DECnet
-interact and how to get the best performance from your hardware. 
-
-Ethernet cards are designed to normally only pass received network frames 
-to a host computer when they are addressed to it, or to the broadcast address.
-
-Linux has an interface which allows the setting of extra addresses for
-an ethernet card to listen to. If the ethernet card supports it, the
-filtering operation will be done in hardware, if not the extra unwanted packets
-received will be discarded by the host computer. In the latter case,
-significant processor time and bus bandwidth can be used up on a busy
-network (see the NAPI documentation for a longer explanation of these
-effects).
-
-DECnet makes use of this interface to allow running DECnet on an ethernet 
-card which has already been configured using TCP/IP (presumably using the 
-built in MAC address of the card, as usual) and/or to allow multiple DECnet
-addresses on each physical interface. If you do this, be aware that if your
-ethernet card doesn't support perfect hashing in its MAC address filter
-then your computer will be doing more work than required. Some cards
-will simply set themselves into promiscuous mode in order to receive
-packets from the DECnet specified addresses. So if you have one of these
-cards its better to set the MAC address of the card as described above
-to gain the best efficiency. Better still is to use a card which supports
-NAPI as well.
-
-
-8) Mailing list
-
-If you are keen to get involved in development, or want to ask questions
-about configuration, or even just report bugs, then there is a mailing
-list that you can join, details are at:
-
-http://sourceforge.net/mail/?group_id=4993
-
-9) Legal Info
-
-The Linux DECnet project team have placed their code under the GPL. The
-software is provided "as is" and without warranty express or implied.
-DECnet is a trademark of Compaq. This software is not a product of
-Compaq. We acknowledge the help of people at Compaq in providing extra
-documentation above and beyond what was previously publicly available.
-
-Steve Whitehouse <SteveW@ACM.org>
-
diff --git a/Documentation/networking/defza.rst b/Documentation/networking/defza.rst
new file mode 100644 (file)
index 0000000..73c2f79
--- /dev/null
@@ -0,0 +1,63 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================================================
+Notes on the DEC FDDIcontroller 700 (DEFZA-xx) driver
+=====================================================
+
+:Version: v.1.1.4
+
+
+DEC FDDIcontroller 700 is DEC's first-generation TURBOchannel FDDI
+network card, designed in 1990 specifically for the DECstation 5000
+model 200 workstation.  The board is a single attachment station and
+it was manufactured in two variations, both of which are supported.
+
+First is the SAS MMF DEFZA-AA option, the original design implementing
+the standard MMF-PMD, however with a pair of ST connectors rather than
+the usual MIC connector.  The other one is the SAS ThinWire/STP DEFZA-CA
+option, denoted 700-C, with the network medium selectable by a switch
+between the DEC proprietary ThinWire-PMD using a BNC connector and the
+standard STP-PMD using a DE-9F connector.  This option can interface to
+a DECconcentrator 500 device and, in the case of the STP-PMD, also other
+FDDI equipment and was designed to make it easier to transition from
+existing IEEE 802.3 10BASE2 Ethernet and IEEE 802.5 Token Ring networks
+by providing means to reuse existing cabling.
+
+This driver handles any number of cards installed in a single system.
+They get fddi0, fddi1, etc. interface names assigned in the order of
+increasing TURBOchannel slot numbers.
+
+The board only supports DMA on the receive side.  Transmission involves
+the use of PIO.  As a result under a heavy transmission load there will
+be a significant impact on system performance.
+
+The board supports a 64-entry CAM for matching destination addresses.
+Two entries are preoccupied by the Directed Beacon and Ring Purger
+multicast addresses and the rest is used as a multicast filter.  An
+all-multi mode is also supported for LLC frames and it is used if
+requested explicitly or if the CAM overflows.  The promiscuous mode
+supports separate enables for LLC and SMT frames, but this driver
+doesn't support changing them individually.
+
+
+Known problems:
+
+None.
+
+
+To do:
+
+5. MAC address change.  The card does not support changing the Media
+   Access Controller's address registers but a similar effect can be
+   achieved by adding an alias to the CAM.  There is no way to disable
+   matching against the original address though.
+
+7. Queueing incoming/outgoing SMT frames in the driver if the SMT
+   receive/RMC transmit ring is full. (?)
+
+8. Retrieving/reporting FDDI/SNMP stats.
+
+
+Both success and failure reports are welcome.
+
+Maciej W. Rozycki  <macro@linux-mips.org>
diff --git a/Documentation/networking/defza.txt b/Documentation/networking/defza.txt
deleted file mode 100644 (file)
index 663e4a9..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-Notes on the DEC FDDIcontroller 700 (DEFZA-xx) driver v.1.1.4.
-
-
-DEC FDDIcontroller 700 is DEC's first-generation TURBOchannel FDDI
-network card, designed in 1990 specifically for the DECstation 5000
-model 200 workstation.  The board is a single attachment station and
-it was manufactured in two variations, both of which are supported.
-
-First is the SAS MMF DEFZA-AA option, the original design implementing
-the standard MMF-PMD, however with a pair of ST connectors rather than
-the usual MIC connector.  The other one is the SAS ThinWire/STP DEFZA-CA
-option, denoted 700-C, with the network medium selectable by a switch
-between the DEC proprietary ThinWire-PMD using a BNC connector and the
-standard STP-PMD using a DE-9F connector.  This option can interface to
-a DECconcentrator 500 device and, in the case of the STP-PMD, also other
-FDDI equipment and was designed to make it easier to transition from
-existing IEEE 802.3 10BASE2 Ethernet and IEEE 802.5 Token Ring networks
-by providing means to reuse existing cabling.
-
-This driver handles any number of cards installed in a single system.
-They get fddi0, fddi1, etc. interface names assigned in the order of
-increasing TURBOchannel slot numbers.
-
-The board only supports DMA on the receive side.  Transmission involves
-the use of PIO.  As a result under a heavy transmission load there will
-be a significant impact on system performance.
-
-The board supports a 64-entry CAM for matching destination addresses.
-Two entries are preoccupied by the Directed Beacon and Ring Purger
-multicast addresses and the rest is used as a multicast filter.  An
-all-multi mode is also supported for LLC frames and it is used if
-requested explicitly or if the CAM overflows.  The promiscuous mode
-supports separate enables for LLC and SMT frames, but this driver
-doesn't support changing them individually.
-
-
-Known problems:
-
-None.
-
-
-To do:
-
-5. MAC address change.  The card does not support changing the Media
-   Access Controller's address registers but a similar effect can be
-   achieved by adding an alias to the CAM.  There is no way to disable
-   matching against the original address though.
-
-7. Queueing incoming/outgoing SMT frames in the driver if the SMT
-   receive/RMC transmit ring is full. (?)
-
-8. Retrieving/reporting FDDI/SNMP stats.
-
-
-Both success and failure reports are welcome.
-
-Maciej W. Rozycki  <macro@linux-mips.org>
diff --git a/Documentation/networking/device_drivers/3com/3c509.rst b/Documentation/networking/device_drivers/3com/3c509.rst
new file mode 100644 (file)
index 0000000..47f706b
--- /dev/null
@@ -0,0 +1,249 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============================================================================
+Linux and the 3Com EtherLink III Series Ethercards (driver v1.18c and higher)
+=============================================================================
+
+This file contains the instructions and caveats for v1.18c and higher versions
+of the 3c509 driver. You should not use the driver without reading this file.
+
+release 1.0
+
+28 February 2002
+
+Current maintainer (corrections to):
+  David Ruggiero <jdr@farfalle.com>
+
+Introduction
+============
+
+The following are notes and information on using the 3Com EtherLink III series
+ethercards in Linux. These cards are commonly known by the most widely-used
+card's 3Com model number, 3c509. They are all 10mb/s ISA-bus cards and shouldn't
+be (but sometimes are) confused with the similarly-numbered PCI-bus "3c905"
+(aka "Vortex" or "Boomerang") series.  Kernel support for the 3c509 family is
+provided by the module 3c509.c, which has code to support all of the following
+models:
+
+ - 3c509 (original ISA card)
+ - 3c509B (later revision of the ISA card; supports full-duplex)
+ - 3c589 (PCMCIA)
+ - 3c589B (later revision of the 3c589; supports full-duplex)
+ - 3c579 (EISA)
+
+Large portions of this documentation were heavily borrowed from the guide
+written the original author of the 3c509 driver, Donald Becker. The master
+copy of that document, which contains notes on older versions of the driver,
+currently resides on Scyld web server: http://www.scyld.com/.
+
+
+Special Driver Features
+=======================
+
+Overriding card settings
+
+The driver allows boot- or load-time overriding of the card's detected IOADDR,
+IRQ, and transceiver settings, although this capability shouldn't generally be
+needed except to enable full-duplex mode (see below). An example of the syntax
+for LILO parameters for doing this::
+
+    ether=10,0x310,3,0x3c509,eth0
+
+This configures the first found 3c509 card for IRQ 10, base I/O 0x310, and
+transceiver type 3 (10base2). The flag "0x3c509" must be set to avoid conflicts
+with other card types when overriding the I/O address. When the driver is
+loaded as a module, only the IRQ may be overridden. For example,
+setting two cards to IRQ10 and IRQ11 is done by using the irq module
+option::
+
+   options 3c509 irq=10,11
+
+
+Full-duplex mode
+================
+
+The v1.18c driver added support for the 3c509B's full-duplex capabilities.
+In order to enable and successfully use full-duplex mode, three conditions
+must be met:
+
+(a) You must have a Etherlink III card model whose hardware supports full-
+duplex operations. Currently, the only members of the 3c509 family that are
+positively known to support full-duplex are the 3c509B (ISA bus) and 3c589B
+(PCMCIA) cards. Cards without the "B" model designation do *not* support
+full-duplex mode; these include the original 3c509 (no "B"), the original
+3c589, the 3c529 (MCA bus), and the 3c579 (EISA bus).
+
+(b) You must be using your card's 10baseT transceiver (i.e., the RJ-45
+connector), not its AUI (thick-net) or 10base2 (thin-net/coax) interfaces.
+AUI and 10base2 network cabling is physically incapable of full-duplex
+operation.
+
+(c) Most importantly, your 3c509B must be connected to a link partner that is
+itself full-duplex capable. This is almost certainly one of two things: a full-
+duplex-capable  Ethernet switch (*not* a hub), or a full-duplex-capable NIC on
+another system that's connected directly to the 3c509B via a crossover cable.
+
+Full-duplex mode can be enabled using 'ethtool'.
+
+.. warning::
+
+  Extremely important caution concerning full-duplex mode
+
+  Understand that the 3c509B's hardware's full-duplex support is much more
+  limited than that provide by more modern network interface cards. Although
+  at the physical layer of the network it fully supports full-duplex operation,
+  the card was designed before the current Ethernet auto-negotiation (N-way)
+  spec was written. This means that the 3c509B family ***cannot and will not
+  auto-negotiate a full-duplex connection with its link partner under any
+  circumstances, no matter how it is initialized***. If the full-duplex mode
+  of the 3c509B is enabled, its link partner will very likely need to be
+  independently _forced_ into full-duplex mode as well; otherwise various nasty
+  failures will occur - at the very least, you'll see massive numbers of packet
+  collisions. This is one of very rare circumstances where disabling auto-
+  negotiation and forcing the duplex mode of a network interface card or switch
+  would ever be necessary or desirable.
+
+
+Available Transceiver Types
+===========================
+
+For versions of the driver v1.18c and above, the available transceiver types are:
+
+== =========================================================================
+0  transceiver type from EEPROM config (normally 10baseT); force half-duplex
+1  AUI (thick-net / DB15 connector)
+2  (undefined)
+3  10base2 (thin-net == coax / BNC connector)
+4  10baseT (RJ-45 connector); force half-duplex mode
+8  transceiver type and duplex mode taken from card's EEPROM config settings
+12 10baseT (RJ-45 connector); force full-duplex mode
+== =========================================================================
+
+Prior to driver version 1.18c, only transceiver codes 0-4 were supported. Note
+that the new transceiver codes 8 and 12 are the *only* ones that will enable
+full-duplex mode, no matter what the card's detected EEPROM settings might be.
+This insured that merely upgrading the driver from an earlier version would
+never automatically enable full-duplex mode in an existing installation;
+it must always be explicitly enabled via one of these code in order to be
+activated.
+
+The transceiver type can be changed using 'ethtool'.
+
+
+Interpretation of error messages and common problems
+----------------------------------------------------
+
+Error Messages
+^^^^^^^^^^^^^^
+
+eth0: Infinite loop in interrupt, status 2011.
+These are "mostly harmless" message indicating that the driver had too much
+work during that interrupt cycle. With a status of 0x2011 you are receiving
+packets faster than they can be removed from the card. This should be rare
+or impossible in normal operation. Possible causes of this error report are:
+
+   - a "green" mode enabled that slows the processor down when there is no
+     keyboard activity.
+
+   - some other device or device driver hogging the bus or disabling interrupts.
+     Check /proc/interrupts for excessive interrupt counts. The timer tick
+     interrupt should always be incrementing faster than the others.
+
+No received packets
+^^^^^^^^^^^^^^^^^^^
+
+If a 3c509, 3c562 or 3c589 can successfully transmit packets, but never
+receives packets (as reported by /proc/net/dev or 'ifconfig') you likely
+have an interrupt line problem. Check /proc/interrupts to verify that the
+card is actually generating interrupts. If the interrupt count is not
+increasing you likely have a physical conflict with two devices trying to
+use the same ISA IRQ line. The common conflict is with a sound card on IRQ10
+or IRQ5, and the easiest solution is to move the 3c509 to a different
+interrupt line. If the device is receiving packets but 'ping' doesn't work,
+you have a routing problem.
+
+Tx Carrier Errors Reported in /proc/net/dev
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+
+If an EtherLink III appears to transmit packets, but the "Tx carrier errors"
+field in /proc/net/dev increments as quickly as the Tx packet count, you
+likely have an unterminated network or the incorrect media transceiver selected.
+
+3c509B card is not detected on machines with an ISA PnP BIOS.
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+While the updated driver works with most PnP BIOS programs, it does not work
+with all. This can be fixed by disabling PnP support using the 3Com-supplied
+setup program.
+
+3c509 card is not detected on overclocked machines
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Increase the delay time in id_read_eeprom() from the current value, 500,
+to an absurdly high value, such as 5000.
+
+
+Decoding Status and Error Messages
+----------------------------------
+
+
+The bits in the main status register are:
+
+=====  ======================================
+value  description
+=====  ======================================
+0x01   Interrupt latch
+0x02   Tx overrun, or Rx underrun
+0x04   Tx complete
+0x08   Tx FIFO room available
+0x10   A complete Rx packet has arrived
+0x20   A Rx packet has started to arrive
+0x40   The driver has requested an interrupt
+0x80   Statistics counter nearly full
+=====  ======================================
+
+The bits in the transmit (Tx) status word are:
+
+=====  ============================================
+value  description
+=====  ============================================
+0x02   Out-of-window collision.
+0x04   Status stack overflow (normally impossible).
+0x08   16 collisions.
+0x10   Tx underrun (not enough PCI bus bandwidth).
+0x20   Tx jabber.
+0x40   Tx interrupt requested.
+0x80   Status is valid (this should always be set).
+=====  ============================================
+
+
+When a transmit error occurs the driver produces a status message such as::
+
+   eth0: Transmit error, Tx status register 82
+
+The two values typically seen here are:
+
+0x82
+^^^^
+
+Out of window collision. This typically occurs when some other Ethernet
+host is incorrectly set to full duplex on a half duplex network.
+
+0x88
+^^^^
+
+16 collisions. This typically occurs when the network is exceptionally busy
+or when another host doesn't correctly back off after a collision. If this
+error is mixed with 0x82 errors it is the result of a host incorrectly set
+to full duplex (see above).
+
+Both of these errors are the result of network problems that should be
+corrected. They do not represent driver malfunction.
+
+
+Revision history (this file)
+============================
+
+28Feb02 v1.0  DR   New; major portions based on Becker original 3c509 docs
+
diff --git a/Documentation/networking/device_drivers/3com/3c509.txt b/Documentation/networking/device_drivers/3com/3c509.txt
deleted file mode 100644 (file)
index fbf722e..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-Linux and the 3Com EtherLink III Series Ethercards (driver v1.18c and higher)
-----------------------------------------------------------------------------
-
-This file contains the instructions and caveats for v1.18c and higher versions
-of the 3c509 driver. You should not use the driver without reading this file.
-
-release 1.0
-28 February 2002
-Current maintainer (corrections to):
-  David Ruggiero <jdr@farfalle.com>
-
-----------------------------------------------------------------------------
-
-(0) Introduction
-
-The following are notes and information on using the 3Com EtherLink III series
-ethercards in Linux. These cards are commonly known by the most widely-used
-card's 3Com model number, 3c509. They are all 10mb/s ISA-bus cards and shouldn't
-be (but sometimes are) confused with the similarly-numbered PCI-bus "3c905"
-(aka "Vortex" or "Boomerang") series.  Kernel support for the 3c509 family is
-provided by the module 3c509.c, which has code to support all of the following
-models:
-
-  3c509 (original ISA card)
-  3c509B (later revision of the ISA card; supports full-duplex)
-  3c589 (PCMCIA)
-  3c589B (later revision of the 3c589; supports full-duplex)
-  3c579 (EISA)
-
-Large portions of this documentation were heavily borrowed from the guide
-written the original author of the 3c509 driver, Donald Becker. The master
-copy of that document, which contains notes on older versions of the driver,
-currently resides on Scyld web server: http://www.scyld.com/.
-
-
-(1) Special Driver Features
-
-Overriding card settings
-
-The driver allows boot- or load-time overriding of the card's detected IOADDR,
-IRQ, and transceiver settings, although this capability shouldn't generally be
-needed except to enable full-duplex mode (see below). An example of the syntax
-for LILO parameters for doing this:
-
-    ether=10,0x310,3,0x3c509,eth0 
-
-This configures the first found 3c509 card for IRQ 10, base I/O 0x310, and
-transceiver type 3 (10base2). The flag "0x3c509" must be set to avoid conflicts
-with other card types when overriding the I/O address. When the driver is
-loaded as a module, only the IRQ may be overridden. For example,
-setting two cards to IRQ10 and IRQ11 is done by using the irq module
-option:
-
-   options 3c509 irq=10,11
-
-
-(2) Full-duplex mode
-
-The v1.18c driver added support for the 3c509B's full-duplex capabilities.
-In order to enable and successfully use full-duplex mode, three conditions
-must be met: 
-
-(a) You must have a Etherlink III card model whose hardware supports full-
-duplex operations. Currently, the only members of the 3c509 family that are
-positively known to support full-duplex are the 3c509B (ISA bus) and 3c589B
-(PCMCIA) cards. Cards without the "B" model designation do *not* support
-full-duplex mode; these include the original 3c509 (no "B"), the original
-3c589, the 3c529 (MCA bus), and the 3c579 (EISA bus).
-
-(b) You must be using your card's 10baseT transceiver (i.e., the RJ-45
-connector), not its AUI (thick-net) or 10base2 (thin-net/coax) interfaces.
-AUI and 10base2 network cabling is physically incapable of full-duplex
-operation.
-
-(c) Most importantly, your 3c509B must be connected to a link partner that is
-itself full-duplex capable. This is almost certainly one of two things: a full-
-duplex-capable  Ethernet switch (*not* a hub), or a full-duplex-capable NIC on
-another system that's connected directly to the 3c509B via a crossover cable.
-
-Full-duplex mode can be enabled using 'ethtool'.
-/////Extremely important caution concerning full-duplex mode/////
-Understand that the 3c509B's hardware's full-duplex support is much more
-limited than that provide by more modern network interface cards. Although
-at the physical layer of the network it fully supports full-duplex operation,
-the card was designed before the current Ethernet auto-negotiation (N-way)
-spec was written. This means that the 3c509B family ***cannot and will not
-auto-negotiate a full-duplex connection with its link partner under any
-circumstances, no matter how it is initialized***. If the full-duplex mode
-of the 3c509B is enabled, its link partner will very likely need to be
-independently _forced_ into full-duplex mode as well; otherwise various nasty
-failures will occur - at the very least, you'll see massive numbers of packet
-collisions. This is one of very rare circumstances where disabling auto-
-negotiation and forcing the duplex mode of a network interface card or switch
-would ever be necessary or desirable.
-
-
-(3) Available Transceiver Types
-
-For versions of the driver v1.18c and above, the available transceiver types are:
-0  transceiver type from EEPROM config (normally 10baseT); force half-duplex
-1  AUI (thick-net / DB15 connector)
-2  (undefined)
-3  10base2 (thin-net == coax / BNC connector)
-4  10baseT (RJ-45 connector); force half-duplex mode
-8  transceiver type and duplex mode taken from card's EEPROM config settings
-12 10baseT (RJ-45 connector); force full-duplex mode
-
-Prior to driver version 1.18c, only transceiver codes 0-4 were supported. Note
-that the new transceiver codes 8 and 12 are the *only* ones that will enable
-full-duplex mode, no matter what the card's detected EEPROM settings might be.
-This insured that merely upgrading the driver from an earlier version would
-never automatically enable full-duplex mode in an existing installation;
-it must always be explicitly enabled via one of these code in order to be
-activated.
-
-The transceiver type can be changed using 'ethtool'.
-  
-
-(4a) Interpretation of error messages and common problems
-
-Error Messages
-
-eth0: Infinite loop in interrupt, status 2011. 
-These are "mostly harmless" message indicating that the driver had too much
-work during that interrupt cycle. With a status of 0x2011 you are receiving
-packets faster than they can be removed from the card. This should be rare
-or impossible in normal operation. Possible causes of this error report are:
-   - a "green" mode enabled that slows the processor down when there is no
-     keyboard activity. 
-
-   - some other device or device driver hogging the bus or disabling interrupts.
-     Check /proc/interrupts for excessive interrupt counts. The timer tick
-     interrupt should always be incrementing faster than the others. 
-
-No received packets 
-If a 3c509, 3c562 or 3c589 can successfully transmit packets, but never
-receives packets (as reported by /proc/net/dev or 'ifconfig') you likely
-have an interrupt line problem. Check /proc/interrupts to verify that the
-card is actually generating interrupts. If the interrupt count is not
-increasing you likely have a physical conflict with two devices trying to
-use the same ISA IRQ line. The common conflict is with a sound card on IRQ10
-or IRQ5, and the easiest solution is to move the 3c509 to a different
-interrupt line. If the device is receiving packets but 'ping' doesn't work,
-you have a routing problem.
-
-Tx Carrier Errors Reported in /proc/net/dev 
-If an EtherLink III appears to transmit packets, but the "Tx carrier errors"
-field in /proc/net/dev increments as quickly as the Tx packet count, you
-likely have an unterminated network or the incorrect media transceiver selected. 
-
-3c509B card is not detected on machines with an ISA PnP BIOS. 
-While the updated driver works with most PnP BIOS programs, it does not work
-with all. This can be fixed by disabling PnP support using the 3Com-supplied
-setup program. 
-
-3c509 card is not detected on overclocked machines 
-Increase the delay time in id_read_eeprom() from the current value, 500,
-to an absurdly high value, such as 5000. 
-
-
-(4b) Decoding Status and Error Messages
-
-The bits in the main status register are: 
-
-value  description
-0x01   Interrupt latch
-0x02   Tx overrun, or Rx underrun
-0x04   Tx complete
-0x08   Tx FIFO room available
-0x10   A complete Rx packet has arrived
-0x20   A Rx packet has started to arrive
-0x40   The driver has requested an interrupt
-0x80   Statistics counter nearly full
-
-The bits in the transmit (Tx) status word are: 
-
-value  description
-0x02   Out-of-window collision.
-0x04   Status stack overflow (normally impossible).
-0x08   16 collisions.
-0x10   Tx underrun (not enough PCI bus bandwidth).
-0x20   Tx jabber.
-0x40   Tx interrupt requested.
-0x80   Status is valid (this should always be set).
-
-
-When a transmit error occurs the driver produces a status message such as 
-
-   eth0: Transmit error, Tx status register 82
-
-The two values typically seen here are:
-
-0x82 
-Out of window collision. This typically occurs when some other Ethernet
-host is incorrectly set to full duplex on a half duplex network. 
-
-0x88 
-16 collisions. This typically occurs when the network is exceptionally busy
-or when another host doesn't correctly back off after a collision. If this
-error is mixed with 0x82 errors it is the result of a host incorrectly set
-to full duplex (see above).
-
-Both of these errors are the result of network problems that should be
-corrected. They do not represent driver malfunction.
-
-
-(5) Revision history (this file)
-
-28Feb02 v1.0  DR   New; major portions based on Becker original 3c509 docs
-
diff --git a/Documentation/networking/device_drivers/3com/vortex.rst b/Documentation/networking/device_drivers/3com/vortex.rst
new file mode 100644 (file)
index 0000000..800add5
--- /dev/null
@@ -0,0 +1,461 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================
+3Com Vortex device driver
+=========================
+
+Documentation/networking/device_drivers/3com/vortex.rst
+
+Andrew Morton
+
+30 April 2000
+
+
+This document describes the usage and errata of the 3Com "Vortex" device
+driver for Linux, 3c59x.c.
+
+The driver was written by Donald Becker <becker@scyld.com>
+
+Don is no longer the prime maintainer of this version of the driver.
+Please report problems to one or more of:
+
+- Andrew Morton
+- Netdev mailing list <netdev@vger.kernel.org>
+- Linux kernel mailing list <linux-kernel@vger.kernel.org>
+
+Please note the 'Reporting and Diagnosing Problems' section at the end
+of this file.
+
+
+Since kernel 2.3.99-pre6, this driver incorporates the support for the
+3c575-series Cardbus cards which used to be handled by 3c575_cb.c.
+
+This driver supports the following hardware:
+
+       - 3c590 Vortex 10Mbps
+       - 3c592 EISA 10Mbps Demon/Vortex
+       - 3c597 EISA Fast Demon/Vortex
+       - 3c595 Vortex 100baseTx
+       - 3c595 Vortex 100baseT4
+       - 3c595 Vortex 100base-MII
+       - 3c900 Boomerang 10baseT
+       - 3c900 Boomerang 10Mbps Combo
+       - 3c900 Cyclone 10Mbps TPO
+       - 3c900 Cyclone 10Mbps Combo
+       - 3c900 Cyclone 10Mbps TPC
+       - 3c900B-FL Cyclone 10base-FL
+       - 3c905 Boomerang 100baseTx
+       - 3c905 Boomerang 100baseT4
+       - 3c905B Cyclone 100baseTx
+       - 3c905B Cyclone 10/100/BNC
+       - 3c905B-FX Cyclone 100baseFx
+       - 3c905C Tornado
+       - 3c920B-EMB-WNM (ATI Radeon 9100 IGP)
+       - 3c980 Cyclone
+       - 3c980C Python-T
+       - 3cSOHO100-TX Hurricane
+       - 3c555 Laptop Hurricane
+       - 3c556 Laptop Tornado
+       - 3c556B Laptop Hurricane
+       - 3c575 [Megahertz] 10/100 LAN  CardBus
+       - 3c575 Boomerang CardBus
+       - 3CCFE575BT Cyclone CardBus
+       - 3CCFE575CT Tornado CardBus
+       - 3CCFE656 Cyclone CardBus
+       - 3CCFEM656B Cyclone+Winmodem CardBus
+       - 3CXFEM656C Tornado+Winmodem CardBus
+       - 3c450 HomePNA Tornado
+       - 3c920 Tornado
+       - 3c982 Hydra Dual Port A
+       - 3c982 Hydra Dual Port B
+       - 3c905B-T4
+       - 3c920B-EMB-WNM Tornado
+
+Module parameters
+=================
+
+There are several parameters which may be provided to the driver when
+its module is loaded.  These are usually placed in ``/etc/modprobe.d/*.conf``
+configuration files.  Example::
+
+    options 3c59x debug=3 rx_copybreak=300
+
+If you are using the PCMCIA tools (cardmgr) then the options may be
+placed in /etc/pcmcia/config.opts::
+
+    module "3c59x" opts "debug=3 rx_copybreak=300"
+
+
+The supported parameters are:
+
+debug=N
+
+  Where N is a number from 0 to 7.  Anything above 3 produces a lot
+  of output in your system logs.  debug=1 is default.
+
+options=N1,N2,N3,...
+
+  Each number in the list provides an option to the corresponding
+  network card.  So if you have two 3c905's and you wish to provide
+  them with option 0x204 you would use::
+
+    options=0x204,0x204
+
+  The individual options are composed of a number of bitfields which
+  have the following meanings:
+
+  Possible media type settings
+
+       ==      =================================
+       0       10baseT
+       1       10Mbs AUI
+       2       undefined
+       3       10base2 (BNC)
+       4       100base-TX
+       5       100base-FX
+       6       MII (Media Independent Interface)
+       7       Use default setting from EEPROM
+       8       Autonegotiate
+       9       External MII
+       10      Use default setting from EEPROM
+       ==      =================================
+
+  When generating a value for the 'options' setting, the above media
+  selection values may be OR'ed (or added to) the following:
+
+  ======  =============================================
+  0x8000  Set driver debugging level to 7
+  0x4000  Set driver debugging level to 2
+  0x0400  Enable Wake-on-LAN
+  0x0200  Force full duplex mode.
+  0x0010  Bus-master enable bit (Old Vortex cards only)
+  ======  =============================================
+
+  For example::
+
+    insmod 3c59x options=0x204
+
+  will force full-duplex 100base-TX, rather than allowing the usual
+  autonegotiation.
+
+global_options=N
+
+  Sets the ``options`` parameter for all 3c59x NICs in the machine.
+  Entries in the ``options`` array above will override any setting of
+  this.
+
+full_duplex=N1,N2,N3...
+
+  Similar to bit 9 of 'options'.  Forces the corresponding card into
+  full-duplex mode.  Please use this in preference to the ``options``
+  parameter.
+
+  In fact, please don't use this at all! You're better off getting
+  autonegotiation working properly.
+
+global_full_duplex=N1
+
+  Sets full duplex mode for all 3c59x NICs in the machine.  Entries
+  in the ``full_duplex`` array above will override any setting of this.
+
+flow_ctrl=N1,N2,N3...
+
+  Use 802.3x MAC-layer flow control.  The 3com cards only support the
+  PAUSE command, which means that they will stop sending packets for a
+  short period if they receive a PAUSE frame from the link partner.
+
+  The driver only allows flow control on a link which is operating in
+  full duplex mode.
+
+  This feature does not appear to work on the 3c905 - only 3c905B and
+  3c905C have been tested.
+
+  The 3com cards appear to only respond to PAUSE frames which are
+  sent to the reserved destination address of 01:80:c2:00:00:01.  They
+  do not honour PAUSE frames which are sent to the station MAC address.
+
+rx_copybreak=M
+
+  The driver preallocates 32 full-sized (1536 byte) network buffers
+  for receiving.  When a packet arrives, the driver has to decide
+  whether to leave the packet in its full-sized buffer, or to allocate
+  a smaller buffer and copy the packet across into it.
+
+  This is a speed/space tradeoff.
+
+  The value of rx_copybreak is used to decide when to make the copy.
+  If the packet size is less than rx_copybreak, the packet is copied.
+  The default value for rx_copybreak is 200 bytes.
+
+max_interrupt_work=N
+
+  The driver's interrupt service routine can handle many receive and
+  transmit packets in a single invocation.  It does this in a loop.
+  The value of max_interrupt_work governs how many times the interrupt
+  service routine will loop.  The default value is 32 loops.  If this
+  is exceeded the interrupt service routine gives up and generates a
+  warning message "eth0: Too much work in interrupt".
+
+hw_checksums=N1,N2,N3,...
+
+  Recent 3com NICs are able to generate IPv4, TCP and UDP checksums
+  in hardware.  Linux has used the Rx checksumming for a long time.
+  The "zero copy" patch which is planned for the 2.4 kernel series
+  allows you to make use of the NIC's DMA scatter/gather and transmit
+  checksumming as well.
+
+  The driver is set up so that, when the zerocopy patch is applied,
+  all Tornado and Cyclone devices will use S/G and Tx checksums.
+
+  This module parameter has been provided so you can override this
+  decision.  If you think that Tx checksums are causing a problem, you
+  may disable the feature with ``hw_checksums=0``.
+
+  If you think your NIC should be performing Tx checksumming and the
+  driver isn't enabling it, you can force the use of hardware Tx
+  checksumming with ``hw_checksums=1``.
+
+  The driver drops a message in the logfiles to indicate whether or
+  not it is using hardware scatter/gather and hardware Tx checksums.
+
+  Scatter/gather and hardware checksums provide considerable
+  performance improvement for the sendfile() system call, but a small
+  decrease in throughput for send().  There is no effect upon receive
+  efficiency.
+
+compaq_ioaddr=N,
+compaq_irq=N,
+compaq_device_id=N
+
+  "Variables to work-around the Compaq PCI BIOS32 problem"....
+
+watchdog=N
+
+  Sets the time duration (in milliseconds) after which the kernel
+  decides that the transmitter has become stuck and needs to be reset.
+  This is mainly for debugging purposes, although it may be advantageous
+  to increase this value on LANs which have very high collision rates.
+  The default value is 5000 (5.0 seconds).
+
+enable_wol=N1,N2,N3,...
+
+  Enable Wake-on-LAN support for the relevant interface.  Donald
+  Becker's ``ether-wake`` application may be used to wake suspended
+  machines.
+
+  Also enables the NIC's power management support.
+
+global_enable_wol=N
+
+  Sets enable_wol mode for all 3c59x NICs in the machine.  Entries in
+  the ``enable_wol`` array above will override any setting of this.
+
+Media selection
+---------------
+
+A number of the older NICs such as the 3c590 and 3c900 series have
+10base2 and AUI interfaces.
+
+Prior to January, 2001 this driver would autoeselect the 10base2 or AUI
+port if it didn't detect activity on the 10baseT port.  It would then
+get stuck on the 10base2 port and a driver reload was necessary to
+switch back to 10baseT.  This behaviour could not be prevented with a
+module option override.
+
+Later (current) versions of the driver _do_ support locking of the
+media type.  So if you load the driver module with
+
+       modprobe 3c59x options=0
+
+it will permanently select the 10baseT port.  Automatic selection of
+other media types does not occur.
+
+
+Transmit error, Tx status register 82
+-------------------------------------
+
+This is a common error which is almost always caused by another host on
+the same network being in full-duplex mode, while this host is in
+half-duplex mode.  You need to find that other host and make it run in
+half-duplex mode or fix this host to run in full-duplex mode.
+
+As a last resort, you can force the 3c59x driver into full-duplex mode
+with
+
+       options 3c59x full_duplex=1
+
+but this has to be viewed as a workaround for broken network gear and
+should only really be used for equipment which cannot autonegotiate.
+
+
+Additional resources
+--------------------
+
+Details of the device driver implementation are at the top of the source file.
+
+Additional documentation is available at Don Becker's Linux Drivers site:
+
+     http://www.scyld.com/vortex.html
+
+Donald Becker's driver development site:
+
+     http://www.scyld.com/network.html
+
+Donald's vortex-diag program is useful for inspecting the NIC's state:
+
+     http://www.scyld.com/ethercard_diag.html
+
+Donald's mii-diag program may be used for inspecting and manipulating
+the NIC's Media Independent Interface subsystem:
+
+     http://www.scyld.com/ethercard_diag.html#mii-diag
+
+Donald's wake-on-LAN page:
+
+     http://www.scyld.com/wakeonlan.html
+
+3Com's DOS-based application for setting up the NICs EEPROMs:
+
+       ftp://ftp.3com.com/pub/nic/3c90x/3c90xx2.exe
+
+
+Autonegotiation notes
+---------------------
+
+  The driver uses a one-minute heartbeat for adapting to changes in
+  the external LAN environment if link is up and 5 seconds if link is down.
+  This means that when, for example, a machine is unplugged from a hubbed
+  10baseT LAN plugged into a  switched 100baseT LAN, the throughput
+  will be quite dreadful for up to sixty seconds.  Be patient.
+
+  Cisco interoperability note from Walter Wong <wcw+@CMU.EDU>:
+
+  On a side note, adding HAS_NWAY seems to share a problem with the
+  Cisco 6509 switch.  Specifically, you need to change the spanning
+  tree parameter for the port the machine is plugged into to 'portfast'
+  mode.  Otherwise, the negotiation fails.  This has been an issue
+  we've noticed for a while but haven't had the time to track down.
+
+  Cisco switches    (Jeff Busch <jbusch@deja.com>)
+
+    My "standard config" for ports to which PC's/servers connect directly::
+
+       interface FastEthernet0/N
+       description machinename
+       load-interval 30
+       spanning-tree portfast
+
+    If autonegotiation is a problem, you may need to specify "speed
+    100" and "duplex full" as well (or "speed 10" and "duplex half").
+
+    WARNING: DO NOT hook up hubs/switches/bridges to these
+    specially-configured ports! The switch will become very confused.
+
+
+Reporting and diagnosing problems
+---------------------------------
+
+Maintainers find that accurate and complete problem reports are
+invaluable in resolving driver problems.  We are frequently not able to
+reproduce problems and must rely on your patience and efforts to get to
+the bottom of the problem.
+
+If you believe you have a driver problem here are some of the
+steps you should take:
+
+- Is it really a driver problem?
+
+   Eliminate some variables: try different cards, different
+   computers, different cables, different ports on the switch/hub,
+   different versions of the kernel or of the driver, etc.
+
+- OK, it's a driver problem.
+
+   You need to generate a report.  Typically this is an email to the
+   maintainer and/or netdev@vger.kernel.org.  The maintainer's
+   email address will be in the driver source or in the MAINTAINERS file.
+
+- The contents of your report will vary a lot depending upon the
+  problem.  If it's a kernel crash then you should refer to the
+  admin-guide/reporting-bugs.rst file.
+
+  But for most problems it is useful to provide the following:
+
+   - Kernel version, driver version
+
+   - A copy of the banner message which the driver generates when
+     it is initialised.  For example:
+
+     eth0: 3Com PCI 3c905C Tornado at 0xa400,  00:50:da:6a:88:f0, IRQ 19
+     8K byte-wide RAM 5:3 Rx:Tx split, autoselect/Autonegotiate interface.
+     MII transceiver found at address 24, status 782d.
+     Enabling bus-master transmits and whole-frame receives.
+
+     NOTE: You must provide the ``debug=2`` modprobe option to generate
+     a full detection message.  Please do this::
+
+       modprobe 3c59x debug=2
+
+   - If it is a PCI device, the relevant output from 'lspci -vx', eg::
+
+       00:09.0 Ethernet controller: 3Com Corporation 3c905C-TX [Fast Etherlink] (rev 74)
+              Subsystem: 3Com Corporation: Unknown device 9200
+              Flags: bus master, medium devsel, latency 32, IRQ 19
+              I/O ports at a400 [size=128]
+              Memory at db000000 (32-bit, non-prefetchable) [size=128]
+              Expansion ROM at <unassigned> [disabled] [size=128K]
+              Capabilities: [dc] Power Management version 2
+       00: b7 10 00 92 07 00 10 02 74 00 00 02 08 20 00 00
+       10: 01 a4 00 00 00 00 00 db 00 00 00 00 00 00 00 00
+       20: 00 00 00 00 00 00 00 00 00 00 00 00 b7 10 00 10
+       30: 00 00 00 00 dc 00 00 00 00 00 00 00 05 01 0a 0a
+
+   - A description of the environment: 10baseT? 100baseT?
+     full/half duplex? switched or hubbed?
+
+   - Any additional module parameters which you may be providing to the driver.
+
+   - Any kernel logs which are produced.  The more the merrier.
+     If this is a large file and you are sending your report to a
+     mailing list, mention that you have the logfile, but don't send
+     it.  If you're reporting direct to the maintainer then just send
+     it.
+
+     To ensure that all kernel logs are available, add the
+     following line to /etc/syslog.conf::
+
+        kern.* /var/log/messages
+
+     Then restart syslogd with::
+
+        /etc/rc.d/init.d/syslog restart
+
+     (The above may vary, depending upon which Linux distribution you use).
+
+    - If your problem is reproducible then that's great.  Try the
+      following:
+
+      1) Increase the debug level.  Usually this is done via:
+
+        a) modprobe driver debug=7
+        b) In /etc/modprobe.d/driver.conf:
+           options driver debug=7
+
+      2) Recreate the problem with the higher debug level,
+        send all logs to the maintainer.
+
+      3) Download you card's diagnostic tool from Donald
+        Becker's website <http://www.scyld.com/ethercard_diag.html>.
+        Download mii-diag.c as well.  Build these.
+
+        a) Run 'vortex-diag -aaee' and 'mii-diag -v' when the card is
+           working correctly.  Save the output.
+
+        b) Run the above commands when the card is malfunctioning.  Send
+           both sets of output.
+
+Finally, please be patient and be prepared to do some work.  You may
+end up working on this problem for a week or more as the maintainer
+asks more questions, asks for more tests, asks for patches to be
+applied, etc.  At the end of it all, the problem may even remain
+unresolved.
diff --git a/Documentation/networking/device_drivers/3com/vortex.txt b/Documentation/networking/device_drivers/3com/vortex.txt
deleted file mode 100644 (file)
index 587f3fc..0000000
+++ /dev/null
@@ -1,448 +0,0 @@
-Documentation/networking/device_drivers/3com/vortex.txt
-Andrew Morton
-30 April 2000
-
-
-This document describes the usage and errata of the 3Com "Vortex" device
-driver for Linux, 3c59x.c.
-
-The driver was written by Donald Becker <becker@scyld.com>
-
-Don is no longer the prime maintainer of this version of the driver. 
-Please report problems to one or more of:
-
-  Andrew Morton
-  Netdev mailing list <netdev@vger.kernel.org>
-  Linux kernel mailing list <linux-kernel@vger.kernel.org>
-
-Please note the 'Reporting and Diagnosing Problems' section at the end
-of this file.
-
-
-Since kernel 2.3.99-pre6, this driver incorporates the support for the
-3c575-series Cardbus cards which used to be handled by 3c575_cb.c.
-
-This driver supports the following hardware:
-
-       3c590 Vortex 10Mbps
-       3c592 EISA 10Mbps Demon/Vortex
-       3c597 EISA Fast Demon/Vortex
-       3c595 Vortex 100baseTx
-       3c595 Vortex 100baseT4
-       3c595 Vortex 100base-MII
-       3c900 Boomerang 10baseT
-       3c900 Boomerang 10Mbps Combo
-       3c900 Cyclone 10Mbps TPO
-       3c900 Cyclone 10Mbps Combo
-       3c900 Cyclone 10Mbps TPC
-       3c900B-FL Cyclone 10base-FL
-       3c905 Boomerang 100baseTx
-       3c905 Boomerang 100baseT4
-       3c905B Cyclone 100baseTx
-       3c905B Cyclone 10/100/BNC
-       3c905B-FX Cyclone 100baseFx
-       3c905C Tornado
-       3c920B-EMB-WNM (ATI Radeon 9100 IGP)
-       3c980 Cyclone
-       3c980C Python-T
-       3cSOHO100-TX Hurricane
-       3c555 Laptop Hurricane
-       3c556 Laptop Tornado
-       3c556B Laptop Hurricane
-       3c575 [Megahertz] 10/100 LAN  CardBus
-       3c575 Boomerang CardBus
-       3CCFE575BT Cyclone CardBus
-       3CCFE575CT Tornado CardBus
-       3CCFE656 Cyclone CardBus
-       3CCFEM656B Cyclone+Winmodem CardBus
-       3CXFEM656C Tornado+Winmodem CardBus
-       3c450 HomePNA Tornado
-       3c920 Tornado
-       3c982 Hydra Dual Port A
-       3c982 Hydra Dual Port B
-       3c905B-T4
-       3c920B-EMB-WNM Tornado
-
-Module parameters
-=================
-
-There are several parameters which may be provided to the driver when
-its module is loaded.  These are usually placed in /etc/modprobe.d/*.conf
-configuration files.  Example:
-
-options 3c59x debug=3 rx_copybreak=300
-
-If you are using the PCMCIA tools (cardmgr) then the options may be
-placed in /etc/pcmcia/config.opts:
-
-module "3c59x" opts "debug=3 rx_copybreak=300"
-
-
-The supported parameters are:
-
-debug=N
-
-  Where N is a number from 0 to 7.  Anything above 3 produces a lot
-  of output in your system logs.  debug=1 is default.
-
-options=N1,N2,N3,...
-
-  Each number in the list provides an option to the corresponding
-  network card.  So if you have two 3c905's and you wish to provide
-  them with option 0x204 you would use:
-
-    options=0x204,0x204
-
-  The individual options are composed of a number of bitfields which
-  have the following meanings:
-
-  Possible media type settings
-       0       10baseT
-       1       10Mbs AUI
-       2       undefined
-       3       10base2 (BNC)
-       4       100base-TX
-       5       100base-FX
-       6       MII (Media Independent Interface)
-       7       Use default setting from EEPROM
-       8       Autonegotiate
-       9       External MII
-       10      Use default setting from EEPROM
-
-  When generating a value for the 'options' setting, the above media
-  selection values may be OR'ed (or added to) the following:
-
-  0x8000  Set driver debugging level to 7
-  0x4000  Set driver debugging level to 2
-  0x0400  Enable Wake-on-LAN
-  0x0200  Force full duplex mode.
-  0x0010  Bus-master enable bit (Old Vortex cards only)
-
-  For example:
-
-    insmod 3c59x options=0x204
-
-  will force full-duplex 100base-TX, rather than allowing the usual
-  autonegotiation.
-
-global_options=N
-
-  Sets the `options' parameter for all 3c59x NICs in the machine. 
-  Entries in the `options' array above will override any setting of
-  this.
-
-full_duplex=N1,N2,N3...
-
-  Similar to bit 9 of 'options'.  Forces the corresponding card into
-  full-duplex mode.  Please use this in preference to the `options'
-  parameter.
-
-  In fact, please don't use this at all! You're better off getting
-  autonegotiation working properly.
-
-global_full_duplex=N1
-
-  Sets full duplex mode for all 3c59x NICs in the machine.  Entries
-  in the `full_duplex' array above will override any setting of this.
-
-flow_ctrl=N1,N2,N3...
-
-  Use 802.3x MAC-layer flow control.  The 3com cards only support the
-  PAUSE command, which means that they will stop sending packets for a
-  short period if they receive a PAUSE frame from the link partner. 
-
-  The driver only allows flow control on a link which is operating in
-  full duplex mode.
-
-  This feature does not appear to work on the 3c905 - only 3c905B and
-  3c905C have been tested.
-
-  The 3com cards appear to only respond to PAUSE frames which are
-  sent to the reserved destination address of 01:80:c2:00:00:01.  They
-  do not honour PAUSE frames which are sent to the station MAC address.
-
-rx_copybreak=M
-
-  The driver preallocates 32 full-sized (1536 byte) network buffers
-  for receiving.  When a packet arrives, the driver has to decide
-  whether to leave the packet in its full-sized buffer, or to allocate
-  a smaller buffer and copy the packet across into it.
-
-  This is a speed/space tradeoff.
-
-  The value of rx_copybreak is used to decide when to make the copy. 
-  If the packet size is less than rx_copybreak, the packet is copied. 
-  The default value for rx_copybreak is 200 bytes.
-
-max_interrupt_work=N
-
-  The driver's interrupt service routine can handle many receive and
-  transmit packets in a single invocation.  It does this in a loop. 
-  The value of max_interrupt_work governs how many times the interrupt
-  service routine will loop.  The default value is 32 loops.  If this
-  is exceeded the interrupt service routine gives up and generates a
-  warning message "eth0: Too much work in interrupt".
-
-hw_checksums=N1,N2,N3,...
-
-  Recent 3com NICs are able to generate IPv4, TCP and UDP checksums
-  in hardware.  Linux has used the Rx checksumming for a long time. 
-  The "zero copy" patch which is planned for the 2.4 kernel series
-  allows you to make use of the NIC's DMA scatter/gather and transmit
-  checksumming as well.
-
-  The driver is set up so that, when the zerocopy patch is applied,
-  all Tornado and Cyclone devices will use S/G and Tx checksums.
-
-  This module parameter has been provided so you can override this
-  decision.  If you think that Tx checksums are causing a problem, you
-  may disable the feature with `hw_checksums=0'.
-
-  If you think your NIC should be performing Tx checksumming and the
-  driver isn't enabling it, you can force the use of hardware Tx
-  checksumming with `hw_checksums=1'.
-
-  The driver drops a message in the logfiles to indicate whether or
-  not it is using hardware scatter/gather and hardware Tx checksums.
-
-  Scatter/gather and hardware checksums provide considerable
-  performance improvement for the sendfile() system call, but a small
-  decrease in throughput for send().  There is no effect upon receive
-  efficiency.
-
-compaq_ioaddr=N
-compaq_irq=N
-compaq_device_id=N
-
-  "Variables to work-around the Compaq PCI BIOS32 problem"....
-
-watchdog=N
-
-  Sets the time duration (in milliseconds) after which the kernel
-  decides that the transmitter has become stuck and needs to be reset. 
-  This is mainly for debugging purposes, although it may be advantageous
-  to increase this value on LANs which have very high collision rates.
-  The default value is 5000 (5.0 seconds).
-
-enable_wol=N1,N2,N3,...
-
-  Enable Wake-on-LAN support for the relevant interface.  Donald
-  Becker's `ether-wake' application may be used to wake suspended
-  machines.
-
-  Also enables the NIC's power management support.
-
-global_enable_wol=N
-
-  Sets enable_wol mode for all 3c59x NICs in the machine.  Entries in
-  the `enable_wol' array above will override any setting of this.
-
-Media selection
----------------
-
-A number of the older NICs such as the 3c590 and 3c900 series have
-10base2 and AUI interfaces.
-
-Prior to January, 2001 this driver would autoeselect the 10base2 or AUI
-port if it didn't detect activity on the 10baseT port.  It would then
-get stuck on the 10base2 port and a driver reload was necessary to
-switch back to 10baseT.  This behaviour could not be prevented with a
-module option override.
-
-Later (current) versions of the driver _do_ support locking of the
-media type.  So if you load the driver module with
-
-       modprobe 3c59x options=0
-
-it will permanently select the 10baseT port.  Automatic selection of
-other media types does not occur.
-
-
-Transmit error, Tx status register 82
--------------------------------------
-
-This is a common error which is almost always caused by another host on
-the same network being in full-duplex mode, while this host is in
-half-duplex mode.  You need to find that other host and make it run in
-half-duplex mode or fix this host to run in full-duplex mode.
-
-As a last resort, you can force the 3c59x driver into full-duplex mode
-with
-
-       options 3c59x full_duplex=1
-
-but this has to be viewed as a workaround for broken network gear and
-should only really be used for equipment which cannot autonegotiate.
-
-
-Additional resources
---------------------
-
-Details of the device driver implementation are at the top of the source file.
-
-Additional documentation is available at Don Becker's Linux Drivers site:
-
-     http://www.scyld.com/vortex.html
-
-Donald Becker's driver development site:
-
-     http://www.scyld.com/network.html
-
-Donald's vortex-diag program is useful for inspecting the NIC's state:
-
-     http://www.scyld.com/ethercard_diag.html
-
-Donald's mii-diag program may be used for inspecting and manipulating
-the NIC's Media Independent Interface subsystem:
-
-     http://www.scyld.com/ethercard_diag.html#mii-diag
-
-Donald's wake-on-LAN page:
-
-     http://www.scyld.com/wakeonlan.html
-
-3Com's DOS-based application for setting up the NICs EEPROMs:
-
-       ftp://ftp.3com.com/pub/nic/3c90x/3c90xx2.exe
-
-
-Autonegotiation notes
----------------------
-
-  The driver uses a one-minute heartbeat for adapting to changes in
-  the external LAN environment if link is up and 5 seconds if link is down.
-  This means that when, for example, a machine is unplugged from a hubbed
-  10baseT LAN plugged into a  switched 100baseT LAN, the throughput
-  will be quite dreadful for up to sixty seconds.  Be patient.
-
-  Cisco interoperability note from Walter Wong <wcw+@CMU.EDU>:
-
-  On a side note, adding HAS_NWAY seems to share a problem with the
-  Cisco 6509 switch.  Specifically, you need to change the spanning
-  tree parameter for the port the machine is plugged into to 'portfast'
-  mode.  Otherwise, the negotiation fails.  This has been an issue
-  we've noticed for a while but haven't had the time to track down.
-
-  Cisco switches    (Jeff Busch <jbusch@deja.com>)
-
-    My "standard config" for ports to which PC's/servers connect directly:
-
-        interface FastEthernet0/N
-        description machinename
-        load-interval 30
-        spanning-tree portfast
-
-    If autonegotiation is a problem, you may need to specify "speed
-    100" and "duplex full" as well (or "speed 10" and "duplex half").
-
-    WARNING: DO NOT hook up hubs/switches/bridges to these
-    specially-configured ports! The switch will become very confused.
-
-
-Reporting and diagnosing problems
----------------------------------
-
-Maintainers find that accurate and complete problem reports are
-invaluable in resolving driver problems.  We are frequently not able to
-reproduce problems and must rely on your patience and efforts to get to
-the bottom of the problem.
-
-If you believe you have a driver problem here are some of the
-steps you should take:
-
-- Is it really a driver problem?
-
-   Eliminate some variables: try different cards, different
-   computers, different cables, different ports on the switch/hub,
-   different versions of the kernel or of the driver, etc.
-
-- OK, it's a driver problem.
-
-   You need to generate a report.  Typically this is an email to the
-   maintainer and/or netdev@vger.kernel.org.  The maintainer's
-   email address will be in the driver source or in the MAINTAINERS file.
-
-- The contents of your report will vary a lot depending upon the
-  problem.  If it's a kernel crash then you should refer to the
-  admin-guide/reporting-bugs.rst file.
-
-  But for most problems it is useful to provide the following:
-
-   o Kernel version, driver version
-
-   o A copy of the banner message which the driver generates when
-     it is initialised.  For example:
-
-     eth0: 3Com PCI 3c905C Tornado at 0xa400,  00:50:da:6a:88:f0, IRQ 19
-     8K byte-wide RAM 5:3 Rx:Tx split, autoselect/Autonegotiate interface.
-     MII transceiver found at address 24, status 782d.
-     Enabling bus-master transmits and whole-frame receives.
-
-     NOTE: You must provide the `debug=2' modprobe option to generate
-     a full detection message.  Please do this:
-
-       modprobe 3c59x debug=2
-
-   o If it is a PCI device, the relevant output from 'lspci -vx', eg:
-
-     00:09.0 Ethernet controller: 3Com Corporation 3c905C-TX [Fast Etherlink] (rev 74)
-             Subsystem: 3Com Corporation: Unknown device 9200
-             Flags: bus master, medium devsel, latency 32, IRQ 19
-             I/O ports at a400 [size=128]
-             Memory at db000000 (32-bit, non-prefetchable) [size=128]
-             Expansion ROM at <unassigned> [disabled] [size=128K]
-             Capabilities: [dc] Power Management version 2
-     00: b7 10 00 92 07 00 10 02 74 00 00 02 08 20 00 00
-     10: 01 a4 00 00 00 00 00 db 00 00 00 00 00 00 00 00
-     20: 00 00 00 00 00 00 00 00 00 00 00 00 b7 10 00 10
-     30: 00 00 00 00 dc 00 00 00 00 00 00 00 05 01 0a 0a
-
-   o A description of the environment: 10baseT? 100baseT?
-     full/half duplex? switched or hubbed?
-
-   o Any additional module parameters which you may be providing to the driver.
-
-   o Any kernel logs which are produced.  The more the merrier. 
-     If this is a large file and you are sending your report to a
-     mailing list, mention that you have the logfile, but don't send
-     it.  If you're reporting direct to the maintainer then just send
-     it.
-
-     To ensure that all kernel logs are available, add the
-     following line to /etc/syslog.conf:
-
-         kern.* /var/log/messages
-
-     Then restart syslogd with:
-
-         /etc/rc.d/init.d/syslog restart
-
-     (The above may vary, depending upon which Linux distribution you use).
-
-    o If your problem is reproducible then that's great.  Try the
-      following:
-
-      1) Increase the debug level.  Usually this is done via:
-
-         a) modprobe driver debug=7
-         b) In /etc/modprobe.d/driver.conf:
-            options driver debug=7
-
-      2) Recreate the problem with the higher debug level,
-         send all logs to the maintainer.
-
-      3) Download you card's diagnostic tool from Donald
-         Becker's website <http://www.scyld.com/ethercard_diag.html>.
-         Download mii-diag.c as well.  Build these.
-
-         a) Run 'vortex-diag -aaee' and 'mii-diag -v' when the card is
-            working correctly.  Save the output.
-
-         b) Run the above commands when the card is malfunctioning.  Send
-            both sets of output.
-
-Finally, please be patient and be prepared to do some work.  You may
-end up working on this problem for a week or more as the maintainer
-asks more questions, asks for more tests, asks for patches to be
-applied, etc.  At the end of it all, the problem may even remain
-unresolved.
diff --git a/Documentation/networking/device_drivers/amazon/ena.rst b/Documentation/networking/device_drivers/amazon/ena.rst
new file mode 100644 (file)
index 0000000..11af638
--- /dev/null
@@ -0,0 +1,344 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================================================
+Linux kernel driver for Elastic Network Adapter (ENA) family
+============================================================
+
+Overview
+========
+
+ENA is a networking interface designed to make good use of modern CPU
+features and system architectures.
+
+The ENA device exposes a lightweight management interface with a
+minimal set of memory mapped registers and extendable command set
+through an Admin Queue.
+
+The driver supports a range of ENA devices, is link-speed independent
+(i.e., the same driver is used for 10GbE, 25GbE, 40GbE, etc.), and has
+a negotiated and extendable feature set.
+
+Some ENA devices support SR-IOV. This driver is used for both the
+SR-IOV Physical Function (PF) and Virtual Function (VF) devices.
+
+ENA devices enable high speed and low overhead network traffic
+processing by providing multiple Tx/Rx queue pairs (the maximum number
+is advertised by the device via the Admin Queue), a dedicated MSI-X
+interrupt vector per Tx/Rx queue pair, adaptive interrupt moderation,
+and CPU cacheline optimized data placement.
+
+The ENA driver supports industry standard TCP/IP offload features such
+as checksum offload and TCP transmit segmentation offload (TSO).
+Receive-side scaling (RSS) is supported for multi-core scaling.
+
+The ENA driver and its corresponding devices implement health
+monitoring mechanisms such as watchdog, enabling the device and driver
+to recover in a manner transparent to the application, as well as
+debug logs.
+
+Some of the ENA devices support a working mode called Low-latency
+Queue (LLQ), which saves several more microseconds.
+
+Supported PCI vendor ID/device IDs
+==================================
+
+=========   =======================
+1d0f:0ec2   ENA PF
+1d0f:1ec2   ENA PF with LLQ support
+1d0f:ec20   ENA VF
+1d0f:ec21   ENA VF with LLQ support
+=========   =======================
+
+ENA Source Code Directory Structure
+===================================
+
+=================   ======================================================
+ena_com.[ch]        Management communication layer. This layer is
+                   responsible for the handling all the management
+                   (admin) communication between the device and the
+                   driver.
+ena_eth_com.[ch]    Tx/Rx data path.
+ena_admin_defs.h    Definition of ENA management interface.
+ena_eth_io_defs.h   Definition of ENA data path interface.
+ena_common_defs.h   Common definitions for ena_com layer.
+ena_regs_defs.h     Definition of ENA PCI memory-mapped (MMIO) registers.
+ena_netdev.[ch]     Main Linux kernel driver.
+ena_syfsfs.[ch]     Sysfs files.
+ena_ethtool.c       ethtool callbacks.
+ena_pci_id_tbl.h    Supported device IDs.
+=================   ======================================================
+
+Management Interface:
+=====================
+
+ENA management interface is exposed by means of:
+
+- PCIe Configuration Space
+- Device Registers
+- Admin Queue (AQ) and Admin Completion Queue (ACQ)
+- Asynchronous Event Notification Queue (AENQ)
+
+ENA device MMIO Registers are accessed only during driver
+initialization and are not involved in further normal device
+operation.
+
+AQ is used for submitting management commands, and the
+results/responses are reported asynchronously through ACQ.
+
+ENA introduces a small set of management commands with room for
+vendor-specific extensions. Most of the management operations are
+framed in a generic Get/Set feature command.
+
+The following admin queue commands are supported:
+
+- Create I/O submission queue
+- Create I/O completion queue
+- Destroy I/O submission queue
+- Destroy I/O completion queue
+- Get feature
+- Set feature
+- Configure AENQ
+- Get statistics
+
+Refer to ena_admin_defs.h for the list of supported Get/Set Feature
+properties.
+
+The Asynchronous Event Notification Queue (AENQ) is a uni-directional
+queue used by the ENA device to send to the driver events that cannot
+be reported using ACQ. AENQ events are subdivided into groups. Each
+group may have multiple syndromes, as shown below
+
+The events are:
+
+       ====================    ===============
+       Group                   Syndrome
+       ====================    ===============
+       Link state change       **X**
+       Fatal error             **X**
+       Notification            Suspend traffic
+       Notification            Resume traffic
+       Keep-Alive              **X**
+       ====================    ===============
+
+ACQ and AENQ share the same MSI-X vector.
+
+Keep-Alive is a special mechanism that allows monitoring of the
+device's health. The driver maintains a watchdog (WD) handler which,
+if fired, logs the current state and statistics then resets and
+restarts the ENA device and driver. A Keep-Alive event is delivered by
+the device every second. The driver re-arms the WD upon reception of a
+Keep-Alive event. A missed Keep-Alive event causes the WD handler to
+fire.
+
+Data Path Interface
+===================
+I/O operations are based on Tx and Rx Submission Queues (Tx SQ and Rx
+SQ correspondingly). Each SQ has a completion queue (CQ) associated
+with it.
+
+The SQs and CQs are implemented as descriptor rings in contiguous
+physical memory.
+
+The ENA driver supports two Queue Operation modes for Tx SQs:
+
+- Regular mode
+
+  * In this mode the Tx SQs reside in the host's memory. The ENA
+    device fetches the ENA Tx descriptors and packet data from host
+    memory.
+
+- Low Latency Queue (LLQ) mode or "push-mode".
+
+  * In this mode the driver pushes the transmit descriptors and the
+    first 128 bytes of the packet directly to the ENA device memory
+    space. The rest of the packet payload is fetched by the
+    device. For this operation mode, the driver uses a dedicated PCI
+    device memory BAR, which is mapped with write-combine capability.
+
+The Rx SQs support only the regular mode.
+
+Note: Not all ENA devices support LLQ, and this feature is negotiated
+      with the device upon initialization. If the ENA device does not
+      support LLQ mode, the driver falls back to the regular mode.
+
+The driver supports multi-queue for both Tx and Rx. This has various
+benefits:
+
+- Reduced CPU/thread/process contention on a given Ethernet interface.
+- Cache miss rate on completion is reduced, particularly for data
+  cache lines that hold the sk_buff structures.
+- Increased process-level parallelism when handling received packets.
+- Increased data cache hit rate, by steering kernel processing of
+  packets to the CPU, where the application thread consuming the
+  packet is running.
+- In hardware interrupt re-direction.
+
+Interrupt Modes
+===============
+The driver assigns a single MSI-X vector per queue pair (for both Tx
+and Rx directions). The driver assigns an additional dedicated MSI-X vector
+for management (for ACQ and AENQ).
+
+Management interrupt registration is performed when the Linux kernel
+probes the adapter, and it is de-registered when the adapter is
+removed. I/O queue interrupt registration is performed when the Linux
+interface of the adapter is opened, and it is de-registered when the
+interface is closed.
+
+The management interrupt is named::
+
+   ena-mgmnt@pci:<PCI domain:bus:slot.function>
+
+and for each queue pair, an interrupt is named::
+
+   <interface name>-Tx-Rx-<queue index>
+
+The ENA device operates in auto-mask and auto-clear interrupt
+modes. That is, once MSI-X is delivered to the host, its Cause bit is
+automatically cleared and the interrupt is masked. The interrupt is
+unmasked by the driver after NAPI processing is complete.
+
+Interrupt Moderation
+====================
+ENA driver and device can operate in conventional or adaptive interrupt
+moderation mode.
+
+In conventional mode the driver instructs device to postpone interrupt
+posting according to static interrupt delay value. The interrupt delay
+value can be configured through ethtool(8). The following ethtool
+parameters are supported by the driver: tx-usecs, rx-usecs
+
+In adaptive interrupt moderation mode the interrupt delay value is
+updated by the driver dynamically and adjusted every NAPI cycle
+according to the traffic nature.
+
+By default ENA driver applies adaptive coalescing on Rx traffic and
+conventional coalescing on Tx traffic.
+
+Adaptive coalescing can be switched on/off through ethtool(8)
+adaptive_rx on|off parameter.
+
+The driver chooses interrupt delay value according to the number of
+bytes and packets received between interrupt unmasking and interrupt
+posting. The driver uses interrupt delay table that subdivides the
+range of received bytes/packets into 5 levels and assigns interrupt
+delay value to each level.
+
+The user can enable/disable adaptive moderation, modify the interrupt
+delay table and restore its default values through sysfs.
+
+RX copybreak
+============
+The rx_copybreak is initialized by default to ENA_DEFAULT_RX_COPYBREAK
+and can be configured by the ETHTOOL_STUNABLE command of the
+SIOCETHTOOL ioctl.
+
+SKB
+===
+The driver-allocated SKB for frames received from Rx handling using
+NAPI context. The allocation method depends on the size of the packet.
+If the frame length is larger than rx_copybreak, napi_get_frags()
+is used, otherwise netdev_alloc_skb_ip_align() is used, the buffer
+content is copied (by CPU) to the SKB, and the buffer is recycled.
+
+Statistics
+==========
+The user can obtain ENA device and driver statistics using ethtool.
+The driver can collect regular or extended statistics (including
+per-queue stats) from the device.
+
+In addition the driver logs the stats to syslog upon device reset.
+
+MTU
+===
+The driver supports an arbitrarily large MTU with a maximum that is
+negotiated with the device. The driver configures MTU using the
+SetFeature command (ENA_ADMIN_MTU property). The user can change MTU
+via ip(8) and similar legacy tools.
+
+Stateless Offloads
+==================
+The ENA driver supports:
+
+- TSO over IPv4/IPv6
+- TSO with ECN
+- IPv4 header checksum offload
+- TCP/UDP over IPv4/IPv6 checksum offloads
+
+RSS
+===
+- The ENA device supports RSS that allows flexible Rx traffic
+  steering.
+- Toeplitz and CRC32 hash functions are supported.
+- Different combinations of L2/L3/L4 fields can be configured as
+  inputs for hash functions.
+- The driver configures RSS settings using the AQ SetFeature command
+  (ENA_ADMIN_RSS_HASH_FUNCTION, ENA_ADMIN_RSS_HASH_INPUT and
+  ENA_ADMIN_RSS_REDIRECTION_TABLE_CONFIG properties).
+- If the NETIF_F_RXHASH flag is set, the 32-bit result of the hash
+  function delivered in the Rx CQ descriptor is set in the received
+  SKB.
+- The user can provide a hash key, hash function, and configure the
+  indirection table through ethtool(8).
+
+DATA PATH
+=========
+Tx
+--
+
+end_start_xmit() is called by the stack. This function does the following:
+
+- Maps data buffers (skb->data and frags).
+- Populates ena_buf for the push buffer (if the driver and device are
+  in push mode.)
+- Prepares ENA bufs for the remaining frags.
+- Allocates a new request ID from the empty req_id ring. The request
+  ID is the index of the packet in the Tx info. This is used for
+  out-of-order TX completions.
+- Adds the packet to the proper place in the Tx ring.
+- Calls ena_com_prepare_tx(), an ENA communication layer that converts
+  the ena_bufs to ENA descriptors (and adds meta ENA descriptors as
+  needed.)
+
+  * This function also copies the ENA descriptors and the push buffer
+    to the Device memory space (if in push mode.)
+
+- Writes doorbell to the ENA device.
+- When the ENA device finishes sending the packet, a completion
+  interrupt is raised.
+- The interrupt handler schedules NAPI.
+- The ena_clean_tx_irq() function is called. This function handles the
+  completion descriptors generated by the ENA, with a single
+  completion descriptor per completed packet.
+
+  * req_id is retrieved from the completion descriptor. The tx_info of
+    the packet is retrieved via the req_id. The data buffers are
+    unmapped and req_id is returned to the empty req_id ring.
+  * The function stops when the completion descriptors are completed or
+    the budget is reached.
+
+Rx
+--
+
+- When a packet is received from the ENA device.
+- The interrupt handler schedules NAPI.
+- The ena_clean_rx_irq() function is called. This function calls
+  ena_rx_pkt(), an ENA communication layer function, which returns the
+  number of descriptors used for a new unhandled packet, and zero if
+  no new packet is found.
+- Then it calls the ena_clean_rx_irq() function.
+- ena_eth_rx_skb() checks packet length:
+
+  * If the packet is small (len < rx_copybreak), the driver allocates
+    a SKB for the new packet, and copies the packet payload into the
+    SKB data buffer.
+
+    - In this way the original data buffer is not passed to the stack
+      and is reused for future Rx packets.
+
+  * Otherwise the function unmaps the Rx buffer, then allocates the
+    new SKB structure and hooks the Rx buffer to the SKB frags.
+
+- The new SKB is updated with the necessary information (protocol,
+  checksum hw verify result, etc.), and then passed to the network
+  stack, using the NAPI interface function napi_gro_receive().
diff --git a/Documentation/networking/device_drivers/amazon/ena.txt b/Documentation/networking/device_drivers/amazon/ena.txt
deleted file mode 100644 (file)
index 1bb55c7..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-Linux kernel driver for Elastic Network Adapter (ENA) family:
-=============================================================
-
-Overview:
-=========
-ENA is a networking interface designed to make good use of modern CPU
-features and system architectures.
-
-The ENA device exposes a lightweight management interface with a
-minimal set of memory mapped registers and extendable command set
-through an Admin Queue.
-
-The driver supports a range of ENA devices, is link-speed independent
-(i.e., the same driver is used for 10GbE, 25GbE, 40GbE, etc.), and has
-a negotiated and extendable feature set.
-
-Some ENA devices support SR-IOV. This driver is used for both the
-SR-IOV Physical Function (PF) and Virtual Function (VF) devices.
-
-ENA devices enable high speed and low overhead network traffic
-processing by providing multiple Tx/Rx queue pairs (the maximum number
-is advertised by the device via the Admin Queue), a dedicated MSI-X
-interrupt vector per Tx/Rx queue pair, adaptive interrupt moderation,
-and CPU cacheline optimized data placement.
-
-The ENA driver supports industry standard TCP/IP offload features such
-as checksum offload and TCP transmit segmentation offload (TSO).
-Receive-side scaling (RSS) is supported for multi-core scaling.
-
-The ENA driver and its corresponding devices implement health
-monitoring mechanisms such as watchdog, enabling the device and driver
-to recover in a manner transparent to the application, as well as
-debug logs.
-
-Some of the ENA devices support a working mode called Low-latency
-Queue (LLQ), which saves several more microseconds.
-
-Supported PCI vendor ID/device IDs:
-===================================
-1d0f:0ec2 - ENA PF
-1d0f:1ec2 - ENA PF with LLQ support
-1d0f:ec20 - ENA VF
-1d0f:ec21 - ENA VF with LLQ support
-
-ENA Source Code Directory Structure:
-====================================
-ena_com.[ch]      - Management communication layer. This layer is
-                    responsible for the handling all the management
-                    (admin) communication between the device and the
-                    driver.
-ena_eth_com.[ch]  - Tx/Rx data path.
-ena_admin_defs.h  - Definition of ENA management interface.
-ena_eth_io_defs.h - Definition of ENA data path interface.
-ena_common_defs.h - Common definitions for ena_com layer.
-ena_regs_defs.h   - Definition of ENA PCI memory-mapped (MMIO) registers.
-ena_netdev.[ch]   - Main Linux kernel driver.
-ena_syfsfs.[ch]   - Sysfs files.
-ena_ethtool.c     - ethtool callbacks.
-ena_pci_id_tbl.h  - Supported device IDs.
-
-Management Interface:
-=====================
-ENA management interface is exposed by means of:
-- PCIe Configuration Space
-- Device Registers
-- Admin Queue (AQ) and Admin Completion Queue (ACQ)
-- Asynchronous Event Notification Queue (AENQ)
-
-ENA device MMIO Registers are accessed only during driver
-initialization and are not involved in further normal device
-operation.
-
-AQ is used for submitting management commands, and the
-results/responses are reported asynchronously through ACQ.
-
-ENA introduces a small set of management commands with room for
-vendor-specific extensions. Most of the management operations are
-framed in a generic Get/Set feature command.
-
-The following admin queue commands are supported:
-- Create I/O submission queue
-- Create I/O completion queue
-- Destroy I/O submission queue
-- Destroy I/O completion queue
-- Get feature
-- Set feature
-- Configure AENQ
-- Get statistics
-
-Refer to ena_admin_defs.h for the list of supported Get/Set Feature
-properties.
-
-The Asynchronous Event Notification Queue (AENQ) is a uni-directional
-queue used by the ENA device to send to the driver events that cannot
-be reported using ACQ. AENQ events are subdivided into groups. Each
-group may have multiple syndromes, as shown below
-
-The events are:
-       Group                   Syndrome
-       Link state change       - X -
-       Fatal error             - X -
-       Notification            Suspend traffic
-       Notification            Resume traffic
-       Keep-Alive              - X -
-
-ACQ and AENQ share the same MSI-X vector.
-
-Keep-Alive is a special mechanism that allows monitoring of the
-device's health. The driver maintains a watchdog (WD) handler which,
-if fired, logs the current state and statistics then resets and
-restarts the ENA device and driver. A Keep-Alive event is delivered by
-the device every second. The driver re-arms the WD upon reception of a
-Keep-Alive event. A missed Keep-Alive event causes the WD handler to
-fire.
-
-Data Path Interface:
-====================
-I/O operations are based on Tx and Rx Submission Queues (Tx SQ and Rx
-SQ correspondingly). Each SQ has a completion queue (CQ) associated
-with it.
-
-The SQs and CQs are implemented as descriptor rings in contiguous
-physical memory.
-
-The ENA driver supports two Queue Operation modes for Tx SQs:
-- Regular mode
-  * In this mode the Tx SQs reside in the host's memory. The ENA
-    device fetches the ENA Tx descriptors and packet data from host
-    memory.
-- Low Latency Queue (LLQ) mode or "push-mode".
-  * In this mode the driver pushes the transmit descriptors and the
-    first 128 bytes of the packet directly to the ENA device memory
-    space. The rest of the packet payload is fetched by the
-    device. For this operation mode, the driver uses a dedicated PCI
-    device memory BAR, which is mapped with write-combine capability.
-
-The Rx SQs support only the regular mode.
-
-Note: Not all ENA devices support LLQ, and this feature is negotiated
-      with the device upon initialization. If the ENA device does not
-      support LLQ mode, the driver falls back to the regular mode.
-
-The driver supports multi-queue for both Tx and Rx. This has various
-benefits:
-- Reduced CPU/thread/process contention on a given Ethernet interface.
-- Cache miss rate on completion is reduced, particularly for data
-  cache lines that hold the sk_buff structures.
-- Increased process-level parallelism when handling received packets.
-- Increased data cache hit rate, by steering kernel processing of
-  packets to the CPU, where the application thread consuming the
-  packet is running.
-- In hardware interrupt re-direction.
-
-Interrupt Modes:
-================
-The driver assigns a single MSI-X vector per queue pair (for both Tx
-and Rx directions). The driver assigns an additional dedicated MSI-X vector
-for management (for ACQ and AENQ).
-
-Management interrupt registration is performed when the Linux kernel
-probes the adapter, and it is de-registered when the adapter is
-removed. I/O queue interrupt registration is performed when the Linux
-interface of the adapter is opened, and it is de-registered when the
-interface is closed.
-
-The management interrupt is named:
-   ena-mgmnt@pci:<PCI domain:bus:slot.function>
-and for each queue pair, an interrupt is named:
-   <interface name>-Tx-Rx-<queue index>
-
-The ENA device operates in auto-mask and auto-clear interrupt
-modes. That is, once MSI-X is delivered to the host, its Cause bit is
-automatically cleared and the interrupt is masked. The interrupt is
-unmasked by the driver after NAPI processing is complete.
-
-Interrupt Moderation:
-=====================
-ENA driver and device can operate in conventional or adaptive interrupt
-moderation mode.
-
-In conventional mode the driver instructs device to postpone interrupt
-posting according to static interrupt delay value. The interrupt delay
-value can be configured through ethtool(8). The following ethtool
-parameters are supported by the driver: tx-usecs, rx-usecs
-
-In adaptive interrupt moderation mode the interrupt delay value is
-updated by the driver dynamically and adjusted every NAPI cycle
-according to the traffic nature.
-
-By default ENA driver applies adaptive coalescing on Rx traffic and
-conventional coalescing on Tx traffic.
-
-Adaptive coalescing can be switched on/off through ethtool(8)
-adaptive_rx on|off parameter.
-
-The driver chooses interrupt delay value according to the number of
-bytes and packets received between interrupt unmasking and interrupt
-posting. The driver uses interrupt delay table that subdivides the
-range of received bytes/packets into 5 levels and assigns interrupt
-delay value to each level.
-
-The user can enable/disable adaptive moderation, modify the interrupt
-delay table and restore its default values through sysfs.
-
-RX copybreak:
-=============
-The rx_copybreak is initialized by default to ENA_DEFAULT_RX_COPYBREAK
-and can be configured by the ETHTOOL_STUNABLE command of the
-SIOCETHTOOL ioctl.
-
-SKB:
-====
-The driver-allocated SKB for frames received from Rx handling using
-NAPI context. The allocation method depends on the size of the packet.
-If the frame length is larger than rx_copybreak, napi_get_frags()
-is used, otherwise netdev_alloc_skb_ip_align() is used, the buffer
-content is copied (by CPU) to the SKB, and the buffer is recycled.
-
-Statistics:
-===========
-The user can obtain ENA device and driver statistics using ethtool.
-The driver can collect regular or extended statistics (including
-per-queue stats) from the device.
-
-In addition the driver logs the stats to syslog upon device reset.
-
-MTU:
-====
-The driver supports an arbitrarily large MTU with a maximum that is
-negotiated with the device. The driver configures MTU using the
-SetFeature command (ENA_ADMIN_MTU property). The user can change MTU
-via ip(8) and similar legacy tools.
-
-Stateless Offloads:
-===================
-The ENA driver supports:
-- TSO over IPv4/IPv6
-- TSO with ECN
-- IPv4 header checksum offload
-- TCP/UDP over IPv4/IPv6 checksum offloads
-
-RSS:
-====
-- The ENA device supports RSS that allows flexible Rx traffic
-  steering.
-- Toeplitz and CRC32 hash functions are supported.
-- Different combinations of L2/L3/L4 fields can be configured as
-  inputs for hash functions.
-- The driver configures RSS settings using the AQ SetFeature command
-  (ENA_ADMIN_RSS_HASH_FUNCTION, ENA_ADMIN_RSS_HASH_INPUT and
-  ENA_ADMIN_RSS_REDIRECTION_TABLE_CONFIG properties).
-- If the NETIF_F_RXHASH flag is set, the 32-bit result of the hash
-  function delivered in the Rx CQ descriptor is set in the received
-  SKB.
-- The user can provide a hash key, hash function, and configure the
-  indirection table through ethtool(8).
-
-DATA PATH:
-==========
-Tx:
----
-end_start_xmit() is called by the stack. This function does the following:
-- Maps data buffers (skb->data and frags).
-- Populates ena_buf for the push buffer (if the driver and device are
-  in push mode.)
-- Prepares ENA bufs for the remaining frags.
-- Allocates a new request ID from the empty req_id ring. The request
-  ID is the index of the packet in the Tx info. This is used for
-  out-of-order TX completions.
-- Adds the packet to the proper place in the Tx ring.
-- Calls ena_com_prepare_tx(), an ENA communication layer that converts
-  the ena_bufs to ENA descriptors (and adds meta ENA descriptors as
-  needed.)
-  * This function also copies the ENA descriptors and the push buffer
-    to the Device memory space (if in push mode.)
-- Writes doorbell to the ENA device.
-- When the ENA device finishes sending the packet, a completion
-  interrupt is raised.
-- The interrupt handler schedules NAPI.
-- The ena_clean_tx_irq() function is called. This function handles the
-  completion descriptors generated by the ENA, with a single
-  completion descriptor per completed packet.
-  * req_id is retrieved from the completion descriptor. The tx_info of
-    the packet is retrieved via the req_id. The data buffers are
-    unmapped and req_id is returned to the empty req_id ring.
-  * The function stops when the completion descriptors are completed or
-    the budget is reached.
-
-Rx:
----
-- When a packet is received from the ENA device.
-- The interrupt handler schedules NAPI.
-- The ena_clean_rx_irq() function is called. This function calls
-  ena_rx_pkt(), an ENA communication layer function, which returns the
-  number of descriptors used for a new unhandled packet, and zero if
-  no new packet is found.
-- Then it calls the ena_clean_rx_irq() function.
-- ena_eth_rx_skb() checks packet length:
-  * If the packet is small (len < rx_copybreak), the driver allocates
-    a SKB for the new packet, and copies the packet payload into the
-    SKB data buffer.
-    - In this way the original data buffer is not passed to the stack
-      and is reused for future Rx packets.
-  * Otherwise the function unmaps the Rx buffer, then allocates the
-    new SKB structure and hooks the Rx buffer to the SKB frags.
-- The new SKB is updated with the necessary information (protocol,
-  checksum hw verify result, etc.), and then passed to the network
-  stack, using the NAPI interface function napi_gro_receive().
diff --git a/Documentation/networking/device_drivers/aquantia/atlantic.rst b/Documentation/networking/device_drivers/aquantia/atlantic.rst
new file mode 100644 (file)
index 0000000..595ddef
--- /dev/null
@@ -0,0 +1,556 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+===============================
+Marvell(Aquantia) AQtion Driver
+===============================
+
+For the aQuantia Multi-Gigabit PCI Express Family of Ethernet Adapters
+
+.. Contents
+
+    - Identifying Your Adapter
+    - Configuration
+    - Supported ethtool options
+    - Command Line Parameters
+    - Config file parameters
+    - Support
+    - License
+
+Identifying Your Adapter
+========================
+
+The driver in this release is compatible with AQC-100, AQC-107, AQC-108
+based ethernet adapters.
+
+
+SFP+ Devices (for AQC-100 based adapters)
+-----------------------------------------
+
+This release tested with passive Direct Attach Cables (DAC) and SFP+/LC
+Optical Transceiver.
+
+Configuration
+=============
+
+Viewing Link Messages
+---------------------
+  Link messages will not be displayed to the console if the distribution is
+  restricting system messages. In order to see network driver link messages on
+  your console, set dmesg to eight by entering the following::
+
+       dmesg -n 8
+
+  .. note::
+
+     This setting is not saved across reboots.
+
+Jumbo Frames
+------------
+  The driver supports Jumbo Frames for all adapters. Jumbo Frames support is
+  enabled by changing the MTU to a value larger than the default of 1500.
+  The maximum value for the MTU is 16000.  Use the `ip` command to
+  increase the MTU size.  For example::
+
+       ip link set mtu 16000 dev enp1s0
+
+ethtool
+-------
+  The driver utilizes the ethtool interface for driver configuration and
+  diagnostics, as well as displaying statistical information. The latest
+  ethtool version is required for this functionality.
+
+NAPI
+----
+  NAPI (Rx polling mode) is supported in the atlantic driver.
+
+Supported ethtool options
+=========================
+
+Viewing adapter settings
+------------------------
+
+ ::
+
+    ethtool <ethX>
+
+ Output example::
+
+  Settings for enp1s0:
+    Supported ports: [ TP ]
+    Supported link modes:   100baseT/Full
+                           1000baseT/Full
+                           10000baseT/Full
+                           2500baseT/Full
+                           5000baseT/Full
+    Supported pause frame use: Symmetric
+    Supports auto-negotiation: Yes
+    Supported FEC modes: Not reported
+    Advertised link modes:  100baseT/Full
+                           1000baseT/Full
+                           10000baseT/Full
+                           2500baseT/Full
+                           5000baseT/Full
+    Advertised pause frame use: Symmetric
+    Advertised auto-negotiation: Yes
+    Advertised FEC modes: Not reported
+    Speed: 10000Mb/s
+    Duplex: Full
+    Port: Twisted Pair
+    PHYAD: 0
+    Transceiver: internal
+    Auto-negotiation: on
+    MDI-X: Unknown
+    Supports Wake-on: g
+    Wake-on: d
+    Link detected: yes
+
+
+ .. note::
+
+    AQrate speeds (2.5/5 Gb/s) will be displayed only with linux kernels > 4.10.
+    But you can still use these speeds::
+
+       ethtool -s eth0 autoneg off speed 2500
+
+Viewing adapter information
+---------------------------
+
+ ::
+
+  ethtool -i <ethX>
+
+ Output example::
+
+  driver: atlantic
+  version: 5.2.0-050200rc5-generic-kern
+  firmware-version: 3.1.78
+  expansion-rom-version:
+  bus-info: 0000:01:00.0
+  supports-statistics: yes
+  supports-test: no
+  supports-eeprom-access: no
+  supports-register-dump: yes
+  supports-priv-flags: no
+
+
+Viewing Ethernet adapter statistics
+-----------------------------------
+
+ ::
+
+    ethtool -S <ethX>
+
+ Output example::
+
+  NIC statistics:
+     InPackets: 13238607
+     InUCast: 13293852
+     InMCast: 52
+     InBCast: 3
+     InErrors: 0
+     OutPackets: 23703019
+     OutUCast: 23704941
+     OutMCast: 67
+     OutBCast: 11
+     InUCastOctects: 213182760
+     OutUCastOctects: 22698443
+     InMCastOctects: 6600
+     OutMCastOctects: 8776
+     InBCastOctects: 192
+     OutBCastOctects: 704
+     InOctects: 2131839552
+     OutOctects: 226938073
+     InPacketsDma: 95532300
+     OutPacketsDma: 59503397
+     InOctetsDma: 1137102462
+     OutOctetsDma: 2394339518
+     InDroppedDma: 0
+     Queue[0] InPackets: 23567131
+     Queue[0] OutPackets: 20070028
+     Queue[0] InJumboPackets: 0
+     Queue[0] InLroPackets: 0
+     Queue[0] InErrors: 0
+     Queue[1] InPackets: 45428967
+     Queue[1] OutPackets: 11306178
+     Queue[1] InJumboPackets: 0
+     Queue[1] InLroPackets: 0
+     Queue[1] InErrors: 0
+     Queue[2] InPackets: 3187011
+     Queue[2] OutPackets: 13080381
+     Queue[2] InJumboPackets: 0
+     Queue[2] InLroPackets: 0
+     Queue[2] InErrors: 0
+     Queue[3] InPackets: 23349136
+     Queue[3] OutPackets: 15046810
+     Queue[3] InJumboPackets: 0
+     Queue[3] InLroPackets: 0
+     Queue[3] InErrors: 0
+
+Interrupt coalescing support
+----------------------------
+
+ ITR mode, TX/RX coalescing timings could be viewed with::
+
+    ethtool -c <ethX>
+
+ and changed with::
+
+    ethtool -C <ethX> tx-usecs <usecs> rx-usecs <usecs>
+
+ To disable coalescing::
+
+    ethtool -C <ethX> tx-usecs 0 rx-usecs 0 tx-max-frames 1 tx-max-frames 1
+
+Wake on LAN support
+-------------------
+
+ WOL support by magic packet::
+
+    ethtool -s <ethX> wol g
+
+ To disable WOL::
+
+    ethtool -s <ethX> wol d
+
+Set and check the driver message level
+--------------------------------------
+
+ Set message level
+
+ ::
+
+    ethtool -s <ethX> msglvl <level>
+
+ Level values:
+
+ ======   =============================
+ 0x0001   general driver status.
+ 0x0002   hardware probing.
+ 0x0004   link state.
+ 0x0008   periodic status check.
+ 0x0010   interface being brought down.
+ 0x0020   interface being brought up.
+ 0x0040   receive error.
+ 0x0080   transmit error.
+ 0x0200   interrupt handling.
+ 0x0400   transmit completion.
+ 0x0800   receive completion.
+ 0x1000   packet contents.
+ 0x2000   hardware status.
+ 0x4000   Wake-on-LAN status.
+ ======   =============================
+
+ By default, the level of debugging messages is set 0x0001(general driver status).
+
+ Check message level
+
+ ::
+
+    ethtool <ethX> | grep "Current message level"
+
+ If you want to disable the output of messages::
+
+    ethtool -s <ethX> msglvl 0
+
+RX flow rules (ntuple filters)
+------------------------------
+
+ There are separate rules supported, that applies in that order:
+
+ 1. 16 VLAN ID rules
+ 2. 16 L2 EtherType rules
+ 3. 8 L3/L4 5-Tuple rules
+
+
+ The driver utilizes the ethtool interface for configuring ntuple filters,
+ via ``ethtool -N <device> <filter>``.
+
+ To enable or disable the RX flow rules::
+
+    ethtool -K ethX ntuple <on|off>
+
+ When disabling ntuple filters, all the user programed filters are
+ flushed from the driver cache and hardware. All needed filters must
+ be re-added when ntuple is re-enabled.
+
+ Because of the fixed order of the rules, the location of filters is also fixed:
+
+ - Locations 0 - 15 for VLAN ID filters
+ - Locations 16 - 31 for L2 EtherType filters
+ - Locations 32 - 39 for L3/L4 5-tuple filters (locations 32, 36 for IPv6)
+
+ The L3/L4 5-tuple (protocol, source and destination IP address, source and
+ destination TCP/UDP/SCTP port) is compared against 8 filters. For IPv4, up to
+ 8 source and destination addresses can be matched. For IPv6, up to 2 pairs of
+ addresses can be supported. Source and destination ports are only compared for
+ TCP/UDP/SCTP packets.
+
+ To add a filter that directs packet to queue 5, use
+ ``<-N|-U|--config-nfc|--config-ntuple>`` switch::
+
+    ethtool -N <ethX> flow-type udp4 src-ip 10.0.0.1 dst-ip 10.0.0.2 src-port 2000 dst-port 2001 action 5 <loc 32>
+
+ - action is the queue number.
+ - loc is the rule number.
+
+ For ``flow-type ip4|udp4|tcp4|sctp4|ip6|udp6|tcp6|sctp6`` you must set the loc
+ number within 32 - 39.
+ For ``flow-type ip4|udp4|tcp4|sctp4|ip6|udp6|tcp6|sctp6`` you can set 8 rules
+ for traffic IPv4 or you can set 2 rules for traffic IPv6. Loc number traffic
+ IPv6 is 32 and 36.
+ At the moment you can not use IPv4 and IPv6 filters at the same time.
+
+ Example filter for IPv6 filter traffic::
+
+    sudo ethtool -N <ethX> flow-type tcp6 src-ip 2001:db8:0:f101::1 dst-ip 2001:db8:0:f101::2 action 1 loc 32
+    sudo ethtool -N <ethX> flow-type ip6 src-ip 2001:db8:0:f101::2 dst-ip 2001:db8:0:f101::5 action -1 loc 36
+
+ Example filter for IPv4 filter traffic::
+
+    sudo ethtool -N <ethX> flow-type udp4 src-ip 10.0.0.4 dst-ip 10.0.0.7 src-port 2000 dst-port 2001 loc 32
+    sudo ethtool -N <ethX> flow-type tcp4 src-ip 10.0.0.3 dst-ip 10.0.0.9 src-port 2000 dst-port 2001 loc 33
+    sudo ethtool -N <ethX> flow-type ip4 src-ip 10.0.0.6 dst-ip 10.0.0.4 loc 34
+
+ If you set action -1, then all traffic corresponding to the filter will be discarded.
+
+ The maximum value action is 31.
+
+
+ The VLAN filter (VLAN id) is compared against 16 filters.
+ VLAN id must be accompanied by mask 0xF000. That is to distinguish VLAN filter
+ from L2 Ethertype filter with UserPriority since both User Priority and VLAN ID
+ are passed in the same 'vlan' parameter.
+
+ To add a filter that directs packets from VLAN 2001 to queue 5::
+
+    ethtool -N <ethX> flow-type ip4 vlan 2001 m 0xF000 action 1 loc 0
+
+
+ L2 EtherType filters allows filter packet by EtherType field or both EtherType
+ and User Priority (PCP) field of 802.1Q.
+ UserPriority (vlan) parameter must be accompanied by mask 0x1FFF. That is to
+ distinguish VLAN filter from L2 Ethertype filter with UserPriority since both
+ User Priority and VLAN ID are passed in the same 'vlan' parameter.
+
+ To add a filter that directs IP4 packess of priority 3 to queue 3::
+
+    ethtool -N <ethX> flow-type ether proto 0x800 vlan 0x600 m 0x1FFF action 3 loc 16
+
+ To see the list of filters currently present::
+
+    ethtool <-u|-n|--show-nfc|--show-ntuple> <ethX>
+
+ Rules may be deleted from the table itself. This is done using::
+
+    sudo ethtool <-N|-U|--config-nfc|--config-ntuple> <ethX> delete <loc>
+
+ - loc is the rule number to be deleted.
+
+ Rx filters is an interface to load the filter table that funnels all flow
+ into queue 0 unless an alternative queue is specified using "action". In that
+ case, any flow that matches the filter criteria will be directed to the
+ appropriate queue. RX filters is supported on all kernels 2.6.30 and later.
+
+RSS for UDP
+-----------
+
+ Currently, NIC does not support RSS for fragmented IP packets, which leads to
+ incorrect working of RSS for fragmented UDP traffic. To disable RSS for UDP the
+ RX Flow L3/L4 rule may be used.
+
+ Example::
+
+    ethtool -N eth0 flow-type udp4 action 0 loc 32
+
+UDP GSO hardware offload
+------------------------
+
+ UDP GSO allows to boost UDP tx rates by offloading UDP headers allocation
+ into hardware. A special userspace socket option is required for this,
+ could be validated with /kernel/tools/testing/selftests/net/::
+
+    udpgso_bench_tx -u -4 -D 10.0.1.1 -s 6300 -S 100
+
+ Will cause sending out of 100 byte sized UDP packets formed from single
+ 6300 bytes user buffer.
+
+ UDP GSO is configured by::
+
+    ethtool -K eth0 tx-udp-segmentation on
+
+Private flags (testing)
+-----------------------
+
+ Atlantic driver supports private flags for hardware custom features::
+
+       $ ethtool --show-priv-flags ethX
+
+       Private flags for ethX:
+       DMASystemLoopback  : off
+       PKTSystemLoopback  : off
+       DMANetworkLoopback : off
+       PHYInternalLoopback: off
+       PHYExternalLoopback: off
+
+ Example::
+
+       $ ethtool --set-priv-flags ethX DMASystemLoopback on
+
+ DMASystemLoopback:   DMA Host loopback.
+ PKTSystemLoopback:   Packet buffer host loopback.
+ DMANetworkLoopback:  Network side loopback on DMA block.
+ PHYInternalLoopback: Internal loopback on Phy.
+ PHYExternalLoopback: External loopback on Phy (with loopback ethernet cable).
+
+
+Command Line Parameters
+=======================
+The following command line parameters are available on atlantic driver:
+
+aq_itr -Interrupt throttling mode
+---------------------------------
+Accepted values: 0, 1, 0xFFFF
+
+Default value: 0xFFFF
+
+======   ==============================================================
+0        Disable interrupt throttling.
+1        Enable interrupt throttling and use specified tx and rx rates.
+0xFFFF   Auto throttling mode. Driver will choose the best RX and TX
+        interrupt throtting settings based on link speed.
+======   ==============================================================
+
+aq_itr_tx - TX interrupt throttle rate
+--------------------------------------
+
+Accepted values: 0 - 0x1FF
+
+Default value: 0
+
+TX side throttling in microseconds. Adapter will setup maximum interrupt delay
+to this value. Minimum interrupt delay will be a half of this value
+
+aq_itr_rx - RX interrupt throttle rate
+--------------------------------------
+
+Accepted values: 0 - 0x1FF
+
+Default value: 0
+
+RX side throttling in microseconds. Adapter will setup maximum interrupt delay
+to this value. Minimum interrupt delay will be a half of this value
+
+.. note::
+
+   ITR settings could be changed in runtime by ethtool -c means (see below)
+
+Config file parameters
+======================
+
+For some fine tuning and performance optimizations,
+some parameters can be changed in the {source_dir}/aq_cfg.h file.
+
+AQ_CFG_RX_PAGEORDER
+-------------------
+
+Default value: 0
+
+RX page order override. Thats a power of 2 number of RX pages allocated for
+each descriptor. Received descriptor size is still limited by
+AQ_CFG_RX_FRAME_MAX.
+
+Increasing pageorder makes page reuse better (actual on iommu enabled systems).
+
+AQ_CFG_RX_REFILL_THRES
+----------------------
+
+Default value: 32
+
+RX refill threshold. RX path will not refill freed descriptors until the
+specified number of free descriptors is observed. Larger values may help
+better page reuse but may lead to packet drops as well.
+
+AQ_CFG_VECS_DEF
+---------------
+
+Number of queues
+
+Valid Range: 0 - 8 (up to AQ_CFG_VECS_MAX)
+
+Default value: 8
+
+Notice this value will be capped by the number of cores available on the system.
+
+AQ_CFG_IS_RSS_DEF
+-----------------
+
+Enable/disable Receive Side Scaling
+
+This feature allows the adapter to distribute receive processing
+across multiple CPU-cores and to prevent from overloading a single CPU core.
+
+Valid values
+
+==  ========
+0   disabled
+1   enabled
+==  ========
+
+Default value: 1
+
+AQ_CFG_NUM_RSS_QUEUES_DEF
+-------------------------
+
+Number of queues for Receive Side Scaling
+
+Valid Range: 0 - 8 (up to AQ_CFG_VECS_DEF)
+
+Default value: AQ_CFG_VECS_DEF
+
+AQ_CFG_IS_LRO_DEF
+-----------------
+
+Enable/disable Large Receive Offload
+
+This offload enables the adapter to coalesce multiple TCP segments and indicate
+them as a single coalesced unit to the OS networking subsystem.
+
+The system consumes less energy but it also introduces more latency in packets
+processing.
+
+Valid values
+
+==  ========
+0   disabled
+1   enabled
+==  ========
+
+Default value: 1
+
+AQ_CFG_TX_CLEAN_BUDGET
+----------------------
+
+Maximum descriptors to cleanup on TX at once.
+
+Default value: 256
+
+After the aq_cfg.h file changed the driver must be rebuilt to take effect.
+
+Support
+=======
+
+If an issue is identified with the released source code on the supported
+kernel with a supported adapter, email the specific information related
+to the issue to aqn_support@marvell.com
+
+License
+=======
+
+aQuantia Corporation Network Driver
+
+Copyright |copy| 2014 - 2019 aQuantia Corporation.
+
+This program is free software; you can redistribute it and/or modify it
+under the terms and conditions of the GNU General Public License,
+version 2, as published by the Free Software Foundation.
diff --git a/Documentation/networking/device_drivers/aquantia/atlantic.txt b/Documentation/networking/device_drivers/aquantia/atlantic.txt
deleted file mode 100644 (file)
index 2013fce..0000000
+++ /dev/null
@@ -1,479 +0,0 @@
-Marvell(Aquantia) AQtion Driver for the aQuantia Multi-Gigabit PCI Express
-Family of Ethernet Adapters
-=============================================================================
-
-Contents
-========
-
-- Identifying Your Adapter
-- Configuration
-- Supported ethtool options
-- Command Line Parameters
-- Config file parameters
-- Support
-- License
-
-Identifying Your Adapter
-========================
-
-The driver in this release is compatible with AQC-100, AQC-107, AQC-108 based ethernet adapters.
-
-
-SFP+ Devices (for AQC-100 based adapters)
-----------------------------------
-
-This release tested with passive Direct Attach Cables (DAC) and SFP+/LC Optical Transceiver.
-
-Configuration
-=========================
-  Viewing Link Messages
-  ---------------------
-  Link messages will not be displayed to the console if the distribution is
-  restricting system messages. In order to see network driver link messages on
-  your console, set dmesg to eight by entering the following:
-
-       dmesg -n 8
-
-  NOTE: This setting is not saved across reboots.
-
-  Jumbo Frames
-  ------------
-  The driver supports Jumbo Frames for all adapters. Jumbo Frames support is
-  enabled by changing the MTU to a value larger than the default of 1500.
-  The maximum value for the MTU is 16000.  Use the `ip` command to
-  increase the MTU size.  For example:
-
-        ip link set mtu 16000 dev enp1s0
-
-  ethtool
-  -------
-  The driver utilizes the ethtool interface for driver configuration and
-  diagnostics, as well as displaying statistical information. The latest
-  ethtool version is required for this functionality.
-
-  NAPI
-  ----
-  NAPI (Rx polling mode) is supported in the atlantic driver.
-
-Supported ethtool options
-============================
- Viewing adapter settings
- ---------------------
- ethtool <ethX>
-
- Output example:
-
-  Settings for enp1s0:
-    Supported ports: [ TP ]
-    Supported link modes:   100baseT/Full
-                            1000baseT/Full
-                            10000baseT/Full
-                            2500baseT/Full
-                            5000baseT/Full
-    Supported pause frame use: Symmetric
-    Supports auto-negotiation: Yes
-    Supported FEC modes: Not reported
-    Advertised link modes:  100baseT/Full
-                            1000baseT/Full
-                            10000baseT/Full
-                            2500baseT/Full
-                            5000baseT/Full
-    Advertised pause frame use: Symmetric
-    Advertised auto-negotiation: Yes
-    Advertised FEC modes: Not reported
-    Speed: 10000Mb/s
-    Duplex: Full
-    Port: Twisted Pair
-    PHYAD: 0
-    Transceiver: internal
-    Auto-negotiation: on
-    MDI-X: Unknown
-    Supports Wake-on: g
-    Wake-on: d
-    Link detected: yes
-
- ---
- Note: AQrate speeds (2.5/5 Gb/s) will be displayed only with linux kernels > 4.10.
-    But you can still use these speeds:
-       ethtool -s eth0 autoneg off speed 2500
-
- Viewing adapter information
- ---------------------
- ethtool -i <ethX>
-
- Output example:
-
-  driver: atlantic
-  version: 5.2.0-050200rc5-generic-kern
-  firmware-version: 3.1.78
-  expansion-rom-version:
-  bus-info: 0000:01:00.0
-  supports-statistics: yes
-  supports-test: no
-  supports-eeprom-access: no
-  supports-register-dump: yes
-  supports-priv-flags: no
-
-
- Viewing Ethernet adapter statistics:
- ---------------------
- ethtool -S <ethX>
-
- Output example:
- NIC statistics:
-     InPackets: 13238607
-     InUCast: 13293852
-     InMCast: 52
-     InBCast: 3
-     InErrors: 0
-     OutPackets: 23703019
-     OutUCast: 23704941
-     OutMCast: 67
-     OutBCast: 11
-     InUCastOctects: 213182760
-     OutUCastOctects: 22698443
-     InMCastOctects: 6600
-     OutMCastOctects: 8776
-     InBCastOctects: 192
-     OutBCastOctects: 704
-     InOctects: 2131839552
-     OutOctects: 226938073
-     InPacketsDma: 95532300
-     OutPacketsDma: 59503397
-     InOctetsDma: 1137102462
-     OutOctetsDma: 2394339518
-     InDroppedDma: 0
-     Queue[0] InPackets: 23567131
-     Queue[0] OutPackets: 20070028
-     Queue[0] InJumboPackets: 0
-     Queue[0] InLroPackets: 0
-     Queue[0] InErrors: 0
-     Queue[1] InPackets: 45428967
-     Queue[1] OutPackets: 11306178
-     Queue[1] InJumboPackets: 0
-     Queue[1] InLroPackets: 0
-     Queue[1] InErrors: 0
-     Queue[2] InPackets: 3187011
-     Queue[2] OutPackets: 13080381
-     Queue[2] InJumboPackets: 0
-     Queue[2] InLroPackets: 0
-     Queue[2] InErrors: 0
-     Queue[3] InPackets: 23349136
-     Queue[3] OutPackets: 15046810
-     Queue[3] InJumboPackets: 0
-     Queue[3] InLroPackets: 0
-     Queue[3] InErrors: 0
-
- Interrupt coalescing support
- ---------------------------------
- ITR mode, TX/RX coalescing timings could be viewed with:
-
- ethtool -c <ethX>
-
- and changed with:
-
- ethtool -C <ethX> tx-usecs <usecs> rx-usecs <usecs>
-
- To disable coalescing:
-
- ethtool -C <ethX> tx-usecs 0 rx-usecs 0 tx-max-frames 1 tx-max-frames 1
-
- Wake on LAN support
- ---------------------------------
-
- WOL support by magic packet:
-
- ethtool -s <ethX> wol g
-
- To disable WOL:
-
- ethtool -s <ethX> wol d
-
- Set and check the driver message level
- ---------------------------------
-
- Set message level
-
- ethtool -s <ethX> msglvl <level>
-
- Level values:
-
- 0x0001 - general driver status.
- 0x0002 - hardware probing.
- 0x0004 - link state.
- 0x0008 - periodic status check.
- 0x0010 - interface being brought down.
- 0x0020 - interface being brought up.
- 0x0040 - receive error.
- 0x0080 - transmit error.
- 0x0200 - interrupt handling.
- 0x0400 - transmit completion.
- 0x0800 - receive completion.
- 0x1000 - packet contents.
- 0x2000 - hardware status.
- 0x4000 - Wake-on-LAN status.
-
- By default, the level of debugging messages is set 0x0001(general driver status).
-
- Check message level
-
- ethtool <ethX> | grep "Current message level"
-
- If you want to disable the output of messages
-
- ethtool -s <ethX> msglvl 0
-
- RX flow rules (ntuple filters)
- ---------------------------------
- There are separate rules supported, that applies in that order:
- 1. 16 VLAN ID rules
- 2. 16 L2 EtherType rules
- 3. 8 L3/L4 5-Tuple rules
-
-
- The driver utilizes the ethtool interface for configuring ntuple filters,
- via "ethtool -N <device> <filter>".
-
- To enable or disable the RX flow rules:
-
- ethtool -K ethX ntuple <on|off>
-
- When disabling ntuple filters, all the user programed filters are
- flushed from the driver cache and hardware. All needed filters must
- be re-added when ntuple is re-enabled.
-
- Because of the fixed order of the rules, the location of filters is also fixed:
- - Locations 0 - 15 for VLAN ID filters
- - Locations 16 - 31 for L2 EtherType filters
- - Locations 32 - 39 for L3/L4 5-tuple filters (locations 32, 36 for IPv6)
-
- The L3/L4 5-tuple (protocol, source and destination IP address, source and
- destination TCP/UDP/SCTP port) is compared against 8 filters. For IPv4, up to
- 8 source and destination addresses can be matched. For IPv6, up to 2 pairs of
- addresses can be supported. Source and destination ports are only compared for
- TCP/UDP/SCTP packets.
-
- To add a filter that directs packet to queue 5, use <-N|-U|--config-nfc|--config-ntuple> switch:
-
- ethtool -N <ethX> flow-type udp4 src-ip 10.0.0.1 dst-ip 10.0.0.2 src-port 2000 dst-port 2001 action 5 <loc 32>
-
- - action is the queue number.
- - loc is the rule number.
-
- For "flow-type ip4|udp4|tcp4|sctp4|ip6|udp6|tcp6|sctp6" you must set the loc
- number within 32 - 39.
- For "flow-type ip4|udp4|tcp4|sctp4|ip6|udp6|tcp6|sctp6" you can set 8 rules
- for traffic IPv4 or you can set 2 rules for traffic IPv6. Loc number traffic
- IPv6 is 32 and 36.
- At the moment you can not use IPv4 and IPv6 filters at the same time.
-
- Example filter for IPv6 filter traffic:
-
- sudo ethtool -N <ethX> flow-type tcp6 src-ip 2001:db8:0:f101::1 dst-ip 2001:db8:0:f101::2 action 1 loc 32
- sudo ethtool -N <ethX> flow-type ip6 src-ip 2001:db8:0:f101::2 dst-ip 2001:db8:0:f101::5 action -1 loc 36
-
- Example filter for IPv4 filter traffic:
-
- sudo ethtool -N <ethX> flow-type udp4 src-ip 10.0.0.4 dst-ip 10.0.0.7 src-port 2000 dst-port 2001 loc 32
- sudo ethtool -N <ethX> flow-type tcp4 src-ip 10.0.0.3 dst-ip 10.0.0.9 src-port 2000 dst-port 2001 loc 33
- sudo ethtool -N <ethX> flow-type ip4 src-ip 10.0.0.6 dst-ip 10.0.0.4 loc 34
-
- If you set action -1, then all traffic corresponding to the filter will be discarded.
- The maximum value action is 31.
-
-
- The VLAN filter (VLAN id) is compared against 16 filters.
- VLAN id must be accompanied by mask 0xF000. That is to distinguish VLAN filter
- from L2 Ethertype filter with UserPriority since both User Priority and VLAN ID
- are passed in the same 'vlan' parameter.
-
- To add a filter that directs packets from VLAN 2001 to queue 5:
- ethtool -N <ethX> flow-type ip4 vlan 2001 m 0xF000 action 1 loc 0
-
-
- L2 EtherType filters allows filter packet by EtherType field or both EtherType
- and User Priority (PCP) field of 802.1Q.
- UserPriority (vlan) parameter must be accompanied by mask 0x1FFF. That is to
- distinguish VLAN filter from L2 Ethertype filter with UserPriority since both
- User Priority and VLAN ID are passed in the same 'vlan' parameter.
-
- To add a filter that directs IP4 packess of priority 3 to queue 3:
- ethtool -N <ethX> flow-type ether proto 0x800 vlan 0x600 m 0x1FFF action 3 loc 16
-
-
- To see the list of filters currently present:
-
- ethtool <-u|-n|--show-nfc|--show-ntuple> <ethX>
-
- Rules may be deleted from the table itself. This is done using:
-
- sudo ethtool <-N|-U|--config-nfc|--config-ntuple> <ethX> delete <loc>
-
- - loc is the rule number to be deleted.
-
- Rx filters is an interface to load the filter table that funnels all flow
- into queue 0 unless an alternative queue is specified using "action". In that
- case, any flow that matches the filter criteria will be directed to the
- appropriate queue. RX filters is supported on all kernels 2.6.30 and later.
-
- RSS for UDP
- ---------------------------------
- Currently, NIC does not support RSS for fragmented IP packets, which leads to
- incorrect working of RSS for fragmented UDP traffic. To disable RSS for UDP the
- RX Flow L3/L4 rule may be used.
-
- Example:
- ethtool -N eth0 flow-type udp4 action 0 loc 32
-
- UDP GSO hardware offload
- ---------------------------------
- UDP GSO allows to boost UDP tx rates by offloading UDP headers allocation
- into hardware. A special userspace socket option is required for this,
- could be validated with /kernel/tools/testing/selftests/net/
-
-    udpgso_bench_tx -u -4 -D 10.0.1.1 -s 6300 -S 100
-
- Will cause sending out of 100 byte sized UDP packets formed from single
- 6300 bytes user buffer.
-
- UDP GSO is configured by:
-
-    ethtool -K eth0 tx-udp-segmentation on
-
- Private flags (testing)
- ---------------------------------
-
- Atlantic driver supports private flags for hardware custom features:
-
-       $ ethtool --show-priv-flags ethX
-
-       Private flags for ethX:
-       DMASystemLoopback  : off
-       PKTSystemLoopback  : off
-       DMANetworkLoopback : off
-       PHYInternalLoopback: off
-       PHYExternalLoopback: off
-
- Example:
-
-       $ ethtool --set-priv-flags ethX DMASystemLoopback on
-
- DMASystemLoopback:   DMA Host loopback.
- PKTSystemLoopback:   Packet buffer host loopback.
- DMANetworkLoopback:  Network side loopback on DMA block.
- PHYInternalLoopback: Internal loopback on Phy.
- PHYExternalLoopback: External loopback on Phy (with loopback ethernet cable).
-
-
-Command Line Parameters
-=======================
-The following command line parameters are available on atlantic driver:
-
-aq_itr -Interrupt throttling mode
-----------------------------------------
-Accepted values: 0, 1, 0xFFFF
-Default value: 0xFFFF
-0      - Disable interrupt throttling.
-1      - Enable interrupt throttling and use specified tx and rx rates.
-0xFFFF - Auto throttling mode. Driver will choose the best RX and TX
-         interrupt throtting settings based on link speed.
-
-aq_itr_tx - TX interrupt throttle rate
-----------------------------------------
-Accepted values: 0 - 0x1FF
-Default value: 0
-TX side throttling in microseconds. Adapter will setup maximum interrupt delay
-to this value. Minimum interrupt delay will be a half of this value
-
-aq_itr_rx - RX interrupt throttle rate
-----------------------------------------
-Accepted values: 0 - 0x1FF
-Default value: 0
-RX side throttling in microseconds. Adapter will setup maximum interrupt delay
-to this value. Minimum interrupt delay will be a half of this value
-
-Note: ITR settings could be changed in runtime by ethtool -c means (see below)
-
-Config file parameters
-=======================
-For some fine tuning and performance optimizations,
-some parameters can be changed in the {source_dir}/aq_cfg.h file.
-
-AQ_CFG_RX_PAGEORDER
-----------------------------------------
-Default value: 0
-RX page order override. Thats a power of 2 number of RX pages allocated for
-each descriptor. Received descriptor size is still limited by AQ_CFG_RX_FRAME_MAX.
-Increasing pageorder makes page reuse better (actual on iommu enabled systems).
-
-AQ_CFG_RX_REFILL_THRES
-----------------------------------------
-Default value: 32
-RX refill threshold. RX path will not refill freed descriptors until the
-specified number of free descriptors is observed. Larger values may help
-better page reuse but may lead to packet drops as well.
-
-AQ_CFG_VECS_DEF
-------------------------------------------------------------
-Number of queues
-Valid Range: 0 - 8 (up to AQ_CFG_VECS_MAX)
-Default value: 8
-Notice this value will be capped by the number of cores available on the system.
-
-AQ_CFG_IS_RSS_DEF
-------------------------------------------------------------
-Enable/disable Receive Side Scaling
-
-This feature allows the adapter to distribute receive processing
-across multiple CPU-cores and to prevent from overloading a single CPU core.
-
-Valid values
-0 - disabled
-1 - enabled
-
-Default value: 1
-
-AQ_CFG_NUM_RSS_QUEUES_DEF
-------------------------------------------------------------
-Number of queues for Receive Side Scaling
-Valid Range: 0 - 8 (up to AQ_CFG_VECS_DEF)
-
-Default value: AQ_CFG_VECS_DEF
-
-AQ_CFG_IS_LRO_DEF
-------------------------------------------------------------
-Enable/disable Large Receive Offload
-
-This offload enables the adapter to coalesce multiple TCP segments and indicate
-them as a single coalesced unit to the OS networking subsystem.
-The system consumes less energy but it also introduces more latency in packets processing.
-
-Valid values
-0 - disabled
-1 - enabled
-
-Default value: 1
-
-AQ_CFG_TX_CLEAN_BUDGET
-----------------------------------------
-Maximum descriptors to cleanup on TX at once.
-Default value: 256
-
-After the aq_cfg.h file changed the driver must be rebuilt to take effect.
-
-Support
-=======
-
-If an issue is identified with the released source code on the supported
-kernel with a supported adapter, email the specific information related
-to the issue to aqn_support@marvell.com
-
-License
-=======
-
-aQuantia Corporation Network Driver
-Copyright(c) 2014 - 2019 aQuantia Corporation.
-
-This program is free software; you can redistribute it and/or modify it
-under the terms and conditions of the GNU General Public License,
-version 2, as published by the Free Software Foundation.
diff --git a/Documentation/networking/device_drivers/chelsio/cxgb.rst b/Documentation/networking/device_drivers/chelsio/cxgb.rst
new file mode 100644 (file)
index 0000000..435dce5
--- /dev/null
@@ -0,0 +1,393 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+=============================================
+Chelsio N210 10Gb Ethernet Network Controller
+=============================================
+
+Driver Release Notes for Linux
+
+Version 2.1.1
+
+June 20, 2005
+
+.. Contents
+
+ INTRODUCTION
+ FEATURES
+ PERFORMANCE
+ DRIVER MESSAGES
+ KNOWN ISSUES
+ SUPPORT
+
+
+Introduction
+============
+
+ This document describes the Linux driver for Chelsio 10Gb Ethernet Network
+ Controller. This driver supports the Chelsio N210 NIC and is backward
+ compatible with the Chelsio N110 model 10Gb NICs.
+
+
+Features
+========
+
+Adaptive Interrupts (adaptive-rx)
+---------------------------------
+
+  This feature provides an adaptive algorithm that adjusts the interrupt
+  coalescing parameters, allowing the driver to dynamically adapt the latency
+  settings to achieve the highest performance during various types of network
+  load.
+
+  The interface used to control this feature is ethtool. Please see the
+  ethtool manpage for additional usage information.
+
+  By default, adaptive-rx is disabled.
+  To enable adaptive-rx::
+
+      ethtool -C <interface> adaptive-rx on
+
+  To disable adaptive-rx, use ethtool::
+
+      ethtool -C <interface> adaptive-rx off
+
+  After disabling adaptive-rx, the timer latency value will be set to 50us.
+  You may set the timer latency after disabling adaptive-rx::
+
+      ethtool -C <interface> rx-usecs <microseconds>
+
+  An example to set the timer latency value to 100us on eth0::
+
+      ethtool -C eth0 rx-usecs 100
+
+  You may also provide a timer latency value while disabling adaptive-rx::
+
+      ethtool -C <interface> adaptive-rx off rx-usecs <microseconds>
+
+  If adaptive-rx is disabled and a timer latency value is specified, the timer
+  will be set to the specified value until changed by the user or until
+  adaptive-rx is enabled.
+
+  To view the status of the adaptive-rx and timer latency values::
+
+      ethtool -c <interface>
+
+
+TCP Segmentation Offloading (TSO) Support
+-----------------------------------------
+
+  This feature, also known as "large send", enables a system's protocol stack
+  to offload portions of outbound TCP processing to a network interface card
+  thereby reducing system CPU utilization and enhancing performance.
+
+  The interface used to control this feature is ethtool version 1.8 or higher.
+  Please see the ethtool manpage for additional usage information.
+
+  By default, TSO is enabled.
+  To disable TSO::
+
+      ethtool -K <interface> tso off
+
+  To enable TSO::
+
+      ethtool -K <interface> tso on
+
+  To view the status of TSO::
+
+      ethtool -k <interface>
+
+
+Performance
+===========
+
+ The following information is provided as an example of how to change system
+ parameters for "performance tuning" an what value to use. You may or may not
+ want to change these system parameters, depending on your server/workstation
+ application. Doing so is not warranted in any way by Chelsio Communications,
+ and is done at "YOUR OWN RISK". Chelsio will not be held responsible for loss
+ of data or damage to equipment.
+
+ Your distribution may have a different way of doing things, or you may prefer
+ a different method. These commands are shown only to provide an example of
+ what to do and are by no means definitive.
+
+ Making any of the following system changes will only last until you reboot
+ your system. You may want to write a script that runs at boot-up which
+ includes the optimal settings for your system.
+
+  Setting PCI Latency Timer::
+
+      setpci -d 1425::
+
+* 0x0c.l=0x0000F800
+
+  Disabling TCP timestamp::
+
+      sysctl -w net.ipv4.tcp_timestamps=0
+
+  Disabling SACK::
+
+      sysctl -w net.ipv4.tcp_sack=0
+
+  Setting large number of incoming connection requests::
+
+      sysctl -w net.ipv4.tcp_max_syn_backlog=3000
+
+  Setting maximum receive socket buffer size::
+
+      sysctl -w net.core.rmem_max=1024000
+
+  Setting maximum send socket buffer size::
+
+      sysctl -w net.core.wmem_max=1024000
+
+  Set smp_affinity (on a multiprocessor system) to a single CPU::
+
+      echo 1 > /proc/irq/<interrupt_number>/smp_affinity
+
+  Setting default receive socket buffer size::
+
+      sysctl -w net.core.rmem_default=524287
+
+  Setting default send socket buffer size::
+
+      sysctl -w net.core.wmem_default=524287
+
+  Setting maximum option memory buffers::
+
+      sysctl -w net.core.optmem_max=524287
+
+  Setting maximum backlog (# of unprocessed packets before kernel drops)::
+
+      sysctl -w net.core.netdev_max_backlog=300000
+
+  Setting TCP read buffers (min/default/max)::
+
+      sysctl -w net.ipv4.tcp_rmem="10000000 10000000 10000000"
+
+  Setting TCP write buffers (min/pressure/max)::
+
+      sysctl -w net.ipv4.tcp_wmem="10000000 10000000 10000000"
+
+  Setting TCP buffer space (min/pressure/max)::
+
+      sysctl -w net.ipv4.tcp_mem="10000000 10000000 10000000"
+
+  TCP window size for single connections:
+
+   The receive buffer (RX_WINDOW) size must be at least as large as the
+   Bandwidth-Delay Product of the communication link between the sender and
+   receiver. Due to the variations of RTT, you may want to increase the buffer
+   size up to 2 times the Bandwidth-Delay Product. Reference page 289 of
+   "TCP/IP Illustrated, Volume 1, The Protocols" by W. Richard Stevens.
+
+   At 10Gb speeds, use the following formula::
+
+       RX_WINDOW >= 1.25MBytes * RTT(in milliseconds)
+       Example for RTT with 100us: RX_WINDOW = (1,250,000 * 0.1) = 125,000
+
+   RX_WINDOW sizes of 256KB - 512KB should be sufficient.
+
+   Setting the min, max, and default receive buffer (RX_WINDOW) size::
+
+       sysctl -w net.ipv4.tcp_rmem="<min> <default> <max>"
+
+  TCP window size for multiple connections:
+   The receive buffer (RX_WINDOW) size may be calculated the same as single
+   connections, but should be divided by the number of connections. The
+   smaller window prevents congestion and facilitates better pacing,
+   especially if/when MAC level flow control does not work well or when it is
+   not supported on the machine. Experimentation may be necessary to attain
+   the correct value. This method is provided as a starting point for the
+   correct receive buffer size.
+
+   Setting the min, max, and default receive buffer (RX_WINDOW) size is
+   performed in the same manner as single connection.
+
+
+Driver Messages
+===============
+
+ The following messages are the most common messages logged by syslog. These
+ may be found in /var/log/messages.
+
+  Driver up::
+
+     Chelsio Network Driver - version 2.1.1
+
+  NIC detected::
+
+     eth#: Chelsio N210 1x10GBaseX NIC (rev #), PCIX 133MHz/64-bit
+
+  Link up::
+
+     eth#: link is up at 10 Gbps, full duplex
+
+  Link down::
+
+     eth#: link is down
+
+
+Known Issues
+============
+
+ These issues have been identified during testing. The following information
+ is provided as a workaround to the problem. In some cases, this problem is
+ inherent to Linux or to a particular Linux Distribution and/or hardware
+ platform.
+
+  1. Large number of TCP retransmits on a multiprocessor (SMP) system.
+
+      On a system with multiple CPUs, the interrupt (IRQ) for the network
+      controller may be bound to more than one CPU. This will cause TCP
+      retransmits if the packet data were to be split across different CPUs
+      and re-assembled in a different order than expected.
+
+      To eliminate the TCP retransmits, set smp_affinity on the particular
+      interrupt to a single CPU. You can locate the interrupt (IRQ) used on
+      the N110/N210 by using ifconfig::
+
+         ifconfig <dev_name> | grep Interrupt
+
+      Set the smp_affinity to a single CPU::
+
+         echo 1 > /proc/irq/<interrupt_number>/smp_affinity
+
+      It is highly suggested that you do not run the irqbalance daemon on your
+      system, as this will change any smp_affinity setting you have applied.
+      The irqbalance daemon runs on a 10 second interval and binds interrupts
+      to the least loaded CPU determined by the daemon. To disable this daemon::
+
+         chkconfig --level 2345 irqbalance off
+
+      By default, some Linux distributions enable the kernel feature,
+      irqbalance, which performs the same function as the daemon. To disable
+      this feature, add the following line to your bootloader::
+
+         noirqbalance
+
+         Example using the Grub bootloader::
+
+             title Red Hat Enterprise Linux AS (2.4.21-27.ELsmp)
+             root (hd0,0)
+             kernel /vmlinuz-2.4.21-27.ELsmp ro root=/dev/hda3 noirqbalance
+             initrd /initrd-2.4.21-27.ELsmp.img
+
+  2. After running insmod, the driver is loaded and the incorrect network
+     interface is brought up without running ifup.
+
+      When using 2.4.x kernels, including RHEL kernels, the Linux kernel
+      invokes a script named "hotplug". This script is primarily used to
+      automatically bring up USB devices when they are plugged in, however,
+      the script also attempts to automatically bring up a network interface
+      after loading the kernel module. The hotplug script does this by scanning
+      the ifcfg-eth# config files in /etc/sysconfig/network-scripts, looking
+      for HWADDR=<mac_address>.
+
+      If the hotplug script does not find the HWADDRR within any of the
+      ifcfg-eth# files, it will bring up the device with the next available
+      interface name. If this interface is already configured for a different
+      network card, your new interface will have incorrect IP address and
+      network settings.
+
+      To solve this issue, you can add the HWADDR=<mac_address> key to the
+      interface config file of your network controller.
+
+      To disable this "hotplug" feature, you may add the driver (module name)
+      to the "blacklist" file located in /etc/hotplug. It has been noted that
+      this does not work for network devices because the net.agent script
+      does not use the blacklist file. Simply remove, or rename, the net.agent
+      script located in /etc/hotplug to disable this feature.
+
+  3. Transport Protocol (TP) hangs when running heavy multi-connection traffic
+     on an AMD Opteron system with HyperTransport PCI-X Tunnel chipset.
+
+      If your AMD Opteron system uses the AMD-8131 HyperTransport PCI-X Tunnel
+      chipset, you may experience the "133-Mhz Mode Split Completion Data
+      Corruption" bug identified by AMD while using a 133Mhz PCI-X card on the
+      bus PCI-X bus.
+
+      AMD states, "Under highly specific conditions, the AMD-8131 PCI-X Tunnel
+      can provide stale data via split completion cycles to a PCI-X card that
+      is operating at 133 Mhz", causing data corruption.
+
+      AMD's provides three workarounds for this problem, however, Chelsio
+      recommends the first option for best performance with this bug:
+
+       For 133Mhz secondary bus operation, limit the transaction length and
+       the number of outstanding transactions, via BIOS configuration
+       programming of the PCI-X card, to the following:
+
+          Data Length (bytes): 1k
+
+          Total allowed outstanding transactions: 2
+
+      Please refer to AMD 8131-HT/PCI-X Errata 26310 Rev 3.08 August 2004,
+      section 56, "133-MHz Mode Split Completion Data Corruption" for more
+      details with this bug and workarounds suggested by AMD.
+
+      It may be possible to work outside AMD's recommended PCI-X settings, try
+      increasing the Data Length to 2k bytes for increased performance. If you
+      have issues with these settings, please revert to the "safe" settings
+      and duplicate the problem before submitting a bug or asking for support.
+
+      .. note::
+
+           The default setting on most systems is 8 outstanding transactions
+           and 2k bytes data length.
+
+  4. On multiprocessor systems, it has been noted that an application which
+     is handling 10Gb networking can switch between CPUs causing degraded
+     and/or unstable performance.
+
+      If running on an SMP system and taking performance measurements, it
+      is suggested you either run the latest netperf-2.4.0+ or use a binding
+      tool such as Tim Hockin's procstate utilities (runon)
+      <http://www.hockin.org/~thockin/procstate/>.
+
+      Binding netserver and netperf (or other applications) to particular
+      CPUs will have a significant difference in performance measurements.
+      You may need to experiment which CPU to bind the application to in
+      order to achieve the best performance for your system.
+
+      If you are developing an application designed for 10Gb networking,
+      please keep in mind you may want to look at kernel functions
+      sched_setaffinity & sched_getaffinity to bind your application.
+
+      If you are just running user-space applications such as ftp, telnet,
+      etc., you may want to try the runon tool provided by Tim Hockin's
+      procstate utility. You could also try binding the interface to a
+      particular CPU: runon 0 ifup eth0
+
+
+Support
+=======
+
+ If you have problems with the software or hardware, please contact our
+ customer support team via email at support@chelsio.com or check our website
+ at http://www.chelsio.com
+
+-------------------------------------------------------------------------------
+
+::
+
+ Chelsio Communications
+ 370 San Aleso Ave.
+ Suite 100
+ Sunnyvale, CA 94085
+ http://www.chelsio.com
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License, version 2, as
+published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+THIS SOFTWARE IS PROVIDED ``AS IS`` AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+Copyright |copy| 2003-2005 Chelsio Communications. All rights reserved.
diff --git a/Documentation/networking/device_drivers/chelsio/cxgb.txt b/Documentation/networking/device_drivers/chelsio/cxgb.txt
deleted file mode 100644 (file)
index 20a8876..0000000
+++ /dev/null
@@ -1,352 +0,0 @@
-                 Chelsio N210 10Gb Ethernet Network Controller
-
-                         Driver Release Notes for Linux
-
-                                 Version 2.1.1
-
-                                 June 20, 2005
-
-CONTENTS
-========
- INTRODUCTION
- FEATURES
- PERFORMANCE
- DRIVER MESSAGES
- KNOWN ISSUES
- SUPPORT
-
-
-INTRODUCTION
-============
-
- This document describes the Linux driver for Chelsio 10Gb Ethernet Network
- Controller. This driver supports the Chelsio N210 NIC and is backward
- compatible with the Chelsio N110 model 10Gb NICs.
-
-
-FEATURES
-========
-
- Adaptive Interrupts (adaptive-rx)
- ---------------------------------
-
-  This feature provides an adaptive algorithm that adjusts the interrupt
-  coalescing parameters, allowing the driver to dynamically adapt the latency
-  settings to achieve the highest performance during various types of network
-  load.
-
-  The interface used to control this feature is ethtool. Please see the
-  ethtool manpage for additional usage information.
-
-  By default, adaptive-rx is disabled.
-  To enable adaptive-rx:
-
-      ethtool -C <interface> adaptive-rx on
-
-  To disable adaptive-rx, use ethtool:
-
-      ethtool -C <interface> adaptive-rx off
-
-  After disabling adaptive-rx, the timer latency value will be set to 50us.
-  You may set the timer latency after disabling adaptive-rx:
-
-      ethtool -C <interface> rx-usecs <microseconds>
-
-  An example to set the timer latency value to 100us on eth0:
-
-      ethtool -C eth0 rx-usecs 100
-
-  You may also provide a timer latency value while disabling adaptive-rx:
-
-      ethtool -C <interface> adaptive-rx off rx-usecs <microseconds>
-
-  If adaptive-rx is disabled and a timer latency value is specified, the timer
-  will be set to the specified value until changed by the user or until
-  adaptive-rx is enabled.
-
-  To view the status of the adaptive-rx and timer latency values:
-
-      ethtool -c <interface>
-
-
- TCP Segmentation Offloading (TSO) Support
- -----------------------------------------
-
-  This feature, also known as "large send", enables a system's protocol stack
-  to offload portions of outbound TCP processing to a network interface card
-  thereby reducing system CPU utilization and enhancing performance.
-
-  The interface used to control this feature is ethtool version 1.8 or higher.
-  Please see the ethtool manpage for additional usage information.
-
-  By default, TSO is enabled.
-  To disable TSO:
-
-      ethtool -K <interface> tso off
-
-  To enable TSO:
-
-      ethtool -K <interface> tso on
-
-  To view the status of TSO:
-
-      ethtool -k <interface>
-
-
-PERFORMANCE
-===========
-
- The following information is provided as an example of how to change system
- parameters for "performance tuning" an what value to use. You may or may not
- want to change these system parameters, depending on your server/workstation
- application. Doing so is not warranted in any way by Chelsio Communications,
- and is done at "YOUR OWN RISK". Chelsio will not be held responsible for loss
- of data or damage to equipment.
-
- Your distribution may have a different way of doing things, or you may prefer
- a different method. These commands are shown only to provide an example of
- what to do and are by no means definitive.
-
- Making any of the following system changes will only last until you reboot
- your system. You may want to write a script that runs at boot-up which
- includes the optimal settings for your system.
-
-  Setting PCI Latency Timer:
-      setpci -d 1425:* 0x0c.l=0x0000F800
-
-  Disabling TCP timestamp:
-      sysctl -w net.ipv4.tcp_timestamps=0
-
-  Disabling SACK:
-      sysctl -w net.ipv4.tcp_sack=0
-
-  Setting large number of incoming connection requests:
-      sysctl -w net.ipv4.tcp_max_syn_backlog=3000
-
-  Setting maximum receive socket buffer size:
-      sysctl -w net.core.rmem_max=1024000
-
-  Setting maximum send socket buffer size:
-      sysctl -w net.core.wmem_max=1024000
-
-  Set smp_affinity (on a multiprocessor system) to a single CPU:
-      echo 1 > /proc/irq/<interrupt_number>/smp_affinity
-
-  Setting default receive socket buffer size:
-      sysctl -w net.core.rmem_default=524287
-
-  Setting default send socket buffer size:
-      sysctl -w net.core.wmem_default=524287
-
-  Setting maximum option memory buffers:
-      sysctl -w net.core.optmem_max=524287
-
-  Setting maximum backlog (# of unprocessed packets before kernel drops):
-      sysctl -w net.core.netdev_max_backlog=300000
-
-  Setting TCP read buffers (min/default/max):
-      sysctl -w net.ipv4.tcp_rmem="10000000 10000000 10000000"
-
-  Setting TCP write buffers (min/pressure/max):
-      sysctl -w net.ipv4.tcp_wmem="10000000 10000000 10000000"
-
-  Setting TCP buffer space (min/pressure/max):
-      sysctl -w net.ipv4.tcp_mem="10000000 10000000 10000000"
-
-  TCP window size for single connections:
-   The receive buffer (RX_WINDOW) size must be at least as large as the
-   Bandwidth-Delay Product of the communication link between the sender and
-   receiver. Due to the variations of RTT, you may want to increase the buffer
-   size up to 2 times the Bandwidth-Delay Product. Reference page 289 of
-   "TCP/IP Illustrated, Volume 1, The Protocols" by W. Richard Stevens.
-   At 10Gb speeds, use the following formula:
-       RX_WINDOW >= 1.25MBytes * RTT(in milliseconds)
-       Example for RTT with 100us: RX_WINDOW = (1,250,000 * 0.1) = 125,000
-   RX_WINDOW sizes of 256KB - 512KB should be sufficient.
-   Setting the min, max, and default receive buffer (RX_WINDOW) size:
-       sysctl -w net.ipv4.tcp_rmem="<min> <default> <max>"
-
-  TCP window size for multiple connections:
-   The receive buffer (RX_WINDOW) size may be calculated the same as single
-   connections, but should be divided by the number of connections. The
-   smaller window prevents congestion and facilitates better pacing,
-   especially if/when MAC level flow control does not work well or when it is
-   not supported on the machine. Experimentation may be necessary to attain
-   the correct value. This method is provided as a starting point for the
-   correct receive buffer size.
-   Setting the min, max, and default receive buffer (RX_WINDOW) size is
-   performed in the same manner as single connection.
-
-
-DRIVER MESSAGES
-===============
-
- The following messages are the most common messages logged by syslog. These
- may be found in /var/log/messages.
-
-  Driver up:
-     Chelsio Network Driver - version 2.1.1
-
-  NIC detected:
-     eth#: Chelsio N210 1x10GBaseX NIC (rev #), PCIX 133MHz/64-bit
-
-  Link up:
-     eth#: link is up at 10 Gbps, full duplex
-
-  Link down:
-     eth#: link is down
-
-
-KNOWN ISSUES
-============
-
- These issues have been identified during testing. The following information
- is provided as a workaround to the problem. In some cases, this problem is
- inherent to Linux or to a particular Linux Distribution and/or hardware
- platform.
-
-  1. Large number of TCP retransmits on a multiprocessor (SMP) system.
-
-      On a system with multiple CPUs, the interrupt (IRQ) for the network
-      controller may be bound to more than one CPU. This will cause TCP
-      retransmits if the packet data were to be split across different CPUs
-      and re-assembled in a different order than expected.
-
-      To eliminate the TCP retransmits, set smp_affinity on the particular
-      interrupt to a single CPU. You can locate the interrupt (IRQ) used on
-      the N110/N210 by using ifconfig:
-          ifconfig <dev_name> | grep Interrupt
-      Set the smp_affinity to a single CPU:
-          echo 1 > /proc/irq/<interrupt_number>/smp_affinity
-
-      It is highly suggested that you do not run the irqbalance daemon on your
-      system, as this will change any smp_affinity setting you have applied.
-      The irqbalance daemon runs on a 10 second interval and binds interrupts
-      to the least loaded CPU determined by the daemon. To disable this daemon:
-          chkconfig --level 2345 irqbalance off
-
-      By default, some Linux distributions enable the kernel feature,
-      irqbalance, which performs the same function as the daemon. To disable
-      this feature, add the following line to your bootloader:
-          noirqbalance
-
-          Example using the Grub bootloader:
-              title Red Hat Enterprise Linux AS (2.4.21-27.ELsmp)
-              root (hd0,0)
-              kernel /vmlinuz-2.4.21-27.ELsmp ro root=/dev/hda3 noirqbalance
-              initrd /initrd-2.4.21-27.ELsmp.img
-
-  2. After running insmod, the driver is loaded and the incorrect network
-     interface is brought up without running ifup.
-
-      When using 2.4.x kernels, including RHEL kernels, the Linux kernel
-      invokes a script named "hotplug". This script is primarily used to
-      automatically bring up USB devices when they are plugged in, however,
-      the script also attempts to automatically bring up a network interface
-      after loading the kernel module. The hotplug script does this by scanning
-      the ifcfg-eth# config files in /etc/sysconfig/network-scripts, looking
-      for HWADDR=<mac_address>.
-
-      If the hotplug script does not find the HWADDRR within any of the
-      ifcfg-eth# files, it will bring up the device with the next available
-      interface name. If this interface is already configured for a different
-      network card, your new interface will have incorrect IP address and
-      network settings.
-
-      To solve this issue, you can add the HWADDR=<mac_address> key to the
-      interface config file of your network controller.
-
-      To disable this "hotplug" feature, you may add the driver (module name)
-      to the "blacklist" file located in /etc/hotplug. It has been noted that
-      this does not work for network devices because the net.agent script
-      does not use the blacklist file. Simply remove, or rename, the net.agent
-      script located in /etc/hotplug to disable this feature.
-
-  3. Transport Protocol (TP) hangs when running heavy multi-connection traffic
-     on an AMD Opteron system with HyperTransport PCI-X Tunnel chipset.
-
-      If your AMD Opteron system uses the AMD-8131 HyperTransport PCI-X Tunnel
-      chipset, you may experience the "133-Mhz Mode Split Completion Data
-      Corruption" bug identified by AMD while using a 133Mhz PCI-X card on the
-      bus PCI-X bus.
-
-      AMD states, "Under highly specific conditions, the AMD-8131 PCI-X Tunnel
-      can provide stale data via split completion cycles to a PCI-X card that
-      is operating at 133 Mhz", causing data corruption.
-
-      AMD's provides three workarounds for this problem, however, Chelsio
-      recommends the first option for best performance with this bug:
-
-        For 133Mhz secondary bus operation, limit the transaction length and
-        the number of outstanding transactions, via BIOS configuration
-        programming of the PCI-X card, to the following:
-
-           Data Length (bytes): 1k
-           Total allowed outstanding transactions: 2
-
-      Please refer to AMD 8131-HT/PCI-X Errata 26310 Rev 3.08 August 2004,
-      section 56, "133-MHz Mode Split Completion Data Corruption" for more
-      details with this bug and workarounds suggested by AMD.
-
-      It may be possible to work outside AMD's recommended PCI-X settings, try
-      increasing the Data Length to 2k bytes for increased performance. If you
-      have issues with these settings, please revert to the "safe" settings
-      and duplicate the problem before submitting a bug or asking for support.
-
-      NOTE: The default setting on most systems is 8 outstanding transactions
-            and 2k bytes data length.
-
-  4. On multiprocessor systems, it has been noted that an application which
-     is handling 10Gb networking can switch between CPUs causing degraded
-     and/or unstable performance.
-
-      If running on an SMP system and taking performance measurements, it
-      is suggested you either run the latest netperf-2.4.0+ or use a binding
-      tool such as Tim Hockin's procstate utilities (runon)
-      <http://www.hockin.org/~thockin/procstate/>.
-
-      Binding netserver and netperf (or other applications) to particular
-      CPUs will have a significant difference in performance measurements.
-      You may need to experiment which CPU to bind the application to in
-      order to achieve the best performance for your system.
-
-      If you are developing an application designed for 10Gb networking,
-      please keep in mind you may want to look at kernel functions
-      sched_setaffinity & sched_getaffinity to bind your application.
-
-      If you are just running user-space applications such as ftp, telnet,
-      etc., you may want to try the runon tool provided by Tim Hockin's
-      procstate utility. You could also try binding the interface to a
-      particular CPU: runon 0 ifup eth0
-
-
-SUPPORT
-=======
-
- If you have problems with the software or hardware, please contact our
- customer support team via email at support@chelsio.com or check our website
- at http://www.chelsio.com
-
-===============================================================================
-
- Chelsio Communications
- 370 San Aleso Ave.
- Suite 100
- Sunnyvale, CA 94085
- http://www.chelsio.com
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License, version 2, as
-published by the Free Software Foundation.
-
-You should have received a copy of the GNU General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-
-THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
-WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
- Copyright (c) 2003-2005 Chelsio Communications. All rights reserved.
-
-===============================================================================
diff --git a/Documentation/networking/device_drivers/cirrus/cs89x0.rst b/Documentation/networking/device_drivers/cirrus/cs89x0.rst
new file mode 100644 (file)
index 0000000..e5c2839
--- /dev/null
@@ -0,0 +1,647 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+================================================
+Cirrus Logic LAN CS8900/CS8920 Ethernet Adapters
+================================================
+
+.. note::
+
+   This document was contributed by Cirrus Logic for kernel 2.2.5.  This version
+   has been updated for 2.3.48 by Andrew Morton.
+
+   Still, this is too outdated! A major cleanup is needed here.
+
+Cirrus make a copy of this driver available at their website, as
+described below.  In general, you should use the driver version which
+comes with your Linux distribution.
+
+
+Linux Network Interface Driver ver. 2.00 <kernel 2.3.48>
+
+
+.. TABLE OF CONTENTS
+
+   1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
+       1.1 Product Overview
+       1.2 Driver Description
+           1.2.1 Driver Name
+           1.2.2 File in the Driver Package
+       1.3 System Requirements
+       1.4 Licensing Information
+
+   2.0 ADAPTER INSTALLATION and CONFIGURATION
+       2.1 CS8900-based Adapter Configuration
+       2.2 CS8920-based Adapter Configuration
+
+   3.0 LOADING THE DRIVER AS A MODULE
+
+   4.0 COMPILING THE DRIVER
+       4.1 Compiling the Driver as a Loadable Module
+       4.2 Compiling the driver to support memory mode
+       4.3 Compiling the driver to support Rx DMA
+
+   5.0 TESTING AND TROUBLESHOOTING
+       5.1 Known Defects and Limitations
+       5.2 Testing the Adapter
+           5.2.1 Diagnostic Self-Test
+           5.2.2 Diagnostic Network Test
+       5.3 Using the Adapter's LEDs
+       5.4 Resolving I/O Conflicts
+
+   6.0 TECHNICAL SUPPORT
+       6.1 Contacting Cirrus Logic's Technical Support
+       6.2 Information Required Before Contacting Technical Support
+       6.3 Obtaining the Latest Driver Version
+       6.4 Current maintainer
+       6.5 Kernel boot parameters
+
+
+1. Cirrus Logic LAN CS8900/CS8920 Ethernet Adapters
+===================================================
+
+
+1.1. Product Overview
+=====================
+
+The CS8900-based ISA Ethernet Adapters from Cirrus Logic follow
+IEEE 802.3 standards and support half or full-duplex operation in ISA bus
+computers on 10 Mbps Ethernet networks.  The adapters are designed for operation
+in 16-bit ISA or EISA bus expansion slots and are available in
+10BaseT-only or 3-media configurations (10BaseT, 10Base2, and AUI for 10Base-5
+or fiber networks).
+
+CS8920-based adapters are similar to the CS8900-based adapter with additional
+features for Plug and Play (PnP) support and Wakeup Frame recognition.  As
+such, the configuration procedures differ somewhat between the two types of
+adapters.  Refer to the "Adapter Configuration" section for details on
+configuring both types of adapters.
+
+
+1.2. Driver Description
+=======================
+
+The CS8900/CS8920 Ethernet Adapter driver for Linux supports the Linux
+v2.3.48 or greater kernel.  It can be compiled directly into the kernel
+or loaded at run-time as a device driver module.
+
+1.2.1 Driver Name: cs89x0
+
+1.2.2 Files in the Driver Archive:
+
+The files in the driver at Cirrus' website include:
+
+  ===================  ====================================================
+  readme.txt           this file
+  build                batch file to compile cs89x0.c.
+  cs89x0.c             driver C code
+  cs89x0.h             driver header file
+  cs89x0.o             pre-compiled module (for v2.2.5 kernel)
+  config/Config.in     sample file to include cs89x0 driver in the kernel.
+  config/Makefile      sample file to include cs89x0 driver in the kernel.
+  config/Space.c       sample file to include cs89x0 driver in the kernel.
+  ===================  ====================================================
+
+
+
+1.3. System Requirements
+------------------------
+
+The following hardware is required:
+
+   * Cirrus Logic LAN (CS8900/20-based) Ethernet ISA Adapter
+
+   * IBM or IBM-compatible PC with:
+     * An 80386 or higher processor
+     * 16 bytes of contiguous IO space available between 210h - 370h
+     * One available IRQ (5,10,11,or 12 for the CS8900, 3-7,9-15 for CS8920).
+
+   * Appropriate cable (and connector for AUI, 10BASE-2) for your network
+     topology.
+
+The following software is required:
+
+* LINUX kernel version 2.3.48 or higher
+
+   * CS8900/20 Setup Utility (DOS-based)
+
+   * LINUX kernel sources for your kernel (if compiling into kernel)
+
+   * GNU Toolkit (gcc and make) v2.6 or above (if compiling into kernel
+     or a module)
+
+
+
+1.4. Licensing Information
+--------------------------
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation, version 1.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+more details.
+
+For a full copy of the GNU General Public License, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+
+2. Adapter Installation and Configuration
+=========================================
+
+Both the CS8900 and CS8920-based adapters can be configured using parameters
+stored in an on-board EEPROM. You must use the DOS-based CS8900/20 Setup
+Utility if you want to change the adapter's configuration in EEPROM.
+
+When loading the driver as a module, you can specify many of the adapter's
+configuration parameters on the command-line to override the EEPROM's settings
+or for interface configuration when an EEPROM is not used. (CS8920-based
+adapters must use an EEPROM.) See Section 3.0 LOADING THE DRIVER AS A MODULE.
+
+Since the CS8900/20 Setup Utility is a DOS-based application, you must install
+and configure the adapter in a DOS-based system using the CS8900/20 Setup
+Utility before installation in the target LINUX system.  (Not required if
+installing a CS8900-based adapter and the default configuration is acceptable.)
+
+
+2.1. CS8900-based Adapter Configuration
+---------------------------------------
+
+CS8900-based adapters shipped from Cirrus Logic have been configured
+with the following "default" settings::
+
+  Operation Mode:      Memory Mode
+  IRQ:                 10
+  Base I/O Address:    300
+  Memory Base Address: D0000
+  Optimization:               DOS Client
+  Transmission Mode:   Half-duplex
+  BootProm:            None
+  Media Type:         Autodetect (3-media cards) or
+                      10BASE-T (10BASE-T only adapter)
+
+You should only change the default configuration settings if conflicts with
+another adapter exists. To change the adapter's configuration, run the
+CS8900/20 Setup Utility.
+
+
+2.2. CS8920-based Adapter Configuration
+---------------------------------------
+
+CS8920-based adapters are shipped from Cirrus Logic configured as Plug
+and Play (PnP) enabled.  However, since the cs89x0 driver does NOT
+support PnP, you must install the CS8920 adapter in a DOS-based PC and
+run the CS8900/20 Setup Utility to disable PnP and configure the
+adapter before installation in the target Linux system.  Failure to do
+this will leave the adapter inactive and the driver will be unable to
+communicate with the adapter.
+
+::
+
+       ****************************************************************
+       *                    CS8920-BASED ADAPTERS:                    *
+       *                                                              *
+       * CS8920-BASED ADAPTERS ARE PLUG and PLAY ENABLED BY DEFAULT.  *
+       * THE CS89X0 DRIVER DOES NOT SUPPORT PnP. THEREFORE, YOU MUST  *
+       * RUN THE CS8900/20 SETUP UTILITY TO DISABLE PnP SUPPORT AND   *
+       * TO ACTIVATE THE ADAPTER.                                     *
+       ****************************************************************
+
+
+
+
+3. Loading the Driver as a Module
+=================================
+
+If the driver is compiled as a loadable module, you can load the driver module
+with the 'modprobe' command.  Many of the adapter's configuration parameters can
+be specified as command-line arguments to the load command.  This facility
+provides a means to override the EEPROM's settings or for interface
+configuration when an EEPROM is not used.
+
+Example::
+
+    insmod cs89x0.o io=0x200 irq=0xA media=aui
+
+This example loads the module and configures the adapter to use an IO port base
+address of 200h, interrupt 10, and use the AUI media connection.  The following
+configuration options are available on the command line::
+
+  io=###               - specify IO address (200h-360h)
+  irq=##               - specify interrupt level
+  use_dma=1            - Enable DMA
+  dma=#                - specify dma channel (Driver is compiled to support
+                        Rx DMA only)
+  dmasize=# (16 or 64) - DMA size 16K or 64K.  Default value is set to 16.
+  media=rj45           - specify media type
+   or media=bnc
+   or media=aui
+   or media=auto
+  duplex=full          - specify forced half/full/autonegotiate duplex
+   or duplex=half
+   or duplex=auto
+  debug=#              - debug level (only available if the driver was compiled
+                        for debugging)
+
+**Notes:**
+
+a) If an EEPROM is present, any specified command-line parameter
+   will override the corresponding configuration value stored in
+   EEPROM.
+
+b) The "io" parameter must be specified on the command-line.
+
+c) The driver's hardware probe routine is designed to avoid
+   writing to I/O space until it knows that there is a cs89x0
+   card at the written addresses.  This could cause problems
+   with device probing.  To avoid this behaviour, add one
+   to the ``io=`` module parameter.  This doesn't actually change
+   the I/O address, but it is a flag to tell the driver
+   to partially initialise the hardware before trying to
+   identify the card.  This could be dangerous if you are
+   not sure that there is a cs89x0 card at the provided address.
+
+   For example, to scan for an adapter located at IO base 0x300,
+   specify an IO address of 0x301.
+
+d) The "duplex=auto" parameter is only supported for the CS8920.
+
+e) The minimum command-line configuration required if an EEPROM is
+   not present is:
+
+   io
+   irq
+   media type (no autodetect)
+
+f) The following additional parameters are CS89XX defaults (values
+   used with no EEPROM or command-line argument).
+
+   * DMA Burst = enabled
+   * IOCHRDY Enabled = enabled
+   * UseSA = enabled
+   * CS8900 defaults to half-duplex if not specified on command-line
+   * CS8920 defaults to autoneg if not specified on command-line
+   * Use reset defaults for other config parameters
+   * dma_mode = 0
+
+g) You can use ifconfig to set the adapter's Ethernet address.
+
+h) Many Linux distributions use the 'modprobe' command to load
+   modules.  This program uses the '/etc/conf.modules' file to
+   determine configuration information which is passed to a driver
+   module when it is loaded.  All the configuration options which are
+   described above may be placed within /etc/conf.modules.
+
+   For example::
+
+     > cat /etc/conf.modules
+     ...
+     alias eth0 cs89x0
+     options cs89x0 io=0x0200 dma=5 use_dma=1
+     ...
+
+   In this example we are telling the module system that the
+   ethernet driver for this machine should use the cs89x0 driver.  We
+   are asking 'modprobe' to pass the 'io', 'dma' and 'use_dma'
+   arguments to the driver when it is loaded.
+
+i) Cirrus recommend that the cs89x0 use the ISA DMA channels 5, 6 or
+   7.  You will probably find that other DMA channels will not work.
+
+j) The cs89x0 supports DMA for receiving only.  DMA mode is
+   significantly more efficient.  Flooding a 400 MHz Celeron machine
+   with large ping packets consumes 82% of its CPU capacity in non-DMA
+   mode.  With DMA this is reduced to 45%.
+
+k) If your Linux kernel was compiled with inbuilt plug-and-play
+   support you will be able to find information about the cs89x0 card
+   with the command::
+
+     cat /proc/isapnp
+
+l) If during DMA operation you find erratic behavior or network data
+   corruption you should use your PC's BIOS to slow the EISA bus clock.
+
+m) If the cs89x0 driver is compiled directly into the kernel
+   (non-modular) then its I/O address is automatically determined by
+   ISA bus probing.  The IRQ number, media options, etc are determined
+   from the card's EEPROM.
+
+n) If the cs89x0 driver is compiled directly into the kernel, DMA
+   mode may be selected by providing the kernel with a boot option
+   'cs89x0_dma=N' where 'N' is the desired DMA channel number (5, 6 or 7).
+
+   Kernel boot options may be provided on the LILO command line::
+
+       LILO boot: linux cs89x0_dma=5
+
+   or they may be placed in /etc/lilo.conf::
+
+       image=/boot/bzImage-2.3.48
+         append="cs89x0_dma=5"
+         label=linux
+         root=/dev/hda5
+         read-only
+
+   The DMA Rx buffer size is hardwired to 16 kbytes in this mode.
+   (64k mode is not available).
+
+
+4. Compiling the Driver
+=======================
+
+The cs89x0 driver can be compiled directly into the kernel or compiled into
+a loadable device driver module.
+
+Just use the standard way to configure the driver and compile the Kernel.
+
+
+4.1. Compiling the Driver to Support Rx DMA
+-------------------------------------------
+
+The compile-time optionality for DMA was removed in the 2.3 kernel
+series.  DMA support is now unconditionally part of the driver.  It is
+enabled by the 'use_dma=1' module option.
+
+
+5. Testing and Troubleshooting
+==============================
+
+5.1. Known Defects and Limitations
+----------------------------------
+
+Refer to the RELEASE.TXT file distributed as part of this archive for a list of
+known defects, driver limitations, and work arounds.
+
+
+5.2. Testing the Adapter
+------------------------
+
+Once the adapter has been installed and configured, the diagnostic option of
+the CS8900/20 Setup Utility can be used to test the functionality of the
+adapter and its network connection.  Use the diagnostics 'Self Test' option to
+test the functionality of the adapter with the hardware configuration you have
+assigned. You can use the diagnostics 'Network Test' to test the ability of the
+adapter to communicate across the Ethernet with another PC equipped with a
+CS8900/20-based adapter card (it must also be running the CS8900/20 Setup
+Utility).
+
+.. note::
+
+        The Setup Utility's diagnostics are designed to run in a
+        DOS-only operating system environment.  DO NOT run the diagnostics
+        from a DOS or command prompt session under Windows 95, Windows NT,
+        OS/2, or other operating system.
+
+To run the diagnostics tests on the CS8900/20 adapter:
+
+   1.  Boot DOS on the PC and start the CS8900/20 Setup Utility.
+
+   2.  The adapter's current configuration is displayed.  Hit the ENTER key to
+       get to the main menu.
+
+   4.  Select 'Diagnostics' (ALT-G) from the main menu.
+       * Select 'Self-Test' to test the adapter's basic functionality.
+       * Select 'Network Test' to test the network connection and cabling.
+
+
+5.2.1. Diagnostic Self-test
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The diagnostic self-test checks the adapter's basic functionality as well as
+its ability to communicate across the ISA bus based on the system resources
+assigned during hardware configuration.  The following tests are performed:
+
+   * IO Register Read/Write Test
+
+     The IO Register Read/Write test insures that the CS8900/20 can be
+     accessed in IO mode, and that the IO base address is correct.
+
+   * Shared Memory Test
+
+     The Shared Memory test insures the CS8900/20 can be accessed in memory
+     mode and that the range of memory addresses assigned does not conflict
+     with other devices in the system.
+
+   * Interrupt Test
+
+     The Interrupt test insures there are no conflicts with the assigned IRQ
+     signal.
+
+   * EEPROM Test
+
+     The EEPROM test insures the EEPROM can be read.
+
+   * Chip RAM Test
+
+     The Chip RAM test insures the 4K of memory internal to the CS8900/20 is
+     working properly.
+
+   * Internal Loop-back Test
+
+     The Internal Loop Back test insures the adapter's transmitter and
+     receiver are operating properly.  If this test fails, make sure the
+     adapter's cable is connected to the network (check for LED activity for
+     example).
+
+   * Boot PROM Test
+
+     The Boot PROM  test insures the Boot PROM is present, and can be read.
+     Failure indicates the Boot PROM  was not successfully read due to a
+     hardware problem or due to a conflicts on the Boot PROM address
+     assignment. (Test only applies if the adapter is configured to use the
+     Boot PROM option.)
+
+Failure of a test item indicates a possible system resource conflict with
+another device on the ISA bus.  In this case, you should use the Manual Setup
+option to reconfigure the adapter by selecting a different value for the system
+resource that failed.
+
+
+5.2.2. Diagnostic Network Test
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The Diagnostic Network Test verifies a working network connection by
+transferring data between two CS8900/20 adapters installed in different PCs
+on the same network. (Note: the diagnostic network test should not be run
+between two nodes across a router.)
+
+This test requires that each of the two PCs have a CS8900/20-based adapter
+installed and have the CS8900/20 Setup Utility running.  The first PC is
+configured as a Responder and the other PC is configured as an Initiator.
+Once the Initiator is started, it sends data frames to the Responder which
+returns the frames to the Initiator.
+
+The total number of frames received and transmitted are displayed on the
+Initiator's display, along with a count of the number of frames received and
+transmitted OK or in error.  The test can be terminated anytime by the user at
+either PC.
+
+To setup the Diagnostic Network Test:
+
+    1.  Select a PC with a CS8900/20-based adapter and a known working network
+       connection to act as the Responder.  Run the CS8900/20 Setup Utility
+       and select 'Diagnostics -> Network Test -> Responder' from the main
+       menu.  Hit ENTER to start the Responder.
+
+    2.  Return to the PC with the CS8900/20-based adapter you want to test and
+       start the CS8900/20 Setup Utility.
+
+    3.  From the main menu, Select 'Diagnostic -> Network Test -> Initiator'.
+       Hit ENTER to start the test.
+
+You may stop the test on the Initiator at any time while allowing the Responder
+to continue running.  In this manner, you can move to additional PCs and test
+them by starting the Initiator on another PC without having to stop/start the
+Responder.
+
+
+
+5.3. Using the Adapter's LEDs
+-----------------------------
+
+The 2 and 3-media adapters have two LEDs visible on the back end of the board
+located near the 10Base-T connector.
+
+Link Integrity LED: A "steady" ON of the green LED indicates a valid 10Base-T
+connection.  (Only applies to 10Base-T.  The green LED has no significance for
+a 10Base-2 or AUI connection.)
+
+TX/RX LED: The yellow LED lights briefly each time the adapter transmits or
+receives data. (The yellow LED will appear to "flicker" on a typical network.)
+
+
+5.4. Resolving I/O Conflicts
+----------------------------
+
+An IO conflict occurs when two or more adapter use the same ISA resource (IO
+address, memory address or IRQ).  You can usually detect an IO conflict in one
+of four ways after installing and or configuring the CS8900/20-based adapter:
+
+    1.  The system does not boot properly (or at all).
+
+    2.  The driver cannot communicate with the adapter, reporting an "Adapter
+       not found" error message.
+
+    3.  You cannot connect to the network or the driver will not load.
+
+    4.  If you have configured the adapter to run in memory mode but the driver
+       reports it is using IO mode when loading, this is an indication of a
+       memory address conflict.
+
+If an IO conflict occurs, run the CS8900/20 Setup Utility and perform a
+diagnostic self-test.  Normally, the ISA resource in conflict will fail the
+self-test.  If so, reconfigure the adapter selecting another choice for the
+resource in conflict.  Run the diagnostics again to check for further IO
+conflicts.
+
+In some cases, such as when the PC will not boot, it may be necessary to remove
+the adapter and reconfigure it by installing it in another PC to run the
+CS8900/20 Setup Utility.  Once reinstalled in the target system, run the
+diagnostics self-test to ensure the new configuration is free of conflicts
+before loading the driver again.
+
+When manually configuring the adapter, keep in mind the typical ISA system
+resource usage as indicated in the tables below.
+
+::
+
+  I/O Address          Device                        IRQ      Device
+  -----------          --------                      ---      --------
+     200-20F           Game I/O adapter               3       COM2, Bus Mouse
+     230-23F           Bus Mouse                      4       COM1
+     270-27F           LPT3: third parallel port      5       LPT2
+     2F0-2FF           COM2: second serial port       6       Floppy Disk controller
+     320-32F           Fixed disk controller          7       LPT1
+                                                        8       Real-time Clock
+                                                    9       EGA/VGA display adapter
+                                                   12       Mouse (PS/2)
+  Memory Address  Device                          13       Math Coprocessor
+  --------------  ---------------------           14       Hard Disk controller
+  A000-BFFF    EGA Graphics Adapter
+  A000-C7FF    VGA Graphics Adapter
+  B000-BFFF    Mono Graphics Adapter
+  B800-BFFF    Color Graphics Adapter
+  E000-FFFF    AT BIOS
+
+
+
+
+6. Technical Support
+====================
+
+6.1. Contacting Cirrus Logic's Technical Support
+------------------------------------------------
+
+Cirrus Logic's CS89XX Technical Application Support can be reached at::
+
+  Telephone  :(800) 888-5016 (from inside U.S. and Canada)
+            :(512) 442-7555 (from outside the U.S. and Canada)
+  Fax        :(512) 912-3871
+  Email      :ethernet@crystal.cirrus.com
+  WWW        :http://www.cirrus.com
+
+
+6.2. Information Required before Contacting Technical Support
+-------------------------------------------------------------
+
+Before contacting Cirrus Logic for technical support, be prepared to provide as
+Much of the following information as possible.
+
+1.) Adapter type (CRD8900, CDB8900, CDB8920, etc.)
+
+2.) Adapter configuration
+
+    * IO Base, Memory Base, IO or memory mode enabled, IRQ, DMA channel
+    * Plug and Play enabled/disabled (CS8920-based adapters only)
+    * Configured for media auto-detect or specific media type (which type).
+
+3.) PC System's Configuration
+
+    * Plug and Play system (yes/no)
+    * BIOS (make and version)
+    * System make and model
+    * CPU (type and speed)
+    * System RAM
+    * SCSI Adapter
+
+4.) Software
+
+    * CS89XX driver and version
+    * Your network operating system and version
+    * Your system's OS version
+    * Version of all protocol support files
+
+5.) Any Error Message displayed.
+
+
+
+6.3 Obtaining the Latest Driver Version
+---------------------------------------
+
+You can obtain the latest CS89XX drivers and support software from Cirrus Logic's
+Web site.  You can also contact Cirrus Logic's Technical Support (email:
+ethernet@crystal.cirrus.com) and request that you be registered for automatic
+software-update notification.
+
+Cirrus Logic maintains a web page at http://www.cirrus.com with the
+latest drivers and technical publications.
+
+
+6.4. Current maintainer
+-----------------------
+
+In February 2000 the maintenance of this driver was assumed by Andrew
+Morton.
+
+6.5 Kernel module parameters
+----------------------------
+
+For use in embedded environments with no cs89x0 EEPROM, the kernel boot
+parameter ``cs89x0_media=`` has been implemented.  Usage is::
+
+       cs89x0_media=rj45    or
+       cs89x0_media=aui     or
+       cs89x0_media=bnc
diff --git a/Documentation/networking/device_drivers/cirrus/cs89x0.txt b/Documentation/networking/device_drivers/cirrus/cs89x0.txt
deleted file mode 100644 (file)
index 0e19018..0000000
+++ /dev/null
@@ -1,624 +0,0 @@
-
-NOTE
-----
-
-This document was contributed by Cirrus Logic for kernel 2.2.5.  This version
-has been updated for 2.3.48 by Andrew Morton.
-
-Cirrus make a copy of this driver available at their website, as
-described below.  In general, you should use the driver version which
-comes with your Linux distribution.
-
-
-
-CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
-Linux Network Interface Driver ver. 2.00 <kernel 2.3.48>
-===============================================================================
-
-TABLE OF CONTENTS
-
-1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
-    1.1 Product Overview 
-    1.2 Driver Description
-       1.2.1 Driver Name
-       1.2.2 File in the Driver Package
-    1.3 System Requirements
-    1.4 Licensing Information
-
-2.0 ADAPTER INSTALLATION and CONFIGURATION
-    2.1 CS8900-based Adapter Configuration
-    2.2 CS8920-based Adapter Configuration 
-
-3.0 LOADING THE DRIVER AS A MODULE
-
-4.0 COMPILING THE DRIVER
-    4.1 Compiling the Driver as a Loadable Module
-    4.2 Compiling the driver to support memory mode
-    4.3 Compiling the driver to support Rx DMA 
-
-5.0 TESTING AND TROUBLESHOOTING
-    5.1 Known Defects and Limitations
-    5.2 Testing the Adapter
-        5.2.1 Diagnostic Self-Test
-        5.2.2 Diagnostic Network Test
-    5.3 Using the Adapter's LEDs
-    5.4 Resolving I/O Conflicts
-
-6.0 TECHNICAL SUPPORT
-    6.1 Contacting Cirrus Logic's Technical Support
-    6.2 Information Required Before Contacting Technical Support
-    6.3 Obtaining the Latest Driver Version
-    6.4 Current maintainer
-    6.5 Kernel boot parameters
-
-
-1.0 CIRRUS LOGIC LAN CS8900/CS8920 ETHERNET ADAPTERS
-===============================================================================
-
-
-1.1 PRODUCT OVERVIEW
-
-The CS8900-based ISA Ethernet Adapters from Cirrus Logic follow 
-IEEE 802.3 standards and support half or full-duplex operation in ISA bus 
-computers on 10 Mbps Ethernet networks.  The adapters are designed for operation 
-in 16-bit ISA or EISA bus expansion slots and are available in 
-10BaseT-only or 3-media configurations (10BaseT, 10Base2, and AUI for 10Base-5 
-or fiber networks).  
-
-CS8920-based adapters are similar to the CS8900-based adapter with additional 
-features for Plug and Play (PnP) support and Wakeup Frame recognition.  As 
-such, the configuration procedures differ somewhat between the two types of 
-adapters.  Refer to the "Adapter Configuration" section for details on 
-configuring both types of adapters.
-
-
-1.2 DRIVER DESCRIPTION
-
-The CS8900/CS8920 Ethernet Adapter driver for Linux supports the Linux
-v2.3.48 or greater kernel.  It can be compiled directly into the kernel
-or loaded at run-time as a device driver module.
-
-1.2.1 Driver Name: cs89x0
-
-1.2.2 Files in the Driver Archive:
-
-The files in the driver at Cirrus' website include:
-
-  readme.txt         - this file
-  build              - batch file to compile cs89x0.c.
-  cs89x0.c           - driver C code
-  cs89x0.h           - driver header file
-  cs89x0.o           - pre-compiled module (for v2.2.5 kernel)
-  config/Config.in   - sample file to include cs89x0 driver in the kernel.
-  config/Makefile    - sample file to include cs89x0 driver in the kernel.
-  config/Space.c     - sample file to include cs89x0 driver in the kernel.
-
-
-
-1.3 SYSTEM REQUIREMENTS
-
-The following hardware is required:
-
-   * Cirrus Logic LAN (CS8900/20-based) Ethernet ISA Adapter   
-
-   * IBM or IBM-compatible PC with:
-     * An 80386 or higher processor
-     * 16 bytes of contiguous IO space available between 210h - 370h
-     * One available IRQ (5,10,11,or 12 for the CS8900, 3-7,9-15 for CS8920).
-
-   * Appropriate cable (and connector for AUI, 10BASE-2) for your network
-     topology.
-
-The following software is required:
-
-* LINUX kernel version 2.3.48 or higher
-
-   * CS8900/20 Setup Utility (DOS-based)
-
-   * LINUX kernel sources for your kernel (if compiling into kernel)
-
-   * GNU Toolkit (gcc and make) v2.6 or above (if compiling into kernel 
-     or a module)   
-
-
-
-1.4 LICENSING INFORMATION
-
-This program is free software; you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free Software
-Foundation, version 1.
-
-This program is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
-FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
-more details.
-
-For a full copy of the GNU General Public License, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-
-
-2.0 ADAPTER INSTALLATION and CONFIGURATION
-===============================================================================
-
-Both the CS8900 and CS8920-based adapters can be configured using parameters 
-stored in an on-board EEPROM. You must use the DOS-based CS8900/20 Setup 
-Utility if you want to change the adapter's configuration in EEPROM.  
-
-When loading the driver as a module, you can specify many of the adapter's 
-configuration parameters on the command-line to override the EEPROM's settings 
-or for interface configuration when an EEPROM is not used. (CS8920-based 
-adapters must use an EEPROM.) See Section 3.0 LOADING THE DRIVER AS A MODULE.
-
-Since the CS8900/20 Setup Utility is a DOS-based application, you must install 
-and configure the adapter in a DOS-based system using the CS8900/20 Setup 
-Utility before installation in the target LINUX system.  (Not required if 
-installing a CS8900-based adapter and the default configuration is acceptable.)
-     
-
-2.1 CS8900-BASED ADAPTER CONFIGURATION
-
-CS8900-based adapters shipped from Cirrus Logic have been configured 
-with the following "default" settings:
-
-  Operation Mode:      Memory Mode
-  IRQ:                 10
-  Base I/O Address:    300
-  Memory Base Address: D0000
-  Optimization:               DOS Client
-  Transmission Mode:   Half-duplex
-  BootProm:            None
-  Media Type:         Autodetect (3-media cards) or 
-                       10BASE-T (10BASE-T only adapter)
-
-You should only change the default configuration settings if conflicts with 
-another adapter exists. To change the adapter's configuration, run the 
-CS8900/20 Setup Utility. 
-
-
-2.2 CS8920-BASED ADAPTER CONFIGURATION
-
-CS8920-based adapters are shipped from Cirrus Logic configured as Plug
-and Play (PnP) enabled.  However, since the cs89x0 driver does NOT
-support PnP, you must install the CS8920 adapter in a DOS-based PC and
-run the CS8900/20 Setup Utility to disable PnP and configure the
-adapter before installation in the target Linux system.  Failure to do
-this will leave the adapter inactive and the driver will be unable to
-communicate with the adapter.  
-
-
-        **************************************************************** 
-        *                    CS8920-BASED ADAPTERS:                    *
-        *                                                              * 
-        * CS8920-BASED ADAPTERS ARE PLUG and PLAY ENABLED BY DEFAULT.  * 
-        * THE CS89X0 DRIVER DOES NOT SUPPORT PnP. THEREFORE, YOU MUST  *
-        * RUN THE CS8900/20 SETUP UTILITY TO DISABLE PnP SUPPORT AND   *
-        * TO ACTIVATE THE ADAPTER.                                     *
-        ****************************************************************
-
-
-
-
-3.0 LOADING THE DRIVER AS A MODULE
-===============================================================================
-
-If the driver is compiled as a loadable module, you can load the driver module
-with the 'modprobe' command.  Many of the adapter's configuration parameters can 
-be specified as command-line arguments to the load command.  This facility 
-provides a means to override the EEPROM's settings or for interface 
-configuration when an EEPROM is not used.
-
-Example:
-
-    insmod cs89x0.o io=0x200 irq=0xA media=aui
-
-This example loads the module and configures the adapter to use an IO port base
-address of 200h, interrupt 10, and use the AUI media connection.  The following
-configuration options are available on the command line:
-
-* io=###               - specify IO address (200h-360h)
-* irq=##               - specify interrupt level
-* use_dma=1            - Enable DMA
-* dma=#                - specify dma channel (Driver is compiled to support
-                         Rx DMA only)
-* dmasize=# (16 or 64) - DMA size 16K or 64K.  Default value is set to 16.
-* media=rj45           - specify media type
-   or media=bnc
-   or media=aui
-   or media=auto
-* duplex=full          - specify forced half/full/autonegotiate duplex
-   or duplex=half
-   or duplex=auto
-* debug=#              - debug level (only available if the driver was compiled
-                         for debugging)
-
-NOTES:
-
-a) If an EEPROM is present, any specified command-line parameter
-   will override the corresponding configuration value stored in
-   EEPROM.
-
-b) The "io" parameter must be specified on the command-line.  
-
-c) The driver's hardware probe routine is designed to avoid
-   writing to I/O space until it knows that there is a cs89x0
-   card at the written addresses.  This could cause problems
-   with device probing.  To avoid this behaviour, add one
-   to the `io=' module parameter.  This doesn't actually change
-   the I/O address, but it is a flag to tell the driver
-   to partially initialise the hardware before trying to
-   identify the card.  This could be dangerous if you are
-   not sure that there is a cs89x0 card at the provided address.
-
-   For example, to scan for an adapter located at IO base 0x300,
-   specify an IO address of 0x301.  
-
-d) The "duplex=auto" parameter is only supported for the CS8920.
-
-e) The minimum command-line configuration required if an EEPROM is
-   not present is:
-
-   io 
-   irq 
-   media type (no autodetect)
-
-f) The following additional parameters are CS89XX defaults (values
-   used with no EEPROM or command-line argument).
-
-   * DMA Burst = enabled
-   * IOCHRDY Enabled = enabled
-   * UseSA = enabled
-   * CS8900 defaults to half-duplex if not specified on command-line
-   * CS8920 defaults to autoneg if not specified on command-line
-   * Use reset defaults for other config parameters
-   * dma_mode = 0
-
-g) You can use ifconfig to set the adapter's Ethernet address.
-
-h) Many Linux distributions use the 'modprobe' command to load
-   modules.  This program uses the '/etc/conf.modules' file to
-   determine configuration information which is passed to a driver
-   module when it is loaded.  All the configuration options which are
-   described above may be placed within /etc/conf.modules.
-
-   For example:
-
-   > cat /etc/conf.modules
-   ...
-   alias eth0 cs89x0
-   options cs89x0 io=0x0200 dma=5 use_dma=1
-   ...
-
-   In this example we are telling the module system that the
-   ethernet driver for this machine should use the cs89x0 driver.  We
-   are asking 'modprobe' to pass the 'io', 'dma' and 'use_dma'
-   arguments to the driver when it is loaded.
-
-i) Cirrus recommend that the cs89x0 use the ISA DMA channels 5, 6 or
-   7.  You will probably find that other DMA channels will not work.
-
-j) The cs89x0 supports DMA for receiving only.  DMA mode is
-   significantly more efficient.  Flooding a 400 MHz Celeron machine
-   with large ping packets consumes 82% of its CPU capacity in non-DMA
-   mode.  With DMA this is reduced to 45%.
-
-k) If your Linux kernel was compiled with inbuilt plug-and-play
-   support you will be able to find information about the cs89x0 card
-   with the command
-
-   cat /proc/isapnp
-
-l) If during DMA operation you find erratic behavior or network data
-   corruption you should use your PC's BIOS to slow the EISA bus clock.
-
-m) If the cs89x0 driver is compiled directly into the kernel
-   (non-modular) then its I/O address is automatically determined by
-   ISA bus probing.  The IRQ number, media options, etc are determined
-   from the card's EEPROM.
-
-n) If the cs89x0 driver is compiled directly into the kernel, DMA
-   mode may be selected by providing the kernel with a boot option
-   'cs89x0_dma=N' where 'N' is the desired DMA channel number (5, 6 or 7).
-
-   Kernel boot options may be provided on the LILO command line:
-
-       LILO boot: linux cs89x0_dma=5
-
-   or they may be placed in /etc/lilo.conf:
-
-       image=/boot/bzImage-2.3.48
-         append="cs89x0_dma=5"
-         label=linux
-         root=/dev/hda5
-         read-only
-
-   The DMA Rx buffer size is hardwired to 16 kbytes in this mode.
-   (64k mode is not available).
-
-
-4.0 COMPILING THE DRIVER
-===============================================================================
-
-The cs89x0 driver can be compiled directly into the kernel or compiled into
-a loadable device driver module.
-
-
-4.1 COMPILING THE DRIVER AS A LOADABLE MODULE
-
-To compile the driver into a loadable module, use the following command 
-(single command line, without quotes):
-
-"gcc -D__KERNEL__ -I/usr/src/linux/include -I/usr/src/linux/net/inet -Wall 
--Wstrict-prototypes -O2 -fomit-frame-pointer -DMODULE -DCONFIG_MODVERSIONS 
--c cs89x0.c"
-
-4.2 COMPILING THE DRIVER TO SUPPORT MEMORY MODE
-
-Support for memory mode was not carried over into the 2.3 series kernels.
-
-4.3 COMPILING THE DRIVER TO SUPPORT Rx DMA
-
-The compile-time optionality for DMA was removed in the 2.3 kernel
-series.  DMA support is now unconditionally part of the driver.  It is
-enabled by the 'use_dma=1' module option.
-
-
-5.0 TESTING AND TROUBLESHOOTING
-===============================================================================
-
-5.1 KNOWN DEFECTS and LIMITATIONS
-
-Refer to the RELEASE.TXT file distributed as part of this archive for a list of 
-known defects, driver limitations, and work arounds.
-
-
-5.2 TESTING THE ADAPTER
-
-Once the adapter has been installed and configured, the diagnostic option of 
-the CS8900/20 Setup Utility can be used to test the functionality of the 
-adapter and its network connection.  Use the diagnostics 'Self Test' option to
-test the functionality of the adapter with the hardware configuration you have
-assigned. You can use the diagnostics 'Network Test' to test the ability of the
-adapter to communicate across the Ethernet with another PC equipped with a 
-CS8900/20-based adapter card (it must also be running the CS8900/20 Setup 
-Utility).
-
-         NOTE: The Setup Utility's diagnostics are designed to run in a
-         DOS-only operating system environment.  DO NOT run the diagnostics 
-         from a DOS or command prompt session under Windows 95, Windows NT, 
-         OS/2, or other operating system.
-
-To run the diagnostics tests on the CS8900/20 adapter:
-
-   1.) Boot DOS on the PC and start the CS8900/20 Setup Utility.
-
-   2.) The adapter's current configuration is displayed.  Hit the ENTER key to
-       get to the main menu.
-
-   4.) Select 'Diagnostics' (ALT-G) from the main menu.  
-       * Select 'Self-Test' to test the adapter's basic functionality.
-       * Select 'Network Test' to test the network connection and cabling.
-
-
-5.2.1 DIAGNOSTIC SELF-TEST
-
-The diagnostic self-test checks the adapter's basic functionality as well as 
-its ability to communicate across the ISA bus based on the system resources 
-assigned during hardware configuration.  The following tests are performed:
-
-   * IO Register Read/Write Test
-     The IO Register Read/Write test insures that the CS8900/20 can be 
-     accessed in IO mode, and that the IO base address is correct.
-
-   * Shared Memory Test
-     The Shared Memory test insures the CS8900/20 can be accessed in memory 
-     mode and that the range of memory addresses assigned does not conflict 
-     with other devices in the system.
-
-   * Interrupt Test
-     The Interrupt test insures there are no conflicts with the assigned IRQ
-     signal.
-
-   * EEPROM Test
-     The EEPROM test insures the EEPROM can be read.
-
-   * Chip RAM Test
-     The Chip RAM test insures the 4K of memory internal to the CS8900/20 is
-     working properly.
-
-   * Internal Loop-back Test
-     The Internal Loop Back test insures the adapter's transmitter and 
-     receiver are operating properly.  If this test fails, make sure the 
-     adapter's cable is connected to the network (check for LED activity for 
-     example).
-
-   * Boot PROM Test
-     The Boot PROM  test insures the Boot PROM is present, and can be read.
-     Failure indicates the Boot PROM  was not successfully read due to a
-     hardware problem or due to a conflicts on the Boot PROM address
-     assignment. (Test only applies if the adapter is configured to use the
-     Boot PROM option.)
-
-Failure of a test item indicates a possible system resource conflict with 
-another device on the ISA bus.  In this case, you should use the Manual Setup 
-option to reconfigure the adapter by selecting a different value for the system
-resource that failed.
-
-
-5.2.2 DIAGNOSTIC NETWORK TEST
-
-The Diagnostic Network Test verifies a working network connection by 
-transferring data between two CS8900/20 adapters installed in different PCs 
-on the same network. (Note: the diagnostic network test should not be run 
-between two nodes across a router.) 
-
-This test requires that each of the two PCs have a CS8900/20-based adapter
-installed and have the CS8900/20 Setup Utility running.  The first PC is 
-configured as a Responder and the other PC is configured as an Initiator.  
-Once the Initiator is started, it sends data frames to the Responder which 
-returns the frames to the Initiator.
-
-The total number of frames received and transmitted are displayed on the 
-Initiator's display, along with a count of the number of frames received and 
-transmitted OK or in error.  The test can be terminated anytime by the user at 
-either PC.
-
-To setup the Diagnostic Network Test:
-
-    1.) Select a PC with a CS8900/20-based adapter and a known working network
-        connection to act as the Responder.  Run the CS8900/20 Setup Utility 
-        and select 'Diagnostics -> Network Test -> Responder' from the main 
-        menu.  Hit ENTER to start the Responder.
-
-    2.) Return to the PC with the CS8900/20-based adapter you want to test and
-        start the CS8900/20 Setup Utility. 
-
-    3.) From the main menu, Select 'Diagnostic -> Network Test -> Initiator'.
-        Hit ENTER to start the test.
-You may stop the test on the Initiator at any time while allowing the Responder
-to continue running.  In this manner, you can move to additional PCs and test 
-them by starting the Initiator on another PC without having to stop/start the 
-Responder.
-
-
-5.3 USING THE ADAPTER'S LEDs
-
-The 2 and 3-media adapters have two LEDs visible on the back end of the board 
-located near the 10Base-T connector.  
-
-Link Integrity LED: A "steady" ON of the green LED indicates a valid 10Base-T 
-connection.  (Only applies to 10Base-T.  The green LED has no significance for
-a 10Base-2 or AUI connection.)
-
-TX/RX LED: The yellow LED lights briefly each time the adapter transmits or 
-receives data. (The yellow LED will appear to "flicker" on a typical network.)
-
-
-5.4 RESOLVING I/O CONFLICTS
-
-An IO conflict occurs when two or more adapter use the same ISA resource (IO 
-address, memory address or IRQ).  You can usually detect an IO conflict in one 
-of four ways after installing and or configuring the CS8900/20-based adapter:
-
-    1.) The system does not boot properly (or at all).
-
-    2.) The driver cannot communicate with the adapter, reporting an "Adapter
-        not found" error message.
-
-    3.) You cannot connect to the network or the driver will not load.
-
-    4.) If you have configured the adapter to run in memory mode but the driver
-        reports it is using IO mode when loading, this is an indication of a
-        memory address conflict.
-
-If an IO conflict occurs, run the CS8900/20 Setup Utility and perform a 
-diagnostic self-test.  Normally, the ISA resource in conflict will fail the 
-self-test.  If so, reconfigure the adapter selecting another choice for the 
-resource in conflict.  Run the diagnostics again to check for further IO 
-conflicts.
-
-In some cases, such as when the PC will not boot, it may be necessary to remove
-the adapter and reconfigure it by installing it in another PC to run the 
-CS8900/20 Setup Utility.  Once reinstalled in the target system, run the 
-diagnostics self-test to ensure the new configuration is free of conflicts 
-before loading the driver again.
-
-When manually configuring the adapter, keep in mind the typical ISA system 
-resource usage as indicated in the tables below.
-
-I/O Address            Device                        IRQ      Device
------------            --------                      ---      --------
- 200-20F               Game I/O adapter               3       COM2, Bus Mouse
- 230-23F               Bus Mouse                      4       COM1
- 270-27F               LPT3: third parallel port      5       LPT2
- 2F0-2FF               COM2: second serial port       6       Floppy Disk controller
- 320-32F               Fixed disk controller          7       LPT1
-                                              8       Real-time Clock
-                                                 9       EGA/VGA display adapter    
-                                                12       Mouse (PS/2)                              
-Memory Address  Device                          13       Math Coprocessor
---------------  ---------------------           14       Hard Disk controller
-A000-BFFF      EGA Graphics Adapter
-A000-C7FF      VGA Graphics Adapter
-B000-BFFF      Mono Graphics Adapter
-B800-BFFF      Color Graphics Adapter
-E000-FFFF      AT BIOS
-
-
-
-
-6.0 TECHNICAL SUPPORT
-===============================================================================
-
-6.1 CONTACTING CIRRUS LOGIC'S TECHNICAL SUPPORT
-
-Cirrus Logic's CS89XX Technical Application Support can be reached at:
-
-Telephone  :(800) 888-5016 (from inside U.S. and Canada)
-           :(512) 442-7555 (from outside the U.S. and Canada)
-Fax        :(512) 912-3871
-Email      :ethernet@crystal.cirrus.com
-WWW        :http://www.cirrus.com
-
-
-6.2 INFORMATION REQUIRED BEFORE CONTACTING TECHNICAL SUPPORT
-
-Before contacting Cirrus Logic for technical support, be prepared to provide as 
-Much of the following information as possible. 
-
-1.) Adapter type (CRD8900, CDB8900, CDB8920, etc.)
-
-2.) Adapter configuration
-
-    * IO Base, Memory Base, IO or memory mode enabled, IRQ, DMA channel
-    * Plug and Play enabled/disabled (CS8920-based adapters only)
-    * Configured for media auto-detect or specific media type (which type).    
-
-3.) PC System's Configuration
-
-    * Plug and Play system (yes/no)
-    * BIOS (make and version)
-    * System make and model
-    * CPU (type and speed)
-    * System RAM
-    * SCSI Adapter
-
-4.) Software
-
-    * CS89XX driver and version
-    * Your network operating system and version
-    * Your system's OS version 
-    * Version of all protocol support files
-
-5.) Any Error Message displayed.
-
-
-
-6.3 OBTAINING THE LATEST DRIVER VERSION
-
-You can obtain the latest CS89XX drivers and support software from Cirrus Logic's 
-Web site.  You can also contact Cirrus Logic's Technical Support (email:
-ethernet@crystal.cirrus.com) and request that you be registered for automatic 
-software-update notification.
-
-Cirrus Logic maintains a web page at http://www.cirrus.com with the
-latest drivers and technical publications.
-
-
-6.4 Current maintainer
-
-In February 2000 the maintenance of this driver was assumed by Andrew
-Morton.
-
-6.5 Kernel module parameters
-
-For use in embedded environments with no cs89x0 EEPROM, the kernel boot
-parameter `cs89x0_media=' has been implemented.  Usage is:
-
-       cs89x0_media=rj45    or
-       cs89x0_media=aui     or
-       cs89x0_media=bnc
-
diff --git a/Documentation/networking/device_drivers/davicom/dm9000.rst b/Documentation/networking/device_drivers/davicom/dm9000.rst
new file mode 100644 (file)
index 0000000..d5458da
--- /dev/null
@@ -0,0 +1,171 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================
+DM9000 Network driver
+=====================
+
+Copyright 2008 Simtec Electronics,
+
+         Ben Dooks <ben@simtec.co.uk> <ben-linux@fluff.org>
+
+
+Introduction
+------------
+
+This file describes how to use the DM9000 platform-device based network driver
+that is contained in the files drivers/net/dm9000.c and drivers/net/dm9000.h.
+
+The driver supports three DM9000 variants, the DM9000E which is the first chip
+supported as well as the newer DM9000A and DM9000B devices. It is currently
+maintained and tested by Ben Dooks, who should be CC: to any patches for this
+driver.
+
+
+Defining the platform device
+----------------------------
+
+The minimum set of resources attached to the platform device are as follows:
+
+    1) The physical address of the address register
+    2) The physical address of the data register
+    3) The IRQ line the device's interrupt pin is connected to.
+
+These resources should be specified in that order, as the ordering of the
+two address regions is important (the driver expects these to be address
+and then data).
+
+An example from arch/arm/mach-s3c2410/mach-bast.c is::
+
+  static struct resource bast_dm9k_resource[] = {
+       [0] = {
+               .start = S3C2410_CS5 + BAST_PA_DM9000,
+               .end   = S3C2410_CS5 + BAST_PA_DM9000 + 3,
+               .flags = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start = S3C2410_CS5 + BAST_PA_DM9000 + 0x40,
+               .end   = S3C2410_CS5 + BAST_PA_DM9000 + 0x40 + 0x3f,
+               .flags = IORESOURCE_MEM,
+       },
+       [2] = {
+               .start = IRQ_DM9000,
+               .end   = IRQ_DM9000,
+               .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
+       }
+  };
+
+  static struct platform_device bast_device_dm9k = {
+       .name           = "dm9000",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(bast_dm9k_resource),
+       .resource       = bast_dm9k_resource,
+  };
+
+Note the setting of the IRQ trigger flag in bast_dm9k_resource[2].flags,
+as this will generate a warning if it is not present. The trigger from
+the flags field will be passed to request_irq() when registering the IRQ
+handler to ensure that the IRQ is setup correctly.
+
+This shows a typical platform device, without the optional configuration
+platform data supplied. The next example uses the same resources, but adds
+the optional platform data to pass extra configuration data::
+
+  static struct dm9000_plat_data bast_dm9k_platdata = {
+       .flags          = DM9000_PLATF_16BITONLY,
+  };
+
+  static struct platform_device bast_device_dm9k = {
+       .name           = "dm9000",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(bast_dm9k_resource),
+       .resource       = bast_dm9k_resource,
+       .dev            = {
+               .platform_data = &bast_dm9k_platdata,
+       }
+  };
+
+The platform data is defined in include/linux/dm9000.h and described below.
+
+
+Platform data
+-------------
+
+Extra platform data for the DM9000 can describe the IO bus width to the
+device, whether or not an external PHY is attached to the device and
+the availability of an external configuration EEPROM.
+
+The flags for the platform data .flags field are as follows:
+
+DM9000_PLATF_8BITONLY
+
+       The IO should be done with 8bit operations.
+
+DM9000_PLATF_16BITONLY
+
+       The IO should be done with 16bit operations.
+
+DM9000_PLATF_32BITONLY
+
+       The IO should be done with 32bit operations.
+
+DM9000_PLATF_EXT_PHY
+
+       The chip is connected to an external PHY.
+
+DM9000_PLATF_NO_EEPROM
+
+       This can be used to signify that the board does not have an
+       EEPROM, or that the EEPROM should be hidden from the user.
+
+DM9000_PLATF_SIMPLE_PHY
+
+       Switch to using the simpler PHY polling method which does not
+       try and read the MII PHY state regularly. This is only available
+       when using the internal PHY. See the section on link state polling
+       for more information.
+
+       The config symbol DM9000_FORCE_SIMPLE_PHY_POLL, Kconfig entry
+       "Force simple NSR based PHY polling" allows this flag to be
+       forced on at build time.
+
+
+PHY Link state polling
+----------------------
+
+The driver keeps track of the link state and informs the network core
+about link (carrier) availability. This is managed by several methods
+depending on the version of the chip and on which PHY is being used.
+
+For the internal PHY, the original (and currently default) method is
+to read the MII state, either when the status changes if we have the
+necessary interrupt support in the chip or every two seconds via a
+periodic timer.
+
+To reduce the overhead for the internal PHY, there is now the option
+of using the DM9000_FORCE_SIMPLE_PHY_POLL config, or DM9000_PLATF_SIMPLE_PHY
+platform data option to read the summary information without the
+expensive MII accesses. This method is faster, but does not print
+as much information.
+
+When using an external PHY, the driver currently has to poll the MII
+link status as there is no method for getting an interrupt on link change.
+
+
+DM9000A / DM9000B
+-----------------
+
+These chips are functionally similar to the DM9000E and are supported easily
+by the same driver. The features are:
+
+   1) Interrupt on internal PHY state change. This means that the periodic
+      polling of the PHY status may be disabled on these devices when using
+      the internal PHY.
+
+   2) TCP/UDP checksum offloading, which the driver does not currently support.
+
+
+ethtool
+-------
+
+The driver supports the ethtool interface for access to the driver
+state information, the PHY state and the EEPROM.
diff --git a/Documentation/networking/device_drivers/davicom/dm9000.txt b/Documentation/networking/device_drivers/davicom/dm9000.txt
deleted file mode 100644 (file)
index 5552e2e..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-DM9000 Network driver
-=====================
-
-Copyright 2008 Simtec Electronics,
-         Ben Dooks <ben@simtec.co.uk> <ben-linux@fluff.org>
-
-
-Introduction
-------------
-
-This file describes how to use the DM9000 platform-device based network driver
-that is contained in the files drivers/net/dm9000.c and drivers/net/dm9000.h.
-
-The driver supports three DM9000 variants, the DM9000E which is the first chip
-supported as well as the newer DM9000A and DM9000B devices. It is currently
-maintained and tested by Ben Dooks, who should be CC: to any patches for this
-driver.
-
-
-Defining the platform device
-----------------------------
-
-The minimum set of resources attached to the platform device are as follows:
-
-    1) The physical address of the address register
-    2) The physical address of the data register
-    3) The IRQ line the device's interrupt pin is connected to.
-
-These resources should be specified in that order, as the ordering of the
-two address regions is important (the driver expects these to be address
-and then data).
-
-An example from arch/arm/mach-s3c2410/mach-bast.c is:
-
-static struct resource bast_dm9k_resource[] = {
-       [0] = {
-               .start = S3C2410_CS5 + BAST_PA_DM9000,
-               .end   = S3C2410_CS5 + BAST_PA_DM9000 + 3,
-               .flags = IORESOURCE_MEM,
-       },
-       [1] = {
-               .start = S3C2410_CS5 + BAST_PA_DM9000 + 0x40,
-               .end   = S3C2410_CS5 + BAST_PA_DM9000 + 0x40 + 0x3f,
-               .flags = IORESOURCE_MEM,
-       },
-       [2] = {
-               .start = IRQ_DM9000,
-               .end   = IRQ_DM9000,
-               .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
-       }
-};
-
-static struct platform_device bast_device_dm9k = {
-       .name           = "dm9000",
-       .id             = 0,
-       .num_resources  = ARRAY_SIZE(bast_dm9k_resource),
-       .resource       = bast_dm9k_resource,
-};
-
-Note the setting of the IRQ trigger flag in bast_dm9k_resource[2].flags,
-as this will generate a warning if it is not present. The trigger from
-the flags field will be passed to request_irq() when registering the IRQ
-handler to ensure that the IRQ is setup correctly.
-
-This shows a typical platform device, without the optional configuration
-platform data supplied. The next example uses the same resources, but adds
-the optional platform data to pass extra configuration data:
-
-static struct dm9000_plat_data bast_dm9k_platdata = {
-       .flags          = DM9000_PLATF_16BITONLY,
-};
-
-static struct platform_device bast_device_dm9k = {
-       .name           = "dm9000",
-       .id             = 0,
-       .num_resources  = ARRAY_SIZE(bast_dm9k_resource),
-       .resource       = bast_dm9k_resource,
-       .dev            = {
-               .platform_data = &bast_dm9k_platdata,
-       }
-};
-
-The platform data is defined in include/linux/dm9000.h and described below.
-
-
-Platform data
--------------
-
-Extra platform data for the DM9000 can describe the IO bus width to the
-device, whether or not an external PHY is attached to the device and
-the availability of an external configuration EEPROM.
-
-The flags for the platform data .flags field are as follows:
-
-DM9000_PLATF_8BITONLY
-
-       The IO should be done with 8bit operations.
-
-DM9000_PLATF_16BITONLY
-
-       The IO should be done with 16bit operations.
-
-DM9000_PLATF_32BITONLY
-
-       The IO should be done with 32bit operations.
-
-DM9000_PLATF_EXT_PHY
-
-       The chip is connected to an external PHY.
-
-DM9000_PLATF_NO_EEPROM
-
-       This can be used to signify that the board does not have an
-       EEPROM, or that the EEPROM should be hidden from the user.
-
-DM9000_PLATF_SIMPLE_PHY
-
-       Switch to using the simpler PHY polling method which does not
-       try and read the MII PHY state regularly. This is only available
-       when using the internal PHY. See the section on link state polling
-       for more information.
-
-       The config symbol DM9000_FORCE_SIMPLE_PHY_POLL, Kconfig entry
-       "Force simple NSR based PHY polling" allows this flag to be
-       forced on at build time.
-
-
-PHY Link state polling
-----------------------
-
-The driver keeps track of the link state and informs the network core
-about link (carrier) availability. This is managed by several methods
-depending on the version of the chip and on which PHY is being used.
-
-For the internal PHY, the original (and currently default) method is
-to read the MII state, either when the status changes if we have the
-necessary interrupt support in the chip or every two seconds via a
-periodic timer.
-
-To reduce the overhead for the internal PHY, there is now the option
-of using the DM9000_FORCE_SIMPLE_PHY_POLL config, or DM9000_PLATF_SIMPLE_PHY
-platform data option to read the summary information without the
-expensive MII accesses. This method is faster, but does not print
-as much information.
-
-When using an external PHY, the driver currently has to poll the MII
-link status as there is no method for getting an interrupt on link change.
-
-
-DM9000A / DM9000B
------------------
-
-These chips are functionally similar to the DM9000E and are supported easily
-by the same driver. The features are:
-
-   1) Interrupt on internal PHY state change. This means that the periodic
-      polling of the PHY status may be disabled on these devices when using
-      the internal PHY.
-
-   2) TCP/UDP checksum offloading, which the driver does not currently support.
-
-
-ethtool
--------
-
-The driver supports the ethtool interface for access to the driver
-state information, the PHY state and the EEPROM.
diff --git a/Documentation/networking/device_drivers/dec/de4x5.rst b/Documentation/networking/device_drivers/dec/de4x5.rst
new file mode 100644 (file)
index 0000000..e03e9c6
--- /dev/null
@@ -0,0 +1,189 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================================
+DEC EtherWORKS Ethernet De4x5 cards
+===================================
+
+    Originally,   this  driver  was    written  for the  Digital   Equipment
+    Corporation series of EtherWORKS Ethernet cards:
+
+        - DE425 TP/COAX EISA
+        - DE434 TP PCI
+        - DE435 TP/COAX/AUI PCI
+        - DE450 TP/COAX/AUI PCI
+        - DE500 10/100 PCI Fasternet
+
+    but it  will  now attempt  to  support all  cards which   conform to the
+    Digital Semiconductor   SROM   Specification.    The  driver   currently
+    recognises the following chips:
+
+        - DC21040  (no SROM)
+        - DC21041[A]
+        - DC21140[A]
+        - DC21142
+        - DC21143
+
+    So far the driver is known to work with the following cards:
+
+        - KINGSTON
+        - Linksys
+        - ZNYX342
+        - SMC8432
+        - SMC9332 (w/new SROM)
+        - ZNYX31[45]
+        - ZNYX346 10/100 4 port (can act as a 10/100 bridge!)
+
+    The driver has been tested on a relatively busy network using the DE425,
+    DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
+    16M of data to a DECstation 5000/200 as follows::
+
+                 TCP           UDP
+              TX     RX     TX     RX
+      DE425   1030k  997k   1170k  1128k
+      DE434   1063k  995k   1170k  1125k
+      DE435   1063k  995k   1170k  1125k
+      DE500   1063k  998k   1170k  1125k  in 10Mb/s mode
+
+    All  values are typical (in   kBytes/sec) from a  sample  of 4 for  each
+    measurement. Their error is +/-20k on a quiet (private) network and also
+    depend on what load the CPU has.
+
+----------------------------------------------------------------------------
+
+    The ability to load this  driver as a loadable  module has been included
+    and used extensively  during the driver development  (to save those long
+    reboot sequences).  Loadable module support  under PCI and EISA has been
+    achieved by letting the driver autoprobe as if it were compiled into the
+    kernel. Do make sure  you're not sharing  interrupts with anything  that
+    cannot accommodate  interrupt  sharing!
+
+    To utilise this ability, you have to do 8 things:
+
+    0) have a copy of the loadable modules code installed on your system.
+    1) copy de4x5.c from the  /linux/drivers/net directory to your favourite
+       temporary directory.
+    2) for fixed  autoprobes (not  recommended),  edit the source code  near
+       line 5594 to reflect the I/O address  you're using, or assign these when
+       loading by::
+
+                  insmod de4x5 io=0xghh           where g = bus number
+                                                       hh = device number
+
+       .. note::
+
+          autoprobing for modules is now supported by default. You may just
+          use::
+
+                  insmod de4x5
+
+          to load all available boards. For a specific board, still use
+          the 'io=?' above.
+    3) compile  de4x5.c, but include -DMODULE in  the command line to ensure
+       that the correct bits are compiled (see end of source code).
+    4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a
+       kernel with the de4x5 configuration turned off and reboot.
+    5) insmod de4x5 [io=0xghh]
+    6) run the net startup bits for your new eth?? interface(s) manually
+       (usually /etc/rc.inet[12] at boot time).
+    7) enjoy!
+
+    To unload a module, turn off the associated interface(s)
+    'ifconfig eth?? down' then 'rmmod de4x5'.
+
+    Automedia detection is included so that in  principle you can disconnect
+    from, e.g.  TP, reconnect  to BNC  and  things will still work  (after a
+    pause while the   driver figures out   where its media went).  My tests
+    using ping showed that it appears to work....
+
+    By  default,  the driver will  now   autodetect any  DECchip based card.
+    Should you have a need to restrict the driver to DIGITAL only cards, you
+    can compile with a  DEC_ONLY define, or if  loading as a module, use the
+    'dec_only=1'  parameter.
+
+    I've changed the timing routines to  use the kernel timer and scheduling
+    functions  so that the  hangs  and other assorted problems that occurred
+    while autosensing the  media  should be gone.  A  bonus  for the DC21040
+    auto  media sense algorithm is  that it can now  use one that is more in
+    line with the  rest (the DC21040  chip doesn't  have a hardware  timer).
+    The downside is the 1 'jiffies' (10ms) resolution.
+
+    IEEE 802.3u MII interface code has  been added in anticipation that some
+    products may use it in the future.
+
+    The SMC9332 card  has a non-compliant SROM  which needs fixing -  I have
+    patched this  driver to detect it  because the SROM format used complies
+    to a previous DEC-STD format.
+
+    I have removed the buffer copies needed for receive on Intels.  I cannot
+    remove them for   Alphas since  the  Tulip hardware   only does longword
+    aligned  DMA transfers  and  the  Alphas get   alignment traps with  non
+    longword aligned data copies (which makes them really slow). No comment.
+
+    I  have added SROM decoding  routines to make this  driver work with any
+    card that  supports the Digital  Semiconductor SROM spec. This will help
+    all  cards running the dc2114x  series chips in particular.  Cards using
+    the dc2104x  chips should run correctly with  the basic  driver.  I'm in
+    debt to <mjacob@feral.com> for the  testing and feedback that helped get
+    this feature working.  So far we have  tested KINGSTON, SMC8432, SMC9332
+    (with the latest SROM complying  with the SROM spec  V3: their first was
+    broken), ZNYX342  and  LinkSys. ZNYX314 (dual  21041  MAC) and  ZNYX 315
+    (quad 21041 MAC)  cards also  appear  to work despite their  incorrectly
+    wired IRQs.
+
+    I have added a temporary fix for interrupt problems when some SCSI cards
+    share the same interrupt as the DECchip based  cards. The problem occurs
+    because  the SCSI card wants to  grab the interrupt  as a fast interrupt
+    (runs the   service routine with interrupts turned   off) vs.  this card
+    which really needs to run the service routine with interrupts turned on.
+    This driver will  now   add the interrupt service   routine  as  a  fast
+    interrupt if it   is bounced from the   slow interrupt.  THIS IS NOT   A
+    RECOMMENDED WAY TO RUN THE DRIVER  and has been done  for a limited time
+    until  people   sort  out their  compatibility    issues and the  kernel
+    interrupt  service code  is  fixed.   YOU  SHOULD SEPARATE OUT  THE FAST
+    INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
+    run on the same interrupt. PCMCIA/CardBus is another can of worms...
+
+    Finally, I think  I have really  fixed  the module  loading problem with
+    more than one DECchip based  card.  As a  side effect, I don't mess with
+    the  device structure any  more which means that  if more than 1 card in
+    2.0.x is    installed (4  in   2.1.x),  the  user   will have   to  edit
+    linux/drivers/net/Space.c  to make room for  them. Hence, module loading
+    is  the preferred way to use   this driver, since  it  doesn't have this
+    limitation.
+
+    Where SROM media  detection is used and  full duplex is specified in the
+    SROM,  the feature is  ignored unless  lp->params.fdx  is set at compile
+    time  OR during  a   module load  (insmod  de4x5   args='eth??:fdx' [see
+    below]).  This is because there  is no way  to automatically detect full
+    duplex   links  except through   autonegotiation.    When I  include the
+    autonegotiation feature in  the SROM autoconf  code, this detection will
+    occur automatically for that case.
+
+    Command line  arguments are  now allowed, similar to  passing  arguments
+    through LILO. This will allow a per adapter board set  up of full duplex
+    and media. The only lexical constraints are:  the board name (dev->name)
+    appears in  the list before its parameters.  The list of parameters ends
+    either at the end of the parameter list or with another board name.  The
+    following parameters are allowed:
+
+           =========  ===============================================
+           fdx        for full duplex
+           autosense  to set the media/speed; with the following
+                      sub-parameters:
+                      TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO
+           =========  ===============================================
+
+    Case sensitivity is important  for  the sub-parameters. They *must*   be
+    upper case. Examples::
+
+       insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
+
+    For a compiled in driver, in linux/drivers/net/CONFIG, place e.g.::
+
+       DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"'
+
+    Yes,  I know full duplex  isn't permissible on BNC  or AUI; they're just
+    examples. By default, full duplex is turned  off and AUTO is the default
+    autosense setting. In  reality, I expect only the  full duplex option to
+    be used. Note the use of single quotes in the two examples above and the
+    lack of commas to separate items.
diff --git a/Documentation/networking/device_drivers/dec/de4x5.txt b/Documentation/networking/device_drivers/dec/de4x5.txt
deleted file mode 100644 (file)
index 452aac5..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-    Originally,   this  driver  was    written  for the  Digital   Equipment
-    Corporation series of EtherWORKS Ethernet cards:
-
-        DE425 TP/COAX EISA
-       DE434 TP PCI
-       DE435 TP/COAX/AUI PCI
-       DE450 TP/COAX/AUI PCI
-       DE500 10/100 PCI Fasternet
-
-    but it  will  now attempt  to  support all  cards which   conform to the
-    Digital Semiconductor   SROM   Specification.    The  driver   currently
-    recognises the following chips:
-
-        DC21040  (no SROM) 
-       DC21041[A]  
-       DC21140[A] 
-       DC21142 
-       DC21143 
-
-    So far the driver is known to work with the following cards:
-
-        KINGSTON
-       Linksys
-       ZNYX342
-       SMC8432
-       SMC9332 (w/new SROM)
-       ZNYX31[45]
-       ZNYX346 10/100 4 port (can act as a 10/100 bridge!) 
-
-    The driver has been tested on a relatively busy network using the DE425,
-    DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
-    16M of data to a DECstation 5000/200 as follows:
-
-                TCP           UDP
-             TX     RX     TX     RX
-    DE425   1030k  997k   1170k  1128k
-    DE434   1063k  995k   1170k  1125k
-    DE435   1063k  995k   1170k  1125k
-    DE500   1063k  998k   1170k  1125k  in 10Mb/s mode
-
-    All  values are typical (in   kBytes/sec) from a  sample  of 4 for  each
-    measurement. Their error is +/-20k on a quiet (private) network and also
-    depend on what load the CPU has.
-
-    =========================================================================
-
-    The ability to load this  driver as a loadable  module has been included
-    and used extensively  during the driver development  (to save those long
-    reboot sequences).  Loadable module support  under PCI and EISA has been
-    achieved by letting the driver autoprobe as if it were compiled into the
-    kernel. Do make sure  you're not sharing  interrupts with anything  that
-    cannot accommodate  interrupt  sharing!
-
-    To utilise this ability, you have to do 8 things:
-
-    0) have a copy of the loadable modules code installed on your system.
-    1) copy de4x5.c from the  /linux/drivers/net directory to your favourite
-    temporary directory.
-    2) for fixed  autoprobes (not  recommended),  edit the source code  near
-    line 5594 to reflect the I/O address  you're using, or assign these when
-    loading by:
-
-                   insmod de4x5 io=0xghh           where g = bus number
-                                                       hh = device number   
-
-       NB: autoprobing for modules is now supported by default. You may just
-           use:
-
-                   insmod de4x5
-
-           to load all available boards. For a specific board, still use
-          the 'io=?' above.
-    3) compile  de4x5.c, but include -DMODULE in  the command line to ensure
-    that the correct bits are compiled (see end of source code).
-    4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a
-    kernel with the de4x5 configuration turned off and reboot.
-    5) insmod de4x5 [io=0xghh]
-    6) run the net startup bits for your new eth?? interface(s) manually 
-    (usually /etc/rc.inet[12] at boot time). 
-    7) enjoy!
-
-    To unload a module, turn off the associated interface(s) 
-    'ifconfig eth?? down' then 'rmmod de4x5'.
-
-    Automedia detection is included so that in  principle you can disconnect
-    from, e.g.  TP, reconnect  to BNC  and  things will still work  (after a
-    pause while the   driver figures out   where its media went).  My tests
-    using ping showed that it appears to work....
-
-    By  default,  the driver will  now   autodetect any  DECchip based card.
-    Should you have a need to restrict the driver to DIGITAL only cards, you
-    can compile with a  DEC_ONLY define, or if  loading as a module, use the
-    'dec_only=1'  parameter. 
-
-    I've changed the timing routines to  use the kernel timer and scheduling
-    functions  so that the  hangs  and other assorted problems that occurred
-    while autosensing the  media  should be gone.  A  bonus  for the DC21040
-    auto  media sense algorithm is  that it can now  use one that is more in
-    line with the  rest (the DC21040  chip doesn't  have a hardware  timer).
-    The downside is the 1 'jiffies' (10ms) resolution.
-
-    IEEE 802.3u MII interface code has  been added in anticipation that some
-    products may use it in the future.
-
-    The SMC9332 card  has a non-compliant SROM  which needs fixing -  I have
-    patched this  driver to detect it  because the SROM format used complies
-    to a previous DEC-STD format.
-
-    I have removed the buffer copies needed for receive on Intels.  I cannot
-    remove them for   Alphas since  the  Tulip hardware   only does longword
-    aligned  DMA transfers  and  the  Alphas get   alignment traps with  non
-    longword aligned data copies (which makes them really slow). No comment.
-
-    I  have added SROM decoding  routines to make this  driver work with any
-    card that  supports the Digital  Semiconductor SROM spec. This will help
-    all  cards running the dc2114x  series chips in particular.  Cards using
-    the dc2104x  chips should run correctly with  the basic  driver.  I'm in
-    debt to <mjacob@feral.com> for the  testing and feedback that helped get
-    this feature working.  So far we have  tested KINGSTON, SMC8432, SMC9332
-    (with the latest SROM complying  with the SROM spec  V3: their first was
-    broken), ZNYX342  and  LinkSys. ZNYX314 (dual  21041  MAC) and  ZNYX 315
-    (quad 21041 MAC)  cards also  appear  to work despite their  incorrectly
-    wired IRQs.
-
-    I have added a temporary fix for interrupt problems when some SCSI cards
-    share the same interrupt as the DECchip based  cards. The problem occurs
-    because  the SCSI card wants to  grab the interrupt  as a fast interrupt
-    (runs the   service routine with interrupts turned   off) vs.  this card
-    which really needs to run the service routine with interrupts turned on.
-    This driver will  now   add the interrupt service   routine  as  a  fast
-    interrupt if it   is bounced from the   slow interrupt.  THIS IS NOT   A
-    RECOMMENDED WAY TO RUN THE DRIVER  and has been done  for a limited time
-    until  people   sort  out their  compatibility    issues and the  kernel
-    interrupt  service code  is  fixed.   YOU  SHOULD SEPARATE OUT  THE FAST
-    INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
-    run on the same interrupt. PCMCIA/CardBus is another can of worms...
-
-    Finally, I think  I have really  fixed  the module  loading problem with
-    more than one DECchip based  card.  As a  side effect, I don't mess with
-    the  device structure any  more which means that  if more than 1 card in
-    2.0.x is    installed (4  in   2.1.x),  the  user   will have   to  edit
-    linux/drivers/net/Space.c  to make room for  them. Hence, module loading
-    is  the preferred way to use   this driver, since  it  doesn't have this
-    limitation.
-
-    Where SROM media  detection is used and  full duplex is specified in the
-    SROM,  the feature is  ignored unless  lp->params.fdx  is set at compile
-    time  OR during  a   module load  (insmod  de4x5   args='eth??:fdx' [see
-    below]).  This is because there  is no way  to automatically detect full
-    duplex   links  except through   autonegotiation.    When I  include the
-    autonegotiation feature in  the SROM autoconf  code, this detection will
-    occur automatically for that case.
-
-    Command line  arguments are  now allowed, similar to  passing  arguments
-    through LILO. This will allow a per adapter board set  up of full duplex
-    and media. The only lexical constraints are:  the board name (dev->name)
-    appears in  the list before its parameters.  The list of parameters ends
-    either at the end of the parameter list or with another board name.  The
-    following parameters are allowed:
-
-            fdx        for full duplex
-           autosense  to set the media/speed; with the following 
-                      sub-parameters:
-                      TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO
-
-    Case sensitivity is important  for  the sub-parameters. They *must*   be
-    upper case. Examples:
-
-        insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
-
-    For a compiled in driver, in linux/drivers/net/CONFIG, place e.g.
-       DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' 
-
-    Yes,  I know full duplex  isn't permissible on BNC  or AUI; they're just
-    examples. By default, full duplex is turned  off and AUTO is the default
-    autosense setting. In  reality, I expect only the  full duplex option to
-    be used. Note the use of single quotes in the two examples above and the
-    lack of commas to separate items.
diff --git a/Documentation/networking/device_drivers/dec/dmfe.rst b/Documentation/networking/device_drivers/dec/dmfe.rst
new file mode 100644 (file)
index 0000000..c4cf809
--- /dev/null
@@ -0,0 +1,71 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================================================
+Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux
+==============================================================
+
+Note: This driver doesn't have a maintainer.
+
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General   Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+
+This driver provides kernel support for Davicom DM9102(A)/DM9132/DM9801 ethernet cards ( CNET
+10/100 ethernet cards uses Davicom chipset too, so this driver supports CNET cards too ).If you
+didn't compile this driver as a module, it will automatically load itself on boot and print a
+line similar to::
+
+       dmfe: Davicom DM9xxx net driver, version 1.36.4 (2002-01-17)
+
+If you compiled this driver as a module, you have to load it on boot.You can load it with command::
+
+       insmod dmfe
+
+This way it will autodetect the device mode.This is the suggested way to load the module.Or you can pass
+a mode= setting to module while loading, like::
+
+       insmod dmfe mode=0 # Force 10M Half Duplex
+       insmod dmfe mode=1 # Force 100M Half Duplex
+       insmod dmfe mode=4 # Force 10M Full Duplex
+       insmod dmfe mode=5 # Force 100M Full Duplex
+
+Next you should configure your network interface with a command similar to::
+
+       ifconfig eth0 172.22.3.18
+                     ^^^^^^^^^^^
+                    Your IP Address
+
+Then you may have to modify the default routing table with command::
+
+       route add default eth0
+
+
+Now your ethernet card should be up and running.
+
+
+TODO:
+
+- Implement pci_driver::suspend() and pci_driver::resume() power management methods.
+- Check on 64 bit boxes.
+- Check and fix on big endian boxes.
+- Test and make sure PCI latency is now correct for all cases.
+
+
+Authors:
+
+Sten Wang <sten_wang@davicom.com.tw >   : Original Author
+
+Contributors:
+
+- Marcelo Tosatti <marcelo@conectiva.com.br>
+- Alan Cox <alan@lxorguk.ukuu.org.uk>
+- Jeff Garzik <jgarzik@pobox.com>
+- Vojtech Pavlik <vojtech@suse.cz>
diff --git a/Documentation/networking/device_drivers/dec/dmfe.txt b/Documentation/networking/device_drivers/dec/dmfe.txt
deleted file mode 100644 (file)
index 25320bf..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-Note: This driver doesn't have a maintainer.
-
-Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux.
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General   Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-
-This driver provides kernel support for Davicom DM9102(A)/DM9132/DM9801 ethernet cards ( CNET
-10/100 ethernet cards uses Davicom chipset too, so this driver supports CNET cards too ).If you
-didn't compile this driver as a module, it will automatically load itself on boot and print a
-line similar to :
-
-       dmfe: Davicom DM9xxx net driver, version 1.36.4 (2002-01-17)
-
-If you compiled this driver as a module, you have to load it on boot.You can load it with command :
-
-       insmod dmfe
-
-This way it will autodetect the device mode.This is the suggested way to load the module.Or you can pass
-a mode= setting to module while loading, like :
-
-       insmod dmfe mode=0 # Force 10M Half Duplex
-       insmod dmfe mode=1 # Force 100M Half Duplex
-       insmod dmfe mode=4 # Force 10M Full Duplex
-       insmod dmfe mode=5 # Force 100M Full Duplex
-
-Next you should configure your network interface with a command similar to :
-
-       ifconfig eth0 172.22.3.18
-                      ^^^^^^^^^^^
-                    Your IP Address
-
-Then you may have to modify the default routing table with command :
-
-       route add default eth0
-
-
-Now your ethernet card should be up and running.
-
-
-TODO:
-
-Implement pci_driver::suspend() and pci_driver::resume() power management methods.
-Check on 64 bit boxes.
-Check and fix on big endian boxes.
-Test and make sure PCI latency is now correct for all cases.
-
-
-Authors:
-
-Sten Wang <sten_wang@davicom.com.tw >   : Original Author
-
-Contributors:
-
-Marcelo Tosatti <marcelo@conectiva.com.br>
-Alan Cox <alan@lxorguk.ukuu.org.uk>
-Jeff Garzik <jgarzik@pobox.com>
-Vojtech Pavlik <vojtech@suse.cz>
diff --git a/Documentation/networking/device_drivers/dlink/dl2k.rst b/Documentation/networking/device_drivers/dlink/dl2k.rst
new file mode 100644 (file)
index 0000000..ccdb5d0
--- /dev/null
@@ -0,0 +1,314 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================================================
+D-Link DL2000-based Gigabit Ethernet Adapter Installation
+=========================================================
+
+May 23, 2002
+
+.. Contents
+
+ - Compatibility List
+ - Quick Install
+ - Compiling the Driver
+ - Installing the Driver
+ - Option parameter
+ - Configuration Script Sample
+ - Troubleshooting
+
+
+Compatibility List
+==================
+
+Adapter Support:
+
+- D-Link DGE-550T Gigabit Ethernet Adapter.
+- D-Link DGE-550SX Gigabit Ethernet Adapter.
+- D-Link DL2000-based Gigabit Ethernet Adapter.
+
+
+The driver support Linux kernel 2.4.7 later. We had tested it
+on the environments below.
+
+ . Red Hat v6.2 (update kernel to 2.4.7)
+ . Red Hat v7.0 (update kernel to 2.4.7)
+ . Red Hat v7.1 (kernel 2.4.7)
+ . Red Hat v7.2 (kernel 2.4.7-10)
+
+
+Quick Install
+=============
+Install linux driver as following command::
+
+    1. make all
+    2. insmod dl2k.ko
+    3. ifconfig eth0 up 10.xxx.xxx.xxx netmask 255.0.0.0
+                       ^^^^^^^^^^^^^^^\            ^^^^^^^^\
+                                       IP                   NETMASK
+
+Now eth0 should active, you can test it by "ping" or get more information by
+"ifconfig". If tested ok, continue the next step.
+
+4. ``cp dl2k.ko /lib/modules/`uname -r`/kernel/drivers/net``
+5. Add the following line to /etc/modprobe.d/dl2k.conf::
+
+       alias eth0 dl2k
+
+6. Run ``depmod`` to updated module indexes.
+7. Run ``netconfig`` or ``netconf`` to create configuration script ifcfg-eth0
+   located at /etc/sysconfig/network-scripts or create it manually.
+
+   [see - Configuration Script Sample]
+8. Driver will automatically load and configure at next boot time.
+
+Compiling the Driver
+====================
+In Linux, NIC drivers are most commonly configured as loadable modules.
+The approach of building a monolithic kernel has become obsolete. The driver
+can be compiled as part of a monolithic kernel, but is strongly discouraged.
+The remainder of this section assumes the driver is built as a loadable module.
+In the Linux environment, it is a good idea to rebuild the driver from the
+source instead of relying on a precompiled version. This approach provides
+better reliability since a precompiled driver might depend on libraries or
+kernel features that are not present in a given Linux installation.
+
+The 3 files necessary to build Linux device driver are dl2k.c, dl2k.h and
+Makefile. To compile, the Linux installation must include the gcc compiler,
+the kernel source, and the kernel headers. The Linux driver supports Linux
+Kernels 2.4.7. Copy the files to a directory and enter the following command
+to compile and link the driver:
+
+CD-ROM drive
+------------
+
+::
+
+    [root@XXX /] mkdir cdrom
+    [root@XXX /] mount -r -t iso9660 -o conv=auto /dev/cdrom /cdrom
+    [root@XXX /] cd root
+    [root@XXX /root] mkdir dl2k
+    [root@XXX /root] cd dl2k
+    [root@XXX dl2k] cp /cdrom/linux/dl2k.tgz /root/dl2k
+    [root@XXX dl2k] tar xfvz dl2k.tgz
+    [root@XXX dl2k] make all
+
+Floppy disc drive
+-----------------
+
+::
+
+    [root@XXX /] cd root
+    [root@XXX /root] mkdir dl2k
+    [root@XXX /root] cd dl2k
+    [root@XXX dl2k] mcopy a:/linux/dl2k.tgz /root/dl2k
+    [root@XXX dl2k] tar xfvz dl2k.tgz
+    [root@XXX dl2k] make all
+
+Installing the Driver
+=====================
+
+Manual Installation
+-------------------
+
+  Once the driver has been compiled, it must be loaded, enabled, and bound
+  to a protocol stack in order to establish network connectivity. To load a
+  module enter the command::
+
+    insmod dl2k.o
+
+  or::
+
+    insmod dl2k.o <optional parameter> ; add parameter
+
+---------------------------------------------------------
+
+  example::
+
+    insmod dl2k.o media=100mbps_hd
+
+   or::
+
+    insmod dl2k.o media=3
+
+   or::
+
+    insmod dl2k.o media=3,2    ; for 2 cards
+
+---------------------------------------------------------
+
+  Please reference the list of the command line parameters supported by
+  the Linux device driver below.
+
+  The insmod command only loads the driver and gives it a name of the form
+  eth0, eth1, etc. To bring the NIC into an operational state,
+  it is necessary to issue the following command::
+
+    ifconfig eth0 up
+
+  Finally, to bind the driver to the active protocol (e.g., TCP/IP with
+  Linux), enter the following command::
+
+    ifup eth0
+
+  Note that this is meaningful only if the system can find a configuration
+  script that contains the necessary network information. A sample will be
+  given in the next paragraph.
+
+  The commands to unload a driver are as follows::
+
+    ifdown eth0
+    ifconfig eth0 down
+    rmmod dl2k.o
+
+  The following are the commands to list the currently loaded modules and
+  to see the current network configuration::
+
+    lsmod
+    ifconfig
+
+
+Automated Installation
+----------------------
+  This section describes how to install the driver such that it is
+  automatically loaded and configured at boot time. The following description
+  is based on a Red Hat 6.0/7.0 distribution, but it can easily be ported to
+  other distributions as well.
+
+Red Hat v6.x/v7.x
+-----------------
+  1. Copy dl2k.o to the network modules directory, typically
+     /lib/modules/2.x.x-xx/net or /lib/modules/2.x.x/kernel/drivers/net.
+  2. Locate the boot module configuration file, most commonly in the
+     /etc/modprobe.d/ directory. Add the following lines::
+
+       alias ethx dl2k
+       options dl2k <optional parameters>
+
+     where ethx will be eth0 if the NIC is the only ethernet adapter, eth1 if
+     one other ethernet adapter is installed, etc. Refer to the table in the
+     previous section for the list of optional parameters.
+  3. Locate the network configuration scripts, normally the
+     /etc/sysconfig/network-scripts directory, and create a configuration
+     script named ifcfg-ethx that contains network information.
+  4. Note that for most Linux distributions, Red Hat included, a configuration
+     utility with a graphical user interface is provided to perform steps 2
+     and 3 above.
+
+
+Parameter Description
+=====================
+You can install this driver without any additional parameter. However, if you
+are going to have extensive functions then it is necessary to set extra
+parameter. Below is a list of the command line parameters supported by the
+Linux device
+driver.
+
+
+===============================   ==============================================
+mtu=packet_size                          Specifies the maximum packet size. default
+                                 is 1500.
+
+media=media_type                 Specifies the media type the NIC operates at.
+                                 autosense     Autosensing active media.
+
+                                 ===========   =========================
+                                 10mbps_hd     10Mbps half duplex.
+                                 10mbps_fd     10Mbps full duplex.
+                                 100mbps_hd    100Mbps half duplex.
+                                 100mbps_fd    100Mbps full duplex.
+                                 1000mbps_fd   1000Mbps full duplex.
+                                 1000mbps_hd   1000Mbps half duplex.
+                                 0             Autosensing active media.
+                                 1             10Mbps half duplex.
+                                 2             10Mbps full duplex.
+                                 3             100Mbps half duplex.
+                                 4             100Mbps full duplex.
+                                 5             1000Mbps half duplex.
+                                 6             1000Mbps full duplex.
+                                 ===========   =========================
+
+                                 By default, the NIC operates at autosense.
+                                 1000mbps_fd and 1000mbps_hd types are only
+                                 available for fiber adapter.
+
+vlan=n                           Specifies the VLAN ID. If vlan=0, the
+                                 Virtual Local Area Network (VLAN) function is
+                                 disable.
+
+jumbo=[0|1]                      Specifies the jumbo frame support. If jumbo=1,
+                                 the NIC accept jumbo frames. By default, this
+                                 function is disabled.
+                                 Jumbo frame usually improve the performance
+                                 int gigabit.
+                                 This feature need jumbo frame compatible
+                                 remote.
+
+rx_coalesce=m                    Number of rx frame handled each interrupt.
+rx_timeout=n                     Rx DMA wait time for an interrupt.
+                                 If set rx_coalesce > 0, hardware only assert
+                                 an interrupt for m frames. Hardware won't
+                                 assert rx interrupt until m frames received or
+                                 reach timeout of n * 640 nano seconds.
+                                 Set proper rx_coalesce and rx_timeout can
+                                 reduce congestion collapse and overload which
+                                 has been a bottleneck for high speed network.
+
+                                 For example, rx_coalesce=10 rx_timeout=800.
+                                 that is, hardware assert only 1 interrupt
+                                 for 10 frames received or timeout of 512 us.
+
+tx_coalesce=n                    Number of tx frame handled each interrupt.
+                                 Set n > 1 can reduce the interrupts
+                                 congestion usually lower performance of
+                                 high speed network card. Default is 16.
+
+tx_flow=[1|0]                    Specifies the Tx flow control. If tx_flow=0,
+                                 the Tx flow control disable else driver
+                                 autodetect.
+rx_flow=[1|0]                    Specifies the Rx flow control. If rx_flow=0,
+                                 the Rx flow control enable else driver
+                                 autodetect.
+===============================   ==============================================
+
+
+Configuration Script Sample
+===========================
+Here is a sample of a simple configuration script::
+
+    DEVICE=eth0
+    USERCTL=no
+    ONBOOT=yes
+    POOTPROTO=none
+    BROADCAST=207.200.5.255
+    NETWORK=207.200.5.0
+    NETMASK=255.255.255.0
+    IPADDR=207.200.5.2
+
+
+Troubleshooting
+===============
+Q1. Source files contain ^ M behind every line.
+
+    Make sure all files are Unix file format (no LF). Try the following
+    shell command to convert files::
+
+       cat dl2k.c | col -b > dl2k.tmp
+       mv dl2k.tmp dl2k.c
+
+    OR::
+
+       cat dl2k.c | tr -d "\r" > dl2k.tmp
+       mv dl2k.tmp dl2k.c
+
+Q2: Could not find header files (``*.h``)?
+
+    To compile the driver, you need kernel header files. After
+    installing the kernel source, the header files are usually located in
+    /usr/src/linux/include, which is the default include directory configured
+    in Makefile. For some distributions, there is a copy of header files in
+    /usr/src/include/linux and /usr/src/include/asm, that you can change the
+    INCLUDEDIR in Makefile to /usr/include without installing kernel source.
+
+    Note that RH 7.0 didn't provide correct header files in /usr/include,
+    including those files will make a wrong version driver.
+
diff --git a/Documentation/networking/device_drivers/dlink/dl2k.txt b/Documentation/networking/device_drivers/dlink/dl2k.txt
deleted file mode 100644 (file)
index cba74f7..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-
-    D-Link DL2000-based Gigabit Ethernet Adapter Installation
-    for Linux
-    May 23, 2002
-
-Contents
-========
- - Compatibility List
- - Quick Install
- - Compiling the Driver
- - Installing the Driver
- - Option parameter
- - Configuration Script Sample
- - Troubleshooting
-
-
-Compatibility List
-=================
-Adapter Support:
-
-D-Link DGE-550T Gigabit Ethernet Adapter.
-D-Link DGE-550SX Gigabit Ethernet Adapter.
-D-Link DL2000-based Gigabit Ethernet Adapter.
-
-
-The driver support Linux kernel 2.4.7 later. We had tested it
-on the environments below.
-
- . Red Hat v6.2 (update kernel to 2.4.7)
- . Red Hat v7.0 (update kernel to 2.4.7)
- . Red Hat v7.1 (kernel 2.4.7)
- . Red Hat v7.2 (kernel 2.4.7-10)
-
-
-Quick Install
-=============
-Install linux driver as following command:
-
-1. make all
-2. insmod dl2k.ko
-3. ifconfig eth0 up 10.xxx.xxx.xxx netmask 255.0.0.0
-                   ^^^^^^^^^^^^^^^\        ^^^^^^^^\
-                                   IP               NETMASK
-Now eth0 should active, you can test it by "ping" or get more information by
-"ifconfig". If tested ok, continue the next step.
-
-4. cp dl2k.ko /lib/modules/`uname -r`/kernel/drivers/net
-5. Add the following line to /etc/modprobe.d/dl2k.conf:
-       alias eth0 dl2k
-6. Run depmod to updated module indexes.
-7. Run "netconfig" or "netconf" to create configuration script ifcfg-eth0
-   located at /etc/sysconfig/network-scripts or create it manually.
-   [see - Configuration Script Sample]
-8. Driver will automatically load and configure at next boot time.
-
-Compiling the Driver
-====================
-  In Linux, NIC drivers are most commonly configured as loadable modules.
-The approach of building a monolithic kernel has become obsolete. The driver
-can be compiled as part of a monolithic kernel, but is strongly discouraged.
-The remainder of this section assumes the driver is built as a loadable module.
-In the Linux environment, it is a good idea to rebuild the driver from the
-source instead of relying on a precompiled version. This approach provides
-better reliability since a precompiled driver might depend on libraries or
-kernel features that are not present in a given Linux installation.
-
-The 3 files necessary to build Linux device driver are dl2k.c, dl2k.h and
-Makefile. To compile, the Linux installation must include the gcc compiler,
-the kernel source, and the kernel headers. The Linux driver supports Linux
-Kernels 2.4.7. Copy the files to a directory and enter the following command
-to compile and link the driver:
-
-CD-ROM drive
-------------
-
-[root@XXX /] mkdir cdrom
-[root@XXX /] mount -r -t iso9660 -o conv=auto /dev/cdrom /cdrom
-[root@XXX /] cd root
-[root@XXX /root] mkdir dl2k
-[root@XXX /root] cd dl2k
-[root@XXX dl2k] cp /cdrom/linux/dl2k.tgz /root/dl2k
-[root@XXX dl2k] tar xfvz dl2k.tgz
-[root@XXX dl2k] make all
-
-Floppy disc drive
------------------
-
-[root@XXX /] cd root
-[root@XXX /root] mkdir dl2k
-[root@XXX /root] cd dl2k
-[root@XXX dl2k] mcopy a:/linux/dl2k.tgz /root/dl2k
-[root@XXX dl2k] tar xfvz dl2k.tgz
-[root@XXX dl2k] make all
-
-Installing the Driver
-=====================
-
-  Manual Installation
-  -------------------
-  Once the driver has been compiled, it must be loaded, enabled, and bound
-  to a protocol stack in order to establish network connectivity. To load a
-  module enter the command:
-
-  insmod dl2k.o
-
-  or
-
-  insmod dl2k.o <optional parameter>   ; add parameter
-
-  ===============================================================
-   example: insmod dl2k.o media=100mbps_hd
-   or      insmod dl2k.o media=3
-   or      insmod dl2k.o media=3,2     ; for 2 cards
-  ===============================================================
-
-  Please reference the list of the command line parameters supported by
-  the Linux device driver below.
-
-  The insmod command only loads the driver and gives it a name of the form
-  eth0, eth1, etc. To bring the NIC into an operational state,
-  it is necessary to issue the following command:
-
-  ifconfig eth0 up
-
-  Finally, to bind the driver to the active protocol (e.g., TCP/IP with
-  Linux), enter the following command:
-
-  ifup eth0
-
-  Note that this is meaningful only if the system can find a configuration
-  script that contains the necessary network information. A sample will be
-  given in the next paragraph.
-
-  The commands to unload a driver are as follows:
-
-  ifdown eth0
-  ifconfig eth0 down
-  rmmod dl2k.o
-
-  The following are the commands to list the currently loaded modules and
-  to see the current network configuration.
-
-  lsmod
-  ifconfig
-
-
-  Automated Installation
-  ----------------------
-  This section describes how to install the driver such that it is
-  automatically loaded and configured at boot time. The following description
-  is based on a Red Hat 6.0/7.0 distribution, but it can easily be ported to
-  other distributions as well.
-
-  Red Hat v6.x/v7.x
-  -----------------
-  1. Copy dl2k.o to the network modules directory, typically
-     /lib/modules/2.x.x-xx/net or /lib/modules/2.x.x/kernel/drivers/net.
-  2. Locate the boot module configuration file, most commonly in the
-     /etc/modprobe.d/ directory. Add the following lines:
-
-     alias ethx dl2k
-     options dl2k <optional parameters>
-
-     where ethx will be eth0 if the NIC is the only ethernet adapter, eth1 if
-     one other ethernet adapter is installed, etc. Refer to the table in the
-     previous section for the list of optional parameters.
-  3. Locate the network configuration scripts, normally the
-     /etc/sysconfig/network-scripts directory, and create a configuration
-     script named ifcfg-ethx that contains network information.
-  4. Note that for most Linux distributions, Red Hat included, a configuration
-     utility with a graphical user interface is provided to perform steps 2
-     and 3 above.
-
-
-Parameter Description
-=====================
-You can install this driver without any additional parameter. However, if you
-are going to have extensive functions then it is necessary to set extra
-parameter. Below is a list of the command line parameters supported by the
-Linux device
-driver.
-
-mtu=packet_size                        - Specifies the maximum packet size. default
-                                 is 1500.
-
-media=media_type               - Specifies the media type the NIC operates at.
-                                 autosense     Autosensing active media.
-                                 10mbps_hd     10Mbps half duplex.
-                                 10mbps_fd     10Mbps full duplex.
-                                 100mbps_hd    100Mbps half duplex.
-                                 100mbps_fd    100Mbps full duplex.
-                                 1000mbps_fd   1000Mbps full duplex.
-                                 1000mbps_hd   1000Mbps half duplex.
-                                 0             Autosensing active media.
-                                 1             10Mbps half duplex.
-                                 2             10Mbps full duplex.
-                                 3             100Mbps half duplex.
-                                 4             100Mbps full duplex.
-                                 5             1000Mbps half duplex.
-                                 6             1000Mbps full duplex.
-
-                                 By default, the NIC operates at autosense.
-                                 1000mbps_fd and 1000mbps_hd types are only
-                                 available for fiber adapter.
-
-vlan=n                         - Specifies the VLAN ID. If vlan=0, the
-                                 Virtual Local Area Network (VLAN) function is
-                                 disable.
-
-jumbo=[0|1]                    - Specifies the jumbo frame support. If jumbo=1,
-                                 the NIC accept jumbo frames. By default, this
-                                 function is disabled.
-                                 Jumbo frame usually improve the performance
-                                 int gigabit.
-                                 This feature need jumbo frame compatible 
-                                 remote.
-                                 
-rx_coalesce=m                  - Number of rx frame handled each interrupt.
-rx_timeout=n                   - Rx DMA wait time for an interrupt. 
-                                 If set rx_coalesce > 0, hardware only assert 
-                                 an interrupt for m frames. Hardware won't 
-                                 assert rx interrupt until m frames received or
-                                 reach timeout of n * 640 nano seconds. 
-                                 Set proper rx_coalesce and rx_timeout can 
-                                 reduce congestion collapse and overload which
-                                 has been a bottleneck for high speed network.
-                                 
-                                 For example, rx_coalesce=10 rx_timeout=800.
-                                 that is, hardware assert only 1 interrupt 
-                                 for 10 frames received or timeout of 512 us. 
-
-tx_coalesce=n                  - Number of tx frame handled each interrupt.
-                                 Set n > 1 can reduce the interrupts 
-                                 congestion usually lower performance of
-                                 high speed network card. Default is 16.
-                                 
-tx_flow=[1|0]                  - Specifies the Tx flow control. If tx_flow=0, 
-                                 the Tx flow control disable else driver
-                                 autodetect.
-rx_flow=[1|0]                  - Specifies the Rx flow control. If rx_flow=0, 
-                                 the Rx flow control enable else driver
-                                 autodetect.
-
-
-Configuration Script Sample
-===========================
-Here is a sample of a simple configuration script:
-
-DEVICE=eth0
-USERCTL=no
-ONBOOT=yes
-POOTPROTO=none
-BROADCAST=207.200.5.255
-NETWORK=207.200.5.0
-NETMASK=255.255.255.0
-IPADDR=207.200.5.2
-
-
-Troubleshooting
-===============
-Q1. Source files contain ^ M behind every line.
-       Make sure all files are Unix file format (no LF). Try the following
-    shell command to convert files.
-
-       cat dl2k.c | col -b > dl2k.tmp
-       mv dl2k.tmp dl2k.c
-
-       OR
-
-       cat dl2k.c | tr -d "\r" > dl2k.tmp
-       mv dl2k.tmp dl2k.c
-
-Q2: Could not find header files (*.h) ?
-       To compile the driver, you need kernel header files. After
-    installing the kernel source, the header files are usually located in
-    /usr/src/linux/include, which is the default include directory configured
-    in Makefile. For some distributions, there is a copy of header files in
-    /usr/src/include/linux and /usr/src/include/asm, that you can change the
-    INCLUDEDIR in Makefile to /usr/include without installing kernel source.
-       Note that RH 7.0 didn't provide correct header files in /usr/include,
-    including those files will make a wrong version driver.
-
diff --git a/Documentation/networking/device_drivers/freescale/dpaa.rst b/Documentation/networking/device_drivers/freescale/dpaa.rst
new file mode 100644 (file)
index 0000000..241c6c6
--- /dev/null
@@ -0,0 +1,269 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================
+The QorIQ DPAA Ethernet Driver
+==============================
+
+Authors:
+- Madalin Bucur <madalin.bucur@nxp.com>
+- Camelia Groza <camelia.groza@nxp.com>
+
+.. Contents
+
+       - DPAA Ethernet Overview
+       - DPAA Ethernet Supported SoCs
+       - Configuring DPAA Ethernet in your kernel
+       - DPAA Ethernet Frame Processing
+       - DPAA Ethernet Features
+       - DPAA IRQ Affinity and Receive Side Scaling
+       - Debugging
+
+DPAA Ethernet Overview
+======================
+
+DPAA stands for Data Path Acceleration Architecture and it is a
+set of networking acceleration IPs that are available on several
+generations of SoCs, both on PowerPC and ARM64.
+
+The Freescale DPAA architecture consists of a series of hardware blocks
+that support Ethernet connectivity. The Ethernet driver depends upon the
+following drivers in the Linux kernel:
+
+ - Peripheral Access Memory Unit (PAMU) (* needed only for PPC platforms)
+    drivers/iommu/fsl_*
+ - Frame Manager (FMan)
+    drivers/net/ethernet/freescale/fman
+ - Queue Manager (QMan), Buffer Manager (BMan)
+    drivers/soc/fsl/qbman
+
+A simplified view of the dpaa_eth interfaces mapped to FMan MACs::
+
+  dpaa_eth       /eth0\     ...       /ethN\
+  driver        |      |             |      |
+  -------------   ----   -----------   ----   -------------
+       -Ports  / Tx  Rx \    ...    / Tx  Rx \
+  FMan        |          |         |          |
+       -MACs  |   MAC0   |         |   MACN   |
+            /   dtsec0   \  ...  /   dtsecN   \ (or tgec)
+           /              \     /              \(or memac)
+  ---------  --------------  ---  --------------  ---------
+      FMan, FMan Port, FMan SP, FMan MURAM drivers
+  ---------------------------------------------------------
+      FMan HW blocks: MURAM, MACs, Ports, SP
+  ---------------------------------------------------------
+
+The dpaa_eth relation to the QMan, BMan and FMan::
+
+             ________________________________
+  dpaa_eth   /            eth0                \
+  driver    /                                  \
+  ---------   -^-   -^-   -^-   ---    ---------
+  QMan driver / \   / \   / \  \   /  | BMan    |
+            |Rx | |Rx | |Tx | |Tx |  | driver  |
+  ---------  |Dfl| |Err| |Cnf| |FQs|  |         |
+  QMan HW    |FQ | |FQ | |FQs| |   |  |         |
+            /   \ /   \ /   \  \ /   |         |
+  ---------   ---   ---   ---   -v-    ---------
+           |        FMan QMI         |         |
+           | FMan HW       FMan BMI  | BMan HW |
+             -----------------------   --------
+
+where the acronyms used above (and in the code) are:
+
+=============== ===========================================================
+DPAA           Data Path Acceleration Architecture
+FMan           DPAA Frame Manager
+QMan           DPAA Queue Manager
+BMan           DPAA Buffers Manager
+QMI            QMan interface in FMan
+BMI            BMan interface in FMan
+FMan SP        FMan Storage Profiles
+MURAM          Multi-user RAM in FMan
+FQ             QMan Frame Queue
+Rx Dfl FQ      default reception FQ
+Rx Err FQ      Rx error frames FQ
+Tx Cnf FQ      Tx confirmation FQs
+Tx FQs                 transmission frame queues
+dtsec          datapath three speed Ethernet controller (10/100/1000 Mbps)
+tgec           ten gigabit Ethernet controller (10 Gbps)
+memac          multirate Ethernet MAC (10/100/1000/10000)
+=============== ===========================================================
+
+DPAA Ethernet Supported SoCs
+============================
+
+The DPAA drivers enable the Ethernet controllers present on the following SoCs:
+
+PPC
+- P1023
+- P2041
+- P3041
+- P4080
+- P5020
+- P5040
+- T1023
+- T1024
+- T1040
+- T1042
+- T2080
+- T4240
+- B4860
+
+ARM
+- LS1043A
+- LS1046A
+
+Configuring DPAA Ethernet in your kernel
+========================================
+
+To enable the DPAA Ethernet driver, the following Kconfig options are required::
+
+  # common for arch/arm64 and arch/powerpc platforms
+  CONFIG_FSL_DPAA=y
+  CONFIG_FSL_FMAN=y
+  CONFIG_FSL_DPAA_ETH=y
+  CONFIG_FSL_XGMAC_MDIO=y
+
+  # for arch/powerpc only
+  CONFIG_FSL_PAMU=y
+
+  # common options needed for the PHYs used on the RDBs
+  CONFIG_VITESSE_PHY=y
+  CONFIG_REALTEK_PHY=y
+  CONFIG_AQUANTIA_PHY=y
+
+DPAA Ethernet Frame Processing
+==============================
+
+On Rx, buffers for the incoming frames are retrieved from the buffers found
+in the dedicated interface buffer pool. The driver initializes and seeds these
+with one page buffers.
+
+On Tx, all transmitted frames are returned to the driver through Tx
+confirmation frame queues. The driver is then responsible for freeing the
+buffers. In order to do this properly, a backpointer is added to the buffer
+before transmission that points to the skb. When the buffer returns to the
+driver on a confirmation FQ, the skb can be correctly consumed.
+
+DPAA Ethernet Features
+======================
+
+Currently the DPAA Ethernet driver enables the basic features required for
+a Linux Ethernet driver. The support for advanced features will be added
+gradually.
+
+The driver has Rx and Tx checksum offloading for UDP and TCP. Currently the Rx
+checksum offload feature is enabled by default and cannot be controlled through
+ethtool. Also, rx-flow-hash and rx-hashing was added. The addition of RSS
+provides a big performance boost for the forwarding scenarios, allowing
+different traffic flows received by one interface to be processed by different
+CPUs in parallel.
+
+The driver has support for multiple prioritized Tx traffic classes. Priorities
+range from 0 (lowest) to 3 (highest). These are mapped to HW workqueues with
+strict priority levels. Each traffic class contains NR_CPU TX queues. By
+default, only one traffic class is enabled and the lowest priority Tx queues
+are used. Higher priority traffic classes can be enabled with the mqprio
+qdisc. For example, all four traffic classes are enabled on an interface with
+the following command. Furthermore, skb priority levels are mapped to traffic
+classes as follows:
+
+       * priorities 0 to 3 - traffic class 0 (low priority)
+       * priorities 4 to 7 - traffic class 1 (medium-low priority)
+       * priorities 8 to 11 - traffic class 2 (medium-high priority)
+       * priorities 12 to 15 - traffic class 3 (high priority)
+
+::
+
+  tc qdisc add dev <int> root handle 1: \
+        mqprio num_tc 4 map 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 hw 1
+
+DPAA IRQ Affinity and Receive Side Scaling
+==========================================
+
+Traffic coming on the DPAA Rx queues or on the DPAA Tx confirmation
+queues is seen by the CPU as ingress traffic on a certain portal.
+The DPAA QMan portal interrupts are affined each to a certain CPU.
+The same portal interrupt services all the QMan portal consumers.
+
+By default the DPAA Ethernet driver enables RSS, making use of the
+DPAA FMan Parser and Keygen blocks to distribute traffic on 128
+hardware frame queues using a hash on IP v4/v6 source and destination
+and L4 source and destination ports, in present in the received frame.
+When RSS is disabled, all traffic received by a certain interface is
+received on the default Rx frame queue. The default DPAA Rx frame
+queues are configured to put the received traffic into a pool channel
+that allows any available CPU portal to dequeue the ingress traffic.
+The default frame queues have the HOLDACTIVE option set, ensuring that
+traffic bursts from a certain queue are serviced by the same CPU.
+This ensures a very low rate of frame reordering. A drawback of this
+is that only one CPU at a time can service the traffic received by a
+certain interface when RSS is not enabled.
+
+To implement RSS, the DPAA Ethernet driver allocates an extra set of
+128 Rx frame queues that are configured to dedicated channels, in a
+round-robin manner. The mapping of the frame queues to CPUs is now
+hardcoded, there is no indirection table to move traffic for a certain
+FQ (hash result) to another CPU. The ingress traffic arriving on one
+of these frame queues will arrive at the same portal and will always
+be processed by the same CPU. This ensures intra-flow order preservation
+and workload distribution for multiple traffic flows.
+
+RSS can be turned off for a certain interface using ethtool, i.e.::
+
+       # ethtool -N fm1-mac9 rx-flow-hash tcp4 ""
+
+To turn it back on, one needs to set rx-flow-hash for tcp4/6 or udp4/6::
+
+       # ethtool -N fm1-mac9 rx-flow-hash udp4 sfdn
+
+There is no independent control for individual protocols, any command
+run for one of tcp4|udp4|ah4|esp4|sctp4|tcp6|udp6|ah6|esp6|sctp6 is
+going to control the rx-flow-hashing for all protocols on that interface.
+
+Besides using the FMan Keygen computed hash for spreading traffic on the
+128 Rx FQs, the DPAA Ethernet driver also sets the skb hash value when
+the NETIF_F_RXHASH feature is on (active by default). This can be turned
+on or off through ethtool, i.e.::
+
+       # ethtool -K fm1-mac9 rx-hashing off
+       # ethtool -k fm1-mac9 | grep hash
+       receive-hashing: off
+       # ethtool -K fm1-mac9 rx-hashing on
+       Actual changes:
+       receive-hashing: on
+       # ethtool -k fm1-mac9 | grep hash
+       receive-hashing: on
+
+Please note that Rx hashing depends upon the rx-flow-hashing being on
+for that interface - turning off rx-flow-hashing will also disable the
+rx-hashing (without ethtool reporting it as off as that depends on the
+NETIF_F_RXHASH feature flag).
+
+Debugging
+=========
+
+The following statistics are exported for each interface through ethtool:
+
+       - interrupt count per CPU
+       - Rx packets count per CPU
+       - Tx packets count per CPU
+       - Tx confirmed packets count per CPU
+       - Tx S/G frames count per CPU
+       - Tx error count per CPU
+       - Rx error count per CPU
+       - Rx error count per type
+       - congestion related statistics:
+
+               - congestion status
+               - time spent in congestion
+               - number of time the device entered congestion
+               - dropped packets count per cause
+
+The driver also exports the following information in sysfs:
+
+       - the FQ IDs for each FQ type
+         /sys/devices/platform/soc/<addr>.fman/<addr>.ethernet/dpaa-ethernet.<id>/net/fm<nr>-mac<nr>/fqids
+
+       - the ID of the buffer pool in use
+         /sys/devices/platform/soc/<addr>.fman/<addr>.ethernet/dpaa-ethernet.<id>/net/fm<nr>-mac<nr>/bpids
diff --git a/Documentation/networking/device_drivers/freescale/dpaa.txt b/Documentation/networking/device_drivers/freescale/dpaa.txt
deleted file mode 100644 (file)
index b06601f..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-The QorIQ DPAA Ethernet Driver
-==============================
-
-Authors:
-Madalin Bucur <madalin.bucur@nxp.com>
-Camelia Groza <camelia.groza@nxp.com>
-
-Contents
-========
-
-       - DPAA Ethernet Overview
-       - DPAA Ethernet Supported SoCs
-       - Configuring DPAA Ethernet in your kernel
-       - DPAA Ethernet Frame Processing
-       - DPAA Ethernet Features
-       - DPAA IRQ Affinity and Receive Side Scaling
-       - Debugging
-
-DPAA Ethernet Overview
-======================
-
-DPAA stands for Data Path Acceleration Architecture and it is a
-set of networking acceleration IPs that are available on several
-generations of SoCs, both on PowerPC and ARM64.
-
-The Freescale DPAA architecture consists of a series of hardware blocks
-that support Ethernet connectivity. The Ethernet driver depends upon the
-following drivers in the Linux kernel:
-
- - Peripheral Access Memory Unit (PAMU) (* needed only for PPC platforms)
-    drivers/iommu/fsl_*
- - Frame Manager (FMan)
-    drivers/net/ethernet/freescale/fman
- - Queue Manager (QMan), Buffer Manager (BMan)
-    drivers/soc/fsl/qbman
-
-A simplified view of the dpaa_eth interfaces mapped to FMan MACs:
-
-  dpaa_eth       /eth0\     ...       /ethN\
-  driver        |      |             |      |
-  -------------   ----   -----------   ----   -------------
-       -Ports  / Tx  Rx \    ...    / Tx  Rx \
-  FMan        |          |         |          |
-       -MACs  |   MAC0   |         |   MACN   |
-             /   dtsec0   \  ...  /   dtsecN   \ (or tgec)
-            /              \     /              \(or memac)
-  ---------  --------------  ---  --------------  ---------
-      FMan, FMan Port, FMan SP, FMan MURAM drivers
-  ---------------------------------------------------------
-      FMan HW blocks: MURAM, MACs, Ports, SP
-  ---------------------------------------------------------
-
-The dpaa_eth relation to the QMan, BMan and FMan:
-              ________________________________
-  dpaa_eth   /            eth0                \
-  driver    /                                  \
-  ---------   -^-   -^-   -^-   ---    ---------
-  QMan driver / \   / \   / \  \   /  | BMan    |
-             |Rx | |Rx | |Tx | |Tx |  | driver  |
-  ---------  |Dfl| |Err| |Cnf| |FQs|  |         |
-  QMan HW    |FQ | |FQ | |FQs| |   |  |         |
-             /   \ /   \ /   \  \ /   |         |
-  ---------   ---   ---   ---   -v-    ---------
-            |        FMan QMI         |         |
-            | FMan HW       FMan BMI  | BMan HW |
-              -----------------------   --------
-
-where the acronyms used above (and in the code) are:
-DPAA = Data Path Acceleration Architecture
-FMan = DPAA Frame Manager
-QMan = DPAA Queue Manager
-BMan = DPAA Buffers Manager
-QMI = QMan interface in FMan
-BMI = BMan interface in FMan
-FMan SP = FMan Storage Profiles
-MURAM = Multi-user RAM in FMan
-FQ = QMan Frame Queue
-Rx Dfl FQ = default reception FQ
-Rx Err FQ = Rx error frames FQ
-Tx Cnf FQ = Tx confirmation FQs
-Tx FQs = transmission frame queues
-dtsec = datapath three speed Ethernet controller (10/100/1000 Mbps)
-tgec = ten gigabit Ethernet controller (10 Gbps)
-memac = multirate Ethernet MAC (10/100/1000/10000)
-
-DPAA Ethernet Supported SoCs
-============================
-
-The DPAA drivers enable the Ethernet controllers present on the following SoCs:
-
-# PPC
-P1023
-P2041
-P3041
-P4080
-P5020
-P5040
-T1023
-T1024
-T1040
-T1042
-T2080
-T4240
-B4860
-
-# ARM
-LS1043A
-LS1046A
-
-Configuring DPAA Ethernet in your kernel
-========================================
-
-To enable the DPAA Ethernet driver, the following Kconfig options are required:
-
-# common for arch/arm64 and arch/powerpc platforms
-CONFIG_FSL_DPAA=y
-CONFIG_FSL_FMAN=y
-CONFIG_FSL_DPAA_ETH=y
-CONFIG_FSL_XGMAC_MDIO=y
-
-# for arch/powerpc only
-CONFIG_FSL_PAMU=y
-
-# common options needed for the PHYs used on the RDBs
-CONFIG_VITESSE_PHY=y
-CONFIG_REALTEK_PHY=y
-CONFIG_AQUANTIA_PHY=y
-
-DPAA Ethernet Frame Processing
-==============================
-
-On Rx, buffers for the incoming frames are retrieved from the buffers found
-in the dedicated interface buffer pool. The driver initializes and seeds these
-with one page buffers.
-
-On Tx, all transmitted frames are returned to the driver through Tx
-confirmation frame queues. The driver is then responsible for freeing the
-buffers. In order to do this properly, a backpointer is added to the buffer
-before transmission that points to the skb. When the buffer returns to the
-driver on a confirmation FQ, the skb can be correctly consumed.
-
-DPAA Ethernet Features
-======================
-
-Currently the DPAA Ethernet driver enables the basic features required for
-a Linux Ethernet driver. The support for advanced features will be added
-gradually.
-
-The driver has Rx and Tx checksum offloading for UDP and TCP. Currently the Rx
-checksum offload feature is enabled by default and cannot be controlled through
-ethtool. Also, rx-flow-hash and rx-hashing was added. The addition of RSS
-provides a big performance boost for the forwarding scenarios, allowing
-different traffic flows received by one interface to be processed by different
-CPUs in parallel.
-
-The driver has support for multiple prioritized Tx traffic classes. Priorities
-range from 0 (lowest) to 3 (highest). These are mapped to HW workqueues with
-strict priority levels. Each traffic class contains NR_CPU TX queues. By
-default, only one traffic class is enabled and the lowest priority Tx queues
-are used. Higher priority traffic classes can be enabled with the mqprio
-qdisc. For example, all four traffic classes are enabled on an interface with
-the following command. Furthermore, skb priority levels are mapped to traffic
-classes as follows:
-
-       * priorities 0 to 3 - traffic class 0 (low priority)
-       * priorities 4 to 7 - traffic class 1 (medium-low priority)
-       * priorities 8 to 11 - traffic class 2 (medium-high priority)
-       * priorities 12 to 15 - traffic class 3 (high priority)
-
-tc qdisc add dev <int> root handle 1: \
-        mqprio num_tc 4 map 0 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3 hw 1
-
-DPAA IRQ Affinity and Receive Side Scaling
-==========================================
-
-Traffic coming on the DPAA Rx queues or on the DPAA Tx confirmation
-queues is seen by the CPU as ingress traffic on a certain portal.
-The DPAA QMan portal interrupts are affined each to a certain CPU.
-The same portal interrupt services all the QMan portal consumers.
-
-By default the DPAA Ethernet driver enables RSS, making use of the
-DPAA FMan Parser and Keygen blocks to distribute traffic on 128
-hardware frame queues using a hash on IP v4/v6 source and destination
-and L4 source and destination ports, in present in the received frame.
-When RSS is disabled, all traffic received by a certain interface is
-received on the default Rx frame queue. The default DPAA Rx frame
-queues are configured to put the received traffic into a pool channel
-that allows any available CPU portal to dequeue the ingress traffic.
-The default frame queues have the HOLDACTIVE option set, ensuring that
-traffic bursts from a certain queue are serviced by the same CPU.
-This ensures a very low rate of frame reordering. A drawback of this
-is that only one CPU at a time can service the traffic received by a
-certain interface when RSS is not enabled.
-
-To implement RSS, the DPAA Ethernet driver allocates an extra set of
-128 Rx frame queues that are configured to dedicated channels, in a
-round-robin manner. The mapping of the frame queues to CPUs is now
-hardcoded, there is no indirection table to move traffic for a certain
-FQ (hash result) to another CPU. The ingress traffic arriving on one
-of these frame queues will arrive at the same portal and will always
-be processed by the same CPU. This ensures intra-flow order preservation
-and workload distribution for multiple traffic flows.
-
-RSS can be turned off for a certain interface using ethtool, i.e.
-
-       # ethtool -N fm1-mac9 rx-flow-hash tcp4 ""
-
-To turn it back on, one needs to set rx-flow-hash for tcp4/6 or udp4/6:
-
-       # ethtool -N fm1-mac9 rx-flow-hash udp4 sfdn
-
-There is no independent control for individual protocols, any command
-run for one of tcp4|udp4|ah4|esp4|sctp4|tcp6|udp6|ah6|esp6|sctp6 is
-going to control the rx-flow-hashing for all protocols on that interface.
-
-Besides using the FMan Keygen computed hash for spreading traffic on the
-128 Rx FQs, the DPAA Ethernet driver also sets the skb hash value when
-the NETIF_F_RXHASH feature is on (active by default). This can be turned
-on or off through ethtool, i.e.:
-
-       # ethtool -K fm1-mac9 rx-hashing off
-       # ethtool -k fm1-mac9 | grep hash
-       receive-hashing: off
-       # ethtool -K fm1-mac9 rx-hashing on
-       Actual changes:
-       receive-hashing: on
-       # ethtool -k fm1-mac9 | grep hash
-       receive-hashing: on
-
-Please note that Rx hashing depends upon the rx-flow-hashing being on
-for that interface - turning off rx-flow-hashing will also disable the
-rx-hashing (without ethtool reporting it as off as that depends on the
-NETIF_F_RXHASH feature flag).
-
-Debugging
-=========
-
-The following statistics are exported for each interface through ethtool:
-
-       - interrupt count per CPU
-       - Rx packets count per CPU
-       - Tx packets count per CPU
-       - Tx confirmed packets count per CPU
-       - Tx S/G frames count per CPU
-       - Tx error count per CPU
-       - Rx error count per CPU
-       - Rx error count per type
-       - congestion related statistics:
-               - congestion status
-               - time spent in congestion
-               - number of time the device entered congestion
-               - dropped packets count per cause
-
-The driver also exports the following information in sysfs:
-
-       - the FQ IDs for each FQ type
-       /sys/devices/platform/soc/<addr>.fman/<addr>.ethernet/dpaa-ethernet.<id>/net/fm<nr>-mac<nr>/fqids
-
-       - the ID of the buffer pool in use
-       /sys/devices/platform/soc/<addr>.fman/<addr>.ethernet/dpaa-ethernet.<id>/net/fm<nr>-mac<nr>/bpids
diff --git a/Documentation/networking/device_drivers/freescale/gianfar.rst b/Documentation/networking/device_drivers/freescale/gianfar.rst
new file mode 100644 (file)
index 0000000..9c4a91d
--- /dev/null
@@ -0,0 +1,51 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================
+The Gianfar Ethernet Driver
+===========================
+
+:Author: Andy Fleming <afleming@freescale.com>
+:Updated: 2005-07-28
+
+
+Checksum Offloading
+===================
+
+The eTSEC controller (first included in parts from late 2005 like
+the 8548) has the ability to perform TCP, UDP, and IP checksums
+in hardware.  The Linux kernel only offloads the TCP and UDP
+checksums (and always performs the pseudo header checksums), so
+the driver only supports checksumming for TCP/IP and UDP/IP
+packets.  Use ethtool to enable or disable this feature for RX
+and TX.
+
+VLAN
+====
+
+In order to use VLAN, please consult Linux documentation on
+configuring VLANs.  The gianfar driver supports hardware insertion and
+extraction of VLAN headers, but not filtering.  Filtering will be
+done by the kernel.
+
+Multicasting
+============
+
+The gianfar driver supports using the group hash table on the
+TSEC (and the extended hash table on the eTSEC) for multicast
+filtering.  On the eTSEC, the exact-match MAC registers are used
+before the hash tables.  See Linux documentation on how to join
+multicast groups.
+
+Padding
+=======
+
+The gianfar driver supports padding received frames with 2 bytes
+to align the IP header to a 16-byte boundary, when supported by
+hardware.
+
+Ethtool
+=======
+
+The gianfar driver supports the use of ethtool for many
+configuration options.  You must run ethtool only on currently
+open interfaces.  See ethtool documentation for details.
diff --git a/Documentation/networking/device_drivers/freescale/gianfar.txt b/Documentation/networking/device_drivers/freescale/gianfar.txt
deleted file mode 100644 (file)
index ba1daea..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-The Gianfar Ethernet Driver
-
-Author: Andy Fleming <afleming@freescale.com>
-Updated: 2005-07-28
-
-
-CHECKSUM OFFLOADING
-
-The eTSEC controller (first included in parts from late 2005 like
-the 8548) has the ability to perform TCP, UDP, and IP checksums
-in hardware.  The Linux kernel only offloads the TCP and UDP
-checksums (and always performs the pseudo header checksums), so
-the driver only supports checksumming for TCP/IP and UDP/IP
-packets.  Use ethtool to enable or disable this feature for RX
-and TX.
-
-VLAN
-
-In order to use VLAN, please consult Linux documentation on
-configuring VLANs.  The gianfar driver supports hardware insertion and
-extraction of VLAN headers, but not filtering.  Filtering will be
-done by the kernel.
-
-MULTICASTING
-
-The gianfar driver supports using the group hash table on the
-TSEC (and the extended hash table on the eTSEC) for multicast
-filtering.  On the eTSEC, the exact-match MAC registers are used
-before the hash tables.  See Linux documentation on how to join
-multicast groups.
-
-PADDING
-
-The gianfar driver supports padding received frames with 2 bytes
-to align the IP header to a 16-byte boundary, when supported by
-hardware.
-
-ETHTOOL
-
-The gianfar driver supports the use of ethtool for many
-configuration options.  You must run ethtool only on currently
-open interfaces.  See ethtool documentation for details.
index a191faa..e18dad1 100644 (file)
@@ -27,6 +27,30 @@ Contents:
    netronome/nfp
    pensando/ionic
    stmicro/stmmac
+   3com/3c509
+   3com/vortex
+   amazon/ena
+   aquantia/atlantic
+   chelsio/cxgb
+   cirrus/cs89x0
+   davicom/dm9000
+   dec/de4x5
+   dec/dmfe
+   dlink/dl2k
+   freescale/dpaa
+   freescale/gianfar
+   intel/ipw2100
+   intel/ipw2200
+   microsoft/netvsc
+   neterion/s2io
+   neterion/vxge
+   qualcomm/rmnet
+   sb1000
+   smsc/smc9
+   ti/cpsw_switchdev
+   ti/cpsw
+   ti/tlan
+   toshiba/spider_net
 
 .. only::  subproject and html
 
index caf023c..3ac21e7 100644 (file)
@@ -33,7 +33,7 @@ The following features are now available in supported kernels:
  - SNMP
 
 Channel Bonding documentation can be found in the Linux kernel source:
-/Documentation/networking/bonding.txt
+/Documentation/networking/bonding.rst
 
 
 Identifying Your Adapter
diff --git a/Documentation/networking/device_drivers/intel/ipw2100.rst b/Documentation/networking/device_drivers/intel/ipw2100.rst
new file mode 100644 (file)
index 0000000..d54ad52
--- /dev/null
@@ -0,0 +1,323 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+===========================================
+Intel(R) PRO/Wireless 2100 Driver for Linux
+===========================================
+
+Support for:
+
+- Intel(R) PRO/Wireless 2100 Network Connection
+
+Copyright |copy| 2003-2006, Intel Corporation
+
+README.ipw2100
+
+:Version: git-1.1.5
+:Date:    January 25, 2006
+
+.. Index
+
+    0. IMPORTANT INFORMATION BEFORE USING THIS DRIVER
+    1. Introduction
+    2. Release git-1.1.5 Current Features
+    3. Command Line Parameters
+    4. Sysfs Helper Files
+    5. Radio Kill Switch
+    6. Dynamic Firmware
+    7. Power Management
+    8. Support
+    9. License
+
+
+0. IMPORTANT INFORMATION BEFORE USING THIS DRIVER
+=================================================
+
+Important Notice FOR ALL USERS OR DISTRIBUTORS!!!!
+
+Intel wireless LAN adapters are engineered, manufactured, tested, and
+quality checked to ensure that they meet all necessary local and
+governmental regulatory agency requirements for the regions that they
+are designated and/or marked to ship into. Since wireless LANs are
+generally unlicensed devices that share spectrum with radars,
+satellites, and other licensed and unlicensed devices, it is sometimes
+necessary to dynamically detect, avoid, and limit usage to avoid
+interference with these devices. In many instances Intel is required to
+provide test data to prove regional and local compliance to regional and
+governmental regulations before certification or approval to use the
+product is granted. Intel's wireless LAN's EEPROM, firmware, and
+software driver are designed to carefully control parameters that affect
+radio operation and to ensure electromagnetic compliance (EMC). These
+parameters include, without limitation, RF power, spectrum usage,
+channel scanning, and human exposure.
+
+For these reasons Intel cannot permit any manipulation by third parties
+of the software provided in binary format with the wireless WLAN
+adapters (e.g., the EEPROM and firmware). Furthermore, if you use any
+patches, utilities, or code with the Intel wireless LAN adapters that
+have been manipulated by an unauthorized party (i.e., patches,
+utilities, or code (including open source code modifications) which have
+not been validated by Intel), (i) you will be solely responsible for
+ensuring the regulatory compliance of the products, (ii) Intel will bear
+no liability, under any theory of liability for any issues associated
+with the modified products, including without limitation, claims under
+the warranty and/or issues arising from regulatory non-compliance, and
+(iii) Intel will not provide or be required to assist in providing
+support to any third parties for such modified products.
+
+Note: Many regulatory agencies consider Wireless LAN adapters to be
+modules, and accordingly, condition system-level regulatory approval
+upon receipt and review of test data documenting that the antennas and
+system configuration do not cause the EMC and radio operation to be
+non-compliant.
+
+The drivers available for download from SourceForge are provided as a
+part of a development project.  Conformance to local regulatory
+requirements is the responsibility of the individual developer.  As
+such, if you are interested in deploying or shipping a driver as part of
+solution intended to be used for purposes other than development, please
+obtain a tested driver from Intel Customer Support at:
+
+http://www.intel.com/support/wireless/sb/CS-006408.htm
+
+1. Introduction
+===============
+
+This document provides a brief overview of the features supported by the
+IPW2100 driver project.  The main project website, where the latest
+development version of the driver can be found, is:
+
+       http://ipw2100.sourceforge.net
+
+There you can find the not only the latest releases, but also information about
+potential fixes and patches, as well as links to the development mailing list
+for the driver project.
+
+
+2. Release git-1.1.5 Current Supported Features
+===============================================
+
+- Managed (BSS) and Ad-Hoc (IBSS)
+- WEP (shared key and open)
+- Wireless Tools support
+- 802.1x (tested with XSupplicant 1.0.1)
+
+Enabled (but not supported) features:
+- Monitor/RFMon mode
+- WPA/WPA2
+
+The distinction between officially supported and enabled is a reflection
+on the amount of validation and interoperability testing that has been
+performed on a given feature.
+
+
+3. Command Line Parameters
+==========================
+
+If the driver is built as a module, the following optional parameters are used
+by entering them on the command line with the modprobe command using this
+syntax::
+
+       modprobe ipw2100 [<option>=<VAL1><,VAL2>...]
+
+For example, to disable the radio on driver loading, enter:
+
+       modprobe ipw2100 disable=1
+
+The ipw2100 driver supports the following module parameters:
+
+=========      ==============  ============  ==============================
+Name           Value           Example       Meaning
+=========      ==============  ============  ==============================
+debug          0x0-0xffffffff  debug=1024    Debug level set to 1024
+mode           0,1,2           mode=1        AdHoc
+channel                int             channel=3     Only valid in AdHoc or Monitor
+associate      boolean         associate=0   Do NOT auto associate
+disable                boolean         disable=1     Do not power the HW
+=========      ==============  ============  ==============================
+
+
+4. Sysfs Helper Files
+=====================
+
+There are several ways to control the behavior of the driver.  Many of the
+general capabilities are exposed through the Wireless Tools (iwconfig).  There
+are a few capabilities that are exposed through entries in the Linux Sysfs.
+
+
+**Driver Level**
+
+For the driver level files, look in /sys/bus/pci/drivers/ipw2100/
+
+  debug_level
+       This controls the same global as the 'debug' module parameter.  For
+       information on the various debugging levels available, run the 'dvals'
+       script found in the driver source directory.
+
+       .. note::
+
+             'debug_level' is only enabled if CONFIG_IPW2100_DEBUG is turn on.
+
+**Device Level**
+
+For the device level files look in::
+
+       /sys/bus/pci/drivers/ipw2100/{PCI-ID}/
+
+For example::
+
+       /sys/bus/pci/drivers/ipw2100/0000:02:01.0
+
+For the device level files, see /sys/bus/pci/drivers/ipw2100:
+
+  rf_kill
+       read
+
+       ==  =========================================
+       0   RF kill not enabled (radio on)
+       1   SW based RF kill active (radio off)
+       2   HW based RF kill active (radio off)
+       3   Both HW and SW RF kill active (radio off)
+       ==  =========================================
+
+       write
+
+       ==  ==================================================
+       0   If SW based RF kill active, turn the radio back on
+       1   If radio is on, activate SW based RF kill
+       ==  ==================================================
+
+       .. note::
+
+          If you enable the SW based RF kill and then toggle the HW
+          based RF kill from ON -> OFF -> ON, the radio will NOT come back on
+
+
+5. Radio Kill Switch
+====================
+
+Most laptops provide the ability for the user to physically disable the radio.
+Some vendors have implemented this as a physical switch that requires no
+software to turn the radio off and on.  On other laptops, however, the switch
+is controlled through a button being pressed and a software driver then making
+calls to turn the radio off and on.  This is referred to as a "software based
+RF kill switch"
+
+See the Sysfs helper file 'rf_kill' for determining the state of the RF switch
+on your system.
+
+
+6. Dynamic Firmware
+===================
+
+As the firmware is licensed under a restricted use license, it can not be
+included within the kernel sources.  To enable the IPW2100 you will need a
+firmware image to load into the wireless NIC's processors.
+
+You can obtain these images from <http://ipw2100.sf.net/firmware.php>.
+
+See INSTALL for instructions on installing the firmware.
+
+
+7. Power Management
+===================
+
+The IPW2100 supports the configuration of the Power Save Protocol
+through a private wireless extension interface.  The IPW2100 supports
+the following different modes:
+
+       ===     ===========================================================
+       off     No power management.  Radio is always on.
+       on      Automatic power management
+       1-5     Different levels of power management.  The higher the
+               number the greater the power savings, but with an impact to
+               packet latencies.
+       ===     ===========================================================
+
+Power management works by powering down the radio after a certain
+interval of time has passed where no packets are passed through the
+radio.  Once powered down, the radio remains in that state for a given
+period of time.  For higher power savings, the interval between last
+packet processed to sleep is shorter and the sleep period is longer.
+
+When the radio is asleep, the access point sending data to the station
+must buffer packets at the AP until the station wakes up and requests
+any buffered packets.  If you have an AP that does not correctly support
+the PSP protocol you may experience packet loss or very poor performance
+while power management is enabled.  If this is the case, you will need
+to try and find a firmware update for your AP, or disable power
+management (via ``iwconfig eth1 power off``)
+
+To configure the power level on the IPW2100 you use a combination of
+iwconfig and iwpriv.  iwconfig is used to turn power management on, off,
+and set it to auto.
+
+       =========================  ====================================
+       iwconfig eth1 power off    Disables radio power down
+       iwconfig eth1 power on     Enables radio power management to
+                                  last set level (defaults to AUTO)
+       iwpriv eth1 set_power 0    Sets power level to AUTO and enables
+                                  power management if not previously
+                                  enabled.
+       iwpriv eth1 set_power 1-5  Set the power level as specified,
+                                  enabling power management if not
+                                  previously enabled.
+       =========================  ====================================
+
+You can view the current power level setting via::
+
+       iwpriv eth1 get_power
+
+It will return the current period or timeout that is configured as a string
+in the form of xxxx/yyyy (z) where xxxx is the timeout interval (amount of
+time after packet processing), yyyy is the period to sleep (amount of time to
+wait before powering the radio and querying the access point for buffered
+packets), and z is the 'power level'.  If power management is turned off the
+xxxx/yyyy will be replaced with 'off' -- the level reported will be the active
+level if `iwconfig eth1 power on` is invoked.
+
+
+8. Support
+==========
+
+For general development information and support,
+go to:
+
+    http://ipw2100.sf.net/
+
+The ipw2100 1.1.0 driver and firmware can be downloaded from:
+
+    http://support.intel.com
+
+For installation support on the ipw2100 1.1.0 driver on Linux kernels
+2.6.8 or greater, email support is available from:
+
+    http://supportmail.intel.com
+
+9. License
+==========
+
+  Copyright |copy| 2003 - 2006 Intel Corporation. All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License (version 2) as
+  published by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc., 59
+  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+  The full GNU General Public License is included in this distribution in the
+  file called LICENSE.
+
+  License Contact Information:
+
+  James P. Ketrenos <ipw2100-admin@linux.intel.com>
+
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
diff --git a/Documentation/networking/device_drivers/intel/ipw2100.txt b/Documentation/networking/device_drivers/intel/ipw2100.txt
deleted file mode 100644 (file)
index 6f85e1d..0000000
+++ /dev/null
@@ -1,293 +0,0 @@
-
-Intel(R) PRO/Wireless 2100 Driver for Linux in support of:
-
-Intel(R) PRO/Wireless 2100 Network Connection
-
-Copyright (C) 2003-2006, Intel Corporation
-
-README.ipw2100
-
-Version: git-1.1.5
-Date   : January 25, 2006
-
-Index
------------------------------------------------
-0. IMPORTANT INFORMATION BEFORE USING THIS DRIVER
-1. Introduction
-2. Release git-1.1.5 Current Features
-3. Command Line Parameters
-4. Sysfs Helper Files
-5. Radio Kill Switch
-6. Dynamic Firmware
-7. Power Management
-8. Support
-9. License
-
-
-0.   IMPORTANT INFORMATION BEFORE USING THIS DRIVER
------------------------------------------------
-
-Important Notice FOR ALL USERS OR DISTRIBUTORS!!!!
-
-Intel wireless LAN adapters are engineered, manufactured, tested, and
-quality checked to ensure that they meet all necessary local and
-governmental regulatory agency requirements for the regions that they
-are designated and/or marked to ship into. Since wireless LANs are
-generally unlicensed devices that share spectrum with radars,
-satellites, and other licensed and unlicensed devices, it is sometimes
-necessary to dynamically detect, avoid, and limit usage to avoid
-interference with these devices. In many instances Intel is required to
-provide test data to prove regional and local compliance to regional and
-governmental regulations before certification or approval to use the
-product is granted. Intel's wireless LAN's EEPROM, firmware, and
-software driver are designed to carefully control parameters that affect
-radio operation and to ensure electromagnetic compliance (EMC). These
-parameters include, without limitation, RF power, spectrum usage,
-channel scanning, and human exposure.
-
-For these reasons Intel cannot permit any manipulation by third parties
-of the software provided in binary format with the wireless WLAN
-adapters (e.g., the EEPROM and firmware). Furthermore, if you use any
-patches, utilities, or code with the Intel wireless LAN adapters that
-have been manipulated by an unauthorized party (i.e., patches,
-utilities, or code (including open source code modifications) which have
-not been validated by Intel), (i) you will be solely responsible for
-ensuring the regulatory compliance of the products, (ii) Intel will bear
-no liability, under any theory of liability for any issues associated
-with the modified products, including without limitation, claims under
-the warranty and/or issues arising from regulatory non-compliance, and
-(iii) Intel will not provide or be required to assist in providing
-support to any third parties for such modified products.
-
-Note: Many regulatory agencies consider Wireless LAN adapters to be
-modules, and accordingly, condition system-level regulatory approval
-upon receipt and review of test data documenting that the antennas and
-system configuration do not cause the EMC and radio operation to be
-non-compliant.
-
-The drivers available for download from SourceForge are provided as a
-part of a development project.  Conformance to local regulatory
-requirements is the responsibility of the individual developer.  As
-such, if you are interested in deploying or shipping a driver as part of
-solution intended to be used for purposes other than development, please
-obtain a tested driver from Intel Customer Support at:
-
-http://www.intel.com/support/wireless/sb/CS-006408.htm
-
-1. Introduction
------------------------------------------------
-
-This document provides a brief overview of the features supported by the 
-IPW2100 driver project.  The main project website, where the latest 
-development version of the driver can be found, is:
-
-       http://ipw2100.sourceforge.net
-
-There you can find the not only the latest releases, but also information about
-potential fixes and patches, as well as links to the development mailing list
-for the driver project.
-
-
-2. Release git-1.1.5 Current Supported Features
------------------------------------------------
-- Managed (BSS) and Ad-Hoc (IBSS)
-- WEP (shared key and open)
-- Wireless Tools support 
-- 802.1x (tested with XSupplicant 1.0.1)
-
-Enabled (but not supported) features:
-- Monitor/RFMon mode
-- WPA/WPA2
-
-The distinction between officially supported and enabled is a reflection
-on the amount of validation and interoperability testing that has been
-performed on a given feature.
-
-
-3. Command Line Parameters
------------------------------------------------
-
-If the driver is built as a module, the following optional parameters are used
-by entering them on the command line with the modprobe command using this
-syntax:
-
-       modprobe ipw2100 [<option>=<VAL1><,VAL2>...]
-
-For example, to disable the radio on driver loading, enter:
-
-       modprobe ipw2100 disable=1
-
-The ipw2100 driver supports the following module parameters:
-
-Name           Value           Example:
-debug          0x0-0xffffffff  debug=1024
-mode           0,1,2           mode=1   /* AdHoc */
-channel                int             channel=3 /* Only valid in AdHoc or Monitor */
-associate      boolean         associate=0 /* Do NOT auto associate */
-disable                boolean         disable=1 /* Do not power the HW */
-
-
-4. Sysfs Helper Files
----------------------------     
------------------------------------------------
-
-There are several ways to control the behavior of the driver.  Many of the 
-general capabilities are exposed through the Wireless Tools (iwconfig).  There
-are a few capabilities that are exposed through entries in the Linux Sysfs.
-
-
------ Driver Level ------
-For the driver level files, look in /sys/bus/pci/drivers/ipw2100/
-
-  debug_level  
-       
-       This controls the same global as the 'debug' module parameter.  For 
-        information on the various debugging levels available, run the 'dvals'
-       script found in the driver source directory.
-
-       NOTE:  'debug_level' is only enabled if CONFIG_IPW2100_DEBUG is turn
-              on.
-
------ Device Level ------
-For the device level files look in
-       
-       /sys/bus/pci/drivers/ipw2100/{PCI-ID}/
-
-For example:
-       /sys/bus/pci/drivers/ipw2100/0000:02:01.0
-
-For the device level files, see /sys/bus/pci/drivers/ipw2100:
-
-  rf_kill
-       read - 
-       0 = RF kill not enabled (radio on)
-       1 = SW based RF kill active (radio off)
-       2 = HW based RF kill active (radio off)
-       3 = Both HW and SW RF kill active (radio off)
-       write -
-       0 = If SW based RF kill active, turn the radio back on
-       1 = If radio is on, activate SW based RF kill
-
-       NOTE: If you enable the SW based RF kill and then toggle the HW
-       based RF kill from ON -> OFF -> ON, the radio will NOT come back on
-
-
-5. Radio Kill Switch
------------------------------------------------
-Most laptops provide the ability for the user to physically disable the radio.
-Some vendors have implemented this as a physical switch that requires no
-software to turn the radio off and on.  On other laptops, however, the switch
-is controlled through a button being pressed and a software driver then making
-calls to turn the radio off and on.  This is referred to as a "software based
-RF kill switch"
-
-See the Sysfs helper file 'rf_kill' for determining the state of the RF switch
-on your system.
-
-
-6. Dynamic Firmware
------------------------------------------------
-As the firmware is licensed under a restricted use license, it can not be 
-included within the kernel sources.  To enable the IPW2100 you will need a 
-firmware image to load into the wireless NIC's processors.
-
-You can obtain these images from <http://ipw2100.sf.net/firmware.php>.
-
-See INSTALL for instructions on installing the firmware.
-
-
-7. Power Management
------------------------------------------------
-The IPW2100 supports the configuration of the Power Save Protocol 
-through a private wireless extension interface.  The IPW2100 supports 
-the following different modes:
-
-       off     No power management.  Radio is always on.
-       on      Automatic power management
-       1-5     Different levels of power management.  The higher the 
-               number the greater the power savings, but with an impact to 
-               packet latencies. 
-
-Power management works by powering down the radio after a certain 
-interval of time has passed where no packets are passed through the 
-radio.  Once powered down, the radio remains in that state for a given 
-period of time.  For higher power savings, the interval between last 
-packet processed to sleep is shorter and the sleep period is longer.
-
-When the radio is asleep, the access point sending data to the station 
-must buffer packets at the AP until the station wakes up and requests 
-any buffered packets.  If you have an AP that does not correctly support 
-the PSP protocol you may experience packet loss or very poor performance 
-while power management is enabled.  If this is the case, you will need 
-to try and find a firmware update for your AP, or disable power 
-management (via `iwconfig eth1 power off`)
-
-To configure the power level on the IPW2100 you use a combination of 
-iwconfig and iwpriv.  iwconfig is used to turn power management on, off, 
-and set it to auto.
-
-       iwconfig eth1 power off    Disables radio power down
-       iwconfig eth1 power on     Enables radio power management to 
-                                  last set level (defaults to AUTO)
-       iwpriv eth1 set_power 0    Sets power level to AUTO and enables 
-                                  power management if not previously 
-                                  enabled.
-       iwpriv eth1 set_power 1-5  Set the power level as specified, 
-                                  enabling power management if not 
-                                  previously enabled.
-
-You can view the current power level setting via:
-       
-       iwpriv eth1 get_power
-
-It will return the current period or timeout that is configured as a string
-in the form of xxxx/yyyy (z) where xxxx is the timeout interval (amount of
-time after packet processing), yyyy is the period to sleep (amount of time to 
-wait before powering the radio and querying the access point for buffered
-packets), and z is the 'power level'.  If power management is turned off the
-xxxx/yyyy will be replaced with 'off' -- the level reported will be the active
-level if `iwconfig eth1 power on` is invoked.
-
-
-8. Support
------------------------------------------------
-
-For general development information and support,
-go to:
-       
-    http://ipw2100.sf.net/
-
-The ipw2100 1.1.0 driver and firmware can be downloaded from:  
-
-    http://support.intel.com
-
-For installation support on the ipw2100 1.1.0 driver on Linux kernels 
-2.6.8 or greater, email support is available from:  
-
-    http://supportmail.intel.com
-
-9. License
------------------------------------------------
-
-  Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
-
-  This program is free software; you can redistribute it and/or modify it 
-  under the terms of the GNU General Public License (version 2) as 
-  published by the Free Software Foundation.
-  
-  This program is distributed in the hope that it will be useful, but WITHOUT 
-  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
-  more details.
-  
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, write to the Free Software Foundation, Inc., 59 
-  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-  
-  The full GNU General Public License is included in this distribution in the
-  file called LICENSE.
-  
-  License Contact Information:
-  James P. Ketrenos <ipw2100-admin@linux.intel.com>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
diff --git a/Documentation/networking/device_drivers/intel/ipw2200.rst b/Documentation/networking/device_drivers/intel/ipw2200.rst
new file mode 100644 (file)
index 0000000..0cb42d2
--- /dev/null
@@ -0,0 +1,526 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+==============================================
+Intel(R) PRO/Wireless 2915ABG Driver for Linux
+==============================================
+
+
+Support for:
+
+- Intel(R) PRO/Wireless 2200BG Network Connection
+- Intel(R) PRO/Wireless 2915ABG Network Connection
+
+Note: The Intel(R) PRO/Wireless 2915ABG Driver for Linux and Intel(R)
+PRO/Wireless 2200BG Driver for Linux is a unified driver that works on
+both hardware adapters listed above. In this document the Intel(R)
+PRO/Wireless 2915ABG Driver for Linux will be used to reference the
+unified driver.
+
+Copyright |copy| 2004-2006, Intel Corporation
+
+README.ipw2200
+
+:Version: 1.1.2
+:Date: March 30, 2006
+
+
+.. Index
+
+    0.   IMPORTANT INFORMATION BEFORE USING THIS DRIVER
+    1.   Introduction
+    1.1. Overview of features
+    1.2. Module parameters
+    1.3. Wireless Extension Private Methods
+    1.4. Sysfs Helper Files
+    1.5. Supported channels
+    2.   Ad-Hoc Networking
+    3.   Interacting with Wireless Tools
+    3.1. iwconfig mode
+    3.2. iwconfig sens
+    4.   About the Version Numbers
+    5.   Firmware installation
+    6.   Support
+    7.   License
+
+
+0. IMPORTANT INFORMATION BEFORE USING THIS DRIVER
+=================================================
+
+Important Notice FOR ALL USERS OR DISTRIBUTORS!!!!
+
+Intel wireless LAN adapters are engineered, manufactured, tested, and
+quality checked to ensure that they meet all necessary local and
+governmental regulatory agency requirements for the regions that they
+are designated and/or marked to ship into. Since wireless LANs are
+generally unlicensed devices that share spectrum with radars,
+satellites, and other licensed and unlicensed devices, it is sometimes
+necessary to dynamically detect, avoid, and limit usage to avoid
+interference with these devices. In many instances Intel is required to
+provide test data to prove regional and local compliance to regional and
+governmental regulations before certification or approval to use the
+product is granted. Intel's wireless LAN's EEPROM, firmware, and
+software driver are designed to carefully control parameters that affect
+radio operation and to ensure electromagnetic compliance (EMC). These
+parameters include, without limitation, RF power, spectrum usage,
+channel scanning, and human exposure.
+
+For these reasons Intel cannot permit any manipulation by third parties
+of the software provided in binary format with the wireless WLAN
+adapters (e.g., the EEPROM and firmware). Furthermore, if you use any
+patches, utilities, or code with the Intel wireless LAN adapters that
+have been manipulated by an unauthorized party (i.e., patches,
+utilities, or code (including open source code modifications) which have
+not been validated by Intel), (i) you will be solely responsible for
+ensuring the regulatory compliance of the products, (ii) Intel will bear
+no liability, under any theory of liability for any issues associated
+with the modified products, including without limitation, claims under
+the warranty and/or issues arising from regulatory non-compliance, and
+(iii) Intel will not provide or be required to assist in providing
+support to any third parties for such modified products.
+
+Note: Many regulatory agencies consider Wireless LAN adapters to be
+modules, and accordingly, condition system-level regulatory approval
+upon receipt and review of test data documenting that the antennas and
+system configuration do not cause the EMC and radio operation to be
+non-compliant.
+
+The drivers available for download from SourceForge are provided as a
+part of a development project.  Conformance to local regulatory
+requirements is the responsibility of the individual developer.  As
+such, if you are interested in deploying or shipping a driver as part of
+solution intended to be used for purposes other than development, please
+obtain a tested driver from Intel Customer Support at:
+
+http://support.intel.com
+
+
+1. Introduction
+===============
+
+The following sections attempt to provide a brief introduction to using
+the Intel(R) PRO/Wireless 2915ABG Driver for Linux.
+
+This document is not meant to be a comprehensive manual on
+understanding or using wireless technologies, but should be sufficient
+to get you moving without wires on Linux.
+
+For information on building and installing the driver, see the INSTALL
+file.
+
+
+1.1. Overview of Features
+-------------------------
+The current release (1.1.2) supports the following features:
+
++ BSS mode (Infrastructure, Managed)
++ IBSS mode (Ad-Hoc)
++ WEP (OPEN and SHARED KEY mode)
++ 802.1x EAP via wpa_supplicant and xsupplicant
++ Wireless Extension support
++ Full B and G rate support (2200 and 2915)
++ Full A rate support (2915 only)
++ Transmit power control
++ S state support (ACPI suspend/resume)
+
+The following features are currently enabled, but not officially
+supported:
+
++ WPA
++ long/short preamble support
++ Monitor mode (aka RFMon)
+
+The distinction between officially supported and enabled is a reflection
+on the amount of validation and interoperability testing that has been
+performed on a given feature.
+
+
+
+1.2. Command Line Parameters
+----------------------------
+
+Like many modules used in the Linux kernel, the Intel(R) PRO/Wireless
+2915ABG Driver for Linux allows configuration options to be provided
+as module parameters.  The most common way to specify a module parameter
+is via the command line.
+
+The general form is::
+
+    % modprobe ipw2200 parameter=value
+
+Where the supported parameter are:
+
+  associate
+       Set to 0 to disable the auto scan-and-associate functionality of the
+       driver.  If disabled, the driver will not attempt to scan
+       for and associate to a network until it has been configured with
+       one or more properties for the target network, for example configuring
+       the network SSID.  Default is 0 (do not auto-associate)
+
+       Example: % modprobe ipw2200 associate=0
+
+  auto_create
+       Set to 0 to disable the auto creation of an Ad-Hoc network
+       matching the channel and network name parameters provided.
+       Default is 1.
+
+  channel
+       channel number for association.  The normal method for setting
+       the channel would be to use the standard wireless tools
+       (i.e. `iwconfig eth1 channel 10`), but it is useful sometimes
+       to set this while debugging.  Channel 0 means 'ANY'
+
+  debug
+       If using a debug build, this is used to control the amount of debug
+       info is logged.  See the 'dvals' and 'load' script for more info on
+       how to use this (the dvals and load scripts are provided as part
+       of the ipw2200 development snapshot releases available from the
+       SourceForge project at http://ipw2200.sf.net)
+
+  led
+       Can be used to turn on experimental LED code.
+       0 = Off, 1 = On.  Default is 1.
+
+  mode
+       Can be used to set the default mode of the adapter.
+       0 = Managed, 1 = Ad-Hoc, 2 = Monitor
+
+
+1.3. Wireless Extension Private Methods
+---------------------------------------
+
+As an interface designed to handle generic hardware, there are certain
+capabilities not exposed through the normal Wireless Tool interface.  As
+such, a provision is provided for a driver to declare custom, or
+private, methods.  The Intel(R) PRO/Wireless 2915ABG Driver for Linux
+defines several of these to configure various settings.
+
+The general form of using the private wireless methods is::
+
+       % iwpriv $IFNAME method parameters
+
+Where $IFNAME is the interface name the device is registered with
+(typically eth1, customized via one of the various network interface
+name managers, such as ifrename)
+
+The supported private methods are:
+
+  get_mode
+       Can be used to report out which IEEE mode the driver is
+       configured to support.  Example:
+
+       % iwpriv eth1 get_mode
+       eth1    get_mode:802.11bg (6)
+
+  set_mode
+       Can be used to configure which IEEE mode the driver will
+       support.
+
+       Usage::
+
+           % iwpriv eth1 set_mode {mode}
+
+       Where {mode} is a number in the range 1-7:
+
+       ==      =====================
+       1       802.11a (2915 only)
+       2       802.11b
+       3       802.11ab (2915 only)
+       4       802.11g
+       5       802.11ag (2915 only)
+       6       802.11bg
+       7       802.11abg (2915 only)
+       ==      =====================
+
+  get_preamble
+       Can be used to report configuration of preamble length.
+
+  set_preamble
+       Can be used to set the configuration of preamble length:
+
+       Usage::
+
+           % iwpriv eth1 set_preamble {mode}
+
+       Where {mode} is one of:
+
+       ==      ========================================
+       1       Long preamble only
+       0       Auto (long or short based on connection)
+       ==      ========================================
+
+
+1.4. Sysfs Helper Files
+-----------------------
+
+The Linux kernel provides a pseudo file system that can be used to
+access various components of the operating system.  The Intel(R)
+PRO/Wireless 2915ABG Driver for Linux exposes several configuration
+parameters through this mechanism.
+
+An entry in the sysfs can support reading and/or writing.  You can
+typically query the contents of a sysfs entry through the use of cat,
+and can set the contents via echo.  For example::
+
+    % cat /sys/bus/pci/drivers/ipw2200/debug_level
+
+Will report the current debug level of the driver's logging subsystem
+(only available if CONFIG_IPW2200_DEBUG was configured when the driver
+was built).
+
+You can set the debug level via::
+
+    % echo $VALUE > /sys/bus/pci/drivers/ipw2200/debug_level
+
+Where $VALUE would be a number in the case of this sysfs entry.  The
+input to sysfs files does not have to be a number.  For example, the
+firmware loader used by hotplug utilizes sysfs entries for transferring
+the firmware image from user space into the driver.
+
+The Intel(R) PRO/Wireless 2915ABG Driver for Linux exposes sysfs entries
+at two levels -- driver level, which apply to all instances of the driver
+(in the event that there are more than one device installed) and device
+level, which applies only to the single specific instance.
+
+
+1.4.1 Driver Level Sysfs Helper Files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For the driver level files, look in /sys/bus/pci/drivers/ipw2200/
+
+  debug_level
+       This controls the same global as the 'debug' module parameter
+
+
+
+1.4.2 Device Level Sysfs Helper Files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For the device level files, look in::
+
+       /sys/bus/pci/drivers/ipw2200/{PCI-ID}/
+
+For example:::
+
+       /sys/bus/pci/drivers/ipw2200/0000:02:01.0
+
+For the device level files, see /sys/bus/pci/drivers/ipw2200:
+
+  rf_kill
+       read -
+
+       ==  =========================================
+       0   RF kill not enabled (radio on)
+       1   SW based RF kill active (radio off)
+       2   HW based RF kill active (radio off)
+       3   Both HW and SW RF kill active (radio off)
+       ==  =========================================
+
+       write -
+
+       ==  ==================================================
+       0   If SW based RF kill active, turn the radio back on
+       1   If radio is on, activate SW based RF kill
+       ==  ==================================================
+
+       .. note::
+
+          If you enable the SW based RF kill and then toggle the HW
+          based RF kill from ON -> OFF -> ON, the radio will NOT come back on
+
+  ucode
+       read-only access to the ucode version number
+
+  led
+       read -
+
+       ==  =================
+       0   LED code disabled
+       1   LED code enabled
+       ==  =================
+
+       write -
+
+       ==  ================
+       0   Disable LED code
+       1   Enable LED code
+       ==  ================
+
+
+       .. note::
+
+          The LED code has been reported to hang some systems when
+          running ifconfig and is therefore disabled by default.
+
+
+1.5. Supported channels
+-----------------------
+
+Upon loading the Intel(R) PRO/Wireless 2915ABG Driver for Linux, a
+message stating the detected geography code and the number of 802.11
+channels supported by the card will be displayed in the log.
+
+The geography code corresponds to a regulatory domain as shown in the
+table below.
+
+       +------+----------------------------+--------------------+
+       |      |                            | Supported channels |
+       | Code |        Geography           +----------+---------+
+       |      |                            | 802.11bg | 802.11a |
+       +======+============================+==========+=========+
+       | ---  | Restricted                 |  11      |   0     |
+       +------+----------------------------+----------+---------+
+       | ZZF  | Custom US/Canada           |  11      |   8     |
+       +------+----------------------------+----------+---------+
+       | ZZD  | Rest of World              |  13      |   0     |
+       +------+----------------------------+----------+---------+
+       | ZZA  | Custom USA & Europe & High |  11      |  13     |
+       +------+----------------------------+----------+---------+
+       | ZZB  | Custom NA & Europe         |  11      |  13     |
+       +------+----------------------------+----------+---------+
+       | ZZC  | Custom Japan               |  11      |   4     |
+       +------+----------------------------+----------+---------+
+       | ZZM  | Custom                     |  11      |   0     |
+       +------+----------------------------+----------+---------+
+       | ZZE  | Europe                     |  13      |  19     |
+       +------+----------------------------+----------+---------+
+       | ZZJ  | Custom Japan               |  14      |   4     |
+       +------+----------------------------+----------+---------+
+       | ZZR  | Rest of World              |  14      |   0     |
+       +------+----------------------------+----------+---------+
+       | ZZH  | High Band                  |  13      |   4     |
+       +------+----------------------------+----------+---------+
+       | ZZG  | Custom Europe              |  13      |   4     |
+       +------+----------------------------+----------+---------+
+       | ZZK  | Europe                     |  13      |  24     |
+       +------+----------------------------+----------+---------+
+       | ZZL  | Europe                     |  11      |  13     |
+       +------+----------------------------+----------+---------+
+
+2.  Ad-Hoc Networking
+=====================
+
+When using a device in an Ad-Hoc network, it is useful to understand the
+sequence and requirements for the driver to be able to create, join, or
+merge networks.
+
+The following attempts to provide enough information so that you can
+have a consistent experience while using the driver as a member of an
+Ad-Hoc network.
+
+2.1. Joining an Ad-Hoc Network
+------------------------------
+
+The easiest way to get onto an Ad-Hoc network is to join one that
+already exists.
+
+2.2. Creating an Ad-Hoc Network
+-------------------------------
+
+An Ad-Hoc networks is created using the syntax of the Wireless tool.
+
+For Example:
+iwconfig eth1 mode ad-hoc essid testing channel 2
+
+2.3. Merging Ad-Hoc Networks
+----------------------------
+
+
+3. Interaction with Wireless Tools
+==================================
+
+3.1 iwconfig mode
+-----------------
+
+When configuring the mode of the adapter, all run-time configured parameters
+are reset to the value used when the module was loaded.  This includes
+channels, rates, ESSID, etc.
+
+3.2 iwconfig sens
+-----------------
+
+The 'iwconfig ethX sens XX' command will not set the signal sensitivity
+threshold, as described in iwconfig documentation, but rather the number
+of consecutive missed beacons that will trigger handover, i.e. roaming
+to another access point. At the same time, it will set the disassociation
+threshold to 3 times the given value.
+
+
+4.  About the Version Numbers
+=============================
+
+Due to the nature of open source development projects, there are
+frequently changes being incorporated that have not gone through
+a complete validation process.  These changes are incorporated into
+development snapshot releases.
+
+Releases are numbered with a three level scheme:
+
+       major.minor.development
+
+Any version where the 'development' portion is 0 (for example
+1.0.0, 1.1.0, etc.) indicates a stable version that will be made
+available for kernel inclusion.
+
+Any version where the 'development' portion is not a 0 (for
+example 1.0.1, 1.1.5, etc.) indicates a development version that is
+being made available for testing and cutting edge users.  The stability
+and functionality of the development releases are not know.  We make
+efforts to try and keep all snapshots reasonably stable, but due to the
+frequency of their release, and the desire to get those releases
+available as quickly as possible, unknown anomalies should be expected.
+
+The major version number will be incremented when significant changes
+are made to the driver.  Currently, there are no major changes planned.
+
+5. Firmware installation
+========================
+
+The driver requires a firmware image, download it and extract the
+files under /lib/firmware (or wherever your hotplug's firmware.agent
+will look for firmware files)
+
+The firmware can be downloaded from the following URL:
+
+    http://ipw2200.sf.net/
+
+
+6. Support
+==========
+
+For direct support of the 1.0.0 version, you can contact
+http://supportmail.intel.com, or you can use the open source project
+support.
+
+For general information and support, go to:
+
+    http://ipw2200.sf.net/
+
+
+7. License
+==========
+
+  Copyright |copy| 2003 - 2006 Intel Corporation. All rights reserved.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 2 as
+  published by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc., 59
+  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+  The full GNU General Public License is included in this distribution in the
+  file called LICENSE.
+
+  Contact Information:
+
+  James P. Ketrenos <ipw2100-admin@linux.intel.com>
+
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
diff --git a/Documentation/networking/device_drivers/intel/ipw2200.txt b/Documentation/networking/device_drivers/intel/ipw2200.txt
deleted file mode 100644 (file)
index b7658be..0000000
+++ /dev/null
@@ -1,472 +0,0 @@
-
-Intel(R) PRO/Wireless 2915ABG Driver for Linux in support of:
-
-Intel(R) PRO/Wireless 2200BG Network Connection
-Intel(R) PRO/Wireless 2915ABG Network Connection
-
-Note: The Intel(R) PRO/Wireless 2915ABG Driver for Linux and Intel(R)
-PRO/Wireless 2200BG Driver for Linux is a unified driver that works on
-both hardware adapters listed above. In this document the Intel(R)
-PRO/Wireless 2915ABG Driver for Linux will be used to reference the
-unified driver.
-
-Copyright (C) 2004-2006, Intel Corporation
-
-README.ipw2200
-
-Version: 1.1.2
-Date   : March 30, 2006
-
-
-Index
------------------------------------------------
-0.   IMPORTANT INFORMATION BEFORE USING THIS DRIVER
-1.   Introduction
-1.1. Overview of features
-1.2. Module parameters
-1.3. Wireless Extension Private Methods
-1.4. Sysfs Helper Files
-1.5. Supported channels
-2.   Ad-Hoc Networking
-3.   Interacting with Wireless Tools
-3.1. iwconfig mode
-3.2. iwconfig sens
-4.   About the Version Numbers
-5.   Firmware installation
-6.   Support
-7.   License
-
-
-0.   IMPORTANT INFORMATION BEFORE USING THIS DRIVER
------------------------------------------------
-
-Important Notice FOR ALL USERS OR DISTRIBUTORS!!!! 
-
-Intel wireless LAN adapters are engineered, manufactured, tested, and
-quality checked to ensure that they meet all necessary local and
-governmental regulatory agency requirements for the regions that they
-are designated and/or marked to ship into. Since wireless LANs are
-generally unlicensed devices that share spectrum with radars,
-satellites, and other licensed and unlicensed devices, it is sometimes
-necessary to dynamically detect, avoid, and limit usage to avoid
-interference with these devices. In many instances Intel is required to
-provide test data to prove regional and local compliance to regional and
-governmental regulations before certification or approval to use the
-product is granted. Intel's wireless LAN's EEPROM, firmware, and
-software driver are designed to carefully control parameters that affect
-radio operation and to ensure electromagnetic compliance (EMC). These
-parameters include, without limitation, RF power, spectrum usage,
-channel scanning, and human exposure. 
-
-For these reasons Intel cannot permit any manipulation by third parties
-of the software provided in binary format with the wireless WLAN
-adapters (e.g., the EEPROM and firmware). Furthermore, if you use any
-patches, utilities, or code with the Intel wireless LAN adapters that
-have been manipulated by an unauthorized party (i.e., patches,
-utilities, or code (including open source code modifications) which have
-not been validated by Intel), (i) you will be solely responsible for
-ensuring the regulatory compliance of the products, (ii) Intel will bear
-no liability, under any theory of liability for any issues associated
-with the modified products, including without limitation, claims under
-the warranty and/or issues arising from regulatory non-compliance, and
-(iii) Intel will not provide or be required to assist in providing
-support to any third parties for such modified products.  
-
-Note: Many regulatory agencies consider Wireless LAN adapters to be
-modules, and accordingly, condition system-level regulatory approval
-upon receipt and review of test data documenting that the antennas and
-system configuration do not cause the EMC and radio operation to be
-non-compliant.
-
-The drivers available for download from SourceForge are provided as a 
-part of a development project.  Conformance to local regulatory 
-requirements is the responsibility of the individual developer.  As 
-such, if you are interested in deploying or shipping a driver as part of 
-solution intended to be used for purposes other than development, please 
-obtain a tested driver from Intel Customer Support at:
-
-http://support.intel.com
-
-
-1.   Introduction
------------------------------------------------
-The following sections attempt to provide a brief introduction to using 
-the Intel(R) PRO/Wireless 2915ABG Driver for Linux.
-
-This document is not meant to be a comprehensive manual on 
-understanding or using wireless technologies, but should be sufficient 
-to get you moving without wires on Linux.
-
-For information on building and installing the driver, see the INSTALL
-file.
-
-
-1.1. Overview of Features
------------------------------------------------
-The current release (1.1.2) supports the following features:
-
-+ BSS mode (Infrastructure, Managed)
-+ IBSS mode (Ad-Hoc)
-+ WEP (OPEN and SHARED KEY mode)
-+ 802.1x EAP via wpa_supplicant and xsupplicant
-+ Wireless Extension support 
-+ Full B and G rate support (2200 and 2915)
-+ Full A rate support (2915 only)
-+ Transmit power control
-+ S state support (ACPI suspend/resume)
-
-The following features are currently enabled, but not officially
-supported:
-
-+ WPA
-+ long/short preamble support
-+ Monitor mode (aka RFMon)
-
-The distinction between officially supported and enabled is a reflection 
-on the amount of validation and interoperability testing that has been
-performed on a given feature. 
-
-
-
-1.2. Command Line Parameters
------------------------------------------------
-
-Like many modules used in the Linux kernel, the Intel(R) PRO/Wireless
-2915ABG Driver for Linux allows configuration options to be provided 
-as module parameters.  The most common way to specify a module parameter 
-is via the command line.  
-
-The general form is:
-
-% modprobe ipw2200 parameter=value
-
-Where the supported parameter are:
-
-  associate
-       Set to 0 to disable the auto scan-and-associate functionality of the
-       driver.  If disabled, the driver will not attempt to scan 
-       for and associate to a network until it has been configured with 
-       one or more properties for the target network, for example configuring 
-       the network SSID.  Default is 0 (do not auto-associate)
-       
-       Example: % modprobe ipw2200 associate=0
-
-  auto_create
-       Set to 0 to disable the auto creation of an Ad-Hoc network 
-       matching the channel and network name parameters provided.  
-       Default is 1.
-
-  channel
-       channel number for association.  The normal method for setting
-        the channel would be to use the standard wireless tools
-        (i.e. `iwconfig eth1 channel 10`), but it is useful sometimes
-       to set this while debugging.  Channel 0 means 'ANY'
-
-  debug
-       If using a debug build, this is used to control the amount of debug
-       info is logged.  See the 'dvals' and 'load' script for more info on
-       how to use this (the dvals and load scripts are provided as part 
-       of the ipw2200 development snapshot releases available from the 
-       SourceForge project at http://ipw2200.sf.net)
-  
-  led
-       Can be used to turn on experimental LED code.
-       0 = Off, 1 = On.  Default is 1.
-
-  mode
-       Can be used to set the default mode of the adapter.  
-       0 = Managed, 1 = Ad-Hoc, 2 = Monitor
-
-
-1.3. Wireless Extension Private Methods
------------------------------------------------
-
-As an interface designed to handle generic hardware, there are certain 
-capabilities not exposed through the normal Wireless Tool interface.  As 
-such, a provision is provided for a driver to declare custom, or 
-private, methods.  The Intel(R) PRO/Wireless 2915ABG Driver for Linux 
-defines several of these to configure various settings.
-
-The general form of using the private wireless methods is:
-
-       % iwpriv $IFNAME method parameters
-
-Where $IFNAME is the interface name the device is registered with 
-(typically eth1, customized via one of the various network interface
-name managers, such as ifrename)
-
-The supported private methods are:
-
-  get_mode
-       Can be used to report out which IEEE mode the driver is 
-       configured to support.  Example:
-       
-       % iwpriv eth1 get_mode
-       eth1    get_mode:802.11bg (6)
-
-  set_mode
-       Can be used to configure which IEEE mode the driver will 
-       support.  
-
-       Usage:
-       % iwpriv eth1 set_mode {mode}
-       Where {mode} is a number in the range 1-7:
-       1       802.11a (2915 only)
-       2       802.11b
-       3       802.11ab (2915 only)
-       4       802.11g 
-       5       802.11ag (2915 only)
-       6       802.11bg
-       7       802.11abg (2915 only)
-
-  get_preamble
-       Can be used to report configuration of preamble length.
-
-  set_preamble
-       Can be used to set the configuration of preamble length:
-
-       Usage:
-       % iwpriv eth1 set_preamble {mode}
-       Where {mode} is one of:
-       1       Long preamble only
-       0       Auto (long or short based on connection)
-       
-
-1.4. Sysfs Helper Files:
------------------------------------------------
-
-The Linux kernel provides a pseudo file system that can be used to 
-access various components of the operating system.  The Intel(R)
-PRO/Wireless 2915ABG Driver for Linux exposes several configuration
-parameters through this mechanism.
-
-An entry in the sysfs can support reading and/or writing.  You can 
-typically query the contents of a sysfs entry through the use of cat, 
-and can set the contents via echo.  For example:
-
-% cat /sys/bus/pci/drivers/ipw2200/debug_level
-
-Will report the current debug level of the driver's logging subsystem 
-(only available if CONFIG_IPW2200_DEBUG was configured when the driver
-was built).
-
-You can set the debug level via:
-
-% echo $VALUE > /sys/bus/pci/drivers/ipw2200/debug_level
-
-Where $VALUE would be a number in the case of this sysfs entry.  The 
-input to sysfs files does not have to be a number.  For example, the 
-firmware loader used by hotplug utilizes sysfs entries for transferring 
-the firmware image from user space into the driver.
-
-The Intel(R) PRO/Wireless 2915ABG Driver for Linux exposes sysfs entries 
-at two levels -- driver level, which apply to all instances of the driver 
-(in the event that there are more than one device installed) and device 
-level, which applies only to the single specific instance.
-
-
-1.4.1 Driver Level Sysfs Helper Files
------------------------------------------------
-
-For the driver level files, look in /sys/bus/pci/drivers/ipw2200/
-
-  debug_level  
-       
-       This controls the same global as the 'debug' module parameter
-
-
-
-1.4.2 Device Level Sysfs Helper Files
------------------------------------------------
-
-For the device level files, look in
-       
-       /sys/bus/pci/drivers/ipw2200/{PCI-ID}/
-
-For example:
-       /sys/bus/pci/drivers/ipw2200/0000:02:01.0
-
-For the device level files, see /sys/bus/pci/drivers/ipw2200:
-
-  rf_kill
-       read - 
-       0 = RF kill not enabled (radio on)
-       1 = SW based RF kill active (radio off)
-       2 = HW based RF kill active (radio off)
-       3 = Both HW and SW RF kill active (radio off)
-       write -
-       0 = If SW based RF kill active, turn the radio back on
-       1 = If radio is on, activate SW based RF kill
-
-       NOTE: If you enable the SW based RF kill and then toggle the HW
-       based RF kill from ON -> OFF -> ON, the radio will NOT come back on
-       
-  ucode 
-       read-only access to the ucode version number
-
-  led
-       read -
-       0 = LED code disabled
-       1 = LED code enabled
-       write -
-       0 = Disable LED code
-       1 = Enable LED code
-
-       NOTE: The LED code has been reported to hang some systems when 
-       running ifconfig and is therefore disabled by default.
-
-
-1.5. Supported channels
------------------------------------------------
-
-Upon loading the Intel(R) PRO/Wireless 2915ABG Driver for Linux, a
-message stating the detected geography code and the number of 802.11
-channels supported by the card will be displayed in the log.
-
-The geography code corresponds to a regulatory domain as shown in the
-table below.
-
-                                         Supported channels
-Code   Geography                       802.11bg        802.11a
-
----    Restricted                      11               0
-ZZF    Custom US/Canada                11               8
-ZZD    Rest of World                   13               0
-ZZA    Custom USA & Europe & High      11              13
-ZZB    Custom NA & Europe              11              13
-ZZC    Custom Japan                    11               4
-ZZM    Custom                          11               0
-ZZE    Europe                          13              19
-ZZJ    Custom Japan                    14               4
-ZZR    Rest of World                   14               0
-ZZH    High Band                       13               4
-ZZG    Custom Europe                   13               4
-ZZK    Europe                          13              24
-ZZL    Europe                          11              13
-
-
-2.   Ad-Hoc Networking
------------------------------------------------
-
-When using a device in an Ad-Hoc network, it is useful to understand the 
-sequence and requirements for the driver to be able to create, join, or 
-merge networks.
-
-The following attempts to provide enough information so that you can 
-have a consistent experience while using the driver as a member of an 
-Ad-Hoc network.
-
-2.1. Joining an Ad-Hoc Network
------------------------------------------------
-
-The easiest way to get onto an Ad-Hoc network is to join one that 
-already exists.
-
-2.2. Creating an Ad-Hoc Network
------------------------------------------------
-
-An Ad-Hoc networks is created using the syntax of the Wireless tool.
-
-For Example:
-iwconfig eth1 mode ad-hoc essid testing channel 2
-
-2.3. Merging Ad-Hoc Networks
------------------------------------------------
-
-
-3.  Interaction with Wireless Tools
------------------------------------------------
-
-3.1 iwconfig mode
------------------------------------------------
-
-When configuring the mode of the adapter, all run-time configured parameters
-are reset to the value used when the module was loaded.  This includes
-channels, rates, ESSID, etc.
-
-3.2 iwconfig sens
------------------------------------------------
-
-The 'iwconfig ethX sens XX' command will not set the signal sensitivity
-threshold, as described in iwconfig documentation, but rather the number
-of consecutive missed beacons that will trigger handover, i.e. roaming
-to another access point. At the same time, it will set the disassociation
-threshold to 3 times the given value.
-
-
-4.   About the Version Numbers
------------------------------------------------
-
-Due to the nature of open source development projects, there are 
-frequently changes being incorporated that have not gone through 
-a complete validation process.  These changes are incorporated into 
-development snapshot releases.
-
-Releases are numbered with a three level scheme: 
-
-       major.minor.development
-
-Any version where the 'development' portion is 0 (for example
-1.0.0, 1.1.0, etc.) indicates a stable version that will be made 
-available for kernel inclusion.
-
-Any version where the 'development' portion is not a 0 (for
-example 1.0.1, 1.1.5, etc.) indicates a development version that is
-being made available for testing and cutting edge users.  The stability 
-and functionality of the development releases are not know.  We make
-efforts to try and keep all snapshots reasonably stable, but due to the
-frequency of their release, and the desire to get those releases 
-available as quickly as possible, unknown anomalies should be expected.
-
-The major version number will be incremented when significant changes
-are made to the driver.  Currently, there are no major changes planned.
-
-5.  Firmware installation
-----------------------------------------------
-
-The driver requires a firmware image, download it and extract the
-files under /lib/firmware (or wherever your hotplug's firmware.agent
-will look for firmware files)
-
-The firmware can be downloaded from the following URL:
-
-    http://ipw2200.sf.net/
-
-
-6.  Support
------------------------------------------------
-
-For direct support of the 1.0.0 version, you can contact 
-http://supportmail.intel.com, or you can use the open source project
-support.
-
-For general information and support, go to:
-       
-    http://ipw2200.sf.net/
-
-
-7.  License
------------------------------------------------
-
-  Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
-
-  This program is free software; you can redistribute it and/or modify it 
-  under the terms of the GNU General Public License version 2 as 
-  published by the Free Software Foundation.
-  
-  This program is distributed in the hope that it will be useful, but WITHOUT 
-  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
-  more details.
-  
-  You should have received a copy of the GNU General Public License along with
-  this program; if not, write to the Free Software Foundation, Inc., 59 
-  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-  
-  The full GNU General Public License is included in this distribution in the
-  file called LICENSE.
-  
-  Contact Information:
-  James P. Ketrenos <ipw2100-admin@linux.intel.com>
-  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
-
index 9450182..ab624f1 100644 (file)
@@ -37,7 +37,7 @@ The following features are available in this kernel:
  - SNMP
 
 Channel Bonding documentation can be found in the Linux kernel source:
-/Documentation/networking/bonding.txt
+/Documentation/networking/bonding.rst
 
 The driver information previously displayed in the /proc filesystem is not
 supported in this release.  Alternatively, you can use ethtool (version 1.6
diff --git a/Documentation/networking/device_drivers/microsoft/netvsc.rst b/Documentation/networking/device_drivers/microsoft/netvsc.rst
new file mode 100644 (file)
index 0000000..c3f51c6
--- /dev/null
@@ -0,0 +1,116 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================
+Hyper-V network driver
+======================
+
+Compatibility
+=============
+
+This driver is compatible with Windows Server 2012 R2, 2016 and
+Windows 10.
+
+Features
+========
+
+Checksum offload
+----------------
+  The netvsc driver supports checksum offload as long as the
+  Hyper-V host version does. Windows Server 2016 and Azure
+  support checksum offload for TCP and UDP for both IPv4 and
+  IPv6. Windows Server 2012 only supports checksum offload for TCP.
+
+Receive Side Scaling
+--------------------
+  Hyper-V supports receive side scaling. For TCP & UDP, packets can
+  be distributed among available queues based on IP address and port
+  number.
+
+  For TCP & UDP, we can switch hash level between L3 and L4 by ethtool
+  command. TCP/UDP over IPv4 and v6 can be set differently. The default
+  hash level is L4. We currently only allow switching TX hash level
+  from within the guests.
+
+  On Azure, fragmented UDP packets have high loss rate with L4
+  hashing. Using L3 hashing is recommended in this case.
+
+  For example, for UDP over IPv4 on eth0:
+
+  To include UDP port numbers in hashing::
+
+       ethtool -N eth0 rx-flow-hash udp4 sdfn
+
+  To exclude UDP port numbers in hashing::
+
+       ethtool -N eth0 rx-flow-hash udp4 sd
+
+  To show UDP hash level::
+
+       ethtool -n eth0 rx-flow-hash udp4
+
+Generic Receive Offload, aka GRO
+--------------------------------
+  The driver supports GRO and it is enabled by default. GRO coalesces
+  like packets and significantly reduces CPU usage under heavy Rx
+  load.
+
+Large Receive Offload (LRO), or Receive Side Coalescing (RSC)
+-------------------------------------------------------------
+  The driver supports LRO/RSC in the vSwitch feature. It reduces the per packet
+  processing overhead by coalescing multiple TCP segments when possible. The
+  feature is enabled by default on VMs running on Windows Server 2019 and
+  later. It may be changed by ethtool command::
+
+       ethtool -K eth0 lro on
+       ethtool -K eth0 lro off
+
+SR-IOV support
+--------------
+  Hyper-V supports SR-IOV as a hardware acceleration option. If SR-IOV
+  is enabled in both the vSwitch and the guest configuration, then the
+  Virtual Function (VF) device is passed to the guest as a PCI
+  device. In this case, both a synthetic (netvsc) and VF device are
+  visible in the guest OS and both NIC's have the same MAC address.
+
+  The VF is enslaved by netvsc device.  The netvsc driver will transparently
+  switch the data path to the VF when it is available and up.
+  Network state (addresses, firewall, etc) should be applied only to the
+  netvsc device; the slave device should not be accessed directly in
+  most cases.  The exceptions are if some special queue discipline or
+  flow direction is desired, these should be applied directly to the
+  VF slave device.
+
+Receive Buffer
+--------------
+  Packets are received into a receive area which is created when device
+  is probed. The receive area is broken into MTU sized chunks and each may
+  contain one or more packets. The number of receive sections may be changed
+  via ethtool Rx ring parameters.
+
+  There is a similar send buffer which is used to aggregate packets for sending.
+  The send area is broken into chunks of 6144 bytes, each of section may
+  contain one or more packets. The send buffer is an optimization, the driver
+  will use slower method to handle very large packets or if the send buffer
+  area is exhausted.
+
+XDP support
+-----------
+  XDP (eXpress Data Path) is a feature that runs eBPF bytecode at the early
+  stage when packets arrive at a NIC card. The goal is to increase performance
+  for packet processing, reducing the overhead of SKB allocation and other
+  upper network layers.
+
+  hv_netvsc supports XDP in native mode, and transparently sets the XDP
+  program on the associated VF NIC as well.
+
+  Setting / unsetting XDP program on synthetic NIC (netvsc) propagates to
+  VF NIC automatically. Setting / unsetting XDP program on VF NIC directly
+  is not recommended, also not propagated to synthetic NIC, and may be
+  overwritten by setting of synthetic NIC.
+
+  XDP program cannot run with LRO (RSC) enabled, so you need to disable LRO
+  before running XDP::
+
+       ethtool -K eth0 lro off
+
+  XDP_REDIRECT action is not yet supported.
diff --git a/Documentation/networking/device_drivers/microsoft/netvsc.txt b/Documentation/networking/device_drivers/microsoft/netvsc.txt
deleted file mode 100644 (file)
index cd63556..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-Hyper-V network driver
-======================
-
-Compatibility
-=============
-
-This driver is compatible with Windows Server 2012 R2, 2016 and
-Windows 10.
-
-Features
-========
-
-  Checksum offload
-  ----------------
-  The netvsc driver supports checksum offload as long as the
-  Hyper-V host version does. Windows Server 2016 and Azure
-  support checksum offload for TCP and UDP for both IPv4 and
-  IPv6. Windows Server 2012 only supports checksum offload for TCP.
-
-  Receive Side Scaling
-  --------------------
-  Hyper-V supports receive side scaling. For TCP & UDP, packets can
-  be distributed among available queues based on IP address and port
-  number.
-
-  For TCP & UDP, we can switch hash level between L3 and L4 by ethtool
-  command. TCP/UDP over IPv4 and v6 can be set differently. The default
-  hash level is L4. We currently only allow switching TX hash level
-  from within the guests.
-
-  On Azure, fragmented UDP packets have high loss rate with L4
-  hashing. Using L3 hashing is recommended in this case.
-
-  For example, for UDP over IPv4 on eth0:
-  To include UDP port numbers in hashing:
-        ethtool -N eth0 rx-flow-hash udp4 sdfn
-  To exclude UDP port numbers in hashing:
-        ethtool -N eth0 rx-flow-hash udp4 sd
-  To show UDP hash level:
-        ethtool -n eth0 rx-flow-hash udp4
-
-  Generic Receive Offload, aka GRO
-  --------------------------------
-  The driver supports GRO and it is enabled by default. GRO coalesces
-  like packets and significantly reduces CPU usage under heavy Rx
-  load.
-
-  Large Receive Offload (LRO), or Receive Side Coalescing (RSC)
-  -------------------------------------------------------------
-  The driver supports LRO/RSC in the vSwitch feature. It reduces the per packet
-  processing overhead by coalescing multiple TCP segments when possible. The
-  feature is enabled by default on VMs running on Windows Server 2019 and
-  later. It may be changed by ethtool command:
-       ethtool -K eth0 lro on
-       ethtool -K eth0 lro off
-
-  SR-IOV support
-  --------------
-  Hyper-V supports SR-IOV as a hardware acceleration option. If SR-IOV
-  is enabled in both the vSwitch and the guest configuration, then the
-  Virtual Function (VF) device is passed to the guest as a PCI
-  device. In this case, both a synthetic (netvsc) and VF device are
-  visible in the guest OS and both NIC's have the same MAC address.
-
-  The VF is enslaved by netvsc device.  The netvsc driver will transparently
-  switch the data path to the VF when it is available and up.
-  Network state (addresses, firewall, etc) should be applied only to the
-  netvsc device; the slave device should not be accessed directly in
-  most cases.  The exceptions are if some special queue discipline or
-  flow direction is desired, these should be applied directly to the
-  VF slave device.
-
-  Receive Buffer
-  --------------
-  Packets are received into a receive area which is created when device
-  is probed. The receive area is broken into MTU sized chunks and each may
-  contain one or more packets. The number of receive sections may be changed
-  via ethtool Rx ring parameters.
-
-  There is a similar send buffer which is used to aggregate packets for sending.
-  The send area is broken into chunks of 6144 bytes, each of section may
-  contain one or more packets. The send buffer is an optimization, the driver
-  will use slower method to handle very large packets or if the send buffer
-  area is exhausted.
-
-  XDP support
-  -----------
-  XDP (eXpress Data Path) is a feature that runs eBPF bytecode at the early
-  stage when packets arrive at a NIC card. The goal is to increase performance
-  for packet processing, reducing the overhead of SKB allocation and other
-  upper network layers.
-
-  hv_netvsc supports XDP in native mode, and transparently sets the XDP
-  program on the associated VF NIC as well.
-
-  Setting / unsetting XDP program on synthetic NIC (netvsc) propagates to
-  VF NIC automatically. Setting / unsetting XDP program on VF NIC directly
-  is not recommended, also not propagated to synthetic NIC, and may be
-  overwritten by setting of synthetic NIC.
-
-  XDP program cannot run with LRO (RSC) enabled, so you need to disable LRO
-  before running XDP:
-       ethtool -K eth0 lro off
-
-  XDP_REDIRECT action is not yet supported.
diff --git a/Documentation/networking/device_drivers/neterion/s2io.rst b/Documentation/networking/device_drivers/neterion/s2io.rst
new file mode 100644 (file)
index 0000000..c5673ec
--- /dev/null
@@ -0,0 +1,196 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================================================
+Neterion's (Formerly S2io) Xframe I/II PCI-X 10GbE driver
+=========================================================
+
+Release notes for Neterion's (Formerly S2io) Xframe I/II PCI-X 10GbE driver.
+
+.. Contents
+  - 1.  Introduction
+  - 2.  Identifying the adapter/interface
+  - 3.  Features supported
+  - 4.  Command line parameters
+  - 5.  Performance suggestions
+  - 6.  Available Downloads
+
+
+1. Introduction
+===============
+This Linux driver supports Neterion's Xframe I PCI-X 1.0 and
+Xframe II PCI-X 2.0 adapters. It supports several features
+such as jumbo frames, MSI/MSI-X, checksum offloads, TSO, UFO and so on.
+See below for complete list of features.
+
+All features are supported for both IPv4 and IPv6.
+
+2. Identifying the adapter/interface
+====================================
+
+a. Insert the adapter(s) in your system.
+b. Build and load driver::
+
+       # insmod s2io.ko
+
+c. View log messages::
+
+       # dmesg | tail -40
+
+You will see messages similar to::
+
+       eth3: Neterion Xframe I 10GbE adapter (rev 3), Version 2.0.9.1, Intr type INTA
+       eth4: Neterion Xframe II 10GbE adapter (rev 2), Version 2.0.9.1, Intr type INTA
+       eth4: Device is on 64 bit 133MHz PCIX(M1) bus
+
+The above messages identify the adapter type(Xframe I/II), adapter revision,
+driver version, interface name(eth3, eth4), Interrupt type(INTA, MSI, MSI-X).
+In case of Xframe II, the PCI/PCI-X bus width and frequency are displayed
+as well.
+
+To associate an interface with a physical adapter use "ethtool -p <ethX>".
+The corresponding adapter's LED will blink multiple times.
+
+3. Features supported
+=====================
+a. Jumbo frames. Xframe I/II supports MTU up to 9600 bytes,
+   modifiable using ip command.
+
+b. Offloads. Supports checksum offload(TCP/UDP/IP) on transmit
+   and receive, TSO.
+
+c. Multi-buffer receive mode. Scattering of packet across multiple
+   buffers. Currently driver supports 2-buffer mode which yields
+   significant performance improvement on certain platforms(SGI Altix,
+   IBM xSeries).
+
+d. MSI/MSI-X. Can be enabled on platforms which support this feature
+   (IA64, Xeon) resulting in noticeable performance improvement(up to 7%
+   on certain platforms).
+
+e. Statistics. Comprehensive MAC-level and software statistics displayed
+   using "ethtool -S" option.
+
+f. Multi-FIFO/Ring. Supports up to 8 transmit queues and receive rings,
+   with multiple steering options.
+
+4. Command line parameters
+==========================
+
+a. tx_fifo_num
+       Number of transmit queues
+
+Valid range: 1-8
+
+Default: 1
+
+b. rx_ring_num
+       Number of receive rings
+
+Valid range: 1-8
+
+Default: 1
+
+c. tx_fifo_len
+       Size of each transmit queue
+
+Valid range: Total length of all queues should not exceed 8192
+
+Default: 4096
+
+d. rx_ring_sz
+       Size of each receive ring(in 4K blocks)
+
+Valid range: Limited by memory on system
+
+Default: 30
+
+e. intr_type
+       Specifies interrupt type. Possible values 0(INTA), 2(MSI-X)
+
+Valid values: 0, 2
+
+Default: 2
+
+5. Performance suggestions
+==========================
+
+General:
+
+a. Set MTU to maximum(9000 for switch setup, 9600 in back-to-back configuration)
+b. Set TCP windows size to optimal value.
+
+For instance, for MTU=1500 a value of 210K has been observed to result in
+good performance::
+
+       # sysctl -w net.ipv4.tcp_rmem="210000 210000 210000"
+       # sysctl -w net.ipv4.tcp_wmem="210000 210000 210000"
+
+For MTU=9000, TCP window size of 10 MB is recommended::
+
+       # sysctl -w net.ipv4.tcp_rmem="10000000 10000000 10000000"
+       # sysctl -w net.ipv4.tcp_wmem="10000000 10000000 10000000"
+
+Transmit performance:
+
+a. By default, the driver respects BIOS settings for PCI bus parameters.
+   However, you may want to experiment with PCI bus parameters
+   max-split-transactions(MOST) and MMRBC (use setpci command).
+
+   A MOST value of 2 has been found optimal for Opterons and 3 for Itanium.
+
+   It could be different for your hardware.
+
+   Set MMRBC to 4K**.
+
+   For example you can set
+
+   For opteron::
+
+       #setpci -d 17d5:* 62=1d
+
+   For Itanium::
+
+       #setpci -d 17d5:* 62=3d
+
+   For detailed description of the PCI registers, please see Xframe User Guide.
+
+b. Ensure Transmit Checksum offload is enabled. Use ethtool to set/verify this
+   parameter.
+
+c. Turn on TSO(using "ethtool -K")::
+
+       # ethtool -K <ethX> tso on
+
+Receive performance:
+
+a. By default, the driver respects BIOS settings for PCI bus parameters.
+   However, you may want to set PCI latency timer to 248::
+
+       #setpci -d 17d5:* LATENCY_TIMER=f8
+
+   For detailed description of the PCI registers, please see Xframe User Guide.
+
+b. Use 2-buffer mode. This results in large performance boost on
+   certain platforms(eg. SGI Altix, IBM xSeries).
+
+c. Ensure Receive Checksum offload is enabled. Use "ethtool -K ethX" command to
+   set/verify this option.
+
+d. Enable NAPI feature(in kernel configuration Device Drivers ---> Network
+   device support --->  Ethernet (10000 Mbit) ---> S2IO 10Gbe Xframe NIC) to
+   bring down CPU utilization.
+
+.. note::
+
+   For AMD opteron platforms with 8131 chipset, MMRBC=1 and MOST=1 are
+   recommended as safe parameters.
+
+For more information, please review the AMD8131 errata at
+http://vip.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/
+26310_AMD-8131_HyperTransport_PCI-X_Tunnel_Revision_Guide_rev_3_18.pdf
+
+6. Support
+==========
+
+For further support please contact either your 10GbE Xframe NIC vendor (IBM,
+HP, SGI etc.)
diff --git a/Documentation/networking/device_drivers/neterion/s2io.txt b/Documentation/networking/device_drivers/neterion/s2io.txt
deleted file mode 100644 (file)
index 0362a42..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-Release notes for Neterion's (Formerly S2io) Xframe I/II PCI-X 10GbE driver.
-
-Contents
-=======
-- 1.  Introduction
-- 2.  Identifying the adapter/interface
-- 3.  Features supported
-- 4.  Command line parameters
-- 5.  Performance suggestions
-- 6.  Available Downloads 
-
-
-1.     Introduction:
-This Linux driver supports Neterion's Xframe I PCI-X 1.0 and
-Xframe II PCI-X 2.0 adapters. It supports several features 
-such as jumbo frames, MSI/MSI-X, checksum offloads, TSO, UFO and so on.
-See below for complete list of features.
-All features are supported for both IPv4 and IPv6.
-
-2.     Identifying the adapter/interface:
-a. Insert the adapter(s) in your system.
-b. Build and load driver 
-# insmod s2io.ko
-c. View log messages
-# dmesg | tail -40
-You will see messages similar to:
-eth3: Neterion Xframe I 10GbE adapter (rev 3), Version 2.0.9.1, Intr type INTA
-eth4: Neterion Xframe II 10GbE adapter (rev 2), Version 2.0.9.1, Intr type INTA
-eth4: Device is on 64 bit 133MHz PCIX(M1) bus
-
-The above messages identify the adapter type(Xframe I/II), adapter revision,
-driver version, interface name(eth3, eth4), Interrupt type(INTA, MSI, MSI-X).
-In case of Xframe II, the PCI/PCI-X bus width and frequency are displayed
-as well.
-
-To associate an interface with a physical adapter use "ethtool -p <ethX>".
-The corresponding adapter's LED will blink multiple times.
-
-3.     Features supported:
-a. Jumbo frames. Xframe I/II supports MTU up to 9600 bytes,
-modifiable using ip command.
-
-b. Offloads. Supports checksum offload(TCP/UDP/IP) on transmit
-and receive, TSO.
-
-c. Multi-buffer receive mode. Scattering of packet across multiple
-buffers. Currently driver supports 2-buffer mode which yields
-significant performance improvement on certain platforms(SGI Altix,
-IBM xSeries).
-
-d. MSI/MSI-X. Can be enabled on platforms which support this feature
-(IA64, Xeon) resulting in noticeable performance improvement(up to 7%
-on certain platforms).
-
-e. Statistics. Comprehensive MAC-level and software statistics displayed
-using "ethtool -S" option.
-
-f. Multi-FIFO/Ring. Supports up to 8 transmit queues and receive rings,
-with multiple steering options.
-
-4.  Command line parameters
-a. tx_fifo_num
-Number of transmit queues
-Valid range: 1-8
-Default: 1
-
-b. rx_ring_num
-Number of receive rings
-Valid range: 1-8
-Default: 1
-
-c. tx_fifo_len
-Size of each transmit queue
-Valid range: Total length of all queues should not exceed 8192
-Default: 4096
-
-d. rx_ring_sz 
-Size of each receive ring(in 4K blocks)
-Valid range: Limited by memory on system
-Default: 30 
-
-e. intr_type
-Specifies interrupt type. Possible values 0(INTA), 2(MSI-X)
-Valid values: 0, 2
-Default: 2
-
-5.  Performance suggestions
-General:
-a. Set MTU to maximum(9000 for switch setup, 9600 in back-to-back configuration)
-b. Set TCP windows size to optimal value. 
-For instance, for MTU=1500 a value of 210K has been observed to result in 
-good performance.
-# sysctl -w net.ipv4.tcp_rmem="210000 210000 210000"
-# sysctl -w net.ipv4.tcp_wmem="210000 210000 210000"
-For MTU=9000, TCP window size of 10 MB is recommended.
-# sysctl -w net.ipv4.tcp_rmem="10000000 10000000 10000000"
-# sysctl -w net.ipv4.tcp_wmem="10000000 10000000 10000000"
-
-Transmit performance:
-a. By default, the driver respects BIOS settings for PCI bus parameters. 
-However, you may want to experiment with PCI bus parameters 
-max-split-transactions(MOST) and MMRBC (use setpci command). 
-A MOST value of 2 has been found optimal for Opterons and 3 for Itanium.  
-It could be different for your hardware.  
-Set MMRBC to 4K**.
-
-For example you can set 
-For opteron
-#setpci -d 17d5:* 62=1d 
-For Itanium
-#setpci -d 17d5:* 62=3d 
-
-For detailed description of the PCI registers, please see Xframe User Guide.
-
-b. Ensure Transmit Checksum offload is enabled. Use ethtool to set/verify this 
-parameter.
-c. Turn on TSO(using "ethtool -K")
-# ethtool -K <ethX> tso on
-
-Receive performance:
-a. By default, the driver respects BIOS settings for PCI bus parameters. 
-However, you may want to set PCI latency timer to 248.
-#setpci -d 17d5:* LATENCY_TIMER=f8
-For detailed description of the PCI registers, please see Xframe User Guide.
-b. Use 2-buffer mode. This results in large performance boost on
-certain platforms(eg. SGI Altix, IBM xSeries).
-c. Ensure Receive Checksum offload is enabled. Use "ethtool -K ethX" command to 
-set/verify this option.
-d. Enable NAPI feature(in kernel configuration Device Drivers ---> Network 
-device support --->  Ethernet (10000 Mbit) ---> S2IO 10Gbe Xframe NIC) to 
-bring down CPU utilization.
-
-** For AMD opteron platforms with 8131 chipset, MMRBC=1 and MOST=1 are 
-recommended as safe parameters.
-For more information, please review the AMD8131 errata at
-http://vip.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/
-26310_AMD-8131_HyperTransport_PCI-X_Tunnel_Revision_Guide_rev_3_18.pdf
-
-6. Support
-For further support please contact either your 10GbE Xframe NIC vendor (IBM, 
-HP, SGI etc.)
diff --git a/Documentation/networking/device_drivers/neterion/vxge.rst b/Documentation/networking/device_drivers/neterion/vxge.rst
new file mode 100644 (file)
index 0000000..589c6b1
--- /dev/null
@@ -0,0 +1,115 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================================================================
+Neterion's (Formerly S2io) X3100 Series 10GbE PCIe Server Adapter Linux driver
+==============================================================================
+
+.. Contents
+
+  1) Introduction
+  2) Features supported
+  3) Configurable driver parameters
+  4) Troubleshooting
+
+1. Introduction
+===============
+
+This Linux driver supports all Neterion's X3100 series 10 GbE PCIe I/O
+Virtualized Server adapters.
+
+The X3100 series supports four modes of operation, configurable via
+firmware:
+
+       - Single function mode
+       - Multi function mode
+       - SRIOV mode
+       - MRIOV mode
+
+The functions share a 10GbE link and the pci-e bus, but hardly anything else
+inside the ASIC. Features like independent hw reset, statistics, bandwidth/
+priority allocation and guarantees, GRO, TSO, interrupt moderation etc are
+supported independently on each function.
+
+(See below for a complete list of features supported for both IPv4 and IPv6)
+
+2. Features supported
+=====================
+
+i)   Single function mode (up to 17 queues)
+
+ii)  Multi function mode (up to 17 functions)
+
+iii) PCI-SIG's I/O Virtualization
+
+       - Single Root mode: v1.0 (up to 17 functions)
+       - Multi-Root mode: v1.0 (up to 17 functions)
+
+iv)  Jumbo frames
+
+       X3100 Series supports MTU up to 9600 bytes, modifiable using
+       ip command.
+
+v)   Offloads supported: (Enabled by default)
+
+       - Checksum offload (TCP/UDP/IP) on transmit and receive paths
+       - TCP Segmentation Offload (TSO) on transmit path
+       - Generic Receive Offload (GRO) on receive path
+
+vi)  MSI-X: (Enabled by default)
+
+       Resulting in noticeable performance improvement (up to 7% on certain
+       platforms).
+
+vii) NAPI: (Enabled by default)
+
+       For better Rx interrupt moderation.
+
+viii)RTH (Receive Traffic Hash): (Enabled by default)
+
+       Receive side steering for better scaling.
+
+ix)  Statistics
+
+       Comprehensive MAC-level and software statistics displayed using
+       "ethtool -S" option.
+
+x)   Multiple hardware queues: (Enabled by default)
+
+       Up to 17 hardware based transmit and receive data channels, with
+       multiple steering options (transmit multiqueue enabled by default).
+
+3) Configurable driver parameters:
+----------------------------------
+
+i)  max_config_dev
+       Specifies maximum device functions to be enabled.
+
+       Valid range: 1-8
+
+ii) max_config_port
+       Specifies number of ports to be enabled.
+
+       Valid range: 1,2
+
+       Default: 1
+
+iii) max_config_vpath
+       Specifies maximum VPATH(s) configured for each device function.
+
+       Valid range: 1-17
+
+iv) vlan_tag_strip
+       Enables/disables vlan tag stripping from all received tagged frames that
+       are not replicated at the internal L2 switch.
+
+       Valid range: 0,1 (disabled, enabled respectively)
+
+       Default: 1
+
+v)  addr_learn_en
+       Enable learning the mac address of the guest OS interface in
+       virtualization environment.
+
+       Valid range: 0,1 (disabled, enabled respectively)
+
+       Default: 0
diff --git a/Documentation/networking/device_drivers/neterion/vxge.txt b/Documentation/networking/device_drivers/neterion/vxge.txt
deleted file mode 100644 (file)
index abfec24..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-Neterion's (Formerly S2io) X3100 Series 10GbE PCIe Server Adapter Linux driver
-==============================================================================
-
-Contents
---------
-
-1) Introduction
-2) Features supported
-3) Configurable driver parameters
-4) Troubleshooting
-
-1) Introduction:
-----------------
-This Linux driver supports all Neterion's X3100 series 10 GbE PCIe I/O
-Virtualized Server adapters.
-The X3100 series supports four modes of operation, configurable via
-firmware -
-       Single function mode
-       Multi function mode
-       SRIOV mode
-       MRIOV mode
-The functions share a 10GbE link and the pci-e bus, but hardly anything else
-inside the ASIC. Features like independent hw reset, statistics, bandwidth/
-priority allocation and guarantees, GRO, TSO, interrupt moderation etc are
-supported independently on each function.
-
-(See below for a complete list of features supported for both IPv4 and IPv6)
-
-2) Features supported:
-----------------------
-
-i)   Single function mode (up to 17 queues)
-
-ii)  Multi function mode (up to 17 functions)
-
-iii) PCI-SIG's I/O Virtualization
-       - Single Root mode: v1.0 (up to 17 functions)
-       - Multi-Root mode: v1.0 (up to 17 functions)
-
-iv)  Jumbo frames
-       X3100 Series supports MTU up to 9600 bytes, modifiable using
-       ip command.
-
-v)   Offloads supported: (Enabled by default)
-       Checksum offload (TCP/UDP/IP) on transmit and receive paths
-       TCP Segmentation Offload (TSO) on transmit path
-       Generic Receive Offload (GRO) on receive path
-
-vi)  MSI-X: (Enabled by default)
-       Resulting in noticeable performance improvement (up to 7% on certain
-       platforms).
-
-vii) NAPI: (Enabled by default)
-       For better Rx interrupt moderation.
-
-viii)RTH (Receive Traffic Hash): (Enabled by default)
-       Receive side steering for better scaling.
-
-ix)  Statistics
-       Comprehensive MAC-level and software statistics displayed using
-       "ethtool -S" option.
-
-x)   Multiple hardware queues: (Enabled by default)
-       Up to 17 hardware based transmit and receive data channels, with
-       multiple steering options (transmit multiqueue enabled by default).
-
-3) Configurable driver parameters:
-----------------------------------
-
-i)  max_config_dev
-       Specifies maximum device functions to be enabled.
-       Valid range: 1-8
-
-ii) max_config_port
-       Specifies number of ports to be enabled.
-       Valid range: 1,2
-       Default: 1
-
-iii)max_config_vpath
-       Specifies maximum VPATH(s) configured for each device function.
-       Valid range: 1-17
-
-iv) vlan_tag_strip
-       Enables/disables vlan tag stripping from all received tagged frames that
-       are not replicated at the internal L2 switch.
-       Valid range: 0,1 (disabled, enabled respectively)
-       Default: 1
-
-v)  addr_learn_en
-       Enable learning the mac address of the guest OS interface in
-       virtualization environment.
-       Valid range: 0,1 (disabled, enabled respectively)
-       Default: 0
diff --git a/Documentation/networking/device_drivers/qualcomm/rmnet.rst b/Documentation/networking/device_drivers/qualcomm/rmnet.rst
new file mode 100644 (file)
index 0000000..70643b5
--- /dev/null
@@ -0,0 +1,95 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============
+Rmnet Driver
+============
+
+1. Introduction
+===============
+
+rmnet driver is used for supporting the Multiplexing and aggregation
+Protocol (MAP). This protocol is used by all recent chipsets using Qualcomm
+Technologies, Inc. modems.
+
+This driver can be used to register onto any physical network device in
+IP mode. Physical transports include USB, HSIC, PCIe and IP accelerator.
+
+Multiplexing allows for creation of logical netdevices (rmnet devices) to
+handle multiple private data networks (PDN) like a default internet, tethering,
+multimedia messaging service (MMS) or IP media subsystem (IMS). Hardware sends
+packets with MAP headers to rmnet. Based on the multiplexer id, rmnet
+routes to the appropriate PDN after removing the MAP header.
+
+Aggregation is required to achieve high data rates. This involves hardware
+sending aggregated bunch of MAP frames. rmnet driver will de-aggregate
+these MAP frames and send them to appropriate PDN's.
+
+2. Packet format
+================
+
+a. MAP packet (data / control)
+
+MAP header has the same endianness of the IP packet.
+
+Packet format::
+
+  Bit             0             1           2-7      8 - 15           16 - 31
+  Function   Command / Data   Reserved     Pad   Multiplexer ID    Payload length
+  Bit            32 - x
+  Function     Raw  Bytes
+
+Command (1)/ Data (0) bit value is to indicate if the packet is a MAP command
+or data packet. Control packet is used for transport level flow control. Data
+packets are standard IP packets.
+
+Reserved bits are usually zeroed out and to be ignored by receiver.
+
+Padding is number of bytes to be added for 4 byte alignment if required by
+hardware.
+
+Multiplexer ID is to indicate the PDN on which data has to be sent.
+
+Payload length includes the padding length but does not include MAP header
+length.
+
+b. MAP packet (command specific)::
+
+    Bit             0             1           2-7      8 - 15           16 - 31
+    Function   Command         Reserved     Pad   Multiplexer ID    Payload length
+    Bit          32 - 39        40 - 45    46 - 47       48 - 63
+    Function   Command name    Reserved   Command Type   Reserved
+    Bit          64 - 95
+    Function   Transaction ID
+    Bit          96 - 127
+    Function   Command data
+
+Command 1 indicates disabling flow while 2 is enabling flow
+
+Command types
+
+= ==========================================
+0 for MAP command request
+1 is to acknowledge the receipt of a command
+2 is for unsupported commands
+3 is for error during processing of commands
+= ==========================================
+
+c. Aggregation
+
+Aggregation is multiple MAP packets (can be data or command) delivered to
+rmnet in a single linear skb. rmnet will process the individual
+packets and either ACK the MAP command or deliver the IP packet to the
+network stack as needed
+
+MAP header|IP Packet|Optional padding|MAP header|IP Packet|Optional padding....
+
+MAP header|IP Packet|Optional padding|MAP header|Command Packet|Optional pad...
+
+3. Userspace configuration
+==========================
+
+rmnet userspace configuration is done through netlink library librmnetctl
+and command line utility rmnetcli. Utility is hosted in codeaurora forum git.
+The driver uses rtnl_link_ops for communication.
+
+https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/dataservices/tree/rmnetctl
diff --git a/Documentation/networking/device_drivers/qualcomm/rmnet.txt b/Documentation/networking/device_drivers/qualcomm/rmnet.txt
deleted file mode 100644 (file)
index 6b341ea..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-1. Introduction
-
-rmnet driver is used for supporting the Multiplexing and aggregation
-Protocol (MAP). This protocol is used by all recent chipsets using Qualcomm
-Technologies, Inc. modems.
-
-This driver can be used to register onto any physical network device in
-IP mode. Physical transports include USB, HSIC, PCIe and IP accelerator.
-
-Multiplexing allows for creation of logical netdevices (rmnet devices) to
-handle multiple private data networks (PDN) like a default internet, tethering,
-multimedia messaging service (MMS) or IP media subsystem (IMS). Hardware sends
-packets with MAP headers to rmnet. Based on the multiplexer id, rmnet
-routes to the appropriate PDN after removing the MAP header.
-
-Aggregation is required to achieve high data rates. This involves hardware
-sending aggregated bunch of MAP frames. rmnet driver will de-aggregate
-these MAP frames and send them to appropriate PDN's.
-
-2. Packet format
-
-a. MAP packet (data / control)
-
-MAP header has the same endianness of the IP packet.
-
-Packet format -
-
-Bit             0             1           2-7      8 - 15           16 - 31
-Function   Command / Data   Reserved     Pad   Multiplexer ID    Payload length
-Bit            32 - x
-Function     Raw  Bytes
-
-Command (1)/ Data (0) bit value is to indicate if the packet is a MAP command
-or data packet. Control packet is used for transport level flow control. Data
-packets are standard IP packets.
-
-Reserved bits are usually zeroed out and to be ignored by receiver.
-
-Padding is number of bytes to be added for 4 byte alignment if required by
-hardware.
-
-Multiplexer ID is to indicate the PDN on which data has to be sent.
-
-Payload length includes the padding length but does not include MAP header
-length.
-
-b. MAP packet (command specific)
-
-Bit             0             1           2-7      8 - 15           16 - 31
-Function   Command         Reserved     Pad   Multiplexer ID    Payload length
-Bit          32 - 39        40 - 45    46 - 47       48 - 63
-Function   Command name    Reserved   Command Type   Reserved
-Bit          64 - 95
-Function   Transaction ID
-Bit          96 - 127
-Function   Command data
-
-Command 1 indicates disabling flow while 2 is enabling flow
-
-Command types -
-0 for MAP command request
-1 is to acknowledge the receipt of a command
-2 is for unsupported commands
-3 is for error during processing of commands
-
-c. Aggregation
-
-Aggregation is multiple MAP packets (can be data or command) delivered to
-rmnet in a single linear skb. rmnet will process the individual
-packets and either ACK the MAP command or deliver the IP packet to the
-network stack as needed
-
-MAP header|IP Packet|Optional padding|MAP header|IP Packet|Optional padding....
-MAP header|IP Packet|Optional padding|MAP header|Command Packet|Optional pad...
-
-3. Userspace configuration
-
-rmnet userspace configuration is done through netlink library librmnetctl
-and command line utility rmnetcli. Utility is hosted in codeaurora forum git.
-The driver uses rtnl_link_ops for communication.
-
-https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/dataservices/tree/rmnetctl
diff --git a/Documentation/networking/device_drivers/sb1000.rst b/Documentation/networking/device_drivers/sb1000.rst
new file mode 100644 (file)
index 0000000..c8582ca
--- /dev/null
@@ -0,0 +1,222 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================
+SB100 device driver
+===================
+
+sb1000 is a module network device driver for the General Instrument (also known
+as NextLevel) SURFboard1000 internal cable modem board.  This is an ISA card
+which is used by a number of cable TV companies to provide cable modem access.
+It's a one-way downstream-only cable modem, meaning that your upstream net link
+is provided by your regular phone modem.
+
+This driver was written by Franco Venturi <fventuri@mediaone.net>.  He deserves
+a great deal of thanks for this wonderful piece of code!
+
+Needed tools
+============
+
+Support for this device is now a part of the standard Linux kernel.  The
+driver source code file is drivers/net/sb1000.c.  In addition to this
+you will need:
+
+1. The "cmconfig" program.  This is a utility which supplements "ifconfig"
+   to configure the cable modem and network interface (usually called "cm0");
+
+2. Several PPP scripts which live in /etc/ppp to make connecting via your
+   cable modem easy.
+
+   These utilities can be obtained from:
+
+      http://www.jacksonville.net/~fventuri/
+
+   in Franco's original source code distribution .tar.gz file.  Support for
+   the sb1000 driver can be found at:
+
+      - http://web.archive.org/web/%2E/http://home.adelphia.net/~siglercm/sb1000.html
+      - http://web.archive.org/web/%2E/http://linuxpower.cx/~cable/
+
+   along with these utilities.
+
+3. The standard isapnp tools.  These are necessary to configure your SB1000
+   card at boot time (or afterwards by hand) since it's a PnP card.
+
+   If you don't have these installed as a standard part of your Linux
+   distribution, you can find them at:
+
+      http://www.roestock.demon.co.uk/isapnptools/
+
+   or check your Linux distribution binary CD or their web site.  For help with
+   isapnp, pnpdump, or /etc/isapnp.conf, go to:
+
+      http://www.roestock.demon.co.uk/isapnptools/isapnpfaq.html
+
+Using the driver
+================
+
+To make the SB1000 card work, follow these steps:
+
+1. Run ``make config``, or ``make menuconfig``, or ``make xconfig``, whichever
+   you prefer, in the top kernel tree directory to set up your kernel
+   configuration.  Make sure to say "Y" to "Prompt for development drivers"
+   and to say "M" to the sb1000 driver.  Also say "Y" or "M" to all the standard
+   networking questions to get TCP/IP and PPP networking support.
+
+2. **BEFORE** you build the kernel, edit drivers/net/sb1000.c.  Make sure
+   to redefine the value of READ_DATA_PORT to match the I/O address used
+   by isapnp to access your PnP cards.  This is the value of READPORT in
+   /etc/isapnp.conf or given by the output of pnpdump.
+
+3. Build and install the kernel and modules as usual.
+
+4. Boot your new kernel following the usual procedures.
+
+5. Set up to configure the new SB1000 PnP card by capturing the output
+   of "pnpdump" to a file and editing this file to set the correct I/O ports,
+   IRQ, and DMA settings for all your PnP cards.  Make sure none of the settings
+   conflict with one another.  Then test this configuration by running the
+   "isapnp" command with your new config file as the input.  Check for
+   errors and fix as necessary.  (As an aside, I use I/O ports 0x110 and
+   0x310 and IRQ 11 for my SB1000 card and these work well for me.  YMMV.)
+   Then save the finished config file as /etc/isapnp.conf for proper
+   configuration on subsequent reboots.
+
+6. Download the original file sb1000-1.1.2.tar.gz from Franco's site or one of
+   the others referenced above.  As root, unpack it into a temporary directory
+   and do a ``make cmconfig`` and then ``install -c cmconfig /usr/local/sbin``.
+   Don't do ``make install`` because it expects to find all the utilities built
+   and ready for installation, not just cmconfig.
+
+7. As root, copy all the files under the ppp/ subdirectory in Franco's
+   tar file into /etc/ppp, being careful not to overwrite any files that are
+   already in there.  Then modify ppp@gi-on to set the correct login name,
+   phone number, and frequency for the cable modem.  Also edit pap-secrets
+   to specify your login name and password and any site-specific information
+   you need.
+
+8. Be sure to modify /etc/ppp/firewall to use ipchains instead of
+   the older ipfwadm commands from the 2.0.x kernels.  There's a neat utility to
+   convert ipfwadm commands to ipchains commands:
+
+       http://users.dhp.com/~whisper/ipfwadm2ipchains/
+
+   You may also wish to modify the firewall script to implement a different
+   firewalling scheme.
+
+9. Start the PPP connection via the script /etc/ppp/ppp@gi-on.  You must be
+   root to do this.  It's better to use a utility like sudo to execute
+   frequently used commands like this with root permissions if possible.  If you
+   connect successfully the cable modem interface will come up and you'll see a
+   driver message like this at the console::
+
+        cm0: sb1000 at (0x110,0x310), csn 1, S/N 0x2a0d16d8, IRQ 11.
+        sb1000.c:v1.1.2 6/01/98 (fventuri@mediaone.net)
+
+   The "ifconfig" command should show two new interfaces, ppp0 and cm0.
+
+   The command "cmconfig cm0" will give you information about the cable modem
+   interface.
+
+10. Try pinging a site via ``ping -c 5 www.yahoo.com``, for example.  You should
+    see packets received.
+
+11. If you can't get site names (like www.yahoo.com) to resolve into
+    IP addresses (like 204.71.200.67), be sure your /etc/resolv.conf file
+    has no syntax errors and has the right nameserver IP addresses in it.
+    If this doesn't help, try something like ``ping -c 5 204.71.200.67`` to
+    see if the networking is running but the DNS resolution is where the
+    problem lies.
+
+12. If you still have problems, go to the support web sites mentioned above
+    and read the information and documentation there.
+
+Common problems
+===============
+
+1. Packets go out on the ppp0 interface but don't come back on the cm0
+   interface.  It looks like I'm connected but I can't even ping any
+   numerical IP addresses.  (This happens predominantly on Debian systems due
+   to a default boot-time configuration script.)
+
+Solution
+   As root ``echo 0 > /proc/sys/net/ipv4/conf/cm0/rp_filter`` so it
+   can share the same IP address as the ppp0 interface.  Note that this
+   command should probably be added to the /etc/ppp/cablemodem script
+   *right*between* the "/sbin/ifconfig" and "/sbin/cmconfig" commands.
+   You may need to do this to /proc/sys/net/ipv4/conf/ppp0/rp_filter as well.
+   If you do this to /proc/sys/net/ipv4/conf/default/rp_filter on each reboot
+   (in rc.local or some such) then any interfaces can share the same IP
+   addresses.
+
+2. I get "unresolved symbol" error messages on executing ``insmod sb1000.o``.
+
+Solution
+   You probably have a non-matching kernel source tree and
+   /usr/include/linux and /usr/include/asm header files.  Make sure you
+   install the correct versions of the header files in these two directories.
+   Then rebuild and reinstall the kernel.
+
+3. When isapnp runs it reports an error, and my SB1000 card isn't working.
+
+Solution
+   There's a problem with later versions of isapnp using the "(CHECK)"
+   option in the lines that allocate the two I/O addresses for the SB1000 card.
+   This first popped up on RH 6.0.  Delete "(CHECK)" for the SB1000 I/O addresses.
+   Make sure they don't conflict with any other pieces of hardware first!  Then
+   rerun isapnp and go from there.
+
+4. I can't execute the /etc/ppp/ppp@gi-on file.
+
+Solution
+   As root do ``chmod ug+x /etc/ppp/ppp@gi-on``.
+
+5. The firewall script isn't working (with 2.2.x and higher kernels).
+
+Solution
+   Use the ipfwadm2ipchains script referenced above to convert the
+   /etc/ppp/firewall script from the deprecated ipfwadm commands to ipchains.
+
+6. I'm getting *tons* of firewall deny messages in the /var/kern.log,
+   /var/messages, and/or /var/syslog files, and they're filling up my /var
+   partition!!!
+
+Solution
+   First, tell your ISP that you're receiving DoS (Denial of Service)
+   and/or portscanning (UDP connection attempts) attacks!  Look over the deny
+   messages to figure out what the attack is and where it's coming from.  Next,
+   edit /etc/ppp/cablemodem and make sure the ",nobroadcast" option is turned on
+   to the "cmconfig" command (uncomment that line).  If you're not receiving these
+   denied packets on your broadcast interface (IP address xxx.yyy.zzz.255
+   typically), then someone is attacking your machine in particular.  Be careful
+   out there....
+
+7. Everything seems to work fine but my computer locks up after a while
+   (and typically during a lengthy download through the cable modem)!
+
+Solution
+   You may need to add a short delay in the driver to 'slow down' the
+   SURFboard because your PC might not be able to keep up with the transfer rate
+   of the SB1000. To do this, it's probably best to download Franco's
+   sb1000-1.1.2.tar.gz archive and build and install sb1000.o manually.  You'll
+   want to edit the 'Makefile' and look for the 'SB1000_DELAY'
+   define.  Uncomment those 'CFLAGS' lines (and comment out the default ones)
+   and try setting the delay to something like 60 microseconds with:
+   '-DSB1000_DELAY=60'.  Then do ``make`` and as root ``make install`` and try
+   it out.  If it still doesn't work or you like playing with the driver, you may
+   try other numbers.  Remember though that the higher the delay, the slower the
+   driver (which slows down the rest of the PC too when it is actively
+   used). Thanks to Ed Daiga for this tip!
+
+Credits
+=======
+
+This README came from Franco Venturi's original README file which is
+still supplied with his driver .tar.gz archive.  I and all other sb1000 users
+owe Franco a tremendous "Thank you!"  Additional thanks goes to Carl Patten
+and Ralph Bonnell who are now managing the Linux SB1000 web site, and to
+the SB1000 users who reported and helped debug the common problems listed
+above.
+
+
+                                       Clemmitt Sigler
+                                       csigler@vt.edu
diff --git a/Documentation/networking/device_drivers/sb1000.txt b/Documentation/networking/device_drivers/sb1000.txt
deleted file mode 100644 (file)
index f92c2aa..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-sb1000 is a module network device driver for the General Instrument (also known
-as NextLevel) SURFboard1000 internal cable modem board.  This is an ISA card
-which is used by a number of cable TV companies to provide cable modem access.
-It's a one-way downstream-only cable modem, meaning that your upstream net link
-is provided by your regular phone modem.
-
-This driver was written by Franco Venturi <fventuri@mediaone.net>.  He deserves
-a great deal of thanks for this wonderful piece of code!
-
------------------------------------------------------------------------------
-
-Support for this device is now a part of the standard Linux kernel.  The
-driver source code file is drivers/net/sb1000.c.  In addition to this
-you will need:
-
-1.) The "cmconfig" program.  This is a utility which supplements "ifconfig"
-to configure the cable modem and network interface (usually called "cm0");
-and
-
-2.) Several PPP scripts which live in /etc/ppp to make connecting via your
-cable modem easy.
-
-   These utilities can be obtained from:
-
-      http://www.jacksonville.net/~fventuri/
-
-   in Franco's original source code distribution .tar.gz file.  Support for
-   the sb1000 driver can be found at:
-
-      http://web.archive.org/web/*/http://home.adelphia.net/~siglercm/sb1000.html
-      http://web.archive.org/web/*/http://linuxpower.cx/~cable/
-
-   along with these utilities.
-
-3.) The standard isapnp tools.  These are necessary to configure your SB1000
-card at boot time (or afterwards by hand) since it's a PnP card.
-
-   If you don't have these installed as a standard part of your Linux
-   distribution, you can find them at:
-
-      http://www.roestock.demon.co.uk/isapnptools/
-
-   or check your Linux distribution binary CD or their web site.  For help with
-   isapnp, pnpdump, or /etc/isapnp.conf, go to:
-
-      http://www.roestock.demon.co.uk/isapnptools/isapnpfaq.html
-
------------------------------------------------------------------------------
-
-To make the SB1000 card work, follow these steps:
-
-1.) Run `make config', or `make menuconfig', or `make xconfig', whichever
-you prefer, in the top kernel tree directory to set up your kernel
-configuration.  Make sure to say "Y" to "Prompt for development drivers"
-and to say "M" to the sb1000 driver.  Also say "Y" or "M" to all the standard
-networking questions to get TCP/IP and PPP networking support.
-
-2.) *BEFORE* you build the kernel, edit drivers/net/sb1000.c.  Make sure
-to redefine the value of READ_DATA_PORT to match the I/O address used
-by isapnp to access your PnP cards.  This is the value of READPORT in
-/etc/isapnp.conf or given by the output of pnpdump.
-
-3.) Build and install the kernel and modules as usual.
-
-4.) Boot your new kernel following the usual procedures.
-
-5.) Set up to configure the new SB1000 PnP card by capturing the output
-of "pnpdump" to a file and editing this file to set the correct I/O ports,
-IRQ, and DMA settings for all your PnP cards.  Make sure none of the settings
-conflict with one another.  Then test this configuration by running the
-"isapnp" command with your new config file as the input.  Check for
-errors and fix as necessary.  (As an aside, I use I/O ports 0x110 and
-0x310 and IRQ 11 for my SB1000 card and these work well for me.  YMMV.)
-Then save the finished config file as /etc/isapnp.conf for proper configuration
-on subsequent reboots.
-
-6.) Download the original file sb1000-1.1.2.tar.gz from Franco's site or one of
-the others referenced above.  As root, unpack it into a temporary directory and
-do a `make cmconfig' and then `install -c cmconfig /usr/local/sbin'.  Don't do
-`make install' because it expects to find all the utilities built and ready for
-installation, not just cmconfig.
-
-7.) As root, copy all the files under the ppp/ subdirectory in Franco's
-tar file into /etc/ppp, being careful not to overwrite any files that are
-already in there.  Then modify ppp@gi-on to set the correct login name,
-phone number, and frequency for the cable modem.  Also edit pap-secrets
-to specify your login name and password and any site-specific information
-you need.
-
-8.) Be sure to modify /etc/ppp/firewall to use ipchains instead of
-the older ipfwadm commands from the 2.0.x kernels.  There's a neat utility to
-convert ipfwadm commands to ipchains commands:
-
-   http://users.dhp.com/~whisper/ipfwadm2ipchains/
-
-You may also wish to modify the firewall script to implement a different
-firewalling scheme.
-
-9.) Start the PPP connection via the script /etc/ppp/ppp@gi-on.  You must be
-root to do this.  It's better to use a utility like sudo to execute
-frequently used commands like this with root permissions if possible.  If you
-connect successfully the cable modem interface will come up and you'll see a
-driver message like this at the console:
-
-         cm0: sb1000 at (0x110,0x310), csn 1, S/N 0x2a0d16d8, IRQ 11.
-         sb1000.c:v1.1.2 6/01/98 (fventuri@mediaone.net)
-
-The "ifconfig" command should show two new interfaces, ppp0 and cm0.
-The command "cmconfig cm0" will give you information about the cable modem
-interface.
-
-10.) Try pinging a site via `ping -c 5 www.yahoo.com', for example.  You should
-see packets received.
-
-11.) If you can't get site names (like www.yahoo.com) to resolve into
-IP addresses (like 204.71.200.67), be sure your /etc/resolv.conf file
-has no syntax errors and has the right nameserver IP addresses in it.
-If this doesn't help, try something like `ping -c 5 204.71.200.67' to
-see if the networking is running but the DNS resolution is where the
-problem lies.
-
-12.) If you still have problems, go to the support web sites mentioned above
-and read the information and documentation there.
-
------------------------------------------------------------------------------
-
-Common problems:
-
-1.) Packets go out on the ppp0 interface but don't come back on the cm0
-interface.  It looks like I'm connected but I can't even ping any
-numerical IP addresses.  (This happens predominantly on Debian systems due
-to a default boot-time configuration script.)
-
-Solution -- As root `echo 0 > /proc/sys/net/ipv4/conf/cm0/rp_filter' so it
-can share the same IP address as the ppp0 interface.  Note that this
-command should probably be added to the /etc/ppp/cablemodem script
-*right*between* the "/sbin/ifconfig" and "/sbin/cmconfig" commands.
-You may need to do this to /proc/sys/net/ipv4/conf/ppp0/rp_filter as well.
-If you do this to /proc/sys/net/ipv4/conf/default/rp_filter on each reboot
-(in rc.local or some such) then any interfaces can share the same IP
-addresses.
-
-2.) I get "unresolved symbol" error messages on executing `insmod sb1000.o'.
-
-Solution -- You probably have a non-matching kernel source tree and
-/usr/include/linux and /usr/include/asm header files.  Make sure you
-install the correct versions of the header files in these two directories.
-Then rebuild and reinstall the kernel.
-
-3.) When isapnp runs it reports an error, and my SB1000 card isn't working.
-
-Solution -- There's a problem with later versions of isapnp using the "(CHECK)"
-option in the lines that allocate the two I/O addresses for the SB1000 card.
-This first popped up on RH 6.0.  Delete "(CHECK)" for the SB1000 I/O addresses.
-Make sure they don't conflict with any other pieces of hardware first!  Then
-rerun isapnp and go from there.
-
-4.) I can't execute the /etc/ppp/ppp@gi-on file.
-
-Solution -- As root do `chmod ug+x /etc/ppp/ppp@gi-on'.
-
-5.) The firewall script isn't working (with 2.2.x and higher kernels).
-
-Solution -- Use the ipfwadm2ipchains script referenced above to convert the
-/etc/ppp/firewall script from the deprecated ipfwadm commands to ipchains.
-
-6.) I'm getting *tons* of firewall deny messages in the /var/kern.log,
-/var/messages, and/or /var/syslog files, and they're filling up my /var
-partition!!!
-
-Solution -- First, tell your ISP that you're receiving DoS (Denial of Service)
-and/or portscanning (UDP connection attempts) attacks!  Look over the deny
-messages to figure out what the attack is and where it's coming from.  Next,
-edit /etc/ppp/cablemodem and make sure the ",nobroadcast" option is turned on
-to the "cmconfig" command (uncomment that line).  If you're not receiving these
-denied packets on your broadcast interface (IP address xxx.yyy.zzz.255
-typically), then someone is attacking your machine in particular.  Be careful
-out there....
-
-7.) Everything seems to work fine but my computer locks up after a while
-(and typically during a lengthy download through the cable modem)!
-
-Solution -- You may need to add a short delay in the driver to 'slow down' the
-SURFboard because your PC might not be able to keep up with the transfer rate
-of the SB1000. To do this, it's probably best to download Franco's
-sb1000-1.1.2.tar.gz archive and build and install sb1000.o manually.  You'll
-want to edit the 'Makefile' and look for the 'SB1000_DELAY'
-define.  Uncomment those 'CFLAGS' lines (and comment out the default ones)
-and try setting the delay to something like 60 microseconds with:
-'-DSB1000_DELAY=60'.  Then do `make' and as root `make install' and try
-it out.  If it still doesn't work or you like playing with the driver, you may
-try other numbers.  Remember though that the higher the delay, the slower the
-driver (which slows down the rest of the PC too when it is actively
-used). Thanks to Ed Daiga for this tip!
-
------------------------------------------------------------------------------
-
-Credits:  This README came from Franco Venturi's original README file which is
-still supplied with his driver .tar.gz archive.  I and all other sb1000 users
-owe Franco a tremendous "Thank you!"  Additional thanks goes to Carl Patten
-and Ralph Bonnell who are now managing the Linux SB1000 web site, and to
-the SB1000 users who reported and helped debug the common problems listed
-above.
-
-
-                                       Clemmitt Sigler
-                                       csigler@vt.edu
diff --git a/Documentation/networking/device_drivers/smsc/smc9.rst b/Documentation/networking/device_drivers/smsc/smc9.rst
new file mode 100644 (file)
index 0000000..e5eac89
--- /dev/null
@@ -0,0 +1,48 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+================
+SMC 9xxxx Driver
+================
+
+Revision 0.12
+
+3/5/96
+
+Copyright 1996  Erik Stahlman
+
+Released under terms of the GNU General Public License.
+
+This file contains the instructions and caveats for my SMC9xxx driver.  You
+should not be using the driver without reading this file.
+
+Things to note about installation:
+
+  1. The driver should work on all kernels from 1.2.13 until 1.3.71.
+     (A kernel patch is supplied for 1.3.71 )
+
+  2. If you include this into the kernel, you might need to change some
+     options, such as for forcing IRQ.
+
+
+  3.  To compile as a module, run 'make'.
+      Make will give you the appropriate options for various kernel support.
+
+  4.  Loading the driver as a module::
+
+       use:   insmod smc9194.o
+       optional parameters:
+               io=xxxx    : your base address
+               irq=xx     : your irq
+               ifport=x   :    0 for whatever is default
+                               1 for twisted pair
+                               2 for AUI  ( or BNC on some cards )
+
+How to obtain the latest version?
+
+FTP:
+       ftp://fenris.campus.vt.edu/smc9/smc9-12.tar.gz
+       ftp://sfbox.vt.edu/filebox/F/fenris/smc9/smc9-12.tar.gz
+
+
+Contacting me:
+    erik@mail.vt.edu
diff --git a/Documentation/networking/device_drivers/smsc/smc9.txt b/Documentation/networking/device_drivers/smsc/smc9.txt
deleted file mode 100644 (file)
index d1e1507..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-
-SMC 9xxxx Driver 
-Revision 0.12 
-3/5/96
-Copyright 1996  Erik Stahlman 
-Released under terms of the GNU General Public License. 
-
-This file contains the instructions and caveats for my SMC9xxx driver.  You
-should not be using the driver without reading this file.  
-
-Things to note about installation:
-
-  1. The driver should work on all kernels from 1.2.13 until 1.3.71.
-       (A kernel patch is supplied for 1.3.71 )
-
-  2. If you include this into the kernel, you might need to change some
-       options, such as for forcing IRQ.   
-
-  3.  To compile as a module, run 'make' .   
-       Make will give you the appropriate options for various kernel support.
-  4.  Loading the driver as a module :
-
-       use:   insmod smc9194.o 
-       optional parameters:
-               io=xxxx    : your base address
-               irq=xx     : your irq 
-               ifport=x   :    0 for whatever is default
-                               1 for twisted pair
-                               2 for AUI  ( or BNC on some cards )
-
-How to obtain the latest version? 
-       
-FTP:  
-       ftp://fenris.campus.vt.edu/smc9/smc9-12.tar.gz
-       ftp://sfbox.vt.edu/filebox/F/fenris/smc9/smc9-12.tar.gz 
-   
-
-Contacting me:
-    erik@mail.vt.edu
diff --git a/Documentation/networking/device_drivers/ti/cpsw.rst b/Documentation/networking/device_drivers/ti/cpsw.rst
new file mode 100644 (file)
index 0000000..a88946b
--- /dev/null
@@ -0,0 +1,587 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================
+Texas Instruments CPSW ethernet driver
+======================================
+
+Multiqueue & CBS & MQPRIO
+=========================
+
+
+The cpsw has 3 CBS shapers for each external ports. This document
+describes MQPRIO and CBS Qdisc offload configuration for cpsw driver
+based on examples. It potentially can be used in audio video bridging
+(AVB) and time sensitive networking (TSN).
+
+The following examples were tested on AM572x EVM and BBB boards.
+
+Test setup
+==========
+
+Under consideration two examples with AM572x EVM running cpsw driver
+in dual_emac mode.
+
+Several prerequisites:
+
+- TX queues must be rated starting from txq0 that has highest priority
+- Traffic classes are used starting from 0, that has highest priority
+- CBS shapers should be used with rated queues
+- The bandwidth for CBS shapers has to be set a little bit more then
+  potential incoming rate, thus, rate of all incoming tx queues has
+  to be a little less
+- Real rates can differ, due to discreetness
+- Map skb-priority to txq is not enough, also skb-priority to l2 prio
+  map has to be created with ip or vconfig tool
+- Any l2/socket prio (0 - 7) for classes can be used, but for
+  simplicity default values are used: 3 and 2
+- only 2 classes tested: A and B, but checked and can work with more,
+  maximum allowed 4, but only for 3 rate can be set.
+
+Test setup for examples
+=======================
+
+::
+
+                                       +-------------------------------+
+                                       |--+                            |
+                                       |  |      Workstation0          |
+                                       |E |  MAC 18:03:73:66:87:42     |
+    +-----------------------------+  +--|t |                            |
+    |                    | 1  | E |  |  |h |./tsn_listener -d \         |
+    |  Target board:     | 0  | t |--+  |0 | 18:03:73:66:87:42 -i eth0 \|
+    |  AM572x EVM        | 0  | h |     |  | -s 1500                    |
+    |                    | 0  | 0 |     |--+                            |
+    |  Only 2 classes:   |Mb  +---|     +-------------------------------+
+    |  class A, class B  |        |
+    |                    |    +---|     +-------------------------------+
+    |                    | 1  | E |     |--+                            |
+    |                    | 0  | t |     |  |      Workstation1          |
+    |                    | 0  | h |--+  |E |  MAC 20:cf:30:85:7d:fd     |
+    |                    |Mb  | 1 |  +--|t |                            |
+    +-----------------------------+     |h |./tsn_listener -d \         |
+                                       |0 | 20:cf:30:85:7d:fd -i eth0 \|
+                                       |  | -s 1500                    |
+                                       |--+                            |
+                                       +-------------------------------+
+
+
+Example 1: One port tx AVB configuration scheme for target board
+----------------------------------------------------------------
+
+(prints and scheme for AM572x evm, applicable for single port boards)
+
+- tc - traffic class
+- txq - transmit queue
+- p - priority
+- f - fifo (cpsw fifo)
+- S - shaper configured
+
+::
+
+    +------------------------------------------------------------------+ u
+    | +---------------+  +---------------+  +------+ +------+          | s
+    | |               |  |               |  |      | |      |          | e
+    | | App 1         |  | App 2         |  | Apps | | Apps |          | r
+    | | Class A       |  | Class B       |  | Rest | | Rest |          |
+    | | Eth0          |  | Eth0          |  | Eth0 | | Eth1 |          | s
+    | | VLAN100       |  | VLAN100       |  |   |  | |   |  |          | p
+    | | 40 Mb/s       |  | 20 Mb/s       |  |   |  | |   |  |          | a
+    | | SO_PRIORITY=3 |  | SO_PRIORITY=2 |  |   |  | |   |  |          | c
+    | |   |           |  |   |           |  |   |  | |   |  |          | e
+    | +---|-----------+  +---|-----------+  +---|--+ +---|--+          |
+    +-----|------------------|------------------|--------|-------------+
+       +-+     +------------+                  |        |
+       |       |             +-----------------+     +--+
+       |       |             |                       |
+    +---|-------|-------------|-----------------------|----------------+
+    | +----+ +----+ +----+ +----+                   +----+             |
+    | | p3 | | p2 | | p1 | | p0 |                   | p0 |             | k
+    | \    / \    / \    / \    /                   \    /             | e
+    |  \  /   \  /   \  /   \  /                     \  /              | r
+    |   \/     \/     \/     \/                       \/               | n
+    |    |     |             |                        |                | e
+    |    |     |       +-----+                        |                | l
+    |    |     |       |                              |                |
+    | +----+ +----+ +----+                          +----+             | s
+    | |tc0 | |tc1 | |tc2 |                          |tc0 |             | p
+    | \    / \    / \    /                          \    /             | a
+    |  \  /   \  /   \  /                            \  /              | c
+    |   \/     \/     \/                              \/               | e
+    |   |      |       +-----+                        |                |
+    |   |      |       |     |                        |                |
+    |   |      |       |     |                        |                |
+    |   |      |       |     |                        |                |
+    | +----+ +----+ +----+ +----+                   +----+             |
+    | |txq0| |txq1| |txq2| |txq3|                   |txq4|             |
+    | \    / \    / \    / \    /                   \    /             |
+    |  \  /   \  /   \  /   \  /                     \  /              |
+    |   \/     \/     \/     \/                       \/               |
+    | +-|------|------|------|--+                  +--|--------------+ |
+    | | |      |      |      |  | Eth0.100         |  |     Eth1     | |
+    +---|------|------|------|------------------------|----------------+
+       |      |      |      |                        |
+       p      p      p      p                        |
+       3      2      0-1, 4-7  <- L2 priority        |
+       |      |      |      |                        |
+       |      |      |      |                        |
+    +---|------|------|------|------------------------|----------------+
+    |   |      |      |      |             |----------+                |
+    | +----+ +----+ +----+ +----+       +----+                         |
+    | |dma7| |dma6| |dma5| |dma4|       |dma3|                         |
+    | \    / \    / \    / \    /       \    /                         | c
+    |  \S /   \S /   \  /   \  /         \  /                          | p
+    |   \/     \/     \/     \/           \/                           | s
+    |   |      |      | +-----            |                            | w
+    |   |      |      | |                 |                            |
+    |   |      |      | |                 |                            | d
+    | +----+ +----+ +----+p            p+----+                         | r
+    | |    | |    | |    |o            o|    |                         | i
+    | | f3 | | f2 | | f0 |r            r| f0 |                         | v
+    | |tc0 | |tc1 | |tc2 |t            t|tc0 |                         | e
+    | \CBS / \CBS / \CBS /1            2\CBS /                         | r
+    |  \S /   \S /   \  /                \  /                          |
+    |   \/     \/     \/                  \/                           |
+    +------------------------------------------------------------------+
+
+
+1) ::
+
+
+       // Add 4 tx queues, for interface Eth0, and 1 tx queue for Eth1
+       $ ethtool -L eth0 rx 1 tx 5
+       rx unmodified, ignoring
+
+2) ::
+
+       // Check if num of queues is set correctly:
+       $ ethtool -l eth0
+       Channel parameters for eth0:
+       Pre-set maximums:
+       RX:             8
+       TX:             8
+       Other:          0
+       Combined:       0
+       Current hardware settings:
+       RX:             1
+       TX:             5
+       Other:          0
+       Combined:       0
+
+3) ::
+
+       // TX queues must be rated starting from 0, so set bws for tx0 and tx1
+       // Set rates 40 and 20 Mb/s appropriately.
+       // Pay attention, real speed can differ a bit due to discreetness.
+       // Leave last 2 tx queues not rated.
+       $ echo 40 > /sys/class/net/eth0/queues/tx-0/tx_maxrate
+       $ echo 20 > /sys/class/net/eth0/queues/tx-1/tx_maxrate
+
+4) ::
+
+       // Check maximum rate of tx (cpdma) queues:
+       $ cat /sys/class/net/eth0/queues/tx-*/tx_maxrate
+       40
+       20
+       0
+       0
+       0
+
+5) ::
+
+       // Map skb->priority to traffic class:
+       // 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
+       // Map traffic class to transmit queue:
+       // tc0 -> txq0, tc1 -> txq1, tc2 -> (txq2, txq3)
+       $ tc qdisc replace dev eth0 handle 100: parent root mqprio num_tc 3 \
+       map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 2@2 hw 1
+
+5a) ::
+
+       // As two interface sharing same set of tx queues, assign all traffic
+       // coming to interface Eth1 to separate queue in order to not mix it
+       // with traffic from interface Eth0, so use separate txq to send
+       // packets to Eth1, so all prio -> tc0 and tc0 -> txq4
+       // Here hw 0, so here still default configuration for eth1 in hw
+       $ tc qdisc replace dev eth1 handle 100: parent root mqprio num_tc 1 \
+       map 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 queues 1@4 hw 0
+
+6) ::
+
+       // Check classes settings
+       $ tc -g class show dev eth0
+       +---(100:ffe2) mqprio
+       |    +---(100:3) mqprio
+       |    +---(100:4) mqprio
+       |
+       +---(100:ffe1) mqprio
+       |    +---(100:2) mqprio
+       |
+       +---(100:ffe0) mqprio
+           +---(100:1) mqprio
+
+       $ tc -g class show dev eth1
+       +---(100:ffe0) mqprio
+           +---(100:5) mqprio
+
+7) ::
+
+       // Set rate for class A - 41 Mbit (tc0, txq0) using CBS Qdisc
+       // Set it +1 Mb for reserve (important!)
+       // here only idle slope is important, others arg are ignored
+       // Pay attention, real speed can differ a bit due to discreetness
+       $ tc qdisc add dev eth0 parent 100:1 cbs locredit -1438 \
+       hicredit 62 sendslope -959000 idleslope 41000 offload 1
+       net eth0: set FIFO3 bw = 50
+
+8) ::
+
+       // Set rate for class B - 21 Mbit (tc1, txq1) using CBS Qdisc:
+       // Set it +1 Mb for reserve (important!)
+       $ tc qdisc add dev eth0 parent 100:2 cbs locredit -1468 \
+       hicredit 65 sendslope -979000 idleslope 21000 offload 1
+       net eth0: set FIFO2 bw = 30
+
+9) ::
+
+       // Create vlan 100 to map sk->priority to vlan qos
+       $ ip link add link eth0 name eth0.100 type vlan id 100
+       8021q: 802.1Q VLAN Support v1.8
+       8021q: adding VLAN 0 to HW filter on device eth0
+       8021q: adding VLAN 0 to HW filter on device eth1
+       net eth0: Adding vlanid 100 to vlan filter
+
+10) ::
+
+       // Map skb->priority to L2 prio, 1 to 1
+       $ ip link set eth0.100 type vlan \
+       egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
+
+11) ::
+
+       // Check egress map for vlan 100
+       $ cat /proc/net/vlan/eth0.100
+       [...]
+       INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
+       EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
+
+12) ::
+
+       // Run your appropriate tools with socket option "SO_PRIORITY"
+       // to 3 for class A and/or to 2 for class B
+       // (I took at https://www.spinics.net/lists/netdev/msg460869.html)
+       ./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p3 -s 1500&
+       ./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p2 -s 1500&
+
+13) ::
+
+       // run your listener on workstation (should be in same vlan)
+       // (I took at https://www.spinics.net/lists/netdev/msg460869.html)
+       ./tsn_listener -d 18:03:73:66:87:42 -i enp5s0 -s 1500
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39000 kbps
+
+14) ::
+
+       // Restore default configuration if needed
+       $ ip link del eth0.100
+       $ tc qdisc del dev eth1 root
+       $ tc qdisc del dev eth0 root
+       net eth0: Prev FIFO2 is shaped
+       net eth0: set FIFO3 bw = 0
+       net eth0: set FIFO2 bw = 0
+       $ ethtool -L eth0 rx 1 tx 1
+
+Example 2: Two port tx AVB configuration scheme for target board
+----------------------------------------------------------------
+
+(prints and scheme for AM572x evm, for dual emac boards only)
+
+::
+
+    +------------------------------------------------------------------+ u
+    | +----------+  +----------+  +------+  +----------+  +----------+ | s
+    | |          |  |          |  |      |  |          |  |          | | e
+    | | App 1    |  | App 2    |  | Apps |  | App 3    |  | App 4    | | r
+    | | Class A  |  | Class B  |  | Rest |  | Class B  |  | Class A  | |
+    | | Eth0     |  | Eth0     |  |   |  |  | Eth1     |  | Eth1     | | s
+    | | VLAN100  |  | VLAN100  |  |   |  |  | VLAN100  |  | VLAN100  | | p
+    | | 40 Mb/s  |  | 20 Mb/s  |  |   |  |  | 10 Mb/s  |  | 30 Mb/s  | | a
+    | | SO_PRI=3 |  | SO_PRI=2 |  |   |  |  | SO_PRI=3 |  | SO_PRI=2 | | c
+    | |   |      |  |   |      |  |   |  |  |   |      |  |   |      | | e
+    | +---|------+  +---|------+  +---|--+  +---|------+  +---|------+ |
+    +-----|-------------|-------------|---------|-------------|--------+
+       +-+     +-------+             |         +----------+  +----+
+       |       |             +-------+------+             |       |
+       |       |             |              |             |       |
+    +---|-------|-------------|--------------|-------------|-------|---+
+    | +----+ +----+ +----+ +----+          +----+ +----+ +----+ +----+ |
+    | | p3 | | p2 | | p1 | | p0 |          | p0 | | p1 | | p2 | | p3 | | k
+    | \    / \    / \    / \    /          \    / \    / \    / \    / | e
+    |  \  /   \  /   \  /   \  /            \  /   \  /   \  /   \  /  | r
+    |   \/     \/     \/     \/              \/     \/     \/     \/   | n
+    |   |      |             |                |             |      |   | e
+    |   |      |        +----+                +----+        |      |   | l
+    |   |      |        |                          |        |      |   |
+    | +----+ +----+ +----+                        +----+ +----+ +----+ | s
+    | |tc0 | |tc1 | |tc2 |                        |tc2 | |tc1 | |tc0 | | p
+    | \    / \    / \    /                        \    / \    / \    / | a
+    |  \  /   \  /   \  /                          \  /   \  /   \  /  | c
+    |   \/     \/     \/                            \/     \/     \/   | e
+    |   |      |       +-----+                +-----+      |       |   |
+    |   |      |       |     |                |     |      |       |   |
+    |   |      |       |     |                |     |      |       |   |
+    |   |      |       |     |    E      E    |     |      |       |   |
+    | +----+ +----+ +----+ +----+ t      t +----+ +----+ +----+ +----+ |
+    | |txq0| |txq1| |txq4| |txq5| h      h |txq6| |txq7| |txq3| |txq2| |
+    | \    / \    / \    / \    / 0      1 \    / \    / \    / \    / |
+    |  \  /   \  /   \  /   \  /  .      .  \  /   \  /   \  /   \  /  |
+    |   \/     \/     \/     \/   1      1   \/     \/     \/     \/   |
+    | +-|------|------|------|--+ 0      0 +-|------|------|------|--+ |
+    | | |      |      |      |  | 0      0 | |      |      |      |  | |
+    +---|------|------|------|---------------|------|------|------|----+
+       |      |      |      |               |      |      |      |
+       p      p      p      p               p      p      p      p
+       3      2      0-1, 4-7   <-L2 pri->  0-1, 4-7      2      3
+       |      |      |      |               |      |      |      |
+       |      |      |      |               |      |      |      |
+    +---|------|------|------|---------------|------|------|------|----+
+    |   |      |      |      |               |      |      |      |    |
+    | +----+ +----+ +----+ +----+          +----+ +----+ +----+ +----+ |
+    | |dma7| |dma6| |dma3| |dma2|          |dma1| |dma0| |dma4| |dma5| |
+    | \    / \    / \    / \    /          \    / \    / \    / \    / | c
+    |  \S /   \S /   \  /   \  /            \  /   \  /   \S /   \S /  | p
+    |   \/     \/     \/     \/              \/     \/     \/     \/   | s
+    |   |      |      | +-----                |      |      |      |   | w
+    |   |      |      | |                     +----+ |      |      |   |
+    |   |      |      | |                          | |      |      |   | d
+    | +----+ +----+ +----+p                      p+----+ +----+ +----+ | r
+    | |    | |    | |    |o                      o|    | |    | |    | | i
+    | | f3 | | f2 | | f0 |r        CPSW          r| f3 | | f2 | | f0 | | v
+    | |tc0 | |tc1 | |tc2 |t                      t|tc0 | |tc1 | |tc2 | | e
+    | \CBS / \CBS / \CBS /1                      2\CBS / \CBS / \CBS / | r
+    |  \S /   \S /   \  /                          \S /   \S /   \  /  |
+    |   \/     \/     \/                            \/     \/     \/   |
+    +------------------------------------------------------------------+
+    ========================================Eth==========================>
+
+1) ::
+
+       // Add 8 tx queues, for interface Eth0, but they are common, so are accessed
+       // by two interfaces Eth0 and Eth1.
+       $ ethtool -L eth1 rx 1 tx 8
+       rx unmodified, ignoring
+
+2) ::
+
+       // Check if num of queues is set correctly:
+       $ ethtool -l eth0
+       Channel parameters for eth0:
+       Pre-set maximums:
+       RX:             8
+       TX:             8
+       Other:          0
+       Combined:       0
+       Current hardware settings:
+       RX:             1
+       TX:             8
+       Other:          0
+       Combined:       0
+
+3) ::
+
+       // TX queues must be rated starting from 0, so set bws for tx0 and tx1 for Eth0
+       // and for tx2 and tx3 for Eth1. That is, rates 40 and 20 Mb/s appropriately
+       // for Eth0 and 30 and 10 Mb/s for Eth1.
+       // Real speed can differ a bit due to discreetness
+       // Leave last 4 tx queues as not rated
+       $ echo 40 > /sys/class/net/eth0/queues/tx-0/tx_maxrate
+       $ echo 20 > /sys/class/net/eth0/queues/tx-1/tx_maxrate
+       $ echo 30 > /sys/class/net/eth1/queues/tx-2/tx_maxrate
+       $ echo 10 > /sys/class/net/eth1/queues/tx-3/tx_maxrate
+
+4) ::
+
+       // Check maximum rate of tx (cpdma) queues:
+       $ cat /sys/class/net/eth0/queues/tx-*/tx_maxrate
+       40
+       20
+       30
+       10
+       0
+       0
+       0
+       0
+
+5) ::
+
+       // Map skb->priority to traffic class for Eth0:
+       // 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
+       // Map traffic class to transmit queue:
+       // tc0 -> txq0, tc1 -> txq1, tc2 -> (txq4, txq5)
+       $ tc qdisc replace dev eth0 handle 100: parent root mqprio num_tc 3 \
+       map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 2@4 hw 1
+
+6) ::
+
+       // Check classes settings
+       $ tc -g class show dev eth0
+       +---(100:ffe2) mqprio
+       |    +---(100:5) mqprio
+       |    +---(100:6) mqprio
+       |
+       +---(100:ffe1) mqprio
+       |    +---(100:2) mqprio
+       |
+       +---(100:ffe0) mqprio
+           +---(100:1) mqprio
+
+7) ::
+
+       // Set rate for class A - 41 Mbit (tc0, txq0) using CBS Qdisc for Eth0
+       // here only idle slope is important, others ignored
+       // Real speed can differ a bit due to discreetness
+       $ tc qdisc add dev eth0 parent 100:1 cbs locredit -1470 \
+       hicredit 62 sendslope -959000 idleslope 41000 offload 1
+       net eth0: set FIFO3 bw = 50
+
+8) ::
+
+       // Set rate for class B - 21 Mbit (tc1, txq1) using CBS Qdisc for Eth0
+       $ tc qdisc add dev eth0 parent 100:2 cbs locredit -1470 \
+       hicredit 65 sendslope -979000 idleslope 21000 offload 1
+       net eth0: set FIFO2 bw = 30
+
+9) ::
+
+       // Create vlan 100 to map sk->priority to vlan qos for Eth0
+       $ ip link add link eth0 name eth0.100 type vlan id 100
+       net eth0: Adding vlanid 100 to vlan filter
+
+10) ::
+
+       // Map skb->priority to L2 prio for Eth0.100, one to one
+       $ ip link set eth0.100 type vlan \
+       egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
+
+11) ::
+
+       // Check egress map for vlan 100
+       $ cat /proc/net/vlan/eth0.100
+       [...]
+       INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
+       EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
+
+12) ::
+
+       // Map skb->priority to traffic class for Eth1:
+       // 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
+       // Map traffic class to transmit queue:
+       // tc0 -> txq2, tc1 -> txq3, tc2 -> (txq6, txq7)
+       $ tc qdisc replace dev eth1 handle 100: parent root mqprio num_tc 3 \
+       map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@2 1@3 2@6 hw 1
+
+13) ::
+
+       // Check classes settings
+       $ tc -g class show dev eth1
+       +---(100:ffe2) mqprio
+       |    +---(100:7) mqprio
+       |    +---(100:8) mqprio
+       |
+       +---(100:ffe1) mqprio
+       |    +---(100:4) mqprio
+       |
+       +---(100:ffe0) mqprio
+           +---(100:3) mqprio
+
+14) ::
+
+       // Set rate for class A - 31 Mbit (tc0, txq2) using CBS Qdisc for Eth1
+       // here only idle slope is important, others ignored, but calculated
+       // for interface speed - 100Mb for eth1 port.
+       // Set it +1 Mb for reserve (important!)
+       $ tc qdisc add dev eth1 parent 100:3 cbs locredit -1035 \
+       hicredit 465 sendslope -69000 idleslope 31000 offload 1
+       net eth1: set FIFO3 bw = 31
+
+15) ::
+
+       // Set rate for class B - 11 Mbit (tc1, txq3) using CBS Qdisc for Eth1
+       // Set it +1 Mb for reserve (important!)
+       $ tc qdisc add dev eth1 parent 100:4 cbs locredit -1335 \
+       hicredit 405 sendslope -89000 idleslope 11000 offload 1
+       net eth1: set FIFO2 bw = 11
+
+16) ::
+
+       // Create vlan 100 to map sk->priority to vlan qos for Eth1
+       $ ip link add link eth1 name eth1.100 type vlan id 100
+       net eth1: Adding vlanid 100 to vlan filter
+
+17) ::
+
+       // Map skb->priority to L2 prio for Eth1.100, one to one
+       $ ip link set eth1.100 type vlan \
+       egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
+
+18) ::
+
+       // Check egress map for vlan 100
+       $ cat /proc/net/vlan/eth1.100
+       [...]
+       INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
+       EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
+
+19) ::
+
+       // Run appropriate tools with socket option "SO_PRIORITY" to 3
+       // for class A and to 2 for class B. For both interfaces
+       ./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p2 -s 1500&
+       ./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p3 -s 1500&
+       ./tsn_talker -d 20:cf:30:85:7d:fd -i eth1.100 -p2 -s 1500&
+       ./tsn_talker -d 20:cf:30:85:7d:fd -i eth1.100 -p3 -s 1500&
+
+20) ::
+
+       // run your listener on workstation (should be in same vlan)
+       // (I took at https://www.spinics.net/lists/netdev/msg460869.html)
+       ./tsn_listener -d 18:03:73:66:87:42 -i enp5s0 -s 1500
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39012 kbps
+       Receiving data rate: 39000 kbps
+
+21) ::
+
+       // Restore default configuration if needed
+       $ ip link del eth1.100
+       $ ip link del eth0.100
+       $ tc qdisc del dev eth1 root
+       net eth1: Prev FIFO2 is shaped
+       net eth1: set FIFO3 bw = 0
+       net eth1: set FIFO2 bw = 0
+       $ tc qdisc del dev eth0 root
+       net eth0: Prev FIFO2 is shaped
+       net eth0: set FIFO3 bw = 0
+       net eth0: set FIFO2 bw = 0
+       $ ethtool -L eth0 rx 1 tx 1
diff --git a/Documentation/networking/device_drivers/ti/cpsw.txt b/Documentation/networking/device_drivers/ti/cpsw.txt
deleted file mode 100644 (file)
index d4d4c07..0000000
+++ /dev/null
@@ -1,541 +0,0 @@
-* Texas Instruments CPSW ethernet driver
-
-Multiqueue & CBS & MQPRIO
-=====================================================================
-=====================================================================
-
-The cpsw has 3 CBS shapers for each external ports. This document
-describes MQPRIO and CBS Qdisc offload configuration for cpsw driver
-based on examples. It potentially can be used in audio video bridging
-(AVB) and time sensitive networking (TSN).
-
-The following examples were tested on AM572x EVM and BBB boards.
-
-Test setup
-==========
-
-Under consideration two examples with AM572x EVM running cpsw driver
-in dual_emac mode.
-
-Several prerequisites:
-- TX queues must be rated starting from txq0 that has highest priority
-- Traffic classes are used starting from 0, that has highest priority
-- CBS shapers should be used with rated queues
-- The bandwidth for CBS shapers has to be set a little bit more then
-  potential incoming rate, thus, rate of all incoming tx queues has
-  to be a little less
-- Real rates can differ, due to discreetness
-- Map skb-priority to txq is not enough, also skb-priority to l2 prio
-  map has to be created with ip or vconfig tool
-- Any l2/socket prio (0 - 7) for classes can be used, but for
-  simplicity default values are used: 3 and 2
-- only 2 classes tested: A and B, but checked and can work with more,
-  maximum allowed 4, but only for 3 rate can be set.
-
-Test setup for examples
-=======================
-                                    +-------------------------------+
-                                    |--+                            |
-                                    |  |      Workstation0          |
-                                    |E |  MAC 18:03:73:66:87:42     |
-+-----------------------------+  +--|t |                            |
-|                    | 1  | E |  |  |h |./tsn_listener -d \         |
-|  Target board:     | 0  | t |--+  |0 | 18:03:73:66:87:42 -i eth0 \|
-|  AM572x EVM        | 0  | h |     |  | -s 1500                    |
-|                    | 0  | 0 |     |--+                            |
-|  Only 2 classes:   |Mb  +---|     +-------------------------------+
-|  class A, class B  |        |
-|                    |    +---|     +-------------------------------+
-|                    | 1  | E |     |--+                            |
-|                    | 0  | t |     |  |      Workstation1          |
-|                    | 0  | h |--+  |E |  MAC 20:cf:30:85:7d:fd     |
-|                    |Mb  | 1 |  +--|t |                            |
-+-----------------------------+     |h |./tsn_listener -d \         |
-                                    |0 | 20:cf:30:85:7d:fd -i eth0 \|
-                                    |  | -s 1500                    |
-                                    |--+                            |
-                                    +-------------------------------+
-
-*********************************************************************
-*********************************************************************
-*********************************************************************
-Example 1: One port tx AVB configuration scheme for target board
-----------------------------------------------------------------------
-(prints and scheme for AM572x evm, applicable for single port boards)
-
-tc - traffic class
-txq - transmit queue
-p - priority
-f - fifo (cpsw fifo)
-S - shaper configured
-
-+------------------------------------------------------------------+ u
-| +---------------+  +---------------+  +------+ +------+          | s
-| |               |  |               |  |      | |      |          | e
-| | App 1         |  | App 2         |  | Apps | | Apps |          | r
-| | Class A       |  | Class B       |  | Rest | | Rest |          |
-| | Eth0          |  | Eth0          |  | Eth0 | | Eth1 |          | s
-| | VLAN100       |  | VLAN100       |  |   |  | |   |  |          | p
-| | 40 Mb/s       |  | 20 Mb/s       |  |   |  | |   |  |          | a
-| | SO_PRIORITY=3 |  | SO_PRIORITY=2 |  |   |  | |   |  |          | c
-| |   |           |  |   |           |  |   |  | |   |  |          | e
-| +---|-----------+  +---|-----------+  +---|--+ +---|--+          |
-+-----|------------------|------------------|--------|-------------+
-    +-+     +------------+                  |        |
-    |       |             +-----------------+     +--+
-    |       |             |                       |
-+---|-------|-------------|-----------------------|----------------+
-| +----+ +----+ +----+ +----+                   +----+             |
-| | p3 | | p2 | | p1 | | p0 |                   | p0 |             | k
-| \    / \    / \    / \    /                   \    /             | e
-|  \  /   \  /   \  /   \  /                     \  /              | r
-|   \/     \/     \/     \/                       \/               | n
-|    |     |             |                        |                | e
-|    |     |       +-----+                        |                | l
-|    |     |       |                              |                |
-| +----+ +----+ +----+                          +----+             | s
-| |tc0 | |tc1 | |tc2 |                          |tc0 |             | p
-| \    / \    / \    /                          \    /             | a
-|  \  /   \  /   \  /                            \  /              | c
-|   \/     \/     \/                              \/               | e
-|   |      |       +-----+                        |                |
-|   |      |       |     |                        |                |
-|   |      |       |     |                        |                |
-|   |      |       |     |                        |                |
-| +----+ +----+ +----+ +----+                   +----+             |
-| |txq0| |txq1| |txq2| |txq3|                   |txq4|             |
-| \    / \    / \    / \    /                   \    /             |
-|  \  /   \  /   \  /   \  /                     \  /              |
-|   \/     \/     \/     \/                       \/               |
-| +-|------|------|------|--+                  +--|--------------+ |
-| | |      |      |      |  | Eth0.100         |  |     Eth1     | |
-+---|------|------|------|------------------------|----------------+
-    |      |      |      |                        |
-    p      p      p      p                        |
-    3      2      0-1, 4-7  <- L2 priority        |
-    |      |      |      |                        |
-    |      |      |      |                        |
-+---|------|------|------|------------------------|----------------+
-|   |      |      |      |             |----------+                |
-| +----+ +----+ +----+ +----+       +----+                         |
-| |dma7| |dma6| |dma5| |dma4|       |dma3|                         |
-| \    / \    / \    / \    /       \    /                         | c
-|  \S /   \S /   \  /   \  /         \  /                          | p
-|   \/     \/     \/     \/           \/                           | s
-|   |      |      | +-----            |                            | w
-|   |      |      | |                 |                            |
-|   |      |      | |                 |                            | d
-| +----+ +----+ +----+p            p+----+                         | r
-| |    | |    | |    |o            o|    |                         | i
-| | f3 | | f2 | | f0 |r            r| f0 |                         | v
-| |tc0 | |tc1 | |tc2 |t            t|tc0 |                         | e
-| \CBS / \CBS / \CBS /1            2\CBS /                         | r
-|  \S /   \S /   \  /                \  /                          |
-|   \/     \/     \/                  \/                           |
-+------------------------------------------------------------------+
-========================================Eth==========================>
-
-1)
-// Add 4 tx queues, for interface Eth0, and 1 tx queue for Eth1
-$ ethtool -L eth0 rx 1 tx 5
-rx unmodified, ignoring
-
-2)
-// Check if num of queues is set correctly:
-$ ethtool -l eth0
-Channel parameters for eth0:
-Pre-set maximums:
-RX:             8
-TX:             8
-Other:          0
-Combined:       0
-Current hardware settings:
-RX:             1
-TX:             5
-Other:          0
-Combined:       0
-
-3)
-// TX queues must be rated starting from 0, so set bws for tx0 and tx1
-// Set rates 40 and 20 Mb/s appropriately.
-// Pay attention, real speed can differ a bit due to discreetness.
-// Leave last 2 tx queues not rated.
-$ echo 40 > /sys/class/net/eth0/queues/tx-0/tx_maxrate
-$ echo 20 > /sys/class/net/eth0/queues/tx-1/tx_maxrate
-
-4)
-// Check maximum rate of tx (cpdma) queues:
-$ cat /sys/class/net/eth0/queues/tx-*/tx_maxrate
-40
-20
-0
-0
-0
-
-5)
-// Map skb->priority to traffic class:
-// 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
-// Map traffic class to transmit queue:
-// tc0 -> txq0, tc1 -> txq1, tc2 -> (txq2, txq3)
-$ tc qdisc replace dev eth0 handle 100: parent root mqprio num_tc 3 \
-map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 2@2 hw 1
-
-5a)
-// As two interface sharing same set of tx queues, assign all traffic
-// coming to interface Eth1 to separate queue in order to not mix it
-// with traffic from interface Eth0, so use separate txq to send
-// packets to Eth1, so all prio -> tc0 and tc0 -> txq4
-// Here hw 0, so here still default configuration for eth1 in hw
-$ tc qdisc replace dev eth1 handle 100: parent root mqprio num_tc 1 \
-map 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 queues 1@4 hw 0
-
-6)
-// Check classes settings
-$ tc -g class show dev eth0
-+---(100:ffe2) mqprio
-|    +---(100:3) mqprio
-|    +---(100:4) mqprio
-|
-+---(100:ffe1) mqprio
-|    +---(100:2) mqprio
-|
-+---(100:ffe0) mqprio
-     +---(100:1) mqprio
-
-$ tc -g class show dev eth1
-+---(100:ffe0) mqprio
-     +---(100:5) mqprio
-
-7)
-// Set rate for class A - 41 Mbit (tc0, txq0) using CBS Qdisc
-// Set it +1 Mb for reserve (important!)
-// here only idle slope is important, others arg are ignored
-// Pay attention, real speed can differ a bit due to discreetness
-$ tc qdisc add dev eth0 parent 100:1 cbs locredit -1438 \
-hicredit 62 sendslope -959000 idleslope 41000 offload 1
-net eth0: set FIFO3 bw = 50
-
-8)
-// Set rate for class B - 21 Mbit (tc1, txq1) using CBS Qdisc:
-// Set it +1 Mb for reserve (important!)
-$ tc qdisc add dev eth0 parent 100:2 cbs locredit -1468 \
-hicredit 65 sendslope -979000 idleslope 21000 offload 1
-net eth0: set FIFO2 bw = 30
-
-9)
-// Create vlan 100 to map sk->priority to vlan qos
-$ ip link add link eth0 name eth0.100 type vlan id 100
-8021q: 802.1Q VLAN Support v1.8
-8021q: adding VLAN 0 to HW filter on device eth0
-8021q: adding VLAN 0 to HW filter on device eth1
-net eth0: Adding vlanid 100 to vlan filter
-
-10)
-// Map skb->priority to L2 prio, 1 to 1
-$ ip link set eth0.100 type vlan \
-egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
-
-11)
-// Check egress map for vlan 100
-$ cat /proc/net/vlan/eth0.100
-[...]
-INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
-EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
-
-12)
-// Run your appropriate tools with socket option "SO_PRIORITY"
-// to 3 for class A and/or to 2 for class B
-// (I took at https://www.spinics.net/lists/netdev/msg460869.html)
-./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p3 -s 1500&
-./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p2 -s 1500&
-
-13)
-// run your listener on workstation (should be in same vlan)
-// (I took at https://www.spinics.net/lists/netdev/msg460869.html)
-./tsn_listener -d 18:03:73:66:87:42 -i enp5s0 -s 1500
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39000 kbps
-
-14)
-// Restore default configuration if needed
-$ ip link del eth0.100
-$ tc qdisc del dev eth1 root
-$ tc qdisc del dev eth0 root
-net eth0: Prev FIFO2 is shaped
-net eth0: set FIFO3 bw = 0
-net eth0: set FIFO2 bw = 0
-$ ethtool -L eth0 rx 1 tx 1
-
-*********************************************************************
-*********************************************************************
-*********************************************************************
-Example 2: Two port tx AVB configuration scheme for target board
-----------------------------------------------------------------------
-(prints and scheme for AM572x evm, for dual emac boards only)
-
-+------------------------------------------------------------------+ u
-| +----------+  +----------+  +------+  +----------+  +----------+ | s
-| |          |  |          |  |      |  |          |  |          | | e
-| | App 1    |  | App 2    |  | Apps |  | App 3    |  | App 4    | | r
-| | Class A  |  | Class B  |  | Rest |  | Class B  |  | Class A  | |
-| | Eth0     |  | Eth0     |  |   |  |  | Eth1     |  | Eth1     | | s
-| | VLAN100  |  | VLAN100  |  |   |  |  | VLAN100  |  | VLAN100  | | p
-| | 40 Mb/s  |  | 20 Mb/s  |  |   |  |  | 10 Mb/s  |  | 30 Mb/s  | | a
-| | SO_PRI=3 |  | SO_PRI=2 |  |   |  |  | SO_PRI=3 |  | SO_PRI=2 | | c
-| |   |      |  |   |      |  |   |  |  |   |      |  |   |      | | e
-| +---|------+  +---|------+  +---|--+  +---|------+  +---|------+ |
-+-----|-------------|-------------|---------|-------------|--------+
-    +-+     +-------+             |         +----------+  +----+
-    |       |             +-------+------+             |       |
-    |       |             |              |             |       |
-+---|-------|-------------|--------------|-------------|-------|---+
-| +----+ +----+ +----+ +----+          +----+ +----+ +----+ +----+ |
-| | p3 | | p2 | | p1 | | p0 |          | p0 | | p1 | | p2 | | p3 | | k
-| \    / \    / \    / \    /          \    / \    / \    / \    / | e
-|  \  /   \  /   \  /   \  /            \  /   \  /   \  /   \  /  | r
-|   \/     \/     \/     \/              \/     \/     \/     \/   | n
-|   |      |             |                |             |      |   | e
-|   |      |        +----+                +----+        |      |   | l
-|   |      |        |                          |        |      |   |
-| +----+ +----+ +----+                        +----+ +----+ +----+ | s
-| |tc0 | |tc1 | |tc2 |                        |tc2 | |tc1 | |tc0 | | p
-| \    / \    / \    /                        \    / \    / \    / | a
-|  \  /   \  /   \  /                          \  /   \  /   \  /  | c
-|   \/     \/     \/                            \/     \/     \/   | e
-|   |      |       +-----+                +-----+      |       |   |
-|   |      |       |     |                |     |      |       |   |
-|   |      |       |     |                |     |      |       |   |
-|   |      |       |     |    E      E    |     |      |       |   |
-| +----+ +----+ +----+ +----+ t      t +----+ +----+ +----+ +----+ |
-| |txq0| |txq1| |txq4| |txq5| h      h |txq6| |txq7| |txq3| |txq2| |
-| \    / \    / \    / \    / 0      1 \    / \    / \    / \    / |
-|  \  /   \  /   \  /   \  /  .      .  \  /   \  /   \  /   \  /  |
-|   \/     \/     \/     \/   1      1   \/     \/     \/     \/   |
-| +-|------|------|------|--+ 0      0 +-|------|------|------|--+ |
-| | |      |      |      |  | 0      0 | |      |      |      |  | |
-+---|------|------|------|---------------|------|------|------|----+
-    |      |      |      |               |      |      |      |
-    p      p      p      p               p      p      p      p
-    3      2      0-1, 4-7   <-L2 pri->  0-1, 4-7      2      3
-    |      |      |      |               |      |      |      |
-    |      |      |      |               |      |      |      |
-+---|------|------|------|---------------|------|------|------|----+
-|   |      |      |      |               |      |      |      |    |
-| +----+ +----+ +----+ +----+          +----+ +----+ +----+ +----+ |
-| |dma7| |dma6| |dma3| |dma2|          |dma1| |dma0| |dma4| |dma5| |
-| \    / \    / \    / \    /          \    / \    / \    / \    / | c
-|  \S /   \S /   \  /   \  /            \  /   \  /   \S /   \S /  | p
-|   \/     \/     \/     \/              \/     \/     \/     \/   | s
-|   |      |      | +-----                |      |      |      |   | w
-|   |      |      | |                     +----+ |      |      |   |
-|   |      |      | |                          | |      |      |   | d
-| +----+ +----+ +----+p                      p+----+ +----+ +----+ | r
-| |    | |    | |    |o                      o|    | |    | |    | | i
-| | f3 | | f2 | | f0 |r        CPSW          r| f3 | | f2 | | f0 | | v
-| |tc0 | |tc1 | |tc2 |t                      t|tc0 | |tc1 | |tc2 | | e
-| \CBS / \CBS / \CBS /1                      2\CBS / \CBS / \CBS / | r
-|  \S /   \S /   \  /                          \S /   \S /   \  /  |
-|   \/     \/     \/                            \/     \/     \/   |
-+------------------------------------------------------------------+
-========================================Eth==========================>
-
-1)
-// Add 8 tx queues, for interface Eth0, but they are common, so are accessed
-// by two interfaces Eth0 and Eth1.
-$ ethtool -L eth1 rx 1 tx 8
-rx unmodified, ignoring
-
-2)
-// Check if num of queues is set correctly:
-$ ethtool -l eth0
-Channel parameters for eth0:
-Pre-set maximums:
-RX:             8
-TX:             8
-Other:          0
-Combined:       0
-Current hardware settings:
-RX:             1
-TX:             8
-Other:          0
-Combined:       0
-
-3)
-// TX queues must be rated starting from 0, so set bws for tx0 and tx1 for Eth0
-// and for tx2 and tx3 for Eth1. That is, rates 40 and 20 Mb/s appropriately
-// for Eth0 and 30 and 10 Mb/s for Eth1.
-// Real speed can differ a bit due to discreetness
-// Leave last 4 tx queues as not rated
-$ echo 40 > /sys/class/net/eth0/queues/tx-0/tx_maxrate
-$ echo 20 > /sys/class/net/eth0/queues/tx-1/tx_maxrate
-$ echo 30 > /sys/class/net/eth1/queues/tx-2/tx_maxrate
-$ echo 10 > /sys/class/net/eth1/queues/tx-3/tx_maxrate
-
-4)
-// Check maximum rate of tx (cpdma) queues:
-$ cat /sys/class/net/eth0/queues/tx-*/tx_maxrate
-40
-20
-30
-10
-0
-0
-0
-0
-
-5)
-// Map skb->priority to traffic class for Eth0:
-// 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
-// Map traffic class to transmit queue:
-// tc0 -> txq0, tc1 -> txq1, tc2 -> (txq4, txq5)
-$ tc qdisc replace dev eth0 handle 100: parent root mqprio num_tc 3 \
-map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@0 1@1 2@4 hw 1
-
-6)
-// Check classes settings
-$ tc -g class show dev eth0
-+---(100:ffe2) mqprio
-|    +---(100:5) mqprio
-|    +---(100:6) mqprio
-|
-+---(100:ffe1) mqprio
-|    +---(100:2) mqprio
-|
-+---(100:ffe0) mqprio
-     +---(100:1) mqprio
-
-7)
-// Set rate for class A - 41 Mbit (tc0, txq0) using CBS Qdisc for Eth0
-// here only idle slope is important, others ignored
-// Real speed can differ a bit due to discreetness
-$ tc qdisc add dev eth0 parent 100:1 cbs locredit -1470 \
-hicredit 62 sendslope -959000 idleslope 41000 offload 1
-net eth0: set FIFO3 bw = 50
-
-8)
-// Set rate for class B - 21 Mbit (tc1, txq1) using CBS Qdisc for Eth0
-$ tc qdisc add dev eth0 parent 100:2 cbs locredit -1470 \
-hicredit 65 sendslope -979000 idleslope 21000 offload 1
-net eth0: set FIFO2 bw = 30
-
-9)
-// Create vlan 100 to map sk->priority to vlan qos for Eth0
-$ ip link add link eth0 name eth0.100 type vlan id 100
-net eth0: Adding vlanid 100 to vlan filter
-
-10)
-// Map skb->priority to L2 prio for Eth0.100, one to one
-$ ip link set eth0.100 type vlan \
-egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
-
-11)
-// Check egress map for vlan 100
-$ cat /proc/net/vlan/eth0.100
-[...]
-INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
-EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
-
-12)
-// Map skb->priority to traffic class for Eth1:
-// 3pri -> tc0, 2pri -> tc1, (0,1,4-7)pri -> tc2
-// Map traffic class to transmit queue:
-// tc0 -> txq2, tc1 -> txq3, tc2 -> (txq6, txq7)
-$ tc qdisc replace dev eth1 handle 100: parent root mqprio num_tc 3 \
-map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 queues 1@2 1@3 2@6 hw 1
-
-13)
-// Check classes settings
-$ tc -g class show dev eth1
-+---(100:ffe2) mqprio
-|    +---(100:7) mqprio
-|    +---(100:8) mqprio
-|
-+---(100:ffe1) mqprio
-|    +---(100:4) mqprio
-|
-+---(100:ffe0) mqprio
-     +---(100:3) mqprio
-
-14)
-// Set rate for class A - 31 Mbit (tc0, txq2) using CBS Qdisc for Eth1
-// here only idle slope is important, others ignored, but calculated
-// for interface speed - 100Mb for eth1 port.
-// Set it +1 Mb for reserve (important!)
-$ tc qdisc add dev eth1 parent 100:3 cbs locredit -1035 \
-hicredit 465 sendslope -69000 idleslope 31000 offload 1
-net eth1: set FIFO3 bw = 31
-
-15)
-// Set rate for class B - 11 Mbit (tc1, txq3) using CBS Qdisc for Eth1
-// Set it +1 Mb for reserve (important!)
-$ tc qdisc add dev eth1 parent 100:4 cbs locredit -1335 \
-hicredit 405 sendslope -89000 idleslope 11000 offload 1
-net eth1: set FIFO2 bw = 11
-
-16)
-// Create vlan 100 to map sk->priority to vlan qos for Eth1
-$ ip link add link eth1 name eth1.100 type vlan id 100
-net eth1: Adding vlanid 100 to vlan filter
-
-17)
-// Map skb->priority to L2 prio for Eth1.100, one to one
-$ ip link set eth1.100 type vlan \
-egress 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
-
-18)
-// Check egress map for vlan 100
-$ cat /proc/net/vlan/eth1.100
-[...]
-INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0
-EGRESS priority mappings: 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
-
-19)
-// Run appropriate tools with socket option "SO_PRIORITY" to 3
-// for class A and to 2 for class B. For both interfaces
-./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p2 -s 1500&
-./tsn_talker -d 18:03:73:66:87:42 -i eth0.100 -p3 -s 1500&
-./tsn_talker -d 20:cf:30:85:7d:fd -i eth1.100 -p2 -s 1500&
-./tsn_talker -d 20:cf:30:85:7d:fd -i eth1.100 -p3 -s 1500&
-
-20)
-// run your listener on workstation (should be in same vlan)
-// (I took at https://www.spinics.net/lists/netdev/msg460869.html)
-./tsn_listener -d 18:03:73:66:87:42 -i enp5s0 -s 1500
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39012 kbps
-Receiving data rate: 39000 kbps
-
-21)
-// Restore default configuration if needed
-$ ip link del eth1.100
-$ ip link del eth0.100
-$ tc qdisc del dev eth1 root
-net eth1: Prev FIFO2 is shaped
-net eth1: set FIFO3 bw = 0
-net eth1: set FIFO2 bw = 0
-$ tc qdisc del dev eth0 root
-net eth0: Prev FIFO2 is shaped
-net eth0: set FIFO3 bw = 0
-net eth0: set FIFO2 bw = 0
-$ ethtool -L eth0 rx 1 tx 1
diff --git a/Documentation/networking/device_drivers/ti/cpsw_switchdev.rst b/Documentation/networking/device_drivers/ti/cpsw_switchdev.rst
new file mode 100644 (file)
index 0000000..1241eca
--- /dev/null
@@ -0,0 +1,242 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================================
+Texas Instruments CPSW switchdev based ethernet driver
+======================================================
+
+:Version: 2.0
+
+Port renaming
+=============
+
+On older udev versions renaming of ethX to swXpY will not be automatically
+supported
+
+In order to rename via udev::
+
+    ip -d link show dev sw0p1 | grep switchid
+
+    SUBSYSTEM=="net", ACTION=="add", ATTR{phys_switch_id}==<switchid>, \
+           ATTR{phys_port_name}!="", NAME="sw0$attr{phys_port_name}"
+
+
+Dual mac mode
+=============
+
+- The new (cpsw_new.c) driver is operating in dual-emac mode by default, thus
+  working as 2 individual network interfaces. Main differences from legacy CPSW
+  driver are:
+
+ - optimized promiscuous mode: The P0_UNI_FLOOD (both ports) is enabled in
+   addition to ALLMULTI (current port) instead of ALE_BYPASS.
+   So, Ports in promiscuous mode will keep possibility of mcast and vlan
+   filtering, which is provides significant benefits when ports are joined
+   to the same bridge, but without enabling "switch" mode, or to different
+   bridges.
+ - learning disabled on ports as it make not too much sense for
+   segregated ports - no forwarding in HW.
+ - enabled basic support for devlink.
+
+   ::
+
+       devlink dev show
+               platform/48484000.switch
+
+       devlink dev param show
+       platform/48484000.switch:
+       name switch_mode type driver-specific
+       values:
+               cmode runtime value false
+       name ale_bypass type driver-specific
+       values:
+               cmode runtime value false
+
+Devlink configuration parameters
+================================
+
+See Documentation/networking/devlink/ti-cpsw-switch.rst
+
+Bridging in dual mac mode
+=========================
+
+The dual_mac mode requires two vids to be reserved for internal purposes,
+which, by default, equal CPSW Port numbers. As result, bridge has to be
+configured in vlan unaware mode or default_pvid has to be adjusted::
+
+       ip link add name br0 type bridge
+       ip link set dev br0 type bridge vlan_filtering 0
+       echo 0 > /sys/class/net/br0/bridge/default_pvid
+       ip link set dev sw0p1 master br0
+       ip link set dev sw0p2 master br0
+
+or::
+
+       ip link add name br0 type bridge
+       ip link set dev br0 type bridge vlan_filtering 0
+       echo 100 > /sys/class/net/br0/bridge/default_pvid
+       ip link set dev br0 type bridge vlan_filtering 1
+       ip link set dev sw0p1 master br0
+       ip link set dev sw0p2 master br0
+
+Enabling "switch"
+=================
+
+The Switch mode can be enabled by configuring devlink driver parameter
+"switch_mode" to 1/true::
+
+       devlink dev param set platform/48484000.switch \
+       name switch_mode value 1 cmode runtime
+
+This can be done regardless of the state of Port's netdev devices - UP/DOWN, but
+Port's netdev devices have to be in UP before joining to the bridge to avoid
+overwriting of bridge configuration as CPSW switch driver copletly reloads its
+configuration when first Port changes its state to UP.
+
+When the both interfaces joined the bridge - CPSW switch driver will enable
+marking packets with offload_fwd_mark flag unless "ale_bypass=0"
+
+All configuration is implemented via switchdev API.
+
+Bridge setup
+============
+
+::
+
+       devlink dev param set platform/48484000.switch \
+       name switch_mode value 1 cmode runtime
+
+       ip link add name br0 type bridge
+       ip link set dev br0 type bridge ageing_time 1000
+       ip link set dev sw0p1 up
+       ip link set dev sw0p2 up
+       ip link set dev sw0p1 master br0
+       ip link set dev sw0p2 master br0
+
+       [*] bridge vlan add dev br0 vid 1 pvid untagged self
+
+       [*] if vlan_filtering=1. where default_pvid=1
+
+       Note. Steps [*] are mandatory.
+
+
+On/off STP
+==========
+
+::
+
+       ip link set dev BRDEV type bridge stp_state 1/0
+
+VLAN configuration
+==================
+
+::
+
+  bridge vlan add dev br0 vid 1 pvid untagged self <---- add cpu port to VLAN 1
+
+Note. This step is mandatory for bridge/default_pvid.
+
+Add extra VLANs
+===============
+
+ 1. untagged::
+
+       bridge vlan add dev sw0p1 vid 100 pvid untagged master
+       bridge vlan add dev sw0p2 vid 100 pvid untagged master
+       bridge vlan add dev br0 vid 100 pvid untagged self <---- Add cpu port to VLAN100
+
+ 2. tagged::
+
+       bridge vlan add dev sw0p1 vid 100 master
+       bridge vlan add dev sw0p2 vid 100 master
+       bridge vlan add dev br0 vid 100 pvid tagged self <---- Add cpu port to VLAN100
+
+FDBs
+----
+
+FDBs are automatically added on the appropriate switch port upon detection
+
+Manually adding FDBs::
+
+    bridge fdb add aa:bb:cc:dd:ee:ff dev sw0p1 master vlan 100
+    bridge fdb add aa:bb:cc:dd:ee:fe dev sw0p2 master <---- Add on all VLANs
+
+MDBs
+----
+
+MDBs are automatically added on the appropriate switch port upon detection
+
+Manually adding MDBs::
+
+  bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent vid 100
+  bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent <---- Add on all VLANs
+
+Multicast flooding
+==================
+CPU port mcast_flooding is always on
+
+Turning flooding on/off on swithch ports:
+bridge link set dev sw0p1 mcast_flood on/off
+
+Access and Trunk port
+=====================
+
+::
+
+ bridge vlan add dev sw0p1 vid 100 pvid untagged master
+ bridge vlan add dev sw0p2 vid 100 master
+
+
+ bridge vlan add dev br0 vid 100 self
+ ip link add link br0 name br0.100 type vlan id 100
+
+Note. Setting PVID on Bridge device itself working only for
+default VLAN (default_pvid).
+
+NFS
+===
+
+The only way for NFS to work is by chrooting to a minimal environment when
+switch configuration that will affect connectivity is needed.
+Assuming you are booting NFS with eth1 interface(the script is hacky and
+it's just there to prove NFS is doable).
+
+setup.sh::
+
+       #!/bin/sh
+       mkdir proc
+       mount -t proc none /proc
+       ifconfig br0  > /dev/null
+       if [ $? -ne 0 ]; then
+               echo "Setting up bridge"
+               ip link add name br0 type bridge
+               ip link set dev br0 type bridge ageing_time 1000
+               ip link set dev br0 type bridge vlan_filtering 1
+
+               ip link set eth1 down
+               ip link set eth1 name sw0p1
+               ip link set dev sw0p1 up
+               ip link set dev sw0p2 up
+               ip link set dev sw0p2 master br0
+               ip link set dev sw0p1 master br0
+               bridge vlan add dev br0 vid 1 pvid untagged self
+               ifconfig sw0p1 0.0.0.0
+               udhchc -i br0
+       fi
+       umount /proc
+
+run_nfs.sh:::
+
+       #!/bin/sh
+       mkdir /tmp/root/bin -p
+       mkdir /tmp/root/lib -p
+
+       cp -r /lib/ /tmp/root/
+       cp -r /bin/ /tmp/root/
+       cp /sbin/ip /tmp/root/bin
+       cp /sbin/bridge /tmp/root/bin
+       cp /sbin/ifconfig /tmp/root/bin
+       cp /sbin/udhcpc /tmp/root/bin
+       cp /path/to/setup.sh /tmp/root/bin
+       chroot /tmp/root/ busybox sh /bin/setup.sh
+
+       run ./run_nfs.sh
diff --git a/Documentation/networking/device_drivers/ti/cpsw_switchdev.txt b/Documentation/networking/device_drivers/ti/cpsw_switchdev.txt
deleted file mode 100644 (file)
index 12855ab..0000000
+++ /dev/null
@@ -1,209 +0,0 @@
-* Texas Instruments CPSW switchdev based ethernet driver 2.0
-
-- Port renaming
-On older udev versions renaming of ethX to swXpY will not be automatically
-supported
-In order to rename via udev:
-ip -d link show dev sw0p1 | grep switchid
-
-SUBSYSTEM=="net", ACTION=="add", ATTR{phys_switch_id}==<switchid>, \
-        ATTR{phys_port_name}!="", NAME="sw0$attr{phys_port_name}"
-
-
-====================
-# Dual mac mode
-====================
-- The new (cpsw_new.c) driver is operating in dual-emac mode by default, thus
-working as 2 individual network interfaces. Main differences from legacy CPSW
-driver are:
- - optimized promiscuous mode: The P0_UNI_FLOOD (both ports) is enabled in
-addition to ALLMULTI (current port) instead of ALE_BYPASS.
-So, Ports in promiscuous mode will keep possibility of mcast and vlan filtering,
-which is provides significant benefits when ports are joined to the same bridge,
-but without enabling "switch" mode, or to different bridges.
- - learning disabled on ports as it make not too much sense for
-   segregated ports - no forwarding in HW.
- - enabled basic support for devlink.
-
-       devlink dev show
-               platform/48484000.switch
-
-       devlink dev param show
-       platform/48484000.switch:
-       name switch_mode type driver-specific
-       values:
-               cmode runtime value false
-       name ale_bypass type driver-specific
-       values:
-               cmode runtime value false
-
-Devlink configuration parameters
-====================
-See Documentation/networking/devlink/ti-cpsw-switch.rst
-
-====================
-# Bridging in dual mac mode
-====================
-The dual_mac mode requires two vids to be reserved for internal purposes,
-which, by default, equal CPSW Port numbers. As result, bridge has to be
-configured in vlan unaware mode or default_pvid has to be adjusted.
-
-       ip link add name br0 type bridge
-       ip link set dev br0 type bridge vlan_filtering 0
-       echo 0 > /sys/class/net/br0/bridge/default_pvid
-       ip link set dev sw0p1 master br0
-       ip link set dev sw0p2 master br0
- - or -
-       ip link add name br0 type bridge
-       ip link set dev br0 type bridge vlan_filtering 0
-       echo 100 > /sys/class/net/br0/bridge/default_pvid
-       ip link set dev br0 type bridge vlan_filtering 1
-       ip link set dev sw0p1 master br0
-       ip link set dev sw0p2 master br0
-
-====================
-# Enabling "switch"
-====================
-The Switch mode can be enabled by configuring devlink driver parameter
-"switch_mode" to 1/true:
-       devlink dev param set platform/48484000.switch \
-       name switch_mode value 1 cmode runtime
-
-This can be done regardless of the state of Port's netdev devices - UP/DOWN, but
-Port's netdev devices have to be in UP before joining to the bridge to avoid
-overwriting of bridge configuration as CPSW switch driver copletly reloads its
-configuration when first Port changes its state to UP.
-
-When the both interfaces joined the bridge - CPSW switch driver will enable
-marking packets with offload_fwd_mark flag unless "ale_bypass=0"
-
-All configuration is implemented via switchdev API.
-
-====================
-# Bridge setup
-====================
-       devlink dev param set platform/48484000.switch \
-       name switch_mode value 1 cmode runtime
-
-       ip link add name br0 type bridge
-       ip link set dev br0 type bridge ageing_time 1000
-       ip link set dev sw0p1 up
-       ip link set dev sw0p2 up
-       ip link set dev sw0p1 master br0
-       ip link set dev sw0p2 master br0
-       [*] bridge vlan add dev br0 vid 1 pvid untagged self
-
-[*] if vlan_filtering=1. where default_pvid=1
-
-=================
-# On/off STP
-=================
-ip link set dev BRDEV type bridge stp_state 1/0
-
-Note. Steps [*] are mandatory.
-
-====================
-# VLAN configuration
-====================
-bridge vlan add dev br0 vid 1 pvid untagged self <---- add cpu port to VLAN 1
-
-Note. This step is mandatory for bridge/default_pvid.
-
-=================
-# Add extra VLANs
-=================
- 1. untagged:
-    bridge vlan add dev sw0p1 vid 100 pvid untagged master
-    bridge vlan add dev sw0p2 vid 100 pvid untagged master
-    bridge vlan add dev br0 vid 100 pvid untagged self <---- Add cpu port to VLAN100
-
- 2. tagged:
-    bridge vlan add dev sw0p1 vid 100 master
-    bridge vlan add dev sw0p2 vid 100 master
-    bridge vlan add dev br0 vid 100 pvid tagged self <---- Add cpu port to VLAN100
-
-====
-FDBs
-====
-FDBs are automatically added on the appropriate switch port upon detection
-
-Manually adding FDBs:
-bridge fdb add aa:bb:cc:dd:ee:ff dev sw0p1 master vlan 100
-bridge fdb add aa:bb:cc:dd:ee:fe dev sw0p2 master <---- Add on all VLANs
-
-====
-MDBs
-====
-MDBs are automatically added on the appropriate switch port upon detection
-
-Manually adding MDBs:
-bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent vid 100
-bridge mdb add dev br0 port sw0p1 grp 239.1.1.1 permanent <---- Add on all VLANs
-
-==================
-Multicast flooding
-==================
-CPU port mcast_flooding is always on
-
-Turning flooding on/off on swithch ports:
-bridge link set dev sw0p1 mcast_flood on/off
-
-==================
-Access and Trunk port
-==================
- bridge vlan add dev sw0p1 vid 100 pvid untagged master
- bridge vlan add dev sw0p2 vid 100 master
-
-
- bridge vlan add dev br0 vid 100 self
- ip link add link br0 name br0.100 type vlan id 100
-
- Note. Setting PVID on Bridge device itself working only for
- default VLAN (default_pvid).
-
-=====================
- NFS
-=====================
-The only way for NFS to work is by chrooting to a minimal environment when
-switch configuration that will affect connectivity is needed.
-Assuming you are booting NFS with eth1 interface(the script is hacky and
-it's just there to prove NFS is doable).
-
-setup.sh:
-#!/bin/sh
-mkdir proc
-mount -t proc none /proc
-ifconfig br0  > /dev/null
-if [ $? -ne 0 ]; then
-        echo "Setting up bridge"
-        ip link add name br0 type bridge
-        ip link set dev br0 type bridge ageing_time 1000
-        ip link set dev br0 type bridge vlan_filtering 1
-
-        ip link set eth1 down
-        ip link set eth1 name sw0p1
-        ip link set dev sw0p1 up
-        ip link set dev sw0p2 up
-        ip link set dev sw0p2 master br0
-        ip link set dev sw0p1 master br0
-        bridge vlan add dev br0 vid 1 pvid untagged self
-        ifconfig sw0p1 0.0.0.0
-        udhchc -i br0
-fi
-umount /proc
-
-run_nfs.sh:
-#!/bin/sh
-mkdir /tmp/root/bin -p
-mkdir /tmp/root/lib -p
-
-cp -r /lib/ /tmp/root/
-cp -r /bin/ /tmp/root/
-cp /sbin/ip /tmp/root/bin
-cp /sbin/bridge /tmp/root/bin
-cp /sbin/ifconfig /tmp/root/bin
-cp /sbin/udhcpc /tmp/root/bin
-cp /path/to/setup.sh /tmp/root/bin
-chroot /tmp/root/ busybox sh /bin/setup.sh
-
-run ./run_nfs.sh
diff --git a/Documentation/networking/device_drivers/ti/tlan.rst b/Documentation/networking/device_drivers/ti/tlan.rst
new file mode 100644 (file)
index 0000000..4fdc090
--- /dev/null
@@ -0,0 +1,140 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================
+TLAN driver for Linux
+=====================
+
+:Version: 1.14a
+
+(C) 1997-1998 Caldera, Inc.
+
+(C) 1998 James Banks
+
+(C) 1999-2001 Torben Mathiasen <tmm@image.dk, torben.mathiasen@compaq.com>
+
+For driver information/updates visit http://www.compaq.com
+
+
+
+
+
+I. Supported Devices
+====================
+
+    Only PCI devices will work with this driver.
+
+    Supported:
+
+    =========  =========       ===========================================
+    Vendor ID  Device ID       Name
+    =========  =========       ===========================================
+    0e11       ae32            Compaq Netelligent 10/100 TX PCI UTP
+    0e11       ae34            Compaq Netelligent 10 T PCI UTP
+    0e11       ae35            Compaq Integrated NetFlex 3/P
+    0e11       ae40            Compaq Netelligent Dual 10/100 TX PCI UTP
+    0e11       ae43            Compaq Netelligent Integrated 10/100 TX UTP
+    0e11       b011            Compaq Netelligent 10/100 TX Embedded UTP
+    0e11       b012            Compaq Netelligent 10 T/2 PCI UTP/Coax
+    0e11       b030            Compaq Netelligent 10/100 TX UTP
+    0e11       f130            Compaq NetFlex 3/P
+    0e11       f150            Compaq NetFlex 3/P
+    108d       0012            Olicom OC-2325
+    108d       0013            Olicom OC-2183
+    108d       0014            Olicom OC-2326
+    =========  =========       ===========================================
+
+
+    Caveats:
+
+    I am not sure if 100BaseTX daughterboards (for those cards which
+    support such things) will work.  I haven't had any solid evidence
+    either way.
+
+    However, if a card supports 100BaseTx without requiring an add
+    on daughterboard, it should work with 100BaseTx.
+
+    The "Netelligent 10 T/2 PCI UTP/Coax" (b012) device is untested,
+    but I do not expect any problems.
+
+
+II. Driver Options
+==================
+
+       1. You can append debug=x to the end of the insmod line to get
+          debug messages, where x is a bit field where the bits mean
+          the following:
+
+          ====         =====================================
+          0x01         Turn on general debugging messages.
+          0x02         Turn on receive debugging messages.
+          0x04         Turn on transmit debugging messages.
+          0x08         Turn on list debugging messages.
+          ====         =====================================
+
+       2. You can append aui=1 to the end of the insmod line to cause
+          the adapter to use the AUI interface instead of the 10 Base T
+          interface.  This is also what to do if you want to use the BNC
+          connector on a TLAN based device.  (Setting this option on a
+          device that does not have an AUI/BNC connector will probably
+          cause it to not function correctly.)
+
+       3. You can set duplex=1 to force half duplex, and duplex=2 to
+          force full duplex.
+
+       4. You can set speed=10 to force 10Mbs operation, and speed=100
+          to force 100Mbs operation. (I'm not sure what will happen
+          if a card which only supports 10Mbs is forced into 100Mbs
+          mode.)
+
+       5. You have to use speed=X duplex=Y together now. If you just
+          do "insmod tlan.o speed=100" the driver will do Auto-Neg.
+          To force a 10Mbps Half-Duplex link do "insmod tlan.o speed=10
+          duplex=1".
+
+       6. If the driver is built into the kernel, you can use the 3rd
+          and 4th parameters to set aui and debug respectively.  For
+          example::
+
+               ether=0,0,0x1,0x7,eth0
+
+          This sets aui to 0x1 and debug to 0x7, assuming eth0 is a
+          supported TLAN device.
+
+          The bits in the third byte are assigned as follows:
+
+               ====   ===============
+               0x01   aui
+               0x02   use half duplex
+               0x04   use full duplex
+               0x08   use 10BaseT
+               0x10   use 100BaseTx
+               ====   ===============
+
+          You also need to set both speed and duplex settings when forcing
+          speeds with kernel-parameters.
+          ether=0,0,0x12,0,eth0 will force link to 100Mbps Half-Duplex.
+
+       7. If you have more than one tlan adapter in your system, you can
+          use the above options on a per adapter basis. To force a 100Mbit/HD
+          link with your eth1 adapter use::
+
+               insmod tlan speed=0,100 duplex=0,1
+
+          Now eth0 will use auto-neg and eth1 will be forced to 100Mbit/HD.
+          Note that the tlan driver supports a maximum of 8 adapters.
+
+
+III. Things to try if you have problems
+=======================================
+
+       1. Make sure your card's PCI id is among those listed in
+          section I, above.
+       2. Make sure routing is correct.
+       3. Try forcing different speed/duplex settings
+
+
+There is also a tlan mailing list which you can join by sending "subscribe tlan"
+in the body of an email to majordomo@vuser.vu.union.edu.
+
+There is also a tlan website at http://www.compaq.com
+
diff --git a/Documentation/networking/device_drivers/ti/tlan.txt b/Documentation/networking/device_drivers/ti/tlan.txt
deleted file mode 100644 (file)
index 34550df..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-(C) 1997-1998 Caldera, Inc.
-(C) 1998 James Banks
-(C) 1999-2001 Torben Mathiasen <tmm@image.dk, torben.mathiasen@compaq.com>
-
-For driver information/updates visit http://www.compaq.com
-
-
-TLAN driver for Linux, version 1.14a
-README
-
-
-I.  Supported Devices.
-
-    Only PCI devices will work with this driver.
-
-    Supported:
-    Vendor ID  Device ID       Name
-    0e11       ae32            Compaq Netelligent 10/100 TX PCI UTP
-    0e11       ae34            Compaq Netelligent 10 T PCI UTP
-    0e11       ae35            Compaq Integrated NetFlex 3/P
-    0e11       ae40            Compaq Netelligent Dual 10/100 TX PCI UTP
-    0e11       ae43            Compaq Netelligent Integrated 10/100 TX UTP
-    0e11       b011            Compaq Netelligent 10/100 TX Embedded UTP
-    0e11       b012            Compaq Netelligent 10 T/2 PCI UTP/Coax
-    0e11       b030            Compaq Netelligent 10/100 TX UTP
-    0e11       f130            Compaq NetFlex 3/P
-    0e11       f150            Compaq NetFlex 3/P
-    108d       0012            Olicom OC-2325  
-    108d       0013            Olicom OC-2183
-    108d       0014            Olicom OC-2326  
-
-
-    Caveats:
-    
-    I am not sure if 100BaseTX daughterboards (for those cards which
-    support such things) will work.  I haven't had any solid evidence
-    either way.
-
-    However, if a card supports 100BaseTx without requiring an add
-    on daughterboard, it should work with 100BaseTx.
-
-    The "Netelligent 10 T/2 PCI UTP/Coax" (b012) device is untested,
-    but I do not expect any problems.
-    
-
-II.   Driver Options
-       1. You can append debug=x to the end of the insmod line to get
-           debug messages, where x is a bit field where the bits mean
-          the following:
-          
-          0x01         Turn on general debugging messages.
-          0x02         Turn on receive debugging messages.
-          0x04         Turn on transmit debugging messages.
-          0x08         Turn on list debugging messages.
-
-       2. You can append aui=1 to the end of the insmod line to cause
-           the adapter to use the AUI interface instead of the 10 Base T
-           interface.  This is also what to do if you want to use the BNC
-          connector on a TLAN based device.  (Setting this option on a
-          device that does not have an AUI/BNC connector will probably
-          cause it to not function correctly.)
-
-       3. You can set duplex=1 to force half duplex, and duplex=2 to
-          force full duplex.
-
-       4. You can set speed=10 to force 10Mbs operation, and speed=100
-          to force 100Mbs operation. (I'm not sure what will happen
-          if a card which only supports 10Mbs is forced into 100Mbs
-          mode.)
-
-       5. You have to use speed=X duplex=Y together now. If you just
-          do "insmod tlan.o speed=100" the driver will do Auto-Neg.
-          To force a 10Mbps Half-Duplex link do "insmod tlan.o speed=10 
-          duplex=1".
-
-       6. If the driver is built into the kernel, you can use the 3rd
-          and 4th parameters to set aui and debug respectively.  For
-          example:
-
-          ether=0,0,0x1,0x7,eth0
-
-          This sets aui to 0x1 and debug to 0x7, assuming eth0 is a
-          supported TLAN device.
-
-          The bits in the third byte are assigned as follows:
-
-               0x01 = aui
-               0x02 = use half duplex
-               0x04 = use full duplex
-               0x08 = use 10BaseT
-               0x10 = use 100BaseTx
-
-          You also need to set both speed and duplex settings when forcing
-          speeds with kernel-parameters. 
-          ether=0,0,0x12,0,eth0 will force link to 100Mbps Half-Duplex.
-
-       7. If you have more than one tlan adapter in your system, you can
-          use the above options on a per adapter basis. To force a 100Mbit/HD
-          link with your eth1 adapter use:
-          
-          insmod tlan speed=0,100 duplex=0,1
-
-          Now eth0 will use auto-neg and eth1 will be forced to 100Mbit/HD.
-          Note that the tlan driver supports a maximum of 8 adapters.
-
-
-III.  Things to try if you have problems.
-       1. Make sure your card's PCI id is among those listed in
-          section I, above.
-       2. Make sure routing is correct.
-       3. Try forcing different speed/duplex settings
-
-
-There is also a tlan mailing list which you can join by sending "subscribe tlan"
-in the body of an email to majordomo@vuser.vu.union.edu.
-There is also a tlan website at http://www.compaq.com
-
diff --git a/Documentation/networking/device_drivers/toshiba/spider_net.rst b/Documentation/networking/device_drivers/toshiba/spider_net.rst
new file mode 100644 (file)
index 0000000..fe5b32b
--- /dev/null
@@ -0,0 +1,202 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================
+The Spidernet Device Driver
+===========================
+
+Written by Linas Vepstas <linas@austin.ibm.com>
+
+Version of 7 June 2007
+
+Abstract
+========
+This document sketches the structure of portions of the spidernet
+device driver in the Linux kernel tree. The spidernet is a gigabit
+ethernet device built into the Toshiba southbridge commonly used
+in the SONY Playstation 3 and the IBM QS20 Cell blade.
+
+The Structure of the RX Ring.
+=============================
+The receive (RX) ring is a circular linked list of RX descriptors,
+together with three pointers into the ring that are used to manage its
+contents.
+
+The elements of the ring are called "descriptors" or "descrs"; they
+describe the received data. This includes a pointer to a buffer
+containing the received data, the buffer size, and various status bits.
+
+There are three primary states that a descriptor can be in: "empty",
+"full" and "not-in-use".  An "empty" or "ready" descriptor is ready
+to receive data from the hardware. A "full" descriptor has data in it,
+and is waiting to be emptied and processed by the OS. A "not-in-use"
+descriptor is neither empty or full; it is simply not ready. It may
+not even have a data buffer in it, or is otherwise unusable.
+
+During normal operation, on device startup, the OS (specifically, the
+spidernet device driver) allocates a set of RX descriptors and RX
+buffers. These are all marked "empty", ready to receive data. This
+ring is handed off to the hardware, which sequentially fills in the
+buffers, and marks them "full". The OS follows up, taking the full
+buffers, processing them, and re-marking them empty.
+
+This filling and emptying is managed by three pointers, the "head"
+and "tail" pointers, managed by the OS, and a hardware current
+descriptor pointer (GDACTDPA). The GDACTDPA points at the descr
+currently being filled. When this descr is filled, the hardware
+marks it full, and advances the GDACTDPA by one.  Thus, when there is
+flowing RX traffic, every descr behind it should be marked "full",
+and everything in front of it should be "empty".  If the hardware
+discovers that the current descr is not empty, it will signal an
+interrupt, and halt processing.
+
+The tail pointer tails or trails the hardware pointer. When the
+hardware is ahead, the tail pointer will be pointing at a "full"
+descr. The OS will process this descr, and then mark it "not-in-use",
+and advance the tail pointer.  Thus, when there is flowing RX traffic,
+all of the descrs in front of the tail pointer should be "full", and
+all of those behind it should be "not-in-use". When RX traffic is not
+flowing, then the tail pointer can catch up to the hardware pointer.
+The OS will then note that the current tail is "empty", and halt
+processing.
+
+The head pointer (somewhat mis-named) follows after the tail pointer.
+When traffic is flowing, then the head pointer will be pointing at
+a "not-in-use" descr. The OS will perform various housekeeping duties
+on this descr. This includes allocating a new data buffer and
+dma-mapping it so as to make it visible to the hardware. The OS will
+then mark the descr as "empty", ready to receive data. Thus, when there
+is flowing RX traffic, everything in front of the head pointer should
+be "not-in-use", and everything behind it should be "empty". If no
+RX traffic is flowing, then the head pointer can catch up to the tail
+pointer, at which point the OS will notice that the head descr is
+"empty", and it will halt processing.
+
+Thus, in an idle system, the GDACTDPA, tail and head pointers will
+all be pointing at the same descr, which should be "empty". All of the
+other descrs in the ring should be "empty" as well.
+
+The show_rx_chain() routine will print out the locations of the
+GDACTDPA, tail and head pointers. It will also summarize the contents
+of the ring, starting at the tail pointer, and listing the status
+of the descrs that follow.
+
+A typical example of the output, for a nearly idle system, might be::
+
+    net eth1: Total number of descrs=256
+    net eth1: Chain tail located at descr=20
+    net eth1: Chain head is at 20
+    net eth1: HW curr desc (GDACTDPA) is at 21
+    net eth1: Have 1 descrs with stat=x40800101
+    net eth1: HW next desc (GDACNEXTDA) is at 22
+    net eth1: Last 255 descrs with stat=xa0800000
+
+In the above, the hardware has filled in one descr, number 20. Both
+head and tail are pointing at 20, because it has not yet been emptied.
+Meanwhile, hw is pointing at 21, which is free.
+
+The "Have nnn decrs" refers to the descr starting at the tail: in this
+case, nnn=1 descr, starting at descr 20. The "Last nnn descrs" refers
+to all of the rest of the descrs, from the last status change. The "nnn"
+is a count of how many descrs have exactly the same status.
+
+The status x4... corresponds to "full" and status xa... corresponds
+to "empty". The actual value printed is RXCOMST_A.
+
+In the device driver source code, a different set of names are
+used for these same concepts, so that::
+
+    "empty" == SPIDER_NET_DESCR_CARDOWNED == 0xa
+    "full"  == SPIDER_NET_DESCR_FRAME_END == 0x4
+    "not in use" == SPIDER_NET_DESCR_NOT_IN_USE == 0xf
+
+
+The RX RAM full bug/feature
+===========================
+
+As long as the OS can empty out the RX buffers at a rate faster than
+the hardware can fill them, there is no problem. If, for some reason,
+the OS fails to empty the RX ring fast enough, the hardware GDACTDPA
+pointer will catch up to the head, notice the not-empty condition,
+ad stop. However, RX packets may still continue arriving on the wire.
+The spidernet chip can save some limited number of these in local RAM.
+When this local ram fills up, the spider chip will issue an interrupt
+indicating this (GHIINT0STS will show ERRINT, and the GRMFLLINT bit
+will be set in GHIINT1STS).  When the RX ram full condition occurs,
+a certain bug/feature is triggered that has to be specially handled.
+This section describes the special handling for this condition.
+
+When the OS finally has a chance to run, it will empty out the RX ring.
+In particular, it will clear the descriptor on which the hardware had
+stopped. However, once the hardware has decided that a certain
+descriptor is invalid, it will not restart at that descriptor; instead
+it will restart at the next descr. This potentially will lead to a
+deadlock condition, as the tail pointer will be pointing at this descr,
+which, from the OS point of view, is empty; the OS will be waiting for
+this descr to be filled. However, the hardware has skipped this descr,
+and is filling the next descrs. Since the OS doesn't see this, there
+is a potential deadlock, with the OS waiting for one descr to fill,
+while the hardware is waiting for a different set of descrs to become
+empty.
+
+A call to show_rx_chain() at this point indicates the nature of the
+problem. A typical print when the network is hung shows the following::
+
+    net eth1: Spider RX RAM full, incoming packets might be discarded!
+    net eth1: Total number of descrs=256
+    net eth1: Chain tail located at descr=255
+    net eth1: Chain head is at 255
+    net eth1: HW curr desc (GDACTDPA) is at 0
+    net eth1: Have 1 descrs with stat=xa0800000
+    net eth1: HW next desc (GDACNEXTDA) is at 1
+    net eth1: Have 127 descrs with stat=x40800101
+    net eth1: Have 1 descrs with stat=x40800001
+    net eth1: Have 126 descrs with stat=x40800101
+    net eth1: Last 1 descrs with stat=xa0800000
+
+Both the tail and head pointers are pointing at descr 255, which is
+marked xa... which is "empty". Thus, from the OS point of view, there
+is nothing to be done. In particular, there is the implicit assumption
+that everything in front of the "empty" descr must surely also be empty,
+as explained in the last section. The OS is waiting for descr 255 to
+become non-empty, which, in this case, will never happen.
+
+The HW pointer is at descr 0. This descr is marked 0x4.. or "full".
+Since its already full, the hardware can do nothing more, and thus has
+halted processing. Notice that descrs 0 through 254 are all marked
+"full", while descr 254 and 255 are empty. (The "Last 1 descrs" is
+descr 254, since tail was at 255.) Thus, the system is deadlocked,
+and there can be no forward progress; the OS thinks there's nothing
+to do, and the hardware has nowhere to put incoming data.
+
+This bug/feature is worked around with the spider_net_resync_head_ptr()
+routine. When the driver receives RX interrupts, but an examination
+of the RX chain seems to show it is empty, then it is probable that
+the hardware has skipped a descr or two (sometimes dozens under heavy
+network conditions). The spider_net_resync_head_ptr() subroutine will
+search the ring for the next full descr, and the driver will resume
+operations there.  Since this will leave "holes" in the ring, there
+is also a spider_net_resync_tail_ptr() that will skip over such holes.
+
+As of this writing, the spider_net_resync() strategy seems to work very
+well, even under heavy network loads.
+
+
+The TX ring
+===========
+The TX ring uses a low-watermark interrupt scheme to make sure that
+the TX queue is appropriately serviced for large packet sizes.
+
+For packet sizes greater than about 1KBytes, the kernel can fill
+the TX ring quicker than the device can drain it. Once the ring
+is full, the netdev is stopped. When there is room in the ring,
+the netdev needs to be reawakened, so that more TX packets are placed
+in the ring. The hardware can empty the ring about four times per jiffy,
+so its not appropriate to wait for the poll routine to refill, since
+the poll routine runs only once per jiffy.  The low-watermark mechanism
+marks a descr about 1/4th of the way from the bottom of the queue, so
+that an interrupt is generated when the descr is processed. This
+interrupt wakes up the netdev, which can then refill the queue.
+For large packets, this mechanism generates a relatively small number
+of interrupts, about 1K/sec. For smaller packets, this will drop to zero
+interrupts, as the hardware can empty the queue faster than the kernel
+can fill it.
diff --git a/Documentation/networking/device_drivers/toshiba/spider_net.txt b/Documentation/networking/device_drivers/toshiba/spider_net.txt
deleted file mode 100644 (file)
index b0b75f8..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-
-            The Spidernet Device Driver
-            ===========================
-
-Written by Linas Vepstas <linas@austin.ibm.com>
-
-Version of 7 June 2007
-
-Abstract
-========
-This document sketches the structure of portions of the spidernet
-device driver in the Linux kernel tree. The spidernet is a gigabit
-ethernet device built into the Toshiba southbridge commonly used
-in the SONY Playstation 3 and the IBM QS20 Cell blade.
-
-The Structure of the RX Ring.
-=============================
-The receive (RX) ring is a circular linked list of RX descriptors,
-together with three pointers into the ring that are used to manage its
-contents.
-
-The elements of the ring are called "descriptors" or "descrs"; they
-describe the received data. This includes a pointer to a buffer
-containing the received data, the buffer size, and various status bits.
-
-There are three primary states that a descriptor can be in: "empty",
-"full" and "not-in-use".  An "empty" or "ready" descriptor is ready
-to receive data from the hardware. A "full" descriptor has data in it,
-and is waiting to be emptied and processed by the OS. A "not-in-use"
-descriptor is neither empty or full; it is simply not ready. It may
-not even have a data buffer in it, or is otherwise unusable.
-
-During normal operation, on device startup, the OS (specifically, the
-spidernet device driver) allocates a set of RX descriptors and RX
-buffers. These are all marked "empty", ready to receive data. This
-ring is handed off to the hardware, which sequentially fills in the
-buffers, and marks them "full". The OS follows up, taking the full
-buffers, processing them, and re-marking them empty.
-
-This filling and emptying is managed by three pointers, the "head"
-and "tail" pointers, managed by the OS, and a hardware current
-descriptor pointer (GDACTDPA). The GDACTDPA points at the descr
-currently being filled. When this descr is filled, the hardware
-marks it full, and advances the GDACTDPA by one.  Thus, when there is
-flowing RX traffic, every descr behind it should be marked "full",
-and everything in front of it should be "empty".  If the hardware
-discovers that the current descr is not empty, it will signal an
-interrupt, and halt processing.
-
-The tail pointer tails or trails the hardware pointer. When the
-hardware is ahead, the tail pointer will be pointing at a "full"
-descr. The OS will process this descr, and then mark it "not-in-use",
-and advance the tail pointer.  Thus, when there is flowing RX traffic,
-all of the descrs in front of the tail pointer should be "full", and
-all of those behind it should be "not-in-use". When RX traffic is not
-flowing, then the tail pointer can catch up to the hardware pointer.
-The OS will then note that the current tail is "empty", and halt
-processing.
-
-The head pointer (somewhat mis-named) follows after the tail pointer.
-When traffic is flowing, then the head pointer will be pointing at
-a "not-in-use" descr. The OS will perform various housekeeping duties
-on this descr. This includes allocating a new data buffer and
-dma-mapping it so as to make it visible to the hardware. The OS will
-then mark the descr as "empty", ready to receive data. Thus, when there
-is flowing RX traffic, everything in front of the head pointer should
-be "not-in-use", and everything behind it should be "empty". If no
-RX traffic is flowing, then the head pointer can catch up to the tail
-pointer, at which point the OS will notice that the head descr is
-"empty", and it will halt processing.
-
-Thus, in an idle system, the GDACTDPA, tail and head pointers will
-all be pointing at the same descr, which should be "empty". All of the
-other descrs in the ring should be "empty" as well.
-
-The show_rx_chain() routine will print out the locations of the
-GDACTDPA, tail and head pointers. It will also summarize the contents
-of the ring, starting at the tail pointer, and listing the status
-of the descrs that follow.
-
-A typical example of the output, for a nearly idle system, might be
-
-net eth1: Total number of descrs=256
-net eth1: Chain tail located at descr=20
-net eth1: Chain head is at 20
-net eth1: HW curr desc (GDACTDPA) is at 21
-net eth1: Have 1 descrs with stat=x40800101
-net eth1: HW next desc (GDACNEXTDA) is at 22
-net eth1: Last 255 descrs with stat=xa0800000
-
-In the above, the hardware has filled in one descr, number 20. Both
-head and tail are pointing at 20, because it has not yet been emptied.
-Meanwhile, hw is pointing at 21, which is free.
-
-The "Have nnn decrs" refers to the descr starting at the tail: in this
-case, nnn=1 descr, starting at descr 20. The "Last nnn descrs" refers
-to all of the rest of the descrs, from the last status change. The "nnn"
-is a count of how many descrs have exactly the same status.
-
-The status x4... corresponds to "full" and status xa... corresponds
-to "empty". The actual value printed is RXCOMST_A.
-
-In the device driver source code, a different set of names are
-used for these same concepts, so that
-
-"empty" == SPIDER_NET_DESCR_CARDOWNED == 0xa
-"full"  == SPIDER_NET_DESCR_FRAME_END == 0x4
-"not in use" == SPIDER_NET_DESCR_NOT_IN_USE == 0xf
-
-
-The RX RAM full bug/feature
-===========================
-
-As long as the OS can empty out the RX buffers at a rate faster than
-the hardware can fill them, there is no problem. If, for some reason,
-the OS fails to empty the RX ring fast enough, the hardware GDACTDPA
-pointer will catch up to the head, notice the not-empty condition,
-ad stop. However, RX packets may still continue arriving on the wire.
-The spidernet chip can save some limited number of these in local RAM.
-When this local ram fills up, the spider chip will issue an interrupt
-indicating this (GHIINT0STS will show ERRINT, and the GRMFLLINT bit
-will be set in GHIINT1STS).  When the RX ram full condition occurs,
-a certain bug/feature is triggered that has to be specially handled.
-This section describes the special handling for this condition.
-
-When the OS finally has a chance to run, it will empty out the RX ring.
-In particular, it will clear the descriptor on which the hardware had
-stopped. However, once the hardware has decided that a certain
-descriptor is invalid, it will not restart at that descriptor; instead
-it will restart at the next descr. This potentially will lead to a
-deadlock condition, as the tail pointer will be pointing at this descr,
-which, from the OS point of view, is empty; the OS will be waiting for
-this descr to be filled. However, the hardware has skipped this descr,
-and is filling the next descrs. Since the OS doesn't see this, there
-is a potential deadlock, with the OS waiting for one descr to fill,
-while the hardware is waiting for a different set of descrs to become
-empty.
-
-A call to show_rx_chain() at this point indicates the nature of the
-problem. A typical print when the network is hung shows the following:
-
-net eth1: Spider RX RAM full, incoming packets might be discarded!
-net eth1: Total number of descrs=256
-net eth1: Chain tail located at descr=255
-net eth1: Chain head is at 255
-net eth1: HW curr desc (GDACTDPA) is at 0
-net eth1: Have 1 descrs with stat=xa0800000
-net eth1: HW next desc (GDACNEXTDA) is at 1
-net eth1: Have 127 descrs with stat=x40800101
-net eth1: Have 1 descrs with stat=x40800001
-net eth1: Have 126 descrs with stat=x40800101
-net eth1: Last 1 descrs with stat=xa0800000
-
-Both the tail and head pointers are pointing at descr 255, which is
-marked xa... which is "empty". Thus, from the OS point of view, there
-is nothing to be done. In particular, there is the implicit assumption
-that everything in front of the "empty" descr must surely also be empty,
-as explained in the last section. The OS is waiting for descr 255 to
-become non-empty, which, in this case, will never happen.
-
-The HW pointer is at descr 0. This descr is marked 0x4.. or "full".
-Since its already full, the hardware can do nothing more, and thus has
-halted processing. Notice that descrs 0 through 254 are all marked
-"full", while descr 254 and 255 are empty. (The "Last 1 descrs" is
-descr 254, since tail was at 255.) Thus, the system is deadlocked,
-and there can be no forward progress; the OS thinks there's nothing
-to do, and the hardware has nowhere to put incoming data.
-
-This bug/feature is worked around with the spider_net_resync_head_ptr()
-routine. When the driver receives RX interrupts, but an examination
-of the RX chain seems to show it is empty, then it is probable that
-the hardware has skipped a descr or two (sometimes dozens under heavy
-network conditions). The spider_net_resync_head_ptr() subroutine will
-search the ring for the next full descr, and the driver will resume
-operations there.  Since this will leave "holes" in the ring, there
-is also a spider_net_resync_tail_ptr() that will skip over such holes.
-
-As of this writing, the spider_net_resync() strategy seems to work very
-well, even under heavy network loads.
-
-
-The TX ring
-===========
-The TX ring uses a low-watermark interrupt scheme to make sure that
-the TX queue is appropriately serviced for large packet sizes.
-
-For packet sizes greater than about 1KBytes, the kernel can fill
-the TX ring quicker than the device can drain it. Once the ring
-is full, the netdev is stopped. When there is room in the ring,
-the netdev needs to be reawakened, so that more TX packets are placed
-in the ring. The hardware can empty the ring about four times per jiffy,
-so its not appropriate to wait for the poll routine to refill, since
-the poll routine runs only once per jiffy.  The low-watermark mechanism
-marks a descr about 1/4th of the way from the bottom of the queue, so
-that an interrupt is generated when the descr is processed. This
-interrupt wakes up the netdev, which can then refill the queue.
-For large packets, this mechanism generates a relatively small number
-of interrupts, about 1K/sec. For smaller packets, this will drop to zero
-interrupts, as the hardware can empty the queue faster than the kernel
-can fill it.
-
-
- ======= END OF DOCUMENT ========
-
index 04e04d1..3654c3e 100644 (file)
@@ -14,6 +14,10 @@ Region snapshots are collected by the driver, and can be accessed via read
 or dump commands. This allows future analysis on the created snapshots.
 Regions may optionally support triggering snapshots on demand.
 
+Snapshot identifiers are scoped to the devlink instance, not a region.
+All snapshots with the same snapshot id within a devlink instance
+correspond to the same event.
+
 The major benefit to creating a region is to provide access to internal
 address regions that are otherwise inaccessible to the user.
 
@@ -23,7 +27,9 @@ states, but see also :doc:`devlink-health`
 Regions may optionally support capturing a snapshot on demand via the
 ``DEVLINK_CMD_REGION_NEW`` netlink message. A driver wishing to allow
 requested snapshots must implement the ``.snapshot`` callback for the region
-in its ``devlink_region_ops`` structure.
+in its ``devlink_region_ops`` structure. If snapshot id is not set in
+the ``DEVLINK_CMD_REGION_NEW`` request kernel will allocate one and send
+the snapshot information to user space.
 
 example usage
 -------------
@@ -45,7 +51,8 @@ example usage
     $ devlink region del pci/0000:00:05.0/cr-space snapshot 1
 
     # Request an immediate snapshot, if supported by the region
-    $ devlink region new pci/0000:00:05.0/cr-space snapshot 5
+    $ devlink region new pci/0000:00:05.0/cr-space
+    pci/0000:00:05.0/cr-space: snapshot 5
 
     # Dump a snapshot:
     $ devlink region dump pci/0000:00:05.0/fw-health snapshot 1
index a09971c..fe089ac 100644 (file)
@@ -257,6 +257,8 @@ drivers:
   * :doc:`netdevsim`
   * :doc:`mlxsw`
 
+.. _Generic-Packet-Trap-Groups:
+
 Generic Packet Trap Groups
 ==========================
 
index 5b58fc4..4574352 100644 (file)
@@ -61,8 +61,8 @@ The ``ice`` driver reports the following versions
       - running
       - ICE OS Default Package
       - The name of the DDP package that is active in the device. The DDP
-        package is loaded by the driver during initialization. Each varation
-        of DDP package shall have a unique name.
+        package is loaded by the driver during initialization. Each
+        variation of the DDP package has a unique name.
     * - ``fw.app``
       - running
       - 1.3.1.0
diff --git a/Documentation/networking/dns_resolver.rst b/Documentation/networking/dns_resolver.rst
new file mode 100644 (file)
index 0000000..add4d59
--- /dev/null
@@ -0,0 +1,155 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================
+DNS Resolver Module
+===================
+
+.. Contents:
+
+ - Overview.
+ - Compilation.
+ - Setting up.
+ - Usage.
+ - Mechanism.
+ - Debugging.
+
+
+Overview
+========
+
+The DNS resolver module provides a way for kernel services to make DNS queries
+by way of requesting a key of key type dns_resolver.  These queries are
+upcalled to userspace through /sbin/request-key.
+
+These routines must be supported by userspace tools dns.upcall, cifs.upcall and
+request-key.  It is under development and does not yet provide the full feature
+set.  The features it does support include:
+
+ (*) Implements the dns_resolver key_type to contact userspace.
+
+It does not yet support the following AFS features:
+
+ (*) Dns query support for AFSDB resource record.
+
+This code is extracted from the CIFS filesystem.
+
+
+Compilation
+===========
+
+The module should be enabled by turning on the kernel configuration options::
+
+       CONFIG_DNS_RESOLVER     - tristate "DNS Resolver support"
+
+
+Setting up
+==========
+
+To set up this facility, the /etc/request-key.conf file must be altered so that
+/sbin/request-key can appropriately direct the upcalls.  For example, to handle
+basic dname to IPv4/IPv6 address resolution, the following line should be
+added::
+
+
+       #OP     TYPE            DESC    CO-INFO PROGRAM ARG1 ARG2 ARG3 ...
+       #====== ============    ======= ======= ==========================
+       create  dns_resolver    *       *       /usr/sbin/cifs.upcall %k
+
+To direct a query for query type 'foo', a line of the following should be added
+before the more general line given above as the first match is the one taken::
+
+       create  dns_resolver    foo:*   *       /usr/sbin/dns.foo %k
+
+
+Usage
+=====
+
+To make use of this facility, one of the following functions that are
+implemented in the module can be called after doing::
+
+       #include <linux/dns_resolver.h>
+
+     ::
+
+       int dns_query(const char *type, const char *name, size_t namelen,
+                    const char *options, char **_result, time_t *_expiry);
+
+     This is the basic access function.  It looks for a cached DNS query and if
+     it doesn't find it, it upcalls to userspace to make a new DNS query, which
+     may then be cached.  The key description is constructed as a string of the
+     form::
+
+               [<type>:]<name>
+
+     where <type> optionally specifies the particular upcall program to invoke,
+     and thus the type of query to do, and <name> specifies the string to be
+     looked up.  The default query type is a straight hostname to IP address
+     set lookup.
+
+     The name parameter is not required to be a NUL-terminated string, and its
+     length should be given by the namelen argument.
+
+     The options parameter may be NULL or it may be a set of options
+     appropriate to the query type.
+
+     The return value is a string appropriate to the query type.  For instance,
+     for the default query type it is just a list of comma-separated IPv4 and
+     IPv6 addresses.  The caller must free the result.
+
+     The length of the result string is returned on success, and a negative
+     error code is returned otherwise.  -EKEYREJECTED will be returned if the
+     DNS lookup failed.
+
+     If _expiry is non-NULL, the expiry time (TTL) of the result will be
+     returned also.
+
+The kernel maintains an internal keyring in which it caches looked up keys.
+This can be cleared by any process that has the CAP_SYS_ADMIN capability by
+the use of KEYCTL_KEYRING_CLEAR on the keyring ID.
+
+
+Reading DNS Keys from Userspace
+===============================
+
+Keys of dns_resolver type can be read from userspace using keyctl_read() or
+"keyctl read/print/pipe".
+
+
+Mechanism
+=========
+
+The dnsresolver module registers a key type called "dns_resolver".  Keys of
+this type are used to transport and cache DNS lookup results from userspace.
+
+When dns_query() is invoked, it calls request_key() to search the local
+keyrings for a cached DNS result.  If that fails to find one, it upcalls to
+userspace to get a new result.
+
+Upcalls to userspace are made through the request_key() upcall vector, and are
+directed by means of configuration lines in /etc/request-key.conf that tell
+/sbin/request-key what program to run to instantiate the key.
+
+The upcall handler program is responsible for querying the DNS, processing the
+result into a form suitable for passing to the keyctl_instantiate_key()
+routine.  This then passes the data to dns_resolver_instantiate() which strips
+off and processes any options included in the data, and then attaches the
+remainder of the string to the key as its payload.
+
+The upcall handler program should set the expiry time on the key to that of the
+lowest TTL of all the records it has extracted a result from.  This means that
+the key will be discarded and recreated when the data it holds has expired.
+
+dns_query() returns a copy of the value attached to the key, or an error if
+that is indicated instead.
+
+See <file:Documentation/security/keys/request-key.rst> for further
+information about request-key function.
+
+
+Debugging
+=========
+
+Debugging messages can be turned on dynamically by writing a 1 into the
+following file::
+
+       /sys/module/dnsresolver/parameters/debug
diff --git a/Documentation/networking/dns_resolver.txt b/Documentation/networking/dns_resolver.txt
deleted file mode 100644 (file)
index eaa8f9a..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-                            ===================
-                            DNS Resolver Module
-                            ===================
-
-Contents:
-
- - Overview.
- - Compilation.
- - Setting up.
- - Usage.
- - Mechanism.
- - Debugging.
-
-
-========
-OVERVIEW
-========
-
-The DNS resolver module provides a way for kernel services to make DNS queries
-by way of requesting a key of key type dns_resolver.  These queries are
-upcalled to userspace through /sbin/request-key.
-
-These routines must be supported by userspace tools dns.upcall, cifs.upcall and
-request-key.  It is under development and does not yet provide the full feature
-set.  The features it does support include:
-
- (*) Implements the dns_resolver key_type to contact userspace.
-
-It does not yet support the following AFS features:
-
- (*) Dns query support for AFSDB resource record.
-
-This code is extracted from the CIFS filesystem.
-
-
-===========
-COMPILATION
-===========
-
-The module should be enabled by turning on the kernel configuration options:
-
-       CONFIG_DNS_RESOLVER     - tristate "DNS Resolver support"
-
-
-==========
-SETTING UP
-==========
-
-To set up this facility, the /etc/request-key.conf file must be altered so that
-/sbin/request-key can appropriately direct the upcalls.  For example, to handle
-basic dname to IPv4/IPv6 address resolution, the following line should be
-added:
-
-       #OP     TYPE            DESC    CO-INFO PROGRAM ARG1 ARG2 ARG3 ...
-       #====== ============    ======= ======= ==========================
-       create  dns_resolver    *       *       /usr/sbin/cifs.upcall %k
-
-To direct a query for query type 'foo', a line of the following should be added
-before the more general line given above as the first match is the one taken.
-
-       create  dns_resolver    foo:*   *       /usr/sbin/dns.foo %k
-
-
-=====
-USAGE
-=====
-
-To make use of this facility, one of the following functions that are
-implemented in the module can be called after doing:
-
-       #include <linux/dns_resolver.h>
-
- (1) int dns_query(const char *type, const char *name, size_t namelen,
-                  const char *options, char **_result, time_t *_expiry);
-
-     This is the basic access function.  It looks for a cached DNS query and if
-     it doesn't find it, it upcalls to userspace to make a new DNS query, which
-     may then be cached.  The key description is constructed as a string of the
-     form:
-
-               [<type>:]<name>
-
-     where <type> optionally specifies the particular upcall program to invoke,
-     and thus the type of query to do, and <name> specifies the string to be
-     looked up.  The default query type is a straight hostname to IP address
-     set lookup.
-
-     The name parameter is not required to be a NUL-terminated string, and its
-     length should be given by the namelen argument.
-
-     The options parameter may be NULL or it may be a set of options
-     appropriate to the query type.
-
-     The return value is a string appropriate to the query type.  For instance,
-     for the default query type it is just a list of comma-separated IPv4 and
-     IPv6 addresses.  The caller must free the result.
-
-     The length of the result string is returned on success, and a negative
-     error code is returned otherwise.  -EKEYREJECTED will be returned if the
-     DNS lookup failed.
-
-     If _expiry is non-NULL, the expiry time (TTL) of the result will be
-     returned also.
-
-The kernel maintains an internal keyring in which it caches looked up keys.
-This can be cleared by any process that has the CAP_SYS_ADMIN capability by
-the use of KEYCTL_KEYRING_CLEAR on the keyring ID.
-
-
-===============================
-READING DNS KEYS FROM USERSPACE
-===============================
-
-Keys of dns_resolver type can be read from userspace using keyctl_read() or
-"keyctl read/print/pipe".
-
-
-=========
-MECHANISM
-=========
-
-The dnsresolver module registers a key type called "dns_resolver".  Keys of
-this type are used to transport and cache DNS lookup results from userspace.
-
-When dns_query() is invoked, it calls request_key() to search the local
-keyrings for a cached DNS result.  If that fails to find one, it upcalls to
-userspace to get a new result.
-
-Upcalls to userspace are made through the request_key() upcall vector, and are
-directed by means of configuration lines in /etc/request-key.conf that tell
-/sbin/request-key what program to run to instantiate the key.
-
-The upcall handler program is responsible for querying the DNS, processing the
-result into a form suitable for passing to the keyctl_instantiate_key()
-routine.  This then passes the data to dns_resolver_instantiate() which strips
-off and processes any options included in the data, and then attaches the
-remainder of the string to the key as its payload.
-
-The upcall handler program should set the expiry time on the key to that of the
-lowest TTL of all the records it has extracted a result from.  This means that
-the key will be discarded and recreated when the data it holds has expired.
-
-dns_query() returns a copy of the value attached to the key, or an error if
-that is indicated instead.
-
-See <file:Documentation/security/keys/request-key.rst> for further
-information about request-key function.
-
-
-=========
-DEBUGGING
-=========
-
-Debugging messages can be turned on dynamically by writing a 1 into the
-following file:
-
-        /sys/module/dnsresolver/parameters/debug
diff --git a/Documentation/networking/driver.rst b/Documentation/networking/driver.rst
new file mode 100644 (file)
index 0000000..c8f59db
--- /dev/null
@@ -0,0 +1,97 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================
+Softnet Driver Issues
+=====================
+
+Transmit path guidelines:
+
+1) The ndo_start_xmit method must not return NETDEV_TX_BUSY under
+   any normal circumstances.  It is considered a hard error unless
+   there is no way your device can tell ahead of time when it's
+   transmit function will become busy.
+
+   Instead it must maintain the queue properly.  For example,
+   for a driver implementing scatter-gather this means::
+
+       static netdev_tx_t drv_hard_start_xmit(struct sk_buff *skb,
+                                              struct net_device *dev)
+       {
+               struct drv *dp = netdev_priv(dev);
+
+               lock_tx(dp);
+               ...
+               /* This is a hard error log it. */
+               if (TX_BUFFS_AVAIL(dp) <= (skb_shinfo(skb)->nr_frags + 1)) {
+                       netif_stop_queue(dev);
+                       unlock_tx(dp);
+                       printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n",
+                              dev->name);
+                       return NETDEV_TX_BUSY;
+               }
+
+               ... queue packet to card ...
+               ... update tx consumer index ...
+
+               if (TX_BUFFS_AVAIL(dp) <= (MAX_SKB_FRAGS + 1))
+                       netif_stop_queue(dev);
+
+               ...
+               unlock_tx(dp);
+               ...
+               return NETDEV_TX_OK;
+       }
+
+   And then at the end of your TX reclamation event handling::
+
+       if (netif_queue_stopped(dp->dev) &&
+           TX_BUFFS_AVAIL(dp) > (MAX_SKB_FRAGS + 1))
+               netif_wake_queue(dp->dev);
+
+   For a non-scatter-gather supporting card, the three tests simply become::
+
+               /* This is a hard error log it. */
+               if (TX_BUFFS_AVAIL(dp) <= 0)
+
+   and::
+
+               if (TX_BUFFS_AVAIL(dp) == 0)
+
+   and::
+
+       if (netif_queue_stopped(dp->dev) &&
+           TX_BUFFS_AVAIL(dp) > 0)
+               netif_wake_queue(dp->dev);
+
+2) An ndo_start_xmit method must not modify the shared parts of a
+   cloned SKB.
+
+3) Do not forget that once you return NETDEV_TX_OK from your
+   ndo_start_xmit method, it is your driver's responsibility to free
+   up the SKB and in some finite amount of time.
+
+   For example, this means that it is not allowed for your TX
+   mitigation scheme to let TX packets "hang out" in the TX
+   ring unreclaimed forever if no new TX packets are sent.
+   This error can deadlock sockets waiting for send buffer room
+   to be freed up.
+
+   If you return NETDEV_TX_BUSY from the ndo_start_xmit method, you
+   must not keep any reference to that SKB and you must not attempt
+   to free it up.
+
+Probing guidelines:
+
+1) Any hardware layer address you obtain for your device should
+   be verified.  For example, for ethernet check it with
+   linux/etherdevice.h:is_valid_ether_addr()
+
+Close/stop guidelines:
+
+1) After the ndo_stop routine has been called, the hardware must
+   not receive or transmit any data.  All in flight packets must
+   be aborted. If necessary, poll or wait for completion of
+   any reset commands.
+
+2) The ndo_stop routine will be called by unregister_netdevice
+   if device is still UP.
diff --git a/Documentation/networking/driver.txt b/Documentation/networking/driver.txt
deleted file mode 100644 (file)
index da59e28..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-Document about softnet driver issues
-
-Transmit path guidelines:
-
-1) The ndo_start_xmit method must not return NETDEV_TX_BUSY under
-   any normal circumstances.  It is considered a hard error unless
-   there is no way your device can tell ahead of time when it's
-   transmit function will become busy.
-
-   Instead it must maintain the queue properly.  For example,
-   for a driver implementing scatter-gather this means:
-
-       static netdev_tx_t drv_hard_start_xmit(struct sk_buff *skb,
-                                              struct net_device *dev)
-       {
-               struct drv *dp = netdev_priv(dev);
-
-               lock_tx(dp);
-               ...
-               /* This is a hard error log it. */
-               if (TX_BUFFS_AVAIL(dp) <= (skb_shinfo(skb)->nr_frags + 1)) {
-                       netif_stop_queue(dev);
-                       unlock_tx(dp);
-                       printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n",
-                              dev->name);
-                       return NETDEV_TX_BUSY;
-               }
-
-               ... queue packet to card ...
-               ... update tx consumer index ...
-
-               if (TX_BUFFS_AVAIL(dp) <= (MAX_SKB_FRAGS + 1))
-                       netif_stop_queue(dev);
-
-               ...
-               unlock_tx(dp);
-               ...
-               return NETDEV_TX_OK;
-       }
-
-   And then at the end of your TX reclamation event handling:
-
-       if (netif_queue_stopped(dp->dev) &&
-            TX_BUFFS_AVAIL(dp) > (MAX_SKB_FRAGS + 1))
-               netif_wake_queue(dp->dev);
-
-   For a non-scatter-gather supporting card, the three tests simply become:
-
-               /* This is a hard error log it. */
-               if (TX_BUFFS_AVAIL(dp) <= 0)
-
-   and:
-
-               if (TX_BUFFS_AVAIL(dp) == 0)
-
-   and:
-
-       if (netif_queue_stopped(dp->dev) &&
-            TX_BUFFS_AVAIL(dp) > 0)
-               netif_wake_queue(dp->dev);
-
-2) An ndo_start_xmit method must not modify the shared parts of a
-   cloned SKB.
-
-3) Do not forget that once you return NETDEV_TX_OK from your
-   ndo_start_xmit method, it is your driver's responsibility to free
-   up the SKB and in some finite amount of time.
-
-   For example, this means that it is not allowed for your TX
-   mitigation scheme to let TX packets "hang out" in the TX
-   ring unreclaimed forever if no new TX packets are sent.
-   This error can deadlock sockets waiting for send buffer room
-   to be freed up.
-
-   If you return NETDEV_TX_BUSY from the ndo_start_xmit method, you
-   must not keep any reference to that SKB and you must not attempt
-   to free it up.
-
-Probing guidelines:
-
-1) Any hardware layer address you obtain for your device should
-   be verified.  For example, for ethernet check it with
-   linux/etherdevice.h:is_valid_ether_addr()
-
-Close/stop guidelines:
-
-1) After the ndo_stop routine has been called, the hardware must
-   not receive or transmit any data.  All in flight packets must
-   be aborted. If necessary, poll or wait for completion of 
-   any reset commands.
-
-2) The ndo_stop routine will be called by unregister_netdevice
-   if device is still UP.
diff --git a/Documentation/networking/eql.rst b/Documentation/networking/eql.rst
new file mode 100644 (file)
index 0000000..a628c4c
--- /dev/null
@@ -0,0 +1,373 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==========================================
+EQL Driver: Serial IP Load Balancing HOWTO
+==========================================
+
+  Simon "Guru Aleph-Null" Janes, simon@ncm.com
+
+  v1.1, February 27, 1995
+
+  This is the manual for the EQL device driver. EQL is a software device
+  that lets you load-balance IP serial links (SLIP or uncompressed PPP)
+  to increase your bandwidth. It will not reduce your latency (i.e. ping
+  times) except in the case where you already have lots of traffic on
+  your link, in which it will help them out. This driver has been tested
+  with the 1.1.75 kernel, and is known to have patched cleanly with
+  1.1.86.  Some testing with 1.1.92 has been done with the v1.1 patch
+  which was only created to patch cleanly in the very latest kernel
+  source trees. (Yes, it worked fine.)
+
+1. Introduction
+===============
+
+  Which is worse? A huge fee for a 56K leased line or two phone lines?
+  It's probably the former.  If you find yourself craving more bandwidth,
+  and have a ISP that is flexible, it is now possible to bind modems
+  together to work as one point-to-point link to increase your
+  bandwidth.  All without having to have a special black box on either
+  side.
+
+
+  The eql driver has only been tested with the Livingston PortMaster-2e
+  terminal server. I do not know if other terminal servers support load-
+  balancing, but I do know that the PortMaster does it, and does it
+  almost as well as the eql driver seems to do it (-- Unfortunately, in
+  my testing so far, the Livingston PortMaster 2e's load-balancing is a
+  good 1 to 2 KB/s slower than the test machine working with a 28.8 Kbps
+  and 14.4 Kbps connection.  However, I am not sure that it really is
+  the PortMaster, or if it's Linux's TCP drivers. I'm told that Linux's
+  TCP implementation is pretty fast though.--)
+
+
+  I suggest to ISPs out there that it would probably be fair to charge
+  a load-balancing client 75% of the cost of the second line and 50% of
+  the cost of the third line etc...
+
+
+  Hey, we can all dream you know...
+
+
+2. Kernel Configuration
+=======================
+
+  Here I describe the general steps of getting a kernel up and working
+  with the eql driver. From patching, building, to installing.
+
+
+2.1. Patching The Kernel
+------------------------
+
+  If you do not have or cannot get a copy of the kernel with the eql
+  driver folded into it, get your copy of the driver from
+  ftp://slaughter.ncm.com/pub/Linux/LOAD_BALANCING/eql-1.1.tar.gz.
+  Unpack this archive someplace obvious like /usr/local/src/.  It will
+  create the following files::
+
+       -rw-r--r-- guru/ncm     198 Jan 19 18:53 1995 eql-1.1/NO-WARRANTY
+       -rw-r--r-- guru/ncm     30620 Feb 27 21:40 1995 eql-1.1/eql-1.1.patch
+       -rwxr-xr-x guru/ncm     16111 Jan 12 22:29 1995 eql-1.1/eql_enslave
+       -rw-r--r-- guru/ncm     2195 Jan 10 21:48 1995 eql-1.1/eql_enslave.c
+
+  Unpack a recent kernel (something after 1.1.92) someplace convenient
+  like say /usr/src/linux-1.1.92.eql. Use symbolic links to point
+  /usr/src/linux to this development directory.
+
+
+  Apply the patch by running the commands::
+
+       cd /usr/src
+       patch </usr/local/src/eql-1.1/eql-1.1.patch
+
+
+2.2. Building The Kernel
+------------------------
+
+  After patching the kernel, run make config and configure the kernel
+  for your hardware.
+
+
+  After configuration, make and install according to your habit.
+
+
+3. Network Configuration
+========================
+
+  So far, I have only used the eql device with the DSLIP SLIP connection
+  manager by Matt Dillon (-- "The man who sold his soul to code so much
+  so quickly."--) .  How you configure it for other "connection"
+  managers is up to you.  Most other connection managers that I've seen
+  don't do a very good job when it comes to handling more than one
+  connection.
+
+
+3.1. /etc/rc.d/rc.inet1
+-----------------------
+
+  In rc.inet1, ifconfig the eql device to the IP address you usually use
+  for your machine, and the MTU you prefer for your SLIP lines.        One
+  could argue that MTU should be roughly half the usual size for two
+  modems, one-third for three, one-fourth for four, etc...  But going
+  too far below 296 is probably overkill. Here is an example ifconfig
+  command that sets up the eql device::
+
+       ifconfig eql 198.67.33.239 mtu 1006
+
+  Once the eql device is up and running, add a static default route to
+  it in the routing table using the cool new route syntax that makes
+  life so much easier::
+
+       route add default eql
+
+
+3.2. Enslaving Devices By Hand
+------------------------------
+
+  Enslaving devices by hand requires two utility programs: eql_enslave
+  and eql_emancipate (-- eql_emancipate hasn't been written because when
+  an enslaved device "dies", it is automatically taken out of the queue.
+  I haven't found a good reason to write it yet... other than for
+  completeness, but that isn't a good motivator is it?--)
+
+
+  The syntax for enslaving a device is "eql_enslave <master-name>
+  <slave-name> <estimated-bps>".  Here are some example enslavings::
+
+       eql_enslave eql sl0 28800
+       eql_enslave eql ppp0 14400
+       eql_enslave eql sl1 57600
+
+  When you want to free a device from its life of slavery, you can
+  either down the device with ifconfig (eql will automatically bury the
+  dead slave and remove it from its queue) or use eql_emancipate to free
+  it. (-- Or just ifconfig it down, and the eql driver will take it out
+  for you.--)::
+
+       eql_emancipate eql sl0
+       eql_emancipate eql ppp0
+       eql_emancipate eql sl1
+
+
+3.3. DSLIP Configuration for the eql Device
+-------------------------------------------
+
+  The general idea is to bring up and keep up as many SLIP connections
+  as you need, automatically.
+
+
+3.3.1.  /etc/slip/runslip.conf
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+  Here is an example runslip.conf::
+
+         name          sl-line-1
+         enabled
+         baud          38400
+         mtu           576
+         ducmd         -e /etc/slip/dialout/cua2-288.xp -t 9
+         command        eql_enslave eql $interface 28800
+         address        198.67.33.239
+         line          /dev/cua2
+
+         name          sl-line-2
+         enabled
+         baud          38400
+         mtu           576
+         ducmd         -e /etc/slip/dialout/cua3-288.xp -t 9
+         command        eql_enslave eql $interface 28800
+         address        198.67.33.239
+         line          /dev/cua3
+
+
+3.4. Using PPP and the eql Device
+---------------------------------
+
+  I have not yet done any load-balancing testing for PPP devices, mainly
+  because I don't have a PPP-connection manager like SLIP has with
+  DSLIP. I did find a good tip from LinuxNET:Billy for PPP performance:
+  make sure you have asyncmap set to something so that control
+  characters are not escaped.
+
+
+  I tried to fix up a PPP script/system for redialing lost PPP
+  connections for use with the eql driver the weekend of Feb 25-26 '95
+  (Hereafter known as the 8-hour PPP Hate Festival).  Perhaps later this
+  year.
+
+
+4. About the Slave Scheduler Algorithm
+======================================
+
+  The slave scheduler probably could be replaced with a dozen other
+  things and push traffic much faster. The formula in the current set
+  up of the driver was tuned to handle slaves with wildly different
+  bits-per-second "priorities".
+
+
+  All testing I have done was with two 28.8 V.FC modems, one connecting
+  at 28800 bps or slower, and the other connecting at 14400 bps all the
+  time.
+
+
+  One version of the scheduler was able to push 5.3 K/s through the
+  28800 and 14400 connections, but when the priorities on the links were
+  very wide apart (57600 vs. 14400) the "faster" modem received all
+  traffic and the "slower" modem starved.
+
+
+5. Testers' Reports
+===================
+
+  Some people have experimented with the eql device with newer
+  kernels (than 1.1.75).  I have since updated the driver to patch
+  cleanly in newer kernels because of the removal of the old "slave-
+  balancing" driver config option.
+
+
+  -  icee from LinuxNET patched 1.1.86 without any rejects and was able
+     to boot the kernel and enslave a couple of ISDN PPP links.
+
+5.1. Randolph Bentson's Test Report
+-----------------------------------
+
+  ::
+
+    From bentson@grieg.seaslug.org Wed Feb  8 19:08:09 1995
+    Date: Tue, 7 Feb 95 22:57 PST
+    From: Randolph Bentson <bentson@grieg.seaslug.org>
+    To: guru@ncm.com
+    Subject: EQL driver tests
+
+
+    I have been checking out your eql driver.  (Nice work, that!)
+    Although you may already done this performance testing, here
+    are some data I've discovered.
+
+    Randolph Bentson
+    bentson@grieg.seaslug.org
+
+------------------------------------------------------------------
+
+
+  A pseudo-device driver, EQL, written by Simon Janes, can be used
+  to bundle multiple SLIP connections into what appears to be a
+  single connection.  This allows one to improve dial-up network
+  connectivity gradually, without having to buy expensive DSU/CSU
+  hardware and services.
+
+  I have done some testing of this software, with two goals in
+  mind: first, to ensure it actually works as described and
+  second, as a method of exercising my device driver.
+
+  The following performance measurements were derived from a set
+  of SLIP connections run between two Linux systems (1.1.84) using
+  a 486DX2/66 with a Cyclom-8Ys and a 486SLC/40 with a Cyclom-16Y.
+  (Ports 0,1,2,3 were used.  A later configuration will distribute
+  port selection across the different Cirrus chips on the boards.)
+  Once a link was established, I timed a binary ftp transfer of
+  289284 bytes of data.        If there were no overhead (packet headers,
+  inter-character and inter-packet delays, etc.) the transfers
+  would take the following times::
+
+      bits/sec seconds
+      345600   8.3
+      234600   12.3
+      172800   16.7
+      153600   18.8
+      76800    37.6
+      57600    50.2
+      38400    75.3
+      28800    100.4
+      19200    150.6
+      9600     301.3
+
+  A single line running at the lower speeds and with large packets
+  comes to within 2% of this.  Performance is limited for the higher
+  speeds (as predicted by the Cirrus databook) to an aggregate of
+  about 160 kbits/sec. The next round of testing will distribute
+  the load across two or more Cirrus chips.
+
+  The good news is that one gets nearly the full advantage of the
+  second, third, and fourth line's bandwidth.  (The bad news is
+  that the connection establishment seemed fragile for the higher
+  speeds.  Once established, the connection seemed robust enough.)
+
+  ======  ========     ===  ========   ======= ======= ===
+  #lines  speed                mtu  seconds    theory  actual  %of
+         kbit/sec           duration   speed   speed   max
+  ======  ========     ===  ========   ======= ======= ===
+  3      115200        900     _       345600
+  3      115200        400     18.1    345600  159825  46
+  2      115200        900     _       230400
+  2      115200        600     18.1    230400  159825  69
+  2      115200        400     19.3    230400  149888  65
+  4      57600         900     _       234600
+  4      57600         600     _       234600
+  4      57600         400     _       234600
+  3      57600         600     20.9    172800  138413  80
+  3      57600         900     21.2    172800  136455  78
+  3      115200        600     21.7    345600  133311  38
+  3      57600         400     22.5    172800  128571  74
+  4      38400         900     25.2    153600  114795  74
+  4      38400         600     26.4    153600  109577  71
+  4      38400         400     27.3    153600  105965  68
+  2      57600         900     29.1    115200  99410.3 86
+  1      115200        900     30.7    115200  94229.3 81
+  2      57600         600     30.2    115200  95789.4 83
+  3      38400         900     30.3    115200  95473.3 82
+  3      38400         600     31.2    115200  92719.2 80
+  1      115200        600     31.3    115200  92423   80
+  2      57600         400     32.3    115200  89561.6 77
+  1      115200        400     32.8    115200  88196.3 76
+  3      38400         400     33.5    115200  86353.4 74
+  2      38400         900     43.7    76800   66197.7 86
+  2      38400         600     44      76800   65746.4 85
+  2      38400         400     47.2    76800   61289   79
+  4      19200         900     50.8    76800   56945.7 74
+  4      19200         400     53.2    76800   54376.7 70
+  4      19200         600     53.7    76800   53870.4 70
+  1      57600         900     54.6    57600   52982.4 91
+  1      57600         600     56.2    57600   51474   89
+  3      19200         900     60.5    57600   47815.5 83
+  1      57600         400     60.2    57600   48053.8 83
+  3      19200         600     62      57600   46658.7 81
+  3      19200         400     64.7    57600   44711.6 77
+  1      38400         900     79.4    38400   36433.8 94
+  1      38400         600     82.4    38400   35107.3 91
+  2      19200         900     84.4    38400   34275.4 89
+  1      38400         400     86.8    38400   33327.6 86
+  2      19200         600     87.6    38400   33023.3 85
+  2      19200         400     91.2    38400   31719.7 82
+  4      9600          900     94.7    38400   30547.4 79
+  4      9600          400     106     38400   27290.9 71
+  4      9600          600     110     38400   26298.5 68
+  3      9600          900     118     28800   24515.6 85
+  3      9600          600     120     28800   24107   83
+  3      9600          400     131     28800   22082.7 76
+  1      19200         900     155     19200   18663.5 97
+  1      19200         600     161     19200   17968   93
+  1      19200         400     170     19200   17016.7 88
+  2      9600          600     176     19200   16436.6 85
+  2      9600          900     180     19200   16071.3 83
+  2      9600          400     181     19200   15982.5 83
+  1      9600          900     305     9600    9484.72 98
+  1      9600          600     314     9600    9212.87 95
+  1      9600          400     332     9600    8713.37 90
+  ======  ========     ===  ========   ======= ======= ===
+
+5.2. Anthony Healy's Report
+---------------------------
+
+  ::
+
+    Date: Mon, 13 Feb 1995 16:17:29 +1100 (EST)
+    From: Antony Healey <ahealey@st.nepean.uws.edu.au>
+    To: Simon Janes <guru@ncm.com>
+    Subject: Re: Load Balancing
+
+    Hi Simon,
+         I've installed your patch and it works great. I have trialed
+         it over twin SL/IP lines, just over null modems, but I was
+         able to data at over 48Kb/s [ISDN link -Simon]. I managed a
+         transfer of up to 7.5 Kbyte/s on one go, but averaged around
+         6.4 Kbyte/s, which I think is pretty cool.  :)
diff --git a/Documentation/networking/eql.txt b/Documentation/networking/eql.txt
deleted file mode 100644 (file)
index 0f15501..0000000
+++ /dev/null
@@ -1,528 +0,0 @@
-  EQL Driver: Serial IP Load Balancing HOWTO
-  Simon "Guru Aleph-Null" Janes, simon@ncm.com
-  v1.1, February 27, 1995
-
-  This is the manual for the EQL device driver. EQL is a software device
-  that lets you load-balance IP serial links (SLIP or uncompressed PPP)
-  to increase your bandwidth. It will not reduce your latency (i.e. ping
-  times) except in the case where you already have lots of traffic on
-  your link, in which it will help them out. This driver has been tested
-  with the 1.1.75 kernel, and is known to have patched cleanly with
-  1.1.86.  Some testing with 1.1.92 has been done with the v1.1 patch
-  which was only created to patch cleanly in the very latest kernel
-  source trees. (Yes, it worked fine.)
-
-  1.  Introduction
-
-  Which is worse? A huge fee for a 56K leased line or two phone lines?
-  It's probably the former.  If you find yourself craving more bandwidth,
-  and have a ISP that is flexible, it is now possible to bind modems
-  together to work as one point-to-point link to increase your
-  bandwidth.  All without having to have a special black box on either
-  side.
-
-
-  The eql driver has only been tested with the Livingston PortMaster-2e
-  terminal server. I do not know if other terminal servers support load-
-  balancing, but I do know that the PortMaster does it, and does it
-  almost as well as the eql driver seems to do it (-- Unfortunately, in
-  my testing so far, the Livingston PortMaster 2e's load-balancing is a
-  good 1 to 2 KB/s slower than the test machine working with a 28.8 Kbps
-  and 14.4 Kbps connection.  However, I am not sure that it really is
-  the PortMaster, or if it's Linux's TCP drivers. I'm told that Linux's
-  TCP implementation is pretty fast though.--)
-
-
-  I suggest to ISPs out there that it would probably be fair to charge
-  a load-balancing client 75% of the cost of the second line and 50% of
-  the cost of the third line etc...
-
-
-  Hey, we can all dream you know...
-
-
-  2.  Kernel Configuration
-
-  Here I describe the general steps of getting a kernel up and working
-  with the eql driver. From patching, building, to installing.
-
-
-  2.1. Patching The Kernel
-
-  If you do not have or cannot get a copy of the kernel with the eql
-  driver folded into it, get your copy of the driver from
-  ftp://slaughter.ncm.com/pub/Linux/LOAD_BALANCING/eql-1.1.tar.gz.
-  Unpack this archive someplace obvious like /usr/local/src/.  It will
-  create the following files:
-
-
-
-       ______________________________________________________________________
-       -rw-r--r-- guru/ncm     198 Jan 19 18:53 1995 eql-1.1/NO-WARRANTY
-       -rw-r--r-- guru/ncm     30620 Feb 27 21:40 1995 eql-1.1/eql-1.1.patch
-       -rwxr-xr-x guru/ncm     16111 Jan 12 22:29 1995 eql-1.1/eql_enslave
-       -rw-r--r-- guru/ncm     2195 Jan 10 21:48 1995 eql-1.1/eql_enslave.c
-       ______________________________________________________________________
-
-  Unpack a recent kernel (something after 1.1.92) someplace convenient
-  like say /usr/src/linux-1.1.92.eql. Use symbolic links to point
-  /usr/src/linux to this development directory.
-
-
-  Apply the patch by running the commands:
-
-
-       ______________________________________________________________________
-       cd /usr/src
-       patch </usr/local/src/eql-1.1/eql-1.1.patch
-       ______________________________________________________________________
-
-
-
-
-
-  2.2. Building The Kernel
-
-  After patching the kernel, run make config and configure the kernel
-  for your hardware.
-
-
-  After configuration, make and install according to your habit.
-
-
-  3.  Network Configuration
-
-  So far, I have only used the eql device with the DSLIP SLIP connection
-  manager by Matt Dillon (-- "The man who sold his soul to code so much
-  so quickly."--) .  How you configure it for other "connection"
-  managers is up to you.  Most other connection managers that I've seen
-  don't do a very good job when it comes to handling more than one
-  connection.
-
-
-  3.1. /etc/rc.d/rc.inet1
-
-  In rc.inet1, ifconfig the eql device to the IP address you usually use
-  for your machine, and the MTU you prefer for your SLIP lines.        One
-  could argue that MTU should be roughly half the usual size for two
-  modems, one-third for three, one-fourth for four, etc...  But going
-  too far below 296 is probably overkill. Here is an example ifconfig
-  command that sets up the eql device:
-
-
-
-       ______________________________________________________________________
-       ifconfig eql 198.67.33.239 mtu 1006
-       ______________________________________________________________________
-
-
-
-
-
-  Once the eql device is up and running, add a static default route to
-  it in the routing table using the cool new route syntax that makes
-  life so much easier:
-
-
-
-       ______________________________________________________________________
-       route add default eql
-       ______________________________________________________________________
-
-
-  3.2. Enslaving Devices By Hand
-
-  Enslaving devices by hand requires two utility programs: eql_enslave
-  and eql_emancipate (-- eql_emancipate hasn't been written because when
-  an enslaved device "dies", it is automatically taken out of the queue.
-  I haven't found a good reason to write it yet... other than for
-  completeness, but that isn't a good motivator is it?--)
-
-
-  The syntax for enslaving a device is "eql_enslave <master-name>
-  <slave-name> <estimated-bps>".  Here are some example enslavings:
-
-
-
-       ______________________________________________________________________
-       eql_enslave eql sl0 28800
-       eql_enslave eql ppp0 14400
-       eql_enslave eql sl1 57600
-       ______________________________________________________________________
-
-
-
-
-
-  When you want to free a device from its life of slavery, you can
-  either down the device with ifconfig (eql will automatically bury the
-  dead slave and remove it from its queue) or use eql_emancipate to free
-  it. (-- Or just ifconfig it down, and the eql driver will take it out
-  for you.--)
-
-
-
-       ______________________________________________________________________
-       eql_emancipate eql sl0
-       eql_emancipate eql ppp0
-       eql_emancipate eql sl1
-       ______________________________________________________________________
-
-
-
-
-
-  3.3. DSLIP Configuration for the eql Device
-
-  The general idea is to bring up and keep up as many SLIP connections
-  as you need, automatically.
-
-
-  3.3.1.  /etc/slip/runslip.conf
-
-  Here is an example runslip.conf:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-  ______________________________________________________________________
-  name         sl-line-1
-  enabled
-  baud         38400
-  mtu          576
-  ducmd                -e /etc/slip/dialout/cua2-288.xp -t 9
-  command       eql_enslave eql $interface 28800
-  address       198.67.33.239
-  line         /dev/cua2
-
-  name         sl-line-2
-  enabled
-  baud         38400
-  mtu          576
-  ducmd                -e /etc/slip/dialout/cua3-288.xp -t 9
-  command       eql_enslave eql $interface 28800
-  address       198.67.33.239
-  line         /dev/cua3
-  ______________________________________________________________________
-
-
-
-
-
-  3.4. Using PPP and the eql Device
-
-  I have not yet done any load-balancing testing for PPP devices, mainly
-  because I don't have a PPP-connection manager like SLIP has with
-  DSLIP. I did find a good tip from LinuxNET:Billy for PPP performance:
-  make sure you have asyncmap set to something so that control
-  characters are not escaped.
-
-
-  I tried to fix up a PPP script/system for redialing lost PPP
-  connections for use with the eql driver the weekend of Feb 25-26 '95
-  (Hereafter known as the 8-hour PPP Hate Festival).  Perhaps later this
-  year.
-
-
-  4.  About the Slave Scheduler Algorithm
-
-  The slave scheduler probably could be replaced with a dozen other
-  things and push traffic much faster. The formula in the current set
-  up of the driver was tuned to handle slaves with wildly different
-  bits-per-second "priorities".
-
-
-  All testing I have done was with two 28.8 V.FC modems, one connecting
-  at 28800 bps or slower, and the other connecting at 14400 bps all the
-  time.
-
-
-  One version of the scheduler was able to push 5.3 K/s through the
-  28800 and 14400 connections, but when the priorities on the links were
-  very wide apart (57600 vs. 14400) the "faster" modem received all
-  traffic and the "slower" modem starved.
-
-
-  5.  Testers' Reports
-
-  Some people have experimented with the eql device with newer
-  kernels (than 1.1.75).  I have since updated the driver to patch
-  cleanly in newer kernels because of the removal of the old "slave-
-  balancing" driver config option.
-
-
-  o  icee from LinuxNET patched 1.1.86 without any rejects and was able
-     to boot the kernel and enslave a couple of ISDN PPP links.
-
-  5.1. Randolph Bentson's Test Report
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-  From bentson@grieg.seaslug.org Wed Feb  8 19:08:09 1995
-  Date: Tue, 7 Feb 95 22:57 PST
-  From: Randolph Bentson <bentson@grieg.seaslug.org>
-  To: guru@ncm.com
-  Subject: EQL driver tests
-
-
-  I have been checking out your eql driver.  (Nice work, that!)
-  Although you may already done this performance testing, here
-  are some data I've discovered.
-
-  Randolph Bentson
-  bentson@grieg.seaslug.org
-
-  ---------------------------------------------------------
-
-
-  A pseudo-device driver, EQL, written by Simon Janes, can be used
-  to bundle multiple SLIP connections into what appears to be a
-  single connection.  This allows one to improve dial-up network
-  connectivity gradually, without having to buy expensive DSU/CSU
-  hardware and services.
-
-  I have done some testing of this software, with two goals in
-  mind: first, to ensure it actually works as described and
-  second, as a method of exercising my device driver.
-
-  The following performance measurements were derived from a set
-  of SLIP connections run between two Linux systems (1.1.84) using
-  a 486DX2/66 with a Cyclom-8Ys and a 486SLC/40 with a Cyclom-16Y.
-  (Ports 0,1,2,3 were used.  A later configuration will distribute
-  port selection across the different Cirrus chips on the boards.)
-  Once a link was established, I timed a binary ftp transfer of
-  289284 bytes of data.        If there were no overhead (packet headers,
-  inter-character and inter-packet delays, etc.) the transfers
-  would take the following times:
-
-      bits/sec seconds
-      345600   8.3
-      234600   12.3
-      172800   16.7
-      153600   18.8
-      76800    37.6
-      57600    50.2
-      38400    75.3
-      28800    100.4
-      19200    150.6
-      9600     301.3
-
-  A single line running at the lower speeds and with large packets
-  comes to within 2% of this.  Performance is limited for the higher
-  speeds (as predicted by the Cirrus databook) to an aggregate of
-  about 160 kbits/sec. The next round of testing will distribute
-  the load across two or more Cirrus chips.
-
-  The good news is that one gets nearly the full advantage of the
-  second, third, and fourth line's bandwidth.  (The bad news is
-  that the connection establishment seemed fragile for the higher
-  speeds.  Once established, the connection seemed robust enough.)
-
-  #lines  speed        mtu  seconds    theory  actual  %of
-        kbit/sec      duration speed   speed   max
-  3    115200  900     _       345600
-  3    115200  400     18.1    345600  159825  46
-  2    115200  900     _       230400
-  2    115200  600     18.1    230400  159825  69
-  2    115200  400     19.3    230400  149888  65
-  4    57600   900     _       234600
-  4    57600   600     _       234600
-  4    57600   400     _       234600
-  3    57600   600     20.9    172800  138413  80
-  3    57600   900     21.2    172800  136455  78
-  3    115200  600     21.7    345600  133311  38
-  3    57600   400     22.5    172800  128571  74
-  4    38400   900     25.2    153600  114795  74
-  4    38400   600     26.4    153600  109577  71
-  4    38400   400     27.3    153600  105965  68
-  2    57600   900     29.1    115200  99410.3 86
-  1    115200  900     30.7    115200  94229.3 81
-  2    57600   600     30.2    115200  95789.4 83
-  3    38400   900     30.3    115200  95473.3 82
-  3    38400   600     31.2    115200  92719.2 80
-  1    115200  600     31.3    115200  92423   80
-  2    57600   400     32.3    115200  89561.6 77
-  1    115200  400     32.8    115200  88196.3 76
-  3    38400   400     33.5    115200  86353.4 74
-  2    38400   900     43.7    76800   66197.7 86
-  2    38400   600     44      76800   65746.4 85
-  2    38400   400     47.2    76800   61289   79
-  4    19200   900     50.8    76800   56945.7 74
-  4    19200   400     53.2    76800   54376.7 70
-  4    19200   600     53.7    76800   53870.4 70
-  1    57600   900     54.6    57600   52982.4 91
-  1    57600   600     56.2    57600   51474   89
-  3    19200   900     60.5    57600   47815.5 83
-  1    57600   400     60.2    57600   48053.8 83
-  3    19200   600     62      57600   46658.7 81
-  3    19200   400     64.7    57600   44711.6 77
-  1    38400   900     79.4    38400   36433.8 94
-  1    38400   600     82.4    38400   35107.3 91
-  2    19200   900     84.4    38400   34275.4 89
-  1    38400   400     86.8    38400   33327.6 86
-  2    19200   600     87.6    38400   33023.3 85
-  2    19200   400     91.2    38400   31719.7 82
-  4    9600    900     94.7    38400   30547.4 79
-  4    9600    400     106     38400   27290.9 71
-  4    9600    600     110     38400   26298.5 68
-  3    9600    900     118     28800   24515.6 85
-  3    9600    600     120     28800   24107   83
-  3    9600    400     131     28800   22082.7 76
-  1    19200   900     155     19200   18663.5 97
-  1    19200   600     161     19200   17968   93
-  1    19200   400     170     19200   17016.7 88
-  2    9600    600     176     19200   16436.6 85
-  2    9600    900     180     19200   16071.3 83
-  2    9600    400     181     19200   15982.5 83
-  1    9600    900     305     9600    9484.72 98
-  1    9600    600     314     9600    9212.87 95
-  1    9600    400     332     9600    8713.37 90
-
-
-
-
-
-  5.2. Anthony Healy's Report
-
-
-
-
-
-
-
-  Date: Mon, 13 Feb 1995 16:17:29 +1100 (EST)
-  From: Antony Healey <ahealey@st.nepean.uws.edu.au>
-  To: Simon Janes <guru@ncm.com>
-  Subject: Re: Load Balancing
-
-  Hi Simon,
-         I've installed your patch and it works great. I have trialed
-         it over twin SL/IP lines, just over null modems, but I was
-         able to data at over 48Kb/s [ISDN link -Simon]. I managed a
-         transfer of up to 7.5 Kbyte/s on one go, but averaged around
-         6.4 Kbyte/s, which I think is pretty cool.  :)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
index 5673264..8f5cefc 100644 (file)
@@ -392,14 +392,16 @@ Request contents:
 
 Kernel response contents:
 
-  ====================================  ======  ==========================
-  ``ETHTOOL_A_LINKMODES_HEADER``        nested  reply header
-  ``ETHTOOL_A_LINKMODES_AUTONEG``       u8      autonegotiation status
-  ``ETHTOOL_A_LINKMODES_OURS``          bitset  advertised link modes
-  ``ETHTOOL_A_LINKMODES_PEER``          bitset  partner link modes
-  ``ETHTOOL_A_LINKMODES_SPEED``         u32     link speed (Mb/s)
-  ``ETHTOOL_A_LINKMODES_DUPLEX``        u8      duplex mode
-  ====================================  ======  ==========================
+  ==========================================  ======  ==========================
+  ``ETHTOOL_A_LINKMODES_HEADER``              nested  reply header
+  ``ETHTOOL_A_LINKMODES_AUTONEG``             u8      autonegotiation status
+  ``ETHTOOL_A_LINKMODES_OURS``                bitset  advertised link modes
+  ``ETHTOOL_A_LINKMODES_PEER``                bitset  partner link modes
+  ``ETHTOOL_A_LINKMODES_SPEED``               u32     link speed (Mb/s)
+  ``ETHTOOL_A_LINKMODES_DUPLEX``              u8      duplex mode
+  ``ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG``    u8      Master/slave port mode
+  ``ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE``  u8      Master/slave port state
+  ==========================================  ======  ==========================
 
 For ``ETHTOOL_A_LINKMODES_OURS``, value represents advertised modes and mask
 represents supported modes. ``ETHTOOL_A_LINKMODES_PEER`` in the reply is a bit
@@ -414,14 +416,15 @@ LINKMODES_SET
 
 Request contents:
 
-  ====================================  ======  ==========================
-  ``ETHTOOL_A_LINKMODES_HEADER``        nested  request header
-  ``ETHTOOL_A_LINKMODES_AUTONEG``       u8      autonegotiation status
-  ``ETHTOOL_A_LINKMODES_OURS``          bitset  advertised link modes
-  ``ETHTOOL_A_LINKMODES_PEER``          bitset  partner link modes
-  ``ETHTOOL_A_LINKMODES_SPEED``         u32     link speed (Mb/s)
-  ``ETHTOOL_A_LINKMODES_DUPLEX``        u8      duplex mode
-  ====================================  ======  ==========================
+  ==========================================  ======  ==========================
+  ``ETHTOOL_A_LINKMODES_HEADER``              nested  request header
+  ``ETHTOOL_A_LINKMODES_AUTONEG``             u8      autonegotiation status
+  ``ETHTOOL_A_LINKMODES_OURS``                bitset  advertised link modes
+  ``ETHTOOL_A_LINKMODES_PEER``                bitset  partner link modes
+  ``ETHTOOL_A_LINKMODES_SPEED``               u32     link speed (Mb/s)
+  ``ETHTOOL_A_LINKMODES_DUPLEX``              u8      duplex mode
+  ``ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG``    u8      Master/slave port mode
+  ==========================================  ======  ==========================
 
 ``ETHTOOL_A_LINKMODES_OURS`` bit set allows setting advertised link modes. If
 autonegotiation is on (either set now or kept from before), advertised modes
diff --git a/Documentation/networking/fib_trie.rst b/Documentation/networking/fib_trie.rst
new file mode 100644 (file)
index 0000000..f1435b7
--- /dev/null
@@ -0,0 +1,149 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================
+LC-trie implementation notes
+============================
+
+Node types
+----------
+leaf
+       An end node with data. This has a copy of the relevant key, along
+       with 'hlist' with routing table entries sorted by prefix length.
+       See struct leaf and struct leaf_info.
+
+trie node or tnode
+       An internal node, holding an array of child (leaf or tnode) pointers,
+       indexed through a subset of the key. See Level Compression.
+
+A few concepts explained
+------------------------
+Bits (tnode)
+       The number of bits in the key segment used for indexing into the
+       child array - the "child index". See Level Compression.
+
+Pos (tnode)
+       The position (in the key) of the key segment used for indexing into
+       the child array. See Path Compression.
+
+Path Compression / skipped bits
+       Any given tnode is linked to from the child array of its parent, using
+       a segment of the key specified by the parent's "pos" and "bits"
+       In certain cases, this tnode's own "pos" will not be immediately
+       adjacent to the parent (pos+bits), but there will be some bits
+       in the key skipped over because they represent a single path with no
+       deviations. These "skipped bits" constitute Path Compression.
+       Note that the search algorithm will simply skip over these bits when
+       searching, making it necessary to save the keys in the leaves to
+       verify that they actually do match the key we are searching for.
+
+Level Compression / child arrays
+       the trie is kept level balanced moving, under certain conditions, the
+       children of a full child (see "full_children") up one level, so that
+       instead of a pure binary tree, each internal node ("tnode") may
+       contain an arbitrarily large array of links to several children.
+       Conversely, a tnode with a mostly empty child array (see empty_children)
+       may be "halved", having some of its children moved downwards one level,
+       in order to avoid ever-increasing child arrays.
+
+empty_children
+       the number of positions in the child array of a given tnode that are
+       NULL.
+
+full_children
+       the number of children of a given tnode that aren't path compressed.
+       (in other words, they aren't NULL or leaves and their "pos" is equal
+       to this tnode's "pos"+"bits").
+
+       (The word "full" here is used more in the sense of "complete" than
+       as the opposite of "empty", which might be a tad confusing.)
+
+Comments
+---------
+
+We have tried to keep the structure of the code as close to fib_hash as
+possible to allow verification and help up reviewing.
+
+fib_find_node()
+       A good start for understanding this code. This function implements a
+       straightforward trie lookup.
+
+fib_insert_node()
+       Inserts a new leaf node in the trie. This is bit more complicated than
+       fib_find_node(). Inserting a new node means we might have to run the
+       level compression algorithm on part of the trie.
+
+trie_leaf_remove()
+       Looks up a key, deletes it and runs the level compression algorithm.
+
+trie_rebalance()
+       The key function for the dynamic trie after any change in the trie
+       it is run to optimize and reorganize. It will walk the trie upwards
+       towards the root from a given tnode, doing a resize() at each step
+       to implement level compression.
+
+resize()
+       Analyzes a tnode and optimizes the child array size by either inflating
+       or shrinking it repeatedly until it fulfills the criteria for optimal
+       level compression. This part follows the original paper pretty closely
+       and there may be some room for experimentation here.
+
+inflate()
+       Doubles the size of the child array within a tnode. Used by resize().
+
+halve()
+       Halves the size of the child array within a tnode - the inverse of
+       inflate(). Used by resize();
+
+fn_trie_insert(), fn_trie_delete(), fn_trie_select_default()
+       The route manipulation functions. Should conform pretty closely to the
+       corresponding functions in fib_hash.
+
+fn_trie_flush()
+       This walks the full trie (using nextleaf()) and searches for empty
+       leaves which have to be removed.
+
+fn_trie_dump()
+       Dumps the routing table ordered by prefix length. This is somewhat
+       slower than the corresponding fib_hash function, as we have to walk the
+       entire trie for each prefix length. In comparison, fib_hash is organized
+       as one "zone"/hash per prefix length.
+
+Locking
+-------
+
+fib_lock is used for an RW-lock in the same way that this is done in fib_hash.
+However, the functions are somewhat separated for other possible locking
+scenarios. It might conceivably be possible to run trie_rebalance via RCU
+to avoid read_lock in the fn_trie_lookup() function.
+
+Main lookup mechanism
+---------------------
+fn_trie_lookup() is the main lookup function.
+
+The lookup is in its simplest form just like fib_find_node(). We descend the
+trie, key segment by key segment, until we find a leaf. check_leaf() does
+the fib_semantic_match in the leaf's sorted prefix hlist.
+
+If we find a match, we are done.
+
+If we don't find a match, we enter prefix matching mode. The prefix length,
+starting out at the same as the key length, is reduced one step at a time,
+and we backtrack upwards through the trie trying to find a longest matching
+prefix. The goal is always to reach a leaf and get a positive result from the
+fib_semantic_match mechanism.
+
+Inside each tnode, the search for longest matching prefix consists of searching
+through the child array, chopping off (zeroing) the least significant "1" of
+the child index until we find a match or the child index consists of nothing but
+zeros.
+
+At this point we backtrack (t->stats.backtrack++) up the trie, continuing to
+chop off part of the key in order to find the longest matching prefix.
+
+At this point we will repeatedly descend subtries to look for a match, and there
+are some optimizations available that can provide us with "shortcuts" to avoid
+descending into dead ends. Look for "HL_OPTIMIZE" sections in the code.
+
+To alleviate any doubts about the correctness of the route selection process,
+a new netlink operation has been added. Look for NETLINK_FIB_LOOKUP, which
+gives userland access to fib_lookup().
diff --git a/Documentation/networking/fib_trie.txt b/Documentation/networking/fib_trie.txt
deleted file mode 100644 (file)
index fe71938..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-                       LC-trie implementation notes.
-
-Node types
-----------
-leaf 
-       An end node with data. This has a copy of the relevant key, along
-       with 'hlist' with routing table entries sorted by prefix length.
-       See struct leaf and struct leaf_info.
-
-trie node or tnode
-       An internal node, holding an array of child (leaf or tnode) pointers,
-       indexed through a subset of the key. See Level Compression.
-
-A few concepts explained
-------------------------
-Bits (tnode) 
-       The number of bits in the key segment used for indexing into the
-       child array - the "child index". See Level Compression.
-
-Pos (tnode)
-       The position (in the key) of the key segment used for indexing into
-       the child array. See Path Compression.
-
-Path Compression / skipped bits
-       Any given tnode is linked to from the child array of its parent, using
-       a segment of the key specified by the parent's "pos" and "bits" 
-       In certain cases, this tnode's own "pos" will not be immediately
-       adjacent to the parent (pos+bits), but there will be some bits
-       in the key skipped over because they represent a single path with no
-       deviations. These "skipped bits" constitute Path Compression.
-       Note that the search algorithm will simply skip over these bits when
-       searching, making it necessary to save the keys in the leaves to
-       verify that they actually do match the key we are searching for.
-
-Level Compression / child arrays
-       the trie is kept level balanced moving, under certain conditions, the
-       children of a full child (see "full_children") up one level, so that
-       instead of a pure binary tree, each internal node ("tnode") may
-       contain an arbitrarily large array of links to several children.
-       Conversely, a tnode with a mostly empty child array (see empty_children)
-       may be "halved", having some of its children moved downwards one level,
-       in order to avoid ever-increasing child arrays.
-
-empty_children
-       the number of positions in the child array of a given tnode that are
-       NULL.
-
-full_children
-       the number of children of a given tnode that aren't path compressed.
-       (in other words, they aren't NULL or leaves and their "pos" is equal
-       to this tnode's "pos"+"bits").
-
-       (The word "full" here is used more in the sense of "complete" than
-       as the opposite of "empty", which might be a tad confusing.)
-
-Comments
----------
-
-We have tried to keep the structure of the code as close to fib_hash as 
-possible to allow verification and help up reviewing. 
-
-fib_find_node()
-       A good start for understanding this code. This function implements a
-       straightforward trie lookup.
-
-fib_insert_node()
-       Inserts a new leaf node in the trie. This is bit more complicated than
-       fib_find_node(). Inserting a new node means we might have to run the
-       level compression algorithm on part of the trie.
-
-trie_leaf_remove()
-       Looks up a key, deletes it and runs the level compression algorithm.
-
-trie_rebalance()
-       The key function for the dynamic trie after any change in the trie
-       it is run to optimize and reorganize. It will walk the trie upwards
-       towards the root from a given tnode, doing a resize() at each step
-       to implement level compression.
-
-resize()
-       Analyzes a tnode and optimizes the child array size by either inflating
-       or shrinking it repeatedly until it fulfills the criteria for optimal
-       level compression. This part follows the original paper pretty closely
-       and there may be some room for experimentation here.
-
-inflate()
-       Doubles the size of the child array within a tnode. Used by resize().
-
-halve()
-       Halves the size of the child array within a tnode - the inverse of
-       inflate(). Used by resize();
-
-fn_trie_insert(), fn_trie_delete(), fn_trie_select_default()
-       The route manipulation functions. Should conform pretty closely to the
-       corresponding functions in fib_hash.
-
-fn_trie_flush()
-       This walks the full trie (using nextleaf()) and searches for empty
-       leaves which have to be removed.
-
-fn_trie_dump()
-       Dumps the routing table ordered by prefix length. This is somewhat
-       slower than the corresponding fib_hash function, as we have to walk the
-       entire trie for each prefix length. In comparison, fib_hash is organized
-       as one "zone"/hash per prefix length.
-
-Locking
--------
-
-fib_lock is used for an RW-lock in the same way that this is done in fib_hash.
-However, the functions are somewhat separated for other possible locking
-scenarios. It might conceivably be possible to run trie_rebalance via RCU
-to avoid read_lock in the fn_trie_lookup() function.
-
-Main lookup mechanism
----------------------
-fn_trie_lookup() is the main lookup function.
-
-The lookup is in its simplest form just like fib_find_node(). We descend the
-trie, key segment by key segment, until we find a leaf. check_leaf() does
-the fib_semantic_match in the leaf's sorted prefix hlist.
-
-If we find a match, we are done.
-
-If we don't find a match, we enter prefix matching mode. The prefix length,
-starting out at the same as the key length, is reduced one step at a time,
-and we backtrack upwards through the trie trying to find a longest matching
-prefix. The goal is always to reach a leaf and get a positive result from the
-fib_semantic_match mechanism.
-
-Inside each tnode, the search for longest matching prefix consists of searching
-through the child array, chopping off (zeroing) the least significant "1" of
-the child index until we find a match or the child index consists of nothing but
-zeros.
-
-At this point we backtrack (t->stats.backtrack++) up the trie, continuing to
-chop off part of the key in order to find the longest matching prefix.
-
-At this point we will repeatedly descend subtries to look for a match, and there
-are some optimizations available that can provide us with "shortcuts" to avoid
-descending into dead ends. Look for "HL_OPTIMIZE" sections in the code.
-
-To alleviate any doubts about the correctness of the route selection process,
-a new netlink operation has been added. Look for NETLINK_FIB_LOOKUP, which
-gives userland access to fib_lookup().
diff --git a/Documentation/networking/filter.rst b/Documentation/networking/filter.rst
new file mode 100644 (file)
index 0000000..a1d3e19
--- /dev/null
@@ -0,0 +1,1651 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======================================================
+Linux Socket Filtering aka Berkeley Packet Filter (BPF)
+=======================================================
+
+Introduction
+------------
+
+Linux Socket Filtering (LSF) is derived from the Berkeley Packet Filter.
+Though there are some distinct differences between the BSD and Linux
+Kernel filtering, but when we speak of BPF or LSF in Linux context, we
+mean the very same mechanism of filtering in the Linux kernel.
+
+BPF allows a user-space program to attach a filter onto any socket and
+allow or disallow certain types of data to come through the socket. LSF
+follows exactly the same filter code structure as BSD's BPF, so referring
+to the BSD bpf.4 manpage is very helpful in creating filters.
+
+On Linux, BPF is much simpler than on BSD. One does not have to worry
+about devices or anything like that. You simply create your filter code,
+send it to the kernel via the SO_ATTACH_FILTER option and if your filter
+code passes the kernel check on it, you then immediately begin filtering
+data on that socket.
+
+You can also detach filters from your socket via the SO_DETACH_FILTER
+option. This will probably not be used much since when you close a socket
+that has a filter on it the filter is automagically removed. The other
+less common case may be adding a different filter on the same socket where
+you had another filter that is still running: the kernel takes care of
+removing the old one and placing your new one in its place, assuming your
+filter has passed the checks, otherwise if it fails the old filter will
+remain on that socket.
+
+SO_LOCK_FILTER option allows to lock the filter attached to a socket. Once
+set, a filter cannot be removed or changed. This allows one process to
+setup a socket, attach a filter, lock it then drop privileges and be
+assured that the filter will be kept until the socket is closed.
+
+The biggest user of this construct might be libpcap. Issuing a high-level
+filter command like `tcpdump -i em1 port 22` passes through the libpcap
+internal compiler that generates a structure that can eventually be loaded
+via SO_ATTACH_FILTER to the kernel. `tcpdump -i em1 port 22 -ddd`
+displays what is being placed into this structure.
+
+Although we were only speaking about sockets here, BPF in Linux is used
+in many more places. There's xt_bpf for netfilter, cls_bpf in the kernel
+qdisc layer, SECCOMP-BPF (SECure COMPuting [1]_), and lots of other places
+such as team driver, PTP code, etc where BPF is being used.
+
+.. [1] Documentation/userspace-api/seccomp_filter.rst
+
+Original BPF paper:
+
+Steven McCanne and Van Jacobson. 1993. The BSD packet filter: a new
+architecture for user-level packet capture. In Proceedings of the
+USENIX Winter 1993 Conference Proceedings on USENIX Winter 1993
+Conference Proceedings (USENIX'93). USENIX Association, Berkeley,
+CA, USA, 2-2. [http://www.tcpdump.org/papers/bpf-usenix93.pdf]
+
+Structure
+---------
+
+User space applications include <linux/filter.h> which contains the
+following relevant structures::
+
+       struct sock_filter {    /* Filter block */
+               __u16   code;   /* Actual filter code */
+               __u8    jt;     /* Jump true */
+               __u8    jf;     /* Jump false */
+               __u32   k;      /* Generic multiuse field */
+       };
+
+Such a structure is assembled as an array of 4-tuples, that contains
+a code, jt, jf and k value. jt and jf are jump offsets and k a generic
+value to be used for a provided code::
+
+       struct sock_fprog {                     /* Required for SO_ATTACH_FILTER. */
+               unsigned short             len; /* Number of filter blocks */
+               struct sock_filter __user *filter;
+       };
+
+For socket filtering, a pointer to this structure (as shown in
+follow-up example) is being passed to the kernel through setsockopt(2).
+
+Example
+-------
+
+::
+
+    #include <sys/socket.h>
+    #include <sys/types.h>
+    #include <arpa/inet.h>
+    #include <linux/if_ether.h>
+    /* ... */
+
+    /* From the example above: tcpdump -i em1 port 22 -dd */
+    struct sock_filter code[] = {
+           { 0x28,  0,  0, 0x0000000c },
+           { 0x15,  0,  8, 0x000086dd },
+           { 0x30,  0,  0, 0x00000014 },
+           { 0x15,  2,  0, 0x00000084 },
+           { 0x15,  1,  0, 0x00000006 },
+           { 0x15,  0, 17, 0x00000011 },
+           { 0x28,  0,  0, 0x00000036 },
+           { 0x15, 14,  0, 0x00000016 },
+           { 0x28,  0,  0, 0x00000038 },
+           { 0x15, 12, 13, 0x00000016 },
+           { 0x15,  0, 12, 0x00000800 },
+           { 0x30,  0,  0, 0x00000017 },
+           { 0x15,  2,  0, 0x00000084 },
+           { 0x15,  1,  0, 0x00000006 },
+           { 0x15,  0,  8, 0x00000011 },
+           { 0x28,  0,  0, 0x00000014 },
+           { 0x45,  6,  0, 0x00001fff },
+           { 0xb1,  0,  0, 0x0000000e },
+           { 0x48,  0,  0, 0x0000000e },
+           { 0x15,  2,  0, 0x00000016 },
+           { 0x48,  0,  0, 0x00000010 },
+           { 0x15,  0,  1, 0x00000016 },
+           { 0x06,  0,  0, 0x0000ffff },
+           { 0x06,  0,  0, 0x00000000 },
+    };
+
+    struct sock_fprog bpf = {
+           .len = ARRAY_SIZE(code),
+           .filter = code,
+    };
+
+    sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+    if (sock < 0)
+           /* ... bail out ... */
+
+    ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
+    if (ret < 0)
+           /* ... bail out ... */
+
+    /* ... */
+    close(sock);
+
+The above example code attaches a socket filter for a PF_PACKET socket
+in order to let all IPv4/IPv6 packets with port 22 pass. The rest will
+be dropped for this socket.
+
+The setsockopt(2) call to SO_DETACH_FILTER doesn't need any arguments
+and SO_LOCK_FILTER for preventing the filter to be detached, takes an
+integer value with 0 or 1.
+
+Note that socket filters are not restricted to PF_PACKET sockets only,
+but can also be used on other socket families.
+
+Summary of system calls:
+
+ * setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &val, sizeof(val));
+ * setsockopt(sockfd, SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val));
+ * setsockopt(sockfd, SOL_SOCKET, SO_LOCK_FILTER,   &val, sizeof(val));
+
+Normally, most use cases for socket filtering on packet sockets will be
+covered by libpcap in high-level syntax, so as an application developer
+you should stick to that. libpcap wraps its own layer around all that.
+
+Unless i) using/linking to libpcap is not an option, ii) the required BPF
+filters use Linux extensions that are not supported by libpcap's compiler,
+iii) a filter might be more complex and not cleanly implementable with
+libpcap's compiler, or iv) particular filter codes should be optimized
+differently than libpcap's internal compiler does; then in such cases
+writing such a filter "by hand" can be of an alternative. For example,
+xt_bpf and cls_bpf users might have requirements that could result in
+more complex filter code, or one that cannot be expressed with libpcap
+(e.g. different return codes for various code paths). Moreover, BPF JIT
+implementors may wish to manually write test cases and thus need low-level
+access to BPF code as well.
+
+BPF engine and instruction set
+------------------------------
+
+Under tools/bpf/ there's a small helper tool called bpf_asm which can
+be used to write low-level filters for example scenarios mentioned in the
+previous section. Asm-like syntax mentioned here has been implemented in
+bpf_asm and will be used for further explanations (instead of dealing with
+less readable opcodes directly, principles are the same). The syntax is
+closely modelled after Steven McCanne's and Van Jacobson's BPF paper.
+
+The BPF architecture consists of the following basic elements:
+
+  =======          ====================================================
+  Element          Description
+  =======          ====================================================
+  A                32 bit wide accumulator
+  X                32 bit wide X register
+  M[]              16 x 32 bit wide misc registers aka "scratch memory
+                  store", addressable from 0 to 15
+  =======          ====================================================
+
+A program, that is translated by bpf_asm into "opcodes" is an array that
+consists of the following elements (as already mentioned)::
+
+  op:16, jt:8, jf:8, k:32
+
+The element op is a 16 bit wide opcode that has a particular instruction
+encoded. jt and jf are two 8 bit wide jump targets, one for condition
+"jump if true", the other one "jump if false". Eventually, element k
+contains a miscellaneous argument that can be interpreted in different
+ways depending on the given instruction in op.
+
+The instruction set consists of load, store, branch, alu, miscellaneous
+and return instructions that are also represented in bpf_asm syntax. This
+table lists all bpf_asm instructions available resp. what their underlying
+opcodes as defined in linux/filter.h stand for:
+
+  ===========      ===================  =====================
+  Instruction      Addressing mode      Description
+  ===========      ===================  =====================
+  ld               1, 2, 3, 4, 12       Load word into A
+  ldi              4                    Load word into A
+  ldh              1, 2                 Load half-word into A
+  ldb              1, 2                 Load byte into A
+  ldx              3, 4, 5, 12          Load word into X
+  ldxi             4                    Load word into X
+  ldxb             5                    Load byte into X
+
+  st               3                    Store A into M[]
+  stx              3                    Store X into M[]
+
+  jmp              6                    Jump to label
+  ja               6                    Jump to label
+  jeq              7, 8, 9, 10          Jump on A == <x>
+  jneq             9, 10                Jump on A != <x>
+  jne              9, 10                Jump on A != <x>
+  jlt              9, 10                Jump on A <  <x>
+  jle              9, 10                Jump on A <= <x>
+  jgt              7, 8, 9, 10          Jump on A >  <x>
+  jge              7, 8, 9, 10          Jump on A >= <x>
+  jset             7, 8, 9, 10          Jump on A &  <x>
+
+  add              0, 4                 A + <x>
+  sub              0, 4                 A - <x>
+  mul              0, 4                 A * <x>
+  div              0, 4                 A / <x>
+  mod              0, 4                 A % <x>
+  neg                                   !A
+  and              0, 4                 A & <x>
+  or               0, 4                 A | <x>
+  xor              0, 4                 A ^ <x>
+  lsh              0, 4                 A << <x>
+  rsh              0, 4                 A >> <x>
+
+  tax                                   Copy A into X
+  txa                                   Copy X into A
+
+  ret              4, 11                Return
+  ===========      ===================  =====================
+
+The next table shows addressing formats from the 2nd column:
+
+  ===============  ===================  ===============================================
+  Addressing mode  Syntax               Description
+  ===============  ===================  ===============================================
+   0               x/%x                 Register X
+   1               [k]                  BHW at byte offset k in the packet
+   2               [x + k]              BHW at the offset X + k in the packet
+   3               M[k]                 Word at offset k in M[]
+   4               #k                   Literal value stored in k
+   5               4*([k]&0xf)          Lower nibble * 4 at byte offset k in the packet
+   6               L                    Jump label L
+   7               #k,Lt,Lf             Jump to Lt if true, otherwise jump to Lf
+   8               x/%x,Lt,Lf           Jump to Lt if true, otherwise jump to Lf
+   9               #k,Lt                Jump to Lt if predicate is true
+  10               x/%x,Lt              Jump to Lt if predicate is true
+  11               a/%a                 Accumulator A
+  12               extension            BPF extension
+  ===============  ===================  ===============================================
+
+The Linux kernel also has a couple of BPF extensions that are used along
+with the class of load instructions by "overloading" the k argument with
+a negative offset + a particular extension offset. The result of such BPF
+extensions are loaded into A.
+
+Possible BPF extensions are shown in the following table:
+
+  ===================================   =================================================
+  Extension                             Description
+  ===================================   =================================================
+  len                                   skb->len
+  proto                                 skb->protocol
+  type                                  skb->pkt_type
+  poff                                  Payload start offset
+  ifidx                                 skb->dev->ifindex
+  nla                                   Netlink attribute of type X with offset A
+  nlan                                  Nested Netlink attribute of type X with offset A
+  mark                                  skb->mark
+  queue                                 skb->queue_mapping
+  hatype                                skb->dev->type
+  rxhash                                skb->hash
+  cpu                                   raw_smp_processor_id()
+  vlan_tci                              skb_vlan_tag_get(skb)
+  vlan_avail                            skb_vlan_tag_present(skb)
+  vlan_tpid                             skb->vlan_proto
+  rand                                  prandom_u32()
+  ===================================   =================================================
+
+These extensions can also be prefixed with '#'.
+Examples for low-level BPF:
+
+**ARP packets**::
+
+  ldh [12]
+  jne #0x806, drop
+  ret #-1
+  drop: ret #0
+
+**IPv4 TCP packets**::
+
+  ldh [12]
+  jne #0x800, drop
+  ldb [23]
+  jneq #6, drop
+  ret #-1
+  drop: ret #0
+
+**(Accelerated) VLAN w/ id 10**::
+
+  ld vlan_tci
+  jneq #10, drop
+  ret #-1
+  drop: ret #0
+
+**icmp random packet sampling, 1 in 4**:
+
+  ldh [12]
+  jne #0x800, drop
+  ldb [23]
+  jneq #1, drop
+  # get a random uint32 number
+  ld rand
+  mod #4
+  jneq #1, drop
+  ret #-1
+  drop: ret #0
+
+**SECCOMP filter example**::
+
+  ld [4]                  /* offsetof(struct seccomp_data, arch) */
+  jne #0xc000003e, bad    /* AUDIT_ARCH_X86_64 */
+  ld [0]                  /* offsetof(struct seccomp_data, nr) */
+  jeq #15, good           /* __NR_rt_sigreturn */
+  jeq #231, good          /* __NR_exit_group */
+  jeq #60, good           /* __NR_exit */
+  jeq #0, good            /* __NR_read */
+  jeq #1, good            /* __NR_write */
+  jeq #5, good            /* __NR_fstat */
+  jeq #9, good            /* __NR_mmap */
+  jeq #14, good           /* __NR_rt_sigprocmask */
+  jeq #13, good           /* __NR_rt_sigaction */
+  jeq #35, good           /* __NR_nanosleep */
+  bad: ret #0             /* SECCOMP_RET_KILL_THREAD */
+  good: ret #0x7fff0000   /* SECCOMP_RET_ALLOW */
+
+The above example code can be placed into a file (here called "foo"), and
+then be passed to the bpf_asm tool for generating opcodes, output that xt_bpf
+and cls_bpf understands and can directly be loaded with. Example with above
+ARP code::
+
+    $ ./bpf_asm foo
+    4,40 0 0 12,21 0 1 2054,6 0 0 4294967295,6 0 0 0,
+
+In copy and paste C-like output::
+
+    $ ./bpf_asm -c foo
+    { 0x28,  0,  0, 0x0000000c },
+    { 0x15,  0,  1, 0x00000806 },
+    { 0x06,  0,  0, 0xffffffff },
+    { 0x06,  0,  0, 0000000000 },
+
+In particular, as usage with xt_bpf or cls_bpf can result in more complex BPF
+filters that might not be obvious at first, it's good to test filters before
+attaching to a live system. For that purpose, there's a small tool called
+bpf_dbg under tools/bpf/ in the kernel source directory. This debugger allows
+for testing BPF filters against given pcap files, single stepping through the
+BPF code on the pcap's packets and to do BPF machine register dumps.
+
+Starting bpf_dbg is trivial and just requires issuing::
+
+    # ./bpf_dbg
+
+In case input and output do not equal stdin/stdout, bpf_dbg takes an
+alternative stdin source as a first argument, and an alternative stdout
+sink as a second one, e.g. `./bpf_dbg test_in.txt test_out.txt`.
+
+Other than that, a particular libreadline configuration can be set via
+file "~/.bpf_dbg_init" and the command history is stored in the file
+"~/.bpf_dbg_history".
+
+Interaction in bpf_dbg happens through a shell that also has auto-completion
+support (follow-up example commands starting with '>' denote bpf_dbg shell).
+The usual workflow would be to ...
+
+* load bpf 6,40 0 0 12,21 0 3 2048,48 0 0 23,21 0 1 1,6 0 0 65535,6 0 0 0
+  Loads a BPF filter from standard output of bpf_asm, or transformed via
+  e.g. ``tcpdump -iem1 -ddd port 22 | tr '\n' ','``. Note that for JIT
+  debugging (next section), this command creates a temporary socket and
+  loads the BPF code into the kernel. Thus, this will also be useful for
+  JIT developers.
+
+* load pcap foo.pcap
+
+  Loads standard tcpdump pcap file.
+
+* run [<n>]
+
+bpf passes:1 fails:9
+  Runs through all packets from a pcap to account how many passes and fails
+  the filter will generate. A limit of packets to traverse can be given.
+
+* disassemble::
+
+       l0:     ldh [12]
+       l1:     jeq #0x800, l2, l5
+       l2:     ldb [23]
+       l3:     jeq #0x1, l4, l5
+       l4:     ret #0xffff
+       l5:     ret #0
+
+  Prints out BPF code disassembly.
+
+* dump::
+
+       /* { op, jt, jf, k }, */
+       { 0x28,  0,  0, 0x0000000c },
+       { 0x15,  0,  3, 0x00000800 },
+       { 0x30,  0,  0, 0x00000017 },
+       { 0x15,  0,  1, 0x00000001 },
+       { 0x06,  0,  0, 0x0000ffff },
+       { 0x06,  0,  0, 0000000000 },
+
+  Prints out C-style BPF code dump.
+
+* breakpoint 0::
+
+       breakpoint at: l0:      ldh [12]
+
+* breakpoint 1::
+
+       breakpoint at: l1:      jeq #0x800, l2, l5
+
+  ...
+
+  Sets breakpoints at particular BPF instructions. Issuing a `run` command
+  will walk through the pcap file continuing from the current packet and
+  break when a breakpoint is being hit (another `run` will continue from
+  the currently active breakpoint executing next instructions):
+
+  * run::
+
+       -- register dump --
+       pc:       [0]                       <-- program counter
+       code:     [40] jt[0] jf[0] k[12]    <-- plain BPF code of current instruction
+       curr:     l0:   ldh [12]              <-- disassembly of current instruction
+       A:        [00000000][0]             <-- content of A (hex, decimal)
+       X:        [00000000][0]             <-- content of X (hex, decimal)
+       M[0,15]:  [00000000][0]             <-- folded content of M (hex, decimal)
+       -- packet dump --                   <-- Current packet from pcap (hex)
+       len: 42
+           0: 00 19 cb 55 55 a4 00 14 a4 43 78 69 08 06 00 01
+       16: 08 00 06 04 00 01 00 14 a4 43 78 69 0a 3b 01 26
+       32: 00 00 00 00 00 00 0a 3b 01 01
+       (breakpoint)
+       >
+
+  * breakpoint::
+
+       breakpoints: 0 1
+
+    Prints currently set breakpoints.
+
+* step [-<n>, +<n>]
+
+  Performs single stepping through the BPF program from the current pc
+  offset. Thus, on each step invocation, above register dump is issued.
+  This can go forwards and backwards in time, a plain `step` will break
+  on the next BPF instruction, thus +1. (No `run` needs to be issued here.)
+
+* select <n>
+
+  Selects a given packet from the pcap file to continue from. Thus, on
+  the next `run` or `step`, the BPF program is being evaluated against
+  the user pre-selected packet. Numbering starts just as in Wireshark
+  with index 1.
+
+* quit
+
+  Exits bpf_dbg.
+
+JIT compiler
+------------
+
+The Linux kernel has a built-in BPF JIT compiler for x86_64, SPARC,
+PowerPC, ARM, ARM64, MIPS, RISC-V and s390 and can be enabled through
+CONFIG_BPF_JIT. The JIT compiler is transparently invoked for each
+attached filter from user space or for internal kernel users if it has
+been previously enabled by root::
+
+  echo 1 > /proc/sys/net/core/bpf_jit_enable
+
+For JIT developers, doing audits etc, each compile run can output the generated
+opcode image into the kernel log via::
+
+  echo 2 > /proc/sys/net/core/bpf_jit_enable
+
+Example output from dmesg::
+
+    [ 3389.935842] flen=6 proglen=70 pass=3 image=ffffffffa0069c8f
+    [ 3389.935847] JIT code: 00000000: 55 48 89 e5 48 83 ec 60 48 89 5d f8 44 8b 4f 68
+    [ 3389.935849] JIT code: 00000010: 44 2b 4f 6c 4c 8b 87 d8 00 00 00 be 0c 00 00 00
+    [ 3389.935850] JIT code: 00000020: e8 1d 94 ff e0 3d 00 08 00 00 75 16 be 17 00 00
+    [ 3389.935851] JIT code: 00000030: 00 e8 28 94 ff e0 83 f8 01 75 07 b8 ff ff 00 00
+    [ 3389.935852] JIT code: 00000040: eb 02 31 c0 c9 c3
+
+When CONFIG_BPF_JIT_ALWAYS_ON is enabled, bpf_jit_enable is permanently set to 1 and
+setting any other value than that will return in failure. This is even the case for
+setting bpf_jit_enable to 2, since dumping the final JIT image into the kernel log
+is discouraged and introspection through bpftool (under tools/bpf/bpftool/) is the
+generally recommended approach instead.
+
+In the kernel source tree under tools/bpf/, there's bpf_jit_disasm for
+generating disassembly out of the kernel log's hexdump::
+
+       # ./bpf_jit_disasm
+       70 bytes emitted from JIT compiler (pass:3, flen:6)
+       ffffffffa0069c8f + <x>:
+       0:      push   %rbp
+       1:      mov    %rsp,%rbp
+       4:      sub    $0x60,%rsp
+       8:      mov    %rbx,-0x8(%rbp)
+       c:      mov    0x68(%rdi),%r9d
+       10:     sub    0x6c(%rdi),%r9d
+       14:     mov    0xd8(%rdi),%r8
+       1b:     mov    $0xc,%esi
+       20:     callq  0xffffffffe0ff9442
+       25:     cmp    $0x800,%eax
+       2a:     jne    0x0000000000000042
+       2c:     mov    $0x17,%esi
+       31:     callq  0xffffffffe0ff945e
+       36:     cmp    $0x1,%eax
+       39:     jne    0x0000000000000042
+       3b:     mov    $0xffff,%eax
+       40:     jmp    0x0000000000000044
+       42:     xor    %eax,%eax
+       44:     leaveq
+       45:     retq
+
+       Issuing option `-o` will "annotate" opcodes to resulting assembler
+       instructions, which can be very useful for JIT developers:
+
+       # ./bpf_jit_disasm -o
+       70 bytes emitted from JIT compiler (pass:3, flen:6)
+       ffffffffa0069c8f + <x>:
+       0:      push   %rbp
+               55
+       1:      mov    %rsp,%rbp
+               48 89 e5
+       4:      sub    $0x60,%rsp
+               48 83 ec 60
+       8:      mov    %rbx,-0x8(%rbp)
+               48 89 5d f8
+       c:      mov    0x68(%rdi),%r9d
+               44 8b 4f 68
+       10:     sub    0x6c(%rdi),%r9d
+               44 2b 4f 6c
+       14:     mov    0xd8(%rdi),%r8
+               4c 8b 87 d8 00 00 00
+       1b:     mov    $0xc,%esi
+               be 0c 00 00 00
+       20:     callq  0xffffffffe0ff9442
+               e8 1d 94 ff e0
+       25:     cmp    $0x800,%eax
+               3d 00 08 00 00
+       2a:     jne    0x0000000000000042
+               75 16
+       2c:     mov    $0x17,%esi
+               be 17 00 00 00
+       31:     callq  0xffffffffe0ff945e
+               e8 28 94 ff e0
+       36:     cmp    $0x1,%eax
+               83 f8 01
+       39:     jne    0x0000000000000042
+               75 07
+       3b:     mov    $0xffff,%eax
+               b8 ff ff 00 00
+       40:     jmp    0x0000000000000044
+               eb 02
+       42:     xor    %eax,%eax
+               31 c0
+       44:     leaveq
+               c9
+       45:     retq
+               c3
+
+For BPF JIT developers, bpf_jit_disasm, bpf_asm and bpf_dbg provides a useful
+toolchain for developing and testing the kernel's JIT compiler.
+
+BPF kernel internals
+--------------------
+Internally, for the kernel interpreter, a different instruction set
+format with similar underlying principles from BPF described in previous
+paragraphs is being used. However, the instruction set format is modelled
+closer to the underlying architecture to mimic native instruction sets, so
+that a better performance can be achieved (more details later). This new
+ISA is called 'eBPF' or 'internal BPF' interchangeably. (Note: eBPF which
+originates from [e]xtended BPF is not the same as BPF extensions! While
+eBPF is an ISA, BPF extensions date back to classic BPF's 'overloading'
+of BPF_LD | BPF_{B,H,W} | BPF_ABS instruction.)
+
+It is designed to be JITed with one to one mapping, which can also open up
+the possibility for GCC/LLVM compilers to generate optimized eBPF code through
+an eBPF backend that performs almost as fast as natively compiled code.
+
+The new instruction set was originally designed with the possible goal in
+mind to write programs in "restricted C" and compile into eBPF with a optional
+GCC/LLVM backend, so that it can just-in-time map to modern 64-bit CPUs with
+minimal performance overhead over two steps, that is, C -> eBPF -> native code.
+
+Currently, the new format is being used for running user BPF programs, which
+includes seccomp BPF, classic socket filters, cls_bpf traffic classifier,
+team driver's classifier for its load-balancing mode, netfilter's xt_bpf
+extension, PTP dissector/classifier, and much more. They are all internally
+converted by the kernel into the new instruction set representation and run
+in the eBPF interpreter. For in-kernel handlers, this all works transparently
+by using bpf_prog_create() for setting up the filter, resp.
+bpf_prog_destroy() for destroying it. The macro
+BPF_PROG_RUN(filter, ctx) transparently invokes eBPF interpreter or JITed
+code to run the filter. 'filter' is a pointer to struct bpf_prog that we
+got from bpf_prog_create(), and 'ctx' the given context (e.g.
+skb pointer). All constraints and restrictions from bpf_check_classic() apply
+before a conversion to the new layout is being done behind the scenes!
+
+Currently, the classic BPF format is being used for JITing on most
+32-bit architectures, whereas x86-64, aarch64, s390x, powerpc64,
+sparc64, arm32, riscv64, riscv32 perform JIT compilation from eBPF
+instruction set.
+
+Some core changes of the new internal format:
+
+- Number of registers increase from 2 to 10:
+
+  The old format had two registers A and X, and a hidden frame pointer. The
+  new layout extends this to be 10 internal registers and a read-only frame
+  pointer. Since 64-bit CPUs are passing arguments to functions via registers
+  the number of args from eBPF program to in-kernel function is restricted
+  to 5 and one register is used to accept return value from an in-kernel
+  function. Natively, x86_64 passes first 6 arguments in registers, aarch64/
+  sparcv9/mips64 have 7 - 8 registers for arguments; x86_64 has 6 callee saved
+  registers, and aarch64/sparcv9/mips64 have 11 or more callee saved registers.
+
+  Therefore, eBPF calling convention is defined as:
+
+    * R0       - return value from in-kernel function, and exit value for eBPF program
+    * R1 - R5  - arguments from eBPF program to in-kernel function
+    * R6 - R9  - callee saved registers that in-kernel function will preserve
+    * R10      - read-only frame pointer to access stack
+
+  Thus, all eBPF registers map one to one to HW registers on x86_64, aarch64,
+  etc, and eBPF calling convention maps directly to ABIs used by the kernel on
+  64-bit architectures.
+
+  On 32-bit architectures JIT may map programs that use only 32-bit arithmetic
+  and may let more complex programs to be interpreted.
+
+  R0 - R5 are scratch registers and eBPF program needs spill/fill them if
+  necessary across calls. Note that there is only one eBPF program (== one
+  eBPF main routine) and it cannot call other eBPF functions, it can only
+  call predefined in-kernel functions, though.
+
+- Register width increases from 32-bit to 64-bit:
+
+  Still, the semantics of the original 32-bit ALU operations are preserved
+  via 32-bit subregisters. All eBPF registers are 64-bit with 32-bit lower
+  subregisters that zero-extend into 64-bit if they are being written to.
+  That behavior maps directly to x86_64 and arm64 subregister definition, but
+  makes other JITs more difficult.
+
+  32-bit architectures run 64-bit internal BPF programs via interpreter.
+  Their JITs may convert BPF programs that only use 32-bit subregisters into
+  native instruction set and let the rest being interpreted.
+
+  Operation is 64-bit, because on 64-bit architectures, pointers are also
+  64-bit wide, and we want to pass 64-bit values in/out of kernel functions,
+  so 32-bit eBPF registers would otherwise require to define register-pair
+  ABI, thus, there won't be able to use a direct eBPF register to HW register
+  mapping and JIT would need to do combine/split/move operations for every
+  register in and out of the function, which is complex, bug prone and slow.
+  Another reason is the use of atomic 64-bit counters.
+
+- Conditional jt/jf targets replaced with jt/fall-through:
+
+  While the original design has constructs such as ``if (cond) jump_true;
+  else jump_false;``, they are being replaced into alternative constructs like
+  ``if (cond) jump_true; /* else fall-through */``.
+
+- Introduces bpf_call insn and register passing convention for zero overhead
+  calls from/to other kernel functions:
+
+  Before an in-kernel function call, the internal BPF program needs to
+  place function arguments into R1 to R5 registers to satisfy calling
+  convention, then the interpreter will take them from registers and pass
+  to in-kernel function. If R1 - R5 registers are mapped to CPU registers
+  that are used for argument passing on given architecture, the JIT compiler
+  doesn't need to emit extra moves. Function arguments will be in the correct
+  registers and BPF_CALL instruction will be JITed as single 'call' HW
+  instruction. This calling convention was picked to cover common call
+  situations without performance penalty.
+
+  After an in-kernel function call, R1 - R5 are reset to unreadable and R0 has
+  a return value of the function. Since R6 - R9 are callee saved, their state
+  is preserved across the call.
+
+  For example, consider three C functions::
+
+    u64 f1() { return (*_f2)(1); }
+    u64 f2(u64 a) { return f3(a + 1, a); }
+    u64 f3(u64 a, u64 b) { return a - b; }
+
+  GCC can compile f1, f3 into x86_64::
+
+    f1:
+       movl $1, %edi
+       movq _f2(%rip), %rax
+       jmp  *%rax
+    f3:
+       movq %rdi, %rax
+       subq %rsi, %rax
+       ret
+
+  Function f2 in eBPF may look like::
+
+    f2:
+       bpf_mov R2, R1
+       bpf_add R1, 1
+       bpf_call f3
+       bpf_exit
+
+  If f2 is JITed and the pointer stored to ``_f2``. The calls f1 -> f2 -> f3 and
+  returns will be seamless. Without JIT, __bpf_prog_run() interpreter needs to
+  be used to call into f2.
+
+  For practical reasons all eBPF programs have only one argument 'ctx' which is
+  already placed into R1 (e.g. on __bpf_prog_run() startup) and the programs
+  can call kernel functions with up to 5 arguments. Calls with 6 or more arguments
+  are currently not supported, but these restrictions can be lifted if necessary
+  in the future.
+
+  On 64-bit architectures all register map to HW registers one to one. For
+  example, x86_64 JIT compiler can map them as ...
+
+  ::
+
+    R0 - rax
+    R1 - rdi
+    R2 - rsi
+    R3 - rdx
+    R4 - rcx
+    R5 - r8
+    R6 - rbx
+    R7 - r13
+    R8 - r14
+    R9 - r15
+    R10 - rbp
+
+  ... since x86_64 ABI mandates rdi, rsi, rdx, rcx, r8, r9 for argument passing
+  and rbx, r12 - r15 are callee saved.
+
+  Then the following internal BPF pseudo-program::
+
+    bpf_mov R6, R1 /* save ctx */
+    bpf_mov R2, 2
+    bpf_mov R3, 3
+    bpf_mov R4, 4
+    bpf_mov R5, 5
+    bpf_call foo
+    bpf_mov R7, R0 /* save foo() return value */
+    bpf_mov R1, R6 /* restore ctx for next call */
+    bpf_mov R2, 6
+    bpf_mov R3, 7
+    bpf_mov R4, 8
+    bpf_mov R5, 9
+    bpf_call bar
+    bpf_add R0, R7
+    bpf_exit
+
+  After JIT to x86_64 may look like::
+
+    push %rbp
+    mov %rsp,%rbp
+    sub $0x228,%rsp
+    mov %rbx,-0x228(%rbp)
+    mov %r13,-0x220(%rbp)
+    mov %rdi,%rbx
+    mov $0x2,%esi
+    mov $0x3,%edx
+    mov $0x4,%ecx
+    mov $0x5,%r8d
+    callq foo
+    mov %rax,%r13
+    mov %rbx,%rdi
+    mov $0x6,%esi
+    mov $0x7,%edx
+    mov $0x8,%ecx
+    mov $0x9,%r8d
+    callq bar
+    add %r13,%rax
+    mov -0x228(%rbp),%rbx
+    mov -0x220(%rbp),%r13
+    leaveq
+    retq
+
+  Which is in this example equivalent in C to::
+
+    u64 bpf_filter(u64 ctx)
+    {
+       return foo(ctx, 2, 3, 4, 5) + bar(ctx, 6, 7, 8, 9);
+    }
+
+  In-kernel functions foo() and bar() with prototype: u64 (*)(u64 arg1, u64
+  arg2, u64 arg3, u64 arg4, u64 arg5); will receive arguments in proper
+  registers and place their return value into ``%rax`` which is R0 in eBPF.
+  Prologue and epilogue are emitted by JIT and are implicit in the
+  interpreter. R0-R5 are scratch registers, so eBPF program needs to preserve
+  them across the calls as defined by calling convention.
+
+  For example the following program is invalid::
+
+    bpf_mov R1, 1
+    bpf_call foo
+    bpf_mov R0, R1
+    bpf_exit
+
+  After the call the registers R1-R5 contain junk values and cannot be read.
+  An in-kernel eBPF verifier is used to validate internal BPF programs.
+
+Also in the new design, eBPF is limited to 4096 insns, which means that any
+program will terminate quickly and will only call a fixed number of kernel
+functions. Original BPF and the new format are two operand instructions,
+which helps to do one-to-one mapping between eBPF insn and x86 insn during JIT.
+
+The input context pointer for invoking the interpreter function is generic,
+its content is defined by a specific use case. For seccomp register R1 points
+to seccomp_data, for converted BPF filters R1 points to a skb.
+
+A program, that is translated internally consists of the following elements::
+
+  op:16, jt:8, jf:8, k:32    ==>    op:8, dst_reg:4, src_reg:4, off:16, imm:32
+
+So far 87 internal BPF instructions were implemented. 8-bit 'op' opcode field
+has room for new instructions. Some of them may use 16/24/32 byte encoding. New
+instructions must be multiple of 8 bytes to preserve backward compatibility.
+
+Internal BPF is a general purpose RISC instruction set. Not every register and
+every instruction are used during translation from original BPF to new format.
+For example, socket filters are not using ``exclusive add`` instruction, but
+tracing filters may do to maintain counters of events, for example. Register R9
+is not used by socket filters either, but more complex filters may be running
+out of registers and would have to resort to spill/fill to stack.
+
+Internal BPF can be used as a generic assembler for last step performance
+optimizations, socket filters and seccomp are using it as assembler. Tracing
+filters may use it as assembler to generate code from kernel. In kernel usage
+may not be bounded by security considerations, since generated internal BPF code
+may be optimizing internal code path and not being exposed to the user space.
+Safety of internal BPF can come from a verifier (TBD). In such use cases as
+described, it may be used as safe instruction set.
+
+Just like the original BPF, the new format runs within a controlled environment,
+is deterministic and the kernel can easily prove that. The safety of the program
+can be determined in two steps: first step does depth-first-search to disallow
+loops and other CFG validation; second step starts from the first insn and
+descends all possible paths. It simulates execution of every insn and observes
+the state change of registers and stack.
+
+eBPF opcode encoding
+--------------------
+
+eBPF is reusing most of the opcode encoding from classic to simplify conversion
+of classic BPF to eBPF. For arithmetic and jump instructions the 8-bit 'code'
+field is divided into three parts::
+
+  +----------------+--------+--------------------+
+  |   4 bits       |  1 bit |   3 bits           |
+  | operation code | source | instruction class  |
+  +----------------+--------+--------------------+
+  (MSB)                                      (LSB)
+
+Three LSB bits store instruction class which is one of:
+
+  ===================     ===============
+  Classic BPF classes     eBPF classes
+  ===================     ===============
+  BPF_LD    0x00          BPF_LD    0x00
+  BPF_LDX   0x01          BPF_LDX   0x01
+  BPF_ST    0x02          BPF_ST    0x02
+  BPF_STX   0x03          BPF_STX   0x03
+  BPF_ALU   0x04          BPF_ALU   0x04
+  BPF_JMP   0x05          BPF_JMP   0x05
+  BPF_RET   0x06          BPF_JMP32 0x06
+  BPF_MISC  0x07          BPF_ALU64 0x07
+  ===================     ===============
+
+When BPF_CLASS(code) == BPF_ALU or BPF_JMP, 4th bit encodes source operand ...
+
+    ::
+
+       BPF_K     0x00
+       BPF_X     0x08
+
+ * in classic BPF, this means::
+
+       BPF_SRC(code) == BPF_X - use register X as source operand
+       BPF_SRC(code) == BPF_K - use 32-bit immediate as source operand
+
+ * in eBPF, this means::
+
+       BPF_SRC(code) == BPF_X - use 'src_reg' register as source operand
+       BPF_SRC(code) == BPF_K - use 32-bit immediate as source operand
+
+... and four MSB bits store operation code.
+
+If BPF_CLASS(code) == BPF_ALU or BPF_ALU64 [ in eBPF ], BPF_OP(code) is one of::
+
+  BPF_ADD   0x00
+  BPF_SUB   0x10
+  BPF_MUL   0x20
+  BPF_DIV   0x30
+  BPF_OR    0x40
+  BPF_AND   0x50
+  BPF_LSH   0x60
+  BPF_RSH   0x70
+  BPF_NEG   0x80
+  BPF_MOD   0x90
+  BPF_XOR   0xa0
+  BPF_MOV   0xb0  /* eBPF only: mov reg to reg */
+  BPF_ARSH  0xc0  /* eBPF only: sign extending shift right */
+  BPF_END   0xd0  /* eBPF only: endianness conversion */
+
+If BPF_CLASS(code) == BPF_JMP or BPF_JMP32 [ in eBPF ], BPF_OP(code) is one of::
+
+  BPF_JA    0x00  /* BPF_JMP only */
+  BPF_JEQ   0x10
+  BPF_JGT   0x20
+  BPF_JGE   0x30
+  BPF_JSET  0x40
+  BPF_JNE   0x50  /* eBPF only: jump != */
+  BPF_JSGT  0x60  /* eBPF only: signed '>' */
+  BPF_JSGE  0x70  /* eBPF only: signed '>=' */
+  BPF_CALL  0x80  /* eBPF BPF_JMP only: function call */
+  BPF_EXIT  0x90  /* eBPF BPF_JMP only: function return */
+  BPF_JLT   0xa0  /* eBPF only: unsigned '<' */
+  BPF_JLE   0xb0  /* eBPF only: unsigned '<=' */
+  BPF_JSLT  0xc0  /* eBPF only: signed '<' */
+  BPF_JSLE  0xd0  /* eBPF only: signed '<=' */
+
+So BPF_ADD | BPF_X | BPF_ALU means 32-bit addition in both classic BPF
+and eBPF. There are only two registers in classic BPF, so it means A += X.
+In eBPF it means dst_reg = (u32) dst_reg + (u32) src_reg; similarly,
+BPF_XOR | BPF_K | BPF_ALU means A ^= imm32 in classic BPF and analogous
+src_reg = (u32) src_reg ^ (u32) imm32 in eBPF.
+
+Classic BPF is using BPF_MISC class to represent A = X and X = A moves.
+eBPF is using BPF_MOV | BPF_X | BPF_ALU code instead. Since there are no
+BPF_MISC operations in eBPF, the class 7 is used as BPF_ALU64 to mean
+exactly the same operations as BPF_ALU, but with 64-bit wide operands
+instead. So BPF_ADD | BPF_X | BPF_ALU64 means 64-bit addition, i.e.:
+dst_reg = dst_reg + src_reg
+
+Classic BPF wastes the whole BPF_RET class to represent a single ``ret``
+operation. Classic BPF_RET | BPF_K means copy imm32 into return register
+and perform function exit. eBPF is modeled to match CPU, so BPF_JMP | BPF_EXIT
+in eBPF means function exit only. The eBPF program needs to store return
+value into register R0 before doing a BPF_EXIT. Class 6 in eBPF is used as
+BPF_JMP32 to mean exactly the same operations as BPF_JMP, but with 32-bit wide
+operands for the comparisons instead.
+
+For load and store instructions the 8-bit 'code' field is divided as::
+
+  +--------+--------+-------------------+
+  | 3 bits | 2 bits |   3 bits          |
+  |  mode  |  size  | instruction class |
+  +--------+--------+-------------------+
+  (MSB)                             (LSB)
+
+Size modifier is one of ...
+
+::
+
+  BPF_W   0x00    /* word */
+  BPF_H   0x08    /* half word */
+  BPF_B   0x10    /* byte */
+  BPF_DW  0x18    /* eBPF only, double word */
+
+... which encodes size of load/store operation::
+
+ B  - 1 byte
+ H  - 2 byte
+ W  - 4 byte
+ DW - 8 byte (eBPF only)
+
+Mode modifier is one of::
+
+  BPF_IMM  0x00  /* used for 32-bit mov in classic BPF and 64-bit in eBPF */
+  BPF_ABS  0x20
+  BPF_IND  0x40
+  BPF_MEM  0x60
+  BPF_LEN  0x80  /* classic BPF only, reserved in eBPF */
+  BPF_MSH  0xa0  /* classic BPF only, reserved in eBPF */
+  BPF_XADD 0xc0  /* eBPF only, exclusive add */
+
+eBPF has two non-generic instructions: (BPF_ABS | <size> | BPF_LD) and
+(BPF_IND | <size> | BPF_LD) which are used to access packet data.
+
+They had to be carried over from classic to have strong performance of
+socket filters running in eBPF interpreter. These instructions can only
+be used when interpreter context is a pointer to ``struct sk_buff`` and
+have seven implicit operands. Register R6 is an implicit input that must
+contain pointer to sk_buff. Register R0 is an implicit output which contains
+the data fetched from the packet. Registers R1-R5 are scratch registers
+and must not be used to store the data across BPF_ABS | BPF_LD or
+BPF_IND | BPF_LD instructions.
+
+These instructions have implicit program exit condition as well. When
+eBPF program is trying to access the data beyond the packet boundary,
+the interpreter will abort the execution of the program. JIT compilers
+therefore must preserve this property. src_reg and imm32 fields are
+explicit inputs to these instructions.
+
+For example::
+
+  BPF_IND | BPF_W | BPF_LD means:
+
+    R0 = ntohl(*(u32 *) (((struct sk_buff *) R6)->data + src_reg + imm32))
+    and R1 - R5 were scratched.
+
+Unlike classic BPF instruction set, eBPF has generic load/store operations::
+
+    BPF_MEM | <size> | BPF_STX:  *(size *) (dst_reg + off) = src_reg
+    BPF_MEM | <size> | BPF_ST:   *(size *) (dst_reg + off) = imm32
+    BPF_MEM | <size> | BPF_LDX:  dst_reg = *(size *) (src_reg + off)
+    BPF_XADD | BPF_W  | BPF_STX: lock xadd *(u32 *)(dst_reg + off16) += src_reg
+    BPF_XADD | BPF_DW | BPF_STX: lock xadd *(u64 *)(dst_reg + off16) += src_reg
+
+Where size is one of: BPF_B or BPF_H or BPF_W or BPF_DW. Note that 1 and
+2 byte atomic increments are not supported.
+
+eBPF has one 16-byte instruction: BPF_LD | BPF_DW | BPF_IMM which consists
+of two consecutive ``struct bpf_insn`` 8-byte blocks and interpreted as single
+instruction that loads 64-bit immediate value into a dst_reg.
+Classic BPF has similar instruction: BPF_LD | BPF_W | BPF_IMM which loads
+32-bit immediate value into a register.
+
+eBPF verifier
+-------------
+The safety of the eBPF program is determined in two steps.
+
+First step does DAG check to disallow loops and other CFG validation.
+In particular it will detect programs that have unreachable instructions.
+(though classic BPF checker allows them)
+
+Second step starts from the first insn and descends all possible paths.
+It simulates execution of every insn and observes the state change of
+registers and stack.
+
+At the start of the program the register R1 contains a pointer to context
+and has type PTR_TO_CTX.
+If verifier sees an insn that does R2=R1, then R2 has now type
+PTR_TO_CTX as well and can be used on the right hand side of expression.
+If R1=PTR_TO_CTX and insn is R2=R1+R1, then R2=SCALAR_VALUE,
+since addition of two valid pointers makes invalid pointer.
+(In 'secure' mode verifier will reject any type of pointer arithmetic to make
+sure that kernel addresses don't leak to unprivileged users)
+
+If register was never written to, it's not readable::
+
+  bpf_mov R0 = R2
+  bpf_exit
+
+will be rejected, since R2 is unreadable at the start of the program.
+
+After kernel function call, R1-R5 are reset to unreadable and
+R0 has a return type of the function.
+
+Since R6-R9 are callee saved, their state is preserved across the call.
+
+::
+
+  bpf_mov R6 = 1
+  bpf_call foo
+  bpf_mov R0 = R6
+  bpf_exit
+
+is a correct program. If there was R1 instead of R6, it would have
+been rejected.
+
+load/store instructions are allowed only with registers of valid types, which
+are PTR_TO_CTX, PTR_TO_MAP, PTR_TO_STACK. They are bounds and alignment checked.
+For example::
+
+ bpf_mov R1 = 1
+ bpf_mov R2 = 2
+ bpf_xadd *(u32 *)(R1 + 3) += R2
+ bpf_exit
+
+will be rejected, since R1 doesn't have a valid pointer type at the time of
+execution of instruction bpf_xadd.
+
+At the start R1 type is PTR_TO_CTX (a pointer to generic ``struct bpf_context``)
+A callback is used to customize verifier to restrict eBPF program access to only
+certain fields within ctx structure with specified size and alignment.
+
+For example, the following insn::
+
+  bpf_ld R0 = *(u32 *)(R6 + 8)
+
+intends to load a word from address R6 + 8 and store it into R0
+If R6=PTR_TO_CTX, via is_valid_access() callback the verifier will know
+that offset 8 of size 4 bytes can be accessed for reading, otherwise
+the verifier will reject the program.
+If R6=PTR_TO_STACK, then access should be aligned and be within
+stack bounds, which are [-MAX_BPF_STACK, 0). In this example offset is 8,
+so it will fail verification, since it's out of bounds.
+
+The verifier will allow eBPF program to read data from stack only after
+it wrote into it.
+
+Classic BPF verifier does similar check with M[0-15] memory slots.
+For example::
+
+  bpf_ld R0 = *(u32 *)(R10 - 4)
+  bpf_exit
+
+is invalid program.
+Though R10 is correct read-only register and has type PTR_TO_STACK
+and R10 - 4 is within stack bounds, there were no stores into that location.
+
+Pointer register spill/fill is tracked as well, since four (R6-R9)
+callee saved registers may not be enough for some programs.
+
+Allowed function calls are customized with bpf_verifier_ops->get_func_proto()
+The eBPF verifier will check that registers match argument constraints.
+After the call register R0 will be set to return type of the function.
+
+Function calls is a main mechanism to extend functionality of eBPF programs.
+Socket filters may let programs to call one set of functions, whereas tracing
+filters may allow completely different set.
+
+If a function made accessible to eBPF program, it needs to be thought through
+from safety point of view. The verifier will guarantee that the function is
+called with valid arguments.
+
+seccomp vs socket filters have different security restrictions for classic BPF.
+Seccomp solves this by two stage verifier: classic BPF verifier is followed
+by seccomp verifier. In case of eBPF one configurable verifier is shared for
+all use cases.
+
+See details of eBPF verifier in kernel/bpf/verifier.c
+
+Register value tracking
+-----------------------
+In order to determine the safety of an eBPF program, the verifier must track
+the range of possible values in each register and also in each stack slot.
+This is done with ``struct bpf_reg_state``, defined in include/linux/
+bpf_verifier.h, which unifies tracking of scalar and pointer values.  Each
+register state has a type, which is either NOT_INIT (the register has not been
+written to), SCALAR_VALUE (some value which is not usable as a pointer), or a
+pointer type.  The types of pointers describe their base, as follows:
+
+
+    PTR_TO_CTX
+                       Pointer to bpf_context.
+    CONST_PTR_TO_MAP
+                       Pointer to struct bpf_map.  "Const" because arithmetic
+                       on these pointers is forbidden.
+    PTR_TO_MAP_VALUE
+                       Pointer to the value stored in a map element.
+    PTR_TO_MAP_VALUE_OR_NULL
+                       Either a pointer to a map value, or NULL; map accesses
+                       (see section 'eBPF maps', below) return this type,
+                       which becomes a PTR_TO_MAP_VALUE when checked != NULL.
+                       Arithmetic on these pointers is forbidden.
+    PTR_TO_STACK
+                       Frame pointer.
+    PTR_TO_PACKET
+                       skb->data.
+    PTR_TO_PACKET_END
+                       skb->data + headlen; arithmetic forbidden.
+    PTR_TO_SOCKET
+                       Pointer to struct bpf_sock_ops, implicitly refcounted.
+    PTR_TO_SOCKET_OR_NULL
+                       Either a pointer to a socket, or NULL; socket lookup
+                       returns this type, which becomes a PTR_TO_SOCKET when
+                       checked != NULL. PTR_TO_SOCKET is reference-counted,
+                       so programs must release the reference through the
+                       socket release function before the end of the program.
+                       Arithmetic on these pointers is forbidden.
+
+However, a pointer may be offset from this base (as a result of pointer
+arithmetic), and this is tracked in two parts: the 'fixed offset' and 'variable
+offset'.  The former is used when an exactly-known value (e.g. an immediate
+operand) is added to a pointer, while the latter is used for values which are
+not exactly known.  The variable offset is also used in SCALAR_VALUEs, to track
+the range of possible values in the register.
+
+The verifier's knowledge about the variable offset consists of:
+
+* minimum and maximum values as unsigned
+* minimum and maximum values as signed
+
+* knowledge of the values of individual bits, in the form of a 'tnum': a u64
+  'mask' and a u64 'value'.  1s in the mask represent bits whose value is unknown;
+  1s in the value represent bits known to be 1.  Bits known to be 0 have 0 in both
+  mask and value; no bit should ever be 1 in both.  For example, if a byte is read
+  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'
+branch it will have a umax_value of 8.  A signed compare (with BPF_JSGT or
+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 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.
+As well as range-checking, the tracked information is also used for enforcing
+alignment of pointer accesses.  For instance, on most systems the packet pointer
+is 2 bytes after a 4-byte alignment.  If a program adds 14 bytes to that to jump
+over the Ethernet header, then reads IHL and addes (IHL * 4), the resulting
+pointer will have a variable offset known to be 4n+2 for some n, so adding the 2
+bytes (NET_IP_ALIGN) gives a 4-byte alignment and so word-sized accesses through
+that pointer are safe.
+The 'id' field is also used on PTR_TO_SOCKET and PTR_TO_SOCKET_OR_NULL, common
+to all copies of the pointer returned from a socket lookup. This has similar
+behaviour to the handling for PTR_TO_MAP_VALUE_OR_NULL->PTR_TO_MAP_VALUE, but
+it also handles reference tracking for the pointer. PTR_TO_SOCKET implicitly
+represents a reference to the corresponding ``struct sock``. To ensure that the
+reference is not leaked, it is imperative to NULL-check the reference and in
+the non-NULL case, and pass the valid reference to the socket release function.
+
+Direct packet access
+--------------------
+In cls_bpf and act_bpf programs the verifier allows direct access to the packet
+data via skb->data and skb->data_end pointers.
+Ex::
+
+    1:  r4 = *(u32 *)(r1 +80)  /* load skb->data_end */
+    2:  r3 = *(u32 *)(r1 +76)  /* load skb->data */
+    3:  r5 = r3
+    4:  r5 += 14
+    5:  if r5 > r4 goto pc+16
+    R1=ctx R3=pkt(id=0,off=0,r=14) R4=pkt_end R5=pkt(id=0,off=14,r=14) R10=fp
+    6:  r0 = *(u16 *)(r3 +12) /* access 12 and 13 bytes of the packet */
+
+this 2byte load from the packet is safe to do, since the program author
+did check ``if (skb->data + 14 > skb->data_end) goto err`` at insn #5 which
+means that in the fall-through case the register R3 (which points to skb->data)
+has at least 14 directly accessible bytes. The verifier marks it
+as R3=pkt(id=0,off=0,r=14).
+id=0 means that no additional variables were added to the register.
+off=0 means that no additional constants were added.
+r=14 is the range of safe access which means that bytes [R3, R3 + 14) are ok.
+Note that R5 is marked as R5=pkt(id=0,off=14,r=14). It also points
+to the packet data, but constant 14 was added to the register, so
+it now points to ``skb->data + 14`` and accessible range is [R5, R5 + 14 - 14)
+which is zero bytes.
+
+More complex packet access may look like::
+
+
+    R0=inv1 R1=ctx R3=pkt(id=0,off=0,r=14) R4=pkt_end R5=pkt(id=0,off=14,r=14) R10=fp
+    6:  r0 = *(u8 *)(r3 +7) /* load 7th byte from the packet */
+    7:  r4 = *(u8 *)(r3 +12)
+    8:  r4 *= 14
+    9:  r3 = *(u32 *)(r1 +76) /* load skb->data */
+    10:  r3 += r4
+    11:  r2 = r1
+    12:  r2 <<= 48
+    13:  r2 >>= 48
+    14:  r3 += r2
+    15:  r2 = r3
+    16:  r2 += 8
+    17:  r1 = *(u32 *)(r1 +80) /* load skb->data_end */
+    18:  if r2 > r1 goto pc+2
+    R0=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R1=pkt_end R2=pkt(id=2,off=8,r=8) R3=pkt(id=2,off=0,r=8) R4=inv(id=0,umax_value=3570,var_off=(0x0; 0xfffe)) R5=pkt(id=0,off=14,r=14) R10=fp
+    19:  r1 = *(u8 *)(r3 +4)
+
+The state of the register R3 is R3=pkt(id=2,off=0,r=8)
+id=2 means that two ``r3 += rX`` instructions were seen, so r3 points to some
+offset within a packet and since the program author did
+``if (r3 + 8 > r1) goto err`` at insn #18, the safe range is [R3, R3 + 8).
+The verifier only allows 'add'/'sub' operations on packet registers. Any other
+operation will set the register state to 'SCALAR_VALUE' and it won't be
+available for direct packet access.
+
+Operation ``r3 += rX`` may overflow and become less than original skb->data,
+therefore the verifier has to prevent that.  So when it sees ``r3 += rX``
+instruction and rX is more than 16-bit value, any subsequent bounds-check of r3
+against skb->data_end will not give us 'range' information, so attempts to read
+through the pointer will give "invalid access to packet" error.
+
+Ex. after insn ``r4 = *(u8 *)(r3 +12)`` (insn #7 above) the state of r4 is
+R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) which means that upper 56 bits
+of the register are guaranteed to be zero, and nothing is known about the lower
+8 bits. After insn ``r4 *= 14`` the state becomes
+R4=inv(id=0,umax_value=3570,var_off=(0x0; 0xfffe)), since multiplying an 8-bit
+value by constant 14 will keep upper 52 bits as zero, also the least significant
+bit will be zero as 14 is even.  Similarly ``r2 >>= 48`` will make
+R2=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff)), since the shift is not sign
+extending.  This logic is implemented in adjust_reg_min_max_vals() function,
+which calls adjust_ptr_min_max_vals() for adding pointer to scalar (or vice
+versa) and adjust_scalar_min_max_vals() for operations on two scalars.
+
+The end result is that bpf program author can access packet directly
+using normal C code as::
+
+  void *data = (void *)(long)skb->data;
+  void *data_end = (void *)(long)skb->data_end;
+  struct eth_hdr *eth = data;
+  struct iphdr *iph = data + sizeof(*eth);
+  struct udphdr *udp = data + sizeof(*eth) + sizeof(*iph);
+
+  if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*udp) > data_end)
+         return 0;
+  if (eth->h_proto != htons(ETH_P_IP))
+         return 0;
+  if (iph->protocol != IPPROTO_UDP || iph->ihl != 5)
+         return 0;
+  if (udp->dest == 53 || udp->source == 9)
+         ...;
+
+which makes such programs easier to write comparing to LD_ABS insn
+and significantly faster.
+
+eBPF maps
+---------
+'maps' is a generic storage of different types for sharing data between kernel
+and userspace.
+
+The maps are accessed from user space via BPF syscall, which has commands:
+
+- create a map with given type and attributes
+  ``map_fd = bpf(BPF_MAP_CREATE, union bpf_attr *attr, u32 size)``
+  using attr->map_type, attr->key_size, attr->value_size, attr->max_entries
+  returns process-local file descriptor or negative error
+
+- lookup key in a given map
+  ``err = bpf(BPF_MAP_LOOKUP_ELEM, union bpf_attr *attr, u32 size)``
+  using attr->map_fd, attr->key, attr->value
+  returns zero and stores found elem into value or negative error
+
+- create or update key/value pair in a given map
+  ``err = bpf(BPF_MAP_UPDATE_ELEM, union bpf_attr *attr, u32 size)``
+  using attr->map_fd, attr->key, attr->value
+  returns zero or negative error
+
+- find and delete element by key in a given map
+  ``err = bpf(BPF_MAP_DELETE_ELEM, union bpf_attr *attr, u32 size)``
+  using attr->map_fd, attr->key
+
+- to delete map: close(fd)
+  Exiting process will delete maps automatically
+
+userspace programs use this syscall to create/access maps that eBPF programs
+are concurrently updating.
+
+maps can have different types: hash, array, bloom filter, radix-tree, etc.
+
+The map is defined by:
+
+  - type
+  - max number of elements
+  - key size in bytes
+  - value size in bytes
+
+Pruning
+-------
+The verifier does not actually walk all possible paths through the program.  For
+each new branch to analyse, the verifier looks at all the states it's previously
+been in when at this instruction.  If any of them contain the current state as a
+subset, the branch is 'pruned' - that is, the fact that the previous state was
+accepted implies the current state would be as well.  For instance, if in the
+previous state, r1 held a packet-pointer, and in the current state, r1 holds a
+packet-pointer with a range as long or longer and at least as strict an
+alignment, then r1 is safe.  Similarly, if r2 was NOT_INIT before then it can't
+have been used by any path from that point, so any value in r2 (including
+another NOT_INIT) is safe.  The implementation is in the function regsafe().
+Pruning considers not only the registers but also the stack (and any spilled
+registers it may hold).  They must all be safe for the branch to be pruned.
+This is implemented in states_equal().
+
+Understanding eBPF verifier messages
+------------------------------------
+
+The following are few examples of invalid eBPF programs and verifier error
+messages as seen in the log:
+
+Program with unreachable instructions::
+
+  static struct bpf_insn prog[] = {
+  BPF_EXIT_INSN(),
+  BPF_EXIT_INSN(),
+  };
+
+Error:
+
+  unreachable insn 1
+
+Program that reads uninitialized register::
+
+  BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
+  BPF_EXIT_INSN(),
+
+Error::
+
+  0: (bf) r0 = r2
+  R2 !read_ok
+
+Program that doesn't initialize R0 before exiting::
+
+  BPF_MOV64_REG(BPF_REG_2, BPF_REG_1),
+  BPF_EXIT_INSN(),
+
+Error::
+
+  0: (bf) r2 = r1
+  1: (95) exit
+  R0 !read_ok
+
+Program that accesses stack out of bounds::
+
+    BPF_ST_MEM(BPF_DW, BPF_REG_10, 8, 0),
+    BPF_EXIT_INSN(),
+
+Error::
+
+    0: (7a) *(u64 *)(r10 +8) = 0
+    invalid stack off=8 size=8
+
+Program that doesn't initialize stack before passing its address into function::
+
+  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+  BPF_LD_MAP_FD(BPF_REG_1, 0),
+  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+  BPF_EXIT_INSN(),
+
+Error::
+
+  0: (bf) r2 = r10
+  1: (07) r2 += -8
+  2: (b7) r1 = 0x0
+  3: (85) call 1
+  invalid indirect read from stack off -8+0 size 8
+
+Program that uses invalid map_fd=0 while calling to map_lookup_elem() function::
+
+  BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
+  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+  BPF_LD_MAP_FD(BPF_REG_1, 0),
+  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+  BPF_EXIT_INSN(),
+
+Error::
+
+  0: (7a) *(u64 *)(r10 -8) = 0
+  1: (bf) r2 = r10
+  2: (07) r2 += -8
+  3: (b7) r1 = 0x0
+  4: (85) call 1
+  fd 0 is not pointing to valid bpf_map
+
+Program that doesn't check return value of map_lookup_elem() before accessing
+map element::
+
+  BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
+  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+  BPF_LD_MAP_FD(BPF_REG_1, 0),
+  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+  BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
+  BPF_EXIT_INSN(),
+
+Error::
+
+  0: (7a) *(u64 *)(r10 -8) = 0
+  1: (bf) r2 = r10
+  2: (07) r2 += -8
+  3: (b7) r1 = 0x0
+  4: (85) call 1
+  5: (7a) *(u64 *)(r0 +0) = 0
+  R0 invalid mem access 'map_value_or_null'
+
+Program that correctly checks map_lookup_elem() returned value for NULL, but
+accesses the memory with incorrect alignment::
+
+  BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
+  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+  BPF_LD_MAP_FD(BPF_REG_1, 0),
+  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+  BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
+  BPF_ST_MEM(BPF_DW, BPF_REG_0, 4, 0),
+  BPF_EXIT_INSN(),
+
+Error::
+
+  0: (7a) *(u64 *)(r10 -8) = 0
+  1: (bf) r2 = r10
+  2: (07) r2 += -8
+  3: (b7) r1 = 1
+  4: (85) call 1
+  5: (15) if r0 == 0x0 goto pc+1
+   R0=map_ptr R10=fp
+  6: (7a) *(u64 *)(r0 +4) = 0
+  misaligned access off 4 size 8
+
+Program that correctly checks map_lookup_elem() returned value for NULL and
+accesses memory with correct alignment in one side of 'if' branch, but fails
+to do so in the other side of 'if' branch::
+
+  BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
+  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+  BPF_LD_MAP_FD(BPF_REG_1, 0),
+  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
+  BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
+  BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
+  BPF_EXIT_INSN(),
+  BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 1),
+  BPF_EXIT_INSN(),
+
+Error::
+
+  0: (7a) *(u64 *)(r10 -8) = 0
+  1: (bf) r2 = r10
+  2: (07) r2 += -8
+  3: (b7) r1 = 1
+  4: (85) call 1
+  5: (15) if r0 == 0x0 goto pc+2
+   R0=map_ptr R10=fp
+  6: (7a) *(u64 *)(r0 +0) = 0
+  7: (95) exit
+
+  from 5 to 8: R0=imm0 R10=fp
+  8: (7a) *(u64 *)(r0 +0) = 1
+  R0 invalid mem access 'imm'
+
+Program that performs a socket lookup then sets the pointer to NULL without
+checking it::
+
+  BPF_MOV64_IMM(BPF_REG_2, 0),
+  BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, -8),
+  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+  BPF_MOV64_IMM(BPF_REG_3, 4),
+  BPF_MOV64_IMM(BPF_REG_4, 0),
+  BPF_MOV64_IMM(BPF_REG_5, 0),
+  BPF_EMIT_CALL(BPF_FUNC_sk_lookup_tcp),
+  BPF_MOV64_IMM(BPF_REG_0, 0),
+  BPF_EXIT_INSN(),
+
+Error::
+
+  0: (b7) r2 = 0
+  1: (63) *(u32 *)(r10 -8) = r2
+  2: (bf) r2 = r10
+  3: (07) r2 += -8
+  4: (b7) r3 = 4
+  5: (b7) r4 = 0
+  6: (b7) r5 = 0
+  7: (85) call bpf_sk_lookup_tcp#65
+  8: (b7) r0 = 0
+  9: (95) exit
+  Unreleased reference id=1, alloc_insn=7
+
+Program that performs a socket lookup but does not NULL-check the returned
+value::
+
+  BPF_MOV64_IMM(BPF_REG_2, 0),
+  BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, -8),
+  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+  BPF_MOV64_IMM(BPF_REG_3, 4),
+  BPF_MOV64_IMM(BPF_REG_4, 0),
+  BPF_MOV64_IMM(BPF_REG_5, 0),
+  BPF_EMIT_CALL(BPF_FUNC_sk_lookup_tcp),
+  BPF_EXIT_INSN(),
+
+Error::
+
+  0: (b7) r2 = 0
+  1: (63) *(u32 *)(r10 -8) = r2
+  2: (bf) r2 = r10
+  3: (07) r2 += -8
+  4: (b7) r3 = 4
+  5: (b7) r4 = 0
+  6: (b7) r5 = 0
+  7: (85) call bpf_sk_lookup_tcp#65
+  8: (95) exit
+  Unreleased reference id=1, alloc_insn=7
+
+Testing
+-------
+
+Next to the BPF toolchain, the kernel also ships a test module that contains
+various test cases for classic and internal BPF that can be executed against
+the BPF interpreter and JIT compiler. It can be found in lib/test_bpf.c and
+enabled via Kconfig::
+
+  CONFIG_TEST_BPF=m
+
+After the module has been built and installed, the test suite can be executed
+via insmod or modprobe against 'test_bpf' module. Results of the test cases
+including timings in nsec can be found in the kernel log (dmesg).
+
+Misc
+----
+
+Also trinity, the Linux syscall fuzzer, has built-in support for BPF and
+SECCOMP-BPF kernel fuzzing.
+
+Written by
+----------
+
+The document was written in the hope that it is found useful and in order
+to give potential BPF hackers or security auditors a better overview of
+the underlying architecture.
+
+- Jay Schulist <jschlst@samba.org>
+- Daniel Borkmann <daniel@iogearbox.net>
+- Alexei Starovoitov <ast@kernel.org>
diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt
deleted file mode 100644 (file)
index 2f0f8b1..0000000
+++ /dev/null
@@ -1,1545 +0,0 @@
-Linux Socket Filtering aka Berkeley Packet Filter (BPF)
-=======================================================
-
-Introduction
-------------
-
-Linux Socket Filtering (LSF) is derived from the Berkeley Packet Filter.
-Though there are some distinct differences between the BSD and Linux
-Kernel filtering, but when we speak of BPF or LSF in Linux context, we
-mean the very same mechanism of filtering in the Linux kernel.
-
-BPF allows a user-space program to attach a filter onto any socket and
-allow or disallow certain types of data to come through the socket. LSF
-follows exactly the same filter code structure as BSD's BPF, so referring
-to the BSD bpf.4 manpage is very helpful in creating filters.
-
-On Linux, BPF is much simpler than on BSD. One does not have to worry
-about devices or anything like that. You simply create your filter code,
-send it to the kernel via the SO_ATTACH_FILTER option and if your filter
-code passes the kernel check on it, you then immediately begin filtering
-data on that socket.
-
-You can also detach filters from your socket via the SO_DETACH_FILTER
-option. This will probably not be used much since when you close a socket
-that has a filter on it the filter is automagically removed. The other
-less common case may be adding a different filter on the same socket where
-you had another filter that is still running: the kernel takes care of
-removing the old one and placing your new one in its place, assuming your
-filter has passed the checks, otherwise if it fails the old filter will
-remain on that socket.
-
-SO_LOCK_FILTER option allows to lock the filter attached to a socket. Once
-set, a filter cannot be removed or changed. This allows one process to
-setup a socket, attach a filter, lock it then drop privileges and be
-assured that the filter will be kept until the socket is closed.
-
-The biggest user of this construct might be libpcap. Issuing a high-level
-filter command like `tcpdump -i em1 port 22` passes through the libpcap
-internal compiler that generates a structure that can eventually be loaded
-via SO_ATTACH_FILTER to the kernel. `tcpdump -i em1 port 22 -ddd`
-displays what is being placed into this structure.
-
-Although we were only speaking about sockets here, BPF in Linux is used
-in many more places. There's xt_bpf for netfilter, cls_bpf in the kernel
-qdisc layer, SECCOMP-BPF (SECure COMPuting [1]), and lots of other places
-such as team driver, PTP code, etc where BPF is being used.
-
- [1] Documentation/userspace-api/seccomp_filter.rst
-
-Original BPF paper:
-
-Steven McCanne and Van Jacobson. 1993. The BSD packet filter: a new
-architecture for user-level packet capture. In Proceedings of the
-USENIX Winter 1993 Conference Proceedings on USENIX Winter 1993
-Conference Proceedings (USENIX'93). USENIX Association, Berkeley,
-CA, USA, 2-2. [http://www.tcpdump.org/papers/bpf-usenix93.pdf]
-
-Structure
----------
-
-User space applications include <linux/filter.h> which contains the
-following relevant structures:
-
-struct sock_filter {   /* Filter block */
-       __u16   code;   /* Actual filter code */
-       __u8    jt;     /* Jump true */
-       __u8    jf;     /* Jump false */
-       __u32   k;      /* Generic multiuse field */
-};
-
-Such a structure is assembled as an array of 4-tuples, that contains
-a code, jt, jf and k value. jt and jf are jump offsets and k a generic
-value to be used for a provided code.
-
-struct sock_fprog {                    /* Required for SO_ATTACH_FILTER. */
-       unsigned short             len; /* Number of filter blocks */
-       struct sock_filter __user *filter;
-};
-
-For socket filtering, a pointer to this structure (as shown in
-follow-up example) is being passed to the kernel through setsockopt(2).
-
-Example
--------
-
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <arpa/inet.h>
-#include <linux/if_ether.h>
-/* ... */
-
-/* From the example above: tcpdump -i em1 port 22 -dd */
-struct sock_filter code[] = {
-       { 0x28,  0,  0, 0x0000000c },
-       { 0x15,  0,  8, 0x000086dd },
-       { 0x30,  0,  0, 0x00000014 },
-       { 0x15,  2,  0, 0x00000084 },
-       { 0x15,  1,  0, 0x00000006 },
-       { 0x15,  0, 17, 0x00000011 },
-       { 0x28,  0,  0, 0x00000036 },
-       { 0x15, 14,  0, 0x00000016 },
-       { 0x28,  0,  0, 0x00000038 },
-       { 0x15, 12, 13, 0x00000016 },
-       { 0x15,  0, 12, 0x00000800 },
-       { 0x30,  0,  0, 0x00000017 },
-       { 0x15,  2,  0, 0x00000084 },
-       { 0x15,  1,  0, 0x00000006 },
-       { 0x15,  0,  8, 0x00000011 },
-       { 0x28,  0,  0, 0x00000014 },
-       { 0x45,  6,  0, 0x00001fff },
-       { 0xb1,  0,  0, 0x0000000e },
-       { 0x48,  0,  0, 0x0000000e },
-       { 0x15,  2,  0, 0x00000016 },
-       { 0x48,  0,  0, 0x00000010 },
-       { 0x15,  0,  1, 0x00000016 },
-       { 0x06,  0,  0, 0x0000ffff },
-       { 0x06,  0,  0, 0x00000000 },
-};
-
-struct sock_fprog bpf = {
-       .len = ARRAY_SIZE(code),
-       .filter = code,
-};
-
-sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
-if (sock < 0)
-       /* ... bail out ... */
-
-ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
-if (ret < 0)
-       /* ... bail out ... */
-
-/* ... */
-close(sock);
-
-The above example code attaches a socket filter for a PF_PACKET socket
-in order to let all IPv4/IPv6 packets with port 22 pass. The rest will
-be dropped for this socket.
-
-The setsockopt(2) call to SO_DETACH_FILTER doesn't need any arguments
-and SO_LOCK_FILTER for preventing the filter to be detached, takes an
-integer value with 0 or 1.
-
-Note that socket filters are not restricted to PF_PACKET sockets only,
-but can also be used on other socket families.
-
-Summary of system calls:
-
- * setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &val, sizeof(val));
- * setsockopt(sockfd, SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val));
- * setsockopt(sockfd, SOL_SOCKET, SO_LOCK_FILTER,   &val, sizeof(val));
-
-Normally, most use cases for socket filtering on packet sockets will be
-covered by libpcap in high-level syntax, so as an application developer
-you should stick to that. libpcap wraps its own layer around all that.
-
-Unless i) using/linking to libpcap is not an option, ii) the required BPF
-filters use Linux extensions that are not supported by libpcap's compiler,
-iii) a filter might be more complex and not cleanly implementable with
-libpcap's compiler, or iv) particular filter codes should be optimized
-differently than libpcap's internal compiler does; then in such cases
-writing such a filter "by hand" can be of an alternative. For example,
-xt_bpf and cls_bpf users might have requirements that could result in
-more complex filter code, or one that cannot be expressed with libpcap
-(e.g. different return codes for various code paths). Moreover, BPF JIT
-implementors may wish to manually write test cases and thus need low-level
-access to BPF code as well.
-
-BPF engine and instruction set
-------------------------------
-
-Under tools/bpf/ there's a small helper tool called bpf_asm which can
-be used to write low-level filters for example scenarios mentioned in the
-previous section. Asm-like syntax mentioned here has been implemented in
-bpf_asm and will be used for further explanations (instead of dealing with
-less readable opcodes directly, principles are the same). The syntax is
-closely modelled after Steven McCanne's and Van Jacobson's BPF paper.
-
-The BPF architecture consists of the following basic elements:
-
-  Element          Description
-
-  A                32 bit wide accumulator
-  X                32 bit wide X register
-  M[]              16 x 32 bit wide misc registers aka "scratch memory
-                   store", addressable from 0 to 15
-
-A program, that is translated by bpf_asm into "opcodes" is an array that
-consists of the following elements (as already mentioned):
-
-  op:16, jt:8, jf:8, k:32
-
-The element op is a 16 bit wide opcode that has a particular instruction
-encoded. jt and jf are two 8 bit wide jump targets, one for condition
-"jump if true", the other one "jump if false". Eventually, element k
-contains a miscellaneous argument that can be interpreted in different
-ways depending on the given instruction in op.
-
-The instruction set consists of load, store, branch, alu, miscellaneous
-and return instructions that are also represented in bpf_asm syntax. This
-table lists all bpf_asm instructions available resp. what their underlying
-opcodes as defined in linux/filter.h stand for:
-
-  Instruction      Addressing mode      Description
-
-  ld               1, 2, 3, 4, 12       Load word into A
-  ldi              4                    Load word into A
-  ldh              1, 2                 Load half-word into A
-  ldb              1, 2                 Load byte into A
-  ldx              3, 4, 5, 12          Load word into X
-  ldxi             4                    Load word into X
-  ldxb             5                    Load byte into X
-
-  st               3                    Store A into M[]
-  stx              3                    Store X into M[]
-
-  jmp              6                    Jump to label
-  ja               6                    Jump to label
-  jeq              7, 8, 9, 10          Jump on A == <x>
-  jneq             9, 10                Jump on A != <x>
-  jne              9, 10                Jump on A != <x>
-  jlt              9, 10                Jump on A <  <x>
-  jle              9, 10                Jump on A <= <x>
-  jgt              7, 8, 9, 10          Jump on A >  <x>
-  jge              7, 8, 9, 10          Jump on A >= <x>
-  jset             7, 8, 9, 10          Jump on A &  <x>
-
-  add              0, 4                 A + <x>
-  sub              0, 4                 A - <x>
-  mul              0, 4                 A * <x>
-  div              0, 4                 A / <x>
-  mod              0, 4                 A % <x>
-  neg                                   !A
-  and              0, 4                 A & <x>
-  or               0, 4                 A | <x>
-  xor              0, 4                 A ^ <x>
-  lsh              0, 4                 A << <x>
-  rsh              0, 4                 A >> <x>
-
-  tax                                   Copy A into X
-  txa                                   Copy X into A
-
-  ret              4, 11                Return
-
-The next table shows addressing formats from the 2nd column:
-
-  Addressing mode  Syntax               Description
-
-   0               x/%x                 Register X
-   1               [k]                  BHW at byte offset k in the packet
-   2               [x + k]              BHW at the offset X + k in the packet
-   3               M[k]                 Word at offset k in M[]
-   4               #k                   Literal value stored in k
-   5               4*([k]&0xf)          Lower nibble * 4 at byte offset k in the packet
-   6               L                    Jump label L
-   7               #k,Lt,Lf             Jump to Lt if true, otherwise jump to Lf
-   8               x/%x,Lt,Lf           Jump to Lt if true, otherwise jump to Lf
-   9               #k,Lt                Jump to Lt if predicate is true
-  10               x/%x,Lt              Jump to Lt if predicate is true
-  11               a/%a                 Accumulator A
-  12               extension            BPF extension
-
-The Linux kernel also has a couple of BPF extensions that are used along
-with the class of load instructions by "overloading" the k argument with
-a negative offset + a particular extension offset. The result of such BPF
-extensions are loaded into A.
-
-Possible BPF extensions are shown in the following table:
-
-  Extension                             Description
-
-  len                                   skb->len
-  proto                                 skb->protocol
-  type                                  skb->pkt_type
-  poff                                  Payload start offset
-  ifidx                                 skb->dev->ifindex
-  nla                                   Netlink attribute of type X with offset A
-  nlan                                  Nested Netlink attribute of type X with offset A
-  mark                                  skb->mark
-  queue                                 skb->queue_mapping
-  hatype                                skb->dev->type
-  rxhash                                skb->hash
-  cpu                                   raw_smp_processor_id()
-  vlan_tci                              skb_vlan_tag_get(skb)
-  vlan_avail                            skb_vlan_tag_present(skb)
-  vlan_tpid                             skb->vlan_proto
-  rand                                  prandom_u32()
-
-These extensions can also be prefixed with '#'.
-Examples for low-level BPF:
-
-** ARP packets:
-
-  ldh [12]
-  jne #0x806, drop
-  ret #-1
-  drop: ret #0
-
-** IPv4 TCP packets:
-
-  ldh [12]
-  jne #0x800, drop
-  ldb [23]
-  jneq #6, drop
-  ret #-1
-  drop: ret #0
-
-** (Accelerated) VLAN w/ id 10:
-
-  ld vlan_tci
-  jneq #10, drop
-  ret #-1
-  drop: ret #0
-
-** icmp random packet sampling, 1 in 4
-  ldh [12]
-  jne #0x800, drop
-  ldb [23]
-  jneq #1, drop
-  # get a random uint32 number
-  ld rand
-  mod #4
-  jneq #1, drop
-  ret #-1
-  drop: ret #0
-
-** SECCOMP filter example:
-
-  ld [4]                  /* offsetof(struct seccomp_data, arch) */
-  jne #0xc000003e, bad    /* AUDIT_ARCH_X86_64 */
-  ld [0]                  /* offsetof(struct seccomp_data, nr) */
-  jeq #15, good           /* __NR_rt_sigreturn */
-  jeq #231, good          /* __NR_exit_group */
-  jeq #60, good           /* __NR_exit */
-  jeq #0, good            /* __NR_read */
-  jeq #1, good            /* __NR_write */
-  jeq #5, good            /* __NR_fstat */
-  jeq #9, good            /* __NR_mmap */
-  jeq #14, good           /* __NR_rt_sigprocmask */
-  jeq #13, good           /* __NR_rt_sigaction */
-  jeq #35, good           /* __NR_nanosleep */
-  bad: ret #0             /* SECCOMP_RET_KILL_THREAD */
-  good: ret #0x7fff0000   /* SECCOMP_RET_ALLOW */
-
-The above example code can be placed into a file (here called "foo"), and
-then be passed to the bpf_asm tool for generating opcodes, output that xt_bpf
-and cls_bpf understands and can directly be loaded with. Example with above
-ARP code:
-
-$ ./bpf_asm foo
-4,40 0 0 12,21 0 1 2054,6 0 0 4294967295,6 0 0 0,
-
-In copy and paste C-like output:
-
-$ ./bpf_asm -c foo
-{ 0x28,  0,  0, 0x0000000c },
-{ 0x15,  0,  1, 0x00000806 },
-{ 0x06,  0,  0, 0xffffffff },
-{ 0x06,  0,  0, 0000000000 },
-
-In particular, as usage with xt_bpf or cls_bpf can result in more complex BPF
-filters that might not be obvious at first, it's good to test filters before
-attaching to a live system. For that purpose, there's a small tool called
-bpf_dbg under tools/bpf/ in the kernel source directory. This debugger allows
-for testing BPF filters against given pcap files, single stepping through the
-BPF code on the pcap's packets and to do BPF machine register dumps.
-
-Starting bpf_dbg is trivial and just requires issuing:
-
-# ./bpf_dbg
-
-In case input and output do not equal stdin/stdout, bpf_dbg takes an
-alternative stdin source as a first argument, and an alternative stdout
-sink as a second one, e.g. `./bpf_dbg test_in.txt test_out.txt`.
-
-Other than that, a particular libreadline configuration can be set via
-file "~/.bpf_dbg_init" and the command history is stored in the file
-"~/.bpf_dbg_history".
-
-Interaction in bpf_dbg happens through a shell that also has auto-completion
-support (follow-up example commands starting with '>' denote bpf_dbg shell).
-The usual workflow would be to ...
-
-> load bpf 6,40 0 0 12,21 0 3 2048,48 0 0 23,21 0 1 1,6 0 0 65535,6 0 0 0
-  Loads a BPF filter from standard output of bpf_asm, or transformed via
-  e.g. `tcpdump -iem1 -ddd port 22 | tr '\n' ','`. Note that for JIT
-  debugging (next section), this command creates a temporary socket and
-  loads the BPF code into the kernel. Thus, this will also be useful for
-  JIT developers.
-
-> load pcap foo.pcap
-  Loads standard tcpdump pcap file.
-
-> run [<n>]
-bpf passes:1 fails:9
-  Runs through all packets from a pcap to account how many passes and fails
-  the filter will generate. A limit of packets to traverse can be given.
-
-> disassemble
-l0:    ldh [12]
-l1:    jeq #0x800, l2, l5
-l2:    ldb [23]
-l3:    jeq #0x1, l4, l5
-l4:    ret #0xffff
-l5:    ret #0
-  Prints out BPF code disassembly.
-
-> dump
-/* { op, jt, jf, k }, */
-{ 0x28,  0,  0, 0x0000000c },
-{ 0x15,  0,  3, 0x00000800 },
-{ 0x30,  0,  0, 0x00000017 },
-{ 0x15,  0,  1, 0x00000001 },
-{ 0x06,  0,  0, 0x0000ffff },
-{ 0x06,  0,  0, 0000000000 },
-  Prints out C-style BPF code dump.
-
-> breakpoint 0
-breakpoint at: l0:     ldh [12]
-> breakpoint 1
-breakpoint at: l1:     jeq #0x800, l2, l5
-  ...
-  Sets breakpoints at particular BPF instructions. Issuing a `run` command
-  will walk through the pcap file continuing from the current packet and
-  break when a breakpoint is being hit (another `run` will continue from
-  the currently active breakpoint executing next instructions):
-
-  > run
-  -- register dump --
-  pc:       [0]                       <-- program counter
-  code:     [40] jt[0] jf[0] k[12]    <-- plain BPF code of current instruction
-  curr:     l0:        ldh [12]              <-- disassembly of current instruction
-  A:        [00000000][0]             <-- content of A (hex, decimal)
-  X:        [00000000][0]             <-- content of X (hex, decimal)
-  M[0,15]:  [00000000][0]             <-- folded content of M (hex, decimal)
-  -- packet dump --                   <-- Current packet from pcap (hex)
-  len: 42
-    0: 00 19 cb 55 55 a4 00 14 a4 43 78 69 08 06 00 01
-   16: 08 00 06 04 00 01 00 14 a4 43 78 69 0a 3b 01 26
-   32: 00 00 00 00 00 00 0a 3b 01 01
-  (breakpoint)
-  >
-
-> breakpoint
-breakpoints: 0 1
-  Prints currently set breakpoints.
-
-> step [-<n>, +<n>]
-  Performs single stepping through the BPF program from the current pc
-  offset. Thus, on each step invocation, above register dump is issued.
-  This can go forwards and backwards in time, a plain `step` will break
-  on the next BPF instruction, thus +1. (No `run` needs to be issued here.)
-
-> select <n>
-  Selects a given packet from the pcap file to continue from. Thus, on
-  the next `run` or `step`, the BPF program is being evaluated against
-  the user pre-selected packet. Numbering starts just as in Wireshark
-  with index 1.
-
-> quit
-#
-  Exits bpf_dbg.
-
-JIT compiler
-------------
-
-The Linux kernel has a built-in BPF JIT compiler for x86_64, SPARC,
-PowerPC, ARM, ARM64, MIPS, RISC-V and s390 and can be enabled through
-CONFIG_BPF_JIT. The JIT compiler is transparently invoked for each
-attached filter from user space or for internal kernel users if it has
-been previously enabled by root:
-
-  echo 1 > /proc/sys/net/core/bpf_jit_enable
-
-For JIT developers, doing audits etc, each compile run can output the generated
-opcode image into the kernel log via:
-
-  echo 2 > /proc/sys/net/core/bpf_jit_enable
-
-Example output from dmesg:
-
-[ 3389.935842] flen=6 proglen=70 pass=3 image=ffffffffa0069c8f
-[ 3389.935847] JIT code: 00000000: 55 48 89 e5 48 83 ec 60 48 89 5d f8 44 8b 4f 68
-[ 3389.935849] JIT code: 00000010: 44 2b 4f 6c 4c 8b 87 d8 00 00 00 be 0c 00 00 00
-[ 3389.935850] JIT code: 00000020: e8 1d 94 ff e0 3d 00 08 00 00 75 16 be 17 00 00
-[ 3389.935851] JIT code: 00000030: 00 e8 28 94 ff e0 83 f8 01 75 07 b8 ff ff 00 00
-[ 3389.935852] JIT code: 00000040: eb 02 31 c0 c9 c3
-
-When CONFIG_BPF_JIT_ALWAYS_ON is enabled, bpf_jit_enable is permanently set to 1 and
-setting any other value than that will return in failure. This is even the case for
-setting bpf_jit_enable to 2, since dumping the final JIT image into the kernel log
-is discouraged and introspection through bpftool (under tools/bpf/bpftool/) is the
-generally recommended approach instead.
-
-In the kernel source tree under tools/bpf/, there's bpf_jit_disasm for
-generating disassembly out of the kernel log's hexdump:
-
-# ./bpf_jit_disasm
-70 bytes emitted from JIT compiler (pass:3, flen:6)
-ffffffffa0069c8f + <x>:
-   0:  push   %rbp
-   1:  mov    %rsp,%rbp
-   4:  sub    $0x60,%rsp
-   8:  mov    %rbx,-0x8(%rbp)
-   c:  mov    0x68(%rdi),%r9d
-  10:  sub    0x6c(%rdi),%r9d
-  14:  mov    0xd8(%rdi),%r8
-  1b:  mov    $0xc,%esi
-  20:  callq  0xffffffffe0ff9442
-  25:  cmp    $0x800,%eax
-  2a:  jne    0x0000000000000042
-  2c:  mov    $0x17,%esi
-  31:  callq  0xffffffffe0ff945e
-  36:  cmp    $0x1,%eax
-  39:  jne    0x0000000000000042
-  3b:  mov    $0xffff,%eax
-  40:  jmp    0x0000000000000044
-  42:  xor    %eax,%eax
-  44:  leaveq
-  45:  retq
-
-Issuing option `-o` will "annotate" opcodes to resulting assembler
-instructions, which can be very useful for JIT developers:
-
-# ./bpf_jit_disasm -o
-70 bytes emitted from JIT compiler (pass:3, flen:6)
-ffffffffa0069c8f + <x>:
-   0:  push   %rbp
-       55
-   1:  mov    %rsp,%rbp
-       48 89 e5
-   4:  sub    $0x60,%rsp
-       48 83 ec 60
-   8:  mov    %rbx,-0x8(%rbp)
-       48 89 5d f8
-   c:  mov    0x68(%rdi),%r9d
-       44 8b 4f 68
-  10:  sub    0x6c(%rdi),%r9d
-       44 2b 4f 6c
-  14:  mov    0xd8(%rdi),%r8
-       4c 8b 87 d8 00 00 00
-  1b:  mov    $0xc,%esi
-       be 0c 00 00 00
-  20:  callq  0xffffffffe0ff9442
-       e8 1d 94 ff e0
-  25:  cmp    $0x800,%eax
-       3d 00 08 00 00
-  2a:  jne    0x0000000000000042
-       75 16
-  2c:  mov    $0x17,%esi
-       be 17 00 00 00
-  31:  callq  0xffffffffe0ff945e
-       e8 28 94 ff e0
-  36:  cmp    $0x1,%eax
-       83 f8 01
-  39:  jne    0x0000000000000042
-       75 07
-  3b:  mov    $0xffff,%eax
-       b8 ff ff 00 00
-  40:  jmp    0x0000000000000044
-       eb 02
-  42:  xor    %eax,%eax
-       31 c0
-  44:  leaveq
-       c9
-  45:  retq
-       c3
-
-For BPF JIT developers, bpf_jit_disasm, bpf_asm and bpf_dbg provides a useful
-toolchain for developing and testing the kernel's JIT compiler.
-
-BPF kernel internals
---------------------
-Internally, for the kernel interpreter, a different instruction set
-format with similar underlying principles from BPF described in previous
-paragraphs is being used. However, the instruction set format is modelled
-closer to the underlying architecture to mimic native instruction sets, so
-that a better performance can be achieved (more details later). This new
-ISA is called 'eBPF' or 'internal BPF' interchangeably. (Note: eBPF which
-originates from [e]xtended BPF is not the same as BPF extensions! While
-eBPF is an ISA, BPF extensions date back to classic BPF's 'overloading'
-of BPF_LD | BPF_{B,H,W} | BPF_ABS instruction.)
-
-It is designed to be JITed with one to one mapping, which can also open up
-the possibility for GCC/LLVM compilers to generate optimized eBPF code through
-an eBPF backend that performs almost as fast as natively compiled code.
-
-The new instruction set was originally designed with the possible goal in
-mind to write programs in "restricted C" and compile into eBPF with a optional
-GCC/LLVM backend, so that it can just-in-time map to modern 64-bit CPUs with
-minimal performance overhead over two steps, that is, C -> eBPF -> native code.
-
-Currently, the new format is being used for running user BPF programs, which
-includes seccomp BPF, classic socket filters, cls_bpf traffic classifier,
-team driver's classifier for its load-balancing mode, netfilter's xt_bpf
-extension, PTP dissector/classifier, and much more. They are all internally
-converted by the kernel into the new instruction set representation and run
-in the eBPF interpreter. For in-kernel handlers, this all works transparently
-by using bpf_prog_create() for setting up the filter, resp.
-bpf_prog_destroy() for destroying it. The macro
-BPF_PROG_RUN(filter, ctx) transparently invokes eBPF interpreter or JITed
-code to run the filter. 'filter' is a pointer to struct bpf_prog that we
-got from bpf_prog_create(), and 'ctx' the given context (e.g.
-skb pointer). All constraints and restrictions from bpf_check_classic() apply
-before a conversion to the new layout is being done behind the scenes!
-
-Currently, the classic BPF format is being used for JITing on most
-32-bit architectures, whereas x86-64, aarch64, s390x, powerpc64,
-sparc64, arm32, riscv64, riscv32 perform JIT compilation from eBPF
-instruction set.
-
-Some core changes of the new internal format:
-
-- Number of registers increase from 2 to 10:
-
-  The old format had two registers A and X, and a hidden frame pointer. The
-  new layout extends this to be 10 internal registers and a read-only frame
-  pointer. Since 64-bit CPUs are passing arguments to functions via registers
-  the number of args from eBPF program to in-kernel function is restricted
-  to 5 and one register is used to accept return value from an in-kernel
-  function. Natively, x86_64 passes first 6 arguments in registers, aarch64/
-  sparcv9/mips64 have 7 - 8 registers for arguments; x86_64 has 6 callee saved
-  registers, and aarch64/sparcv9/mips64 have 11 or more callee saved registers.
-
-  Therefore, eBPF calling convention is defined as:
-
-    * R0       - return value from in-kernel function, and exit value for eBPF program
-    * R1 - R5  - arguments from eBPF program to in-kernel function
-    * R6 - R9  - callee saved registers that in-kernel function will preserve
-    * R10      - read-only frame pointer to access stack
-
-  Thus, all eBPF registers map one to one to HW registers on x86_64, aarch64,
-  etc, and eBPF calling convention maps directly to ABIs used by the kernel on
-  64-bit architectures.
-
-  On 32-bit architectures JIT may map programs that use only 32-bit arithmetic
-  and may let more complex programs to be interpreted.
-
-  R0 - R5 are scratch registers and eBPF program needs spill/fill them if
-  necessary across calls. Note that there is only one eBPF program (== one
-  eBPF main routine) and it cannot call other eBPF functions, it can only
-  call predefined in-kernel functions, though.
-
-- Register width increases from 32-bit to 64-bit:
-
-  Still, the semantics of the original 32-bit ALU operations are preserved
-  via 32-bit subregisters. All eBPF registers are 64-bit with 32-bit lower
-  subregisters that zero-extend into 64-bit if they are being written to.
-  That behavior maps directly to x86_64 and arm64 subregister definition, but
-  makes other JITs more difficult.
-
-  32-bit architectures run 64-bit internal BPF programs via interpreter.
-  Their JITs may convert BPF programs that only use 32-bit subregisters into
-  native instruction set and let the rest being interpreted.
-
-  Operation is 64-bit, because on 64-bit architectures, pointers are also
-  64-bit wide, and we want to pass 64-bit values in/out of kernel functions,
-  so 32-bit eBPF registers would otherwise require to define register-pair
-  ABI, thus, there won't be able to use a direct eBPF register to HW register
-  mapping and JIT would need to do combine/split/move operations for every
-  register in and out of the function, which is complex, bug prone and slow.
-  Another reason is the use of atomic 64-bit counters.
-
-- Conditional jt/jf targets replaced with jt/fall-through:
-
-  While the original design has constructs such as "if (cond) jump_true;
-  else jump_false;", they are being replaced into alternative constructs like
-  "if (cond) jump_true; /* else fall-through */".
-
-- Introduces bpf_call insn and register passing convention for zero overhead
-  calls from/to other kernel functions:
-
-  Before an in-kernel function call, the internal BPF program needs to
-  place function arguments into R1 to R5 registers to satisfy calling
-  convention, then the interpreter will take them from registers and pass
-  to in-kernel function. If R1 - R5 registers are mapped to CPU registers
-  that are used for argument passing on given architecture, the JIT compiler
-  doesn't need to emit extra moves. Function arguments will be in the correct
-  registers and BPF_CALL instruction will be JITed as single 'call' HW
-  instruction. This calling convention was picked to cover common call
-  situations without performance penalty.
-
-  After an in-kernel function call, R1 - R5 are reset to unreadable and R0 has
-  a return value of the function. Since R6 - R9 are callee saved, their state
-  is preserved across the call.
-
-  For example, consider three C functions:
-
-  u64 f1() { return (*_f2)(1); }
-  u64 f2(u64 a) { return f3(a + 1, a); }
-  u64 f3(u64 a, u64 b) { return a - b; }
-
-  GCC can compile f1, f3 into x86_64:
-
-  f1:
-    movl $1, %edi
-    movq _f2(%rip), %rax
-    jmp  *%rax
-  f3:
-    movq %rdi, %rax
-    subq %rsi, %rax
-    ret
-
-  Function f2 in eBPF may look like:
-
-  f2:
-    bpf_mov R2, R1
-    bpf_add R1, 1
-    bpf_call f3
-    bpf_exit
-
-  If f2 is JITed and the pointer stored to '_f2'. The calls f1 -> f2 -> f3 and
-  returns will be seamless. Without JIT, __bpf_prog_run() interpreter needs to
-  be used to call into f2.
-
-  For practical reasons all eBPF programs have only one argument 'ctx' which is
-  already placed into R1 (e.g. on __bpf_prog_run() startup) and the programs
-  can call kernel functions with up to 5 arguments. Calls with 6 or more arguments
-  are currently not supported, but these restrictions can be lifted if necessary
-  in the future.
-
-  On 64-bit architectures all register map to HW registers one to one. For
-  example, x86_64 JIT compiler can map them as ...
-
-    R0 - rax
-    R1 - rdi
-    R2 - rsi
-    R3 - rdx
-    R4 - rcx
-    R5 - r8
-    R6 - rbx
-    R7 - r13
-    R8 - r14
-    R9 - r15
-    R10 - rbp
-
-  ... since x86_64 ABI mandates rdi, rsi, rdx, rcx, r8, r9 for argument passing
-  and rbx, r12 - r15 are callee saved.
-
-  Then the following internal BPF pseudo-program:
-
-    bpf_mov R6, R1 /* save ctx */
-    bpf_mov R2, 2
-    bpf_mov R3, 3
-    bpf_mov R4, 4
-    bpf_mov R5, 5
-    bpf_call foo
-    bpf_mov R7, R0 /* save foo() return value */
-    bpf_mov R1, R6 /* restore ctx for next call */
-    bpf_mov R2, 6
-    bpf_mov R3, 7
-    bpf_mov R4, 8
-    bpf_mov R5, 9
-    bpf_call bar
-    bpf_add R0, R7
-    bpf_exit
-
-  After JIT to x86_64 may look like:
-
-    push %rbp
-    mov %rsp,%rbp
-    sub $0x228,%rsp
-    mov %rbx,-0x228(%rbp)
-    mov %r13,-0x220(%rbp)
-    mov %rdi,%rbx
-    mov $0x2,%esi
-    mov $0x3,%edx
-    mov $0x4,%ecx
-    mov $0x5,%r8d
-    callq foo
-    mov %rax,%r13
-    mov %rbx,%rdi
-    mov $0x6,%esi
-    mov $0x7,%edx
-    mov $0x8,%ecx
-    mov $0x9,%r8d
-    callq bar
-    add %r13,%rax
-    mov -0x228(%rbp),%rbx
-    mov -0x220(%rbp),%r13
-    leaveq
-    retq
-
-  Which is in this example equivalent in C to:
-
-    u64 bpf_filter(u64 ctx)
-    {
-        return foo(ctx, 2, 3, 4, 5) + bar(ctx, 6, 7, 8, 9);
-    }
-
-  In-kernel functions foo() and bar() with prototype: u64 (*)(u64 arg1, u64
-  arg2, u64 arg3, u64 arg4, u64 arg5); will receive arguments in proper
-  registers and place their return value into '%rax' which is R0 in eBPF.
-  Prologue and epilogue are emitted by JIT and are implicit in the
-  interpreter. R0-R5 are scratch registers, so eBPF program needs to preserve
-  them across the calls as defined by calling convention.
-
-  For example the following program is invalid:
-
-    bpf_mov R1, 1
-    bpf_call foo
-    bpf_mov R0, R1
-    bpf_exit
-
-  After the call the registers R1-R5 contain junk values and cannot be read.
-  An in-kernel eBPF verifier is used to validate internal BPF programs.
-
-Also in the new design, eBPF is limited to 4096 insns, which means that any
-program will terminate quickly and will only call a fixed number of kernel
-functions. Original BPF and the new format are two operand instructions,
-which helps to do one-to-one mapping between eBPF insn and x86 insn during JIT.
-
-The input context pointer for invoking the interpreter function is generic,
-its content is defined by a specific use case. For seccomp register R1 points
-to seccomp_data, for converted BPF filters R1 points to a skb.
-
-A program, that is translated internally consists of the following elements:
-
-  op:16, jt:8, jf:8, k:32    ==>    op:8, dst_reg:4, src_reg:4, off:16, imm:32
-
-So far 87 internal BPF instructions were implemented. 8-bit 'op' opcode field
-has room for new instructions. Some of them may use 16/24/32 byte encoding. New
-instructions must be multiple of 8 bytes to preserve backward compatibility.
-
-Internal BPF is a general purpose RISC instruction set. Not every register and
-every instruction are used during translation from original BPF to new format.
-For example, socket filters are not using 'exclusive add' instruction, but
-tracing filters may do to maintain counters of events, for example. Register R9
-is not used by socket filters either, but more complex filters may be running
-out of registers and would have to resort to spill/fill to stack.
-
-Internal BPF can be used as a generic assembler for last step performance
-optimizations, socket filters and seccomp are using it as assembler. Tracing
-filters may use it as assembler to generate code from kernel. In kernel usage
-may not be bounded by security considerations, since generated internal BPF code
-may be optimizing internal code path and not being exposed to the user space.
-Safety of internal BPF can come from a verifier (TBD). In such use cases as
-described, it may be used as safe instruction set.
-
-Just like the original BPF, the new format runs within a controlled environment,
-is deterministic and the kernel can easily prove that. The safety of the program
-can be determined in two steps: first step does depth-first-search to disallow
-loops and other CFG validation; second step starts from the first insn and
-descends all possible paths. It simulates execution of every insn and observes
-the state change of registers and stack.
-
-eBPF opcode encoding
---------------------
-
-eBPF is reusing most of the opcode encoding from classic to simplify conversion
-of classic BPF to eBPF. For arithmetic and jump instructions the 8-bit 'code'
-field is divided into three parts:
-
-  +----------------+--------+--------------------+
-  |   4 bits       |  1 bit |   3 bits           |
-  | operation code | source | instruction class  |
-  +----------------+--------+--------------------+
-  (MSB)                                      (LSB)
-
-Three LSB bits store instruction class which is one of:
-
-  Classic BPF classes:    eBPF classes:
-
-  BPF_LD    0x00          BPF_LD    0x00
-  BPF_LDX   0x01          BPF_LDX   0x01
-  BPF_ST    0x02          BPF_ST    0x02
-  BPF_STX   0x03          BPF_STX   0x03
-  BPF_ALU   0x04          BPF_ALU   0x04
-  BPF_JMP   0x05          BPF_JMP   0x05
-  BPF_RET   0x06          BPF_JMP32 0x06
-  BPF_MISC  0x07          BPF_ALU64 0x07
-
-When BPF_CLASS(code) == BPF_ALU or BPF_JMP, 4th bit encodes source operand ...
-
-  BPF_K     0x00
-  BPF_X     0x08
-
- * in classic BPF, this means:
-
-  BPF_SRC(code) == BPF_X - use register X as source operand
-  BPF_SRC(code) == BPF_K - use 32-bit immediate as source operand
-
- * in eBPF, this means:
-
-  BPF_SRC(code) == BPF_X - use 'src_reg' register as source operand
-  BPF_SRC(code) == BPF_K - use 32-bit immediate as source operand
-
-... and four MSB bits store operation code.
-
-If BPF_CLASS(code) == BPF_ALU or BPF_ALU64 [ in eBPF ], BPF_OP(code) is one of:
-
-  BPF_ADD   0x00
-  BPF_SUB   0x10
-  BPF_MUL   0x20
-  BPF_DIV   0x30
-  BPF_OR    0x40
-  BPF_AND   0x50
-  BPF_LSH   0x60
-  BPF_RSH   0x70
-  BPF_NEG   0x80
-  BPF_MOD   0x90
-  BPF_XOR   0xa0
-  BPF_MOV   0xb0  /* eBPF only: mov reg to reg */
-  BPF_ARSH  0xc0  /* eBPF only: sign extending shift right */
-  BPF_END   0xd0  /* eBPF only: endianness conversion */
-
-If BPF_CLASS(code) == BPF_JMP or BPF_JMP32 [ in eBPF ], BPF_OP(code) is one of:
-
-  BPF_JA    0x00  /* BPF_JMP only */
-  BPF_JEQ   0x10
-  BPF_JGT   0x20
-  BPF_JGE   0x30
-  BPF_JSET  0x40
-  BPF_JNE   0x50  /* eBPF only: jump != */
-  BPF_JSGT  0x60  /* eBPF only: signed '>' */
-  BPF_JSGE  0x70  /* eBPF only: signed '>=' */
-  BPF_CALL  0x80  /* eBPF BPF_JMP only: function call */
-  BPF_EXIT  0x90  /* eBPF BPF_JMP only: function return */
-  BPF_JLT   0xa0  /* eBPF only: unsigned '<' */
-  BPF_JLE   0xb0  /* eBPF only: unsigned '<=' */
-  BPF_JSLT  0xc0  /* eBPF only: signed '<' */
-  BPF_JSLE  0xd0  /* eBPF only: signed '<=' */
-
-So BPF_ADD | BPF_X | BPF_ALU means 32-bit addition in both classic BPF
-and eBPF. There are only two registers in classic BPF, so it means A += X.
-In eBPF it means dst_reg = (u32) dst_reg + (u32) src_reg; similarly,
-BPF_XOR | BPF_K | BPF_ALU means A ^= imm32 in classic BPF and analogous
-src_reg = (u32) src_reg ^ (u32) imm32 in eBPF.
-
-Classic BPF is using BPF_MISC class to represent A = X and X = A moves.
-eBPF is using BPF_MOV | BPF_X | BPF_ALU code instead. Since there are no
-BPF_MISC operations in eBPF, the class 7 is used as BPF_ALU64 to mean
-exactly the same operations as BPF_ALU, but with 64-bit wide operands
-instead. So BPF_ADD | BPF_X | BPF_ALU64 means 64-bit addition, i.e.:
-dst_reg = dst_reg + src_reg
-
-Classic BPF wastes the whole BPF_RET class to represent a single 'ret'
-operation. Classic BPF_RET | BPF_K means copy imm32 into return register
-and perform function exit. eBPF is modeled to match CPU, so BPF_JMP | BPF_EXIT
-in eBPF means function exit only. The eBPF program needs to store return
-value into register R0 before doing a BPF_EXIT. Class 6 in eBPF is used as
-BPF_JMP32 to mean exactly the same operations as BPF_JMP, but with 32-bit wide
-operands for the comparisons instead.
-
-For load and store instructions the 8-bit 'code' field is divided as:
-
-  +--------+--------+-------------------+
-  | 3 bits | 2 bits |   3 bits          |
-  |  mode  |  size  | instruction class |
-  +--------+--------+-------------------+
-  (MSB)                             (LSB)
-
-Size modifier is one of ...
-
-  BPF_W   0x00    /* word */
-  BPF_H   0x08    /* half word */
-  BPF_B   0x10    /* byte */
-  BPF_DW  0x18    /* eBPF only, double word */
-
-... which encodes size of load/store operation:
-
- B  - 1 byte
- H  - 2 byte
- W  - 4 byte
- DW - 8 byte (eBPF only)
-
-Mode modifier is one of:
-
-  BPF_IMM  0x00  /* used for 32-bit mov in classic BPF and 64-bit in eBPF */
-  BPF_ABS  0x20
-  BPF_IND  0x40
-  BPF_MEM  0x60
-  BPF_LEN  0x80  /* classic BPF only, reserved in eBPF */
-  BPF_MSH  0xa0  /* classic BPF only, reserved in eBPF */
-  BPF_XADD 0xc0  /* eBPF only, exclusive add */
-
-eBPF has two non-generic instructions: (BPF_ABS | <size> | BPF_LD) and
-(BPF_IND | <size> | BPF_LD) which are used to access packet data.
-
-They had to be carried over from classic to have strong performance of
-socket filters running in eBPF interpreter. These instructions can only
-be used when interpreter context is a pointer to 'struct sk_buff' and
-have seven implicit operands. Register R6 is an implicit input that must
-contain pointer to sk_buff. Register R0 is an implicit output which contains
-the data fetched from the packet. Registers R1-R5 are scratch registers
-and must not be used to store the data across BPF_ABS | BPF_LD or
-BPF_IND | BPF_LD instructions.
-
-These instructions have implicit program exit condition as well. When
-eBPF program is trying to access the data beyond the packet boundary,
-the interpreter will abort the execution of the program. JIT compilers
-therefore must preserve this property. src_reg and imm32 fields are
-explicit inputs to these instructions.
-
-For example:
-
-  BPF_IND | BPF_W | BPF_LD means:
-
-    R0 = ntohl(*(u32 *) (((struct sk_buff *) R6)->data + src_reg + imm32))
-    and R1 - R5 were scratched.
-
-Unlike classic BPF instruction set, eBPF has generic load/store operations:
-
-BPF_MEM | <size> | BPF_STX:  *(size *) (dst_reg + off) = src_reg
-BPF_MEM | <size> | BPF_ST:   *(size *) (dst_reg + off) = imm32
-BPF_MEM | <size> | BPF_LDX:  dst_reg = *(size *) (src_reg + off)
-BPF_XADD | BPF_W  | BPF_STX: lock xadd *(u32 *)(dst_reg + off16) += src_reg
-BPF_XADD | BPF_DW | BPF_STX: lock xadd *(u64 *)(dst_reg + off16) += src_reg
-
-Where size is one of: BPF_B or BPF_H or BPF_W or BPF_DW. Note that 1 and
-2 byte atomic increments are not supported.
-
-eBPF has one 16-byte instruction: BPF_LD | BPF_DW | BPF_IMM which consists
-of two consecutive 'struct bpf_insn' 8-byte blocks and interpreted as single
-instruction that loads 64-bit immediate value into a dst_reg.
-Classic BPF has similar instruction: BPF_LD | BPF_W | BPF_IMM which loads
-32-bit immediate value into a register.
-
-eBPF verifier
--------------
-The safety of the eBPF program is determined in two steps.
-
-First step does DAG check to disallow loops and other CFG validation.
-In particular it will detect programs that have unreachable instructions.
-(though classic BPF checker allows them)
-
-Second step starts from the first insn and descends all possible paths.
-It simulates execution of every insn and observes the state change of
-registers and stack.
-
-At the start of the program the register R1 contains a pointer to context
-and has type PTR_TO_CTX.
-If verifier sees an insn that does R2=R1, then R2 has now type
-PTR_TO_CTX as well and can be used on the right hand side of expression.
-If R1=PTR_TO_CTX and insn is R2=R1+R1, then R2=SCALAR_VALUE,
-since addition of two valid pointers makes invalid pointer.
-(In 'secure' mode verifier will reject any type of pointer arithmetic to make
-sure that kernel addresses don't leak to unprivileged users)
-
-If register was never written to, it's not readable:
-  bpf_mov R0 = R2
-  bpf_exit
-will be rejected, since R2 is unreadable at the start of the program.
-
-After kernel function call, R1-R5 are reset to unreadable and
-R0 has a return type of the function.
-
-Since R6-R9 are callee saved, their state is preserved across the call.
-  bpf_mov R6 = 1
-  bpf_call foo
-  bpf_mov R0 = R6
-  bpf_exit
-is a correct program. If there was R1 instead of R6, it would have
-been rejected.
-
-load/store instructions are allowed only with registers of valid types, which
-are PTR_TO_CTX, PTR_TO_MAP, PTR_TO_STACK. They are bounds and alignment checked.
-For example:
- bpf_mov R1 = 1
- bpf_mov R2 = 2
- bpf_xadd *(u32 *)(R1 + 3) += R2
- bpf_exit
-will be rejected, since R1 doesn't have a valid pointer type at the time of
-execution of instruction bpf_xadd.
-
-At the start R1 type is PTR_TO_CTX (a pointer to generic 'struct bpf_context')
-A callback is used to customize verifier to restrict eBPF program access to only
-certain fields within ctx structure with specified size and alignment.
-
-For example, the following insn:
-  bpf_ld R0 = *(u32 *)(R6 + 8)
-intends to load a word from address R6 + 8 and store it into R0
-If R6=PTR_TO_CTX, via is_valid_access() callback the verifier will know
-that offset 8 of size 4 bytes can be accessed for reading, otherwise
-the verifier will reject the program.
-If R6=PTR_TO_STACK, then access should be aligned and be within
-stack bounds, which are [-MAX_BPF_STACK, 0). In this example offset is 8,
-so it will fail verification, since it's out of bounds.
-
-The verifier will allow eBPF program to read data from stack only after
-it wrote into it.
-Classic BPF verifier does similar check with M[0-15] memory slots.
-For example:
-  bpf_ld R0 = *(u32 *)(R10 - 4)
-  bpf_exit
-is invalid program.
-Though R10 is correct read-only register and has type PTR_TO_STACK
-and R10 - 4 is within stack bounds, there were no stores into that location.
-
-Pointer register spill/fill is tracked as well, since four (R6-R9)
-callee saved registers may not be enough for some programs.
-
-Allowed function calls are customized with bpf_verifier_ops->get_func_proto()
-The eBPF verifier will check that registers match argument constraints.
-After the call register R0 will be set to return type of the function.
-
-Function calls is a main mechanism to extend functionality of eBPF programs.
-Socket filters may let programs to call one set of functions, whereas tracing
-filters may allow completely different set.
-
-If a function made accessible to eBPF program, it needs to be thought through
-from safety point of view. The verifier will guarantee that the function is
-called with valid arguments.
-
-seccomp vs socket filters have different security restrictions for classic BPF.
-Seccomp solves this by two stage verifier: classic BPF verifier is followed
-by seccomp verifier. In case of eBPF one configurable verifier is shared for
-all use cases.
-
-See details of eBPF verifier in kernel/bpf/verifier.c
-
-Register value tracking
------------------------
-In order to determine the safety of an eBPF program, the verifier must track
-the range of possible values in each register and also in each stack slot.
-This is done with 'struct bpf_reg_state', defined in include/linux/
-bpf_verifier.h, which unifies tracking of scalar and pointer values.  Each
-register state has a type, which is either NOT_INIT (the register has not been
-written to), SCALAR_VALUE (some value which is not usable as a pointer), or a
-pointer type.  The types of pointers describe their base, as follows:
-    PTR_TO_CTX          Pointer to bpf_context.
-    CONST_PTR_TO_MAP    Pointer to struct bpf_map.  "Const" because arithmetic
-                        on these pointers is forbidden.
-    PTR_TO_MAP_VALUE    Pointer to the value stored in a map element.
-    PTR_TO_MAP_VALUE_OR_NULL
-                        Either a pointer to a map value, or NULL; map accesses
-                        (see section 'eBPF maps', below) return this type,
-                        which becomes a PTR_TO_MAP_VALUE when checked != NULL.
-                        Arithmetic on these pointers is forbidden.
-    PTR_TO_STACK        Frame pointer.
-    PTR_TO_PACKET       skb->data.
-    PTR_TO_PACKET_END   skb->data + headlen; arithmetic forbidden.
-    PTR_TO_SOCKET       Pointer to struct bpf_sock_ops, implicitly refcounted.
-    PTR_TO_SOCKET_OR_NULL
-                        Either a pointer to a socket, or NULL; socket lookup
-                        returns this type, which becomes a PTR_TO_SOCKET when
-                        checked != NULL. PTR_TO_SOCKET is reference-counted,
-                        so programs must release the reference through the
-                        socket release function before the end of the program.
-                        Arithmetic on these pointers is forbidden.
-However, a pointer may be offset from this base (as a result of pointer
-arithmetic), and this is tracked in two parts: the 'fixed offset' and 'variable
-offset'.  The former is used when an exactly-known value (e.g. an immediate
-operand) is added to a pointer, while the latter is used for values which are
-not exactly known.  The variable offset is also used in SCALAR_VALUEs, to track
-the range of possible values in the register.
-The verifier's knowledge about the variable offset consists of:
-* minimum and maximum values as unsigned
-* minimum and maximum values as signed
-* knowledge of the values of individual bits, in the form of a 'tnum': a u64
-'mask' and a u64 'value'.  1s in the mask represent bits whose value is unknown;
-1s in the value represent bits known to be 1.  Bits known to be 0 have 0 in both
-mask and value; no bit should ever be 1 in both.  For example, if a byte is read
-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'
-branch it will have a umax_value of 8.  A signed compare (with BPF_JSGT or
-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 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.
-As well as range-checking, the tracked information is also used for enforcing
-alignment of pointer accesses.  For instance, on most systems the packet pointer
-is 2 bytes after a 4-byte alignment.  If a program adds 14 bytes to that to jump
-over the Ethernet header, then reads IHL and addes (IHL * 4), the resulting
-pointer will have a variable offset known to be 4n+2 for some n, so adding the 2
-bytes (NET_IP_ALIGN) gives a 4-byte alignment and so word-sized accesses through
-that pointer are safe.
-The 'id' field is also used on PTR_TO_SOCKET and PTR_TO_SOCKET_OR_NULL, common
-to all copies of the pointer returned from a socket lookup. This has similar
-behaviour to the handling for PTR_TO_MAP_VALUE_OR_NULL->PTR_TO_MAP_VALUE, but
-it also handles reference tracking for the pointer. PTR_TO_SOCKET implicitly
-represents a reference to the corresponding 'struct sock'. To ensure that the
-reference is not leaked, it is imperative to NULL-check the reference and in
-the non-NULL case, and pass the valid reference to the socket release function.
-
-Direct packet access
---------------------
-In cls_bpf and act_bpf programs the verifier allows direct access to the packet
-data via skb->data and skb->data_end pointers.
-Ex:
-1:  r4 = *(u32 *)(r1 +80)  /* load skb->data_end */
-2:  r3 = *(u32 *)(r1 +76)  /* load skb->data */
-3:  r5 = r3
-4:  r5 += 14
-5:  if r5 > r4 goto pc+16
-R1=ctx R3=pkt(id=0,off=0,r=14) R4=pkt_end R5=pkt(id=0,off=14,r=14) R10=fp
-6:  r0 = *(u16 *)(r3 +12) /* access 12 and 13 bytes of the packet */
-
-this 2byte load from the packet is safe to do, since the program author
-did check 'if (skb->data + 14 > skb->data_end) goto err' at insn #5 which
-means that in the fall-through case the register R3 (which points to skb->data)
-has at least 14 directly accessible bytes. The verifier marks it
-as R3=pkt(id=0,off=0,r=14).
-id=0 means that no additional variables were added to the register.
-off=0 means that no additional constants were added.
-r=14 is the range of safe access which means that bytes [R3, R3 + 14) are ok.
-Note that R5 is marked as R5=pkt(id=0,off=14,r=14). It also points
-to the packet data, but constant 14 was added to the register, so
-it now points to 'skb->data + 14' and accessible range is [R5, R5 + 14 - 14)
-which is zero bytes.
-
-More complex packet access may look like:
- R0=inv1 R1=ctx R3=pkt(id=0,off=0,r=14) R4=pkt_end R5=pkt(id=0,off=14,r=14) R10=fp
- 6:  r0 = *(u8 *)(r3 +7) /* load 7th byte from the packet */
- 7:  r4 = *(u8 *)(r3 +12)
- 8:  r4 *= 14
- 9:  r3 = *(u32 *)(r1 +76) /* load skb->data */
-10:  r3 += r4
-11:  r2 = r1
-12:  r2 <<= 48
-13:  r2 >>= 48
-14:  r3 += r2
-15:  r2 = r3
-16:  r2 += 8
-17:  r1 = *(u32 *)(r1 +80) /* load skb->data_end */
-18:  if r2 > r1 goto pc+2
- R0=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R1=pkt_end R2=pkt(id=2,off=8,r=8) R3=pkt(id=2,off=0,r=8) R4=inv(id=0,umax_value=3570,var_off=(0x0; 0xfffe)) R5=pkt(id=0,off=14,r=14) R10=fp
-19:  r1 = *(u8 *)(r3 +4)
-The state of the register R3 is R3=pkt(id=2,off=0,r=8)
-id=2 means that two 'r3 += rX' instructions were seen, so r3 points to some
-offset within a packet and since the program author did
-'if (r3 + 8 > r1) goto err' at insn #18, the safe range is [R3, R3 + 8).
-The verifier only allows 'add'/'sub' operations on packet registers. Any other
-operation will set the register state to 'SCALAR_VALUE' and it won't be
-available for direct packet access.
-Operation 'r3 += rX' may overflow and become less than original skb->data,
-therefore the verifier has to prevent that.  So when it sees 'r3 += rX'
-instruction and rX is more than 16-bit value, any subsequent bounds-check of r3
-against skb->data_end will not give us 'range' information, so attempts to read
-through the pointer will give "invalid access to packet" error.
-Ex. after insn 'r4 = *(u8 *)(r3 +12)' (insn #7 above) the state of r4 is
-R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) which means that upper 56 bits
-of the register are guaranteed to be zero, and nothing is known about the lower
-8 bits. After insn 'r4 *= 14' the state becomes
-R4=inv(id=0,umax_value=3570,var_off=(0x0; 0xfffe)), since multiplying an 8-bit
-value by constant 14 will keep upper 52 bits as zero, also the least significant
-bit will be zero as 14 is even.  Similarly 'r2 >>= 48' will make
-R2=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff)), since the shift is not sign
-extending.  This logic is implemented in adjust_reg_min_max_vals() function,
-which calls adjust_ptr_min_max_vals() for adding pointer to scalar (or vice
-versa) and adjust_scalar_min_max_vals() for operations on two scalars.
-
-The end result is that bpf program author can access packet directly
-using normal C code as:
-  void *data = (void *)(long)skb->data;
-  void *data_end = (void *)(long)skb->data_end;
-  struct eth_hdr *eth = data;
-  struct iphdr *iph = data + sizeof(*eth);
-  struct udphdr *udp = data + sizeof(*eth) + sizeof(*iph);
-
-  if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*udp) > data_end)
-          return 0;
-  if (eth->h_proto != htons(ETH_P_IP))
-          return 0;
-  if (iph->protocol != IPPROTO_UDP || iph->ihl != 5)
-          return 0;
-  if (udp->dest == 53 || udp->source == 9)
-          ...;
-which makes such programs easier to write comparing to LD_ABS insn
-and significantly faster.
-
-eBPF maps
----------
-'maps' is a generic storage of different types for sharing data between kernel
-and userspace.
-
-The maps are accessed from user space via BPF syscall, which has commands:
-- create a map with given type and attributes
-  map_fd = bpf(BPF_MAP_CREATE, union bpf_attr *attr, u32 size)
-  using attr->map_type, attr->key_size, attr->value_size, attr->max_entries
-  returns process-local file descriptor or negative error
-
-- lookup key in a given map
-  err = bpf(BPF_MAP_LOOKUP_ELEM, union bpf_attr *attr, u32 size)
-  using attr->map_fd, attr->key, attr->value
-  returns zero and stores found elem into value or negative error
-
-- create or update key/value pair in a given map
-  err = bpf(BPF_MAP_UPDATE_ELEM, union bpf_attr *attr, u32 size)
-  using attr->map_fd, attr->key, attr->value
-  returns zero or negative error
-
-- find and delete element by key in a given map
-  err = bpf(BPF_MAP_DELETE_ELEM, union bpf_attr *attr, u32 size)
-  using attr->map_fd, attr->key
-
-- to delete map: close(fd)
-  Exiting process will delete maps automatically
-
-userspace programs use this syscall to create/access maps that eBPF programs
-are concurrently updating.
-
-maps can have different types: hash, array, bloom filter, radix-tree, etc.
-
-The map is defined by:
-  . type
-  . max number of elements
-  . key size in bytes
-  . value size in bytes
-
-Pruning
--------
-The verifier does not actually walk all possible paths through the program.  For
-each new branch to analyse, the verifier looks at all the states it's previously
-been in when at this instruction.  If any of them contain the current state as a
-subset, the branch is 'pruned' - that is, the fact that the previous state was
-accepted implies the current state would be as well.  For instance, if in the
-previous state, r1 held a packet-pointer, and in the current state, r1 holds a
-packet-pointer with a range as long or longer and at least as strict an
-alignment, then r1 is safe.  Similarly, if r2 was NOT_INIT before then it can't
-have been used by any path from that point, so any value in r2 (including
-another NOT_INIT) is safe.  The implementation is in the function regsafe().
-Pruning considers not only the registers but also the stack (and any spilled
-registers it may hold).  They must all be safe for the branch to be pruned.
-This is implemented in states_equal().
-
-Understanding eBPF verifier messages
-------------------------------------
-
-The following are few examples of invalid eBPF programs and verifier error
-messages as seen in the log:
-
-Program with unreachable instructions:
-static struct bpf_insn prog[] = {
-  BPF_EXIT_INSN(),
-  BPF_EXIT_INSN(),
-};
-Error:
-  unreachable insn 1
-
-Program that reads uninitialized register:
-  BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
-  BPF_EXIT_INSN(),
-Error:
-  0: (bf) r0 = r2
-  R2 !read_ok
-
-Program that doesn't initialize R0 before exiting:
-  BPF_MOV64_REG(BPF_REG_2, BPF_REG_1),
-  BPF_EXIT_INSN(),
-Error:
-  0: (bf) r2 = r1
-  1: (95) exit
-  R0 !read_ok
-
-Program that accesses stack out of bounds:
-  BPF_ST_MEM(BPF_DW, BPF_REG_10, 8, 0),
-  BPF_EXIT_INSN(),
-Error:
-  0: (7a) *(u64 *)(r10 +8) = 0
-  invalid stack off=8 size=8
-
-Program that doesn't initialize stack before passing its address into function:
-  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
-  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
-  BPF_LD_MAP_FD(BPF_REG_1, 0),
-  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
-  BPF_EXIT_INSN(),
-Error:
-  0: (bf) r2 = r10
-  1: (07) r2 += -8
-  2: (b7) r1 = 0x0
-  3: (85) call 1
-  invalid indirect read from stack off -8+0 size 8
-
-Program that uses invalid map_fd=0 while calling to map_lookup_elem() function:
-  BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
-  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
-  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
-  BPF_LD_MAP_FD(BPF_REG_1, 0),
-  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
-  BPF_EXIT_INSN(),
-Error:
-  0: (7a) *(u64 *)(r10 -8) = 0
-  1: (bf) r2 = r10
-  2: (07) r2 += -8
-  3: (b7) r1 = 0x0
-  4: (85) call 1
-  fd 0 is not pointing to valid bpf_map
-
-Program that doesn't check return value of map_lookup_elem() before accessing
-map element:
-  BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
-  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
-  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
-  BPF_LD_MAP_FD(BPF_REG_1, 0),
-  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
-  BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
-  BPF_EXIT_INSN(),
-Error:
-  0: (7a) *(u64 *)(r10 -8) = 0
-  1: (bf) r2 = r10
-  2: (07) r2 += -8
-  3: (b7) r1 = 0x0
-  4: (85) call 1
-  5: (7a) *(u64 *)(r0 +0) = 0
-  R0 invalid mem access 'map_value_or_null'
-
-Program that correctly checks map_lookup_elem() returned value for NULL, but
-accesses the memory with incorrect alignment:
-  BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
-  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
-  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
-  BPF_LD_MAP_FD(BPF_REG_1, 0),
-  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
-  BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
-  BPF_ST_MEM(BPF_DW, BPF_REG_0, 4, 0),
-  BPF_EXIT_INSN(),
-Error:
-  0: (7a) *(u64 *)(r10 -8) = 0
-  1: (bf) r2 = r10
-  2: (07) r2 += -8
-  3: (b7) r1 = 1
-  4: (85) call 1
-  5: (15) if r0 == 0x0 goto pc+1
-   R0=map_ptr R10=fp
-  6: (7a) *(u64 *)(r0 +4) = 0
-  misaligned access off 4 size 8
-
-Program that correctly checks map_lookup_elem() returned value for NULL and
-accesses memory with correct alignment in one side of 'if' branch, but fails
-to do so in the other side of 'if' branch:
-  BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
-  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
-  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
-  BPF_LD_MAP_FD(BPF_REG_1, 0),
-  BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
-  BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
-  BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
-  BPF_EXIT_INSN(),
-  BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 1),
-  BPF_EXIT_INSN(),
-Error:
-  0: (7a) *(u64 *)(r10 -8) = 0
-  1: (bf) r2 = r10
-  2: (07) r2 += -8
-  3: (b7) r1 = 1
-  4: (85) call 1
-  5: (15) if r0 == 0x0 goto pc+2
-   R0=map_ptr R10=fp
-  6: (7a) *(u64 *)(r0 +0) = 0
-  7: (95) exit
-
-  from 5 to 8: R0=imm0 R10=fp
-  8: (7a) *(u64 *)(r0 +0) = 1
-  R0 invalid mem access 'imm'
-
-Program that performs a socket lookup then sets the pointer to NULL without
-checking it:
-value:
-  BPF_MOV64_IMM(BPF_REG_2, 0),
-  BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, -8),
-  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
-  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
-  BPF_MOV64_IMM(BPF_REG_3, 4),
-  BPF_MOV64_IMM(BPF_REG_4, 0),
-  BPF_MOV64_IMM(BPF_REG_5, 0),
-  BPF_EMIT_CALL(BPF_FUNC_sk_lookup_tcp),
-  BPF_MOV64_IMM(BPF_REG_0, 0),
-  BPF_EXIT_INSN(),
-Error:
-  0: (b7) r2 = 0
-  1: (63) *(u32 *)(r10 -8) = r2
-  2: (bf) r2 = r10
-  3: (07) r2 += -8
-  4: (b7) r3 = 4
-  5: (b7) r4 = 0
-  6: (b7) r5 = 0
-  7: (85) call bpf_sk_lookup_tcp#65
-  8: (b7) r0 = 0
-  9: (95) exit
-  Unreleased reference id=1, alloc_insn=7
-
-Program that performs a socket lookup but does not NULL-check the returned
-value:
-  BPF_MOV64_IMM(BPF_REG_2, 0),
-  BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, -8),
-  BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
-  BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
-  BPF_MOV64_IMM(BPF_REG_3, 4),
-  BPF_MOV64_IMM(BPF_REG_4, 0),
-  BPF_MOV64_IMM(BPF_REG_5, 0),
-  BPF_EMIT_CALL(BPF_FUNC_sk_lookup_tcp),
-  BPF_EXIT_INSN(),
-Error:
-  0: (b7) r2 = 0
-  1: (63) *(u32 *)(r10 -8) = r2
-  2: (bf) r2 = r10
-  3: (07) r2 += -8
-  4: (b7) r3 = 4
-  5: (b7) r4 = 0
-  6: (b7) r5 = 0
-  7: (85) call bpf_sk_lookup_tcp#65
-  8: (95) exit
-  Unreleased reference id=1, alloc_insn=7
-
-Testing
--------
-
-Next to the BPF toolchain, the kernel also ships a test module that contains
-various test cases for classic and internal BPF that can be executed against
-the BPF interpreter and JIT compiler. It can be found in lib/test_bpf.c and
-enabled via Kconfig:
-
-  CONFIG_TEST_BPF=m
-
-After the module has been built and installed, the test suite can be executed
-via insmod or modprobe against 'test_bpf' module. Results of the test cases
-including timings in nsec can be found in the kernel log (dmesg).
-
-Misc
-----
-
-Also trinity, the Linux syscall fuzzer, has built-in support for BPF and
-SECCOMP-BPF kernel fuzzing.
-
-Written by
-----------
-
-The document was written in the hope that it is found useful and in order
-to give potential BPF hackers or security auditors a better overview of
-the underlying architecture.
-
-Jay Schulist <jschlst@samba.org>
-Daniel Borkmann <daniel@iogearbox.net>
-Alexei Starovoitov <ast@kernel.org>
diff --git a/Documentation/networking/fore200e.rst b/Documentation/networking/fore200e.rst
new file mode 100644 (file)
index 0000000..55df9ec
--- /dev/null
@@ -0,0 +1,66 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============================================
+FORE Systems PCA-200E/SBA-200E ATM NIC driver
+=============================================
+
+This driver adds support for the FORE Systems 200E-series ATM adapters
+to the Linux operating system. It is based on the earlier PCA-200E driver
+written by Uwe Dannowski.
+
+The driver simultaneously supports PCA-200E and SBA-200E adapters on
+i386, alpha (untested), powerpc, sparc and sparc64 archs.
+
+The intent is to enable the use of different models of FORE adapters at the
+same time, by hosts that have several bus interfaces (such as PCI+SBUS,
+or PCI+EISA).
+
+Only PCI and SBUS devices are currently supported by the driver, but support
+for other bus interfaces such as EISA should not be too hard to add.
+
+
+Firmware Copyright Notice
+-------------------------
+
+Please read the fore200e_firmware_copyright file present
+in the linux/drivers/atm directory for details and restrictions.
+
+
+Firmware Updates
+----------------
+
+The FORE Systems 200E-series driver is shipped with firmware data being
+uploaded to the ATM adapters at system boot time or at module loading time.
+The supplied firmware images should work with all adapters.
+
+However, if you encounter problems (the firmware doesn't start or the driver
+is unable to read the PROM data), you may consider trying another firmware
+version. Alternative binary firmware images can be found somewhere on the
+ForeThought CD-ROM supplied with your adapter by FORE Systems.
+
+You can also get the latest firmware images from FORE Systems at
+https://en.wikipedia.org/wiki/FORE_Systems. Register TACTics Online and go to
+the 'software updates' pages. The firmware binaries are part of
+the various ForeThought software distributions.
+
+Notice that different versions of the PCA-200E firmware exist, depending
+on the endianness of the host architecture. The driver is shipped with
+both little and big endian PCA firmware images.
+
+Name and location of the new firmware images can be set at kernel
+configuration time:
+
+1. Copy the new firmware binary files (with .bin, .bin1 or .bin2 suffix)
+   to some directory, such as linux/drivers/atm.
+
+2. Reconfigure your kernel to set the new firmware name and location.
+   Expected pathnames are absolute or relative to the drivers/atm directory.
+
+3. Rebuild and re-install your kernel or your module.
+
+
+Feedback
+--------
+
+Feedback is welcome. Please send success stories/bug reports/
+patches/improvement/comments/flames to <lizzi@cnam.fr>.
diff --git a/Documentation/networking/fore200e.txt b/Documentation/networking/fore200e.txt
deleted file mode 100644 (file)
index 1f98f62..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-
-FORE Systems PCA-200E/SBA-200E ATM NIC driver
----------------------------------------------
-
-This driver adds support for the FORE Systems 200E-series ATM adapters
-to the Linux operating system. It is based on the earlier PCA-200E driver
-written by Uwe Dannowski.
-
-The driver simultaneously supports PCA-200E and SBA-200E adapters on
-i386, alpha (untested), powerpc, sparc and sparc64 archs.
-
-The intent is to enable the use of different models of FORE adapters at the
-same time, by hosts that have several bus interfaces (such as PCI+SBUS,
-or PCI+EISA).
-
-Only PCI and SBUS devices are currently supported by the driver, but support
-for other bus interfaces such as EISA should not be too hard to add.
-
-
-Firmware Copyright Notice
--------------------------
-
-Please read the fore200e_firmware_copyright file present
-in the linux/drivers/atm directory for details and restrictions.
-
-
-Firmware Updates
-----------------
-
-The FORE Systems 200E-series driver is shipped with firmware data being 
-uploaded to the ATM adapters at system boot time or at module loading time. 
-The supplied firmware images should work with all adapters.
-
-However, if you encounter problems (the firmware doesn't start or the driver
-is unable to read the PROM data), you may consider trying another firmware
-version. Alternative binary firmware images can be found somewhere on the
-ForeThought CD-ROM supplied with your adapter by FORE Systems.
-
-You can also get the latest firmware images from FORE Systems at
-https://en.wikipedia.org/wiki/FORE_Systems. Register TACTics Online and go to
-the 'software updates' pages. The firmware binaries are part of
-the various ForeThought software distributions.
-
-Notice that different versions of the PCA-200E firmware exist, depending
-on the endianness of the host architecture. The driver is shipped with
-both little and big endian PCA firmware images.
-
-Name and location of the new firmware images can be set at kernel
-configuration time:
-
-1. Copy the new firmware binary files (with .bin, .bin1 or .bin2 suffix)
-   to some directory, such as linux/drivers/atm.
-
-2. Reconfigure your kernel to set the new firmware name and location.
-   Expected pathnames are absolute or relative to the drivers/atm directory.
-
-3. Rebuild and re-install your kernel or your module.
-
-
-Feedback
---------
-
-Feedback is welcome. Please send success stories/bug reports/
-patches/improvement/comments/flames to <lizzi@cnam.fr>.
diff --git a/Documentation/networking/framerelay.rst b/Documentation/networking/framerelay.rst
new file mode 100644 (file)
index 0000000..6d90439
--- /dev/null
@@ -0,0 +1,44 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+================
+Frame Relay (FR)
+================
+
+Frame Relay (FR) support for linux is built into a two tiered system of device
+drivers.  The upper layer implements RFC1490 FR specification, and uses the
+Data Link Connection Identifier (DLCI) as its hardware address.  Usually these
+are assigned by your network supplier, they give you the number/numbers of
+the Virtual Connections (VC) assigned to you.
+
+Each DLCI is a point-to-point link between your machine and a remote one.
+As such, a separate device is needed to accommodate the routing.  Within the
+net-tools archives is 'dlcicfg'.  This program will communicate with the
+base "DLCI" device, and create new net devices named 'dlci00', 'dlci01'...
+The configuration script will ask you how many DLCIs you need, as well as
+how many DLCIs you want to assign to each Frame Relay Access Device (FRAD).
+
+The DLCI uses a number of function calls to communicate with the FRAD, all
+of which are stored in the FRAD's private data area.  assoc/deassoc,
+activate/deactivate and dlci_config.  The DLCI supplies a receive function
+to the FRAD to accept incoming packets.
+
+With this initial offering, only 1 FRAD driver is available.  With many thanks
+to Sangoma Technologies, David Mandelstam & Gene Kozin, the S502A, S502E &
+S508 are supported.  This driver is currently set up for only FR, but as
+Sangoma makes more firmware modules available, it can be updated to provide
+them as well.
+
+Configuration of the FRAD makes use of another net-tools program, 'fradcfg'.
+This program makes use of a configuration file (which dlcicfg can also read)
+to specify the types of boards to be configured as FRADs, as well as perform
+any board specific configuration.  The Sangoma module of fradcfg loads the
+FR firmware into the card, sets the irq/port/memory information, and provides
+an initial configuration.
+
+Additional FRAD device drivers can be added as hardware is available.
+
+At this time, the dlcicfg and fradcfg programs have not been incorporated into
+the net-tools distribution.  They can be found at ftp.invlogic.com, in
+/pub/linux.  Note that with OS/2 FTPD, you end up in /pub by default, so just
+use 'cd linux'.  v0.10 is for use on pre-2.0.3 and earlier, v0.15 is for
+pre-2.0.4 and later.
diff --git a/Documentation/networking/framerelay.txt b/Documentation/networking/framerelay.txt
deleted file mode 100644 (file)
index 1a0b720..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-Frame Relay (FR) support for linux is built into a two tiered system of device 
-drivers.  The upper layer implements RFC1490 FR specification, and uses the
-Data Link Connection Identifier (DLCI) as its hardware address.  Usually these
-are assigned by your network supplier, they give you the number/numbers of
-the Virtual Connections (VC) assigned to you.
-
-Each DLCI is a point-to-point link between your machine and a remote one.
-As such, a separate device is needed to accommodate the routing.  Within the
-net-tools archives is 'dlcicfg'.  This program will communicate with the
-base "DLCI" device, and create new net devices named 'dlci00', 'dlci01'... 
-The configuration script will ask you how many DLCIs you need, as well as
-how many DLCIs you want to assign to each Frame Relay Access Device (FRAD).
-
-The DLCI uses a number of function calls to communicate with the FRAD, all
-of which are stored in the FRAD's private data area.  assoc/deassoc, 
-activate/deactivate and dlci_config.  The DLCI supplies a receive function
-to the FRAD to accept incoming packets.
-
-With this initial offering, only 1 FRAD driver is available.  With many thanks
-to Sangoma Technologies, David Mandelstam & Gene Kozin, the S502A, S502E & 
-S508 are supported.  This driver is currently set up for only FR, but as 
-Sangoma makes more firmware modules available, it can be updated to provide
-them as well.
-
-Configuration of the FRAD makes use of another net-tools program, 'fradcfg'.
-This program makes use of a configuration file (which dlcicfg can also read)
-to specify the types of boards to be configured as FRADs, as well as perform
-any board specific configuration.  The Sangoma module of fradcfg loads the
-FR firmware into the card, sets the irq/port/memory information, and provides
-an initial configuration.
-
-Additional FRAD device drivers can be added as hardware is available.
-
-At this time, the dlcicfg and fradcfg programs have not been incorporated into
-the net-tools distribution.  They can be found at ftp.invlogic.com, in 
-/pub/linux.  Note that with OS/2 FTPD, you end up in /pub by default, so just
-use 'cd linux'.  v0.10 is for use on pre-2.0.3 and earlier, v0.15 is for 
-pre-2.0.4 and later.
-
diff --git a/Documentation/networking/gen_stats.rst b/Documentation/networking/gen_stats.rst
new file mode 100644 (file)
index 0000000..595a83b
--- /dev/null
@@ -0,0 +1,129 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============================================
+Generic networking statistics for netlink users
+===============================================
+
+Statistic counters are grouped into structs:
+
+==================== ===================== =====================
+Struct               TLV type              Description
+==================== ===================== =====================
+gnet_stats_basic     TCA_STATS_BASIC       Basic statistics
+gnet_stats_rate_est  TCA_STATS_RATE_EST    Rate estimator
+gnet_stats_queue     TCA_STATS_QUEUE       Queue statistics
+none                 TCA_STATS_APP         Application specific
+==================== ===================== =====================
+
+
+Collecting:
+-----------
+
+Declare the statistic structs you need::
+
+       struct mystruct {
+               struct gnet_stats_basic bstats;
+               struct gnet_stats_queue qstats;
+               ...
+       };
+
+Update statistics, in dequeue() methods only, (while owning qdisc->running)::
+
+       mystruct->tstats.packet++;
+       mystruct->qstats.backlog += skb->pkt_len;
+
+
+Export to userspace (Dump):
+---------------------------
+
+::
+
+    my_dumping_routine(struct sk_buff *skb, ...)
+    {
+           struct gnet_dump dump;
+
+           if (gnet_stats_start_copy(skb, TCA_STATS2, &mystruct->lock, &dump,
+                                   TCA_PAD) < 0)
+                   goto rtattr_failure;
+
+           if (gnet_stats_copy_basic(&dump, &mystruct->bstats) < 0 ||
+               gnet_stats_copy_queue(&dump, &mystruct->qstats) < 0 ||
+                   gnet_stats_copy_app(&dump, &xstats, sizeof(xstats)) < 0)
+                   goto rtattr_failure;
+
+           if (gnet_stats_finish_copy(&dump) < 0)
+                   goto rtattr_failure;
+           ...
+    }
+
+TCA_STATS/TCA_XSTATS backward compatibility:
+--------------------------------------------
+
+Prior users of struct tc_stats and xstats can maintain backward
+compatibility by calling the compat wrappers to keep providing the
+existing TLV types::
+
+    my_dumping_routine(struct sk_buff *skb, ...)
+    {
+       if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS,
+                                       TCA_XSTATS, &mystruct->lock, &dump,
+                                       TCA_PAD) < 0)
+                   goto rtattr_failure;
+           ...
+    }
+
+A struct tc_stats will be filled out during gnet_stats_copy_* calls
+and appended to the skb. TCA_XSTATS is provided if gnet_stats_copy_app
+was called.
+
+
+Locking:
+--------
+
+Locks are taken before writing and released once all statistics have
+been written. Locks are always released in case of an error. You
+are responsible for making sure that the lock is initialized.
+
+
+Rate Estimator:
+---------------
+
+0) Prepare an estimator attribute. Most likely this would be in user
+   space. The value of this TLV should contain a tc_estimator structure.
+   As usual, such a TLV needs to be 32 bit aligned and therefore the
+   length needs to be appropriately set, etc. The estimator interval
+   and ewma log need to be converted to the appropriate values.
+   tc_estimator.c::tc_setup_estimator() is advisable to be used as the
+   conversion routine. It does a few clever things. It takes a time
+   interval in microsecs, a time constant also in microsecs and a struct
+   tc_estimator to  be populated. The returned tc_estimator can be
+   transported to the kernel.  Transfer such a structure in a TLV of type
+   TCA_RATE to your code in the kernel.
+
+In the kernel when setting up:
+
+1) make sure you have basic stats and rate stats setup first.
+2) make sure you have initialized stats lock that is used to setup such
+   stats.
+3) Now initialize a new estimator::
+
+    int ret = gen_new_estimator(my_basicstats,my_rate_est_stats,
+       mystats_lock, attr_with_tcestimator_struct);
+
+    if ret == 0
+       success
+    else
+       failed
+
+From now on, every time you dump my_rate_est_stats it will contain
+up-to-date info.
+
+Once you are done, call gen_kill_estimator(my_basicstats,
+my_rate_est_stats) Make sure that my_basicstats and my_rate_est_stats
+are still valid (i.e still exist) at the time of making this call.
+
+
+Authors:
+--------
+- Thomas Graf <tgraf@suug.ch>
+- Jamal Hadi Salim <hadi@cyberus.ca>
diff --git a/Documentation/networking/gen_stats.txt b/Documentation/networking/gen_stats.txt
deleted file mode 100644 (file)
index 179b18c..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-Generic networking statistics for netlink users
-======================================================================
-
-Statistic counters are grouped into structs:
-
-Struct               TLV type              Description
-----------------------------------------------------------------------
-gnet_stats_basic     TCA_STATS_BASIC       Basic statistics
-gnet_stats_rate_est  TCA_STATS_RATE_EST    Rate estimator
-gnet_stats_queue     TCA_STATS_QUEUE       Queue statistics
-none                 TCA_STATS_APP         Application specific
-
-
-Collecting:
------------
-
-Declare the statistic structs you need:
-struct mystruct {
-       struct gnet_stats_basic bstats;
-       struct gnet_stats_queue qstats;
-       ...
-};
-
-Update statistics, in dequeue() methods only, (while owning qdisc->running)
-mystruct->tstats.packet++;
-mystruct->qstats.backlog += skb->pkt_len;
-
-
-Export to userspace (Dump):
----------------------------
-
-my_dumping_routine(struct sk_buff *skb, ...)
-{
-       struct gnet_dump dump;
-
-       if (gnet_stats_start_copy(skb, TCA_STATS2, &mystruct->lock, &dump,
-                                 TCA_PAD) < 0)
-               goto rtattr_failure;
-
-       if (gnet_stats_copy_basic(&dump, &mystruct->bstats) < 0 ||
-           gnet_stats_copy_queue(&dump, &mystruct->qstats) < 0 ||
-               gnet_stats_copy_app(&dump, &xstats, sizeof(xstats)) < 0)
-               goto rtattr_failure;
-
-       if (gnet_stats_finish_copy(&dump) < 0)
-               goto rtattr_failure;
-       ...
-}
-
-TCA_STATS/TCA_XSTATS backward compatibility:
---------------------------------------------
-
-Prior users of struct tc_stats and xstats can maintain backward
-compatibility by calling the compat wrappers to keep providing the
-existing TLV types.
-
-my_dumping_routine(struct sk_buff *skb, ...)
-{
-    if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS,
-                                    TCA_XSTATS, &mystruct->lock, &dump,
-                                    TCA_PAD) < 0)
-               goto rtattr_failure;
-       ...
-}
-
-A struct tc_stats will be filled out during gnet_stats_copy_* calls
-and appended to the skb. TCA_XSTATS is provided if gnet_stats_copy_app
-was called.
-
-
-Locking:
---------
-
-Locks are taken before writing and released once all statistics have
-been written. Locks are always released in case of an error. You
-are responsible for making sure that the lock is initialized.
-
-
-Rate Estimator:
---------------
-
-0) Prepare an estimator attribute. Most likely this would be in user
-   space. The value of this TLV should contain a tc_estimator structure.
-   As usual, such a TLV needs to be 32 bit aligned and therefore the
-   length needs to be appropriately set, etc. The estimator interval
-   and ewma log need to be converted to the appropriate values.
-   tc_estimator.c::tc_setup_estimator() is advisable to be used as the
-   conversion routine. It does a few clever things. It takes a time
-   interval in microsecs, a time constant also in microsecs and a struct
-   tc_estimator to  be populated. The returned tc_estimator can be
-   transported to the kernel.  Transfer such a structure in a TLV of type
-   TCA_RATE to your code in the kernel.
-
-In the kernel when setting up:
-1) make sure you have basic stats and rate stats setup first.
-2) make sure you have initialized stats lock that is used to setup such
-   stats.
-3) Now initialize a new estimator:
-
-   int ret = gen_new_estimator(my_basicstats,my_rate_est_stats,
-       mystats_lock, attr_with_tcestimator_struct);
-
-   if ret == 0
-       success
-   else
-       failed
-
-From now on, every time you dump my_rate_est_stats it will contain
-up-to-date info.
-
-Once you are done, call gen_kill_estimator(my_basicstats,
-my_rate_est_stats) Make sure that my_basicstats and my_rate_est_stats
-are still valid (i.e still exist) at the time of making this call.
-
-
-Authors:
---------
-Thomas Graf <tgraf@suug.ch>
-Jamal Hadi Salim <hadi@cyberus.ca>
diff --git a/Documentation/networking/generic-hdlc.rst b/Documentation/networking/generic-hdlc.rst
new file mode 100644 (file)
index 0000000..1c3bb5c
--- /dev/null
@@ -0,0 +1,170 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================
+Generic HDLC layer
+==================
+
+Krzysztof Halasa <khc@pm.waw.pl>
+
+
+Generic HDLC layer currently supports:
+
+1. Frame Relay (ANSI, CCITT, Cisco and no LMI)
+
+   - Normal (routed) and Ethernet-bridged (Ethernet device emulation)
+     interfaces can share a single PVC.
+   - ARP support (no InARP support in the kernel - there is an
+     experimental InARP user-space daemon available on:
+     http://www.kernel.org/pub/linux/utils/net/hdlc/).
+
+2. raw HDLC - either IP (IPv4) interface or Ethernet device emulation
+3. Cisco HDLC
+4. PPP
+5. X.25 (uses X.25 routines).
+
+Generic HDLC is a protocol driver only - it needs a low-level driver
+for your particular hardware.
+
+Ethernet device emulation (using HDLC or Frame-Relay PVC) is compatible
+with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging).
+
+
+Make sure the hdlc.o and the hardware driver are loaded. It should
+create a number of "hdlc" (hdlc0 etc) network devices, one for each
+WAN port. You'll need the "sethdlc" utility, get it from:
+
+       http://www.kernel.org/pub/linux/utils/net/hdlc/
+
+Compile sethdlc.c utility::
+
+       gcc -O2 -Wall -o sethdlc sethdlc.c
+
+Make sure you're using a correct version of sethdlc for your kernel.
+
+Use sethdlc to set physical interface, clock rate, HDLC mode used,
+and add any required PVCs if using Frame Relay.
+Usually you want something like::
+
+       sethdlc hdlc0 clock int rate 128000
+       sethdlc hdlc0 cisco interval 10 timeout 25
+
+or::
+
+       sethdlc hdlc0 rs232 clock ext
+       sethdlc hdlc0 fr lmi ansi
+       sethdlc hdlc0 create 99
+       ifconfig hdlc0 up
+       ifconfig pvc0 localIP pointopoint remoteIP
+
+In Frame Relay mode, ifconfig master hdlc device up (without assigning
+any IP address to it) before using pvc devices.
+
+
+Setting interface:
+
+* v35 | rs232 | x21 | t1 | e1
+    - sets physical interface for a given port
+      if the card has software-selectable interfaces
+  loopback
+    - activate hardware loopback (for testing only)
+* clock ext
+    - both RX clock and TX clock external
+* clock int
+    - both RX clock and TX clock internal
+* clock txint
+    - RX clock external, TX clock internal
+* clock txfromrx
+    - RX clock external, TX clock derived from RX clock
+* rate
+    - sets clock rate in bps (for "int" or "txint" clock only)
+
+
+Setting protocol:
+
+* hdlc - sets raw HDLC (IP-only) mode
+
+  nrz / nrzi / fm-mark / fm-space / manchester - sets transmission code
+
+  no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu
+
+  crc16-itu (CRC16 with ITU-T polynomial) / crc16-itu-pr0 - sets parity
+
+* hdlc-eth - Ethernet device emulation using HDLC. Parity and encoding
+  as above.
+
+* cisco - sets Cisco HDLC mode (IP, IPv6 and IPX supported)
+
+  interval - time in seconds between keepalive packets
+
+  timeout - time in seconds after last received keepalive packet before
+           we assume the link is down
+
+* ppp - sets synchronous PPP mode
+
+* x25 - sets X.25 mode
+
+* fr - Frame Relay mode
+
+  lmi ansi / ccitt / cisco / none - LMI (link management) type
+
+  dce - Frame Relay DCE (network) side LMI instead of default DTE (user).
+
+  It has nothing to do with clocks!
+
+  - t391 - link integrity verification polling timer (in seconds) - user
+  - t392 - polling verification timer (in seconds) - network
+  - n391 - full status polling counter - user
+  - n392 - error threshold - both user and network
+  - n393 - monitored events count - both user and network
+
+Frame-Relay only:
+
+* create n | delete n - adds / deletes PVC interface with DLCI #n.
+  Newly created interface will be named pvc0, pvc1 etc.
+
+* create ether n | delete ether n - adds a device for Ethernet-bridged
+  frames. The device will be named pvceth0, pvceth1 etc.
+
+
+
+
+Board-specific issues
+---------------------
+
+n2.o and c101.o need parameters to work::
+
+       insmod n2 hw=io,irq,ram,ports[:io,irq,...]
+
+example::
+
+       insmod n2 hw=0x300,10,0xD0000,01
+
+or::
+
+       insmod c101 hw=irq,ram[:irq,...]
+
+example::
+
+       insmod c101 hw=9,0xdc000
+
+If built into the kernel, these drivers need kernel (command line) parameters::
+
+       n2.hw=io,irq,ram,ports:...
+
+or::
+
+       c101.hw=irq,ram:...
+
+
+
+If you have a problem with N2, C101 or PLX200SYN card, you can issue the
+"private" command to see port's packet descriptor rings (in kernel logs)::
+
+       sethdlc hdlc0 private
+
+The hardware driver has to be build with #define DEBUG_RINGS.
+Attaching this info to bug reports would be helpful. Anyway, let me know
+if you have problems using this.
+
+For patches and other info look at:
+<http://www.kernel.org/pub/linux/utils/net/hdlc/>.
diff --git a/Documentation/networking/generic-hdlc.txt b/Documentation/networking/generic-hdlc.txt
deleted file mode 100644 (file)
index 4eb3cc4..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-Generic HDLC layer
-Krzysztof Halasa <khc@pm.waw.pl>
-
-
-Generic HDLC layer currently supports:
-1. Frame Relay (ANSI, CCITT, Cisco and no LMI)
-   - Normal (routed) and Ethernet-bridged (Ethernet device emulation)
-     interfaces can share a single PVC.
-   - ARP support (no InARP support in the kernel - there is an
-     experimental InARP user-space daemon available on:
-     http://www.kernel.org/pub/linux/utils/net/hdlc/).
-2. raw HDLC - either IP (IPv4) interface or Ethernet device emulation
-3. Cisco HDLC
-4. PPP
-5. X.25 (uses X.25 routines).
-
-Generic HDLC is a protocol driver only - it needs a low-level driver
-for your particular hardware.
-
-Ethernet device emulation (using HDLC or Frame-Relay PVC) is compatible
-with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging).
-
-
-Make sure the hdlc.o and the hardware driver are loaded. It should
-create a number of "hdlc" (hdlc0 etc) network devices, one for each
-WAN port. You'll need the "sethdlc" utility, get it from:
-       http://www.kernel.org/pub/linux/utils/net/hdlc/
-
-Compile sethdlc.c utility:
-       gcc -O2 -Wall -o sethdlc sethdlc.c
-Make sure you're using a correct version of sethdlc for your kernel.
-
-Use sethdlc to set physical interface, clock rate, HDLC mode used,
-and add any required PVCs if using Frame Relay.
-Usually you want something like:
-
-       sethdlc hdlc0 clock int rate 128000
-       sethdlc hdlc0 cisco interval 10 timeout 25
-or
-       sethdlc hdlc0 rs232 clock ext
-       sethdlc hdlc0 fr lmi ansi
-       sethdlc hdlc0 create 99
-       ifconfig hdlc0 up
-       ifconfig pvc0 localIP pointopoint remoteIP
-
-In Frame Relay mode, ifconfig master hdlc device up (without assigning
-any IP address to it) before using pvc devices.
-
-
-Setting interface:
-
-* v35 | rs232 | x21 | t1 | e1 - sets physical interface for a given port
-                                if the card has software-selectable interfaces
-  loopback - activate hardware loopback (for testing only)
-* clock ext - both RX clock and TX clock external
-* clock int - both RX clock and TX clock internal
-* clock txint - RX clock external, TX clock internal
-* clock txfromrx - RX clock external, TX clock derived from RX clock
-* rate - sets clock rate in bps (for "int" or "txint" clock only)
-
-
-Setting protocol:
-
-* hdlc - sets raw HDLC (IP-only) mode
-  nrz / nrzi / fm-mark / fm-space / manchester - sets transmission code
-  no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu
-  crc16-itu (CRC16 with ITU-T polynomial) / crc16-itu-pr0 - sets parity
-
-* hdlc-eth - Ethernet device emulation using HDLC. Parity and encoding
-  as above.
-
-* cisco - sets Cisco HDLC mode (IP, IPv6 and IPX supported)
-  interval - time in seconds between keepalive packets
-  timeout - time in seconds after last received keepalive packet before
-            we assume the link is down
-
-* ppp - sets synchronous PPP mode
-
-* x25 - sets X.25 mode
-
-* fr - Frame Relay mode
-  lmi ansi / ccitt / cisco / none - LMI (link management) type
-  dce - Frame Relay DCE (network) side LMI instead of default DTE (user).
-  It has nothing to do with clocks!
-  t391 - link integrity verification polling timer (in seconds) - user
-  t392 - polling verification timer (in seconds) - network
-  n391 - full status polling counter - user
-  n392 - error threshold - both user and network
-  n393 - monitored events count - both user and network
-
-Frame-Relay only:
-* create n | delete n - adds / deletes PVC interface with DLCI #n.
-  Newly created interface will be named pvc0, pvc1 etc.
-
-* create ether n | delete ether n - adds a device for Ethernet-bridged
-  frames. The device will be named pvceth0, pvceth1 etc.
-
-
-
-
-Board-specific issues
----------------------
-
-n2.o and c101.o need parameters to work:
-
-       insmod n2 hw=io,irq,ram,ports[:io,irq,...]
-example:
-       insmod n2 hw=0x300,10,0xD0000,01
-
-or
-       insmod c101 hw=irq,ram[:irq,...]
-example:
-       insmod c101 hw=9,0xdc000
-
-If built into the kernel, these drivers need kernel (command line) parameters:
-       n2.hw=io,irq,ram,ports:...
-or
-       c101.hw=irq,ram:...
-
-
-
-If you have a problem with N2, C101 or PLX200SYN card, you can issue the
-"private" command to see port's packet descriptor rings (in kernel logs):
-
-       sethdlc hdlc0 private
-
-The hardware driver has to be build with #define DEBUG_RINGS.
-Attaching this info to bug reports would be helpful. Anyway, let me know
-if you have problems using this.
-
-For patches and other info look at:
-<http://www.kernel.org/pub/linux/utils/net/hdlc/>.
diff --git a/Documentation/networking/generic_netlink.rst b/Documentation/networking/generic_netlink.rst
new file mode 100644 (file)
index 0000000..59e04cc
--- /dev/null
@@ -0,0 +1,9 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============
+Generic Netlink
+===============
+
+A wiki document on how to use Generic Netlink can be found here:
+
+ * http://www.linuxfoundation.org/collaborate/workgroups/networking/generic_netlink_howto
diff --git a/Documentation/networking/generic_netlink.txt b/Documentation/networking/generic_netlink.txt
deleted file mode 100644 (file)
index 3e07111..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-A wiki document on how to use Generic Netlink can be found here:
-
- * http://www.linuxfoundation.org/collaborate/workgroups/networking/generic_netlink_howto
diff --git a/Documentation/networking/gtp.rst b/Documentation/networking/gtp.rst
new file mode 100644 (file)
index 0000000..1563fb9
--- /dev/null
@@ -0,0 +1,251 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================================
+The Linux kernel GTP tunneling module
+=====================================
+
+Documentation by
+                Harald Welte <laforge@gnumonks.org> and
+                Andreas Schultz <aschultz@tpip.net>
+
+In 'drivers/net/gtp.c' you are finding a kernel-level implementation
+of a GTP tunnel endpoint.
+
+What is GTP
+===========
+
+GTP is the Generic Tunnel Protocol, which is a 3GPP protocol used for
+tunneling User-IP payload between a mobile station (phone, modem)
+and the interconnection between an external packet data network (such
+as the internet).
+
+So when you start a 'data connection' from your mobile phone, the
+phone will use the control plane to signal for the establishment of
+such a tunnel between that external data network and the phone.  The
+tunnel endpoints thus reside on the phone and in the gateway.  All
+intermediate nodes just transport the encapsulated packet.
+
+The phone itself does not implement GTP but uses some other
+technology-dependent protocol stack for transmitting the user IP
+payload, such as LLC/SNDCP/RLC/MAC.
+
+At some network element inside the cellular operator infrastructure
+(SGSN in case of GPRS/EGPRS or classic UMTS, hNodeB in case of a 3G
+femtocell, eNodeB in case of 4G/LTE), the cellular protocol stacking
+is translated into GTP *without breaking the end-to-end tunnel*.  So
+intermediate nodes just perform some specific relay function.
+
+At some point the GTP packet ends up on the so-called GGSN (GSM/UMTS)
+or P-GW (LTE), which terminates the tunnel, decapsulates the packet
+and forwards it onto an external packet data network.  This can be
+public internet, but can also be any private IP network (or even
+theoretically some non-IP network like X.25).
+
+You can find the protocol specification in 3GPP TS 29.060, available
+publicly via the 3GPP website at http://www.3gpp.org/DynaReport/29060.htm
+
+A direct PDF link to v13.6.0 is provided for convenience below:
+http://www.etsi.org/deliver/etsi_ts/129000_129099/129060/13.06.00_60/ts_129060v130600p.pdf
+
+The Linux GTP tunnelling module
+===============================
+
+The module implements the function of a tunnel endpoint, i.e. it is
+able to decapsulate tunneled IP packets in the uplink originated by
+the phone, and encapsulate raw IP packets received from the external
+packet network in downlink towards the phone.
+
+It *only* implements the so-called 'user plane', carrying the User-IP
+payload, called GTP-U.  It does not implement the 'control plane',
+which is a signaling protocol used for establishment and teardown of
+GTP tunnels (GTP-C).
+
+So in order to have a working GGSN/P-GW setup, you will need a
+userspace program that implements the GTP-C protocol and which then
+uses the netlink interface provided by the GTP-U module in the kernel
+to configure the kernel module.
+
+This split architecture follows the tunneling modules of other
+protocols, e.g. PPPoE or L2TP, where you also run a userspace daemon
+to handle the tunnel establishment, authentication etc. and only the
+data plane is accelerated inside the kernel.
+
+Don't be confused by terminology:  The GTP User Plane goes through
+kernel accelerated path, while the GTP Control Plane goes to
+Userspace :)
+
+The official homepage of the module is at
+https://osmocom.org/projects/linux-kernel-gtp-u/wiki
+
+Userspace Programs with Linux Kernel GTP-U support
+==================================================
+
+At the time of this writing, there are at least two Free Software
+implementations that implement GTP-C and can use the netlink interface
+to make use of the Linux kernel GTP-U support:
+
+* OpenGGSN (classic 2G/3G GGSN in C):
+  https://osmocom.org/projects/openggsn/wiki/OpenGGSN
+
+* ergw (GGSN + P-GW in Erlang):
+  https://github.com/travelping/ergw
+
+Userspace Library / Command Line Utilities
+==========================================
+
+There is a userspace library called 'libgtpnl' which is based on
+libmnl and which implements a C-language API towards the netlink
+interface provided by the Kernel GTP module:
+
+http://git.osmocom.org/libgtpnl/
+
+Protocol Versions
+=================
+
+There are two different versions of GTP-U: v0 [GSM TS 09.60] and v1
+[3GPP TS 29.281].  Both are implemented in the Kernel GTP module.
+Version 0 is a legacy version, and deprecated from recent 3GPP
+specifications.
+
+GTP-U uses UDP for transporting PDUs.  The receiving UDP port is 2151
+for GTPv1-U and 3386 for GTPv0-U.
+
+There are three versions of GTP-C: v0, v1, and v2.  As the kernel
+doesn't implement GTP-C, we don't have to worry about this.  It's the
+responsibility of the control plane implementation in userspace to
+implement that.
+
+IPv6
+====
+
+The 3GPP specifications indicate either IPv4 or IPv6 can be used both
+on the inner (user) IP layer, or on the outer (transport) layer.
+
+Unfortunately, the Kernel module currently supports IPv6 neither for
+the User IP payload, nor for the outer IP layer.  Patches or other
+Contributions to fix this are most welcome!
+
+Mailing List
+============
+
+If you have questions regarding how to use the Kernel GTP module from
+your own software, or want to contribute to the code, please use the
+osmocom-net-grps mailing list for related discussion. The list can be
+reached at osmocom-net-gprs@lists.osmocom.org and the mailman
+interface for managing your subscription is at
+https://lists.osmocom.org/mailman/listinfo/osmocom-net-gprs
+
+Issue Tracker
+=============
+
+The Osmocom project maintains an issue tracker for the Kernel GTP-U
+module at
+https://osmocom.org/projects/linux-kernel-gtp-u/issues
+
+History / Acknowledgements
+==========================
+
+The Module was originally created in 2012 by Harald Welte, but never
+completed.  Pablo came in to finish the mess Harald left behind.  But
+doe to a lack of user interest, it never got merged.
+
+In 2015, Andreas Schultz came to the rescue and fixed lots more bugs,
+extended it with new features and finally pushed all of us to get it
+mainline, where it was merged in 4.7.0.
+
+Architectural Details
+=====================
+
+Local GTP-U entity and tunnel identification
+--------------------------------------------
+
+GTP-U uses UDP for transporting PDU's. The receiving UDP port is 2152
+for GTPv1-U and 3386 for GTPv0-U.
+
+There is only one GTP-U entity (and therefor SGSN/GGSN/S-GW/PDN-GW
+instance) per IP address. Tunnel Endpoint Identifier (TEID) are unique
+per GTP-U entity.
+
+A specific tunnel is only defined by the destination entity. Since the
+destination port is constant, only the destination IP and TEID define
+a tunnel. The source IP and Port have no meaning for the tunnel.
+
+Therefore:
+
+  * when sending, the remote entity is defined by the remote IP and
+    the tunnel endpoint id. The source IP and port have no meaning and
+    can be changed at any time.
+
+  * when receiving the local entity is defined by the local
+    destination IP and the tunnel endpoint id. The source IP and port
+    have no meaning and can change at any time.
+
+[3GPP TS 29.281] Section 4.3.0 defines this so::
+
+  The TEID in the GTP-U header is used to de-multiplex traffic
+  incoming from remote tunnel endpoints so that it is delivered to the
+  User plane entities in a way that allows multiplexing of different
+  users, different packet protocols and different QoS levels.
+  Therefore no two remote GTP-U endpoints shall send traffic to a
+  GTP-U protocol entity using the same TEID value except
+  for data forwarding as part of mobility procedures.
+
+The definition above only defines that two remote GTP-U endpoints
+*should not* send to the same TEID, it *does not* forbid or exclude
+such a scenario. In fact, the mentioned mobility procedures make it
+necessary that the GTP-U entity accepts traffic for TEIDs from
+multiple or unknown peers.
+
+Therefore, the receiving side identifies tunnels exclusively based on
+TEIDs, not based on the source IP!
+
+APN vs. Network Device
+======================
+
+The GTP-U driver creates a Linux network device for each Gi/SGi
+interface.
+
+[3GPP TS 29.281] calls the Gi/SGi reference point an interface. This
+may lead to the impression that the GGSN/P-GW can have only one such
+interface.
+
+Correct is that the Gi/SGi reference point defines the interworking
+between +the 3GPP packet domain (PDN) based on GTP-U tunnel and IP
+based networks.
+
+There is no provision in any of the 3GPP documents that limits the
+number of Gi/SGi interfaces implemented by a GGSN/P-GW.
+
+[3GPP TS 29.061] Section 11.3 makes it clear that the selection of a
+specific Gi/SGi interfaces is made through the Access Point Name
+(APN)::
+
+  2. each private network manages its own addressing. In general this
+     will result in different private networks having overlapping
+     address ranges. A logically separate connection (e.g. an IP in IP
+     tunnel or layer 2 virtual circuit) is used between the GGSN/P-GW
+     and each private network.
+
+     In this case the IP address alone is not necessarily unique.  The
+     pair of values, Access Point Name (APN) and IPv4 address and/or
+     IPv6 prefixes, is unique.
+
+In order to support the overlapping address range use case, each APN
+is mapped to a separate Gi/SGi interface (network device).
+
+.. note::
+
+   The Access Point Name is purely a control plane (GTP-C) concept.
+   At the GTP-U level, only Tunnel Endpoint Identifiers are present in
+   GTP-U packets and network devices are known
+
+Therefore for a given UE the mapping in IP to PDN network is:
+
+  * network device + MS IP -> Peer IP + Peer TEID,
+
+and from PDN to IP network:
+
+  * local GTP-U IP + TEID  -> network device
+
+Furthermore, before a received T-PDU is injected into the network
+device the MS IP is checked against the IP recorded in PDP context.
diff --git a/Documentation/networking/gtp.txt b/Documentation/networking/gtp.txt
deleted file mode 100644 (file)
index 6966bbe..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-The Linux kernel GTP tunneling module
-======================================================================
-Documentation by Harald Welte <laforge@gnumonks.org> and
-                 Andreas Schultz <aschultz@tpip.net>
-
-In 'drivers/net/gtp.c' you are finding a kernel-level implementation
-of a GTP tunnel endpoint.
-
-== What is GTP ==
-
-GTP is the Generic Tunnel Protocol, which is a 3GPP protocol used for
-tunneling User-IP payload between a mobile station (phone, modem)
-and the interconnection between an external packet data network (such
-as the internet).
-
-So when you start a 'data connection' from your mobile phone, the
-phone will use the control plane to signal for the establishment of
-such a tunnel between that external data network and the phone.  The
-tunnel endpoints thus reside on the phone and in the gateway.  All
-intermediate nodes just transport the encapsulated packet.
-
-The phone itself does not implement GTP but uses some other
-technology-dependent protocol stack for transmitting the user IP
-payload, such as LLC/SNDCP/RLC/MAC.
-
-At some network element inside the cellular operator infrastructure
-(SGSN in case of GPRS/EGPRS or classic UMTS, hNodeB in case of a 3G
-femtocell, eNodeB in case of 4G/LTE), the cellular protocol stacking
-is translated into GTP *without breaking the end-to-end tunnel*.  So
-intermediate nodes just perform some specific relay function.
-
-At some point the GTP packet ends up on the so-called GGSN (GSM/UMTS)
-or P-GW (LTE), which terminates the tunnel, decapsulates the packet
-and forwards it onto an external packet data network.  This can be
-public internet, but can also be any private IP network (or even
-theoretically some non-IP network like X.25).
-
-You can find the protocol specification in 3GPP TS 29.060, available
-publicly via the 3GPP website at http://www.3gpp.org/DynaReport/29060.htm
-
-A direct PDF link to v13.6.0 is provided for convenience below:
-http://www.etsi.org/deliver/etsi_ts/129000_129099/129060/13.06.00_60/ts_129060v130600p.pdf
-
-== The Linux GTP tunnelling module ==
-
-The module implements the function of a tunnel endpoint, i.e. it is
-able to decapsulate tunneled IP packets in the uplink originated by
-the phone, and encapsulate raw IP packets received from the external
-packet network in downlink towards the phone.
-
-It *only* implements the so-called 'user plane', carrying the User-IP
-payload, called GTP-U.  It does not implement the 'control plane',
-which is a signaling protocol used for establishment and teardown of
-GTP tunnels (GTP-C).
-
-So in order to have a working GGSN/P-GW setup, you will need a
-userspace program that implements the GTP-C protocol and which then
-uses the netlink interface provided by the GTP-U module in the kernel
-to configure the kernel module.
-
-This split architecture follows the tunneling modules of other
-protocols, e.g. PPPoE or L2TP, where you also run a userspace daemon
-to handle the tunnel establishment, authentication etc. and only the
-data plane is accelerated inside the kernel.
-
-Don't be confused by terminology:  The GTP User Plane goes through
-kernel accelerated path, while the GTP Control Plane goes to
-Userspace :)
-
-The official homepage of the module is at
-https://osmocom.org/projects/linux-kernel-gtp-u/wiki
-
-== Userspace Programs with Linux Kernel GTP-U support ==
-
-At the time of this writing, there are at least two Free Software
-implementations that implement GTP-C and can use the netlink interface
-to make use of the Linux kernel GTP-U support:
-
-* OpenGGSN (classic 2G/3G GGSN in C):
-  https://osmocom.org/projects/openggsn/wiki/OpenGGSN
-
-* ergw (GGSN + P-GW in Erlang):
-  https://github.com/travelping/ergw
-
-== Userspace Library / Command Line Utilities ==
-
-There is a userspace library called 'libgtpnl' which is based on
-libmnl and which implements a C-language API towards the netlink
-interface provided by the Kernel GTP module:
-
-http://git.osmocom.org/libgtpnl/
-
-== Protocol Versions ==
-
-There are two different versions of GTP-U: v0 [GSM TS 09.60] and v1
-[3GPP TS 29.281].  Both are implemented in the Kernel GTP module.
-Version 0 is a legacy version, and deprecated from recent 3GPP
-specifications.
-
-GTP-U uses UDP for transporting PDUs.  The receiving UDP port is 2151
-for GTPv1-U and 3386 for GTPv0-U.
-
-There are three versions of GTP-C: v0, v1, and v2.  As the kernel
-doesn't implement GTP-C, we don't have to worry about this.  It's the
-responsibility of the control plane implementation in userspace to
-implement that.
-
-== IPv6 ==
-
-The 3GPP specifications indicate either IPv4 or IPv6 can be used both
-on the inner (user) IP layer, or on the outer (transport) layer.
-
-Unfortunately, the Kernel module currently supports IPv6 neither for
-the User IP payload, nor for the outer IP layer.  Patches or other
-Contributions to fix this are most welcome!
-
-== Mailing List ==
-
-If yo have questions regarding how to use the Kernel GTP module from
-your own software, or want to contribute to the code, please use the
-osmocom-net-grps mailing list for related discussion. The list can be
-reached at osmocom-net-gprs@lists.osmocom.org and the mailman
-interface for managing your subscription is at
-https://lists.osmocom.org/mailman/listinfo/osmocom-net-gprs
-
-== Issue Tracker ==
-
-The Osmocom project maintains an issue tracker for the Kernel GTP-U
-module at
-https://osmocom.org/projects/linux-kernel-gtp-u/issues
-
-== History / Acknowledgements ==
-
-The Module was originally created in 2012 by Harald Welte, but never
-completed.  Pablo came in to finish the mess Harald left behind.  But
-doe to a lack of user interest, it never got merged.
-
-In 2015, Andreas Schultz came to the rescue and fixed lots more bugs,
-extended it with new features and finally pushed all of us to get it
-mainline, where it was merged in 4.7.0.
-
-== Architectural Details ==
-
-=== Local GTP-U entity and tunnel identification ===
-
-GTP-U uses UDP for transporting PDU's. The receiving UDP port is 2152
-for GTPv1-U and 3386 for GTPv0-U.
-
-There is only one GTP-U entity (and therefor SGSN/GGSN/S-GW/PDN-GW
-instance) per IP address. Tunnel Endpoint Identifier (TEID) are unique
-per GTP-U entity.
-
-A specific tunnel is only defined by the destination entity. Since the
-destination port is constant, only the destination IP and TEID define
-a tunnel. The source IP and Port have no meaning for the tunnel.
-
-Therefore:
-
-  * when sending, the remote entity is defined by the remote IP and
-    the tunnel endpoint id. The source IP and port have no meaning and
-    can be changed at any time.
-
-  * when receiving the local entity is defined by the local
-    destination IP and the tunnel endpoint id. The source IP and port
-    have no meaning and can change at any time.
-
-[3GPP TS 29.281] Section 4.3.0 defines this so:
-
-> The TEID in the GTP-U header is used to de-multiplex traffic
-> incoming from remote tunnel endpoints so that it is delivered to the
-> User plane entities in a way that allows multiplexing of different
-> users, different packet protocols and different QoS levels.
-> Therefore no two remote GTP-U endpoints shall send traffic to a
-> GTP-U protocol entity using the same TEID value except
-> for data forwarding as part of mobility procedures.
-
-The definition above only defines that two remote GTP-U endpoints
-*should not* send to the same TEID, it *does not* forbid or exclude
-such a scenario. In fact, the mentioned mobility procedures make it
-necessary that the GTP-U entity accepts traffic for TEIDs from
-multiple or unknown peers.
-
-Therefore, the receiving side identifies tunnels exclusively based on
-TEIDs, not based on the source IP!
-
-== APN vs. Network Device ==
-
-The GTP-U driver creates a Linux network device for each Gi/SGi
-interface.
-
-[3GPP TS 29.281] calls the Gi/SGi reference point an interface. This
-may lead to the impression that the GGSN/P-GW can have only one such
-interface.
-
-Correct is that the Gi/SGi reference point defines the interworking
-between +the 3GPP packet domain (PDN) based on GTP-U tunnel and IP
-based networks.
-
-There is no provision in any of the 3GPP documents that limits the
-number of Gi/SGi interfaces implemented by a GGSN/P-GW.
-
-[3GPP TS 29.061] Section 11.3 makes it clear that the selection of a
-specific Gi/SGi interfaces is made through the Access Point Name
-(APN):
-
-> 2. each private network manages its own addressing. In general this
->    will result in different private networks having overlapping
->    address ranges. A logically separate connection (e.g. an IP in IP
->    tunnel or layer 2 virtual circuit) is used between the GGSN/P-GW
->    and each private network.
->
->    In this case the IP address alone is not necessarily unique.  The
->    pair of values, Access Point Name (APN) and IPv4 address and/or
->    IPv6 prefixes, is unique.
-
-In order to support the overlapping address range use case, each APN
-is mapped to a separate Gi/SGi interface (network device).
-
-NOTE: The Access Point Name is purely a control plane (GTP-C) concept.
-At the GTP-U level, only Tunnel Endpoint Identifiers are present in
-GTP-U packets and network devices are known
-
-Therefore for a given UE the mapping in IP to PDN network is:
-  * network device + MS IP -> Peer IP + Peer TEID,
-
-and from PDN to IP network:
-  * local GTP-U IP + TEID  -> network device
-
-Furthermore, before a received T-PDU is injected into the network
-device the MS IP is checked against the IP recorded in PDP context.
diff --git a/Documentation/networking/hinic.rst b/Documentation/networking/hinic.rst
new file mode 100644 (file)
index 0000000..867ac8f
--- /dev/null
@@ -0,0 +1,128 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================================================
+Linux Kernel Driver for Huawei Intelligent NIC(HiNIC) family
+============================================================
+
+Overview:
+=========
+HiNIC is a network interface card for the Data Center Area.
+
+The driver supports a range of link-speed devices (10GbE, 25GbE, 40GbE, etc.).
+The driver supports also a negotiated and extendable feature set.
+
+Some HiNIC devices support SR-IOV. This driver is used for Physical Function
+(PF).
+
+HiNIC devices support MSI-X interrupt vector for each Tx/Rx queue and
+adaptive interrupt moderation.
+
+HiNIC devices support also various offload features such as checksum offload,
+TCP Transmit Segmentation Offload(TSO), Receive-Side Scaling(RSS) and
+LRO(Large Receive Offload).
+
+
+Supported PCI vendor ID/device IDs:
+===================================
+
+19e5:1822 - HiNIC PF
+
+
+Driver Architecture and Source Code:
+====================================
+
+hinic_dev - Implement a Logical Network device that is independent from
+specific HW details about HW data structure formats.
+
+hinic_hwdev - Implement the HW details of the device and include the components
+for accessing the PCI NIC.
+
+hinic_hwdev contains the following components:
+===============================================
+
+HW Interface:
+=============
+
+The interface for accessing the pci device (DMA memory and PCI BARs).
+(hinic_hw_if.c, hinic_hw_if.h)
+
+Configuration Status Registers Area that describes the HW Registers on the
+configuration and status BAR0. (hinic_hw_csr.h)
+
+MGMT components:
+================
+
+Asynchronous Event Queues(AEQs) - The event queues for receiving messages from
+the MGMT modules on the cards. (hinic_hw_eqs.c, hinic_hw_eqs.h)
+
+Application Programmable Interface commands(API CMD) - Interface for sending
+MGMT commands to the card. (hinic_hw_api_cmd.c, hinic_hw_api_cmd.h)
+
+Management (MGMT) - the PF to MGMT channel that uses API CMD for sending MGMT
+commands to the card and receives notifications from the MGMT modules on the
+card by AEQs. Also set the addresses of the IO CMDQs in HW.
+(hinic_hw_mgmt.c, hinic_hw_mgmt.h)
+
+IO components:
+==============
+
+Completion Event Queues(CEQs) - The completion Event Queues that describe IO
+tasks that are finished. (hinic_hw_eqs.c, hinic_hw_eqs.h)
+
+Work Queues(WQ) - Contain the memory and operations for use by CMD queues and
+the Queue Pairs. The WQ is a Memory Block in a Page. The Block contains
+pointers to Memory Areas that are the Memory for the Work Queue Elements(WQEs).
+(hinic_hw_wq.c, hinic_hw_wq.h)
+
+Command Queues(CMDQ) - The queues for sending commands for IO management and is
+used to set the QPs addresses in HW. The commands completion events are
+accumulated on the CEQ that is configured to receive the CMDQ completion events.
+(hinic_hw_cmdq.c, hinic_hw_cmdq.h)
+
+Queue Pairs(QPs) - The HW Receive and Send queues for Receiving and Transmitting
+Data. (hinic_hw_qp.c, hinic_hw_qp.h, hinic_hw_qp_ctxt.h)
+
+IO - de/constructs all the IO components. (hinic_hw_io.c, hinic_hw_io.h)
+
+HW device:
+==========
+
+HW device - de/constructs the HW Interface, the MGMT components on the
+initialization of the driver and the IO components on the case of Interface
+UP/DOWN Events. (hinic_hw_dev.c, hinic_hw_dev.h)
+
+
+hinic_dev contains the following components:
+===============================================
+
+PCI ID table - Contains the supported PCI Vendor/Device IDs.
+(hinic_pci_tbl.h)
+
+Port Commands - Send commands to the HW device for port management
+(MAC, Vlan, MTU, ...). (hinic_port.c, hinic_port.h)
+
+Tx Queues - Logical Tx Queues that use the HW Send Queues for transmit.
+The Logical Tx queue is not dependent on the format of the HW Send Queue.
+(hinic_tx.c, hinic_tx.h)
+
+Rx Queues - Logical Rx Queues that use the HW Receive Queues for receive.
+The Logical Rx queue is not dependent on the format of the HW Receive Queue.
+(hinic_rx.c, hinic_rx.h)
+
+hinic_dev - de/constructs the Logical Tx and Rx Queues.
+(hinic_main.c, hinic_dev.h)
+
+
+Miscellaneous
+=============
+
+Common functions that are used by HW and Logical Device.
+(hinic_common.c, hinic_common.h)
+
+
+Support
+=======
+
+If an issue is identified with the released source code on the supported kernel
+with a supported adapter, email the specific information related to the issue to
+aviad.krawczyk@huawei.com.
diff --git a/Documentation/networking/hinic.txt b/Documentation/networking/hinic.txt
deleted file mode 100644 (file)
index 989366a..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-Linux Kernel Driver for Huawei Intelligent NIC(HiNIC) family
-============================================================
-
-Overview:
-=========
-HiNIC is a network interface card for the Data Center Area.
-
-The driver supports a range of link-speed devices (10GbE, 25GbE, 40GbE, etc.).
-The driver supports also a negotiated and extendable feature set.
-
-Some HiNIC devices support SR-IOV. This driver is used for Physical Function
-(PF).
-
-HiNIC devices support MSI-X interrupt vector for each Tx/Rx queue and
-adaptive interrupt moderation.
-
-HiNIC devices support also various offload features such as checksum offload,
-TCP Transmit Segmentation Offload(TSO), Receive-Side Scaling(RSS) and
-LRO(Large Receive Offload).
-
-
-Supported PCI vendor ID/device IDs:
-===================================
-
-19e5:1822 - HiNIC PF
-
-
-Driver Architecture and Source Code:
-====================================
-
-hinic_dev - Implement a Logical Network device that is independent from
-specific HW details about HW data structure formats.
-
-hinic_hwdev - Implement the HW details of the device and include the components
-for accessing the PCI NIC.
-
-hinic_hwdev contains the following components:
-===============================================
-
-HW Interface:
-=============
-
-The interface for accessing the pci device (DMA memory and PCI BARs).
-(hinic_hw_if.c, hinic_hw_if.h)
-
-Configuration Status Registers Area that describes the HW Registers on the
-configuration and status BAR0. (hinic_hw_csr.h)
-
-MGMT components:
-================
-
-Asynchronous Event Queues(AEQs) - The event queues for receiving messages from
-the MGMT modules on the cards. (hinic_hw_eqs.c, hinic_hw_eqs.h)
-
-Application Programmable Interface commands(API CMD) - Interface for sending
-MGMT commands to the card. (hinic_hw_api_cmd.c, hinic_hw_api_cmd.h)
-
-Management (MGMT) - the PF to MGMT channel that uses API CMD for sending MGMT
-commands to the card and receives notifications from the MGMT modules on the
-card by AEQs. Also set the addresses of the IO CMDQs in HW.
-(hinic_hw_mgmt.c, hinic_hw_mgmt.h)
-
-IO components:
-==============
-
-Completion Event Queues(CEQs) - The completion Event Queues that describe IO
-tasks that are finished. (hinic_hw_eqs.c, hinic_hw_eqs.h)
-
-Work Queues(WQ) - Contain the memory and operations for use by CMD queues and
-the Queue Pairs. The WQ is a Memory Block in a Page. The Block contains
-pointers to Memory Areas that are the Memory for the Work Queue Elements(WQEs).
-(hinic_hw_wq.c, hinic_hw_wq.h)
-
-Command Queues(CMDQ) - The queues for sending commands for IO management and is
-used to set the QPs addresses in HW. The commands completion events are
-accumulated on the CEQ that is configured to receive the CMDQ completion events.
-(hinic_hw_cmdq.c, hinic_hw_cmdq.h)
-
-Queue Pairs(QPs) - The HW Receive and Send queues for Receiving and Transmitting
-Data. (hinic_hw_qp.c, hinic_hw_qp.h, hinic_hw_qp_ctxt.h)
-
-IO - de/constructs all the IO components. (hinic_hw_io.c, hinic_hw_io.h)
-
-HW device:
-==========
-
-HW device - de/constructs the HW Interface, the MGMT components on the
-initialization of the driver and the IO components on the case of Interface
-UP/DOWN Events. (hinic_hw_dev.c, hinic_hw_dev.h)
-
-
-hinic_dev contains the following components:
-===============================================
-
-PCI ID table - Contains the supported PCI Vendor/Device IDs.
-(hinic_pci_tbl.h)
-
-Port Commands - Send commands to the HW device for port management
-(MAC, Vlan, MTU, ...). (hinic_port.c, hinic_port.h)
-
-Tx Queues - Logical Tx Queues that use the HW Send Queues for transmit.
-The Logical Tx queue is not dependent on the format of the HW Send Queue.
-(hinic_tx.c, hinic_tx.h)
-
-Rx Queues - Logical Rx Queues that use the HW Receive Queues for receive.
-The Logical Rx queue is not dependent on the format of the HW Receive Queue.
-(hinic_rx.c, hinic_rx.h)
-
-hinic_dev - de/constructs the Logical Tx and Rx Queues.
-(hinic_main.c, hinic_dev.h)
-
-
-Miscellaneous:
-=============
-
-Common functions that are used by HW and Logical Device.
-(hinic_common.c, hinic_common.h)
-
-
-Support
-=======
-
-If an issue is identified with the released source code on the supported kernel
-with a supported adapter, email the specific information related to the issue to
-aviad.krawczyk@huawei.com.
diff --git a/Documentation/networking/ila.rst b/Documentation/networking/ila.rst
new file mode 100644 (file)
index 0000000..5ac0a62
--- /dev/null
@@ -0,0 +1,296 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================================
+Identifier Locator Addressing (ILA)
+===================================
+
+
+Introduction
+============
+
+Identifier-locator addressing (ILA) is a technique used with IPv6 that
+differentiates between location and identity of a network node. Part of an
+address expresses the immutable identity of the node, and another part
+indicates the location of the node which can be dynamic. Identifier-locator
+addressing can be used to efficiently implement overlay networks for
+network virtualization as well as solutions for use cases in mobility.
+
+ILA can be thought of as means to implement an overlay network without
+encapsulation. This is accomplished by performing network address
+translation on destination addresses as a packet traverses a network. To
+the network, an ILA translated packet appears to be no different than any
+other IPv6 packet. For instance, if the transport protocol is TCP then an
+ILA translated packet looks like just another TCP/IPv6 packet. The
+advantage of this is that ILA is transparent to the network so that
+optimizations in the network, such as ECMP, RSS, GRO, GSO, etc., just work.
+
+The ILA protocol is described in Internet-Draft draft-herbert-intarea-ila.
+
+
+ILA terminology
+===============
+
+  - Identifier
+               A number that identifies an addressable node in the network
+               independent of its location. ILA identifiers are sixty-four
+               bit values.
+
+  - Locator
+               A network prefix that routes to a physical host. Locators
+               provide the topological location of an addressed node. ILA
+               locators are sixty-four bit prefixes.
+
+  - ILA mapping
+               A mapping of an ILA identifier to a locator (or to a
+               locator and meta data). An ILA domain maintains a database
+               that contains mappings for all destinations in the domain.
+
+  - SIR address
+               An IPv6 address composed of a SIR prefix (upper sixty-
+               four bits) and an identifier (lower sixty-four bits).
+               SIR addresses are visible to applications and provide a
+               means for them to address nodes independent of their
+               location.
+
+  - ILA address
+               An IPv6 address composed of a locator (upper sixty-four
+               bits) and an identifier (low order sixty-four bits). ILA
+               addresses are never visible to an application.
+
+  - ILA host
+               An end host that is capable of performing ILA translations
+               on transmit or receive.
+
+  - ILA router
+               A network node that performs ILA translation and forwarding
+               of translated packets.
+
+  - ILA forwarding cache
+               A type of ILA router that only maintains a working set
+               cache of mappings.
+
+  - ILA node
+               A network node capable of performing ILA translations. This
+               can be an ILA router, ILA forwarding cache, or ILA host.
+
+
+Operation
+=========
+
+There are two fundamental operations with ILA:
+
+  - Translate a SIR address to an ILA address. This is performed on ingress
+    to an ILA overlay.
+
+  - Translate an ILA address to a SIR address. This is performed on egress
+    from the ILA overlay.
+
+ILA can be deployed either on end hosts or intermediate devices in the
+network; these are provided by "ILA hosts" and "ILA routers" respectively.
+Configuration and datapath for these two points of deployment is somewhat
+different.
+
+The diagram below illustrates the flow of packets through ILA as well
+as showing ILA hosts and routers::
+
+    +--------+                                                +--------+
+    | Host A +-+                                         +--->| Host B |
+    |        | |              (2) ILA                   (')   |        |
+    +--------+ |            ...addressed....           (   )  +--------+
+              V  +---+--+  .  packet      .  +---+--+  (_)
+   (1) SIR     |  | ILA  |----->-------->---->| ILA  |   |   (3) SIR
+    addressed  +->|router|  .              .  |router|->-+    addressed
+    packet        +---+--+  .     IPv6     .  +---+--+        packet
+                  /        .    Network   .
+                 /         .              .   +--+-++--------+
+    +--------+   /          .              .   |ILA ||  Host  |
+    |  Host  +--+           .              .- -|host||        |
+    |        |              .              .   +--+-++--------+
+    +--------+              ................
+
+
+Transport checksum handling
+===========================
+
+When an address is translated by ILA, an encapsulated transport checksum
+that includes the translated address in a pseudo header may be rendered
+incorrect on the wire. This is a problem for intermediate devices,
+including checksum offload in NICs, that process the checksum. There are
+three options to deal with this:
+
+- no action    Allow the checksum to be incorrect on the wire. Before
+               a receiver verifies a checksum the ILA to SIR address
+               translation must be done.
+
+- adjust transport checksum
+               When ILA translation is performed the packet is parsed
+               and if a transport layer checksum is found then it is
+               adjusted to reflect the correct checksum per the
+               translated address.
+
+- checksum neutral mapping
+               When an address is translated the difference can be offset
+               elsewhere in a part of the packet that is covered by
+               the checksum. The low order sixteen bits of the identifier
+               are used. This method is preferred since it doesn't require
+               parsing a packet beyond the IP header and in most cases the
+               adjustment can be precomputed and saved with the mapping.
+
+Note that the checksum neutral adjustment affects the low order sixteen
+bits of the identifier. When ILA to SIR address translation is done on
+egress the low order bits are restored to the original value which
+restores the identifier as it was originally sent.
+
+
+Identifier types
+================
+
+ILA defines different types of identifiers for different use cases.
+
+The defined types are:
+
+      0: interface identifier
+
+      1: locally unique identifier
+
+      2: virtual networking identifier for IPv4 address
+
+      3: virtual networking identifier for IPv6 unicast address
+
+      4: virtual networking identifier for IPv6 multicast address
+
+      5: non-local address identifier
+
+In the current implementation of kernel ILA only locally unique identifiers
+(LUID) are supported. LUID allows for a generic, unformatted 64 bit
+identifier.
+
+
+Identifier formats
+==================
+
+Kernel ILA supports two optional fields in an identifier for formatting:
+"C-bit" and "identifier type". The presence of these fields is determined
+by configuration as demonstrated below.
+
+If the identifier type is present it occupies the three highest order
+bits of an identifier. The possible values are given in the above list.
+
+If the C-bit is present,  this is used as an indication that checksum
+neutral mapping has been done. The C-bit can only be set in an
+ILA address, never a SIR address.
+
+In the simplest format the identifier types, C-bit, and checksum
+adjustment value are not present so an identifier is considered an
+unstructured sixty-four bit value::
+
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                            Identifier                         |
+     +                                                               +
+     |                                                               |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+The checksum neutral adjustment may be configured to always be
+present using neutral-map-auto. In this case there is no C-bit, but the
+checksum adjustment is in the low order 16 bits. The identifier is
+still sixty-four bits::
+
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                            Identifier                         |
+     |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                               |  Checksum-neutral adjustment  |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+The C-bit may used to explicitly indicate that checksum neutral
+mapping has been applied to an ILA address. The format is::
+
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |     |C|                    Identifier                         |
+     |     +-+                       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                               |  Checksum-neutral adjustment  |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+The identifier type field may be present to indicate the identifier
+type. If it is not present then the type is inferred based on mapping
+configuration. The checksum neutral adjustment may automatically
+used with the identifier type as illustrated below::
+
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     | Type|                      Identifier                         |
+     +-+-+-+                         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                               |  Checksum-neutral adjustment  |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+If the identifier type and the C-bit can be present simultaneously so
+the identifier format would be::
+
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     | Type|C|                    Identifier                         |
+     +-+-+-+-+                       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+     |                               |  Checksum-neutral adjustment  |
+     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+Configuration
+=============
+
+There are two methods to configure ILA mappings. One is by using LWT routes
+and the other is ila_xlat (called from NFHOOK PREROUTING hook). ila_xlat
+is intended to be used in the receive path for ILA hosts .
+
+An ILA router has also been implemented in XDP. Description of that is
+outside the scope of this document.
+
+The usage of for ILA LWT routes is:
+
+ip route add DEST/128 encap ila LOC csum-mode MODE ident-type TYPE via ADDR
+
+Destination (DEST) can either be a SIR address (for an ILA host or ingress
+ILA router) or an ILA address (egress ILA router). LOC is the sixty-four
+bit locator (with format W:X:Y:Z) that overwrites the upper sixty-four
+bits of the destination address.  Checksum MODE is one of "no-action",
+"adj-transport", "neutral-map", and "neutral-map-auto". If neutral-map is
+set then the C-bit will be present. Identifier TYPE one of "luid" or
+"use-format." In the case of use-format, the identifier type field is
+present and the effective type is taken from that.
+
+The usage of ila_xlat is:
+
+ip ila add loc_match MATCH loc LOC csum-mode MODE ident-type TYPE
+
+MATCH indicates the incoming locator that must be matched to apply
+a the translaiton. LOC is the locator that overwrites the upper
+sixty-four bits of the destination address. MODE and TYPE have the
+same meanings as described above.
+
+
+Some examples
+=============
+
+::
+
+     # Configure an ILA route that uses checksum neutral mapping as well
+     # as type field. Note that the type field is set in the SIR address
+     # (the 2000 implies type is 1 which is LUID).
+     ip route add 3333:0:0:1:2000:0:1:87/128 encap ila 2001:0:87:0 \
+         csum-mode neutral-map ident-type use-format
+
+     # Configure an ILA LWT route that uses auto checksum neutral mapping
+     # (no C-bit) and configure identifier type to be LUID so that the
+     # identifier type field will not be present.
+     ip route add 3333:0:0:1:2000:0:2:87/128 encap ila 2001:0:87:1 \
+         csum-mode neutral-map-auto ident-type luid
+
+     ila_xlat configuration
+
+     # Configure an ILA to SIR mapping that matches a locator and overwrites
+     # it with a SIR address (3333:0:0:1 in this example). The C-bit and
+     # identifier field are used.
+     ip ila add loc_match 2001:0:119:0 loc 3333:0:0:1 \
+        csum-mode neutral-map-auto ident-type use-format
+
+     # Configure an ILA to SIR mapping where checksum neutral is automatically
+     # set without the C-bit and the identifier type is configured to be LUID
+     # so that the identifier type field is not present.
+     ip ila add loc_match 2001:0:119:0 loc 3333:0:0:1 \
+        csum-mode neutral-map-auto ident-type use-format
diff --git a/Documentation/networking/ila.txt b/Documentation/networking/ila.txt
deleted file mode 100644 (file)
index a17dac9..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-Identifier Locator Addressing (ILA)
-
-
-Introduction
-============
-
-Identifier-locator addressing (ILA) is a technique used with IPv6 that
-differentiates between location and identity of a network node. Part of an
-address expresses the immutable identity of the node, and another part
-indicates the location of the node which can be dynamic. Identifier-locator
-addressing can be used to efficiently implement overlay networks for
-network virtualization as well as solutions for use cases in mobility.
-
-ILA can be thought of as means to implement an overlay network without
-encapsulation. This is accomplished by performing network address
-translation on destination addresses as a packet traverses a network. To
-the network, an ILA translated packet appears to be no different than any
-other IPv6 packet. For instance, if the transport protocol is TCP then an
-ILA translated packet looks like just another TCP/IPv6 packet. The
-advantage of this is that ILA is transparent to the network so that
-optimizations in the network, such as ECMP, RSS, GRO, GSO, etc., just work.
-
-The ILA protocol is described in Internet-Draft draft-herbert-intarea-ila.
-
-
-ILA terminology
-===============
-
-  - Identifier A number that identifies an addressable node in the network
-               independent of its location. ILA identifiers are sixty-four
-               bit values.
-
-  - Locator    A network prefix that routes to a physical host. Locators
-               provide the topological location of an addressed node. ILA
-               locators are sixty-four bit prefixes.
-
-  - ILA mapping
-               A mapping of an ILA identifier to a locator (or to a
-               locator and meta data). An ILA domain maintains a database
-               that contains mappings for all destinations in the domain.
-
-  - SIR address
-               An IPv6 address composed of a SIR prefix (upper sixty-
-               four bits) and an identifier (lower sixty-four bits).
-               SIR addresses are visible to applications and provide a
-               means for them to address nodes independent of their
-               location.
-
-  - ILA address
-               An IPv6 address composed of a locator (upper sixty-four
-               bits) and an identifier (low order sixty-four bits). ILA
-               addresses are never visible to an application.
-
-  - ILA host   An end host that is capable of performing ILA translations
-               on transmit or receive.
-
-  - ILA router A network node that performs ILA translation and forwarding
-               of translated packets.
-
-  - ILA forwarding cache
-               A type of ILA router that only maintains a working set
-               cache of mappings.
-
-  - ILA node   A network node capable of performing ILA translations. This
-               can be an ILA router, ILA forwarding cache, or ILA host.
-
-
-Operation
-=========
-
-There are two fundamental operations with ILA:
-
-  - Translate a SIR address to an ILA address. This is performed on ingress
-    to an ILA overlay.
-
-  - Translate an ILA address to a SIR address. This is performed on egress
-    from the ILA overlay.
-
-ILA can be deployed either on end hosts or intermediate devices in the
-network; these are provided by "ILA hosts" and "ILA routers" respectively.
-Configuration and datapath for these two points of deployment is somewhat
-different.
-
-The diagram below illustrates the flow of packets through ILA as well
-as showing ILA hosts and routers.
-
-    +--------+                                                +--------+
-    | Host A +-+                                         +--->| Host B |
-    |        | |              (2) ILA                   (')   |        |
-    +--------+ |            ...addressed....           (   )  +--------+
-               V  +---+--+  .  packet      .  +---+--+  (_)
-   (1) SIR     |  | ILA  |----->-------->---->| ILA  |   |   (3) SIR
-    addressed  +->|router|  .              .  |router|->-+    addressed
-    packet        +---+--+  .     IPv6     .  +---+--+        packet
-                   /        .    Network   .
-                  /         .              .   +--+-++--------+
-    +--------+   /          .              .   |ILA ||  Host  |
-    |  Host  +--+           .              .- -|host||        |
-    |        |              .              .   +--+-++--------+
-    +--------+              ................
-
-
-Transport checksum handling
-===========================
-
-When an address is translated by ILA, an encapsulated transport checksum
-that includes the translated address in a pseudo header may be rendered
-incorrect on the wire. This is a problem for intermediate devices,
-including checksum offload in NICs, that process the checksum. There are
-three options to deal with this:
-
-- no action    Allow the checksum to be incorrect on the wire. Before
-               a receiver verifies a checksum the ILA to SIR address
-               translation must be done.
-
-- adjust transport checksum
-               When ILA translation is performed the packet is parsed
-               and if a transport layer checksum is found then it is
-               adjusted to reflect the correct checksum per the
-               translated address.
-
-- checksum neutral mapping
-               When an address is translated the difference can be offset
-               elsewhere in a part of the packet that is covered by
-               the checksum. The low order sixteen bits of the identifier
-               are used. This method is preferred since it doesn't require
-               parsing a packet beyond the IP header and in most cases the
-               adjustment can be precomputed and saved with the mapping.
-
-Note that the checksum neutral adjustment affects the low order sixteen
-bits of the identifier. When ILA to SIR address translation is done on
-egress the low order bits are restored to the original value which
-restores the identifier as it was originally sent.
-
-
-Identifier types
-================
-
-ILA defines different types of identifiers for different use cases.
-
-The defined types are:
-
-      0: interface identifier
-
-      1: locally unique identifier
-
-      2: virtual networking identifier for IPv4 address
-
-      3: virtual networking identifier for IPv6 unicast address
-
-      4: virtual networking identifier for IPv6 multicast address
-
-      5: non-local address identifier
-
-In the current implementation of kernel ILA only locally unique identifiers
-(LUID) are supported. LUID allows for a generic, unformatted 64 bit
-identifier.
-
-
-Identifier formats
-==================
-
-Kernel ILA supports two optional fields in an identifier for formatting:
-"C-bit" and "identifier type". The presence of these fields is determined
-by configuration as demonstrated below.
-
-If the identifier type is present it occupies the three highest order
-bits of an identifier. The possible values are given in the above list.
-
-If the C-bit is present,  this is used as an indication that checksum
-neutral mapping has been done. The C-bit can only be set in an
-ILA address, never a SIR address.
-
-In the simplest format the identifier types, C-bit, and checksum
-adjustment value are not present so an identifier is considered an
-unstructured sixty-four bit value.
-
-     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-     |                            Identifier                         |
-     +                                                               +
-     |                                                               |
-     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-The checksum neutral adjustment may be configured to always be
-present using neutral-map-auto. In this case there is no C-bit, but the
-checksum adjustment is in the low order 16 bits. The identifier is
-still sixty-four bits.
-
-     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-     |                            Identifier                         |
-     |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-     |                               |  Checksum-neutral adjustment  |
-     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-The C-bit may used to explicitly indicate that checksum neutral
-mapping has been applied to an ILA address. The format is:
-
-     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-     |     |C|                    Identifier                         |
-     |     +-+                       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-     |                               |  Checksum-neutral adjustment  |
-     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-The identifier type field may be present to indicate the identifier
-type. If it is not present then the type is inferred based on mapping
-configuration. The checksum neutral adjustment may automatically
-used with the identifier type as illustrated below.
-
-     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-     | Type|                      Identifier                         |
-     +-+-+-+                         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-     |                               |  Checksum-neutral adjustment  |
-     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-If the identifier type and the C-bit can be present simultaneously so
-the identifier format would be:
-
-     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-     | Type|C|                    Identifier                         |
-     +-+-+-+-+                       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-     |                               |  Checksum-neutral adjustment  |
-     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-Configuration
-=============
-
-There are two methods to configure ILA mappings. One is by using LWT routes
-and the other is ila_xlat (called from NFHOOK PREROUTING hook). ila_xlat
-is intended to be used in the receive path for ILA hosts .
-
-An ILA router has also been implemented in XDP. Description of that is
-outside the scope of this document.
-
-The usage of for ILA LWT routes is:
-
-ip route add DEST/128 encap ila LOC csum-mode MODE ident-type TYPE via ADDR
-
-Destination (DEST) can either be a SIR address (for an ILA host or ingress
-ILA router) or an ILA address (egress ILA router). LOC is the sixty-four
-bit locator (with format W:X:Y:Z) that overwrites the upper sixty-four
-bits of the destination address.  Checksum MODE is one of "no-action",
-"adj-transport", "neutral-map", and "neutral-map-auto". If neutral-map is
-set then the C-bit will be present. Identifier TYPE one of "luid" or
-"use-format." In the case of use-format, the identifier type field is
-present and the effective type is taken from that.
-
-The usage of ila_xlat is:
-
-ip ila add loc_match MATCH loc LOC csum-mode MODE ident-type TYPE
-
-MATCH indicates the incoming locator that must be matched to apply
-a the translaiton. LOC is the locator that overwrites the upper
-sixty-four bits of the destination address. MODE and TYPE have the
-same meanings as described above.
-
-
-Some examples
-=============
-
-# Configure an ILA route that uses checksum neutral mapping as well
-# as type field. Note that the type field is set in the SIR address
-# (the 2000 implies type is 1 which is LUID).
-ip route add 3333:0:0:1:2000:0:1:87/128 encap ila 2001:0:87:0 \
-     csum-mode neutral-map ident-type use-format
-
-# Configure an ILA LWT route that uses auto checksum neutral mapping
-# (no C-bit) and configure identifier type to be LUID so that the
-# identifier type field will not be present.
-ip route add 3333:0:0:1:2000:0:2:87/128 encap ila 2001:0:87:1 \
-     csum-mode neutral-map-auto ident-type luid
-
-ila_xlat configuration
-
-# Configure an ILA to SIR mapping that matches a locator and overwrites
-# it with a SIR address (3333:0:0:1 in this example). The C-bit and
-# identifier field are used.
-ip ila add loc_match 2001:0:119:0 loc 3333:0:0:1 \
-    csum-mode neutral-map-auto ident-type use-format
-
-# Configure an ILA to SIR mapping where checksum neutral is automatically
-# set without the C-bit and the identifier type is configured to be LUID
-# so that the identifier type field is not present.
-ip ila add loc_match 2001:0:119:0 loc 3333:0:0:1 \
-    csum-mode neutral-map-auto ident-type use-format
index 50133d9..0186e27 100644 (file)
@@ -15,6 +15,7 @@ Contents:
    device_drivers/index
    dsa/index
    devlink/index
+   caif/index
    ethtool-netlink
    ieee802154
    j1939
@@ -22,7 +23,9 @@ Contents:
    z8530book
    msg_zerocopy
    failover
+   net_dim
    net_failover
+   page_pool
    phy
    sfp-phylink
    alias
@@ -35,6 +38,91 @@ Contents:
    tls-offload
    nfc
    6lowpan
+   6pack
+   altera_tse
+   arcnet-hardware
+   arcnet
+   atm
+   ax25
+   baycom
+   bonding
+   cdc_mbim
+   cops
+   cxacru
+   dccp
+   dctcp
+   decnet
+   defza
+   dns_resolver
+   driver
+   eql
+   fib_trie
+   filter
+   fore200e
+   framerelay
+   generic-hdlc
+   generic_netlink
+   gen_stats
+   gtp
+   hinic
+   ila
+   ipddp
+   ip_dynaddr
+   iphase
+   ipsec
+   ip-sysctl
+   ipv6
+   ipvlan
+   ipvs-sysctl
+   kcm
+   l2tp
+   lapb-module
+   ltpc
+   mac80211-injection
+   mpls-sysctl
+   multiqueue
+   netconsole
+   netdev-features
+   netdevices
+   netfilter-sysctl
+   netif-msg
+   nf_conntrack-sysctl
+   nf_flowtable
+   openvswitch
+   operstates
+   packet_mmap
+   phonet
+   pktgen
+   plip
+   ppp_generic
+   proc_net_tcp
+   radiotap-headers
+   ray_cs
+   rds
+   regulatory
+   rxrpc
+   sctp
+   secid
+   seg6-sysctl
+   skfp
+   strparser
+   switchdev
+   tc-actions-env-rules
+   tcp-thin
+   team
+   timestamping
+   tproxy
+   tuntap
+   udplite
+   vrf
+   vxlan
+   x25-iface
+   x25
+   xfrm_device
+   xfrm_proc
+   xfrm_sync
+   xfrm_sysctl
+   z8530drv
 
 .. only::  subproject and html
 
diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst
new file mode 100644 (file)
index 0000000..b72f89d
--- /dev/null
@@ -0,0 +1,2657 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========
+IP Sysctl
+=========
+
+/proc/sys/net/ipv4/* Variables
+==============================
+
+ip_forward - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       Forward Packets between interfaces.
+
+       This variable is special, its change resets all configuration
+       parameters to their default state (RFC1122 for hosts, RFC1812
+       for routers)
+
+ip_default_ttl - INTEGER
+       Default value of TTL field (Time To Live) for outgoing (but not
+       forwarded) IP packets. Should be between 1 and 255 inclusive.
+       Default: 64 (as recommended by RFC1700)
+
+ip_no_pmtu_disc - INTEGER
+       Disable Path MTU Discovery. If enabled in mode 1 and a
+       fragmentation-required ICMP is received, the PMTU to this
+       destination will be set to min_pmtu (see below). You will need
+       to raise min_pmtu to the smallest interface MTU on your system
+       manually if you want to avoid locally generated fragments.
+
+       In mode 2 incoming Path MTU Discovery messages will be
+       discarded. Outgoing frames are handled the same as in mode 1,
+       implicitly setting IP_PMTUDISC_DONT on every created socket.
+
+       Mode 3 is a hardened pmtu discover mode. The kernel will only
+       accept fragmentation-needed errors if the underlying protocol
+       can verify them besides a plain socket lookup. Current
+       protocols for which pmtu events will be honored are TCP, SCTP
+       and DCCP as they verify e.g. the sequence number or the
+       association. This mode should not be enabled globally but is
+       only intended to secure e.g. name servers in namespaces where
+       TCP path mtu must still work but path MTU information of other
+       protocols should be discarded. If enabled globally this mode
+       could break other protocols.
+
+       Possible values: 0-3
+
+       Default: FALSE
+
+min_pmtu - INTEGER
+       default 552 - minimum discovered Path MTU
+
+ip_forward_use_pmtu - BOOLEAN
+       By default we don't trust protocol path MTUs while forwarding
+       because they could be easily forged and can lead to unwanted
+       fragmentation by the router.
+       You only need to enable this if you have user-space software
+       which tries to discover path mtus by itself and depends on the
+       kernel honoring this information. This is normally not the
+       case.
+
+       Default: 0 (disabled)
+
+       Possible values:
+
+       - 0 - disabled
+       - 1 - enabled
+
+fwmark_reflect - BOOLEAN
+       Controls the fwmark of kernel-generated IPv4 reply packets that are not
+       associated with a socket for example, TCP RSTs or ICMP echo replies).
+       If unset, these packets have a fwmark of zero. If set, they have the
+       fwmark of the packet they are replying to.
+
+       Default: 0
+
+fib_multipath_use_neigh - BOOLEAN
+       Use status of existing neighbor entry when determining nexthop for
+       multipath routes. If disabled, neighbor information is not used and
+       packets could be directed to a failed nexthop. Only valid for kernels
+       built with CONFIG_IP_ROUTE_MULTIPATH enabled.
+
+       Default: 0 (disabled)
+
+       Possible values:
+
+       - 0 - disabled
+       - 1 - enabled
+
+fib_multipath_hash_policy - INTEGER
+       Controls which hash policy to use for multipath routes. Only valid
+       for kernels built with CONFIG_IP_ROUTE_MULTIPATH enabled.
+
+       Default: 0 (Layer 3)
+
+       Possible values:
+
+       - 0 - Layer 3
+       - 1 - Layer 4
+       - 2 - Layer 3 or inner Layer 3 if present
+
+fib_sync_mem - UNSIGNED INTEGER
+       Amount of dirty memory from fib entries that can be backlogged before
+       synchronize_rcu is forced.
+
+       Default: 512kB   Minimum: 64kB   Maximum: 64MB
+
+ip_forward_update_priority - INTEGER
+       Whether to update SKB priority from "TOS" field in IPv4 header after it
+       is forwarded. The new SKB priority is mapped from TOS field value
+       according to an rt_tos2priority table (see e.g. man tc-prio).
+
+       Default: 1 (Update priority.)
+
+       Possible values:
+
+       - 0 - Do not update priority.
+       - 1 - Update priority.
+
+route/max_size - INTEGER
+       Maximum number of routes allowed in the kernel.  Increase
+       this when using large numbers of interfaces and/or routes.
+
+       From linux kernel 3.6 onwards, this is deprecated for ipv4
+       as route cache is no longer used.
+
+neigh/default/gc_thresh1 - INTEGER
+       Minimum number of entries to keep.  Garbage collector will not
+       purge entries if there are fewer than this number.
+
+       Default: 128
+
+neigh/default/gc_thresh2 - INTEGER
+       Threshold when garbage collector becomes more aggressive about
+       purging entries. Entries older than 5 seconds will be cleared
+       when over this number.
+
+       Default: 512
+
+neigh/default/gc_thresh3 - INTEGER
+       Maximum number of non-PERMANENT neighbor entries allowed.  Increase
+       this when using large numbers of interfaces and when communicating
+       with large numbers of directly-connected peers.
+
+       Default: 1024
+
+neigh/default/unres_qlen_bytes - INTEGER
+       The maximum number of bytes which may be used by packets
+       queued for each unresolved address by other network layers.
+       (added in linux 3.3)
+
+       Setting negative value is meaningless and will return error.
+
+       Default: SK_WMEM_MAX, (same as net.core.wmem_default).
+
+               Exact value depends on architecture and kernel options,
+               but should be enough to allow queuing 256 packets
+               of medium size.
+
+neigh/default/unres_qlen - INTEGER
+       The maximum number of packets which may be queued for each
+       unresolved address by other network layers.
+
+       (deprecated in linux 3.3) : use unres_qlen_bytes instead.
+
+       Prior to linux 3.3, the default value is 3 which may cause
+       unexpected packet loss. The current default value is calculated
+       according to default value of unres_qlen_bytes and true size of
+       packet.
+
+       Default: 101
+
+mtu_expires - INTEGER
+       Time, in seconds, that cached PMTU information is kept.
+
+min_adv_mss - INTEGER
+       The advertised MSS depends on the first hop route MTU, but will
+       never be lower than this setting.
+
+IP Fragmentation:
+
+ipfrag_high_thresh - LONG INTEGER
+       Maximum memory used to reassemble IP fragments.
+
+ipfrag_low_thresh - LONG INTEGER
+       (Obsolete since linux-4.17)
+       Maximum memory used to reassemble IP fragments before the kernel
+       begins to remove incomplete fragment queues to free up resources.
+       The kernel still accepts new fragments for defragmentation.
+
+ipfrag_time - INTEGER
+       Time in seconds to keep an IP fragment in memory.
+
+ipfrag_max_dist - INTEGER
+       ipfrag_max_dist is a non-negative integer value which defines the
+       maximum "disorder" which is allowed among fragments which share a
+       common IP source address. Note that reordering of packets is
+       not unusual, but if a large number of fragments arrive from a source
+       IP address while a particular fragment queue remains incomplete, it
+       probably indicates that one or more fragments belonging to that queue
+       have been lost. When ipfrag_max_dist is positive, an additional check
+       is done on fragments before they are added to a reassembly queue - if
+       ipfrag_max_dist (or more) fragments have arrived from a particular IP
+       address between additions to any IP fragment queue using that source
+       address, it's presumed that one or more fragments in the queue are
+       lost. The existing fragment queue will be dropped, and a new one
+       started. An ipfrag_max_dist value of zero disables this check.
+
+       Using a very small value, e.g. 1 or 2, for ipfrag_max_dist can
+       result in unnecessarily dropping fragment queues when normal
+       reordering of packets occurs, which could lead to poor application
+       performance. Using a very large value, e.g. 50000, increases the
+       likelihood of incorrectly reassembling IP fragments that originate
+       from different IP datagrams, which could result in data corruption.
+       Default: 64
+
+INET peer storage
+=================
+
+inet_peer_threshold - INTEGER
+       The approximate size of the storage.  Starting from this threshold
+       entries will be thrown aggressively.  This threshold also determines
+       entries' time-to-live and time intervals between garbage collection
+       passes.  More entries, less time-to-live, less GC interval.
+
+inet_peer_minttl - INTEGER
+       Minimum time-to-live of entries.  Should be enough to cover fragment
+       time-to-live on the reassembling side.  This minimum time-to-live  is
+       guaranteed if the pool size is less than inet_peer_threshold.
+       Measured in seconds.
+
+inet_peer_maxttl - INTEGER
+       Maximum time-to-live of entries.  Unused entries will expire after
+       this period of time if there is no memory pressure on the pool (i.e.
+       when the number of entries in the pool is very small).
+       Measured in seconds.
+
+TCP variables
+=============
+
+somaxconn - INTEGER
+       Limit of socket listen() backlog, known in userspace as SOMAXCONN.
+       Defaults to 4096. (Was 128 before linux-5.4)
+       See also tcp_max_syn_backlog for additional tuning for TCP sockets.
+
+tcp_abort_on_overflow - BOOLEAN
+       If listening service is too slow to accept new connections,
+       reset them. Default state is FALSE. It means that if overflow
+       occurred due to a burst, connection will recover. Enable this
+       option _only_ if you are really sure that listening daemon
+       cannot be tuned to accept connections faster. Enabling this
+       option can harm clients of your server.
+
+tcp_adv_win_scale - INTEGER
+       Count buffering overhead as bytes/2^tcp_adv_win_scale
+       (if tcp_adv_win_scale > 0) or bytes-bytes/2^(-tcp_adv_win_scale),
+       if it is <= 0.
+
+       Possible values are [-31, 31], inclusive.
+
+       Default: 1
+
+tcp_allowed_congestion_control - STRING
+       Show/set the congestion control choices available to non-privileged
+       processes. The list is a subset of those listed in
+       tcp_available_congestion_control.
+
+       Default is "reno" and the default setting (tcp_congestion_control).
+
+tcp_app_win - INTEGER
+       Reserve max(window/2^tcp_app_win, mss) of window for application
+       buffer. Value 0 is special, it means that nothing is reserved.
+
+       Default: 31
+
+tcp_autocorking - BOOLEAN
+       Enable TCP auto corking :
+       When applications do consecutive small write()/sendmsg() system calls,
+       we try to coalesce these small writes as much as possible, to lower
+       total amount of sent packets. This is done if at least one prior
+       packet for the flow is waiting in Qdisc queues or device transmit
+       queue. Applications can still use TCP_CORK for optimal behavior
+       when they know how/when to uncork their sockets.
+
+       Default : 1
+
+tcp_available_congestion_control - STRING
+       Shows the available congestion control choices that are registered.
+       More congestion control algorithms may be available as modules,
+       but not loaded.
+
+tcp_base_mss - INTEGER
+       The initial value of search_low to be used by the packetization layer
+       Path MTU discovery (MTU probing).  If MTU probing is enabled,
+       this is the initial MSS used by the connection.
+
+tcp_mtu_probe_floor - INTEGER
+       If MTU probing is enabled this caps the minimum MSS used for search_low
+       for the connection.
+
+       Default : 48
+
+tcp_min_snd_mss - INTEGER
+       TCP SYN and SYNACK messages usually advertise an ADVMSS option,
+       as described in RFC 1122 and RFC 6691.
+
+       If this ADVMSS option is smaller than tcp_min_snd_mss,
+       it is silently capped to tcp_min_snd_mss.
+
+       Default : 48 (at least 8 bytes of payload per segment)
+
+tcp_congestion_control - STRING
+       Set the congestion control algorithm to be used for new
+       connections. The algorithm "reno" is always available, but
+       additional choices may be available based on kernel configuration.
+       Default is set as part of kernel configuration.
+       For passive connections, the listener congestion control choice
+       is inherited.
+
+       [see setsockopt(listenfd, SOL_TCP, TCP_CONGESTION, "name" ...) ]
+
+tcp_dsack - BOOLEAN
+       Allows TCP to send "duplicate" SACKs.
+
+tcp_early_retrans - INTEGER
+       Tail loss probe (TLP) converts RTOs occurring due to tail
+       losses into fast recovery (draft-ietf-tcpm-rack). Note that
+       TLP requires RACK to function properly (see tcp_recovery below)
+
+       Possible values:
+
+               - 0 disables TLP
+               - 3 or 4 enables TLP
+
+       Default: 3
+
+tcp_ecn - INTEGER
+       Control use of Explicit Congestion Notification (ECN) by TCP.
+       ECN is used only when both ends of the TCP connection indicate
+       support for it.  This feature is useful in avoiding losses due
+       to congestion by allowing supporting routers to signal
+       congestion before having to drop packets.
+
+       Possible values are:
+
+               =  =====================================================
+               0  Disable ECN.  Neither initiate nor accept ECN.
+               1  Enable ECN when requested by incoming connections and
+                  also request ECN on outgoing connection attempts.
+               2  Enable ECN when requested by incoming connections
+                  but do not request ECN on outgoing connections.
+               =  =====================================================
+
+       Default: 2
+
+tcp_ecn_fallback - BOOLEAN
+       If the kernel detects that ECN connection misbehaves, enable fall
+       back to non-ECN. Currently, this knob implements the fallback
+       from RFC3168, section 6.1.1.1., but we reserve that in future,
+       additional detection mechanisms could be implemented under this
+       knob. The value is not used, if tcp_ecn or per route (or congestion
+       control) ECN settings are disabled.
+
+       Default: 1 (fallback enabled)
+
+tcp_fack - BOOLEAN
+       This is a legacy option, it has no effect anymore.
+
+tcp_fin_timeout - INTEGER
+       The length of time an orphaned (no longer referenced by any
+       application) connection will remain in the FIN_WAIT_2 state
+       before it is aborted at the local end.  While a perfectly
+       valid "receive only" state for an un-orphaned connection, an
+       orphaned connection in FIN_WAIT_2 state could otherwise wait
+       forever for the remote to close its end of the connection.
+
+       Cf. tcp_max_orphans
+
+       Default: 60 seconds
+
+tcp_frto - INTEGER
+       Enables Forward RTO-Recovery (F-RTO) defined in RFC5682.
+       F-RTO is an enhanced recovery algorithm for TCP retransmission
+       timeouts.  It is particularly beneficial in networks where the
+       RTT fluctuates (e.g., wireless). F-RTO is sender-side only
+       modification. It does not require any support from the peer.
+
+       By default it's enabled with a non-zero value. 0 disables F-RTO.
+
+tcp_fwmark_accept - BOOLEAN
+       If set, incoming connections to listening sockets that do not have a
+       socket mark will set the mark of the accepting socket to the fwmark of
+       the incoming SYN packet. This will cause all packets on that connection
+       (starting from the first SYNACK) to be sent with that fwmark. The
+       listening socket's mark is unchanged. Listening sockets that already
+       have a fwmark set via setsockopt(SOL_SOCKET, SO_MARK, ...) are
+       unaffected.
+
+       Default: 0
+
+tcp_invalid_ratelimit - INTEGER
+       Limit the maximal rate for sending duplicate acknowledgments
+       in response to incoming TCP packets that are for an existing
+       connection but that are invalid due to any of these reasons:
+
+         (a) out-of-window sequence number,
+         (b) out-of-window acknowledgment number, or
+         (c) PAWS (Protection Against Wrapped Sequence numbers) check failure
+
+       This can help mitigate simple "ack loop" DoS attacks, wherein
+       a buggy or malicious middlebox or man-in-the-middle can
+       rewrite TCP header fields in manner that causes each endpoint
+       to think that the other is sending invalid TCP segments, thus
+       causing each side to send an unterminating stream of duplicate
+       acknowledgments for invalid segments.
+
+       Using 0 disables rate-limiting of dupacks in response to
+       invalid segments; otherwise this value specifies the minimal
+       space between sending such dupacks, in milliseconds.
+
+       Default: 500 (milliseconds).
+
+tcp_keepalive_time - INTEGER
+       How often TCP sends out keepalive messages when keepalive is enabled.
+       Default: 2hours.
+
+tcp_keepalive_probes - INTEGER
+       How many keepalive probes TCP sends out, until it decides that the
+       connection is broken. Default value: 9.
+
+tcp_keepalive_intvl - INTEGER
+       How frequently the probes are send out. Multiplied by
+       tcp_keepalive_probes it is time to kill not responding connection,
+       after probes started. Default value: 75sec i.e. connection
+       will be aborted after ~11 minutes of retries.
+
+tcp_l3mdev_accept - BOOLEAN
+       Enables child sockets to inherit the L3 master device index.
+       Enabling this option allows a "global" listen socket to work
+       across L3 master domains (e.g., VRFs) with connected sockets
+       derived from the listen socket to be bound to the L3 domain in
+       which the packets originated. Only valid when the kernel was
+       compiled with CONFIG_NET_L3_MASTER_DEV.
+
+       Default: 0 (disabled)
+
+tcp_low_latency - BOOLEAN
+       This is a legacy option, it has no effect anymore.
+
+tcp_max_orphans - INTEGER
+       Maximal number of TCP sockets not attached to any user file handle,
+       held by system. If this number is exceeded orphaned connections are
+       reset immediately and warning is printed. This limit exists
+       only to prevent simple DoS attacks, you _must_ not rely on this
+       or lower the limit artificially, but rather increase it
+       (probably, after increasing installed memory),
+       if network conditions require more than default value,
+       and tune network services to linger and kill such states
+       more aggressively. Let me to remind again: each orphan eats
+       up to ~64K of unswappable memory.
+
+tcp_max_syn_backlog - INTEGER
+       Maximal number of remembered connection requests (SYN_RECV),
+       which have not received an acknowledgment from connecting client.
+
+       This is a per-listener limit.
+
+       The minimal value is 128 for low memory machines, and it will
+       increase in proportion to the memory of machine.
+
+       If server suffers from overload, try increasing this number.
+
+       Remember to also check /proc/sys/net/core/somaxconn
+       A SYN_RECV request socket consumes about 304 bytes of memory.
+
+tcp_max_tw_buckets - INTEGER
+       Maximal number of timewait sockets held by system simultaneously.
+       If this number is exceeded time-wait socket is immediately destroyed
+       and warning is printed. This limit exists only to prevent
+       simple DoS attacks, you _must_ not lower the limit artificially,
+       but rather increase it (probably, after increasing installed memory),
+       if network conditions require more than default value.
+
+tcp_mem - vector of 3 INTEGERs: min, pressure, max
+       min: below this number of pages TCP is not bothered about its
+       memory appetite.
+
+       pressure: when amount of memory allocated by TCP exceeds this number
+       of pages, TCP moderates its memory consumption and enters memory
+       pressure mode, which is exited when memory consumption falls
+       under "min".
+
+       max: number of pages allowed for queueing by all TCP sockets.
+
+       Defaults are calculated at boot time from amount of available
+       memory.
+
+tcp_min_rtt_wlen - INTEGER
+       The window length of the windowed min filter to track the minimum RTT.
+       A shorter window lets a flow more quickly pick up new (higher)
+       minimum RTT when it is moved to a longer path (e.g., due to traffic
+       engineering). A longer window makes the filter more resistant to RTT
+       inflations such as transient congestion. The unit is seconds.
+
+       Possible values: 0 - 86400 (1 day)
+
+       Default: 300
+
+tcp_moderate_rcvbuf - BOOLEAN
+       If set, TCP performs receive buffer auto-tuning, attempting to
+       automatically size the buffer (no greater than tcp_rmem[2]) to
+       match the size required by the path for full throughput.  Enabled by
+       default.
+
+tcp_mtu_probing - INTEGER
+       Controls TCP Packetization-Layer Path MTU Discovery.  Takes three
+       values:
+
+       - 0 - Disabled
+       - 1 - Disabled by default, enabled when an ICMP black hole detected
+       - 2 - Always enabled, use initial MSS of tcp_base_mss.
+
+tcp_probe_interval - UNSIGNED INTEGER
+       Controls how often to start TCP Packetization-Layer Path MTU
+       Discovery reprobe. The default is reprobing every 10 minutes as
+       per RFC4821.
+
+tcp_probe_threshold - INTEGER
+       Controls when TCP Packetization-Layer Path MTU Discovery probing
+       will stop in respect to the width of search range in bytes. Default
+       is 8 bytes.
+
+tcp_no_metrics_save - BOOLEAN
+       By default, TCP saves various connection metrics in the route cache
+       when the connection closes, so that connections established in the
+       near future can use these to set initial conditions.  Usually, this
+       increases overall performance, but may sometimes cause performance
+       degradation.  If set, TCP will not cache metrics on closing
+       connections.
+
+tcp_no_ssthresh_metrics_save - BOOLEAN
+       Controls whether TCP saves ssthresh metrics in the route cache.
+
+       Default is 1, which disables ssthresh metrics.
+
+tcp_orphan_retries - INTEGER
+       This value influences the timeout of a locally closed TCP connection,
+       when RTO retransmissions remain unacknowledged.
+       See tcp_retries2 for more details.
+
+       The default value is 8.
+
+       If your machine is a loaded WEB server,
+       you should think about lowering this value, such sockets
+       may consume significant resources. Cf. tcp_max_orphans.
+
+tcp_recovery - INTEGER
+       This value is a bitmap to enable various experimental loss recovery
+       features.
+
+       =========   =============================================================
+       RACK: 0x1   enables the RACK loss detection for fast detection of lost
+                   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
+
+tcp_reordering - INTEGER
+       Initial reordering level of packets in a TCP stream.
+       TCP stack can then dynamically adjust flow reordering level
+       between this initial value and tcp_max_reordering
+
+       Default: 3
+
+tcp_max_reordering - INTEGER
+       Maximal reordering level of packets in a TCP stream.
+       300 is a fairly conservative value, but you might increase it
+       if paths are using per packet load balancing (like bonding rr mode)
+
+       Default: 300
+
+tcp_retrans_collapse - BOOLEAN
+       Bug-to-bug compatibility with some broken printers.
+       On retransmit try to send bigger packets to work around bugs in
+       certain TCP stacks.
+
+tcp_retries1 - INTEGER
+       This value influences the time, after which TCP decides, that
+       something is wrong due to unacknowledged RTO retransmissions,
+       and reports this suspicion to the network layer.
+       See tcp_retries2 for more details.
+
+       RFC 1122 recommends at least 3 retransmissions, which is the
+       default.
+
+tcp_retries2 - INTEGER
+       This value influences the timeout of an alive TCP connection,
+       when RTO retransmissions remain unacknowledged.
+       Given a value of N, a hypothetical TCP connection following
+       exponential backoff with an initial RTO of TCP_RTO_MIN would
+       retransmit N times before killing the connection at the (N+1)th RTO.
+
+       The default value of 15 yields a hypothetical timeout of 924.6
+       seconds and is a lower bound for the effective timeout.
+       TCP will effectively time out at the first RTO which exceeds the
+       hypothetical timeout.
+
+       RFC 1122 recommends at least 100 seconds for the timeout,
+       which corresponds to a value of at least 8.
+
+tcp_rfc1337 - BOOLEAN
+       If set, the TCP stack behaves conforming to RFC1337. If unset,
+       we are not conforming to RFC, but prevent TCP TIME_WAIT
+       assassination.
+
+       Default: 0
+
+tcp_rmem - vector of 3 INTEGERs: min, default, max
+       min: Minimal size of receive buffer used by TCP sockets.
+       It is guaranteed to each TCP socket, even under moderate memory
+       pressure.
+
+       Default: 4K
+
+       default: initial size of receive buffer used by TCP sockets.
+       This value overrides net.core.rmem_default used by other protocols.
+       Default: 87380 bytes. This value results in window of 65535 with
+       default setting of tcp_adv_win_scale and tcp_app_win:0 and a bit
+       less for default tcp_app_win. See below about these variables.
+
+       max: maximal size of receive buffer allowed for automatically
+       selected receiver buffers for TCP socket. This value does not override
+       net.core.rmem_max.  Calling setsockopt() with SO_RCVBUF disables
+       automatic tuning of that socket's receive buffer size, in which
+       case this value is ignored.
+       Default: between 87380B and 6MB, depending on RAM size.
+
+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_slack_ns - LONG INTEGER
+       This sysctl control the slack used when arming the
+       timer used by SACK compression. This gives extra time
+       for small RTT flows, and reduces system overhead by allowing
+       opportunistic reduction of timer interrupts.
+
+       Default : 100,000 ns (100 us)
+
+tcp_comp_sack_nr - INTEGER
+       Max number of SACK that can be compressed.
+       Using 0 disables SACK compression.
+
+       Default : 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
+       the current RTO.  If unset, the congestion window will not
+       be timed out after an idle period.
+
+       Default: 1
+
+tcp_stdurg - BOOLEAN
+       Use the Host requirements interpretation of the TCP urgent pointer field.
+       Most hosts use the older BSD interpretation, so if you turn this on
+       Linux might not communicate correctly with them.
+
+       Default: FALSE
+
+tcp_synack_retries - INTEGER
+       Number of times SYNACKs for a passive TCP connection attempt will
+       be retransmitted. Should not be higher than 255. Default value
+       is 5, which corresponds to 31seconds till the last retransmission
+       with the current initial RTO of 1second. With this the final timeout
+       for a passive TCP connection will happen after 63seconds.
+
+tcp_syncookies - INTEGER
+       Only valid when the kernel was compiled with CONFIG_SYN_COOKIES
+       Send out syncookies when the syn backlog queue of a socket
+       overflows. This is to prevent against the common 'SYN flood attack'
+       Default: 1
+
+       Note, that syncookies is fallback facility.
+       It MUST NOT be used to help highly loaded servers to stand
+       against legal connection rate. If you see SYN flood warnings
+       in your logs, but investigation shows that they occur
+       because of overload with legal connections, you should tune
+       another parameters until this warning disappear.
+       See: tcp_max_syn_backlog, tcp_synack_retries, tcp_abort_on_overflow.
+
+       syncookies seriously violate TCP protocol, do not allow
+       to use TCP extensions, can result in serious degradation
+       of some services (f.e. SMTP relaying), visible not by you,
+       but your clients and relays, contacting you. While you see
+       SYN flood warnings in logs not being really flooded, your server
+       is seriously misconfigured.
+
+       If you want to test which effects syncookies have to your
+       network connections you can set this knob to 2 to enable
+       unconditionally generation of syncookies.
+
+tcp_fastopen - INTEGER
+       Enable TCP Fast Open (RFC7413) to send and accept data in the opening
+       SYN packet.
+
+       The client support is enabled by flag 0x1 (on by default). The client
+       then must use sendmsg() or sendto() with the MSG_FASTOPEN flag,
+       rather than connect() to send data in SYN.
+
+       The server support is enabled by flag 0x2 (off by default). Then
+       either enable for all listeners with another flag (0x400) or
+       enable individual listeners via TCP_FASTOPEN socket option with
+       the option value being the length of the syn-data backlog.
+
+       The values (bitmap) are
+
+       =====  ======== ======================================================
+         0x1  (client) enables sending data in the opening SYN on the client.
+         0x2  (server) enables the server support, i.e., allowing data in
+                       a SYN packet to be accepted and passed to the
+                       application before 3-way handshake finishes.
+         0x4  (client) send data in the opening SYN regardless of cookie
+                       availability and without a cookie option.
+       0x200  (server) accept data-in-SYN w/o any cookie option present.
+       0x400  (server) enable all listeners to support Fast Open by
+                       default without explicit TCP_FASTOPEN socket option.
+       =====  ======== ======================================================
+
+       Default: 0x1
+
+       Note that that additional client or server features are only
+       effective if the basic support (0x1 and 0x2) are enabled respectively.
+
+tcp_fastopen_blackhole_timeout_sec - INTEGER
+       Initial time period in second to disable Fastopen on active TCP sockets
+       when a TFO firewall blackhole issue happens.
+       This time period will grow exponentially when more blackhole issues
+       get detected right after Fastopen is re-enabled and will reset to
+       initial value when the blackhole issue goes away.
+       0 to disable the blackhole detection.
+
+       By default, it is set to 1hr.
+
+tcp_fastopen_key - list of comma separated 32-digit hexadecimal INTEGERs
+       The list consists of a primary key and an optional backup key. The
+       primary key is used for both creating and validating cookies, while the
+       optional backup key is only used for validating cookies. The purpose of
+       the backup key is to maximize TFO validation when keys are rotated.
+
+       A randomly chosen primary key may be configured by the kernel if
+       the tcp_fastopen sysctl is set to 0x400 (see above), or if the
+       TCP_FASTOPEN setsockopt() optname is set and a key has not been
+       previously configured via sysctl. If keys are configured via
+       setsockopt() by using the TCP_FASTOPEN_KEY optname, then those
+       per-socket keys will be used instead of any keys that are specified via
+       sysctl.
+
+       A key is specified as 4 8-digit hexadecimal integers which are separated
+       by a '-' as: xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx. Leading zeros may be
+       omitted. A primary and a backup key may be specified by separating them
+       by a comma. If only one key is specified, it becomes the primary key and
+       any previously configured backup keys are removed.
+
+tcp_syn_retries - INTEGER
+       Number of times initial SYNs for an active TCP connection attempt
+       will be retransmitted. Should not be higher than 127. Default value
+       is 6, which corresponds to 63seconds till the last retransmission
+       with the current initial RTO of 1second. With this the final timeout
+       for an active TCP connection attempt will happen after 127seconds.
+
+tcp_timestamps - INTEGER
+       Enable timestamps as defined in RFC1323.
+
+       - 0: Disabled.
+       - 1: Enable timestamps as defined in RFC1323 and use random offset for
+         each connection rather than only using the current time.
+       - 2: Like 1, but without random offsets.
+
+       Default: 1
+
+tcp_min_tso_segs - INTEGER
+       Minimal number of segments per TSO frame.
+
+       Since linux-3.12, TCP does an automatic sizing of TSO frames,
+       depending on flow rate, instead of filling 64Kbytes packets.
+       For specific usages, it's possible to force TCP to build big
+       TSO frames. Note that TCP stack might split too big TSO packets
+       if available window is too small.
+
+       Default: 2
+
+tcp_pacing_ss_ratio - INTEGER
+       sk->sk_pacing_rate is set by TCP stack using a ratio applied
+       to current rate. (current_rate = cwnd * mss / srtt)
+       If TCP is in slow start, tcp_pacing_ss_ratio is applied
+       to let TCP probe for bigger speeds, assuming cwnd can be
+       doubled every other RTT.
+
+       Default: 200
+
+tcp_pacing_ca_ratio - INTEGER
+       sk->sk_pacing_rate is set by TCP stack using a ratio applied
+       to current rate. (current_rate = cwnd * mss / srtt)
+       If TCP is in congestion avoidance phase, tcp_pacing_ca_ratio
+       is applied to conservatively probe for bigger throughput.
+
+       Default: 120
+
+tcp_tso_win_divisor - INTEGER
+       This allows control over what percentage of the congestion window
+       can be consumed by a single TSO frame.
+       The setting of this parameter is a choice between burstiness and
+       building larger TSO frames.
+
+       Default: 3
+
+tcp_tw_reuse - INTEGER
+       Enable reuse of TIME-WAIT sockets for new connections when it is
+       safe from protocol viewpoint.
+
+       - 0 - disable
+       - 1 - global enable
+       - 2 - enable for loopback traffic only
+
+       It should not be changed without advice/request of technical
+       experts.
+
+       Default: 2
+
+tcp_window_scaling - BOOLEAN
+       Enable window scaling as defined in RFC1323.
+
+tcp_wmem - vector of 3 INTEGERs: min, default, max
+       min: Amount of memory reserved for send buffers for TCP sockets.
+       Each TCP socket has rights to use it due to fact of its birth.
+
+       Default: 4K
+
+       default: initial size of send buffer used by TCP sockets.  This
+       value overrides net.core.wmem_default used by other protocols.
+
+       It is usually lower than net.core.wmem_default.
+
+       Default: 16K
+
+       max: Maximal amount of memory allowed for automatically tuned
+       send buffers for TCP sockets. This value does not override
+       net.core.wmem_max.  Calling setsockopt() with SO_SNDBUF disables
+       automatic tuning of that socket's send buffer size, in which case
+       this value is ignored.
+
+       Default: between 64K and 4MB, depending on RAM size.
+
+tcp_notsent_lowat - UNSIGNED INTEGER
+       A TCP socket can control the amount of unsent bytes in its write queue,
+       thanks to TCP_NOTSENT_LOWAT socket option. poll()/select()/epoll()
+       reports POLLOUT events if the amount of unsent bytes is below a per
+       socket value, and if the write queue is not full. sendmsg() will
+       also not add new buffers if the limit is hit.
+
+       This global variable controls the amount of unsent data for
+       sockets not using TCP_NOTSENT_LOWAT. For these sockets, a change
+       to the global variable has immediate effect.
+
+       Default: UINT_MAX (0xFFFFFFFF)
+
+tcp_workaround_signed_windows - BOOLEAN
+       If set, assume no receipt of a window scaling option means the
+       remote TCP is broken and treats the window as a signed quantity.
+       If unset, assume the remote TCP is not broken even if we do
+       not receive a window scaling option from them.
+
+       Default: 0
+
+tcp_thin_linear_timeouts - BOOLEAN
+       Enable dynamic triggering of linear timeouts for thin streams.
+       If set, a check is performed upon retransmission by timeout to
+       determine if the stream is thin (less than 4 packets in flight).
+       As long as the stream is found to be thin, up to 6 linear
+       timeouts may be performed before exponential backoff mode is
+       initiated. This improves retransmission latency for
+       non-aggressive thin streams, often found to be time-dependent.
+       For more information on thin streams, see
+       Documentation/networking/tcp-thin.rst
+
+       Default: 0
+
+tcp_limit_output_bytes - INTEGER
+       Controls TCP Small Queue limit per tcp socket.
+       TCP bulk sender tends to increase packets in flight until it
+       gets losses notifications. With SNDBUF autotuning, this can
+       result in a large amount of packets queued on the local machine
+       (e.g.: qdiscs, CPU backlog, or device) hurting latency of other
+       flows, for typical pfifo_fast qdiscs.  tcp_limit_output_bytes
+       limits the number of bytes on qdisc or device to reduce artificial
+       RTT/cwnd and reduce bufferbloat.
+
+       Default: 1048576 (16 * 65536)
+
+tcp_challenge_ack_limit - INTEGER
+       Limits number of Challenge ACK sent per second, as recommended
+       in RFC 5961 (Improving TCP's Robustness to Blind In-Window Attacks)
+       Default: 1000
+
+tcp_rx_skb_cache - BOOLEAN
+       Controls a per TCP socket cache of one skb, that might help
+       performance of some workloads. This might be dangerous
+       on systems with a lot of TCP sockets, since it increases
+       memory usage.
+
+       Default: 0 (disabled)
+
+UDP variables
+=============
+
+udp_l3mdev_accept - BOOLEAN
+       Enabling this option allows a "global" bound socket to work
+       across L3 master domains (e.g., VRFs) with packets capable of
+       being received regardless of the L3 domain in which they
+       originated. Only valid when the kernel was compiled with
+       CONFIG_NET_L3_MASTER_DEV.
+
+       Default: 0 (disabled)
+
+udp_mem - vector of 3 INTEGERs: min, pressure, max
+       Number of pages allowed for queueing by all UDP sockets.
+
+       min: Below this number of pages UDP is not bothered about its
+       memory appetite. When amount of memory allocated by UDP exceeds
+       this number, UDP starts to moderate memory usage.
+
+       pressure: This value was introduced to follow format of tcp_mem.
+
+       max: Number of pages allowed for queueing by all UDP sockets.
+
+       Default is calculated at boot time from amount of available memory.
+
+udp_rmem_min - INTEGER
+       Minimal size of receive buffer used by UDP sockets in moderation.
+       Each UDP socket is able to use the size for receiving data, even if
+       total pages of UDP sockets exceed udp_mem pressure. The unit is byte.
+
+       Default: 4K
+
+udp_wmem_min - INTEGER
+       Minimal size of send buffer used by UDP sockets in moderation.
+       Each UDP socket is able to use the size for sending data, even if
+       total pages of UDP sockets exceed udp_mem pressure. The unit is byte.
+
+       Default: 4K
+
+RAW variables
+=============
+
+raw_l3mdev_accept - BOOLEAN
+       Enabling this option allows a "global" bound socket to work
+       across L3 master domains (e.g., VRFs) with packets capable of
+       being received regardless of the L3 domain in which they
+       originated. Only valid when the kernel was compiled with
+       CONFIG_NET_L3_MASTER_DEV.
+
+       Default: 1 (enabled)
+
+CIPSOv4 Variables
+=================
+
+cipso_cache_enable - BOOLEAN
+       If set, enable additions to and lookups from the CIPSO label mapping
+       cache.  If unset, additions are ignored and lookups always result in a
+       miss.  However, regardless of the setting the cache is still
+       invalidated when required when means you can safely toggle this on and
+       off and the cache will always be "safe".
+
+       Default: 1
+
+cipso_cache_bucket_size - INTEGER
+       The CIPSO label cache consists of a fixed size hash table with each
+       hash bucket containing a number of cache entries.  This variable limits
+       the number of entries in each hash bucket; the larger the value the
+       more CIPSO label mappings that can be cached.  When the number of
+       entries in a given hash bucket reaches this limit adding new entries
+       causes the oldest entry in the bucket to be removed to make room.
+
+       Default: 10
+
+cipso_rbm_optfmt - BOOLEAN
+       Enable the "Optimized Tag 1 Format" as defined in section 3.4.2.6 of
+       the CIPSO draft specification (see Documentation/netlabel for details).
+       This means that when set the CIPSO tag will be padded with empty
+       categories in order to make the packet data 32-bit aligned.
+
+       Default: 0
+
+cipso_rbm_structvalid - BOOLEAN
+       If set, do a very strict check of the CIPSO option when
+       ip_options_compile() is called.  If unset, relax the checks done during
+       ip_options_compile().  Either way is "safe" as errors are caught else
+       where in the CIPSO processing code but setting this to 0 (False) should
+       result in less work (i.e. it should be faster) but could cause problems
+       with other implementations that require strict checking.
+
+       Default: 0
+
+IP Variables
+============
+
+ip_local_port_range - 2 INTEGERS
+       Defines the local port range that is used by TCP and UDP to
+       choose the local port. The first number is the first, the
+       second the last local port number.
+       If possible, it is better these numbers have different parity
+       (one even and one odd value).
+       Must be greater than or equal to ip_unprivileged_port_start.
+       The default values are 32768 and 60999 respectively.
+
+ip_local_reserved_ports - list of comma separated ranges
+       Specify the ports which are reserved for known third-party
+       applications. These ports will not be used by automatic port
+       assignments (e.g. when calling connect() or bind() with port
+       number 0). Explicit port allocation behavior is unchanged.
+
+       The format used for both input and output is a comma separated
+       list of ranges (e.g. "1,2-4,10-10" for ports 1, 2, 3, 4 and
+       10). Writing to the file will clear all previously reserved
+       ports and update the current list with the one given in the
+       input.
+
+       Note that ip_local_port_range and ip_local_reserved_ports
+       settings are independent and both are considered by the kernel
+       when determining which ports are available for automatic port
+       assignments.
+
+       You can reserve ports which are not in the current
+       ip_local_port_range, e.g.::
+
+           $ cat /proc/sys/net/ipv4/ip_local_port_range
+           32000       60999
+           $ cat /proc/sys/net/ipv4/ip_local_reserved_ports
+           8080,9148
+
+       although this is redundant. However such a setting is useful
+       if later the port range is changed to a value that will
+       include the reserved ports.
+
+       Default: Empty
+
+ip_unprivileged_port_start - INTEGER
+       This is a per-namespace sysctl.  It defines the first
+       unprivileged port in the network namespace.  Privileged ports
+       require root or CAP_NET_BIND_SERVICE in order to bind to them.
+       To disable all privileged ports, set this to 0.  They must not
+       overlap with the ip_local_port_range.
+
+       Default: 1024
+
+ip_nonlocal_bind - BOOLEAN
+       If set, allows processes to bind() to non-local IP addresses,
+       which can be quite useful - but may break some applications.
+
+       Default: 0
+
+ip_autobind_reuse - BOOLEAN
+       By default, bind() does not select the ports automatically even if
+       the new socket and all sockets bound to the port have SO_REUSEADDR.
+       ip_autobind_reuse allows bind() to reuse the port and this is useful
+       when you use bind()+connect(), but may break some applications.
+       The preferred solution is to use IP_BIND_ADDRESS_NO_PORT and this
+       option should only be set by experts.
+       Default: 0
+
+ip_dynaddr - BOOLEAN
+       If set non-zero, enables support for dynamic addresses.
+       If set to a non-zero value larger than 1, a kernel log
+       message will be printed when dynamic address rewriting
+       occurs.
+
+       Default: 0
+
+ip_early_demux - BOOLEAN
+       Optimize input packet processing down to one demux for
+       certain kinds of local sockets.  Currently we only do this
+       for established TCP and connected UDP sockets.
+
+       It may add an additional cost for pure routing workloads that
+       reduces overall throughput, in such case you should disable it.
+
+       Default: 1
+
+ping_group_range - 2 INTEGERS
+       Restrict ICMP_PROTO datagram sockets to users in the group range.
+       The default is "1 0", meaning, that nobody (not even root) may
+       create ping sockets.  Setting it to "100 100" would grant permissions
+       to the single group. "0 4294967295" would enable it for the world, "100
+       4294967295" would enable it for the users, but not daemons.
+
+tcp_early_demux - BOOLEAN
+       Enable early demux for established TCP sockets.
+
+       Default: 1
+
+udp_early_demux - BOOLEAN
+       Enable early demux for connected UDP sockets. Disable this if
+       your system could experience more unconnected load.
+
+       Default: 1
+
+icmp_echo_ignore_all - BOOLEAN
+       If set non-zero, then the kernel will ignore all ICMP ECHO
+       requests sent to it.
+
+       Default: 0
+
+icmp_echo_ignore_broadcasts - BOOLEAN
+       If set non-zero, then the kernel will ignore all ICMP ECHO and
+       TIMESTAMP requests sent to it via broadcast/multicast.
+
+       Default: 1
+
+icmp_ratelimit - INTEGER
+       Limit the maximal rates for sending ICMP packets whose type matches
+       icmp_ratemask (see below) to specific targets.
+       0 to disable any limiting,
+       otherwise the minimal space between responses in milliseconds.
+       Note that another sysctl, icmp_msgs_per_sec limits the number
+       of ICMP packets sent on all targets.
+
+       Default: 1000
+
+icmp_msgs_per_sec - INTEGER
+       Limit maximal number of ICMP packets sent per second from this host.
+       Only messages whose type matches icmp_ratemask (see below) are
+       controlled by this limit.
+
+       Default: 1000
+
+icmp_msgs_burst - INTEGER
+       icmp_msgs_per_sec controls number of ICMP packets sent per second,
+       while icmp_msgs_burst controls the burst size of these packets.
+
+       Default: 50
+
+icmp_ratemask - INTEGER
+       Mask made of ICMP types for which rates are being limited.
+
+       Significant bits: IHGFEDCBA9876543210
+
+       Default mask:     0000001100000011000 (6168)
+
+       Bit definitions (see include/linux/icmp.h):
+
+               = =========================
+               0 Echo Reply
+               3 Destination Unreachable [1]_
+               4 Source Quench [1]_
+               5 Redirect
+               8 Echo Request
+               B Time Exceeded [1]_
+               C Parameter Problem [1]_
+               D Timestamp Request
+               E Timestamp Reply
+               F Info Request
+               G Info Reply
+               H Address Mask Request
+               I Address Mask Reply
+               = =========================
+
+       .. [1] These are rate limited by default (see default mask above)
+
+icmp_ignore_bogus_error_responses - BOOLEAN
+       Some routers violate RFC1122 by sending bogus responses to broadcast
+       frames.  Such violations are normally logged via a kernel warning.
+       If this is set to TRUE, the kernel will not give such warnings, which
+       will avoid log file clutter.
+
+       Default: 1
+
+icmp_errors_use_inbound_ifaddr - BOOLEAN
+
+       If zero, icmp error messages are sent with the primary address of
+       the exiting interface.
+
+       If non-zero, the message will be sent with the primary address of
+       the interface that received the packet that caused the icmp error.
+       This is the behaviour network many administrators will expect from
+       a router. And it can make debugging complicated network layouts
+       much easier.
+
+       Note that if no primary address exists for the interface selected,
+       then the primary address of the first non-loopback interface that
+       has one will be used regardless of this setting.
+
+       Default: 0
+
+igmp_max_memberships - INTEGER
+       Change the maximum number of multicast groups we can subscribe to.
+       Default: 20
+
+       Theoretical maximum value is bounded by having to send a membership
+       report in a single datagram (i.e. the report can't span multiple
+       datagrams, or risk confusing the switch and leaving groups you don't
+       intend to).
+
+       The number of supported groups 'M' is bounded by the number of group
+       report entries you can fit into a single datagram of 65535 bytes.
+
+       M = 65536-sizeof (ip header)/(sizeof(Group record))
+
+       Group records are variable length, with a minimum of 12 bytes.
+       So net.ipv4.igmp_max_memberships should not be set higher than:
+
+       (65536-24) / 12 = 5459
+
+       The value 5459 assumes no IP header options, so in practice
+       this number may be lower.
+
+igmp_max_msf - INTEGER
+       Maximum number of addresses allowed in the source filter list for a
+       multicast group.
+
+       Default: 10
+
+igmp_qrv - INTEGER
+       Controls the IGMP query robustness variable (see RFC2236 8.1).
+
+       Default: 2 (as specified by RFC2236 8.1)
+
+       Minimum: 1 (as specified by RFC6636 4.5)
+
+force_igmp_version - INTEGER
+       - 0 - (default) No enforcement of a IGMP version, IGMPv1/v2 fallback
+         allowed. Will back to IGMPv3 mode again if all IGMPv1/v2 Querier
+         Present timer expires.
+       - 1 - Enforce to use IGMP version 1. Will also reply IGMPv1 report if
+         receive IGMPv2/v3 query.
+       - 2 - Enforce to use IGMP version 2. Will fallback to IGMPv1 if receive
+         IGMPv1 query message. Will reply report if receive IGMPv3 query.
+       - 3 - Enforce to use IGMP version 3. The same react with default 0.
+
+       .. note::
+
+          this is not the same with force_mld_version because IGMPv3 RFC3376
+          Security Considerations does not have clear description that we could
+          ignore other version messages completely as MLDv2 RFC3810. So make
+          this value as default 0 is recommended.
+
+``conf/interface/*``
+       changes special settings per interface (where
+       interface" is the name of your network interface)
+
+``conf/all/*``
+         is special, changes the settings for all interfaces
+
+log_martians - BOOLEAN
+       Log packets with impossible addresses to kernel log.
+       log_martians for the interface will be enabled if at least one of
+       conf/{all,interface}/log_martians is set to TRUE,
+       it will be disabled otherwise
+
+accept_redirects - BOOLEAN
+       Accept ICMP redirect messages.
+       accept_redirects for the interface will be enabled if:
+
+       - both conf/{all,interface}/accept_redirects are TRUE in the case
+         forwarding for the interface is enabled
+
+       or
+
+       - at least one of conf/{all,interface}/accept_redirects is TRUE in the
+         case forwarding for the interface is disabled
+
+       accept_redirects for the interface will be disabled otherwise
+
+       default:
+
+               - TRUE (host)
+               - FALSE (router)
+
+forwarding - BOOLEAN
+       Enable IP forwarding on this interface.  This controls whether packets
+       received _on_ this interface can be forwarded.
+
+mc_forwarding - BOOLEAN
+       Do multicast routing. The kernel needs to be compiled with CONFIG_MROUTE
+       and a multicast routing daemon is required.
+       conf/all/mc_forwarding must also be set to TRUE to enable multicast
+       routing for the interface
+
+medium_id - INTEGER
+       Integer value used to differentiate the devices by the medium they
+       are attached to. Two devices can have different id values when
+       the broadcast packets are received only on one of them.
+       The default value 0 means that the device is the only interface
+       to its medium, value of -1 means that medium is not known.
+
+       Currently, it is used to change the proxy_arp behavior:
+       the proxy_arp feature is enabled for packets forwarded between
+       two devices attached to different media.
+
+proxy_arp - BOOLEAN
+       Do proxy arp.
+
+       proxy_arp for the interface will be enabled if at least one of
+       conf/{all,interface}/proxy_arp is set to TRUE,
+       it will be disabled otherwise
+
+proxy_arp_pvlan - BOOLEAN
+       Private VLAN proxy arp.
+
+       Basically allow proxy arp replies back to the same interface
+       (from which the ARP request/solicitation was received).
+
+       This is done to support (ethernet) switch features, like RFC
+       3069, where the individual ports are NOT allowed to
+       communicate with each other, but they are allowed to talk to
+       the upstream router.  As described in RFC 3069, it is possible
+       to allow these hosts to communicate through the upstream
+       router by proxy_arp'ing. Don't need to be used together with
+       proxy_arp.
+
+       This technology is known by different names:
+
+         In RFC 3069 it is called VLAN Aggregation.
+         Cisco and Allied Telesyn call it Private VLAN.
+         Hewlett-Packard call it Source-Port filtering or port-isolation.
+         Ericsson call it MAC-Forced Forwarding (RFC Draft).
+
+shared_media - BOOLEAN
+       Send(router) or accept(host) RFC1620 shared media redirects.
+       Overrides secure_redirects.
+
+       shared_media for the interface will be enabled if at least one of
+       conf/{all,interface}/shared_media is set to TRUE,
+       it will be disabled otherwise
+
+       default TRUE
+
+secure_redirects - BOOLEAN
+       Accept ICMP redirect messages only to gateways listed in the
+       interface's current gateway list. Even if disabled, RFC1122 redirect
+       rules still apply.
+
+       Overridden by shared_media.
+
+       secure_redirects for the interface will be enabled if at least one of
+       conf/{all,interface}/secure_redirects is set to TRUE,
+       it will be disabled otherwise
+
+       default TRUE
+
+send_redirects - BOOLEAN
+       Send redirects, if router.
+
+       send_redirects for the interface will be enabled if at least one of
+       conf/{all,interface}/send_redirects is set to TRUE,
+       it will be disabled otherwise
+
+       Default: TRUE
+
+bootp_relay - BOOLEAN
+       Accept packets with source address 0.b.c.d destined
+       not to this host as local ones. It is supposed, that
+       BOOTP relay daemon will catch and forward such packets.
+       conf/all/bootp_relay must also be set to TRUE to enable BOOTP relay
+       for the interface
+
+       default FALSE
+
+       Not Implemented Yet.
+
+accept_source_route - BOOLEAN
+       Accept packets with SRR option.
+       conf/all/accept_source_route must also be set to TRUE to accept packets
+       with SRR option on the interface
+
+       default
+
+               - TRUE (router)
+               - FALSE (host)
+
+accept_local - BOOLEAN
+       Accept packets with local source addresses. In combination with
+       suitable routing, this can be used to direct packets between two
+       local interfaces over the wire and have them accepted properly.
+       default FALSE
+
+route_localnet - BOOLEAN
+       Do not consider loopback addresses as martian source or destination
+       while routing. This enables the use of 127/8 for local routing purposes.
+
+       default FALSE
+
+rp_filter - INTEGER
+       - 0 - No source validation.
+       - 1 - Strict mode as defined in RFC3704 Strict Reverse Path
+         Each incoming packet is tested against the FIB and if the interface
+         is not the best reverse path the packet check will fail.
+         By default failed packets are discarded.
+       - 2 - Loose mode as defined in RFC3704 Loose Reverse Path
+         Each incoming packet's source address is also tested against the FIB
+         and if the source address is not reachable via any interface
+         the packet check will fail.
+
+       Current recommended practice in RFC3704 is to enable strict mode
+       to prevent IP spoofing from DDos attacks. If using asymmetric routing
+       or other complicated routing, then loose mode is recommended.
+
+       The max value from conf/{all,interface}/rp_filter is used
+       when doing source validation on the {interface}.
+
+       Default value is 0. Note that some distributions enable it
+       in startup scripts.
+
+arp_filter - BOOLEAN
+       - 1 - Allows you to have multiple network interfaces on the same
+         subnet, and have the ARPs for each interface be answered
+         based on whether or not the kernel would route a packet from
+         the ARP'd IP out that interface (therefore you must use source
+         based routing for this to work). In other words it allows control
+         of which cards (usually 1) will respond to an arp request.
+
+       - 0 - (default) The kernel can respond to arp requests with addresses
+         from other interfaces. This may seem wrong but it usually makes
+         sense, because it increases the chance of successful communication.
+         IP addresses are owned by the complete host on Linux, not by
+         particular interfaces. Only for more complex setups like load-
+         balancing, does this behaviour cause problems.
+
+       arp_filter for the interface will be enabled if at least one of
+       conf/{all,interface}/arp_filter is set to TRUE,
+       it will be disabled otherwise
+
+arp_announce - INTEGER
+       Define different restriction levels for announcing the local
+       source IP address from IP packets in ARP requests sent on
+       interface:
+
+       - 0 - (default) Use any local address, configured on any interface
+       - 1 - Try to avoid local addresses that are not in the target's
+         subnet for this interface. This mode is useful when target
+         hosts reachable via this interface require the source IP
+         address in ARP requests to be part of their logical network
+         configured on the receiving interface. When we generate the
+         request we will check all our subnets that include the
+         target IP and will preserve the source address if it is from
+         such subnet. If there is no such subnet we select source
+         address according to the rules for level 2.
+       - 2 - Always use the best local address for this target.
+         In this mode we ignore the source address in the IP packet
+         and try to select local address that we prefer for talks with
+         the target host. Such local address is selected by looking
+         for primary IP addresses on all our subnets on the outgoing
+         interface that include the target IP address. If no suitable
+         local address is found we select the first local address
+         we have on the outgoing interface or on all other interfaces,
+         with the hope we will receive reply for our request and
+         even sometimes no matter the source IP address we announce.
+
+       The max value from conf/{all,interface}/arp_announce is used.
+
+       Increasing the restriction level gives more chance for
+       receiving answer from the resolved target while decreasing
+       the level announces more valid sender's information.
+
+arp_ignore - INTEGER
+       Define different modes for sending replies in response to
+       received ARP requests that resolve local target IP addresses:
+
+       - 0 - (default): reply for any local target IP address, configured
+         on any interface
+       - 1 - reply only if the target IP address is local address
+         configured on the incoming interface
+       - 2 - reply only if the target IP address is local address
+         configured on the incoming interface and both with the
+         sender's IP address are part from same subnet on this interface
+       - 3 - do not reply for local addresses configured with scope host,
+         only resolutions for global and link addresses are replied
+       - 4-7 - reserved
+       - 8 - do not reply for all local addresses
+
+       The max value from conf/{all,interface}/arp_ignore is used
+       when ARP request is received on the {interface}
+
+arp_notify - BOOLEAN
+       Define mode for notification of address and device changes.
+
+        ==  ==========================================================
+         0  (default): do nothing
+         1  Generate gratuitous arp requests when device is brought up
+            or hardware address changes.
+        ==  ==========================================================
+
+arp_accept - BOOLEAN
+       Define behavior for gratuitous ARP frames who's IP is not
+       already present in the ARP table:
+
+       - 0 - don't create new entries in the ARP table
+       - 1 - create new entries in the ARP table
+
+       Both replies and requests type gratuitous arp will trigger the
+       ARP table to be updated, if this setting is on.
+
+       If the ARP table already contains the IP address of the
+       gratuitous arp frame, the arp table will be updated regardless
+       if this setting is on or off.
+
+mcast_solicit - INTEGER
+       The maximum number of multicast probes in INCOMPLETE state,
+       when the associated hardware address is unknown.  Defaults
+       to 3.
+
+ucast_solicit - INTEGER
+       The maximum number of unicast probes in PROBE state, when
+       the hardware address is being reconfirmed.  Defaults to 3.
+
+app_solicit - INTEGER
+       The maximum number of probes to send to the user space ARP daemon
+       via netlink before dropping back to multicast probes (see
+       mcast_resolicit).  Defaults to 0.
+
+mcast_resolicit - INTEGER
+       The maximum number of multicast probes after unicast and
+       app probes in PROBE state.  Defaults to 0.
+
+disable_policy - BOOLEAN
+       Disable IPSEC policy (SPD) for this interface
+
+disable_xfrm - BOOLEAN
+       Disable IPSEC encryption on this interface, whatever the policy
+
+igmpv2_unsolicited_report_interval - INTEGER
+       The interval in milliseconds in which the next unsolicited
+       IGMPv1 or IGMPv2 report retransmit will take place.
+
+       Default: 10000 (10 seconds)
+
+igmpv3_unsolicited_report_interval - INTEGER
+       The interval in milliseconds in which the next unsolicited
+       IGMPv3 report retransmit will take place.
+
+       Default: 1000 (1 seconds)
+
+promote_secondaries - BOOLEAN
+       When a primary IP address is removed from this interface
+       promote a corresponding secondary IP address instead of
+       removing all the corresponding secondary IP addresses.
+
+drop_unicast_in_l2_multicast - BOOLEAN
+       Drop any unicast IP packets that are received in link-layer
+       multicast (or broadcast) frames.
+
+       This behavior (for multicast) is actually a SHOULD in RFC
+       1122, but is disabled by default for compatibility reasons.
+
+       Default: off (0)
+
+drop_gratuitous_arp - BOOLEAN
+       Drop all gratuitous ARP frames, for example if there's a known
+       good ARP proxy on the network and such frames need not be used
+       (or in the case of 802.11, must not be used to prevent attacks.)
+
+       Default: off (0)
+
+
+tag - INTEGER
+       Allows you to write a number, which can be used as required.
+
+       Default value is 0.
+
+xfrm4_gc_thresh - INTEGER
+       (Obsolete since linux-4.14)
+       The threshold at which we will start garbage collecting for IPv4
+       destination cache entries.  At twice this value the system will
+       refuse new allocations.
+
+igmp_link_local_mcast_reports - BOOLEAN
+       Enable IGMP reports for link local multicast groups in the
+       224.0.0.X range.
+
+       Default TRUE
+
+Alexey Kuznetsov.
+kuznet@ms2.inr.ac.ru
+
+Updated by:
+
+- Andi Kleen
+  ak@muc.de
+- Nicolas Delon
+  delon.nicolas@wanadoo.fr
+
+
+
+
+/proc/sys/net/ipv6/* Variables
+==============================
+
+IPv6 has no global variables such as tcp_*.  tcp_* settings under ipv4/ also
+apply to IPv6 [XXX?].
+
+bindv6only - BOOLEAN
+       Default value for IPV6_V6ONLY socket option,
+       which restricts use of the IPv6 socket to IPv6 communication
+       only.
+
+               - TRUE: disable IPv4-mapped address feature
+               - FALSE: enable IPv4-mapped address feature
+
+       Default: FALSE (as specified in RFC3493)
+
+flowlabel_consistency - BOOLEAN
+       Protect the consistency (and unicity) of flow label.
+       You have to disable it to use IPV6_FL_F_REFLECT flag on the
+       flow label manager.
+
+       - TRUE: enabled
+       - FALSE: disabled
+
+       Default: TRUE
+
+auto_flowlabels - INTEGER
+       Automatically generate flow labels based on a flow hash of the
+       packet. This allows intermediate devices, such as routers, to
+       identify packet flows for mechanisms like Equal Cost Multipath
+       Routing (see RFC 6438).
+
+       =  ===========================================================
+       0  automatic flow labels are completely disabled
+       1  automatic flow labels are enabled by default, they can be
+          disabled on a per socket basis using the IPV6_AUTOFLOWLABEL
+          socket option
+       2  automatic flow labels are allowed, they may be enabled on a
+          per socket basis using the IPV6_AUTOFLOWLABEL socket option
+       3  automatic flow labels are enabled and enforced, they cannot
+          be disabled by the socket option
+       =  ===========================================================
+
+       Default: 1
+
+flowlabel_state_ranges - BOOLEAN
+       Split the flow label number space into two ranges. 0-0x7FFFF is
+       reserved for the IPv6 flow manager facility, 0x80000-0xFFFFF
+       is reserved for stateless flow labels as described in RFC6437.
+
+       - TRUE: enabled
+       - FALSE: disabled
+
+       Default: true
+
+flowlabel_reflect - INTEGER
+       Control flow label reflection. Needed for Path MTU
+       Discovery to work with Equal Cost Multipath Routing in anycast
+       environments. See RFC 7690 and:
+       https://tools.ietf.org/html/draft-wang-6man-flow-label-reflection-01
+
+       This is a bitmask.
+
+       - 1: enabled for established flows
+
+         Note that this prevents automatic flowlabel changes, as done
+         in "tcp: change IPv6 flow-label upon receiving spurious retransmission"
+         and "tcp: Change txhash on every SYN and RTO retransmit"
+
+       - 2: enabled for TCP RESET packets (no active listener)
+         If set, a RST packet sent in response to a SYN packet on a closed
+         port will reflect the incoming flow label.
+
+       - 4: enabled for ICMPv6 echo reply messages.
+
+       Default: 0
+
+fib_multipath_hash_policy - INTEGER
+       Controls which hash policy to use for multipath routes.
+
+       Default: 0 (Layer 3)
+
+       Possible values:
+
+       - 0 - Layer 3 (source and destination addresses plus flow label)
+       - 1 - Layer 4 (standard 5-tuple)
+       - 2 - Layer 3 or inner Layer 3 if present
+
+anycast_src_echo_reply - BOOLEAN
+       Controls the use of anycast addresses as source addresses for ICMPv6
+       echo reply
+
+       - TRUE:  enabled
+       - FALSE: disabled
+
+       Default: FALSE
+
+idgen_delay - INTEGER
+       Controls the delay in seconds after which time to retry
+       privacy stable address generation if a DAD conflict is
+       detected.
+
+       Default: 1 (as specified in RFC7217)
+
+idgen_retries - INTEGER
+       Controls the number of retries to generate a stable privacy
+       address if a DAD conflict is detected.
+
+       Default: 3 (as specified in RFC7217)
+
+mld_qrv - INTEGER
+       Controls the MLD query robustness variable (see RFC3810 9.1).
+
+       Default: 2 (as specified by RFC3810 9.1)
+
+       Minimum: 1 (as specified by RFC6636 4.5)
+
+max_dst_opts_number - INTEGER
+       Maximum number of non-padding TLVs allowed in a Destination
+       options extension header. If this value is less than zero
+       then unknown options are disallowed and the number of known
+       TLVs allowed is the absolute value of this number.
+
+       Default: 8
+
+max_hbh_opts_number - INTEGER
+       Maximum number of non-padding TLVs allowed in a Hop-by-Hop
+       options extension header. If this value is less than zero
+       then unknown options are disallowed and the number of known
+       TLVs allowed is the absolute value of this number.
+
+       Default: 8
+
+max_dst_opts_length - INTEGER
+       Maximum length allowed for a Destination options extension
+       header.
+
+       Default: INT_MAX (unlimited)
+
+max_hbh_length - INTEGER
+       Maximum length allowed for a Hop-by-Hop options extension
+       header.
+
+       Default: INT_MAX (unlimited)
+
+skip_notify_on_dev_down - BOOLEAN
+       Controls whether an RTM_DELROUTE message is generated for routes
+       removed when a device is taken down or deleted. IPv4 does not
+       generate this message; IPv6 does by default. Setting this sysctl
+       to true skips the message, making IPv4 and IPv6 on par in relying
+       on userspace caches to track link events and evict routes.
+
+       Default: false (generate message)
+
+nexthop_compat_mode - BOOLEAN
+       New nexthop API provides a means for managing nexthops independent of
+       prefixes. Backwards compatibilty with old route format is enabled by
+       default which means route dumps and notifications contain the new
+       nexthop attribute but also the full, expanded nexthop definition.
+       Further, updates or deletes of a nexthop configuration generate route
+       notifications for each fib entry using the nexthop. Once a system
+       understands the new API, this sysctl can be disabled to achieve full
+       performance benefits of the new API by disabling the nexthop expansion
+       and extraneous notifications.
+       Default: true (backward compat mode)
+
+IPv6 Fragmentation:
+
+ip6frag_high_thresh - INTEGER
+       Maximum memory used to reassemble IPv6 fragments. When
+       ip6frag_high_thresh bytes of memory is allocated for this purpose,
+       the fragment handler will toss packets until ip6frag_low_thresh
+       is reached.
+
+ip6frag_low_thresh - INTEGER
+       See ip6frag_high_thresh
+
+ip6frag_time - INTEGER
+       Time in seconds to keep an IPv6 fragment in memory.
+
+IPv6 Segment Routing:
+
+seg6_flowlabel - INTEGER
+       Controls the behaviour of computing the flowlabel of outer
+       IPv6 header in case of SR T.encaps
+
+        == =======================================================
+        -1  set flowlabel to zero.
+         0  copy flowlabel from Inner packet in case of Inner IPv6
+            (Set flowlabel to 0 in case IPv4/L2)
+         1  Compute the flowlabel using seg6_make_flowlabel()
+        == =======================================================
+
+       Default is 0.
+
+``conf/default/*``:
+       Change the interface-specific default settings.
+
+
+``conf/all/*``:
+       Change all the interface-specific settings.
+
+       [XXX:  Other special features than forwarding?]
+
+conf/all/forwarding - BOOLEAN
+       Enable global IPv6 forwarding between all interfaces.
+
+       IPv4 and IPv6 work differently here; e.g. netfilter must be used
+       to control which interfaces may forward packets and which not.
+
+       This also sets all interfaces' Host/Router setting
+       'forwarding' to the specified value.  See below for details.
+
+       This referred to as global forwarding.
+
+proxy_ndp - BOOLEAN
+       Do proxy ndp.
+
+fwmark_reflect - BOOLEAN
+       Controls the fwmark of kernel-generated IPv6 reply packets that are not
+       associated with a socket for example, TCP RSTs or ICMPv6 echo replies).
+       If unset, these packets have a fwmark of zero. If set, they have the
+       fwmark of the packet they are replying to.
+
+       Default: 0
+
+``conf/interface/*``:
+       Change special settings per interface.
+
+       The functional behaviour for certain settings is different
+       depending on whether local forwarding is enabled or not.
+
+accept_ra - INTEGER
+       Accept Router Advertisements; autoconfigure using them.
+
+       It also determines whether or not to transmit Router
+       Solicitations. If and only if the functional setting is to
+       accept Router Advertisements, Router Solicitations will be
+       transmitted.
+
+       Possible values are:
+
+               ==  ===========================================================
+                0  Do not accept Router Advertisements.
+                1  Accept Router Advertisements if forwarding is disabled.
+                2  Overrule forwarding behaviour. Accept Router Advertisements
+                   even if forwarding is enabled.
+               ==  ===========================================================
+
+       Functional default:
+
+               - enabled if local forwarding is disabled.
+               - disabled if local forwarding is enabled.
+
+accept_ra_defrtr - BOOLEAN
+       Learn default router in Router Advertisement.
+
+       Functional default:
+
+               - enabled if accept_ra is enabled.
+               - disabled if accept_ra is disabled.
+
+accept_ra_from_local - BOOLEAN
+       Accept RA with source-address that is found on local machine
+       if the RA is otherwise proper and able to be accepted.
+
+       Default is to NOT accept these as it may be an un-intended
+       network loop.
+
+       Functional default:
+
+          - enabled if accept_ra_from_local is enabled
+            on a specific interface.
+          - disabled if accept_ra_from_local is disabled
+            on a specific interface.
+
+accept_ra_min_hop_limit - INTEGER
+       Minimum hop limit Information in Router Advertisement.
+
+       Hop limit Information in Router Advertisement less than this
+       variable shall be ignored.
+
+       Default: 1
+
+accept_ra_pinfo - BOOLEAN
+       Learn Prefix Information in Router Advertisement.
+
+       Functional default:
+
+               - enabled if accept_ra is enabled.
+               - disabled if accept_ra is disabled.
+
+accept_ra_rt_info_min_plen - INTEGER
+       Minimum prefix length of Route Information in RA.
+
+       Route Information w/ prefix smaller than this variable shall
+       be ignored.
+
+       Functional default:
+
+               * 0 if accept_ra_rtr_pref is enabled.
+               * -1 if accept_ra_rtr_pref is disabled.
+
+accept_ra_rt_info_max_plen - INTEGER
+       Maximum prefix length of Route Information in RA.
+
+       Route Information w/ prefix larger than this variable shall
+       be ignored.
+
+       Functional default:
+
+               * 0 if accept_ra_rtr_pref is enabled.
+               * -1 if accept_ra_rtr_pref is disabled.
+
+accept_ra_rtr_pref - BOOLEAN
+       Accept Router Preference in RA.
+
+       Functional default:
+
+               - enabled if accept_ra is enabled.
+               - disabled if accept_ra is disabled.
+
+accept_ra_mtu - BOOLEAN
+       Apply the MTU value specified in RA option 5 (RFC4861). If
+       disabled, the MTU specified in the RA will be ignored.
+
+       Functional default:
+
+               - enabled if accept_ra is enabled.
+               - disabled if accept_ra is disabled.
+
+accept_redirects - BOOLEAN
+       Accept Redirects.
+
+       Functional default:
+
+               - enabled if local forwarding is disabled.
+               - disabled if local forwarding is enabled.
+
+accept_source_route - INTEGER
+       Accept source routing (routing extension header).
+
+       - >= 0: Accept only routing header type 2.
+       - < 0: Do not accept routing header.
+
+       Default: 0
+
+autoconf - BOOLEAN
+       Autoconfigure addresses using Prefix Information in Router
+       Advertisements.
+
+       Functional default:
+
+               - enabled if accept_ra_pinfo is enabled.
+               - disabled if accept_ra_pinfo is disabled.
+
+dad_transmits - INTEGER
+       The amount of Duplicate Address Detection probes to send.
+
+       Default: 1
+
+forwarding - INTEGER
+       Configure interface-specific Host/Router behaviour.
+
+       .. note::
+
+          It is recommended to have the same setting on all
+          interfaces; mixed router/host scenarios are rather uncommon.
+
+       Possible values are:
+
+               - 0 Forwarding disabled
+               - 1 Forwarding enabled
+
+       **FALSE (0)**:
+
+       By default, Host behaviour is assumed.  This means:
+
+       1. IsRouter flag is not set in Neighbour Advertisements.
+       2. If accept_ra is TRUE (default), transmit Router
+          Solicitations.
+       3. If accept_ra is TRUE (default), accept Router
+          Advertisements (and do autoconfiguration).
+       4. If accept_redirects is TRUE (default), accept Redirects.
+
+       **TRUE (1)**:
+
+       If local forwarding is enabled, Router behaviour is assumed.
+       This means exactly the reverse from the above:
+
+       1. IsRouter flag is set in Neighbour Advertisements.
+       2. Router Solicitations are not sent unless accept_ra is 2.
+       3. Router Advertisements are ignored unless accept_ra is 2.
+       4. Redirects are ignored.
+
+       Default: 0 (disabled) if global forwarding is disabled (default),
+       otherwise 1 (enabled).
+
+hop_limit - INTEGER
+       Default Hop Limit to set.
+
+       Default: 64
+
+mtu - INTEGER
+       Default Maximum Transfer Unit
+
+       Default: 1280 (IPv6 required minimum)
+
+ip_nonlocal_bind - BOOLEAN
+       If set, allows processes to bind() to non-local IPv6 addresses,
+       which can be quite useful - but may break some applications.
+
+       Default: 0
+
+router_probe_interval - INTEGER
+       Minimum interval (in seconds) between Router Probing described
+       in RFC4191.
+
+       Default: 60
+
+router_solicitation_delay - INTEGER
+       Number of seconds to wait after interface is brought up
+       before sending Router Solicitations.
+
+       Default: 1
+
+router_solicitation_interval - INTEGER
+       Number of seconds to wait between Router Solicitations.
+
+       Default: 4
+
+router_solicitations - INTEGER
+       Number of Router Solicitations to send until assuming no
+       routers are present.
+
+       Default: 3
+
+use_oif_addrs_only - BOOLEAN
+       When enabled, the candidate source addresses for destinations
+       routed via this interface are restricted to the set of addresses
+       configured on this interface (vis. RFC 6724, section 4).
+
+       Default: false
+
+use_tempaddr - INTEGER
+       Preference for Privacy Extensions (RFC3041).
+
+         * <= 0 : disable Privacy Extensions
+         * == 1 : enable Privacy Extensions, but prefer public
+           addresses over temporary addresses.
+         * >  1 : enable Privacy Extensions and prefer temporary
+           addresses over public addresses.
+
+       Default:
+
+               * 0 (for most devices)
+               * -1 (for point-to-point devices and loopback devices)
+
+temp_valid_lft - INTEGER
+       valid lifetime (in seconds) for temporary addresses.
+
+       Default: 172800 (2 days)
+
+temp_prefered_lft - INTEGER
+       Preferred lifetime (in seconds) for temporary addresses.
+
+       Default: 86400 (1 day)
+
+keep_addr_on_down - INTEGER
+       Keep all IPv6 addresses on an interface down event. If set static
+       global addresses with no expiration time are not flushed.
+
+       *   >0 : enabled
+       *    0 : system default
+       *   <0 : disabled
+
+       Default: 0 (addresses are removed)
+
+max_desync_factor - INTEGER
+       Maximum value for DESYNC_FACTOR, which is a random value
+       that ensures that clients don't synchronize with each
+       other and generate new addresses at exactly the same time.
+       value is in seconds.
+
+       Default: 600
+
+regen_max_retry - INTEGER
+       Number of attempts before give up attempting to generate
+       valid temporary addresses.
+
+       Default: 5
+
+max_addresses - INTEGER
+       Maximum number of autoconfigured addresses per interface.  Setting
+       to zero disables the limitation.  It is not recommended to set this
+       value too large (or to zero) because it would be an easy way to
+       crash the kernel by allowing too many addresses to be created.
+
+       Default: 16
+
+disable_ipv6 - BOOLEAN
+       Disable IPv6 operation.  If accept_dad is set to 2, this value
+       will be dynamically set to TRUE if DAD fails for the link-local
+       address.
+
+       Default: FALSE (enable IPv6 operation)
+
+       When this value is changed from 1 to 0 (IPv6 is being enabled),
+       it will dynamically create a link-local address on the given
+       interface and start Duplicate Address Detection, if necessary.
+
+       When this value is changed from 0 to 1 (IPv6 is being disabled),
+       it will dynamically delete all addresses and routes on the given
+       interface. From now on it will not possible to add addresses/routes
+       to the selected interface.
+
+accept_dad - INTEGER
+       Whether to accept DAD (Duplicate Address Detection).
+
+        == ==============================================================
+         0  Disable DAD
+         1  Enable DAD (default)
+         2  Enable DAD, and disable IPv6 operation if MAC-based duplicate
+            link-local address has been found.
+        == ==============================================================
+
+       DAD operation and mode on a given interface will be selected according
+       to the maximum value of conf/{all,interface}/accept_dad.
+
+force_tllao - BOOLEAN
+       Enable sending the target link-layer address option even when
+       responding to a unicast neighbor solicitation.
+
+       Default: FALSE
+
+       Quoting from RFC 2461, section 4.4, Target link-layer address:
+
+       "The option MUST be included for multicast solicitations in order to
+       avoid infinite Neighbor Solicitation "recursion" when the peer node
+       does not have a cache entry to return a Neighbor Advertisements
+       message.  When responding to unicast solicitations, the option can be
+       omitted since the sender of the solicitation has the correct link-
+       layer address; otherwise it would not have be able to send the unicast
+       solicitation in the first place. However, including the link-layer
+       address in this case adds little overhead and eliminates a potential
+       race condition where the sender deletes the cached link-layer address
+       prior to receiving a response to a previous solicitation."
+
+ndisc_notify - BOOLEAN
+       Define mode for notification of address and device changes.
+
+       * 0 - (default): do nothing
+       * 1 - Generate unsolicited neighbour advertisements when device is brought
+         up or hardware address changes.
+
+ndisc_tclass - INTEGER
+       The IPv6 Traffic Class to use by default when sending IPv6 Neighbor
+       Discovery (Router Solicitation, Router Advertisement, Neighbor
+       Solicitation, Neighbor Advertisement, Redirect) messages.
+       These 8 bits can be interpreted as 6 high order bits holding the DSCP
+       value and 2 low order bits representing ECN (which you probably want
+       to leave cleared).
+
+       * 0 - (default)
+
+mldv1_unsolicited_report_interval - INTEGER
+       The interval in milliseconds in which the next unsolicited
+       MLDv1 report retransmit will take place.
+
+       Default: 10000 (10 seconds)
+
+mldv2_unsolicited_report_interval - INTEGER
+       The interval in milliseconds in which the next unsolicited
+       MLDv2 report retransmit will take place.
+
+       Default: 1000 (1 second)
+
+force_mld_version - INTEGER
+       * 0 - (default) No enforcement of a MLD version, MLDv1 fallback allowed
+       * 1 - Enforce to use MLD version 1
+       * 2 - Enforce to use MLD version 2
+
+suppress_frag_ndisc - INTEGER
+       Control RFC 6980 (Security Implications of IPv6 Fragmentation
+       with IPv6 Neighbor Discovery) behavior:
+
+       * 1 - (default) discard fragmented neighbor discovery packets
+       * 0 - allow fragmented neighbor discovery packets
+
+optimistic_dad - BOOLEAN
+       Whether to perform Optimistic Duplicate Address Detection (RFC 4429).
+
+       * 0: disabled (default)
+       * 1: enabled
+
+       Optimistic Duplicate Address Detection for the interface will be enabled
+       if at least one of conf/{all,interface}/optimistic_dad is set to 1,
+       it will be disabled otherwise.
+
+use_optimistic - BOOLEAN
+       If enabled, do not classify optimistic addresses as deprecated during
+       source address selection.  Preferred addresses will still be chosen
+       before optimistic addresses, subject to other ranking in the source
+       address selection algorithm.
+
+       * 0: disabled (default)
+       * 1: enabled
+
+       This will be enabled if at least one of
+       conf/{all,interface}/use_optimistic is set to 1, disabled otherwise.
+
+stable_secret - IPv6 address
+       This IPv6 address will be used as a secret to generate IPv6
+       addresses for link-local addresses and autoconfigured
+       ones. All addresses generated after setting this secret will
+       be stable privacy ones by default. This can be changed via the
+       addrgenmode ip-link. conf/default/stable_secret is used as the
+       secret for the namespace, the interface specific ones can
+       overwrite that. Writes to conf/all/stable_secret are refused.
+
+       It is recommended to generate this secret during installation
+       of a system and keep it stable after that.
+
+       By default the stable secret is unset.
+
+addr_gen_mode - INTEGER
+       Defines how link-local and autoconf addresses are generated.
+
+       =  =================================================================
+       0  generate address based on EUI64 (default)
+       1  do no generate a link-local address, use EUI64 for addresses
+          generated from autoconf
+       2  generate stable privacy addresses, using the secret from
+          stable_secret (RFC7217)
+       3  generate stable privacy addresses, using a random secret if unset
+       =  =================================================================
+
+drop_unicast_in_l2_multicast - BOOLEAN
+       Drop any unicast IPv6 packets that are received in link-layer
+       multicast (or broadcast) frames.
+
+       By default this is turned off.
+
+drop_unsolicited_na - BOOLEAN
+       Drop all unsolicited neighbor advertisements, for example if there's
+       a known good NA proxy on the network and such frames need not be used
+       (or in the case of 802.11, must not be used to prevent attacks.)
+
+       By default this is turned off.
+
+enhanced_dad - BOOLEAN
+       Include a nonce option in the IPv6 neighbor solicitation messages used for
+       duplicate address detection per RFC7527. A received DAD NS will only signal
+       a duplicate address if the nonce is different. This avoids any false
+       detection of duplicates due to loopback of the NS messages that we send.
+       The nonce option will be sent on an interface unless both of
+       conf/{all,interface}/enhanced_dad are set to FALSE.
+
+       Default: TRUE
+
+``icmp/*``:
+===========
+
+ratelimit - INTEGER
+       Limit the maximal rates for sending ICMPv6 messages.
+
+       0 to disable any limiting,
+       otherwise the minimal space between responses in milliseconds.
+
+       Default: 1000
+
+ratemask - list of comma separated ranges
+       For ICMPv6 message types matching the ranges in the ratemask, limit
+       the sending of the message according to ratelimit parameter.
+
+       The format used for both input and output is a comma separated
+       list of ranges (e.g. "0-127,129" for ICMPv6 message type 0 to 127 and
+       129). Writing to the file will clear all previous ranges of ICMPv6
+       message types and update the current list with the input.
+
+       Refer to: https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml
+       for numerical values of ICMPv6 message types, e.g. echo request is 128
+       and echo reply is 129.
+
+       Default: 0-1,3-127 (rate limit ICMPv6 errors except Packet Too Big)
+
+echo_ignore_all - BOOLEAN
+       If set non-zero, then the kernel will ignore all ICMP ECHO
+       requests sent to it over the IPv6 protocol.
+
+       Default: 0
+
+echo_ignore_multicast - BOOLEAN
+       If set non-zero, then the kernel will ignore all ICMP ECHO
+       requests sent to it over the IPv6 protocol via multicast.
+
+       Default: 0
+
+echo_ignore_anycast - BOOLEAN
+       If set non-zero, then the kernel will ignore all ICMP ECHO
+       requests sent to it over the IPv6 protocol destined to anycast address.
+
+       Default: 0
+
+xfrm6_gc_thresh - INTEGER
+       (Obsolete since linux-4.14)
+       The threshold at which we will start garbage collecting for IPv6
+       destination cache entries.  At twice this value the system will
+       refuse new allocations.
+
+
+IPv6 Update by:
+Pekka Savola <pekkas@netcore.fi>
+YOSHIFUJI Hideaki / USAGI Project <yoshfuji@linux-ipv6.org>
+
+
+/proc/sys/net/bridge/* Variables:
+=================================
+
+bridge-nf-call-arptables - BOOLEAN
+       - 1 : pass bridged ARP traffic to arptables' FORWARD chain.
+       - 0 : disable this.
+
+       Default: 1
+
+bridge-nf-call-iptables - BOOLEAN
+       - 1 : pass bridged IPv4 traffic to iptables' chains.
+       - 0 : disable this.
+
+       Default: 1
+
+bridge-nf-call-ip6tables - BOOLEAN
+       - 1 : pass bridged IPv6 traffic to ip6tables' chains.
+       - 0 : disable this.
+
+       Default: 1
+
+bridge-nf-filter-vlan-tagged - BOOLEAN
+       - 1 : pass bridged vlan-tagged ARP/IP/IPv6 traffic to {arp,ip,ip6}tables.
+       - 0 : disable this.
+
+       Default: 0
+
+bridge-nf-filter-pppoe-tagged - BOOLEAN
+       - 1 : pass bridged pppoe-tagged IP/IPv6 traffic to {ip,ip6}tables.
+       - 0 : disable this.
+
+       Default: 0
+
+bridge-nf-pass-vlan-input-dev - BOOLEAN
+       - 1: if bridge-nf-filter-vlan-tagged is enabled, try to find a vlan
+         interface on the bridge and set the netfilter input device to the
+         vlan. This allows use of e.g. "iptables -i br0.1" and makes the
+         REDIRECT target work with vlan-on-top-of-bridge interfaces.  When no
+         matching vlan interface is found, or this switch is off, the input
+         device is set to the bridge interface.
+
+       - 0: disable bridge netfilter vlan interface lookup.
+
+       Default: 0
+
+``proc/sys/net/sctp/*`` Variables:
+==================================
+
+addip_enable - BOOLEAN
+       Enable or disable extension of  Dynamic Address Reconfiguration
+       (ADD-IP) functionality specified in RFC5061.  This extension provides
+       the ability to dynamically add and remove new addresses for the SCTP
+       associations.
+
+       1: Enable extension.
+
+       0: Disable extension.
+
+       Default: 0
+
+pf_enable - INTEGER
+       Enable or disable pf (pf is short for potentially failed) state. A value
+       of pf_retrans > path_max_retrans also disables pf state. That is, one of
+       both pf_enable and pf_retrans > path_max_retrans can disable pf state.
+       Since pf_retrans and path_max_retrans can be changed by userspace
+       application, sometimes user expects to disable pf state by the value of
+       pf_retrans > path_max_retrans, but occasionally the value of pf_retrans
+       or path_max_retrans is changed by the user application, this pf state is
+       enabled. As such, it is necessary to add this to dynamically enable
+       and disable pf state. See:
+       https://datatracker.ietf.org/doc/draft-ietf-tsvwg-sctp-failover for
+       details.
+
+       1: Enable pf.
+
+       0: Disable pf.
+
+       Default: 1
+
+pf_expose - INTEGER
+       Unset or enable/disable pf (pf is short for potentially failed) state
+       exposure.  Applications can control the exposure of the PF path state
+       in the SCTP_PEER_ADDR_CHANGE event and the SCTP_GET_PEER_ADDR_INFO
+       sockopt.   When it's unset, no SCTP_PEER_ADDR_CHANGE event with
+       SCTP_ADDR_PF state will be sent and a SCTP_PF-state transport info
+       can be got via SCTP_GET_PEER_ADDR_INFO sockopt;  When it's enabled,
+       a SCTP_PEER_ADDR_CHANGE event will be sent for a transport becoming
+       SCTP_PF state and a SCTP_PF-state transport info can be got via
+       SCTP_GET_PEER_ADDR_INFO sockopt;  When it's diabled, no
+       SCTP_PEER_ADDR_CHANGE event will be sent and it returns -EACCES when
+       trying to get a SCTP_PF-state transport info via SCTP_GET_PEER_ADDR_INFO
+       sockopt.
+
+       0: Unset pf state exposure, Compatible with old applications.
+
+       1: Disable pf state exposure.
+
+       2: Enable pf state exposure.
+
+       Default: 0
+
+addip_noauth_enable - BOOLEAN
+       Dynamic Address Reconfiguration (ADD-IP) requires the use of
+       authentication to protect the operations of adding or removing new
+       addresses.  This requirement is mandated so that unauthorized hosts
+       would not be able to hijack associations.  However, older
+       implementations may not have implemented this requirement while
+       allowing the ADD-IP extension.  For reasons of interoperability,
+       we provide this variable to control the enforcement of the
+       authentication requirement.
+
+       == ===============================================================
+       1  Allow ADD-IP extension to be used without authentication.  This
+          should only be set in a closed environment for interoperability
+          with older implementations.
+
+       0  Enforce the authentication requirement
+       == ===============================================================
+
+       Default: 0
+
+auth_enable - BOOLEAN
+       Enable or disable Authenticated Chunks extension.  This extension
+       provides the ability to send and receive authenticated chunks and is
+       required for secure operation of Dynamic Address Reconfiguration
+       (ADD-IP) extension.
+
+       - 1: Enable this extension.
+       - 0: Disable this extension.
+
+       Default: 0
+
+prsctp_enable - BOOLEAN
+       Enable or disable the Partial Reliability extension (RFC3758) which
+       is used to notify peers that a given DATA should no longer be expected.
+
+       - 1: Enable extension
+       - 0: Disable
+
+       Default: 1
+
+max_burst - INTEGER
+       The limit of the number of new packets that can be initially sent.  It
+       controls how bursty the generated traffic can be.
+
+       Default: 4
+
+association_max_retrans - INTEGER
+       Set the maximum number for retransmissions that an association can
+       attempt deciding that the remote end is unreachable.  If this value
+       is exceeded, the association is terminated.
+
+       Default: 10
+
+max_init_retransmits - INTEGER
+       The maximum number of retransmissions of INIT and COOKIE-ECHO chunks
+       that an association will attempt before declaring the destination
+       unreachable and terminating.
+
+       Default: 8
+
+path_max_retrans - INTEGER
+       The maximum number of retransmissions that will be attempted on a given
+       path.  Once this threshold is exceeded, the path is considered
+       unreachable, and new traffic will use a different path when the
+       association is multihomed.
+
+       Default: 5
+
+pf_retrans - INTEGER
+       The number of retransmissions that will be attempted on a given path
+       before traffic is redirected to an alternate transport (should one
+       exist).  Note this is distinct from path_max_retrans, as a path that
+       passes the pf_retrans threshold can still be used.  Its only
+       deprioritized when a transmission path is selected by the stack.  This
+       setting is primarily used to enable fast failover mechanisms without
+       having to reduce path_max_retrans to a very low value.  See:
+       http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt
+       for details.  Note also that a value of pf_retrans > path_max_retrans
+       disables this feature. Since both pf_retrans and path_max_retrans can
+       be changed by userspace application, a variable pf_enable is used to
+       disable pf state.
+
+       Default: 0
+
+ps_retrans - INTEGER
+       Primary.Switchover.Max.Retrans (PSMR), it's a tunable parameter coming
+       from section-5 "Primary Path Switchover" in rfc7829.  The primary path
+       will be changed to another active path when the path error counter on
+       the old primary path exceeds PSMR, so that "the SCTP sender is allowed
+       to continue data transmission on a new working path even when the old
+       primary destination address becomes active again".   Note this feature
+       is disabled by initializing 'ps_retrans' per netns as 0xffff by default,
+       and its value can't be less than 'pf_retrans' when changing by sysctl.
+
+       Default: 0xffff
+
+rto_initial - INTEGER
+       The initial round trip timeout value in milliseconds that will be used
+       in calculating round trip times.  This is the initial time interval
+       for retransmissions.
+
+       Default: 3000
+
+rto_max - INTEGER
+       The maximum value (in milliseconds) of the round trip timeout.  This
+       is the largest time interval that can elapse between retransmissions.
+
+       Default: 60000
+
+rto_min - INTEGER
+       The minimum value (in milliseconds) of the round trip timeout.  This
+       is the smallest time interval the can elapse between retransmissions.
+
+       Default: 1000
+
+hb_interval - INTEGER
+       The interval (in milliseconds) between HEARTBEAT chunks.  These chunks
+       are sent at the specified interval on idle paths to probe the state of
+       a given path between 2 associations.
+
+       Default: 30000
+
+sack_timeout - INTEGER
+       The amount of time (in milliseconds) that the implementation will wait
+       to send a SACK.
+
+       Default: 200
+
+valid_cookie_life - INTEGER
+       The default lifetime of the SCTP cookie (in milliseconds).  The cookie
+       is used during association establishment.
+
+       Default: 60000
+
+cookie_preserve_enable - BOOLEAN
+       Enable or disable the ability to extend the lifetime of the SCTP cookie
+       that is used during the establishment phase of SCTP association
+
+       - 1: Enable cookie lifetime extension.
+       - 0: Disable
+
+       Default: 1
+
+cookie_hmac_alg - STRING
+       Select the hmac algorithm used when generating the cookie value sent by
+       a listening sctp socket to a connecting client in the INIT-ACK chunk.
+       Valid values are:
+
+       * md5
+       * sha1
+       * none
+
+       Ability to assign md5 or sha1 as the selected alg is predicated on the
+       configuration of those algorithms at build time (CONFIG_CRYPTO_MD5 and
+       CONFIG_CRYPTO_SHA1).
+
+       Default: Dependent on configuration.  MD5 if available, else SHA1 if
+       available, else none.
+
+rcvbuf_policy - INTEGER
+       Determines if the receive buffer is attributed to the socket or to
+       association.   SCTP supports the capability to create multiple
+       associations on a single socket.  When using this capability, it is
+       possible that a single stalled association that's buffering a lot
+       of data may block other associations from delivering their data by
+       consuming all of the receive buffer space.  To work around this,
+       the rcvbuf_policy could be set to attribute the receiver buffer space
+       to each association instead of the socket.  This prevents the described
+       blocking.
+
+       - 1: rcvbuf space is per association
+       - 0: rcvbuf space is per socket
+
+       Default: 0
+
+sndbuf_policy - INTEGER
+       Similar to rcvbuf_policy above, this applies to send buffer space.
+
+       - 1: Send buffer is tracked per association
+       - 0: Send buffer is tracked per socket.
+
+       Default: 0
+
+sctp_mem - vector of 3 INTEGERs: min, pressure, max
+       Number of pages allowed for queueing by all SCTP sockets.
+
+       min: Below this number of pages SCTP is not bothered about its
+       memory appetite. When amount of memory allocated by SCTP exceeds
+       this number, SCTP starts to moderate memory usage.
+
+       pressure: This value was introduced to follow format of tcp_mem.
+
+       max: Number of pages allowed for queueing by all SCTP sockets.
+
+       Default is calculated at boot time from amount of available memory.
+
+sctp_rmem - vector of 3 INTEGERs: min, default, max
+       Only the first value ("min") is used, "default" and "max" are
+       ignored.
+
+       min: Minimal size of receive buffer used by SCTP socket.
+       It is guaranteed to each SCTP socket (but not association) even
+       under moderate memory pressure.
+
+       Default: 4K
+
+sctp_wmem  - vector of 3 INTEGERs: min, default, max
+       Currently this tunable has no effect.
+
+addr_scope_policy - INTEGER
+       Control IPv4 address scoping - draft-stewart-tsvwg-sctp-ipv4-00
+
+       - 0   - Disable IPv4 address scoping
+       - 1   - Enable IPv4 address scoping
+       - 2   - Follow draft but allow IPv4 private addresses
+       - 3   - Follow draft but allow IPv4 link local addresses
+
+       Default: 1
+
+
+``/proc/sys/net/core/*``
+========================
+
+       Please see: Documentation/admin-guide/sysctl/net.rst for descriptions of these entries.
+
+
+``/proc/sys/net/unix/*``
+========================
+
+max_dgram_qlen - INTEGER
+       The maximum length of dgram socket receive queue
+
+       Default: 10
+
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
deleted file mode 100644 (file)
index ee961d3..0000000
+++ /dev/null
@@ -1,2355 +0,0 @@
-/proc/sys/net/ipv4/* Variables:
-
-ip_forward - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
-
-       Forward Packets between interfaces.
-
-       This variable is special, its change resets all configuration
-       parameters to their default state (RFC1122 for hosts, RFC1812
-       for routers)
-
-ip_default_ttl - INTEGER
-       Default value of TTL field (Time To Live) for outgoing (but not
-       forwarded) IP packets. Should be between 1 and 255 inclusive.
-       Default: 64 (as recommended by RFC1700)
-
-ip_no_pmtu_disc - INTEGER
-       Disable Path MTU Discovery. If enabled in mode 1 and a
-       fragmentation-required ICMP is received, the PMTU to this
-       destination will be set to min_pmtu (see below). You will need
-       to raise min_pmtu to the smallest interface MTU on your system
-       manually if you want to avoid locally generated fragments.
-
-       In mode 2 incoming Path MTU Discovery messages will be
-       discarded. Outgoing frames are handled the same as in mode 1,
-       implicitly setting IP_PMTUDISC_DONT on every created socket.
-
-       Mode 3 is a hardened pmtu discover mode. The kernel will only
-       accept fragmentation-needed errors if the underlying protocol
-       can verify them besides a plain socket lookup. Current
-       protocols for which pmtu events will be honored are TCP, SCTP
-       and DCCP as they verify e.g. the sequence number or the
-       association. This mode should not be enabled globally but is
-       only intended to secure e.g. name servers in namespaces where
-       TCP path mtu must still work but path MTU information of other
-       protocols should be discarded. If enabled globally this mode
-       could break other protocols.
-
-       Possible values: 0-3
-       Default: FALSE
-
-min_pmtu - INTEGER
-       default 552 - minimum discovered Path MTU
-
-ip_forward_use_pmtu - BOOLEAN
-       By default we don't trust protocol path MTUs while forwarding
-       because they could be easily forged and can lead to unwanted
-       fragmentation by the router.
-       You only need to enable this if you have user-space software
-       which tries to discover path mtus by itself and depends on the
-       kernel honoring this information. This is normally not the
-       case.
-       Default: 0 (disabled)
-       Possible values:
-       0 - disabled
-       1 - enabled
-
-fwmark_reflect - BOOLEAN
-       Controls the fwmark of kernel-generated IPv4 reply packets that are not
-       associated with a socket for example, TCP RSTs or ICMP echo replies).
-       If unset, these packets have a fwmark of zero. If set, they have the
-       fwmark of the packet they are replying to.
-       Default: 0
-
-fib_multipath_use_neigh - BOOLEAN
-       Use status of existing neighbor entry when determining nexthop for
-       multipath routes. If disabled, neighbor information is not used and
-       packets could be directed to a failed nexthop. Only valid for kernels
-       built with CONFIG_IP_ROUTE_MULTIPATH enabled.
-       Default: 0 (disabled)
-       Possible values:
-       0 - disabled
-       1 - enabled
-
-fib_multipath_hash_policy - INTEGER
-       Controls which hash policy to use for multipath routes. Only valid
-       for kernels built with CONFIG_IP_ROUTE_MULTIPATH enabled.
-       Default: 0 (Layer 3)
-       Possible values:
-       0 - Layer 3
-       1 - Layer 4
-       2 - Layer 3 or inner Layer 3 if present
-
-fib_sync_mem - UNSIGNED INTEGER
-       Amount of dirty memory from fib entries that can be backlogged before
-       synchronize_rcu is forced.
-         Default: 512kB   Minimum: 64kB   Maximum: 64MB
-
-ip_forward_update_priority - INTEGER
-       Whether to update SKB priority from "TOS" field in IPv4 header after it
-       is forwarded. The new SKB priority is mapped from TOS field value
-       according to an rt_tos2priority table (see e.g. man tc-prio).
-       Default: 1 (Update priority.)
-       Possible values:
-       0 - Do not update priority.
-       1 - Update priority.
-
-route/max_size - INTEGER
-       Maximum number of routes allowed in the kernel.  Increase
-       this when using large numbers of interfaces and/or routes.
-       From linux kernel 3.6 onwards, this is deprecated for ipv4
-       as route cache is no longer used.
-
-neigh/default/gc_thresh1 - INTEGER
-       Minimum number of entries to keep.  Garbage collector will not
-       purge entries if there are fewer than this number.
-       Default: 128
-
-neigh/default/gc_thresh2 - INTEGER
-       Threshold when garbage collector becomes more aggressive about
-       purging entries. Entries older than 5 seconds will be cleared
-       when over this number.
-       Default: 512
-
-neigh/default/gc_thresh3 - INTEGER
-       Maximum number of non-PERMANENT neighbor entries allowed.  Increase
-       this when using large numbers of interfaces and when communicating
-       with large numbers of directly-connected peers.
-       Default: 1024
-
-neigh/default/unres_qlen_bytes - INTEGER
-       The maximum number of bytes which may be used by packets
-       queued for each unresolved address by other network layers.
-       (added in linux 3.3)
-       Setting negative value is meaningless and will return error.
-       Default: SK_WMEM_MAX, (same as net.core.wmem_default).
-               Exact value depends on architecture and kernel options,
-               but should be enough to allow queuing 256 packets
-               of medium size.
-
-neigh/default/unres_qlen - INTEGER
-       The maximum number of packets which may be queued for each
-       unresolved address by other network layers.
-       (deprecated in linux 3.3) : use unres_qlen_bytes instead.
-       Prior to linux 3.3, the default value is 3 which may cause
-       unexpected packet loss. The current default value is calculated
-       according to default value of unres_qlen_bytes and true size of
-       packet.
-       Default: 101
-
-mtu_expires - INTEGER
-       Time, in seconds, that cached PMTU information is kept.
-
-min_adv_mss - INTEGER
-       The advertised MSS depends on the first hop route MTU, but will
-       never be lower than this setting.
-
-IP Fragmentation:
-
-ipfrag_high_thresh - LONG INTEGER
-       Maximum memory used to reassemble IP fragments.
-
-ipfrag_low_thresh - LONG INTEGER
-       (Obsolete since linux-4.17)
-       Maximum memory used to reassemble IP fragments before the kernel
-       begins to remove incomplete fragment queues to free up resources.
-       The kernel still accepts new fragments for defragmentation.
-
-ipfrag_time - INTEGER
-       Time in seconds to keep an IP fragment in memory.
-
-ipfrag_max_dist - INTEGER
-       ipfrag_max_dist is a non-negative integer value which defines the
-       maximum "disorder" which is allowed among fragments which share a
-       common IP source address. Note that reordering of packets is
-       not unusual, but if a large number of fragments arrive from a source
-       IP address while a particular fragment queue remains incomplete, it
-       probably indicates that one or more fragments belonging to that queue
-       have been lost. When ipfrag_max_dist is positive, an additional check
-       is done on fragments before they are added to a reassembly queue - if
-       ipfrag_max_dist (or more) fragments have arrived from a particular IP
-       address between additions to any IP fragment queue using that source
-       address, it's presumed that one or more fragments in the queue are
-       lost. The existing fragment queue will be dropped, and a new one
-       started. An ipfrag_max_dist value of zero disables this check.
-
-       Using a very small value, e.g. 1 or 2, for ipfrag_max_dist can
-       result in unnecessarily dropping fragment queues when normal
-       reordering of packets occurs, which could lead to poor application
-       performance. Using a very large value, e.g. 50000, increases the
-       likelihood of incorrectly reassembling IP fragments that originate
-       from different IP datagrams, which could result in data corruption.
-       Default: 64
-
-INET peer storage:
-
-inet_peer_threshold - INTEGER
-       The approximate size of the storage.  Starting from this threshold
-       entries will be thrown aggressively.  This threshold also determines
-       entries' time-to-live and time intervals between garbage collection
-       passes.  More entries, less time-to-live, less GC interval.
-
-inet_peer_minttl - INTEGER
-       Minimum time-to-live of entries.  Should be enough to cover fragment
-       time-to-live on the reassembling side.  This minimum time-to-live  is
-       guaranteed if the pool size is less than inet_peer_threshold.
-       Measured in seconds.
-
-inet_peer_maxttl - INTEGER
-       Maximum time-to-live of entries.  Unused entries will expire after
-       this period of time if there is no memory pressure on the pool (i.e.
-       when the number of entries in the pool is very small).
-       Measured in seconds.
-
-TCP variables:
-
-somaxconn - INTEGER
-       Limit of socket listen() backlog, known in userspace as SOMAXCONN.
-       Defaults to 4096. (Was 128 before linux-5.4)
-       See also tcp_max_syn_backlog for additional tuning for TCP sockets.
-
-tcp_abort_on_overflow - BOOLEAN
-       If listening service is too slow to accept new connections,
-       reset them. Default state is FALSE. It means that if overflow
-       occurred due to a burst, connection will recover. Enable this
-       option _only_ if you are really sure that listening daemon
-       cannot be tuned to accept connections faster. Enabling this
-       option can harm clients of your server.
-
-tcp_adv_win_scale - INTEGER
-       Count buffering overhead as bytes/2^tcp_adv_win_scale
-       (if tcp_adv_win_scale > 0) or bytes-bytes/2^(-tcp_adv_win_scale),
-       if it is <= 0.
-       Possible values are [-31, 31], inclusive.
-       Default: 1
-
-tcp_allowed_congestion_control - STRING
-       Show/set the congestion control choices available to non-privileged
-       processes. The list is a subset of those listed in
-       tcp_available_congestion_control.
-       Default is "reno" and the default setting (tcp_congestion_control).
-
-tcp_app_win - INTEGER
-       Reserve max(window/2^tcp_app_win, mss) of window for application
-       buffer. Value 0 is special, it means that nothing is reserved.
-       Default: 31
-
-tcp_autocorking - BOOLEAN
-       Enable TCP auto corking :
-       When applications do consecutive small write()/sendmsg() system calls,
-       we try to coalesce these small writes as much as possible, to lower
-       total amount of sent packets. This is done if at least one prior
-       packet for the flow is waiting in Qdisc queues or device transmit
-       queue. Applications can still use TCP_CORK for optimal behavior
-       when they know how/when to uncork their sockets.
-       Default : 1
-
-tcp_available_congestion_control - STRING
-       Shows the available congestion control choices that are registered.
-       More congestion control algorithms may be available as modules,
-       but not loaded.
-
-tcp_base_mss - INTEGER
-       The initial value of search_low to be used by the packetization layer
-       Path MTU discovery (MTU probing).  If MTU probing is enabled,
-       this is the initial MSS used by the connection.
-
-tcp_mtu_probe_floor - INTEGER
-       If MTU probing is enabled this caps the minimum MSS used for search_low
-       for the connection.
-
-       Default : 48
-
-tcp_min_snd_mss - INTEGER
-       TCP SYN and SYNACK messages usually advertise an ADVMSS option,
-       as described in RFC 1122 and RFC 6691.
-       If this ADVMSS option is smaller than tcp_min_snd_mss,
-       it is silently capped to tcp_min_snd_mss.
-
-       Default : 48 (at least 8 bytes of payload per segment)
-
-tcp_congestion_control - STRING
-       Set the congestion control algorithm to be used for new
-       connections. The algorithm "reno" is always available, but
-       additional choices may be available based on kernel configuration.
-       Default is set as part of kernel configuration.
-       For passive connections, the listener congestion control choice
-       is inherited.
-       [see setsockopt(listenfd, SOL_TCP, TCP_CONGESTION, "name" ...) ]
-
-tcp_dsack - BOOLEAN
-       Allows TCP to send "duplicate" SACKs.
-
-tcp_early_retrans - INTEGER
-       Tail loss probe (TLP) converts RTOs occurring due to tail
-       losses into fast recovery (draft-ietf-tcpm-rack). Note that
-       TLP requires RACK to function properly (see tcp_recovery below)
-       Possible values:
-               0 disables TLP
-               3 or 4 enables TLP
-       Default: 3
-
-tcp_ecn - INTEGER
-       Control use of Explicit Congestion Notification (ECN) by TCP.
-       ECN is used only when both ends of the TCP connection indicate
-       support for it.  This feature is useful in avoiding losses due
-       to congestion by allowing supporting routers to signal
-       congestion before having to drop packets.
-       Possible values are:
-               0 Disable ECN.  Neither initiate nor accept ECN.
-               1 Enable ECN when requested by incoming connections and
-                 also request ECN on outgoing connection attempts.
-               2 Enable ECN when requested by incoming connections
-                 but do not request ECN on outgoing connections.
-       Default: 2
-
-tcp_ecn_fallback - BOOLEAN
-       If the kernel detects that ECN connection misbehaves, enable fall
-       back to non-ECN. Currently, this knob implements the fallback
-       from RFC3168, section 6.1.1.1., but we reserve that in future,
-       additional detection mechanisms could be implemented under this
-       knob. The value is not used, if tcp_ecn or per route (or congestion
-       control) ECN settings are disabled.
-       Default: 1 (fallback enabled)
-
-tcp_fack - BOOLEAN
-       This is a legacy option, it has no effect anymore.
-
-tcp_fin_timeout - INTEGER
-       The length of time an orphaned (no longer referenced by any
-       application) connection will remain in the FIN_WAIT_2 state
-       before it is aborted at the local end.  While a perfectly
-       valid "receive only" state for an un-orphaned connection, an
-       orphaned connection in FIN_WAIT_2 state could otherwise wait
-       forever for the remote to close its end of the connection.
-       Cf. tcp_max_orphans
-       Default: 60 seconds
-
-tcp_frto - INTEGER
-       Enables Forward RTO-Recovery (F-RTO) defined in RFC5682.
-       F-RTO is an enhanced recovery algorithm for TCP retransmission
-       timeouts.  It is particularly beneficial in networks where the
-       RTT fluctuates (e.g., wireless). F-RTO is sender-side only
-       modification. It does not require any support from the peer.
-
-       By default it's enabled with a non-zero value. 0 disables F-RTO.
-
-tcp_fwmark_accept - BOOLEAN
-       If set, incoming connections to listening sockets that do not have a
-       socket mark will set the mark of the accepting socket to the fwmark of
-       the incoming SYN packet. This will cause all packets on that connection
-       (starting from the first SYNACK) to be sent with that fwmark. The
-       listening socket's mark is unchanged. Listening sockets that already
-       have a fwmark set via setsockopt(SOL_SOCKET, SO_MARK, ...) are
-       unaffected.
-
-       Default: 0
-
-tcp_invalid_ratelimit - INTEGER
-       Limit the maximal rate for sending duplicate acknowledgments
-       in response to incoming TCP packets that are for an existing
-       connection but that are invalid due to any of these reasons:
-
-         (a) out-of-window sequence number,
-         (b) out-of-window acknowledgment number, or
-         (c) PAWS (Protection Against Wrapped Sequence numbers) check failure
-
-       This can help mitigate simple "ack loop" DoS attacks, wherein
-       a buggy or malicious middlebox or man-in-the-middle can
-       rewrite TCP header fields in manner that causes each endpoint
-       to think that the other is sending invalid TCP segments, thus
-       causing each side to send an unterminating stream of duplicate
-       acknowledgments for invalid segments.
-
-       Using 0 disables rate-limiting of dupacks in response to
-       invalid segments; otherwise this value specifies the minimal
-       space between sending such dupacks, in milliseconds.
-
-       Default: 500 (milliseconds).
-
-tcp_keepalive_time - INTEGER
-       How often TCP sends out keepalive messages when keepalive is enabled.
-       Default: 2hours.
-
-tcp_keepalive_probes - INTEGER
-       How many keepalive probes TCP sends out, until it decides that the
-       connection is broken. Default value: 9.
-
-tcp_keepalive_intvl - INTEGER
-       How frequently the probes are send out. Multiplied by
-       tcp_keepalive_probes it is time to kill not responding connection,
-       after probes started. Default value: 75sec i.e. connection
-       will be aborted after ~11 minutes of retries.
-
-tcp_l3mdev_accept - BOOLEAN
-       Enables child sockets to inherit the L3 master device index.
-       Enabling this option allows a "global" listen socket to work
-       across L3 master domains (e.g., VRFs) with connected sockets
-       derived from the listen socket to be bound to the L3 domain in
-       which the packets originated. Only valid when the kernel was
-       compiled with CONFIG_NET_L3_MASTER_DEV.
-        Default: 0 (disabled)
-
-tcp_low_latency - BOOLEAN
-       This is a legacy option, it has no effect anymore.
-
-tcp_max_orphans - INTEGER
-       Maximal number of TCP sockets not attached to any user file handle,
-       held by system. If this number is exceeded orphaned connections are
-       reset immediately and warning is printed. This limit exists
-       only to prevent simple DoS attacks, you _must_ not rely on this
-       or lower the limit artificially, but rather increase it
-       (probably, after increasing installed memory),
-       if network conditions require more than default value,
-       and tune network services to linger and kill such states
-       more aggressively. Let me to remind again: each orphan eats
-       up to ~64K of unswappable memory.
-
-tcp_max_syn_backlog - INTEGER
-       Maximal number of remembered connection requests (SYN_RECV),
-       which have not received an acknowledgment from connecting client.
-       This is a per-listener limit.
-       The minimal value is 128 for low memory machines, and it will
-       increase in proportion to the memory of machine.
-       If server suffers from overload, try increasing this number.
-       Remember to also check /proc/sys/net/core/somaxconn
-       A SYN_RECV request socket consumes about 304 bytes of memory.
-
-tcp_max_tw_buckets - INTEGER
-       Maximal number of timewait sockets held by system simultaneously.
-       If this number is exceeded time-wait socket is immediately destroyed
-       and warning is printed. This limit exists only to prevent
-       simple DoS attacks, you _must_ not lower the limit artificially,
-       but rather increase it (probably, after increasing installed memory),
-       if network conditions require more than default value.
-
-tcp_mem - vector of 3 INTEGERs: min, pressure, max
-       min: below this number of pages TCP is not bothered about its
-       memory appetite.
-
-       pressure: when amount of memory allocated by TCP exceeds this number
-       of pages, TCP moderates its memory consumption and enters memory
-       pressure mode, which is exited when memory consumption falls
-       under "min".
-
-       max: number of pages allowed for queueing by all TCP sockets.
-
-       Defaults are calculated at boot time from amount of available
-       memory.
-
-tcp_min_rtt_wlen - INTEGER
-       The window length of the windowed min filter to track the minimum RTT.
-       A shorter window lets a flow more quickly pick up new (higher)
-       minimum RTT when it is moved to a longer path (e.g., due to traffic
-       engineering). A longer window makes the filter more resistant to RTT
-       inflations such as transient congestion. The unit is seconds.
-       Possible values: 0 - 86400 (1 day)
-       Default: 300
-
-tcp_moderate_rcvbuf - BOOLEAN
-       If set, TCP performs receive buffer auto-tuning, attempting to
-       automatically size the buffer (no greater than tcp_rmem[2]) to
-       match the size required by the path for full throughput.  Enabled by
-       default.
-
-tcp_mtu_probing - INTEGER
-       Controls TCP Packetization-Layer Path MTU Discovery.  Takes three
-       values:
-         0 - Disabled
-         1 - Disabled by default, enabled when an ICMP black hole detected
-         2 - Always enabled, use initial MSS of tcp_base_mss.
-
-tcp_probe_interval - UNSIGNED INTEGER
-       Controls how often to start TCP Packetization-Layer Path MTU
-       Discovery reprobe. The default is reprobing every 10 minutes as
-       per RFC4821.
-
-tcp_probe_threshold - INTEGER
-       Controls when TCP Packetization-Layer Path MTU Discovery probing
-       will stop in respect to the width of search range in bytes. Default
-       is 8 bytes.
-
-tcp_no_metrics_save - BOOLEAN
-       By default, TCP saves various connection metrics in the route cache
-       when the connection closes, so that connections established in the
-       near future can use these to set initial conditions.  Usually, this
-       increases overall performance, but may sometimes cause performance
-       degradation.  If set, TCP will not cache metrics on closing
-       connections.
-
-tcp_no_ssthresh_metrics_save - BOOLEAN
-       Controls whether TCP saves ssthresh metrics in the route cache.
-       Default is 1, which disables ssthresh metrics.
-
-tcp_orphan_retries - INTEGER
-       This value influences the timeout of a locally closed TCP connection,
-       when RTO retransmissions remain unacknowledged.
-       See tcp_retries2 for more details.
-
-       The default value is 8.
-       If your machine is a loaded WEB server,
-       you should think about lowering this value, such sockets
-       may consume significant resources. Cf. tcp_max_orphans.
-
-tcp_recovery - INTEGER
-       This value is a bitmap to enable various experimental loss recovery
-       features.
-
-       RACK: 0x1 enables the RACK loss detection for fast detection of lost
-             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
-
-tcp_reordering - INTEGER
-       Initial reordering level of packets in a TCP stream.
-       TCP stack can then dynamically adjust flow reordering level
-       between this initial value and tcp_max_reordering
-       Default: 3
-
-tcp_max_reordering - INTEGER
-       Maximal reordering level of packets in a TCP stream.
-       300 is a fairly conservative value, but you might increase it
-       if paths are using per packet load balancing (like bonding rr mode)
-       Default: 300
-
-tcp_retrans_collapse - BOOLEAN
-       Bug-to-bug compatibility with some broken printers.
-       On retransmit try to send bigger packets to work around bugs in
-       certain TCP stacks.
-
-tcp_retries1 - INTEGER
-       This value influences the time, after which TCP decides, that
-       something is wrong due to unacknowledged RTO retransmissions,
-       and reports this suspicion to the network layer.
-       See tcp_retries2 for more details.
-
-       RFC 1122 recommends at least 3 retransmissions, which is the
-       default.
-
-tcp_retries2 - INTEGER
-       This value influences the timeout of an alive TCP connection,
-       when RTO retransmissions remain unacknowledged.
-       Given a value of N, a hypothetical TCP connection following
-       exponential backoff with an initial RTO of TCP_RTO_MIN would
-       retransmit N times before killing the connection at the (N+1)th RTO.
-
-       The default value of 15 yields a hypothetical timeout of 924.6
-       seconds and is a lower bound for the effective timeout.
-       TCP will effectively time out at the first RTO which exceeds the
-       hypothetical timeout.
-
-       RFC 1122 recommends at least 100 seconds for the timeout,
-       which corresponds to a value of at least 8.
-
-tcp_rfc1337 - BOOLEAN
-       If set, the TCP stack behaves conforming to RFC1337. If unset,
-       we are not conforming to RFC, but prevent TCP TIME_WAIT
-       assassination.
-       Default: 0
-
-tcp_rmem - vector of 3 INTEGERs: min, default, max
-       min: Minimal size of receive buffer used by TCP sockets.
-       It is guaranteed to each TCP socket, even under moderate memory
-       pressure.
-       Default: 4K
-
-       default: initial size of receive buffer used by TCP sockets.
-       This value overrides net.core.rmem_default used by other protocols.
-       Default: 87380 bytes. This value results in window of 65535 with
-       default setting of tcp_adv_win_scale and tcp_app_win:0 and a bit
-       less for default tcp_app_win. See below about these variables.
-
-       max: maximal size of receive buffer allowed for automatically
-       selected receiver buffers for TCP socket. This value does not override
-       net.core.rmem_max.  Calling setsockopt() with SO_RCVBUF disables
-       automatic tuning of that socket's receive buffer size, in which
-       case this value is ignored.
-       Default: between 87380B and 6MB, depending on RAM size.
-
-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 number of SACK that can be compressed.
-       Using 0 disables SACK compression.
-
-       Default : 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
-       the current RTO.  If unset, the congestion window will not
-       be timed out after an idle period.
-       Default: 1
-
-tcp_stdurg - BOOLEAN
-       Use the Host requirements interpretation of the TCP urgent pointer field.
-       Most hosts use the older BSD interpretation, so if you turn this on
-       Linux might not communicate correctly with them.
-       Default: FALSE
-
-tcp_synack_retries - INTEGER
-       Number of times SYNACKs for a passive TCP connection attempt will
-       be retransmitted. Should not be higher than 255. Default value
-       is 5, which corresponds to 31seconds till the last retransmission
-       with the current initial RTO of 1second. With this the final timeout
-       for a passive TCP connection will happen after 63seconds.
-
-tcp_syncookies - INTEGER
-       Only valid when the kernel was compiled with CONFIG_SYN_COOKIES
-       Send out syncookies when the syn backlog queue of a socket
-       overflows. This is to prevent against the common 'SYN flood attack'
-       Default: 1
-
-       Note, that syncookies is fallback facility.
-       It MUST NOT be used to help highly loaded servers to stand
-       against legal connection rate. If you see SYN flood warnings
-       in your logs, but investigation shows that they occur
-       because of overload with legal connections, you should tune
-       another parameters until this warning disappear.
-       See: tcp_max_syn_backlog, tcp_synack_retries, tcp_abort_on_overflow.
-
-       syncookies seriously violate TCP protocol, do not allow
-       to use TCP extensions, can result in serious degradation
-       of some services (f.e. SMTP relaying), visible not by you,
-       but your clients and relays, contacting you. While you see
-       SYN flood warnings in logs not being really flooded, your server
-       is seriously misconfigured.
-
-       If you want to test which effects syncookies have to your
-       network connections you can set this knob to 2 to enable
-       unconditionally generation of syncookies.
-
-tcp_fastopen - INTEGER
-       Enable TCP Fast Open (RFC7413) to send and accept data in the opening
-       SYN packet.
-
-       The client support is enabled by flag 0x1 (on by default). The client
-       then must use sendmsg() or sendto() with the MSG_FASTOPEN flag,
-       rather than connect() to send data in SYN.
-
-       The server support is enabled by flag 0x2 (off by default). Then
-       either enable for all listeners with another flag (0x400) or
-       enable individual listeners via TCP_FASTOPEN socket option with
-       the option value being the length of the syn-data backlog.
-
-       The values (bitmap) are
-         0x1: (client) enables sending data in the opening SYN on the client.
-         0x2: (server) enables the server support, i.e., allowing data in
-                       a SYN packet to be accepted and passed to the
-                       application before 3-way handshake finishes.
-         0x4: (client) send data in the opening SYN regardless of cookie
-                       availability and without a cookie option.
-       0x200: (server) accept data-in-SYN w/o any cookie option present.
-       0x400: (server) enable all listeners to support Fast Open by
-                       default without explicit TCP_FASTOPEN socket option.
-
-       Default: 0x1
-
-       Note that that additional client or server features are only
-       effective if the basic support (0x1 and 0x2) are enabled respectively.
-
-tcp_fastopen_blackhole_timeout_sec - INTEGER
-       Initial time period in second to disable Fastopen on active TCP sockets
-       when a TFO firewall blackhole issue happens.
-       This time period will grow exponentially when more blackhole issues
-       get detected right after Fastopen is re-enabled and will reset to
-       initial value when the blackhole issue goes away.
-       0 to disable the blackhole detection.
-       By default, it is set to 1hr.
-
-tcp_fastopen_key - list of comma separated 32-digit hexadecimal INTEGERs
-       The list consists of a primary key and an optional backup key. The
-       primary key is used for both creating and validating cookies, while the
-       optional backup key is only used for validating cookies. The purpose of
-       the backup key is to maximize TFO validation when keys are rotated.
-
-       A randomly chosen primary key may be configured by the kernel if
-       the tcp_fastopen sysctl is set to 0x400 (see above), or if the
-       TCP_FASTOPEN setsockopt() optname is set and a key has not been
-       previously configured via sysctl. If keys are configured via
-       setsockopt() by using the TCP_FASTOPEN_KEY optname, then those
-       per-socket keys will be used instead of any keys that are specified via
-       sysctl.
-
-       A key is specified as 4 8-digit hexadecimal integers which are separated
-       by a '-' as: xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx. Leading zeros may be
-       omitted. A primary and a backup key may be specified by separating them
-       by a comma. If only one key is specified, it becomes the primary key and
-       any previously configured backup keys are removed.
-
-tcp_syn_retries - INTEGER
-       Number of times initial SYNs for an active TCP connection attempt
-       will be retransmitted. Should not be higher than 127. Default value
-       is 6, which corresponds to 63seconds till the last retransmission
-       with the current initial RTO of 1second. With this the final timeout
-       for an active TCP connection attempt will happen after 127seconds.
-
-tcp_timestamps - INTEGER
-Enable timestamps as defined in RFC1323.
-       0: Disabled.
-       1: Enable timestamps as defined in RFC1323 and use random offset for
-       each connection rather than only using the current time.
-       2: Like 1, but without random offsets.
-       Default: 1
-
-tcp_min_tso_segs - INTEGER
-       Minimal number of segments per TSO frame.
-       Since linux-3.12, TCP does an automatic sizing of TSO frames,
-       depending on flow rate, instead of filling 64Kbytes packets.
-       For specific usages, it's possible to force TCP to build big
-       TSO frames. Note that TCP stack might split too big TSO packets
-       if available window is too small.
-       Default: 2
-
-tcp_pacing_ss_ratio - INTEGER
-       sk->sk_pacing_rate is set by TCP stack using a ratio applied
-       to current rate. (current_rate = cwnd * mss / srtt)
-       If TCP is in slow start, tcp_pacing_ss_ratio is applied
-       to let TCP probe for bigger speeds, assuming cwnd can be
-       doubled every other RTT.
-       Default: 200
-
-tcp_pacing_ca_ratio - INTEGER
-       sk->sk_pacing_rate is set by TCP stack using a ratio applied
-       to current rate. (current_rate = cwnd * mss / srtt)
-       If TCP is in congestion avoidance phase, tcp_pacing_ca_ratio
-       is applied to conservatively probe for bigger throughput.
-       Default: 120
-
-tcp_tso_win_divisor - INTEGER
-       This allows control over what percentage of the congestion window
-       can be consumed by a single TSO frame.
-       The setting of this parameter is a choice between burstiness and
-       building larger TSO frames.
-       Default: 3
-
-tcp_tw_reuse - INTEGER
-       Enable reuse of TIME-WAIT sockets for new connections when it is
-       safe from protocol viewpoint.
-       0 - disable
-       1 - global enable
-       2 - enable for loopback traffic only
-       It should not be changed without advice/request of technical
-       experts.
-       Default: 2
-
-tcp_window_scaling - BOOLEAN
-       Enable window scaling as defined in RFC1323.
-
-tcp_wmem - vector of 3 INTEGERs: min, default, max
-       min: Amount of memory reserved for send buffers for TCP sockets.
-       Each TCP socket has rights to use it due to fact of its birth.
-       Default: 4K
-
-       default: initial size of send buffer used by TCP sockets.  This
-       value overrides net.core.wmem_default used by other protocols.
-       It is usually lower than net.core.wmem_default.
-       Default: 16K
-
-       max: Maximal amount of memory allowed for automatically tuned
-       send buffers for TCP sockets. This value does not override
-       net.core.wmem_max.  Calling setsockopt() with SO_SNDBUF disables
-       automatic tuning of that socket's send buffer size, in which case
-       this value is ignored.
-       Default: between 64K and 4MB, depending on RAM size.
-
-tcp_notsent_lowat - UNSIGNED INTEGER
-       A TCP socket can control the amount of unsent bytes in its write queue,
-       thanks to TCP_NOTSENT_LOWAT socket option. poll()/select()/epoll()
-       reports POLLOUT events if the amount of unsent bytes is below a per
-       socket value, and if the write queue is not full. sendmsg() will
-       also not add new buffers if the limit is hit.
-
-       This global variable controls the amount of unsent data for
-       sockets not using TCP_NOTSENT_LOWAT. For these sockets, a change
-       to the global variable has immediate effect.
-
-       Default: UINT_MAX (0xFFFFFFFF)
-
-tcp_workaround_signed_windows - BOOLEAN
-       If set, assume no receipt of a window scaling option means the
-       remote TCP is broken and treats the window as a signed quantity.
-       If unset, assume the remote TCP is not broken even if we do
-       not receive a window scaling option from them.
-       Default: 0
-
-tcp_thin_linear_timeouts - BOOLEAN
-       Enable dynamic triggering of linear timeouts for thin streams.
-       If set, a check is performed upon retransmission by timeout to
-       determine if the stream is thin (less than 4 packets in flight).
-       As long as the stream is found to be thin, up to 6 linear
-       timeouts may be performed before exponential backoff mode is
-       initiated. This improves retransmission latency for
-       non-aggressive thin streams, often found to be time-dependent.
-       For more information on thin streams, see
-       Documentation/networking/tcp-thin.txt
-       Default: 0
-
-tcp_limit_output_bytes - INTEGER
-       Controls TCP Small Queue limit per tcp socket.
-       TCP bulk sender tends to increase packets in flight until it
-       gets losses notifications. With SNDBUF autotuning, this can
-       result in a large amount of packets queued on the local machine
-       (e.g.: qdiscs, CPU backlog, or device) hurting latency of other
-       flows, for typical pfifo_fast qdiscs.  tcp_limit_output_bytes
-       limits the number of bytes on qdisc or device to reduce artificial
-       RTT/cwnd and reduce bufferbloat.
-       Default: 1048576 (16 * 65536)
-
-tcp_challenge_ack_limit - INTEGER
-       Limits number of Challenge ACK sent per second, as recommended
-       in RFC 5961 (Improving TCP's Robustness to Blind In-Window Attacks)
-       Default: 100
-
-tcp_rx_skb_cache - BOOLEAN
-       Controls a per TCP socket cache of one skb, that might help
-       performance of some workloads. This might be dangerous
-       on systems with a lot of TCP sockets, since it increases
-       memory usage.
-
-       Default: 0 (disabled)
-
-UDP variables:
-
-udp_l3mdev_accept - BOOLEAN
-       Enabling this option allows a "global" bound socket to work
-       across L3 master domains (e.g., VRFs) with packets capable of
-       being received regardless of the L3 domain in which they
-       originated. Only valid when the kernel was compiled with
-       CONFIG_NET_L3_MASTER_DEV.
-        Default: 0 (disabled)
-
-udp_mem - vector of 3 INTEGERs: min, pressure, max
-       Number of pages allowed for queueing by all UDP sockets.
-
-       min: Below this number of pages UDP is not bothered about its
-       memory appetite. When amount of memory allocated by UDP exceeds
-       this number, UDP starts to moderate memory usage.
-
-       pressure: This value was introduced to follow format of tcp_mem.
-
-       max: Number of pages allowed for queueing by all UDP sockets.
-
-       Default is calculated at boot time from amount of available memory.
-
-udp_rmem_min - INTEGER
-       Minimal size of receive buffer used by UDP sockets in moderation.
-       Each UDP socket is able to use the size for receiving data, even if
-       total pages of UDP sockets exceed udp_mem pressure. The unit is byte.
-       Default: 4K
-
-udp_wmem_min - INTEGER
-       Minimal size of send buffer used by UDP sockets in moderation.
-       Each UDP socket is able to use the size for sending data, even if
-       total pages of UDP sockets exceed udp_mem pressure. The unit is byte.
-       Default: 4K
-
-RAW variables:
-
-raw_l3mdev_accept - BOOLEAN
-       Enabling this option allows a "global" bound socket to work
-       across L3 master domains (e.g., VRFs) with packets capable of
-       being received regardless of the L3 domain in which they
-       originated. Only valid when the kernel was compiled with
-       CONFIG_NET_L3_MASTER_DEV.
-       Default: 1 (enabled)
-
-CIPSOv4 Variables:
-
-cipso_cache_enable - BOOLEAN
-       If set, enable additions to and lookups from the CIPSO label mapping
-       cache.  If unset, additions are ignored and lookups always result in a
-       miss.  However, regardless of the setting the cache is still
-       invalidated when required when means you can safely toggle this on and
-       off and the cache will always be "safe".
-       Default: 1
-
-cipso_cache_bucket_size - INTEGER
-       The CIPSO label cache consists of a fixed size hash table with each
-       hash bucket containing a number of cache entries.  This variable limits
-       the number of entries in each hash bucket; the larger the value the
-       more CIPSO label mappings that can be cached.  When the number of
-       entries in a given hash bucket reaches this limit adding new entries
-       causes the oldest entry in the bucket to be removed to make room.
-       Default: 10
-
-cipso_rbm_optfmt - BOOLEAN
-       Enable the "Optimized Tag 1 Format" as defined in section 3.4.2.6 of
-       the CIPSO draft specification (see Documentation/netlabel for details).
-       This means that when set the CIPSO tag will be padded with empty
-       categories in order to make the packet data 32-bit aligned.
-       Default: 0
-
-cipso_rbm_structvalid - BOOLEAN
-       If set, do a very strict check of the CIPSO option when
-       ip_options_compile() is called.  If unset, relax the checks done during
-       ip_options_compile().  Either way is "safe" as errors are caught else
-       where in the CIPSO processing code but setting this to 0 (False) should
-       result in less work (i.e. it should be faster) but could cause problems
-       with other implementations that require strict checking.
-       Default: 0
-
-IP Variables:
-
-ip_local_port_range - 2 INTEGERS
-       Defines the local port range that is used by TCP and UDP to
-       choose the local port. The first number is the first, the
-       second the last local port number.
-       If possible, it is better these numbers have different parity
-       (one even and one odd value).
-       Must be greater than or equal to ip_unprivileged_port_start.
-       The default values are 32768 and 60999 respectively.
-
-ip_local_reserved_ports - list of comma separated ranges
-       Specify the ports which are reserved for known third-party
-       applications. These ports will not be used by automatic port
-       assignments (e.g. when calling connect() or bind() with port
-       number 0). Explicit port allocation behavior is unchanged.
-
-       The format used for both input and output is a comma separated
-       list of ranges (e.g. "1,2-4,10-10" for ports 1, 2, 3, 4 and
-       10). Writing to the file will clear all previously reserved
-       ports and update the current list with the one given in the
-       input.
-
-       Note that ip_local_port_range and ip_local_reserved_ports
-       settings are independent and both are considered by the kernel
-       when determining which ports are available for automatic port
-       assignments.
-
-       You can reserve ports which are not in the current
-       ip_local_port_range, e.g.:
-
-       $ cat /proc/sys/net/ipv4/ip_local_port_range
-       32000   60999
-       $ cat /proc/sys/net/ipv4/ip_local_reserved_ports
-       8080,9148
-
-       although this is redundant. However such a setting is useful
-       if later the port range is changed to a value that will
-       include the reserved ports.
-
-       Default: Empty
-
-ip_unprivileged_port_start - INTEGER
-       This is a per-namespace sysctl.  It defines the first
-       unprivileged port in the network namespace.  Privileged ports
-       require root or CAP_NET_BIND_SERVICE in order to bind to them.
-       To disable all privileged ports, set this to 0.  They must not
-       overlap with the ip_local_port_range.
-
-       Default: 1024
-
-ip_nonlocal_bind - BOOLEAN
-       If set, allows processes to bind() to non-local IP addresses,
-       which can be quite useful - but may break some applications.
-       Default: 0
-
-ip_autobind_reuse - BOOLEAN
-       By default, bind() does not select the ports automatically even if
-       the new socket and all sockets bound to the port have SO_REUSEADDR.
-       ip_autobind_reuse allows bind() to reuse the port and this is useful
-       when you use bind()+connect(), but may break some applications.
-       The preferred solution is to use IP_BIND_ADDRESS_NO_PORT and this
-       option should only be set by experts.
-       Default: 0
-
-ip_dynaddr - BOOLEAN
-       If set non-zero, enables support for dynamic addresses.
-       If set to a non-zero value larger than 1, a kernel log
-       message will be printed when dynamic address rewriting
-       occurs.
-       Default: 0
-
-ip_early_demux - BOOLEAN
-       Optimize input packet processing down to one demux for
-       certain kinds of local sockets.  Currently we only do this
-       for established TCP and connected UDP sockets.
-
-       It may add an additional cost for pure routing workloads that
-       reduces overall throughput, in such case you should disable it.
-       Default: 1
-
-tcp_early_demux - BOOLEAN
-       Enable early demux for established TCP sockets.
-       Default: 1
-
-udp_early_demux - BOOLEAN
-       Enable early demux for connected UDP sockets. Disable this if
-       your system could experience more unconnected load.
-       Default: 1
-
-icmp_echo_ignore_all - BOOLEAN
-       If set non-zero, then the kernel will ignore all ICMP ECHO
-       requests sent to it.
-       Default: 0
-
-icmp_echo_ignore_broadcasts - BOOLEAN
-       If set non-zero, then the kernel will ignore all ICMP ECHO and
-       TIMESTAMP requests sent to it via broadcast/multicast.
-       Default: 1
-
-icmp_ratelimit - INTEGER
-       Limit the maximal rates for sending ICMP packets whose type matches
-       icmp_ratemask (see below) to specific targets.
-       0 to disable any limiting,
-       otherwise the minimal space between responses in milliseconds.
-       Note that another sysctl, icmp_msgs_per_sec limits the number
-       of ICMP packets sent on all targets.
-       Default: 1000
-
-icmp_msgs_per_sec - INTEGER
-       Limit maximal number of ICMP packets sent per second from this host.
-       Only messages whose type matches icmp_ratemask (see below) are
-       controlled by this limit.
-       Default: 1000
-
-icmp_msgs_burst - INTEGER
-       icmp_msgs_per_sec controls number of ICMP packets sent per second,
-       while icmp_msgs_burst controls the burst size of these packets.
-       Default: 50
-
-icmp_ratemask - INTEGER
-       Mask made of ICMP types for which rates are being limited.
-       Significant bits: IHGFEDCBA9876543210
-       Default mask:     0000001100000011000 (6168)
-
-       Bit definitions (see include/linux/icmp.h):
-               0 Echo Reply
-               3 Destination Unreachable *
-               4 Source Quench *
-               5 Redirect
-               8 Echo Request
-               B Time Exceeded *
-               C Parameter Problem *
-               D Timestamp Request
-               E Timestamp Reply
-               F Info Request
-               G Info Reply
-               H Address Mask Request
-               I Address Mask Reply
-
-       * These are rate limited by default (see default mask above)
-
-icmp_ignore_bogus_error_responses - BOOLEAN
-       Some routers violate RFC1122 by sending bogus responses to broadcast
-       frames.  Such violations are normally logged via a kernel warning.
-       If this is set to TRUE, the kernel will not give such warnings, which
-       will avoid log file clutter.
-       Default: 1
-
-icmp_errors_use_inbound_ifaddr - BOOLEAN
-
-       If zero, icmp error messages are sent with the primary address of
-       the exiting interface.
-
-       If non-zero, the message will be sent with the primary address of
-       the interface that received the packet that caused the icmp error.
-       This is the behaviour network many administrators will expect from
-       a router. And it can make debugging complicated network layouts
-       much easier.
-
-       Note that if no primary address exists for the interface selected,
-       then the primary address of the first non-loopback interface that
-       has one will be used regardless of this setting.
-
-       Default: 0
-
-igmp_max_memberships - INTEGER
-       Change the maximum number of multicast groups we can subscribe to.
-       Default: 20
-
-       Theoretical maximum value is bounded by having to send a membership
-       report in a single datagram (i.e. the report can't span multiple
-       datagrams, or risk confusing the switch and leaving groups you don't
-       intend to).
-
-       The number of supported groups 'M' is bounded by the number of group
-       report entries you can fit into a single datagram of 65535 bytes.
-
-       M = 65536-sizeof (ip header)/(sizeof(Group record))
-
-       Group records are variable length, with a minimum of 12 bytes.
-       So net.ipv4.igmp_max_memberships should not be set higher than:
-
-       (65536-24) / 12 = 5459
-
-       The value 5459 assumes no IP header options, so in practice
-       this number may be lower.
-
-igmp_max_msf - INTEGER
-       Maximum number of addresses allowed in the source filter list for a
-       multicast group.
-       Default: 10
-
-igmp_qrv - INTEGER
-       Controls the IGMP query robustness variable (see RFC2236 8.1).
-       Default: 2 (as specified by RFC2236 8.1)
-       Minimum: 1 (as specified by RFC6636 4.5)
-
-force_igmp_version - INTEGER
-       0 - (default) No enforcement of a IGMP version, IGMPv1/v2 fallback
-           allowed. Will back to IGMPv3 mode again if all IGMPv1/v2 Querier
-           Present timer expires.
-       1 - Enforce to use IGMP version 1. Will also reply IGMPv1 report if
-           receive IGMPv2/v3 query.
-       2 - Enforce to use IGMP version 2. Will fallback to IGMPv1 if receive
-           IGMPv1 query message. Will reply report if receive IGMPv3 query.
-       3 - Enforce to use IGMP version 3. The same react with default 0.
-
-       Note: this is not the same with force_mld_version because IGMPv3 RFC3376
-       Security Considerations does not have clear description that we could
-       ignore other version messages completely as MLDv2 RFC3810. So make
-       this value as default 0 is recommended.
-
-conf/interface/*  changes special settings per interface (where
-"interface" is the name of your network interface)
-
-conf/all/*       is special, changes the settings for all interfaces
-
-log_martians - BOOLEAN
-       Log packets with impossible addresses to kernel log.
-       log_martians for the interface will be enabled if at least one of
-       conf/{all,interface}/log_martians is set to TRUE,
-       it will be disabled otherwise
-
-accept_redirects - BOOLEAN
-       Accept ICMP redirect messages.
-       accept_redirects for the interface will be enabled if:
-       - both conf/{all,interface}/accept_redirects are TRUE in the case
-         forwarding for the interface is enabled
-       or
-       - at least one of conf/{all,interface}/accept_redirects is TRUE in the
-         case forwarding for the interface is disabled
-       accept_redirects for the interface will be disabled otherwise
-       default TRUE (host)
-               FALSE (router)
-
-forwarding - BOOLEAN
-       Enable IP forwarding on this interface.  This controls whether packets
-       received _on_ this interface can be forwarded.
-
-mc_forwarding - BOOLEAN
-       Do multicast routing. The kernel needs to be compiled with CONFIG_MROUTE
-       and a multicast routing daemon is required.
-       conf/all/mc_forwarding must also be set to TRUE to enable multicast
-       routing for the interface
-
-medium_id - INTEGER
-       Integer value used to differentiate the devices by the medium they
-       are attached to. Two devices can have different id values when
-       the broadcast packets are received only on one of them.
-       The default value 0 means that the device is the only interface
-       to its medium, value of -1 means that medium is not known.
-
-       Currently, it is used to change the proxy_arp behavior:
-       the proxy_arp feature is enabled for packets forwarded between
-       two devices attached to different media.
-
-proxy_arp - BOOLEAN
-       Do proxy arp.
-       proxy_arp for the interface will be enabled if at least one of
-       conf/{all,interface}/proxy_arp is set to TRUE,
-       it will be disabled otherwise
-
-proxy_arp_pvlan - BOOLEAN
-       Private VLAN proxy arp.
-       Basically allow proxy arp replies back to the same interface
-       (from which the ARP request/solicitation was received).
-
-       This is done to support (ethernet) switch features, like RFC
-       3069, where the individual ports are NOT allowed to
-       communicate with each other, but they are allowed to talk to
-       the upstream router.  As described in RFC 3069, it is possible
-       to allow these hosts to communicate through the upstream
-       router by proxy_arp'ing. Don't need to be used together with
-       proxy_arp.
-
-       This technology is known by different names:
-         In RFC 3069 it is called VLAN Aggregation.
-         Cisco and Allied Telesyn call it Private VLAN.
-         Hewlett-Packard call it Source-Port filtering or port-isolation.
-         Ericsson call it MAC-Forced Forwarding (RFC Draft).
-
-shared_media - BOOLEAN
-       Send(router) or accept(host) RFC1620 shared media redirects.
-       Overrides secure_redirects.
-       shared_media for the interface will be enabled if at least one of
-       conf/{all,interface}/shared_media is set to TRUE,
-       it will be disabled otherwise
-       default TRUE
-
-secure_redirects - BOOLEAN
-       Accept ICMP redirect messages only to gateways listed in the
-       interface's current gateway list. Even if disabled, RFC1122 redirect
-       rules still apply.
-       Overridden by shared_media.
-       secure_redirects for the interface will be enabled if at least one of
-       conf/{all,interface}/secure_redirects is set to TRUE,
-       it will be disabled otherwise
-       default TRUE
-
-send_redirects - BOOLEAN
-       Send redirects, if router.
-       send_redirects for the interface will be enabled if at least one of
-       conf/{all,interface}/send_redirects is set to TRUE,
-       it will be disabled otherwise
-       Default: TRUE
-
-bootp_relay - BOOLEAN
-       Accept packets with source address 0.b.c.d destined
-       not to this host as local ones. It is supposed, that
-       BOOTP relay daemon will catch and forward such packets.
-       conf/all/bootp_relay must also be set to TRUE to enable BOOTP relay
-       for the interface
-       default FALSE
-       Not Implemented Yet.
-
-accept_source_route - BOOLEAN
-       Accept packets with SRR option.
-       conf/all/accept_source_route must also be set to TRUE to accept packets
-       with SRR option on the interface
-       default TRUE (router)
-               FALSE (host)
-
-accept_local - BOOLEAN
-       Accept packets with local source addresses. In combination with
-       suitable routing, this can be used to direct packets between two
-       local interfaces over the wire and have them accepted properly.
-       default FALSE
-
-route_localnet - BOOLEAN
-       Do not consider loopback addresses as martian source or destination
-       while routing. This enables the use of 127/8 for local routing purposes.
-       default FALSE
-
-rp_filter - INTEGER
-       0 - No source validation.
-       1 - Strict mode as defined in RFC3704 Strict Reverse Path
-           Each incoming packet is tested against the FIB and if the interface
-           is not the best reverse path the packet check will fail.
-           By default failed packets are discarded.
-       2 - Loose mode as defined in RFC3704 Loose Reverse Path
-           Each incoming packet's source address is also tested against the FIB
-           and if the source address is not reachable via any interface
-           the packet check will fail.
-
-       Current recommended practice in RFC3704 is to enable strict mode
-       to prevent IP spoofing from DDos attacks. If using asymmetric routing
-       or other complicated routing, then loose mode is recommended.
-
-       The max value from conf/{all,interface}/rp_filter is used
-       when doing source validation on the {interface}.
-
-       Default value is 0. Note that some distributions enable it
-       in startup scripts.
-
-arp_filter - BOOLEAN
-       1 - Allows you to have multiple network interfaces on the same
-       subnet, and have the ARPs for each interface be answered
-       based on whether or not the kernel would route a packet from
-       the ARP'd IP out that interface (therefore you must use source
-       based routing for this to work). In other words it allows control
-       of which cards (usually 1) will respond to an arp request.
-
-       0 - (default) The kernel can respond to arp requests with addresses
-       from other interfaces. This may seem wrong but it usually makes
-       sense, because it increases the chance of successful communication.
-       IP addresses are owned by the complete host on Linux, not by
-       particular interfaces. Only for more complex setups like load-
-       balancing, does this behaviour cause problems.
-
-       arp_filter for the interface will be enabled if at least one of
-       conf/{all,interface}/arp_filter is set to TRUE,
-       it will be disabled otherwise
-
-arp_announce - INTEGER
-       Define different restriction levels for announcing the local
-       source IP address from IP packets in ARP requests sent on
-       interface:
-       0 - (default) Use any local address, configured on any interface
-       1 - Try to avoid local addresses that are not in the target's
-       subnet for this interface. This mode is useful when target
-       hosts reachable via this interface require the source IP
-       address in ARP requests to be part of their logical network
-       configured on the receiving interface. When we generate the
-       request we will check all our subnets that include the
-       target IP and will preserve the source address if it is from
-       such subnet. If there is no such subnet we select source
-       address according to the rules for level 2.
-       2 - Always use the best local address for this target.
-       In this mode we ignore the source address in the IP packet
-       and try to select local address that we prefer for talks with
-       the target host. Such local address is selected by looking
-       for primary IP addresses on all our subnets on the outgoing
-       interface that include the target IP address. If no suitable
-       local address is found we select the first local address
-       we have on the outgoing interface or on all other interfaces,
-       with the hope we will receive reply for our request and
-       even sometimes no matter the source IP address we announce.
-
-       The max value from conf/{all,interface}/arp_announce is used.
-
-       Increasing the restriction level gives more chance for
-       receiving answer from the resolved target while decreasing
-       the level announces more valid sender's information.
-
-arp_ignore - INTEGER
-       Define different modes for sending replies in response to
-       received ARP requests that resolve local target IP addresses:
-       0 - (default): reply for any local target IP address, configured
-       on any interface
-       1 - reply only if the target IP address is local address
-       configured on the incoming interface
-       2 - reply only if the target IP address is local address
-       configured on the incoming interface and both with the
-       sender's IP address are part from same subnet on this interface
-       3 - do not reply for local addresses configured with scope host,
-       only resolutions for global and link addresses are replied
-       4-7 - reserved
-       8 - do not reply for all local addresses
-
-       The max value from conf/{all,interface}/arp_ignore is used
-       when ARP request is received on the {interface}
-
-arp_notify - BOOLEAN
-       Define mode for notification of address and device changes.
-       0 - (default): do nothing
-       1 - Generate gratuitous arp requests when device is brought up
-           or hardware address changes.
-
-arp_accept - BOOLEAN
-       Define behavior for gratuitous ARP frames who's IP is not
-       already present in the ARP table:
-       0 - don't create new entries in the ARP table
-       1 - create new entries in the ARP table
-
-       Both replies and requests type gratuitous arp will trigger the
-       ARP table to be updated, if this setting is on.
-
-       If the ARP table already contains the IP address of the
-       gratuitous arp frame, the arp table will be updated regardless
-       if this setting is on or off.
-
-mcast_solicit - INTEGER
-       The maximum number of multicast probes in INCOMPLETE state,
-       when the associated hardware address is unknown.  Defaults
-       to 3.
-
-ucast_solicit - INTEGER
-       The maximum number of unicast probes in PROBE state, when
-       the hardware address is being reconfirmed.  Defaults to 3.
-
-app_solicit - INTEGER
-       The maximum number of probes to send to the user space ARP daemon
-       via netlink before dropping back to multicast probes (see
-       mcast_resolicit).  Defaults to 0.
-
-mcast_resolicit - INTEGER
-       The maximum number of multicast probes after unicast and
-       app probes in PROBE state.  Defaults to 0.
-
-disable_policy - BOOLEAN
-       Disable IPSEC policy (SPD) for this interface
-
-disable_xfrm - BOOLEAN
-       Disable IPSEC encryption on this interface, whatever the policy
-
-igmpv2_unsolicited_report_interval - INTEGER
-       The interval in milliseconds in which the next unsolicited
-       IGMPv1 or IGMPv2 report retransmit will take place.
-       Default: 10000 (10 seconds)
-
-igmpv3_unsolicited_report_interval - INTEGER
-       The interval in milliseconds in which the next unsolicited
-       IGMPv3 report retransmit will take place.
-       Default: 1000 (1 seconds)
-
-promote_secondaries - BOOLEAN
-       When a primary IP address is removed from this interface
-       promote a corresponding secondary IP address instead of
-       removing all the corresponding secondary IP addresses.
-
-drop_unicast_in_l2_multicast - BOOLEAN
-       Drop any unicast IP packets that are received in link-layer
-       multicast (or broadcast) frames.
-       This behavior (for multicast) is actually a SHOULD in RFC
-       1122, but is disabled by default for compatibility reasons.
-       Default: off (0)
-
-drop_gratuitous_arp - BOOLEAN
-       Drop all gratuitous ARP frames, for example if there's a known
-       good ARP proxy on the network and such frames need not be used
-       (or in the case of 802.11, must not be used to prevent attacks.)
-       Default: off (0)
-
-
-tag - INTEGER
-       Allows you to write a number, which can be used as required.
-       Default value is 0.
-
-xfrm4_gc_thresh - INTEGER
-       (Obsolete since linux-4.14)
-       The threshold at which we will start garbage collecting for IPv4
-       destination cache entries.  At twice this value the system will
-       refuse new allocations.
-
-igmp_link_local_mcast_reports - BOOLEAN
-       Enable IGMP reports for link local multicast groups in the
-       224.0.0.X range.
-       Default TRUE
-
-Alexey Kuznetsov.
-kuznet@ms2.inr.ac.ru
-
-Updated by:
-Andi Kleen
-ak@muc.de
-Nicolas Delon
-delon.nicolas@wanadoo.fr
-
-
-
-
-/proc/sys/net/ipv6/* Variables:
-
-IPv6 has no global variables such as tcp_*.  tcp_* settings under ipv4/ also
-apply to IPv6 [XXX?].
-
-bindv6only - BOOLEAN
-       Default value for IPV6_V6ONLY socket option,
-       which restricts use of the IPv6 socket to IPv6 communication
-       only.
-               TRUE: disable IPv4-mapped address feature
-               FALSE: enable IPv4-mapped address feature
-
-       Default: FALSE (as specified in RFC3493)
-
-flowlabel_consistency - BOOLEAN
-       Protect the consistency (and unicity) of flow label.
-       You have to disable it to use IPV6_FL_F_REFLECT flag on the
-       flow label manager.
-       TRUE: enabled
-       FALSE: disabled
-       Default: TRUE
-
-auto_flowlabels - INTEGER
-       Automatically generate flow labels based on a flow hash of the
-       packet. This allows intermediate devices, such as routers, to
-       identify packet flows for mechanisms like Equal Cost Multipath
-       Routing (see RFC 6438).
-       0: automatic flow labels are completely disabled
-       1: automatic flow labels are enabled by default, they can be
-          disabled on a per socket basis using the IPV6_AUTOFLOWLABEL
-          socket option
-       2: automatic flow labels are allowed, they may be enabled on a
-          per socket basis using the IPV6_AUTOFLOWLABEL socket option
-       3: automatic flow labels are enabled and enforced, they cannot
-          be disabled by the socket option
-       Default: 1
-
-flowlabel_state_ranges - BOOLEAN
-       Split the flow label number space into two ranges. 0-0x7FFFF is
-       reserved for the IPv6 flow manager facility, 0x80000-0xFFFFF
-       is reserved for stateless flow labels as described in RFC6437.
-       TRUE: enabled
-       FALSE: disabled
-       Default: true
-
-flowlabel_reflect - INTEGER
-       Control flow label reflection. Needed for Path MTU
-       Discovery to work with Equal Cost Multipath Routing in anycast
-       environments. See RFC 7690 and:
-       https://tools.ietf.org/html/draft-wang-6man-flow-label-reflection-01
-
-       This is a bitmask.
-       1: enabled for established flows
-
-       Note that this prevents automatic flowlabel changes, as done
-       in "tcp: change IPv6 flow-label upon receiving spurious retransmission"
-       and "tcp: Change txhash on every SYN and RTO retransmit"
-
-       2: enabled for TCP RESET packets (no active listener)
-       If set, a RST packet sent in response to a SYN packet on a closed
-       port will reflect the incoming flow label.
-
-       4: enabled for ICMPv6 echo reply messages.
-
-       Default: 0
-
-fib_multipath_hash_policy - INTEGER
-       Controls which hash policy to use for multipath routes.
-       Default: 0 (Layer 3)
-       Possible values:
-       0 - Layer 3 (source and destination addresses plus flow label)
-       1 - Layer 4 (standard 5-tuple)
-       2 - Layer 3 or inner Layer 3 if present
-
-anycast_src_echo_reply - BOOLEAN
-       Controls the use of anycast addresses as source addresses for ICMPv6
-       echo reply
-       TRUE:  enabled
-       FALSE: disabled
-       Default: FALSE
-
-idgen_delay - INTEGER
-       Controls the delay in seconds after which time to retry
-       privacy stable address generation if a DAD conflict is
-       detected.
-       Default: 1 (as specified in RFC7217)
-
-idgen_retries - INTEGER
-       Controls the number of retries to generate a stable privacy
-       address if a DAD conflict is detected.
-       Default: 3 (as specified in RFC7217)
-
-mld_qrv - INTEGER
-       Controls the MLD query robustness variable (see RFC3810 9.1).
-       Default: 2 (as specified by RFC3810 9.1)
-       Minimum: 1 (as specified by RFC6636 4.5)
-
-max_dst_opts_number - INTEGER
-       Maximum number of non-padding TLVs allowed in a Destination
-       options extension header. If this value is less than zero
-       then unknown options are disallowed and the number of known
-       TLVs allowed is the absolute value of this number.
-       Default: 8
-
-max_hbh_opts_number - INTEGER
-       Maximum number of non-padding TLVs allowed in a Hop-by-Hop
-       options extension header. If this value is less than zero
-       then unknown options are disallowed and the number of known
-       TLVs allowed is the absolute value of this number.
-       Default: 8
-
-max_dst_opts_length - INTEGER
-       Maximum length allowed for a Destination options extension
-       header.
-       Default: INT_MAX (unlimited)
-
-max_hbh_length - INTEGER
-       Maximum length allowed for a Hop-by-Hop options extension
-       header.
-       Default: INT_MAX (unlimited)
-
-skip_notify_on_dev_down - BOOLEAN
-       Controls whether an RTM_DELROUTE message is generated for routes
-       removed when a device is taken down or deleted. IPv4 does not
-       generate this message; IPv6 does by default. Setting this sysctl
-       to true skips the message, making IPv4 and IPv6 on par in relying
-       on userspace caches to track link events and evict routes.
-       Default: false (generate message)
-
-IPv6 Fragmentation:
-
-ip6frag_high_thresh - INTEGER
-       Maximum memory used to reassemble IPv6 fragments. When
-       ip6frag_high_thresh bytes of memory is allocated for this purpose,
-       the fragment handler will toss packets until ip6frag_low_thresh
-       is reached.
-
-ip6frag_low_thresh - INTEGER
-       See ip6frag_high_thresh
-
-ip6frag_time - INTEGER
-       Time in seconds to keep an IPv6 fragment in memory.
-
-IPv6 Segment Routing:
-
-seg6_flowlabel - INTEGER
-       Controls the behaviour of computing the flowlabel of outer
-       IPv6 header in case of SR T.encaps
-
-       -1 set flowlabel to zero.
-       0 copy flowlabel from Inner packet in case of Inner IPv6
-               (Set flowlabel to 0 in case IPv4/L2)
-       1 Compute the flowlabel using seg6_make_flowlabel()
-
-       Default is 0.
-
-conf/default/*:
-       Change the interface-specific default settings.
-
-
-conf/all/*:
-       Change all the interface-specific settings.
-
-       [XXX:  Other special features than forwarding?]
-
-conf/all/forwarding - BOOLEAN
-       Enable global IPv6 forwarding between all interfaces.
-
-       IPv4 and IPv6 work differently here; e.g. netfilter must be used
-       to control which interfaces may forward packets and which not.
-
-       This also sets all interfaces' Host/Router setting
-       'forwarding' to the specified value.  See below for details.
-
-       This referred to as global forwarding.
-
-proxy_ndp - BOOLEAN
-       Do proxy ndp.
-
-fwmark_reflect - BOOLEAN
-       Controls the fwmark of kernel-generated IPv6 reply packets that are not
-       associated with a socket for example, TCP RSTs or ICMPv6 echo replies).
-       If unset, these packets have a fwmark of zero. If set, they have the
-       fwmark of the packet they are replying to.
-       Default: 0
-
-conf/interface/*:
-       Change special settings per interface.
-
-       The functional behaviour for certain settings is different
-       depending on whether local forwarding is enabled or not.
-
-accept_ra - INTEGER
-       Accept Router Advertisements; autoconfigure using them.
-
-       It also determines whether or not to transmit Router
-       Solicitations. If and only if the functional setting is to
-       accept Router Advertisements, Router Solicitations will be
-       transmitted.
-
-       Possible values are:
-               0 Do not accept Router Advertisements.
-               1 Accept Router Advertisements if forwarding is disabled.
-               2 Overrule forwarding behaviour. Accept Router Advertisements
-                 even if forwarding is enabled.
-
-       Functional default: enabled if local forwarding is disabled.
-                           disabled if local forwarding is enabled.
-
-accept_ra_defrtr - BOOLEAN
-       Learn default router in Router Advertisement.
-
-       Functional default: enabled if accept_ra is enabled.
-                           disabled if accept_ra is disabled.
-
-accept_ra_from_local - BOOLEAN
-       Accept RA with source-address that is found on local machine
-        if the RA is otherwise proper and able to be accepted.
-        Default is to NOT accept these as it may be an un-intended
-        network loop.
-
-       Functional default:
-           enabled if accept_ra_from_local is enabled
-               on a specific interface.
-          disabled if accept_ra_from_local is disabled
-               on a specific interface.
-
-accept_ra_min_hop_limit - INTEGER
-       Minimum hop limit Information in Router Advertisement.
-
-       Hop limit Information in Router Advertisement less than this
-       variable shall be ignored.
-
-       Default: 1
-
-accept_ra_pinfo - BOOLEAN
-       Learn Prefix Information in Router Advertisement.
-
-       Functional default: enabled if accept_ra is enabled.
-                           disabled if accept_ra is disabled.
-
-accept_ra_rt_info_min_plen - INTEGER
-       Minimum prefix length of Route Information in RA.
-
-       Route Information w/ prefix smaller than this variable shall
-       be ignored.
-
-       Functional default: 0 if accept_ra_rtr_pref is enabled.
-                           -1 if accept_ra_rtr_pref is disabled.
-
-accept_ra_rt_info_max_plen - INTEGER
-       Maximum prefix length of Route Information in RA.
-
-       Route Information w/ prefix larger than this variable shall
-       be ignored.
-
-       Functional default: 0 if accept_ra_rtr_pref is enabled.
-                           -1 if accept_ra_rtr_pref is disabled.
-
-accept_ra_rtr_pref - BOOLEAN
-       Accept Router Preference in RA.
-
-       Functional default: enabled if accept_ra is enabled.
-                           disabled if accept_ra is disabled.
-
-accept_ra_mtu - BOOLEAN
-       Apply the MTU value specified in RA option 5 (RFC4861). If
-       disabled, the MTU specified in the RA will be ignored.
-
-       Functional default: enabled if accept_ra is enabled.
-                           disabled if accept_ra is disabled.
-
-accept_redirects - BOOLEAN
-       Accept Redirects.
-
-       Functional default: enabled if local forwarding is disabled.
-                           disabled if local forwarding is enabled.
-
-accept_source_route - INTEGER
-       Accept source routing (routing extension header).
-
-       >= 0: Accept only routing header type 2.
-       < 0: Do not accept routing header.
-
-       Default: 0
-
-autoconf - BOOLEAN
-       Autoconfigure addresses using Prefix Information in Router
-       Advertisements.
-
-       Functional default: enabled if accept_ra_pinfo is enabled.
-                           disabled if accept_ra_pinfo is disabled.
-
-dad_transmits - INTEGER
-       The amount of Duplicate Address Detection probes to send.
-       Default: 1
-
-forwarding - INTEGER
-       Configure interface-specific Host/Router behaviour.
-
-       Note: It is recommended to have the same setting on all
-       interfaces; mixed router/host scenarios are rather uncommon.
-
-       Possible values are:
-               0 Forwarding disabled
-               1 Forwarding enabled
-
-       FALSE (0):
-
-       By default, Host behaviour is assumed.  This means:
-
-       1. IsRouter flag is not set in Neighbour Advertisements.
-       2. If accept_ra is TRUE (default), transmit Router
-          Solicitations.
-       3. If accept_ra is TRUE (default), accept Router
-          Advertisements (and do autoconfiguration).
-       4. If accept_redirects is TRUE (default), accept Redirects.
-
-       TRUE (1):
-
-       If local forwarding is enabled, Router behaviour is assumed.
-       This means exactly the reverse from the above:
-
-       1. IsRouter flag is set in Neighbour Advertisements.
-       2. Router Solicitations are not sent unless accept_ra is 2.
-       3. Router Advertisements are ignored unless accept_ra is 2.
-       4. Redirects are ignored.
-
-       Default: 0 (disabled) if global forwarding is disabled (default),
-                otherwise 1 (enabled).
-
-hop_limit - INTEGER
-       Default Hop Limit to set.
-       Default: 64
-
-mtu - INTEGER
-       Default Maximum Transfer Unit
-       Default: 1280 (IPv6 required minimum)
-
-ip_nonlocal_bind - BOOLEAN
-       If set, allows processes to bind() to non-local IPv6 addresses,
-       which can be quite useful - but may break some applications.
-       Default: 0
-
-router_probe_interval - INTEGER
-       Minimum interval (in seconds) between Router Probing described
-       in RFC4191.
-
-       Default: 60
-
-router_solicitation_delay - INTEGER
-       Number of seconds to wait after interface is brought up
-       before sending Router Solicitations.
-       Default: 1
-
-router_solicitation_interval - INTEGER
-       Number of seconds to wait between Router Solicitations.
-       Default: 4
-
-router_solicitations - INTEGER
-       Number of Router Solicitations to send until assuming no
-       routers are present.
-       Default: 3
-
-use_oif_addrs_only - BOOLEAN
-       When enabled, the candidate source addresses for destinations
-       routed via this interface are restricted to the set of addresses
-       configured on this interface (vis. RFC 6724, section 4).
-
-       Default: false
-
-use_tempaddr - INTEGER
-       Preference for Privacy Extensions (RFC3041).
-         <= 0 : disable Privacy Extensions
-         == 1 : enable Privacy Extensions, but prefer public
-                addresses over temporary addresses.
-         >  1 : enable Privacy Extensions and prefer temporary
-                addresses over public addresses.
-       Default:  0 (for most devices)
-                -1 (for point-to-point devices and loopback devices)
-
-temp_valid_lft - INTEGER
-       valid lifetime (in seconds) for temporary addresses.
-       Default: 604800 (7 days)
-
-temp_prefered_lft - INTEGER
-       Preferred lifetime (in seconds) for temporary addresses.
-       Default: 86400 (1 day)
-
-keep_addr_on_down - INTEGER
-       Keep all IPv6 addresses on an interface down event. If set static
-       global addresses with no expiration time are not flushed.
-         >0 : enabled
-          0 : system default
-         <0 : disabled
-
-       Default: 0 (addresses are removed)
-
-max_desync_factor - INTEGER
-       Maximum value for DESYNC_FACTOR, which is a random value
-       that ensures that clients don't synchronize with each
-       other and generate new addresses at exactly the same time.
-       value is in seconds.
-       Default: 600
-
-regen_max_retry - INTEGER
-       Number of attempts before give up attempting to generate
-       valid temporary addresses.
-       Default: 5
-
-max_addresses - INTEGER
-       Maximum number of autoconfigured addresses per interface.  Setting
-       to zero disables the limitation.  It is not recommended to set this
-       value too large (or to zero) because it would be an easy way to
-       crash the kernel by allowing too many addresses to be created.
-       Default: 16
-
-disable_ipv6 - BOOLEAN
-       Disable IPv6 operation.  If accept_dad is set to 2, this value
-       will be dynamically set to TRUE if DAD fails for the link-local
-       address.
-       Default: FALSE (enable IPv6 operation)
-
-       When this value is changed from 1 to 0 (IPv6 is being enabled),
-       it will dynamically create a link-local address on the given
-       interface and start Duplicate Address Detection, if necessary.
-
-       When this value is changed from 0 to 1 (IPv6 is being disabled),
-       it will dynamically delete all addresses and routes on the given
-       interface. From now on it will not possible to add addresses/routes
-       to the selected interface.
-
-accept_dad - INTEGER
-       Whether to accept DAD (Duplicate Address Detection).
-       0: Disable DAD
-       1: Enable DAD (default)
-       2: Enable DAD, and disable IPv6 operation if MAC-based duplicate
-          link-local address has been found.
-
-       DAD operation and mode on a given interface will be selected according
-       to the maximum value of conf/{all,interface}/accept_dad.
-
-force_tllao - BOOLEAN
-       Enable sending the target link-layer address option even when
-       responding to a unicast neighbor solicitation.
-       Default: FALSE
-
-       Quoting from RFC 2461, section 4.4, Target link-layer address:
-
-       "The option MUST be included for multicast solicitations in order to
-       avoid infinite Neighbor Solicitation "recursion" when the peer node
-       does not have a cache entry to return a Neighbor Advertisements
-       message.  When responding to unicast solicitations, the option can be
-       omitted since the sender of the solicitation has the correct link-
-       layer address; otherwise it would not have be able to send the unicast
-       solicitation in the first place. However, including the link-layer
-       address in this case adds little overhead and eliminates a potential
-       race condition where the sender deletes the cached link-layer address
-       prior to receiving a response to a previous solicitation."
-
-ndisc_notify - BOOLEAN
-       Define mode for notification of address and device changes.
-       0 - (default): do nothing
-       1 - Generate unsolicited neighbour advertisements when device is brought
-           up or hardware address changes.
-
-ndisc_tclass - INTEGER
-       The IPv6 Traffic Class to use by default when sending IPv6 Neighbor
-       Discovery (Router Solicitation, Router Advertisement, Neighbor
-       Solicitation, Neighbor Advertisement, Redirect) messages.
-       These 8 bits can be interpreted as 6 high order bits holding the DSCP
-       value and 2 low order bits representing ECN (which you probably want
-       to leave cleared).
-       0 - (default)
-
-mldv1_unsolicited_report_interval - INTEGER
-       The interval in milliseconds in which the next unsolicited
-       MLDv1 report retransmit will take place.
-       Default: 10000 (10 seconds)
-
-mldv2_unsolicited_report_interval - INTEGER
-       The interval in milliseconds in which the next unsolicited
-       MLDv2 report retransmit will take place.
-       Default: 1000 (1 second)
-
-force_mld_version - INTEGER
-       0 - (default) No enforcement of a MLD version, MLDv1 fallback allowed
-       1 - Enforce to use MLD version 1
-       2 - Enforce to use MLD version 2
-
-suppress_frag_ndisc - INTEGER
-       Control RFC 6980 (Security Implications of IPv6 Fragmentation
-       with IPv6 Neighbor Discovery) behavior:
-       1 - (default) discard fragmented neighbor discovery packets
-       0 - allow fragmented neighbor discovery packets
-
-optimistic_dad - BOOLEAN
-       Whether to perform Optimistic Duplicate Address Detection (RFC 4429).
-       0: disabled (default)
-       1: enabled
-
-       Optimistic Duplicate Address Detection for the interface will be enabled
-       if at least one of conf/{all,interface}/optimistic_dad is set to 1,
-       it will be disabled otherwise.
-
-use_optimistic - BOOLEAN
-       If enabled, do not classify optimistic addresses as deprecated during
-       source address selection.  Preferred addresses will still be chosen
-       before optimistic addresses, subject to other ranking in the source
-       address selection algorithm.
-       0: disabled (default)
-       1: enabled
-
-       This will be enabled if at least one of
-       conf/{all,interface}/use_optimistic is set to 1, disabled otherwise.
-
-stable_secret - IPv6 address
-       This IPv6 address will be used as a secret to generate IPv6
-       addresses for link-local addresses and autoconfigured
-       ones. All addresses generated after setting this secret will
-       be stable privacy ones by default. This can be changed via the
-       addrgenmode ip-link. conf/default/stable_secret is used as the
-       secret for the namespace, the interface specific ones can
-       overwrite that. Writes to conf/all/stable_secret are refused.
-
-       It is recommended to generate this secret during installation
-       of a system and keep it stable after that.
-
-       By default the stable secret is unset.
-
-addr_gen_mode - INTEGER
-       Defines how link-local and autoconf addresses are generated.
-
-       0: generate address based on EUI64 (default)
-       1: do no generate a link-local address, use EUI64 for addresses generated
-          from autoconf
-       2: generate stable privacy addresses, using the secret from
-          stable_secret (RFC7217)
-       3: generate stable privacy addresses, using a random secret if unset
-
-drop_unicast_in_l2_multicast - BOOLEAN
-       Drop any unicast IPv6 packets that are received in link-layer
-       multicast (or broadcast) frames.
-
-       By default this is turned off.
-
-drop_unsolicited_na - BOOLEAN
-       Drop all unsolicited neighbor advertisements, for example if there's
-       a known good NA proxy on the network and such frames need not be used
-       (or in the case of 802.11, must not be used to prevent attacks.)
-
-       By default this is turned off.
-
-enhanced_dad - BOOLEAN
-       Include a nonce option in the IPv6 neighbor solicitation messages used for
-       duplicate address detection per RFC7527. A received DAD NS will only signal
-       a duplicate address if the nonce is different. This avoids any false
-       detection of duplicates due to loopback of the NS messages that we send.
-       The nonce option will be sent on an interface unless both of
-       conf/{all,interface}/enhanced_dad are set to FALSE.
-       Default: TRUE
-
-icmp/*:
-ratelimit - INTEGER
-       Limit the maximal rates for sending ICMPv6 messages.
-       0 to disable any limiting,
-       otherwise the minimal space between responses in milliseconds.
-       Default: 1000
-
-ratemask - list of comma separated ranges
-       For ICMPv6 message types matching the ranges in the ratemask, limit
-       the sending of the message according to ratelimit parameter.
-
-       The format used for both input and output is a comma separated
-       list of ranges (e.g. "0-127,129" for ICMPv6 message type 0 to 127 and
-       129). Writing to the file will clear all previous ranges of ICMPv6
-       message types and update the current list with the input.
-
-       Refer to: https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml
-       for numerical values of ICMPv6 message types, e.g. echo request is 128
-       and echo reply is 129.
-
-       Default: 0-1,3-127 (rate limit ICMPv6 errors except Packet Too Big)
-
-echo_ignore_all - BOOLEAN
-       If set non-zero, then the kernel will ignore all ICMP ECHO
-       requests sent to it over the IPv6 protocol.
-       Default: 0
-
-echo_ignore_multicast - BOOLEAN
-       If set non-zero, then the kernel will ignore all ICMP ECHO
-       requests sent to it over the IPv6 protocol via multicast.
-       Default: 0
-
-echo_ignore_anycast - BOOLEAN
-       If set non-zero, then the kernel will ignore all ICMP ECHO
-       requests sent to it over the IPv6 protocol destined to anycast address.
-       Default: 0
-
-xfrm6_gc_thresh - INTEGER
-       (Obsolete since linux-4.14)
-       The threshold at which we will start garbage collecting for IPv6
-       destination cache entries.  At twice this value the system will
-       refuse new allocations.
-
-
-IPv6 Update by:
-Pekka Savola <pekkas@netcore.fi>
-YOSHIFUJI Hideaki / USAGI Project <yoshfuji@linux-ipv6.org>
-
-
-/proc/sys/net/bridge/* Variables:
-
-bridge-nf-call-arptables - BOOLEAN
-       1 : pass bridged ARP traffic to arptables' FORWARD chain.
-       0 : disable this.
-       Default: 1
-
-bridge-nf-call-iptables - BOOLEAN
-       1 : pass bridged IPv4 traffic to iptables' chains.
-       0 : disable this.
-       Default: 1
-
-bridge-nf-call-ip6tables - BOOLEAN
-       1 : pass bridged IPv6 traffic to ip6tables' chains.
-       0 : disable this.
-       Default: 1
-
-bridge-nf-filter-vlan-tagged - BOOLEAN
-       1 : pass bridged vlan-tagged ARP/IP/IPv6 traffic to {arp,ip,ip6}tables.
-       0 : disable this.
-       Default: 0
-
-bridge-nf-filter-pppoe-tagged - BOOLEAN
-       1 : pass bridged pppoe-tagged IP/IPv6 traffic to {ip,ip6}tables.
-       0 : disable this.
-       Default: 0
-
-bridge-nf-pass-vlan-input-dev - BOOLEAN
-       1: if bridge-nf-filter-vlan-tagged is enabled, try to find a vlan
-       interface on the bridge and set the netfilter input device to the vlan.
-       This allows use of e.g. "iptables -i br0.1" and makes the REDIRECT
-       target work with vlan-on-top-of-bridge interfaces.  When no matching
-       vlan interface is found, or this switch is off, the input device is
-       set to the bridge interface.
-       0: disable bridge netfilter vlan interface lookup.
-       Default: 0
-
-proc/sys/net/sctp/* Variables:
-
-addip_enable - BOOLEAN
-       Enable or disable extension of  Dynamic Address Reconfiguration
-       (ADD-IP) functionality specified in RFC5061.  This extension provides
-       the ability to dynamically add and remove new addresses for the SCTP
-       associations.
-
-       1: Enable extension.
-
-       0: Disable extension.
-
-       Default: 0
-
-pf_enable - INTEGER
-       Enable or disable pf (pf is short for potentially failed) state. A value
-       of pf_retrans > path_max_retrans also disables pf state. That is, one of
-       both pf_enable and pf_retrans > path_max_retrans can disable pf state.
-       Since pf_retrans and path_max_retrans can be changed by userspace
-       application, sometimes user expects to disable pf state by the value of
-       pf_retrans > path_max_retrans, but occasionally the value of pf_retrans
-       or path_max_retrans is changed by the user application, this pf state is
-       enabled. As such, it is necessary to add this to dynamically enable
-       and disable pf state. See:
-       https://datatracker.ietf.org/doc/draft-ietf-tsvwg-sctp-failover for
-       details.
-
-       1: Enable pf.
-
-       0: Disable pf.
-
-       Default: 1
-
-pf_expose - INTEGER
-       Unset or enable/disable pf (pf is short for potentially failed) state
-       exposure.  Applications can control the exposure of the PF path state
-       in the SCTP_PEER_ADDR_CHANGE event and the SCTP_GET_PEER_ADDR_INFO
-       sockopt.   When it's unset, no SCTP_PEER_ADDR_CHANGE event with
-       SCTP_ADDR_PF state will be sent and a SCTP_PF-state transport info
-       can be got via SCTP_GET_PEER_ADDR_INFO sockopt;  When it's enabled,
-       a SCTP_PEER_ADDR_CHANGE event will be sent for a transport becoming
-       SCTP_PF state and a SCTP_PF-state transport info can be got via
-       SCTP_GET_PEER_ADDR_INFO sockopt;  When it's diabled, no
-       SCTP_PEER_ADDR_CHANGE event will be sent and it returns -EACCES when
-       trying to get a SCTP_PF-state transport info via SCTP_GET_PEER_ADDR_INFO
-       sockopt.
-
-       0: Unset pf state exposure, Compatible with old applications.
-
-       1: Disable pf state exposure.
-
-       2: Enable pf state exposure.
-
-       Default: 0
-
-addip_noauth_enable - BOOLEAN
-       Dynamic Address Reconfiguration (ADD-IP) requires the use of
-       authentication to protect the operations of adding or removing new
-       addresses.  This requirement is mandated so that unauthorized hosts
-       would not be able to hijack associations.  However, older
-       implementations may not have implemented this requirement while
-       allowing the ADD-IP extension.  For reasons of interoperability,
-       we provide this variable to control the enforcement of the
-       authentication requirement.
-
-       1: Allow ADD-IP extension to be used without authentication.  This
-          should only be set in a closed environment for interoperability
-          with older implementations.
-
-       0: Enforce the authentication requirement
-
-       Default: 0
-
-auth_enable - BOOLEAN
-       Enable or disable Authenticated Chunks extension.  This extension
-       provides the ability to send and receive authenticated chunks and is
-       required for secure operation of Dynamic Address Reconfiguration
-       (ADD-IP) extension.
-
-       1: Enable this extension.
-       0: Disable this extension.
-
-       Default: 0
-
-prsctp_enable - BOOLEAN
-       Enable or disable the Partial Reliability extension (RFC3758) which
-       is used to notify peers that a given DATA should no longer be expected.
-
-       1: Enable extension
-       0: Disable
-
-       Default: 1
-
-max_burst - INTEGER
-       The limit of the number of new packets that can be initially sent.  It
-       controls how bursty the generated traffic can be.
-
-       Default: 4
-
-association_max_retrans - INTEGER
-       Set the maximum number for retransmissions that an association can
-       attempt deciding that the remote end is unreachable.  If this value
-       is exceeded, the association is terminated.
-
-       Default: 10
-
-max_init_retransmits - INTEGER
-       The maximum number of retransmissions of INIT and COOKIE-ECHO chunks
-       that an association will attempt before declaring the destination
-       unreachable and terminating.
-
-       Default: 8
-
-path_max_retrans - INTEGER
-       The maximum number of retransmissions that will be attempted on a given
-       path.  Once this threshold is exceeded, the path is considered
-       unreachable, and new traffic will use a different path when the
-       association is multihomed.
-
-       Default: 5
-
-pf_retrans - INTEGER
-       The number of retransmissions that will be attempted on a given path
-       before traffic is redirected to an alternate transport (should one
-       exist).  Note this is distinct from path_max_retrans, as a path that
-       passes the pf_retrans threshold can still be used.  Its only
-       deprioritized when a transmission path is selected by the stack.  This
-       setting is primarily used to enable fast failover mechanisms without
-       having to reduce path_max_retrans to a very low value.  See:
-       http://www.ietf.org/id/draft-nishida-tsvwg-sctp-failover-05.txt
-       for details.  Note also that a value of pf_retrans > path_max_retrans
-       disables this feature. Since both pf_retrans and path_max_retrans can
-       be changed by userspace application, a variable pf_enable is used to
-       disable pf state.
-
-       Default: 0
-
-ps_retrans - INTEGER
-       Primary.Switchover.Max.Retrans (PSMR), it's a tunable parameter coming
-       from section-5 "Primary Path Switchover" in rfc7829.  The primary path
-       will be changed to another active path when the path error counter on
-       the old primary path exceeds PSMR, so that "the SCTP sender is allowed
-       to continue data transmission on a new working path even when the old
-       primary destination address becomes active again".   Note this feature
-       is disabled by initializing 'ps_retrans' per netns as 0xffff by default,
-       and its value can't be less than 'pf_retrans' when changing by sysctl.
-
-       Default: 0xffff
-
-rto_initial - INTEGER
-       The initial round trip timeout value in milliseconds that will be used
-       in calculating round trip times.  This is the initial time interval
-       for retransmissions.
-
-       Default: 3000
-
-rto_max - INTEGER
-       The maximum value (in milliseconds) of the round trip timeout.  This
-       is the largest time interval that can elapse between retransmissions.
-
-       Default: 60000
-
-rto_min - INTEGER
-       The minimum value (in milliseconds) of the round trip timeout.  This
-       is the smallest time interval the can elapse between retransmissions.
-
-       Default: 1000
-
-hb_interval - INTEGER
-       The interval (in milliseconds) between HEARTBEAT chunks.  These chunks
-       are sent at the specified interval on idle paths to probe the state of
-       a given path between 2 associations.
-
-       Default: 30000
-
-sack_timeout - INTEGER
-       The amount of time (in milliseconds) that the implementation will wait
-       to send a SACK.
-
-       Default: 200
-
-valid_cookie_life - INTEGER
-       The default lifetime of the SCTP cookie (in milliseconds).  The cookie
-       is used during association establishment.
-
-       Default: 60000
-
-cookie_preserve_enable - BOOLEAN
-       Enable or disable the ability to extend the lifetime of the SCTP cookie
-       that is used during the establishment phase of SCTP association
-
-       1: Enable cookie lifetime extension.
-       0: Disable
-
-       Default: 1
-
-cookie_hmac_alg - STRING
-       Select the hmac algorithm used when generating the cookie value sent by
-       a listening sctp socket to a connecting client in the INIT-ACK chunk.
-       Valid values are:
-       * md5
-       * sha1
-       * none
-       Ability to assign md5 or sha1 as the selected alg is predicated on the
-       configuration of those algorithms at build time (CONFIG_CRYPTO_MD5 and
-       CONFIG_CRYPTO_SHA1).
-
-       Default: Dependent on configuration.  MD5 if available, else SHA1 if
-       available, else none.
-
-rcvbuf_policy - INTEGER
-       Determines if the receive buffer is attributed to the socket or to
-       association.   SCTP supports the capability to create multiple
-       associations on a single socket.  When using this capability, it is
-       possible that a single stalled association that's buffering a lot
-       of data may block other associations from delivering their data by
-       consuming all of the receive buffer space.  To work around this,
-       the rcvbuf_policy could be set to attribute the receiver buffer space
-       to each association instead of the socket.  This prevents the described
-       blocking.
-
-       1: rcvbuf space is per association
-       0: rcvbuf space is per socket
-
-       Default: 0
-
-sndbuf_policy - INTEGER
-       Similar to rcvbuf_policy above, this applies to send buffer space.
-
-       1: Send buffer is tracked per association
-       0: Send buffer is tracked per socket.
-
-       Default: 0
-
-sctp_mem - vector of 3 INTEGERs: min, pressure, max
-       Number of pages allowed for queueing by all SCTP sockets.
-
-       min: Below this number of pages SCTP is not bothered about its
-       memory appetite. When amount of memory allocated by SCTP exceeds
-       this number, SCTP starts to moderate memory usage.
-
-       pressure: This value was introduced to follow format of tcp_mem.
-
-       max: Number of pages allowed for queueing by all SCTP sockets.
-
-       Default is calculated at boot time from amount of available memory.
-
-sctp_rmem - vector of 3 INTEGERs: min, default, max
-       Only the first value ("min") is used, "default" and "max" are
-       ignored.
-
-       min: Minimal size of receive buffer used by SCTP socket.
-       It is guaranteed to each SCTP socket (but not association) even
-       under moderate memory pressure.
-
-       Default: 4K
-
-sctp_wmem  - vector of 3 INTEGERs: min, default, max
-       Currently this tunable has no effect.
-
-addr_scope_policy - INTEGER
-       Control IPv4 address scoping - draft-stewart-tsvwg-sctp-ipv4-00
-
-       0   - Disable IPv4 address scoping
-       1   - Enable IPv4 address scoping
-       2   - Follow draft but allow IPv4 private addresses
-       3   - Follow draft but allow IPv4 link local addresses
-
-       Default: 1
-
-
-/proc/sys/net/core/*
-       Please see: Documentation/admin-guide/sysctl/net.rst for descriptions of these entries.
-
-
-/proc/sys/net/unix/*
-max_dgram_qlen - INTEGER
-       The maximum length of dgram socket receive queue
-
-       Default: 10
-
diff --git a/Documentation/networking/ip_dynaddr.rst b/Documentation/networking/ip_dynaddr.rst
new file mode 100644 (file)
index 0000000..eacc0c7
--- /dev/null
@@ -0,0 +1,40 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================================
+IP dynamic address hack-port v0.03
+==================================
+
+This stuff allows diald ONESHOT connections to get established by
+dynamically changing packet source address (and socket's if local procs).
+It is implemented for TCP diald-box connections(1) and IP_MASQuerading(2).
+
+If enabled\ [#]_ and forwarding interface has changed:
+
+  1)  Socket (and packet) source address is rewritten ON RETRANSMISSIONS
+      while in SYN_SENT state (diald-box processes).
+  2)  Out-bounded MASQueraded source address changes ON OUTPUT (when
+      internal host does retransmission) until a packet from outside is
+      received by the tunnel.
+
+This is specially helpful for auto dialup links (diald), where the
+``actual`` outgoing address is unknown at the moment the link is
+going up. So, the *same* (local AND masqueraded) connections requests that
+bring the link up will be able to get established.
+
+.. [#] At boot, by default no address rewriting is attempted.
+
+  To enable::
+
+     # echo 1 > /proc/sys/net/ipv4/ip_dynaddr
+
+  To enable verbose mode::
+
+    # echo 2 > /proc/sys/net/ipv4/ip_dynaddr
+
+  To disable (default)::
+
+     # echo 0 > /proc/sys/net/ipv4/ip_dynaddr
+
+Enjoy!
+
+Juanjo  <jjciarla@raiz.uncu.edu.ar>
diff --git a/Documentation/networking/ip_dynaddr.txt b/Documentation/networking/ip_dynaddr.txt
deleted file mode 100644 (file)
index 45f3c12..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-IP dynamic address hack-port v0.03
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-This stuff allows diald ONESHOT connections to get established by
-dynamically changing packet source address (and socket's if local procs).
-It is implemented for TCP diald-box connections(1) and IP_MASQuerading(2).
-
-If enabled[*] and forwarding interface has changed:
-  1)  Socket (and packet) source address is rewritten ON RETRANSMISSIONS
-      while in SYN_SENT state (diald-box processes).
-  2)  Out-bounded MASQueraded source address changes ON OUTPUT (when
-      internal host does retransmission) until a packet from outside is
-      received by the tunnel.
-
-This is specially helpful for auto dialup links (diald), where the
-``actual'' outgoing address is unknown at the moment the link is
-going up. So, the *same* (local AND masqueraded) connections requests that
-bring the link up will be able to get established.
-
-[*] At boot, by default no address rewriting is attempted. 
-  To enable:
-     # echo 1 > /proc/sys/net/ipv4/ip_dynaddr
-  To enable verbose mode:
-     # echo 2 > /proc/sys/net/ipv4/ip_dynaddr
-  To disable (default)
-     # echo 0 > /proc/sys/net/ipv4/ip_dynaddr
-
-Enjoy!
-
--- Juanjo  <jjciarla@raiz.uncu.edu.ar>
diff --git a/Documentation/networking/ipddp.rst b/Documentation/networking/ipddp.rst
new file mode 100644 (file)
index 0000000..be7091b
--- /dev/null
@@ -0,0 +1,78 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================================================
+AppleTalk-IP Decapsulation and AppleTalk-IP Encapsulation
+=========================================================
+
+Documentation ipddp.c
+
+This file is written by Jay Schulist <jschlst@samba.org>
+
+Introduction
+------------
+
+AppleTalk-IP (IPDDP) is the method computers connected to AppleTalk
+networks can use to communicate via IP. AppleTalk-IP is simply IP datagrams
+inside AppleTalk packets.
+
+Through this driver you can either allow your Linux box to communicate
+IP over an AppleTalk network or you can provide IP gatewaying functions
+for your AppleTalk users.
+
+You can currently encapsulate or decapsulate AppleTalk-IP on LocalTalk,
+EtherTalk and PPPTalk. The only limit on the protocol is that of what
+kernel AppleTalk layer and drivers are available.
+
+Each mode requires its own user space software.
+
+Compiling AppleTalk-IP Decapsulation/Encapsulation
+==================================================
+
+AppleTalk-IP decapsulation needs to be compiled into your kernel. You
+will need to turn on AppleTalk-IP driver support. Then you will need to
+select ONE of the two options; IP to AppleTalk-IP encapsulation support or
+AppleTalk-IP to IP decapsulation support. If you compile the driver
+statically you will only be able to use the driver for the function you have
+enabled in the kernel. If you compile the driver as a module you can
+select what mode you want it to run in via a module loading param.
+ipddp_mode=1 for AppleTalk-IP encapsulation and ipddp_mode=2 for
+AppleTalk-IP to IP decapsulation.
+
+Basic instructions for user space tools
+=======================================
+
+I will briefly describe the operation of the tools, but you will
+need to consult the supporting documentation for each set of tools.
+
+Decapsulation - You will need to download a software package called
+MacGate. In this distribution there will be a tool called MacRoute
+which enables you to add routes to the kernel for your Macs by hand.
+Also the tool MacRegGateWay is included to register the
+proper IP Gateway and IP addresses for your machine. Included in this
+distribution is a patch to netatalk-1.4b2+asun2.0a17.2 (available from
+ftp.u.washington.edu/pub/user-supported/asun/) this patch is optional
+but it allows automatic adding and deleting of routes for Macs. (Handy
+for locations with large Mac installations)
+
+Encapsulation - You will need to download a software daemon called ipddpd.
+This software expects there to be an AppleTalk-IP gateway on the network.
+You will also need to add the proper routes to route your Linux box's IP
+traffic out the ipddp interface.
+
+Common Uses of ipddp.c
+----------------------
+Of course AppleTalk-IP decapsulation and encapsulation, but specifically
+decapsulation is being used most for connecting LocalTalk networks to
+IP networks. Although it has been used on EtherTalk networks to allow
+Macs that are only able to tunnel IP over EtherTalk.
+
+Encapsulation has been used to allow a Linux box stuck on a LocalTalk
+network to use IP. It should work equally well if you are stuck on an
+EtherTalk only network.
+
+Further Assistance
+-------------------
+You can contact me (Jay Schulist <jschlst@samba.org>) with any
+questions regarding decapsulation or encapsulation. Bradford W. Johnson
+<johns393@maroon.tc.umn.edu> originally wrote the ipddp.c driver for IP
+encapsulation in AppleTalk.
diff --git a/Documentation/networking/ipddp.txt b/Documentation/networking/ipddp.txt
deleted file mode 100644 (file)
index ba5c217..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-Text file for ipddp.c:
-       AppleTalk-IP Decapsulation and AppleTalk-IP Encapsulation
-
-This text file is written by Jay Schulist <jschlst@samba.org>
-
-Introduction
-------------
-
-AppleTalk-IP (IPDDP) is the method computers connected to AppleTalk
-networks can use to communicate via IP. AppleTalk-IP is simply IP datagrams
-inside AppleTalk packets.
-
-Through this driver you can either allow your Linux box to communicate
-IP over an AppleTalk network or you can provide IP gatewaying functions
-for your AppleTalk users.
-
-You can currently encapsulate or decapsulate AppleTalk-IP on LocalTalk,
-EtherTalk and PPPTalk. The only limit on the protocol is that of what
-kernel AppleTalk layer and drivers are available.
-
-Each mode requires its own user space software.
-
-Compiling AppleTalk-IP Decapsulation/Encapsulation
-=================================================
-
-AppleTalk-IP decapsulation needs to be compiled into your kernel. You
-will need to turn on AppleTalk-IP driver support. Then you will need to
-select ONE of the two options; IP to AppleTalk-IP encapsulation support or
-AppleTalk-IP to IP decapsulation support. If you compile the driver
-statically you will only be able to use the driver for the function you have
-enabled in the kernel. If you compile the driver as a module you can
-select what mode you want it to run in via a module loading param.
-ipddp_mode=1 for AppleTalk-IP encapsulation and ipddp_mode=2 for
-AppleTalk-IP to IP decapsulation.
-
-Basic instructions for user space tools
-=======================================
-
-I will briefly describe the operation of the tools, but you will
-need to consult the supporting documentation for each set of tools.
-
-Decapsulation - You will need to download a software package called
-MacGate. In this distribution there will be a tool called MacRoute
-which enables you to add routes to the kernel for your Macs by hand.
-Also the tool MacRegGateWay is included to register the
-proper IP Gateway and IP addresses for your machine. Included in this
-distribution is a patch to netatalk-1.4b2+asun2.0a17.2 (available from
-ftp.u.washington.edu/pub/user-supported/asun/) this patch is optional
-but it allows automatic adding and deleting of routes for Macs. (Handy
-for locations with large Mac installations)
-
-Encapsulation - You will need to download a software daemon called ipddpd.
-This software expects there to be an AppleTalk-IP gateway on the network.
-You will also need to add the proper routes to route your Linux box's IP
-traffic out the ipddp interface.
-
-Common Uses of ipddp.c
-----------------------
-Of course AppleTalk-IP decapsulation and encapsulation, but specifically
-decapsulation is being used most for connecting LocalTalk networks to
-IP networks. Although it has been used on EtherTalk networks to allow
-Macs that are only able to tunnel IP over EtherTalk.
-
-Encapsulation has been used to allow a Linux box stuck on a LocalTalk
-network to use IP. It should work equally well if you are stuck on an
-EtherTalk only network.
-
-Further Assistance
--------------------
-You can contact me (Jay Schulist <jschlst@samba.org>) with any
-questions regarding decapsulation or encapsulation. Bradford W. Johnson
-<johns393@maroon.tc.umn.edu> originally wrote the ipddp.c driver for IP
-encapsulation in AppleTalk.
diff --git a/Documentation/networking/iphase.rst b/Documentation/networking/iphase.rst
new file mode 100644 (file)
index 0000000..92d9b75
--- /dev/null
@@ -0,0 +1,193 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================================
+ATM (i)Chip IA Linux Driver Source
+==================================
+
+                             READ ME FISRT
+
+--------------------------------------------------------------------------------
+
+                    Read This Before You Begin!
+
+--------------------------------------------------------------------------------
+
+Description
+===========
+
+This is the README file for the Interphase PCI ATM (i)Chip IA Linux driver
+source release.
+
+The features and limitations of this driver are as follows:
+
+    - A single VPI (VPI value of 0) is supported.
+    - Supports 4K VCs for the server board (with 512K control memory) and 1K
+      VCs for the client board (with 128K control memory).
+    - UBR, ABR and CBR service categories are supported.
+    - Only AAL5 is supported.
+    - Supports setting of PCR on the VCs.
+    - Multiple adapters in a system are supported.
+    - All variants of Interphase ATM PCI (i)Chip adapter cards are supported,
+      including x575 (OC3, control memory 128K , 512K and packet memory 128K,
+      512K and 1M), x525 (UTP25) and x531 (DS3 and E3). See
+      http://www.iphase.com/
+      for details.
+    - Only x86 platforms are supported.
+    - SMP is supported.
+
+
+Before You Start
+================
+
+
+Installation
+------------
+
+1. Installing the adapters in the system
+
+   To install the ATM adapters in the system, follow the steps below.
+
+       a. Login as root.
+       b. Shut down the system and power off the system.
+       c. Install one or more ATM adapters in the system.
+       d. Connect each adapter to a port on an ATM switch. The green 'Link'
+         LED on the front panel of the adapter will be on if the adapter is
+         connected to the switch properly when the system is powered up.
+       e. Power on and boot the system.
+
+2. [ Removed ]
+
+3. Rebuild kernel with ABR support
+
+   [ a. and b. removed ]
+
+    c. Reconfigure the kernel, choose the Interphase ia driver through "make
+       menuconfig" or "make xconfig".
+    d. Rebuild the kernel, loadable modules and the atm tools.
+    e. Install the new built kernel and modules and reboot.
+
+4. Load the adapter hardware driver (ia driver) if it is built as a module
+
+       a. Login as root.
+       b. Change directory to /lib/modules/<kernel-version>/atm.
+       c. Run "insmod suni.o;insmod iphase.o"
+         The yellow 'status' LED on the front panel of the adapter will blink
+         while the driver is loaded in the system.
+       d. To verify that the 'ia' driver is loaded successfully, run the
+         following command::
+
+             cat /proc/atm/devices
+
+         If the driver is loaded successfully, the output of the command will
+         be similar to the following lines::
+
+             Itf Type    ESI/"MAC"addr AAL(TX,err,RX,err,drop) ...
+             0   ia      xxxxxxxxx  0 ( 0 0 0 0 0 )  5 ( 0 0 0 0 0 )
+
+         You can also check the system log file /var/log/messages for messages
+         related to the ATM driver.
+
+5. Ia Driver Configuration
+
+5.1 Configuration of adapter buffers
+    The (i)Chip boards have 3 different packet RAM size variants: 128K, 512K and
+    1M. The RAM size decides the number of buffers and buffer size. The default
+    size and number of buffers are set as following:
+
+       =========  =======  ======   ======   ======   ======   ======
+        Total     Rx RAM   Tx RAM   Rx Buf   Tx Buf   Rx buf   Tx buf
+        RAM size  size     size     size     size     cnt      cnt
+       =========  =======  ======   ======   ======   ======   ======
+          128K      64K      64K      10K      10K       6        6
+          512K     256K     256K      10K      10K      25       25
+            1M     512K     512K      10K      10K      51       51
+       =========  =======  ======   ======   ======   ======   ======
+
+       These setting should work well in most environments, but can be
+       changed by typing the following command::
+
+          insmod <IA_DIR>/ia.o IA_RX_BUF=<RX_CNT> IA_RX_BUF_SZ=<RX_SIZE> \
+                  IA_TX_BUF=<TX_CNT> IA_TX_BUF_SZ=<TX_SIZE>
+
+       Where:
+
+           - RX_CNT = number of receive buffers in the range (1-128)
+           - RX_SIZE = size of receive buffers in the range (48-64K)
+           - TX_CNT = number of transmit buffers in the range (1-128)
+           - TX_SIZE = size of transmit buffers in the range (48-64K)
+
+           1. Transmit and receive buffer size must be a multiple of 4.
+           2. Care should be taken so that the memory required for the
+              transmit and receive buffers is less than or equal to the
+              total adapter packet memory.
+
+5.2 Turn on ia debug trace
+
+    When the ia driver is built with the CONFIG_ATM_IA_DEBUG flag, the driver
+    can provide more debug trace if needed. There is a bit mask variable,
+    IADebugFlag, which controls the output of the traces. You can find the bit
+    map of the IADebugFlag in iphase.h.
+    The debug trace can be turn on through the insmod command line option, for
+    example, "insmod iphase.o IADebugFlag=0xffffffff" can turn on all the debug
+    traces together with loading the driver.
+
+6. Ia Driver Test Using ttcp_atm and PVC
+
+   For the PVC setup, the test machines can either be connected back-to-back or
+   through a switch. If connected through the switch, the switch must be
+   configured for the PVC(s).
+
+   a. For UBR test:
+
+      At the test machine intended to receive data, type::
+
+        ttcp_atm -r -a -s 0.100
+
+      At the other test machine, type::
+
+        ttcp_atm -t -a -s 0.100 -n 10000
+
+      Run "ttcp_atm -h" to display more options of the ttcp_atm tool.
+   b. For ABR test:
+
+      It is the same as the UBR testing, but with an extra command option::
+
+        -Pabr:max_pcr=<xxx>
+
+      where:
+
+            xxx = the maximum peak cell rate, from 170 - 353207.
+
+      This option must be set on both the machines.
+
+   c. For CBR test:
+
+      It is the same as the UBR testing, but with an extra command option::
+
+        -Pcbr:max_pcr=<xxx>
+
+      where:
+
+            xxx = the maximum peak cell rate, from 170 - 353207.
+
+      This option may only be set on the transmit machine.
+
+
+Outstanding Issues
+==================
+
+
+
+Contact Information
+-------------------
+
+::
+
+     Customer Support:
+        United States: Telephone:      (214) 654-5555
+                       Fax:            (214) 654-5500
+                       E-Mail:         intouch@iphase.com
+        Europe:        Telephone:      33 (0)1 41 15 44 00
+                       Fax:            33 (0)1 41 15 12 13
+     World Wide Web:   http://www.iphase.com
+     Anonymous FTP:    ftp.iphase.com
diff --git a/Documentation/networking/iphase.txt b/Documentation/networking/iphase.txt
deleted file mode 100644 (file)
index 670b72f..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-
-                              READ ME FISRT
-                 ATM (i)Chip IA Linux Driver Source
---------------------------------------------------------------------------------
-                     Read This Before You Begin!
---------------------------------------------------------------------------------
-
-Description
------------
-
-This is the README file for the Interphase PCI ATM (i)Chip IA Linux driver 
-source release.
-
-The features and limitations of this driver are as follows:
-    - A single VPI (VPI value of 0) is supported.
-    - Supports 4K VCs for the server board (with 512K control memory) and 1K 
-      VCs for the client board (with 128K control memory).
-    - UBR, ABR and CBR service categories are supported.
-    - Only AAL5 is supported. 
-    - Supports setting of PCR on the VCs. 
-    - Multiple adapters in a system are supported.
-    - All variants of Interphase ATM PCI (i)Chip adapter cards are supported, 
-      including x575 (OC3, control memory 128K , 512K and packet memory 128K, 
-      512K and 1M), x525 (UTP25) and x531 (DS3 and E3). See 
-      http://www.iphase.com/
-      for details.
-    - Only x86 platforms are supported.
-    - SMP is supported.
-
-
-Before You Start
----------------- 
-
-
-Installation
-------------
-
-1. Installing the adapters in the system
-   To install the ATM adapters in the system, follow the steps below.
-       a. Login as root.
-       b. Shut down the system and power off the system.
-       c. Install one or more ATM adapters in the system.
-       d. Connect each adapter to a port on an ATM switch. The green 'Link' 
-          LED on the front panel of the adapter will be on if the adapter is 
-          connected to the switch properly when the system is powered up.
-       e. Power on and boot the system.
-
-2. [ Removed ]
-
-3. Rebuild kernel with ABR support
-   [ a. and b. removed ]
-    c. Reconfigure the kernel, choose the Interphase ia driver through "make 
-       menuconfig" or "make xconfig".
-    d. Rebuild the kernel, loadable modules and the atm tools. 
-    e. Install the new built kernel and modules and reboot.
-
-4. Load the adapter hardware driver (ia driver) if it is built as a module
-       a. Login as root.
-       b. Change directory to /lib/modules/<kernel-version>/atm.
-       c. Run "insmod suni.o;insmod iphase.o"
-         The yellow 'status' LED on the front panel of the adapter will blink 
-          while the driver is loaded in the system.
-       d. To verify that the 'ia' driver is loaded successfully, run the 
-          following command:
-
-              cat /proc/atm/devices
-
-          If the driver is loaded successfully, the output of the command will 
-          be similar to the following lines:
-
-              Itf Type    ESI/"MAC"addr AAL(TX,err,RX,err,drop) ...
-              0   ia      xxxxxxxxx  0 ( 0 0 0 0 0 )  5 ( 0 0 0 0 0 )
-
-          You can also check the system log file /var/log/messages for messages
-          related to the ATM driver.
-
-5. Ia Driver Configuration 
-
-5.1 Configuration of adapter buffers
-    The (i)Chip boards have 3 different packet RAM size variants: 128K, 512K and
-    1M. The RAM size decides the number of buffers and buffer size. The default 
-    size and number of buffers are set as following: 
-
-          Total    Rx RAM   Tx RAM   Rx Buf   Tx Buf   Rx buf   Tx buf
-         RAM size   size     size     size     size      cnt      cnt
-         --------  ------   ------   ------   ------   ------   ------
-           128K      64K      64K      10K      10K       6        6
-           512K     256K     256K      10K      10K      25       25
-             1M     512K     512K      10K      10K      51       51
-
-       These setting should work well in most environments, but can be
-       changed by typing the following command: 
-           insmod <IA_DIR>/ia.o IA_RX_BUF=<RX_CNT> IA_RX_BUF_SZ=<RX_SIZE> \
-                   IA_TX_BUF=<TX_CNT> IA_TX_BUF_SZ=<TX_SIZE> 
-       Where:
-            RX_CNT = number of receive buffers in the range (1-128)
-            RX_SIZE = size of receive buffers in the range (48-64K)
-            TX_CNT = number of transmit buffers in the range (1-128)
-            TX_SIZE = size of transmit buffers in the range (48-64K)
-
-            1. Transmit and receive buffer size must be a multiple of 4.
-            2. Care should be taken so that the memory required for the
-               transmit and receive buffers is less than or equal to the
-               total adapter packet memory.   
-
-5.2 Turn on ia debug trace
-
-    When the ia driver is built with the CONFIG_ATM_IA_DEBUG flag, the driver 
-    can provide more debug trace if needed. There is a bit mask variable, 
-    IADebugFlag, which controls the output of the traces. You can find the bit 
-    map of the IADebugFlag in iphase.h. 
-    The debug trace can be turn on through the insmod command line option, for 
-    example, "insmod iphase.o IADebugFlag=0xffffffff" can turn on all the debug 
-    traces together with loading the driver.
-
-6. Ia Driver Test Using ttcp_atm and PVC
-
-   For the PVC setup, the test machines can either be connected back-to-back or 
-   through a switch. If connected through the switch, the switch must be 
-   configured for the PVC(s).
-
-   a. For UBR test:
-      At the test machine intended to receive data, type:
-         ttcp_atm -r -a -s 0.100 
-      At the other test machine, type:
-         ttcp_atm -t -a -s 0.100 -n 10000
-      Run "ttcp_atm -h" to display more options of the ttcp_atm tool.
-   b. For ABR test:
-      It is the same as the UBR testing, but with an extra command option:
-         -Pabr:max_pcr=<xxx>
-         where:
-             xxx = the maximum peak cell rate, from 170 - 353207.
-         This option must be set on both the machines.
-   c. For CBR test:
-      It is the same as the UBR testing, but with an extra command option:
-         -Pcbr:max_pcr=<xxx>
-         where:
-             xxx = the maximum peak cell rate, from 170 - 353207.
-         This option may only be set on the transmit machine.
-
-
-OUTSTANDING ISSUES
-------------------
-
-
-
-Contact Information
--------------------
-
-     Customer Support:
-         United States:        Telephone:      (214) 654-5555
-                       Fax:            (214) 654-5500
-                       E-Mail:         intouch@iphase.com
-        Europe:        Telephone:      33 (0)1 41 15 44 00
-                       Fax:            33 (0)1 41 15 12 13
-     World Wide Web:   http://www.iphase.com
-     Anonymous FTP:    ftp.iphase.com
diff --git a/Documentation/networking/ipsec.rst b/Documentation/networking/ipsec.rst
new file mode 100644 (file)
index 0000000..afe9d7b
--- /dev/null
@@ -0,0 +1,46 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====
+IPsec
+=====
+
+
+Here documents known IPsec corner cases which need to be keep in mind when
+deploy various IPsec configuration in real world production environment.
+
+1. IPcomp:
+          Small IP packet won't get compressed at sender, and failed on
+          policy check on receiver.
+
+Quote from RFC3173::
+
+  2.2. Non-Expansion Policy
+
+   If the total size of a compressed payload and the IPComp header, as
+   defined in section 3, is not smaller than the size of the original
+   payload, the IP datagram MUST be sent in the original non-compressed
+   form.  To clarify: If an IP datagram is sent non-compressed, no
+
+   IPComp header is added to the datagram.  This policy ensures saving
+   the decompression processing cycles and avoiding incurring IP
+   datagram fragmentation when the expanded datagram is larger than the
+   MTU.
+
+   Small IP datagrams are likely to expand as a result of compression.
+   Therefore, a numeric threshold should be applied before compression,
+   where IP datagrams of size smaller than the threshold are sent in the
+   original form without attempting compression.  The numeric threshold
+   is implementation dependent.
+
+Current IPComp implementation is indeed by the book, while as in practice
+when sending non-compressed packet to the peer (whether or not packet len
+is smaller than the threshold or the compressed len is larger than original
+packet len), the packet is dropped when checking the policy as this packet
+matches the selector but not coming from any XFRM layer, i.e., with no
+security path. Such naked packet will not eventually make it to upper layer.
+The result is much more wired to the user when ping peer with different
+payload length.
+
+One workaround is try to set "level use" for each policy if user observed
+above scenario. The consequence of doing so is small packet(uncompressed)
+will skip policy checking on receiver side.
diff --git a/Documentation/networking/ipsec.txt b/Documentation/networking/ipsec.txt
deleted file mode 100644 (file)
index ba794b7..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-
-Here documents known IPsec corner cases which need to be keep in mind when
-deploy various IPsec configuration in real world production environment.
-
-1. IPcomp: Small IP packet won't get compressed at sender, and failed on
-          policy check on receiver.
-
-Quote from RFC3173:
-2.2. Non-Expansion Policy
-
-   If the total size of a compressed payload and the IPComp header, as
-   defined in section 3, is not smaller than the size of the original
-   payload, the IP datagram MUST be sent in the original non-compressed
-   form.  To clarify: If an IP datagram is sent non-compressed, no
-
-   IPComp header is added to the datagram.  This policy ensures saving
-   the decompression processing cycles and avoiding incurring IP
-   datagram fragmentation when the expanded datagram is larger than the
-   MTU.
-
-   Small IP datagrams are likely to expand as a result of compression.
-   Therefore, a numeric threshold should be applied before compression,
-   where IP datagrams of size smaller than the threshold are sent in the
-   original form without attempting compression.  The numeric threshold
-   is implementation dependent.
-
-Current IPComp implementation is indeed by the book, while as in practice
-when sending non-compressed packet to the peer (whether or not packet len
-is smaller than the threshold or the compressed len is larger than original
-packet len), the packet is dropped when checking the policy as this packet
-matches the selector but not coming from any XFRM layer, i.e., with no
-security path. Such naked packet will not eventually make it to upper layer.
-The result is much more wired to the user when ping peer with different
-payload length.
-
-One workaround is try to set "level use" for each policy if user observed
-above scenario. The consequence of doing so is small packet(uncompressed)
-will skip policy checking on receiver side.
diff --git a/Documentation/networking/ipv6.rst b/Documentation/networking/ipv6.rst
new file mode 100644 (file)
index 0000000..ba09c2f
--- /dev/null
@@ -0,0 +1,78 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====
+IPv6
+====
+
+
+Options for the ipv6 module are supplied as parameters at load time.
+
+Module options may be given as command line arguments to the insmod
+or modprobe command, but are usually specified in either
+``/etc/modules.d/*.conf`` configuration files, or in a distro-specific
+configuration file.
+
+The available ipv6 module parameters are listed below.  If a parameter
+is not specified the default value is used.
+
+The parameters are as follows:
+
+disable
+
+       Specifies whether to load the IPv6 module, but disable all
+       its functionality.  This might be used when another module
+       has a dependency on the IPv6 module being loaded, but no
+       IPv6 addresses or operations are desired.
+
+       The possible values and their effects are:
+
+       0
+               IPv6 is enabled.
+
+               This is the default value.
+
+       1
+               IPv6 is disabled.
+
+               No IPv6 addresses will be added to interfaces, and
+               it will not be possible to open an IPv6 socket.
+
+               A reboot is required to enable IPv6.
+
+autoconf
+
+       Specifies whether to enable IPv6 address autoconfiguration
+       on all interfaces.  This might be used when one does not wish
+       for addresses to be automatically generated from prefixes
+       received in Router Advertisements.
+
+       The possible values and their effects are:
+
+       0
+               IPv6 address autoconfiguration is disabled on all interfaces.
+
+               Only the IPv6 loopback address (::1) and link-local addresses
+               will be added to interfaces.
+
+       1
+               IPv6 address autoconfiguration is enabled on all interfaces.
+
+               This is the default value.
+
+disable_ipv6
+
+       Specifies whether to disable IPv6 on all interfaces.
+       This might be used when no IPv6 addresses are desired.
+
+       The possible values and their effects are:
+
+       0
+               IPv6 is enabled on all interfaces.
+
+               This is the default value.
+
+       1
+               IPv6 is disabled on all interfaces.
+
+               No IPv6 addresses will be added to interfaces.
+
diff --git a/Documentation/networking/ipv6.txt b/Documentation/networking/ipv6.txt
deleted file mode 100644 (file)
index 6cd74fa..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-
-Options for the ipv6 module are supplied as parameters at load time.
-
-Module options may be given as command line arguments to the insmod
-or modprobe command, but are usually specified in either
-/etc/modules.d/*.conf configuration files, or in a distro-specific
-configuration file.
-
-The available ipv6 module parameters are listed below.  If a parameter
-is not specified the default value is used.
-
-The parameters are as follows:
-
-disable
-
-       Specifies whether to load the IPv6 module, but disable all
-       its functionality.  This might be used when another module
-       has a dependency on the IPv6 module being loaded, but no
-       IPv6 addresses or operations are desired.
-
-       The possible values and their effects are:
-
-       0
-               IPv6 is enabled.
-
-               This is the default value.
-
-       1
-               IPv6 is disabled.
-
-               No IPv6 addresses will be added to interfaces, and
-               it will not be possible to open an IPv6 socket.
-
-               A reboot is required to enable IPv6.
-
-autoconf
-
-       Specifies whether to enable IPv6 address autoconfiguration
-       on all interfaces.  This might be used when one does not wish
-       for addresses to be automatically generated from prefixes
-       received in Router Advertisements.
-
-       The possible values and their effects are:
-
-       0
-               IPv6 address autoconfiguration is disabled on all interfaces.
-
-               Only the IPv6 loopback address (::1) and link-local addresses
-               will be added to interfaces.
-
-       1
-               IPv6 address autoconfiguration is enabled on all interfaces.
-
-               This is the default value.
-
-disable_ipv6
-
-       Specifies whether to disable IPv6 on all interfaces.
-       This might be used when no IPv6 addresses are desired.
-
-       The possible values and their effects are:
-
-       0
-               IPv6 is enabled on all interfaces.
-
-               This is the default value.
-
-       1
-               IPv6 is disabled on all interfaces.
-
-               No IPv6 addresses will be added to interfaces.
-
diff --git a/Documentation/networking/ipvlan.rst b/Documentation/networking/ipvlan.rst
new file mode 100644 (file)
index 0000000..694adcb
--- /dev/null
@@ -0,0 +1,189 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================
+IPVLAN Driver HOWTO
+===================
+
+Initial Release:
+       Mahesh Bandewar <maheshb AT google.com>
+
+1. Introduction:
+================
+This is conceptually very similar to the macvlan driver with one major
+exception of using L3 for mux-ing /demux-ing among slaves. This property makes
+the master device share the L2 with it's slave devices. I have developed this
+driver in conjunction with network namespaces and not sure if there is use case
+outside of it.
+
+
+2. Building and Installation:
+=============================
+
+In order to build the driver, please select the config item CONFIG_IPVLAN.
+The driver can be built into the kernel (CONFIG_IPVLAN=y) or as a module
+(CONFIG_IPVLAN=m).
+
+
+3. Configuration:
+=================
+
+There are no module parameters for this driver and it can be configured
+using IProute2/ip utility.
+::
+
+    ip link add link <master> name <slave> type ipvlan [ mode MODE ] [ FLAGS ]
+       where
+        MODE: l3 (default) | l3s | l2
+        FLAGS: bridge (default) | private | vepa
+
+e.g.
+
+    (a) Following will create IPvlan link with eth0 as master in
+       L3 bridge mode::
+
+         bash# ip link add link eth0 name ipvl0 type ipvlan
+    (b) This command will create IPvlan link in L2 bridge mode::
+
+         bash# ip link add link eth0 name ipvl0 type ipvlan mode l2 bridge
+
+    (c) This command will create an IPvlan device in L2 private mode::
+
+         bash# ip link add link eth0 name ipvlan type ipvlan mode l2 private
+
+    (d) This command will create an IPvlan device in L2 vepa mode::
+
+         bash# ip link add link eth0 name ipvlan type ipvlan mode l2 vepa
+
+
+4. Operating modes:
+===================
+
+IPvlan has two modes of operation - L2 and L3. For a given master device,
+you can select one of these two modes and all slaves on that master will
+operate in the same (selected) mode. The RX mode is almost identical except
+that in L3 mode the slaves wont receive any multicast / broadcast traffic.
+L3 mode is more restrictive since routing is controlled from the other (mostly)
+default namespace.
+
+4.1 L2 mode:
+------------
+
+In this mode TX processing happens on the stack instance attached to the
+slave device and packets are switched and queued to the master device to send
+out. In this mode the slaves will RX/TX multicast and broadcast (if applicable)
+as well.
+
+4.2 L3 mode:
+------------
+
+In this mode TX processing up to L3 happens on the stack instance attached
+to the slave device and packets are switched to the stack instance of the
+master device for the L2 processing and routing from that instance will be
+used before packets are queued on the outbound device. In this mode the slaves
+will not receive nor can send multicast / broadcast traffic.
+
+4.3 L3S mode:
+-------------
+
+This is very similar to the L3 mode except that iptables (conn-tracking)
+works in this mode and hence it is L3-symmetric (L3s). This will have slightly less
+performance but that shouldn't matter since you are choosing this mode over plain-L3
+mode to make conn-tracking work.
+
+5. Mode flags:
+==============
+
+At this time following mode flags are available
+
+5.1 bridge:
+-----------
+This is the default option. To configure the IPvlan port in this mode,
+user can choose to either add this option on the command-line or don't specify
+anything. This is the traditional mode where slaves can cross-talk among
+themselves apart from talking through the master device.
+
+5.2 private:
+------------
+If this option is added to the command-line, the port is set in private
+mode. i.e. port won't allow cross communication between slaves.
+
+5.3 vepa:
+---------
+If this is added to the command-line, the port is set in VEPA mode.
+i.e. port will offload switching functionality to the external entity as
+described in 802.1Qbg
+Note: VEPA mode in IPvlan has limitations. IPvlan uses the mac-address of the
+master-device, so the packets which are emitted in this mode for the adjacent
+neighbor will have source and destination mac same. This will make the switch /
+router send the redirect message.
+
+6. What to choose (macvlan vs. ipvlan)?
+=======================================
+
+These two devices are very similar in many regards and the specific use
+case could very well define which device to choose. if one of the following
+situations defines your use case then you can choose to use ipvlan:
+
+
+(a) The Linux host that is connected to the external switch / router has
+    policy configured that allows only one mac per port.
+(b) No of virtual devices created on a master exceed the mac capacity and
+    puts the NIC in promiscuous mode and degraded performance is a concern.
+(c) If the slave device is to be put into the hostile / untrusted network
+    namespace where L2 on the slave could be changed / misused.
+
+
+6. Example configuration:
+=========================
+
+::
+
+  +=============================================================+
+  |  Host: host1                                                |
+  |                                                             |
+  |   +----------------------+      +----------------------+    |
+  |   |   NS:ns0             |      |  NS:ns1              |    |
+  |   |                      |      |                      |    |
+  |   |                      |      |                      |    |
+  |   |        ipvl0         |      |         ipvl1        |    |
+  |   +----------#-----------+      +-----------#----------+    |
+  |              #                              #               |
+  |              ################################               |
+  |                              # eth0                         |
+  +==============================#==============================+
+
+
+(a) Create two network namespaces - ns0, ns1::
+
+       ip netns add ns0
+       ip netns add ns1
+
+(b) Create two ipvlan slaves on eth0 (master device)::
+
+       ip link add link eth0 ipvl0 type ipvlan mode l2
+       ip link add link eth0 ipvl1 type ipvlan mode l2
+
+(c) Assign slaves to the respective network namespaces::
+
+       ip link set dev ipvl0 netns ns0
+       ip link set dev ipvl1 netns ns1
+
+(d) Now switch to the namespace (ns0 or ns1) to configure the slave devices
+
+       - For ns0::
+
+               (1) ip netns exec ns0 bash
+               (2) ip link set dev ipvl0 up
+               (3) ip link set dev lo up
+               (4) ip -4 addr add 127.0.0.1 dev lo
+               (5) ip -4 addr add $IPADDR dev ipvl0
+               (6) ip -4 route add default via $ROUTER dev ipvl0
+
+       - For ns1::
+
+               (1) ip netns exec ns1 bash
+               (2) ip link set dev ipvl1 up
+               (3) ip link set dev lo up
+               (4) ip -4 addr add 127.0.0.1 dev lo
+               (5) ip -4 addr add $IPADDR dev ipvl1
+               (6) ip -4 route add default via $ROUTER dev ipvl1
diff --git a/Documentation/networking/ipvlan.txt b/Documentation/networking/ipvlan.txt
deleted file mode 100644 (file)
index 27a38e5..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-
-                            IPVLAN Driver HOWTO
-
-Initial Release:
-       Mahesh Bandewar <maheshb AT google.com>
-
-1. Introduction:
-       This is conceptually very similar to the macvlan driver with one major
-exception of using L3 for mux-ing /demux-ing among slaves. This property makes
-the master device share the L2 with it's slave devices. I have developed this
-driver in conjunction with network namespaces and not sure if there is use case
-outside of it.
-
-
-2. Building and Installation:
-       In order to build the driver, please select the config item CONFIG_IPVLAN.
-The driver can be built into the kernel (CONFIG_IPVLAN=y) or as a module
-(CONFIG_IPVLAN=m).
-
-
-3. Configuration:
-       There are no module parameters for this driver and it can be configured
-using IProute2/ip utility.
-
-    ip link add link <master> name <slave> type ipvlan [ mode MODE ] [ FLAGS ]
-       where
-         MODE: l3 (default) | l3s | l2
-         FLAGS: bridge (default) | private | vepa
-
-    e.g.
-    (a) Following will create IPvlan link with eth0 as master in
-        L3 bridge mode
-          bash# ip link add link eth0 name ipvl0 type ipvlan
-    (b) This command will create IPvlan link in L2 bridge mode.
-          bash# ip link add link eth0 name ipvl0 type ipvlan mode l2 bridge
-    (c) This command will create an IPvlan device in L2 private mode.
-          bash# ip link add link eth0 name ipvlan type ipvlan mode l2 private
-    (d) This command will create an IPvlan device in L2 vepa mode.
-          bash# ip link add link eth0 name ipvlan type ipvlan mode l2 vepa
-
-
-4. Operating modes:
-       IPvlan has two modes of operation - L2 and L3. For a given master device,
-you can select one of these two modes and all slaves on that master will
-operate in the same (selected) mode. The RX mode is almost identical except
-that in L3 mode the slaves wont receive any multicast / broadcast traffic.
-L3 mode is more restrictive since routing is controlled from the other (mostly)
-default namespace.
-
-4.1 L2 mode:
-       In this mode TX processing happens on the stack instance attached to the
-slave device and packets are switched and queued to the master device to send
-out. In this mode the slaves will RX/TX multicast and broadcast (if applicable)
-as well.
-
-4.2 L3 mode:
-       In this mode TX processing up to L3 happens on the stack instance attached
-to the slave device and packets are switched to the stack instance of the
-master device for the L2 processing and routing from that instance will be
-used before packets are queued on the outbound device. In this mode the slaves
-will not receive nor can send multicast / broadcast traffic.
-
-4.3 L3S mode:
-       This is very similar to the L3 mode except that iptables (conn-tracking)
-works in this mode and hence it is L3-symmetric (L3s). This will have slightly less
-performance but that shouldn't matter since you are choosing this mode over plain-L3
-mode to make conn-tracking work.
-
-5. Mode flags:
-       At this time following mode flags are available
-
-5.1 bridge:
-       This is the default option. To configure the IPvlan port in this mode,
-user can choose to either add this option on the command-line or don't specify
-anything. This is the traditional mode where slaves can cross-talk among
-themselves apart from talking through the master device.
-
-5.2 private:
-       If this option is added to the command-line, the port is set in private
-mode. i.e. port won't allow cross communication between slaves.
-
-5.3 vepa:
-       If this is added to the command-line, the port is set in VEPA mode.
-i.e. port will offload switching functionality to the external entity as
-described in 802.1Qbg
-Note: VEPA mode in IPvlan has limitations. IPvlan uses the mac-address of the
-master-device, so the packets which are emitted in this mode for the adjacent
-neighbor will have source and destination mac same. This will make the switch /
-router send the redirect message.
-
-6. What to choose (macvlan vs. ipvlan)?
-       These two devices are very similar in many regards and the specific use
-case could very well define which device to choose. if one of the following
-situations defines your use case then you can choose to use ipvlan -
-       (a) The Linux host that is connected to the external switch / router has
-policy configured that allows only one mac per port.
-       (b) No of virtual devices created on a master exceed the mac capacity and
-puts the NIC in promiscuous mode and degraded performance is a concern.
-       (c) If the slave device is to be put into the hostile / untrusted network
-namespace where L2 on the slave could be changed / misused.
-
-
-6. Example configuration:
-
-  +=============================================================+
-  |  Host: host1                                                |
-  |                                                             |
-  |   +----------------------+      +----------------------+    |
-  |   |   NS:ns0             |      |  NS:ns1              |    |
-  |   |                      |      |                      |    |
-  |   |                      |      |                      |    |
-  |   |        ipvl0         |      |         ipvl1        |    |
-  |   +----------#-----------+      +-----------#----------+    |
-  |              #                              #               |
-  |              ################################               |
-  |                              # eth0                         |
-  +==============================#==============================+
-
-
-       (a) Create two network namespaces - ns0, ns1
-               ip netns add ns0
-               ip netns add ns1
-
-       (b) Create two ipvlan slaves on eth0 (master device)
-               ip link add link eth0 ipvl0 type ipvlan mode l2
-               ip link add link eth0 ipvl1 type ipvlan mode l2
-
-       (c) Assign slaves to the respective network namespaces
-               ip link set dev ipvl0 netns ns0
-               ip link set dev ipvl1 netns ns1
-
-       (d) Now switch to the namespace (ns0 or ns1) to configure the slave devices
-               - For ns0
-                       (1) ip netns exec ns0 bash
-                       (2) ip link set dev ipvl0 up
-                       (3) ip link set dev lo up
-                       (4) ip -4 addr add 127.0.0.1 dev lo
-                       (5) ip -4 addr add $IPADDR dev ipvl0
-                       (6) ip -4 route add default via $ROUTER dev ipvl0
-               - For ns1
-                       (1) ip netns exec ns1 bash
-                       (2) ip link set dev ipvl1 up
-                       (3) ip link set dev lo up
-                       (4) ip -4 addr add 127.0.0.1 dev lo
-                       (5) ip -4 addr add $IPADDR dev ipvl1
-                       (6) ip -4 route add default via $ROUTER dev ipvl1
diff --git a/Documentation/networking/ipvs-sysctl.rst b/Documentation/networking/ipvs-sysctl.rst
new file mode 100644 (file)
index 0000000..be36c46
--- /dev/null
@@ -0,0 +1,302 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========
+IPvs-sysctl
+===========
+
+/proc/sys/net/ipv4/vs/* Variables:
+==================================
+
+am_droprate - INTEGER
+       default 10
+
+       It sets the always mode drop rate, which is used in the mode 3
+       of the drop_rate defense.
+
+amemthresh - INTEGER
+       default 1024
+
+       It sets the available memory threshold (in pages), which is
+       used in the automatic modes of defense. When there is no
+       enough available memory, the respective strategy will be
+       enabled and the variable is automatically set to 2, otherwise
+       the strategy is disabled and the variable is  set  to 1.
+
+backup_only - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       If set, disable the director function while the server is
+       in backup mode to avoid packet loops for DR/TUN methods.
+
+conn_reuse_mode - INTEGER
+       1 - default
+
+       Controls how ipvs will deal with connections that are detected
+       port reuse. It is a bitmap, with the values being:
+
+       0: disable any special handling on port reuse. The new
+       connection will be delivered to the same real server that was
+       servicing the previous connection. This will effectively
+       disable expire_nodest_conn.
+
+       bit 1: enable rescheduling of new connections when it is safe.
+       That is, whenever expire_nodest_conn and for TCP sockets, when
+       the connection is in TIME_WAIT state (which is only possible if
+       you use NAT mode).
+
+       bit 2: it is bit 1 plus, for TCP connections, when connections
+       are in FIN_WAIT state, as this is the last state seen by load
+       balancer in Direct Routing mode. This bit helps on adding new
+       real servers to a very busy cluster.
+
+conntrack - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       If set, maintain connection tracking entries for
+       connections handled by IPVS.
+
+       This should be enabled if connections handled by IPVS are to be
+       also handled by stateful firewall rules. That is, iptables rules
+       that make use of connection tracking.  It is a performance
+       optimisation to disable this setting otherwise.
+
+       Connections handled by the IPVS FTP application module
+       will have connection tracking entries regardless of this setting.
+
+       Only available when IPVS is compiled with CONFIG_IP_VS_NFCT enabled.
+
+cache_bypass - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       If it is enabled, forward packets to the original destination
+       directly when no cache server is available and destination
+       address is not local (iph->daddr is RTN_UNICAST). It is mostly
+       used in transparent web cache cluster.
+
+debug_level - INTEGER
+       - 0          - transmission error messages (default)
+       - 1          - non-fatal error messages
+       - 2          - configuration
+       - 3          - destination trash
+       - 4          - drop entry
+       - 5          - service lookup
+       - 6          - scheduling
+       - 7          - connection new/expire, lookup and synchronization
+       - 8          - state transition
+       - 9          - binding destination, template checks and applications
+       - 10         - IPVS packet transmission
+       - 11         - IPVS packet handling (ip_vs_in/ip_vs_out)
+       - 12 or more - packet traversal
+
+       Only available when IPVS is compiled with CONFIG_IP_VS_DEBUG enabled.
+
+       Higher debugging levels include the messages for lower debugging
+       levels, so setting debug level 2, includes level 0, 1 and 2
+       messages. Thus, logging becomes more and more verbose the higher
+       the level.
+
+drop_entry - INTEGER
+       - 0  - disabled (default)
+
+       The drop_entry defense is to randomly drop entries in the
+       connection hash table, just in order to collect back some
+       memory for new connections. In the current code, the
+       drop_entry procedure can be activated every second, then it
+       randomly scans 1/32 of the whole and drops entries that are in
+       the SYN-RECV/SYNACK state, which should be effective against
+       syn-flooding attack.
+
+       The valid values of drop_entry are from 0 to 3, where 0 means
+       that this strategy is always disabled, 1 and 2 mean automatic
+       modes (when there is no enough available memory, the strategy
+       is enabled and the variable is automatically set to 2,
+       otherwise the strategy is disabled and the variable is set to
+       1), and 3 means that that the strategy is always enabled.
+
+drop_packet - INTEGER
+       - 0  - disabled (default)
+
+       The drop_packet defense is designed to drop 1/rate packets
+       before forwarding them to real servers. If the rate is 1, then
+       drop all the incoming packets.
+
+       The value definition is the same as that of the drop_entry. In
+       the automatic mode, the rate is determined by the follow
+       formula: rate = amemthresh / (amemthresh - available_memory)
+       when available memory is less than the available memory
+       threshold. When the mode 3 is set, the always mode drop rate
+       is controlled by the /proc/sys/net/ipv4/vs/am_droprate.
+
+expire_nodest_conn - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       The default value is 0, the load balancer will silently drop
+       packets when its destination server is not available. It may
+       be useful, when user-space monitoring program deletes the
+       destination server (because of server overload or wrong
+       detection) and add back the server later, and the connections
+       to the server can continue.
+
+       If this feature is enabled, the load balancer will expire the
+       connection immediately when a packet arrives and its
+       destination server is not available, then the client program
+       will be notified that the connection is closed. This is
+       equivalent to the feature some people requires to flush
+       connections when its destination is not available.
+
+expire_quiescent_template - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       When set to a non-zero value, the load balancer will expire
+       persistent templates when the destination server is quiescent.
+       This may be useful, when a user makes a destination server
+       quiescent by setting its weight to 0 and it is desired that
+       subsequent otherwise persistent connections are sent to a
+       different destination server.  By default new persistent
+       connections are allowed to quiescent destination servers.
+
+       If this feature is enabled, the load balancer will expire the
+       persistence template if it is to be used to schedule a new
+       connection and the destination server is quiescent.
+
+ignore_tunneled - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       If set, ipvs will set the ipvs_property on all packets which are of
+       unrecognized protocols.  This prevents us from routing tunneled
+       protocols like ipip, which is useful to prevent rescheduling
+       packets that have been tunneled to the ipvs host (i.e. to prevent
+       ipvs routing loops when ipvs is also acting as a real server).
+
+nat_icmp_send - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       It controls sending icmp error messages (ICMP_DEST_UNREACH)
+       for VS/NAT when the load balancer receives packets from real
+       servers but the connection entries don't exist.
+
+pmtu_disc - BOOLEAN
+       - 0 - disabled
+       - not 0 - enabled (default)
+
+       By default, reject with FRAG_NEEDED all DF packets that exceed
+       the PMTU, irrespective of the forwarding method. For TUN method
+       the flag can be disabled to fragment such packets.
+
+secure_tcp - INTEGER
+       - 0  - disabled (default)
+
+       The secure_tcp defense is to use a more complicated TCP state
+       transition table. For VS/NAT, it also delays entering the
+       TCP ESTABLISHED state until the three way handshake is completed.
+
+       The value definition is the same as that of drop_entry and
+       drop_packet.
+
+sync_threshold - vector of 2 INTEGERs: sync_threshold, sync_period
+       default 3 50
+
+       It sets synchronization threshold, which is the minimum number
+       of incoming packets that a connection needs to receive before
+       the connection will be synchronized. A connection will be
+       synchronized, every time the number of its incoming packets
+       modulus sync_period equals the threshold. The range of the
+       threshold is from 0 to sync_period.
+
+       When sync_period and sync_refresh_period are 0, send sync only
+       for state changes or only once when pkts matches sync_threshold
+
+sync_refresh_period - UNSIGNED INTEGER
+       default 0
+
+       In seconds, difference in reported connection timer that triggers
+       new sync message. It can be used to avoid sync messages for the
+       specified period (or half of the connection timeout if it is lower)
+       if connection state is not changed since last sync.
+
+       This is useful for normal connections with high traffic to reduce
+       sync rate. Additionally, retry sync_retries times with period of
+       sync_refresh_period/8.
+
+sync_retries - INTEGER
+       default 0
+
+       Defines sync retries with period of sync_refresh_period/8. Useful
+       to protect against loss of sync messages. The range of the
+       sync_retries is from 0 to 3.
+
+sync_qlen_max - UNSIGNED LONG
+
+       Hard limit for queued sync messages that are not sent yet. It
+       defaults to 1/32 of the memory pages but actually represents
+       number of messages. It will protect us from allocating large
+       parts of memory when the sending rate is lower than the queuing
+       rate.
+
+sync_sock_size - INTEGER
+       default 0
+
+       Configuration of SNDBUF (master) or RCVBUF (slave) socket limit.
+       Default value is 0 (preserve system defaults).
+
+sync_ports - INTEGER
+       default 1
+
+       The number of threads that master and backup servers can use for
+       sync traffic. Every thread will use single UDP port, thread 0 will
+       use the default port 8848 while last thread will use port
+       8848+sync_ports-1.
+
+snat_reroute - BOOLEAN
+       - 0 - disabled
+       - not 0 - enabled (default)
+
+       If enabled, recalculate the route of SNATed packets from
+       realservers so that they are routed as if they originate from the
+       director. Otherwise they are routed as if they are forwarded by the
+       director.
+
+       If policy routing is in effect then it is possible that the route
+       of a packet originating from a director is routed differently to a
+       packet being forwarded by the director.
+
+       If policy routing is not in effect then the recalculated route will
+       always be the same as the original route so it is an optimisation
+       to disable snat_reroute and avoid the recalculation.
+
+sync_persist_mode - INTEGER
+       default 0
+
+       Controls the synchronisation of connections when using persistence
+
+       0: All types of connections are synchronised
+
+       1: Attempt to reduce the synchronisation traffic depending on
+       the connection type. For persistent services avoid synchronisation
+       for normal connections, do it only for persistence templates.
+       In such case, for TCP and SCTP it may need enabling sloppy_tcp and
+       sloppy_sctp flags on backup servers. For non-persistent services
+       such optimization is not applied, mode 0 is assumed.
+
+sync_version - INTEGER
+       default 1
+
+       The version of the synchronisation protocol used when sending
+       synchronisation messages.
+
+       0 selects the original synchronisation protocol (version 0). This
+       should be used when sending synchronisation messages to a legacy
+       system that only understands the original synchronisation protocol.
+
+       1 selects the current synchronisation protocol (version 1). This
+       should be used where possible.
+
+       Kernels with this sync_version entry are able to receive messages
+       of both version 1 and version 2 of the synchronisation protocol.
diff --git a/Documentation/networking/ipvs-sysctl.txt b/Documentation/networking/ipvs-sysctl.txt
deleted file mode 100644 (file)
index 0568986..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-/proc/sys/net/ipv4/vs/* Variables:
-
-am_droprate - INTEGER
-        default 10
-
-        It sets the always mode drop rate, which is used in the mode 3
-        of the drop_rate defense.
-
-amemthresh - INTEGER
-        default 1024
-
-        It sets the available memory threshold (in pages), which is
-        used in the automatic modes of defense. When there is no
-        enough available memory, the respective strategy will be
-        enabled and the variable is automatically set to 2, otherwise
-        the strategy is disabled and the variable is  set  to 1.
-
-backup_only - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
-
-       If set, disable the director function while the server is
-       in backup mode to avoid packet loops for DR/TUN methods.
-
-conn_reuse_mode - INTEGER
-       1 - default
-
-       Controls how ipvs will deal with connections that are detected
-       port reuse. It is a bitmap, with the values being:
-
-       0: disable any special handling on port reuse. The new
-       connection will be delivered to the same real server that was
-       servicing the previous connection. This will effectively
-       disable expire_nodest_conn.
-
-       bit 1: enable rescheduling of new connections when it is safe.
-       That is, whenever expire_nodest_conn and for TCP sockets, when
-       the connection is in TIME_WAIT state (which is only possible if
-       you use NAT mode).
-
-       bit 2: it is bit 1 plus, for TCP connections, when connections
-       are in FIN_WAIT state, as this is the last state seen by load
-       balancer in Direct Routing mode. This bit helps on adding new
-       real servers to a very busy cluster.
-
-conntrack - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
-
-       If set, maintain connection tracking entries for
-       connections handled by IPVS.
-
-       This should be enabled if connections handled by IPVS are to be
-       also handled by stateful firewall rules. That is, iptables rules
-       that make use of connection tracking.  It is a performance
-       optimisation to disable this setting otherwise.
-
-       Connections handled by the IPVS FTP application module
-       will have connection tracking entries regardless of this setting.
-
-       Only available when IPVS is compiled with CONFIG_IP_VS_NFCT enabled.
-
-cache_bypass - BOOLEAN
-        0 - disabled (default)
-        not 0 - enabled
-
-        If it is enabled, forward packets to the original destination
-        directly when no cache server is available and destination
-        address is not local (iph->daddr is RTN_UNICAST). It is mostly
-        used in transparent web cache cluster.
-
-debug_level - INTEGER
-       0          - transmission error messages (default)
-       1          - non-fatal error messages
-       2          - configuration
-       3          - destination trash
-       4          - drop entry
-       5          - service lookup
-       6          - scheduling
-       7          - connection new/expire, lookup and synchronization
-       8          - state transition
-       9          - binding destination, template checks and applications
-       10         - IPVS packet transmission
-       11         - IPVS packet handling (ip_vs_in/ip_vs_out)
-       12 or more - packet traversal
-
-       Only available when IPVS is compiled with CONFIG_IP_VS_DEBUG enabled.
-
-       Higher debugging levels include the messages for lower debugging
-       levels, so setting debug level 2, includes level 0, 1 and 2
-       messages. Thus, logging becomes more and more verbose the higher
-       the level.
-
-drop_entry - INTEGER
-        0  - disabled (default)
-
-        The drop_entry defense is to randomly drop entries in the
-        connection hash table, just in order to collect back some
-        memory for new connections. In the current code, the
-        drop_entry procedure can be activated every second, then it
-        randomly scans 1/32 of the whole and drops entries that are in
-        the SYN-RECV/SYNACK state, which should be effective against
-        syn-flooding attack.
-
-        The valid values of drop_entry are from 0 to 3, where 0 means
-        that this strategy is always disabled, 1 and 2 mean automatic
-        modes (when there is no enough available memory, the strategy
-        is enabled and the variable is automatically set to 2,
-        otherwise the strategy is disabled and the variable is set to
-        1), and 3 means that that the strategy is always enabled.
-
-drop_packet - INTEGER
-        0  - disabled (default)
-
-        The drop_packet defense is designed to drop 1/rate packets
-        before forwarding them to real servers. If the rate is 1, then
-        drop all the incoming packets.
-
-        The value definition is the same as that of the drop_entry. In
-        the automatic mode, the rate is determined by the follow
-        formula: rate = amemthresh / (amemthresh - available_memory)
-        when available memory is less than the available memory
-        threshold. When the mode 3 is set, the always mode drop rate
-        is controlled by the /proc/sys/net/ipv4/vs/am_droprate.
-
-expire_nodest_conn - BOOLEAN
-        0 - disabled (default)
-        not 0 - enabled
-
-        The default value is 0, the load balancer will silently drop
-        packets when its destination server is not available. It may
-        be useful, when user-space monitoring program deletes the
-        destination server (because of server overload or wrong
-        detection) and add back the server later, and the connections
-        to the server can continue.
-
-        If this feature is enabled, the load balancer will expire the
-        connection immediately when a packet arrives and its
-        destination server is not available, then the client program
-        will be notified that the connection is closed. This is
-        equivalent to the feature some people requires to flush
-        connections when its destination is not available.
-
-expire_quiescent_template - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
-
-       When set to a non-zero value, the load balancer will expire
-       persistent templates when the destination server is quiescent.
-       This may be useful, when a user makes a destination server
-       quiescent by setting its weight to 0 and it is desired that
-       subsequent otherwise persistent connections are sent to a
-       different destination server.  By default new persistent
-       connections are allowed to quiescent destination servers.
-
-       If this feature is enabled, the load balancer will expire the
-       persistence template if it is to be used to schedule a new
-       connection and the destination server is quiescent.
-
-ignore_tunneled - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
-
-       If set, ipvs will set the ipvs_property on all packets which are of
-       unrecognized protocols.  This prevents us from routing tunneled
-       protocols like ipip, which is useful to prevent rescheduling
-       packets that have been tunneled to the ipvs host (i.e. to prevent
-       ipvs routing loops when ipvs is also acting as a real server).
-
-nat_icmp_send - BOOLEAN
-        0 - disabled (default)
-        not 0 - enabled
-
-        It controls sending icmp error messages (ICMP_DEST_UNREACH)
-        for VS/NAT when the load balancer receives packets from real
-        servers but the connection entries don't exist.
-
-pmtu_disc - BOOLEAN
-       0 - disabled
-       not 0 - enabled (default)
-
-       By default, reject with FRAG_NEEDED all DF packets that exceed
-       the PMTU, irrespective of the forwarding method. For TUN method
-       the flag can be disabled to fragment such packets.
-
-secure_tcp - INTEGER
-        0  - disabled (default)
-
-       The secure_tcp defense is to use a more complicated TCP state
-       transition table. For VS/NAT, it also delays entering the
-       TCP ESTABLISHED state until the three way handshake is completed.
-
-        The value definition is the same as that of drop_entry and
-        drop_packet.
-
-sync_threshold - vector of 2 INTEGERs: sync_threshold, sync_period
-       default 3 50
-
-       It sets synchronization threshold, which is the minimum number
-       of incoming packets that a connection needs to receive before
-       the connection will be synchronized. A connection will be
-       synchronized, every time the number of its incoming packets
-       modulus sync_period equals the threshold. The range of the
-       threshold is from 0 to sync_period.
-
-       When sync_period and sync_refresh_period are 0, send sync only
-       for state changes or only once when pkts matches sync_threshold
-
-sync_refresh_period - UNSIGNED INTEGER
-       default 0
-
-       In seconds, difference in reported connection timer that triggers
-       new sync message. It can be used to avoid sync messages for the
-       specified period (or half of the connection timeout if it is lower)
-       if connection state is not changed since last sync.
-
-       This is useful for normal connections with high traffic to reduce
-       sync rate. Additionally, retry sync_retries times with period of
-       sync_refresh_period/8.
-
-sync_retries - INTEGER
-       default 0
-
-       Defines sync retries with period of sync_refresh_period/8. Useful
-       to protect against loss of sync messages. The range of the
-       sync_retries is from 0 to 3.
-
-sync_qlen_max - UNSIGNED LONG
-
-       Hard limit for queued sync messages that are not sent yet. It
-       defaults to 1/32 of the memory pages but actually represents
-       number of messages. It will protect us from allocating large
-       parts of memory when the sending rate is lower than the queuing
-       rate.
-
-sync_sock_size - INTEGER
-       default 0
-
-       Configuration of SNDBUF (master) or RCVBUF (slave) socket limit.
-       Default value is 0 (preserve system defaults).
-
-sync_ports - INTEGER
-       default 1
-
-       The number of threads that master and backup servers can use for
-       sync traffic. Every thread will use single UDP port, thread 0 will
-       use the default port 8848 while last thread will use port
-       8848+sync_ports-1.
-
-snat_reroute - BOOLEAN
-       0 - disabled
-       not 0 - enabled (default)
-
-       If enabled, recalculate the route of SNATed packets from
-       realservers so that they are routed as if they originate from the
-       director. Otherwise they are routed as if they are forwarded by the
-       director.
-
-       If policy routing is in effect then it is possible that the route
-       of a packet originating from a director is routed differently to a
-       packet being forwarded by the director.
-
-       If policy routing is not in effect then the recalculated route will
-       always be the same as the original route so it is an optimisation
-       to disable snat_reroute and avoid the recalculation.
-
-sync_persist_mode - INTEGER
-       default 0
-
-       Controls the synchronisation of connections when using persistence
-
-       0: All types of connections are synchronised
-       1: Attempt to reduce the synchronisation traffic depending on
-       the connection type. For persistent services avoid synchronisation
-       for normal connections, do it only for persistence templates.
-       In such case, for TCP and SCTP it may need enabling sloppy_tcp and
-       sloppy_sctp flags on backup servers. For non-persistent services
-       such optimization is not applied, mode 0 is assumed.
-
-sync_version - INTEGER
-       default 1
-
-       The version of the synchronisation protocol used when sending
-       synchronisation messages.
-
-       0 selects the original synchronisation protocol (version 0). This
-       should be used when sending synchronisation messages to a legacy
-       system that only understands the original synchronisation protocol.
-
-       1 selects the current synchronisation protocol (version 1). This
-       should be used where possible.
-
-       Kernels with this sync_version entry are able to receive messages
-       of both version 1 and version 2 of the synchronisation protocol.
diff --git a/Documentation/networking/kcm.rst b/Documentation/networking/kcm.rst
new file mode 100644 (file)
index 0000000..db0f556
--- /dev/null
@@ -0,0 +1,290 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============================
+Kernel Connection Multiplexor
+=============================
+
+Kernel Connection Multiplexor (KCM) is a mechanism that provides a message based
+interface over TCP for generic application protocols. With KCM an application
+can efficiently send and receive application protocol messages over TCP using
+datagram sockets.
+
+KCM implements an NxM multiplexor in the kernel as diagrammed below::
+
+    +------------+   +------------+   +------------+   +------------+
+    | KCM socket |   | KCM socket |   | KCM socket |   | KCM socket |
+    +------------+   +------------+   +------------+   +------------+
+       |                 |               |                |
+       +-----------+     |               |     +----------+
+                   |     |               |     |
+               +----------------------------------+
+               |           Multiplexor            |
+               +----------------------------------+
+                   |   |           |           |  |
+       +---------+   |           |           |  ------------+
+       |             |           |           |              |
+    +----------+  +----------+  +----------+  +----------+ +----------+
+    |  Psock   |  |  Psock   |  |  Psock   |  |  Psock   | |  Psock   |
+    +----------+  +----------+  +----------+  +----------+ +----------+
+       |              |           |            |             |
+    +----------+  +----------+  +----------+  +----------+ +----------+
+    | TCP sock |  | TCP sock |  | TCP sock |  | TCP sock | | TCP sock |
+    +----------+  +----------+  +----------+  +----------+ +----------+
+
+KCM sockets
+===========
+
+The KCM sockets provide the user interface to the multiplexor. All the KCM sockets
+bound to a multiplexor are considered to have equivalent function, and I/O
+operations in different sockets may be done in parallel without the need for
+synchronization between threads in userspace.
+
+Multiplexor
+===========
+
+The multiplexor provides the message steering. In the transmit path, messages
+written on a KCM socket are sent atomically on an appropriate TCP socket.
+Similarly, in the receive path, messages are constructed on each TCP socket
+(Psock) and complete messages are steered to a KCM socket.
+
+TCP sockets & Psocks
+====================
+
+TCP sockets may be bound to a KCM multiplexor. A Psock structure is allocated
+for each bound TCP socket, this structure holds the state for constructing
+messages on receive as well as other connection specific information for KCM.
+
+Connected mode semantics
+========================
+
+Each multiplexor assumes that all attached TCP connections are to the same
+destination and can use the different connections for load balancing when
+transmitting. The normal send and recv calls (include sendmmsg and recvmmsg)
+can be used to send and receive messages from the KCM socket.
+
+Socket types
+============
+
+KCM supports SOCK_DGRAM and SOCK_SEQPACKET socket types.
+
+Message delineation
+-------------------
+
+Messages are sent over a TCP stream with some application protocol message
+format that typically includes a header which frames the messages. The length
+of a received message can be deduced from the application protocol header
+(often just a simple length field).
+
+A TCP stream must be parsed to determine message boundaries. Berkeley Packet
+Filter (BPF) is used for this. When attaching a TCP socket to a multiplexor a
+BPF program must be specified. The program is called at the start of receiving
+a new message and is given an skbuff that contains the bytes received so far.
+It parses the message header and returns the length of the message. Given this
+information, KCM will construct the message of the stated length and deliver it
+to a KCM socket.
+
+TCP socket management
+---------------------
+
+When a TCP socket is attached to a KCM multiplexor data ready (POLLIN) and
+write space available (POLLOUT) events are handled by the multiplexor. If there
+is a state change (disconnection) or other error on a TCP socket, an error is
+posted on the TCP socket so that a POLLERR event happens and KCM discontinues
+using the socket. When the application gets the error notification for a
+TCP socket, it should unattach the socket from KCM and then handle the error
+condition (the typical response is to close the socket and create a new
+connection if necessary).
+
+KCM limits the maximum receive message size to be the size of the receive
+socket buffer on the attached TCP socket (the socket buffer size can be set by
+SO_RCVBUF). If the length of a new message reported by the BPF program is
+greater than this limit a corresponding error (EMSGSIZE) is posted on the TCP
+socket. The BPF program may also enforce a maximum messages size and report an
+error when it is exceeded.
+
+A timeout may be set for assembling messages on a receive socket. The timeout
+value is taken from the receive timeout of the attached TCP socket (this is set
+by SO_RCVTIMEO). If the timer expires before assembly is complete an error
+(ETIMEDOUT) is posted on the socket.
+
+User interface
+==============
+
+Creating a multiplexor
+----------------------
+
+A new multiplexor and initial KCM socket is created by a socket call::
+
+  socket(AF_KCM, type, protocol)
+
+- type is either SOCK_DGRAM or SOCK_SEQPACKET
+- protocol is KCMPROTO_CONNECTED
+
+Cloning KCM sockets
+-------------------
+
+After the first KCM socket is created using the socket call as described
+above, additional sockets for the multiplexor can be created by cloning
+a KCM socket. This is accomplished by an ioctl on a KCM socket::
+
+  /* From linux/kcm.h */
+  struct kcm_clone {
+       int fd;
+  };
+
+  struct kcm_clone info;
+
+  memset(&info, 0, sizeof(info));
+
+  err = ioctl(kcmfd, SIOCKCMCLONE, &info);
+
+  if (!err)
+    newkcmfd = info.fd;
+
+Attach transport sockets
+------------------------
+
+Attaching of transport sockets to a multiplexor is performed by calling an
+ioctl on a KCM socket for the multiplexor. e.g.::
+
+  /* From linux/kcm.h */
+  struct kcm_attach {
+       int fd;
+       int bpf_fd;
+  };
+
+  struct kcm_attach info;
+
+  memset(&info, 0, sizeof(info));
+
+  info.fd = tcpfd;
+  info.bpf_fd = bpf_prog_fd;
+
+  ioctl(kcmfd, SIOCKCMATTACH, &info);
+
+The kcm_attach structure contains:
+
+  - fd: file descriptor for TCP socket being attached
+  - bpf_prog_fd: file descriptor for compiled BPF program downloaded
+
+Unattach transport sockets
+--------------------------
+
+Unattaching a transport socket from a multiplexor is straightforward. An
+"unattach" ioctl is done with the kcm_unattach structure as the argument::
+
+  /* From linux/kcm.h */
+  struct kcm_unattach {
+       int fd;
+  };
+
+  struct kcm_unattach info;
+
+  memset(&info, 0, sizeof(info));
+
+  info.fd = cfd;
+
+  ioctl(fd, SIOCKCMUNATTACH, &info);
+
+Disabling receive on KCM socket
+-------------------------------
+
+A setsockopt is used to disable or enable receiving on a KCM socket.
+When receive is disabled, any pending messages in the socket's
+receive buffer are moved to other sockets. This feature is useful
+if an application thread knows that it will be doing a lot of
+work on a request and won't be able to service new messages for a
+while. Example use::
+
+  int val = 1;
+
+  setsockopt(kcmfd, SOL_KCM, KCM_RECV_DISABLE, &val, sizeof(val))
+
+BFP programs for message delineation
+------------------------------------
+
+BPF programs can be compiled using the BPF LLVM backend. For example,
+the BPF program for parsing Thrift is::
+
+  #include "bpf.h" /* for __sk_buff */
+  #include "bpf_helpers.h" /* for load_word intrinsic */
+
+  SEC("socket_kcm")
+  int bpf_prog1(struct __sk_buff *skb)
+  {
+       return load_word(skb, 0) + 4;
+  }
+
+  char _license[] SEC("license") = "GPL";
+
+Use in applications
+===================
+
+KCM accelerates application layer protocols. Specifically, it allows
+applications to use a message based interface for sending and receiving
+messages. The kernel provides necessary assurances that messages are sent
+and received atomically. This relieves much of the burden applications have
+in mapping a message based protocol onto the TCP stream. KCM also make
+application layer messages a unit of work in the kernel for the purposes of
+steering and scheduling, which in turn allows a simpler networking model in
+multithreaded applications.
+
+Configurations
+--------------
+
+In an Nx1 configuration, KCM logically provides multiple socket handles
+to the same TCP connection. This allows parallelism between in I/O
+operations on the TCP socket (for instance copyin and copyout of data is
+parallelized). In an application, a KCM socket can be opened for each
+processing thread and inserted into the epoll (similar to how SO_REUSEPORT
+is used to allow multiple listener sockets on the same port).
+
+In a MxN configuration, multiple connections are established to the
+same destination. These are used for simple load balancing.
+
+Message batching
+----------------
+
+The primary purpose of KCM is load balancing between KCM sockets and hence
+threads in a nominal use case. Perfect load balancing, that is steering
+each received message to a different KCM socket or steering each sent
+message to a different TCP socket, can negatively impact performance
+since this doesn't allow for affinities to be established. Balancing
+based on groups, or batches of messages, can be beneficial for performance.
+
+On transmit, there are three ways an application can batch (pipeline)
+messages on a KCM socket.
+
+  1) Send multiple messages in a single sendmmsg.
+  2) Send a group of messages each with a sendmsg call, where all messages
+     except the last have MSG_BATCH in the flags of sendmsg call.
+  3) Create "super message" composed of multiple messages and send this
+     with a single sendmsg.
+
+On receive, the KCM module attempts to queue messages received on the
+same KCM socket during each TCP ready callback. The targeted KCM socket
+changes at each receive ready callback on the KCM socket. The application
+does not need to configure this.
+
+Error handling
+--------------
+
+An application should include a thread to monitor errors raised on
+the TCP connection. Normally, this will be done by placing each
+TCP socket attached to a KCM multiplexor in epoll set for POLLERR
+event. If an error occurs on an attached TCP socket, KCM sets an EPIPE
+on the socket thus waking up the application thread. When the application
+sees the error (which may just be a disconnect) it should unattach the
+socket from KCM and then close it. It is assumed that once an error is
+posted on the TCP socket the data stream is unrecoverable (i.e. an error
+may have occurred in the middle of receiving a message).
+
+TCP connection monitoring
+-------------------------
+
+In KCM there is no means to correlate a message to the TCP socket that
+was used to send or receive the message (except in the case there is
+only one attached TCP socket). However, the application does retain
+an open file descriptor to the socket so it will be able to get statistics
+from the socket which can be used in detecting issues (such as high
+retransmissions on the socket).
diff --git a/Documentation/networking/kcm.txt b/Documentation/networking/kcm.txt
deleted file mode 100644 (file)
index b773a52..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-Kernel Connection Multiplexor
------------------------------
-
-Kernel Connection Multiplexor (KCM) is a mechanism that provides a message based
-interface over TCP for generic application protocols. With KCM an application
-can efficiently send and receive application protocol messages over TCP using
-datagram sockets.
-
-KCM implements an NxM multiplexor in the kernel as diagrammed below:
-
-+------------+   +------------+   +------------+   +------------+
-| KCM socket |   | KCM socket |   | KCM socket |   | KCM socket |
-+------------+   +------------+   +------------+   +------------+
-      |                 |               |                |
-      +-----------+     |               |     +----------+
-                  |     |               |     |
-               +----------------------------------+
-               |           Multiplexor            |
-               +----------------------------------+
-                 |   |           |           |  |
-       +---------+   |           |           |  ------------+
-       |             |           |           |              |
-+----------+  +----------+  +----------+  +----------+ +----------+
-|  Psock   |  |  Psock   |  |  Psock   |  |  Psock   | |  Psock   |
-+----------+  +----------+  +----------+  +----------+ +----------+
-      |              |           |            |             |
-+----------+  +----------+  +----------+  +----------+ +----------+
-| TCP sock |  | TCP sock |  | TCP sock |  | TCP sock | | TCP sock |
-+----------+  +----------+  +----------+  +----------+ +----------+
-
-KCM sockets
------------
-
-The KCM sockets provide the user interface to the multiplexor. All the KCM sockets
-bound to a multiplexor are considered to have equivalent function, and I/O
-operations in different sockets may be done in parallel without the need for
-synchronization between threads in userspace.
-
-Multiplexor
------------
-
-The multiplexor provides the message steering. In the transmit path, messages
-written on a KCM socket are sent atomically on an appropriate TCP socket.
-Similarly, in the receive path, messages are constructed on each TCP socket
-(Psock) and complete messages are steered to a KCM socket.
-
-TCP sockets & Psocks
---------------------
-
-TCP sockets may be bound to a KCM multiplexor. A Psock structure is allocated
-for each bound TCP socket, this structure holds the state for constructing
-messages on receive as well as other connection specific information for KCM.
-
-Connected mode semantics
-------------------------
-
-Each multiplexor assumes that all attached TCP connections are to the same
-destination and can use the different connections for load balancing when
-transmitting. The normal send and recv calls (include sendmmsg and recvmmsg)
-can be used to send and receive messages from the KCM socket.
-
-Socket types
-------------
-
-KCM supports SOCK_DGRAM and SOCK_SEQPACKET socket types.
-
-Message delineation
--------------------
-
-Messages are sent over a TCP stream with some application protocol message
-format that typically includes a header which frames the messages. The length
-of a received message can be deduced from the application protocol header
-(often just a simple length field).
-
-A TCP stream must be parsed to determine message boundaries. Berkeley Packet
-Filter (BPF) is used for this. When attaching a TCP socket to a multiplexor a
-BPF program must be specified. The program is called at the start of receiving
-a new message and is given an skbuff that contains the bytes received so far.
-It parses the message header and returns the length of the message. Given this
-information, KCM will construct the message of the stated length and deliver it
-to a KCM socket.
-
-TCP socket management
----------------------
-
-When a TCP socket is attached to a KCM multiplexor data ready (POLLIN) and
-write space available (POLLOUT) events are handled by the multiplexor. If there
-is a state change (disconnection) or other error on a TCP socket, an error is
-posted on the TCP socket so that a POLLERR event happens and KCM discontinues
-using the socket. When the application gets the error notification for a
-TCP socket, it should unattach the socket from KCM and then handle the error
-condition (the typical response is to close the socket and create a new
-connection if necessary).
-
-KCM limits the maximum receive message size to be the size of the receive
-socket buffer on the attached TCP socket (the socket buffer size can be set by
-SO_RCVBUF). If the length of a new message reported by the BPF program is
-greater than this limit a corresponding error (EMSGSIZE) is posted on the TCP
-socket. The BPF program may also enforce a maximum messages size and report an
-error when it is exceeded.
-
-A timeout may be set for assembling messages on a receive socket. The timeout
-value is taken from the receive timeout of the attached TCP socket (this is set
-by SO_RCVTIMEO). If the timer expires before assembly is complete an error
-(ETIMEDOUT) is posted on the socket.
-
-User interface
-==============
-
-Creating a multiplexor
-----------------------
-
-A new multiplexor and initial KCM socket is created by a socket call:
-
-  socket(AF_KCM, type, protocol)
-
-  - type is either SOCK_DGRAM or SOCK_SEQPACKET
-  - protocol is KCMPROTO_CONNECTED
-
-Cloning KCM sockets
--------------------
-
-After the first KCM socket is created using the socket call as described
-above, additional sockets for the multiplexor can be created by cloning
-a KCM socket. This is accomplished by an ioctl on a KCM socket:
-
-  /* From linux/kcm.h */
-  struct kcm_clone {
-        int fd;
-  };
-
-  struct kcm_clone info;
-
-  memset(&info, 0, sizeof(info));
-
-  err = ioctl(kcmfd, SIOCKCMCLONE, &info);
-
-  if (!err)
-    newkcmfd = info.fd;
-
-Attach transport sockets
-------------------------
-
-Attaching of transport sockets to a multiplexor is performed by calling an
-ioctl on a KCM socket for the multiplexor. e.g.:
-
-  /* From linux/kcm.h */
-  struct kcm_attach {
-        int fd;
-       int bpf_fd;
-  };
-
-  struct kcm_attach info;
-
-  memset(&info, 0, sizeof(info));
-
-  info.fd = tcpfd;
-  info.bpf_fd = bpf_prog_fd;
-
-  ioctl(kcmfd, SIOCKCMATTACH, &info);
-
-The kcm_attach structure contains:
-  fd: file descriptor for TCP socket being attached
-  bpf_prog_fd: file descriptor for compiled BPF program downloaded
-
-Unattach transport sockets
---------------------------
-
-Unattaching a transport socket from a multiplexor is straightforward. An
-"unattach" ioctl is done with the kcm_unattach structure as the argument:
-
-  /* From linux/kcm.h */
-  struct kcm_unattach {
-        int fd;
-  };
-
-  struct kcm_unattach info;
-
-  memset(&info, 0, sizeof(info));
-
-  info.fd = cfd;
-
-  ioctl(fd, SIOCKCMUNATTACH, &info);
-
-Disabling receive on KCM socket
--------------------------------
-
-A setsockopt is used to disable or enable receiving on a KCM socket.
-When receive is disabled, any pending messages in the socket's
-receive buffer are moved to other sockets. This feature is useful
-if an application thread knows that it will be doing a lot of
-work on a request and won't be able to service new messages for a
-while. Example use:
-
-  int val = 1;
-
-  setsockopt(kcmfd, SOL_KCM, KCM_RECV_DISABLE, &val, sizeof(val))
-
-BFP programs for message delineation
-------------------------------------
-
-BPF programs can be compiled using the BPF LLVM backend. For example,
-the BPF program for parsing Thrift is:
-
-  #include "bpf.h" /* for __sk_buff */
-  #include "bpf_helpers.h" /* for load_word intrinsic */
-
-  SEC("socket_kcm")
-  int bpf_prog1(struct __sk_buff *skb)
-  {
-       return load_word(skb, 0) + 4;
-  }
-
-  char _license[] SEC("license") = "GPL";
-
-Use in applications
-===================
-
-KCM accelerates application layer protocols. Specifically, it allows
-applications to use a message based interface for sending and receiving
-messages. The kernel provides necessary assurances that messages are sent
-and received atomically. This relieves much of the burden applications have
-in mapping a message based protocol onto the TCP stream. KCM also make
-application layer messages a unit of work in the kernel for the purposes of
-steering and scheduling, which in turn allows a simpler networking model in
-multithreaded applications.
-
-Configurations
---------------
-
-In an Nx1 configuration, KCM logically provides multiple socket handles
-to the same TCP connection. This allows parallelism between in I/O
-operations on the TCP socket (for instance copyin and copyout of data is
-parallelized). In an application, a KCM socket can be opened for each
-processing thread and inserted into the epoll (similar to how SO_REUSEPORT
-is used to allow multiple listener sockets on the same port).
-
-In a MxN configuration, multiple connections are established to the
-same destination. These are used for simple load balancing.
-
-Message batching
-----------------
-
-The primary purpose of KCM is load balancing between KCM sockets and hence
-threads in a nominal use case. Perfect load balancing, that is steering
-each received message to a different KCM socket or steering each sent
-message to a different TCP socket, can negatively impact performance
-since this doesn't allow for affinities to be established. Balancing
-based on groups, or batches of messages, can be beneficial for performance.
-
-On transmit, there are three ways an application can batch (pipeline)
-messages on a KCM socket.
-  1) Send multiple messages in a single sendmmsg.
-  2) Send a group of messages each with a sendmsg call, where all messages
-     except the last have MSG_BATCH in the flags of sendmsg call.
-  3) Create "super message" composed of multiple messages and send this
-     with a single sendmsg.
-
-On receive, the KCM module attempts to queue messages received on the
-same KCM socket during each TCP ready callback. The targeted KCM socket
-changes at each receive ready callback on the KCM socket. The application
-does not need to configure this.
-
-Error handling
---------------
-
-An application should include a thread to monitor errors raised on
-the TCP connection. Normally, this will be done by placing each
-TCP socket attached to a KCM multiplexor in epoll set for POLLERR
-event. If an error occurs on an attached TCP socket, KCM sets an EPIPE
-on the socket thus waking up the application thread. When the application
-sees the error (which may just be a disconnect) it should unattach the
-socket from KCM and then close it. It is assumed that once an error is
-posted on the TCP socket the data stream is unrecoverable (i.e. an error
-may have occurred in the middle of receiving a message).
-
-TCP connection monitoring
--------------------------
-
-In KCM there is no means to correlate a message to the TCP socket that
-was used to send or receive the message (except in the case there is
-only one attached TCP socket). However, the application does retain
-an open file descriptor to the socket so it will be able to get statistics
-from the socket which can be used in detecting issues (such as high
-retransmissions on the socket).
diff --git a/Documentation/networking/l2tp.rst b/Documentation/networking/l2tp.rst
new file mode 100644 (file)
index 0000000..a48238a
--- /dev/null
@@ -0,0 +1,358 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====
+L2TP
+====
+
+This document describes how to use the kernel's L2TP drivers to
+provide L2TP functionality. L2TP is a protocol that tunnels one or
+more sessions over an IP tunnel. It is commonly used for VPNs
+(L2TP/IPSec) and by ISPs to tunnel subscriber PPP sessions over an IP
+network infrastructure. With L2TPv3, it is also useful as a Layer-2
+tunneling infrastructure.
+
+Features
+========
+
+L2TPv2 (PPP over L2TP (UDP tunnels)).
+L2TPv3 ethernet pseudowires.
+L2TPv3 PPP pseudowires.
+L2TPv3 IP encapsulation.
+Netlink sockets for L2TPv3 configuration management.
+
+History
+=======
+
+The original pppol2tp driver was introduced in 2.6.23 and provided
+L2TPv2 functionality (rfc2661). L2TPv2 is used to tunnel one or more PPP
+sessions over a UDP tunnel.
+
+L2TPv3 (rfc3931) changes the protocol to allow different frame types
+to be passed over an L2TP tunnel by moving the PPP-specific parts of
+the protocol out of the core L2TP packet headers. Each frame type is
+known as a pseudowire type. Ethernet, PPP, HDLC, Frame Relay and ATM
+pseudowires for L2TP are defined in separate RFC standards. Another
+change for L2TPv3 is that it can be carried directly over IP with no
+UDP header (UDP is optional). It is also possible to create static
+unmanaged L2TPv3 tunnels manually without a control protocol
+(userspace daemon) to manage them.
+
+To support L2TPv3, the original pppol2tp driver was split up to
+separate the L2TP and PPP functionality. Existing L2TPv2 userspace
+apps should be unaffected as the original pppol2tp sockets API is
+retained. L2TPv3, however, uses netlink to manage L2TPv3 tunnels and
+sessions.
+
+Design
+======
+
+The L2TP protocol separates control and data frames.  The L2TP kernel
+drivers handle only L2TP data frames; control frames are always
+handled by userspace. L2TP control frames carry messages between L2TP
+clients/servers and are used to setup / teardown tunnels and
+sessions. An L2TP client or server is implemented in userspace.
+
+Each L2TP tunnel is implemented using a UDP or L2TPIP socket; L2TPIP
+provides L2TPv3 IP encapsulation (no UDP) and is implemented using a
+new l2tpip socket family. The tunnel socket is typically created by
+userspace, though for unmanaged L2TPv3 tunnels, the socket can also be
+created by the kernel. Each L2TP session (pseudowire) gets a network
+interface instance. In the case of PPP, these interfaces are created
+indirectly by pppd using a pppol2tp socket. In the case of ethernet,
+the netdevice is created upon a netlink request to create an L2TPv3
+ethernet pseudowire.
+
+For PPP, the PPPoL2TP driver, net/l2tp/l2tp_ppp.c, provides a
+mechanism by which PPP frames carried through an L2TP session are
+passed through the kernel's PPP subsystem. The standard PPP daemon,
+pppd, handles all PPP interaction with the peer. PPP network
+interfaces are created for each local PPP endpoint. The kernel's PPP
+subsystem arranges for PPP control frames to be delivered to pppd,
+while data frames are forwarded as usual.
+
+For ethernet, the L2TPETH driver, net/l2tp/l2tp_eth.c, implements a
+netdevice driver, managing virtual ethernet devices, one per
+pseudowire. These interfaces can be managed using standard Linux tools
+such as "ip" and "ifconfig". If only IP frames are passed over the
+tunnel, the interface can be given an IP addresses of itself and its
+peer. If non-IP frames are to be passed over the tunnel, the interface
+can be added to a bridge using brctl. All L2TP datapath protocol
+functions are handled by the L2TP core driver.
+
+Each tunnel and session within a tunnel is assigned a unique tunnel_id
+and session_id. These ids are carried in the L2TP header of every
+control and data packet. (Actually, in L2TPv3, the tunnel_id isn't
+present in data frames - it is inferred from the IP connection on
+which the packet was received.) The L2TP driver uses the ids to lookup
+internal tunnel and/or session contexts to determine how to handle the
+packet. Zero tunnel / session ids are treated specially - zero ids are
+never assigned to tunnels or sessions in the network. In the driver,
+the tunnel context keeps a reference to the tunnel UDP or L2TPIP
+socket. The session context holds data that lets the driver interface
+to the kernel's network frame type subsystems, i.e. PPP, ethernet.
+
+Userspace Programming
+=====================
+
+For L2TPv2, there are a number of requirements on the userspace L2TP
+daemon in order to use the pppol2tp driver.
+
+1. Use a UDP socket per tunnel.
+
+2. Create a single PPPoL2TP socket per tunnel bound to a special null
+   session id. This is used only for communicating with the driver but
+   must remain open while the tunnel is active. Opening this tunnel
+   management socket causes the driver to mark the tunnel socket as an
+   L2TP UDP encapsulation socket and flags it for use by the
+   referenced tunnel id. This hooks up the UDP receive path via
+   udp_encap_rcv() in net/ipv4/udp.c. PPP data frames are never passed
+   in this special PPPoX socket.
+
+3. Create a PPPoL2TP socket per L2TP session. This is typically done
+   by starting pppd with the pppol2tp plugin and appropriate
+   arguments. A PPPoL2TP tunnel management socket (Step 2) must be
+   created before the first PPPoL2TP session socket is created.
+
+When creating PPPoL2TP sockets, the application provides information
+to the driver about the socket in a socket connect() call. Source and
+destination tunnel and session ids are provided, as well as the file
+descriptor of a UDP socket. See struct pppol2tp_addr in
+include/linux/if_pppol2tp.h. Note that zero tunnel / session ids are
+treated specially. When creating the per-tunnel PPPoL2TP management
+socket in Step 2 above, zero source and destination session ids are
+specified, which tells the driver to prepare the supplied UDP file
+descriptor for use as an L2TP tunnel socket.
+
+Userspace may control behavior of the tunnel or session using
+setsockopt and ioctl on the PPPoX socket. The following socket
+options are supported:-
+
+=========   ===========================================================
+DEBUG       bitmask of debug message categories. See below.
+SENDSEQ     - 0 => don't send packets with sequence numbers
+           - 1 => send packets with sequence numbers
+RECVSEQ     - 0 => receive packet sequence numbers are optional
+           - 1 => drop receive packets without sequence numbers
+LNSMODE     - 0 => act as LAC.
+           - 1 => act as LNS.
+REORDERTO   reorder timeout (in millisecs). If 0, don't try to reorder.
+=========   ===========================================================
+
+Only the DEBUG option is supported by the special tunnel management
+PPPoX socket.
+
+In addition to the standard PPP ioctls, a PPPIOCGL2TPSTATS is provided
+to retrieve tunnel and session statistics from the kernel using the
+PPPoX socket of the appropriate tunnel or session.
+
+For L2TPv3, userspace must use the netlink API defined in
+include/linux/l2tp.h to manage tunnel and session contexts. The
+general procedure to create a new L2TP tunnel with one session is:-
+
+1. Open a GENL socket using L2TP_GENL_NAME for configuring the kernel
+   using netlink.
+
+2. Create a UDP or L2TPIP socket for the tunnel.
+
+3. Create a new L2TP tunnel using a L2TP_CMD_TUNNEL_CREATE
+   request. Set attributes according to desired tunnel parameters,
+   referencing the UDP or L2TPIP socket created in the previous step.
+
+4. Create a new L2TP session in the tunnel using a
+   L2TP_CMD_SESSION_CREATE request.
+
+The tunnel and all of its sessions are closed when the tunnel socket
+is closed. The netlink API may also be used to delete sessions and
+tunnels. Configuration and status info may be set or read using netlink.
+
+The L2TP driver also supports static (unmanaged) L2TPv3 tunnels. These
+are where there is no L2TP control message exchange with the peer to
+setup the tunnel; the tunnel is configured manually at each end of the
+tunnel. There is no need for an L2TP userspace application in this
+case -- the tunnel socket is created by the kernel and configured
+using parameters sent in the L2TP_CMD_TUNNEL_CREATE netlink
+request. The "ip" utility of iproute2 has commands for managing static
+L2TPv3 tunnels; do "ip l2tp help" for more information.
+
+Debugging
+=========
+
+The driver supports a flexible debug scheme where kernel trace
+messages may be optionally enabled per tunnel and per session. Care is
+needed when debugging a live system since the messages are not
+rate-limited and a busy system could be swamped. Userspace uses
+setsockopt on the PPPoX socket to set a debug mask.
+
+The following debug mask bits are available:
+
+================  ==============================
+L2TP_MSG_DEBUG    verbose debug (if compiled in)
+L2TP_MSG_CONTROL  userspace - kernel interface
+L2TP_MSG_SEQ      sequence numbers handling
+L2TP_MSG_DATA     data packets
+================  ==============================
+
+If enabled, files under a l2tp debugfs directory can be used to dump
+kernel state about L2TP tunnels and sessions. To access it, the
+debugfs filesystem must first be mounted::
+
+       # mount -t debugfs debugfs /debug
+
+Files under the l2tp directory can then be accessed::
+
+       # cat /debug/l2tp/tunnels
+
+The debugfs files should not be used by applications to obtain L2TP
+state information because the file format is subject to change. It is
+implemented to provide extra debug information to help diagnose
+problems.) Users should use the netlink API.
+
+/proc/net/pppol2tp is also provided for backwards compatibility with
+the original pppol2tp driver. It lists information about L2TPv2
+tunnels and sessions only. Its use is discouraged.
+
+Unmanaged L2TPv3 Tunnels
+========================
+
+Some commercial L2TP products support unmanaged L2TPv3 ethernet
+tunnels, where there is no L2TP control protocol; tunnels are
+configured at each side manually. New commands are available in
+iproute2's ip utility to support this.
+
+To create an L2TPv3 ethernet pseudowire between local host 192.168.1.1
+and peer 192.168.1.2, using IP addresses 10.5.1.1 and 10.5.1.2 for the
+tunnel endpoints::
+
+       # ip l2tp add tunnel tunnel_id 1 peer_tunnel_id 1 udp_sport 5000 \
+         udp_dport 5000 encap udp local 192.168.1.1 remote 192.168.1.2
+       # ip l2tp add session tunnel_id 1 session_id 1 peer_session_id 1
+       # ip -s -d show dev l2tpeth0
+       # ip addr add 10.5.1.2/32 peer 10.5.1.1/32 dev l2tpeth0
+       # ip li set dev l2tpeth0 up
+
+Choose IP addresses to be the address of a local IP interface and that
+of the remote system. The IP addresses of the l2tpeth0 interface can be
+anything suitable.
+
+Repeat the above at the peer, with ports, tunnel/session ids and IP
+addresses reversed.  The tunnel and session IDs can be any non-zero
+32-bit number, but the values must be reversed at the peer.
+
+========================       ===================
+Host 1                         Host2
+========================       ===================
+udp_sport=5000                 udp_sport=5001
+udp_dport=5001                 udp_dport=5000
+tunnel_id=42                   tunnel_id=45
+peer_tunnel_id=45              peer_tunnel_id=42
+session_id=128                 session_id=5196755
+peer_session_id=5196755        peer_session_id=128
+========================       ===================
+
+When done at both ends of the tunnel, it should be possible to send
+data over the network. e.g.::
+
+       # ping 10.5.1.1
+
+
+Sample Userspace Code
+=====================
+
+1. Create tunnel management PPPoX socket::
+
+       kernel_fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP);
+       if (kernel_fd >= 0) {
+               struct sockaddr_pppol2tp sax;
+               struct sockaddr_in const *peer_addr;
+
+               peer_addr = l2tp_tunnel_get_peer_addr(tunnel);
+               memset(&sax, 0, sizeof(sax));
+               sax.sa_family = AF_PPPOX;
+               sax.sa_protocol = PX_PROTO_OL2TP;
+               sax.pppol2tp.fd = udp_fd;       /* fd of tunnel UDP socket */
+               sax.pppol2tp.addr.sin_addr.s_addr = peer_addr->sin_addr.s_addr;
+               sax.pppol2tp.addr.sin_port = peer_addr->sin_port;
+               sax.pppol2tp.addr.sin_family = AF_INET;
+               sax.pppol2tp.s_tunnel = tunnel_id;
+               sax.pppol2tp.s_session = 0;     /* special case: mgmt socket */
+               sax.pppol2tp.d_tunnel = 0;
+               sax.pppol2tp.d_session = 0;     /* special case: mgmt socket */
+
+               if(connect(kernel_fd, (struct sockaddr *)&sax, sizeof(sax) ) < 0 ) {
+                       perror("connect failed");
+                       result = -errno;
+                       goto err;
+               }
+       }
+
+2. Create session PPPoX data socket::
+
+       struct sockaddr_pppol2tp sax;
+       int fd;
+
+       /* Note, the target socket must be bound already, else it will not be ready */
+       sax.sa_family = AF_PPPOX;
+       sax.sa_protocol = PX_PROTO_OL2TP;
+       sax.pppol2tp.fd = tunnel_fd;
+       sax.pppol2tp.addr.sin_addr.s_addr = addr->sin_addr.s_addr;
+       sax.pppol2tp.addr.sin_port = addr->sin_port;
+       sax.pppol2tp.addr.sin_family = AF_INET;
+       sax.pppol2tp.s_tunnel  = tunnel_id;
+       sax.pppol2tp.s_session = session_id;
+       sax.pppol2tp.d_tunnel  = peer_tunnel_id;
+       sax.pppol2tp.d_session = peer_session_id;
+
+       /* session_fd is the fd of the session's PPPoL2TP socket.
+        * tunnel_fd is the fd of the tunnel UDP socket.
+        */
+       fd = connect(session_fd, (struct sockaddr *)&sax, sizeof(sax));
+       if (fd < 0 )    {
+               return -errno;
+       }
+       return 0;
+
+Internal Implementation
+=======================
+
+The driver keeps a struct l2tp_tunnel context per L2TP tunnel and a
+struct l2tp_session context for each session. The l2tp_tunnel is
+always associated with a UDP or L2TP/IP socket and keeps a list of
+sessions in the tunnel. The l2tp_session context keeps kernel state
+about the session. It has private data which is used for data specific
+to the session type. With L2TPv2, the session always carried PPP
+traffic. With L2TPv3, the session can also carry ethernet frames
+(ethernet pseudowire) or other data types such as ATM, HDLC or Frame
+Relay.
+
+When a tunnel is first opened, the reference count on the socket is
+increased using sock_hold(). This ensures that the kernel socket
+cannot be removed while L2TP's data structures reference it.
+
+Some L2TP sessions also have a socket (PPP pseudowires) while others
+do not (ethernet pseudowires). We can't use the socket reference count
+as the reference count for session contexts. The L2TP implementation
+therefore has its own internal reference counts on the session
+contexts.
+
+To Do
+=====
+
+Add L2TP tunnel switching support. This would route tunneled traffic
+from one L2TP tunnel into another. Specified in
+http://tools.ietf.org/html/draft-ietf-l2tpext-tunnel-switching-08
+
+Add L2TPv3 VLAN pseudowire support.
+
+Add L2TPv3 IP pseudowire support.
+
+Add L2TPv3 ATM pseudowire support.
+
+Miscellaneous
+=============
+
+The L2TP drivers were developed as part of the OpenL2TP project by
+Katalix Systems Ltd. OpenL2TP is a full-featured L2TP client / server,
+designed from the ground up to have the L2TP datapath in the
+kernel. The project also implemented the pppol2tp plugin for pppd
+which allows pppd to use the kernel driver. Details can be found at
+http://www.openl2tp.org.
diff --git a/Documentation/networking/l2tp.txt b/Documentation/networking/l2tp.txt
deleted file mode 100644 (file)
index 9bc271c..0000000
+++ /dev/null
@@ -1,345 +0,0 @@
-This document describes how to use the kernel's L2TP drivers to
-provide L2TP functionality. L2TP is a protocol that tunnels one or
-more sessions over an IP tunnel. It is commonly used for VPNs
-(L2TP/IPSec) and by ISPs to tunnel subscriber PPP sessions over an IP
-network infrastructure. With L2TPv3, it is also useful as a Layer-2
-tunneling infrastructure.
-
-Features
-========
-
-L2TPv2 (PPP over L2TP (UDP tunnels)).
-L2TPv3 ethernet pseudowires.
-L2TPv3 PPP pseudowires.
-L2TPv3 IP encapsulation.
-Netlink sockets for L2TPv3 configuration management.
-
-History
-=======
-
-The original pppol2tp driver was introduced in 2.6.23 and provided
-L2TPv2 functionality (rfc2661). L2TPv2 is used to tunnel one or more PPP
-sessions over a UDP tunnel.
-
-L2TPv3 (rfc3931) changes the protocol to allow different frame types
-to be passed over an L2TP tunnel by moving the PPP-specific parts of
-the protocol out of the core L2TP packet headers. Each frame type is
-known as a pseudowire type. Ethernet, PPP, HDLC, Frame Relay and ATM
-pseudowires for L2TP are defined in separate RFC standards. Another
-change for L2TPv3 is that it can be carried directly over IP with no
-UDP header (UDP is optional). It is also possible to create static
-unmanaged L2TPv3 tunnels manually without a control protocol
-(userspace daemon) to manage them.
-
-To support L2TPv3, the original pppol2tp driver was split up to
-separate the L2TP and PPP functionality. Existing L2TPv2 userspace
-apps should be unaffected as the original pppol2tp sockets API is
-retained. L2TPv3, however, uses netlink to manage L2TPv3 tunnels and
-sessions.
-
-Design
-======
-
-The L2TP protocol separates control and data frames.  The L2TP kernel
-drivers handle only L2TP data frames; control frames are always
-handled by userspace. L2TP control frames carry messages between L2TP
-clients/servers and are used to setup / teardown tunnels and
-sessions. An L2TP client or server is implemented in userspace.
-
-Each L2TP tunnel is implemented using a UDP or L2TPIP socket; L2TPIP
-provides L2TPv3 IP encapsulation (no UDP) and is implemented using a
-new l2tpip socket family. The tunnel socket is typically created by
-userspace, though for unmanaged L2TPv3 tunnels, the socket can also be
-created by the kernel. Each L2TP session (pseudowire) gets a network
-interface instance. In the case of PPP, these interfaces are created
-indirectly by pppd using a pppol2tp socket. In the case of ethernet,
-the netdevice is created upon a netlink request to create an L2TPv3
-ethernet pseudowire.
-
-For PPP, the PPPoL2TP driver, net/l2tp/l2tp_ppp.c, provides a
-mechanism by which PPP frames carried through an L2TP session are
-passed through the kernel's PPP subsystem. The standard PPP daemon,
-pppd, handles all PPP interaction with the peer. PPP network
-interfaces are created for each local PPP endpoint. The kernel's PPP
-subsystem arranges for PPP control frames to be delivered to pppd,
-while data frames are forwarded as usual.
-
-For ethernet, the L2TPETH driver, net/l2tp/l2tp_eth.c, implements a
-netdevice driver, managing virtual ethernet devices, one per
-pseudowire. These interfaces can be managed using standard Linux tools
-such as "ip" and "ifconfig". If only IP frames are passed over the
-tunnel, the interface can be given an IP addresses of itself and its
-peer. If non-IP frames are to be passed over the tunnel, the interface
-can be added to a bridge using brctl. All L2TP datapath protocol
-functions are handled by the L2TP core driver.
-
-Each tunnel and session within a tunnel is assigned a unique tunnel_id
-and session_id. These ids are carried in the L2TP header of every
-control and data packet. (Actually, in L2TPv3, the tunnel_id isn't
-present in data frames - it is inferred from the IP connection on
-which the packet was received.) The L2TP driver uses the ids to lookup
-internal tunnel and/or session contexts to determine how to handle the
-packet. Zero tunnel / session ids are treated specially - zero ids are
-never assigned to tunnels or sessions in the network. In the driver,
-the tunnel context keeps a reference to the tunnel UDP or L2TPIP
-socket. The session context holds data that lets the driver interface
-to the kernel's network frame type subsystems, i.e. PPP, ethernet.
-
-Userspace Programming
-=====================
-
-For L2TPv2, there are a number of requirements on the userspace L2TP
-daemon in order to use the pppol2tp driver.
-
-1. Use a UDP socket per tunnel.
-
-2. Create a single PPPoL2TP socket per tunnel bound to a special null
-   session id. This is used only for communicating with the driver but
-   must remain open while the tunnel is active. Opening this tunnel
-   management socket causes the driver to mark the tunnel socket as an
-   L2TP UDP encapsulation socket and flags it for use by the
-   referenced tunnel id. This hooks up the UDP receive path via
-   udp_encap_rcv() in net/ipv4/udp.c. PPP data frames are never passed
-   in this special PPPoX socket.
-
-3. Create a PPPoL2TP socket per L2TP session. This is typically done
-   by starting pppd with the pppol2tp plugin and appropriate
-   arguments. A PPPoL2TP tunnel management socket (Step 2) must be
-   created before the first PPPoL2TP session socket is created.
-
-When creating PPPoL2TP sockets, the application provides information
-to the driver about the socket in a socket connect() call. Source and
-destination tunnel and session ids are provided, as well as the file
-descriptor of a UDP socket. See struct pppol2tp_addr in
-include/linux/if_pppol2tp.h. Note that zero tunnel / session ids are
-treated specially. When creating the per-tunnel PPPoL2TP management
-socket in Step 2 above, zero source and destination session ids are
-specified, which tells the driver to prepare the supplied UDP file
-descriptor for use as an L2TP tunnel socket.
-
-Userspace may control behavior of the tunnel or session using
-setsockopt and ioctl on the PPPoX socket. The following socket
-options are supported:-
-
-DEBUG     - bitmask of debug message categories. See below.
-SENDSEQ   - 0 => don't send packets with sequence numbers
-            1 => send packets with sequence numbers
-RECVSEQ   - 0 => receive packet sequence numbers are optional
-            1 => drop receive packets without sequence numbers
-LNSMODE   - 0 => act as LAC.
-            1 => act as LNS.
-REORDERTO - reorder timeout (in millisecs). If 0, don't try to reorder.
-
-Only the DEBUG option is supported by the special tunnel management
-PPPoX socket.
-
-In addition to the standard PPP ioctls, a PPPIOCGL2TPSTATS is provided
-to retrieve tunnel and session statistics from the kernel using the
-PPPoX socket of the appropriate tunnel or session.
-
-For L2TPv3, userspace must use the netlink API defined in
-include/linux/l2tp.h to manage tunnel and session contexts. The
-general procedure to create a new L2TP tunnel with one session is:-
-
-1. Open a GENL socket using L2TP_GENL_NAME for configuring the kernel
-   using netlink.
-
-2. Create a UDP or L2TPIP socket for the tunnel.
-
-3. Create a new L2TP tunnel using a L2TP_CMD_TUNNEL_CREATE
-   request. Set attributes according to desired tunnel parameters,
-   referencing the UDP or L2TPIP socket created in the previous step.
-
-4. Create a new L2TP session in the tunnel using a
-   L2TP_CMD_SESSION_CREATE request.
-
-The tunnel and all of its sessions are closed when the tunnel socket
-is closed. The netlink API may also be used to delete sessions and
-tunnels. Configuration and status info may be set or read using netlink.
-
-The L2TP driver also supports static (unmanaged) L2TPv3 tunnels. These
-are where there is no L2TP control message exchange with the peer to
-setup the tunnel; the tunnel is configured manually at each end of the
-tunnel. There is no need for an L2TP userspace application in this
-case -- the tunnel socket is created by the kernel and configured
-using parameters sent in the L2TP_CMD_TUNNEL_CREATE netlink
-request. The "ip" utility of iproute2 has commands for managing static
-L2TPv3 tunnels; do "ip l2tp help" for more information.
-
-Debugging
-=========
-
-The driver supports a flexible debug scheme where kernel trace
-messages may be optionally enabled per tunnel and per session. Care is
-needed when debugging a live system since the messages are not
-rate-limited and a busy system could be swamped. Userspace uses
-setsockopt on the PPPoX socket to set a debug mask.
-
-The following debug mask bits are available:
-
-L2TP_MSG_DEBUG    verbose debug (if compiled in)
-L2TP_MSG_CONTROL  userspace - kernel interface
-L2TP_MSG_SEQ      sequence numbers handling
-L2TP_MSG_DATA     data packets
-
-If enabled, files under a l2tp debugfs directory can be used to dump
-kernel state about L2TP tunnels and sessions. To access it, the
-debugfs filesystem must first be mounted.
-
-# mount -t debugfs debugfs /debug
-
-Files under the l2tp directory can then be accessed.
-
-# cat /debug/l2tp/tunnels
-
-The debugfs files should not be used by applications to obtain L2TP
-state information because the file format is subject to change. It is
-implemented to provide extra debug information to help diagnose
-problems.) Users should use the netlink API.
-
-/proc/net/pppol2tp is also provided for backwards compatibility with
-the original pppol2tp driver. It lists information about L2TPv2
-tunnels and sessions only. Its use is discouraged.
-
-Unmanaged L2TPv3 Tunnels
-========================
-
-Some commercial L2TP products support unmanaged L2TPv3 ethernet
-tunnels, where there is no L2TP control protocol; tunnels are
-configured at each side manually. New commands are available in
-iproute2's ip utility to support this.
-
-To create an L2TPv3 ethernet pseudowire between local host 192.168.1.1
-and peer 192.168.1.2, using IP addresses 10.5.1.1 and 10.5.1.2 for the
-tunnel endpoints:-
-
-# ip l2tp add tunnel tunnel_id 1 peer_tunnel_id 1 udp_sport 5000 \
-  udp_dport 5000 encap udp local 192.168.1.1 remote 192.168.1.2
-# ip l2tp add session tunnel_id 1 session_id 1 peer_session_id 1
-# ip -s -d show dev l2tpeth0
-# ip addr add 10.5.1.2/32 peer 10.5.1.1/32 dev l2tpeth0
-# ip li set dev l2tpeth0 up
-
-Choose IP addresses to be the address of a local IP interface and that
-of the remote system. The IP addresses of the l2tpeth0 interface can be
-anything suitable.
-
-Repeat the above at the peer, with ports, tunnel/session ids and IP
-addresses reversed.  The tunnel and session IDs can be any non-zero
-32-bit number, but the values must be reversed at the peer.
-
-Host 1                         Host2
-udp_sport=5000                 udp_sport=5001
-udp_dport=5001                 udp_dport=5000
-tunnel_id=42                   tunnel_id=45
-peer_tunnel_id=45              peer_tunnel_id=42
-session_id=128                 session_id=5196755
-peer_session_id=5196755        peer_session_id=128
-
-When done at both ends of the tunnel, it should be possible to send
-data over the network. e.g.
-
-# ping 10.5.1.1
-
-
-Sample Userspace Code
-=====================
-
-1. Create tunnel management PPPoX socket
-
-        kernel_fd = socket(AF_PPPOX, SOCK_DGRAM, PX_PROTO_OL2TP);
-        if (kernel_fd >= 0) {
-                struct sockaddr_pppol2tp sax;
-                struct sockaddr_in const *peer_addr;
-
-                peer_addr = l2tp_tunnel_get_peer_addr(tunnel);
-                memset(&sax, 0, sizeof(sax));
-                sax.sa_family = AF_PPPOX;
-                sax.sa_protocol = PX_PROTO_OL2TP;
-                sax.pppol2tp.fd = udp_fd;       /* fd of tunnel UDP socket */
-                sax.pppol2tp.addr.sin_addr.s_addr = peer_addr->sin_addr.s_addr;
-                sax.pppol2tp.addr.sin_port = peer_addr->sin_port;
-                sax.pppol2tp.addr.sin_family = AF_INET;
-                sax.pppol2tp.s_tunnel = tunnel_id;
-                sax.pppol2tp.s_session = 0;     /* special case: mgmt socket */
-                sax.pppol2tp.d_tunnel = 0;
-                sax.pppol2tp.d_session = 0;     /* special case: mgmt socket */
-
-                if(connect(kernel_fd, (struct sockaddr *)&sax, sizeof(sax) ) < 0 ) {
-                        perror("connect failed");
-                        result = -errno;
-                        goto err;
-                }
-        }
-
-2. Create session PPPoX data socket
-
-        struct sockaddr_pppol2tp sax;
-        int fd;
-
-        /* Note, the target socket must be bound already, else it will not be ready */
-        sax.sa_family = AF_PPPOX;
-        sax.sa_protocol = PX_PROTO_OL2TP;
-        sax.pppol2tp.fd = tunnel_fd;
-        sax.pppol2tp.addr.sin_addr.s_addr = addr->sin_addr.s_addr;
-        sax.pppol2tp.addr.sin_port = addr->sin_port;
-        sax.pppol2tp.addr.sin_family = AF_INET;
-        sax.pppol2tp.s_tunnel  = tunnel_id;
-        sax.pppol2tp.s_session = session_id;
-        sax.pppol2tp.d_tunnel  = peer_tunnel_id;
-        sax.pppol2tp.d_session = peer_session_id;
-
-        /* session_fd is the fd of the session's PPPoL2TP socket.
-         * tunnel_fd is the fd of the tunnel UDP socket.
-         */
-        fd = connect(session_fd, (struct sockaddr *)&sax, sizeof(sax));
-        if (fd < 0 )    {
-                return -errno;
-        }
-        return 0;
-
-Internal Implementation
-=======================
-
-The driver keeps a struct l2tp_tunnel context per L2TP tunnel and a
-struct l2tp_session context for each session. The l2tp_tunnel is
-always associated with a UDP or L2TP/IP socket and keeps a list of
-sessions in the tunnel. The l2tp_session context keeps kernel state
-about the session. It has private data which is used for data specific
-to the session type. With L2TPv2, the session always carried PPP
-traffic. With L2TPv3, the session can also carry ethernet frames
-(ethernet pseudowire) or other data types such as ATM, HDLC or Frame
-Relay.
-
-When a tunnel is first opened, the reference count on the socket is
-increased using sock_hold(). This ensures that the kernel socket
-cannot be removed while L2TP's data structures reference it.
-
-Some L2TP sessions also have a socket (PPP pseudowires) while others
-do not (ethernet pseudowires). We can't use the socket reference count
-as the reference count for session contexts. The L2TP implementation
-therefore has its own internal reference counts on the session
-contexts.
-
-To Do
-=====
-
-Add L2TP tunnel switching support. This would route tunneled traffic
-from one L2TP tunnel into another. Specified in
-http://tools.ietf.org/html/draft-ietf-l2tpext-tunnel-switching-08
-
-Add L2TPv3 VLAN pseudowire support.
-
-Add L2TPv3 IP pseudowire support.
-
-Add L2TPv3 ATM pseudowire support.
-
-Miscellaneous
-=============
-
-The L2TP drivers were developed as part of the OpenL2TP project by
-Katalix Systems Ltd. OpenL2TP is a full-featured L2TP client / server,
-designed from the ground up to have the L2TP datapath in the
-kernel. The project also implemented the pppol2tp plugin for pppd
-which allows pppd to use the kernel driver. Details can be found at
-http://www.openl2tp.org.
diff --git a/Documentation/networking/lapb-module.rst b/Documentation/networking/lapb-module.rst
new file mode 100644 (file)
index 0000000..ff586bc
--- /dev/null
@@ -0,0 +1,305 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============================
+The Linux LAPB Module Interface
+===============================
+
+Version 1.3
+
+Jonathan Naylor 29.12.96
+
+Changed (Henner Eisen, 2000-10-29): int return value for data_indication()
+
+The LAPB module will be a separately compiled module for use by any parts of
+the Linux operating system that require a LAPB service. This document
+defines the interfaces to, and the services provided by this module. The
+term module in this context does not imply that the LAPB module is a
+separately loadable module, although it may be. The term module is used in
+its more standard meaning.
+
+The interface to the LAPB module consists of functions to the module,
+callbacks from the module to indicate important state changes, and
+structures for getting and setting information about the module.
+
+Structures
+----------
+
+Probably the most important structure is the skbuff structure for holding
+received and transmitted data, however it is beyond the scope of this
+document.
+
+The two LAPB specific structures are the LAPB initialisation structure and
+the LAPB parameter structure. These will be defined in a standard header
+file, <linux/lapb.h>. The header file <net/lapb.h> is internal to the LAPB
+module and is not for use.
+
+LAPB Initialisation Structure
+-----------------------------
+
+This structure is used only once, in the call to lapb_register (see below).
+It contains information about the device driver that requires the services
+of the LAPB module::
+
+       struct lapb_register_struct {
+               void (*connect_confirmation)(int token, int reason);
+               void (*connect_indication)(int token, int reason);
+               void (*disconnect_confirmation)(int token, int reason);
+               void (*disconnect_indication)(int token, int reason);
+               int  (*data_indication)(int token, struct sk_buff *skb);
+               void (*data_transmit)(int token, struct sk_buff *skb);
+       };
+
+Each member of this structure corresponds to a function in the device driver
+that is called when a particular event in the LAPB module occurs. These will
+be described in detail below. If a callback is not required (!!) then a NULL
+may be substituted.
+
+
+LAPB Parameter Structure
+------------------------
+
+This structure is used with the lapb_getparms and lapb_setparms functions
+(see below). They are used to allow the device driver to get and set the
+operational parameters of the LAPB implementation for a given connection::
+
+       struct lapb_parms_struct {
+               unsigned int t1;
+               unsigned int t1timer;
+               unsigned int t2;
+               unsigned int t2timer;
+               unsigned int n2;
+               unsigned int n2count;
+               unsigned int window;
+               unsigned int state;
+               unsigned int mode;
+       };
+
+T1 and T2 are protocol timing parameters and are given in units of 100ms. N2
+is the maximum number of tries on the link before it is declared a failure.
+The window size is the maximum number of outstanding data packets allowed to
+be unacknowledged by the remote end, the value of the window is between 1
+and 7 for a standard LAPB link, and between 1 and 127 for an extended LAPB
+link.
+
+The mode variable is a bit field used for setting (at present) three values.
+The bit fields have the following meanings:
+
+======  =================================================
+Bit    Meaning
+======  =================================================
+0      LAPB operation (0=LAPB_STANDARD 1=LAPB_EXTENDED).
+1      [SM]LP operation (0=LAPB_SLP 1=LAPB=MLP).
+2      DTE/DCE operation (0=LAPB_DTE 1=LAPB_DCE)
+3-31   Reserved, must be 0.
+======  =================================================
+
+Extended LAPB operation indicates the use of extended sequence numbers and
+consequently larger window sizes, the default is standard LAPB operation.
+MLP operation is the same as SLP operation except that the addresses used by
+LAPB are different to indicate the mode of operation, the default is Single
+Link Procedure. The difference between DCE and DTE operation is (i) the
+addresses used for commands and responses, and (ii) when the DCE is not
+connected, it sends DM without polls set, every T1. The upper case constant
+names will be defined in the public LAPB header file.
+
+
+Functions
+---------
+
+The LAPB module provides a number of function entry points.
+
+::
+
+    int lapb_register(void *token, struct lapb_register_struct);
+
+This must be called before the LAPB module may be used. If the call is
+successful then LAPB_OK is returned. The token must be a unique identifier
+generated by the device driver to allow for the unique identification of the
+instance of the LAPB link. It is returned by the LAPB module in all of the
+callbacks, and is used by the device driver in all calls to the LAPB module.
+For multiple LAPB links in a single device driver, multiple calls to
+lapb_register must be made. The format of the lapb_register_struct is given
+above. The return values are:
+
+=============          =============================
+LAPB_OK                        LAPB registered successfully.
+LAPB_BADTOKEN          Token is already registered.
+LAPB_NOMEM             Out of memory
+=============          =============================
+
+::
+
+    int lapb_unregister(void *token);
+
+This releases all the resources associated with a LAPB link. Any current
+LAPB link will be abandoned without further messages being passed. After
+this call, the value of token is no longer valid for any calls to the LAPB
+function. The valid return values are:
+
+=============          ===============================
+LAPB_OK                        LAPB unregistered successfully.
+LAPB_BADTOKEN          Invalid/unknown LAPB token.
+=============          ===============================
+
+::
+
+    int lapb_getparms(void *token, struct lapb_parms_struct *parms);
+
+This allows the device driver to get the values of the current LAPB
+variables, the lapb_parms_struct is described above. The valid return values
+are:
+
+=============          =============================
+LAPB_OK                        LAPB getparms was successful.
+LAPB_BADTOKEN          Invalid/unknown LAPB token.
+=============          =============================
+
+::
+
+    int lapb_setparms(void *token, struct lapb_parms_struct *parms);
+
+This allows the device driver to set the values of the current LAPB
+variables, the lapb_parms_struct is described above. The values of t1timer,
+t2timer and n2count are ignored, likewise changing the mode bits when
+connected will be ignored. An error implies that none of the values have
+been changed. The valid return values are:
+
+=============          =================================================
+LAPB_OK                        LAPB getparms was successful.
+LAPB_BADTOKEN          Invalid/unknown LAPB token.
+LAPB_INVALUE           One of the values was out of its allowable range.
+=============          =================================================
+
+::
+
+    int lapb_connect_request(void *token);
+
+Initiate a connect using the current parameter settings. The valid return
+values are:
+
+==============         =================================
+LAPB_OK                        LAPB is starting to connect.
+LAPB_BADTOKEN          Invalid/unknown LAPB token.
+LAPB_CONNECTED         LAPB module is already connected.
+==============         =================================
+
+::
+
+    int lapb_disconnect_request(void *token);
+
+Initiate a disconnect. The valid return values are:
+
+=================      ===============================
+LAPB_OK                        LAPB is starting to disconnect.
+LAPB_BADTOKEN          Invalid/unknown LAPB token.
+LAPB_NOTCONNECTED      LAPB module is not connected.
+=================      ===============================
+
+::
+
+    int lapb_data_request(void *token, struct sk_buff *skb);
+
+Queue data with the LAPB module for transmitting over the link. If the call
+is successful then the skbuff is owned by the LAPB module and may not be
+used by the device driver again. The valid return values are:
+
+=================      =============================
+LAPB_OK                        LAPB has accepted the data.
+LAPB_BADTOKEN          Invalid/unknown LAPB token.
+LAPB_NOTCONNECTED      LAPB module is not connected.
+=================      =============================
+
+::
+
+    int lapb_data_received(void *token, struct sk_buff *skb);
+
+Queue data with the LAPB module which has been received from the device. It
+is expected that the data passed to the LAPB module has skb->data pointing
+to the beginning of the LAPB data. If the call is successful then the skbuff
+is owned by the LAPB module and may not be used by the device driver again.
+The valid return values are:
+
+=============          ===========================
+LAPB_OK                        LAPB has accepted the data.
+LAPB_BADTOKEN          Invalid/unknown LAPB token.
+=============          ===========================
+
+Callbacks
+---------
+
+These callbacks are functions provided by the device driver for the LAPB
+module to call when an event occurs. They are registered with the LAPB
+module with lapb_register (see above) in the structure lapb_register_struct
+(see above).
+
+::
+
+    void (*connect_confirmation)(void *token, int reason);
+
+This is called by the LAPB module when a connection is established after
+being requested by a call to lapb_connect_request (see above). The reason is
+always LAPB_OK.
+
+::
+
+    void (*connect_indication)(void *token, int reason);
+
+This is called by the LAPB module when the link is established by the remote
+system. The value of reason is always LAPB_OK.
+
+::
+
+    void (*disconnect_confirmation)(void *token, int reason);
+
+This is called by the LAPB module when an event occurs after the device
+driver has called lapb_disconnect_request (see above). The reason indicates
+what has happened. In all cases the LAPB link can be regarded as being
+terminated. The values for reason are:
+
+=================      ====================================================
+LAPB_OK                        The LAPB link was terminated normally.
+LAPB_NOTCONNECTED      The remote system was not connected.
+LAPB_TIMEDOUT          No response was received in N2 tries from the remote
+                       system.
+=================      ====================================================
+
+::
+
+    void (*disconnect_indication)(void *token, int reason);
+
+This is called by the LAPB module when the link is terminated by the remote
+system or another event has occurred to terminate the link. This may be
+returned in response to a lapb_connect_request (see above) if the remote
+system refused the request. The values for reason are:
+
+=================      ====================================================
+LAPB_OK                        The LAPB link was terminated normally by the remote
+                       system.
+LAPB_REFUSED           The remote system refused the connect request.
+LAPB_NOTCONNECTED      The remote system was not connected.
+LAPB_TIMEDOUT          No response was received in N2 tries from the remote
+                       system.
+=================      ====================================================
+
+::
+
+    int (*data_indication)(void *token, struct sk_buff *skb);
+
+This is called by the LAPB module when data has been received from the
+remote system that should be passed onto the next layer in the protocol
+stack. The skbuff becomes the property of the device driver and the LAPB
+module will not perform any more actions on it. The skb->data pointer will
+be pointing to the first byte of data after the LAPB header.
+
+This method should return NET_RX_DROP (as defined in the header
+file include/linux/netdevice.h) if and only if the frame was dropped
+before it could be delivered to the upper layer.
+
+::
+
+    void (*data_transmit)(void *token, struct sk_buff *skb);
+
+This is called by the LAPB module when data is to be transmitted to the
+remote system by the device driver. The skbuff becomes the property of the
+device driver and the LAPB module will not perform any more actions on it.
+The skb->data pointer will be pointing to the first byte of the LAPB header.
diff --git a/Documentation/networking/lapb-module.txt b/Documentation/networking/lapb-module.txt
deleted file mode 100644 (file)
index d4fc8f2..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-               The Linux LAPB Module Interface 1.3
-
-                     Jonathan Naylor 29.12.96
-
-Changed (Henner Eisen, 2000-10-29): int return value for data_indication() 
-
-The LAPB module will be a separately compiled module for use by any parts of
-the Linux operating system that require a LAPB service. This document
-defines the interfaces to, and the services provided by this module. The
-term module in this context does not imply that the LAPB module is a
-separately loadable module, although it may be. The term module is used in
-its more standard meaning.
-
-The interface to the LAPB module consists of functions to the module,
-callbacks from the module to indicate important state changes, and
-structures for getting and setting information about the module.
-
-Structures
-----------
-
-Probably the most important structure is the skbuff structure for holding
-received and transmitted data, however it is beyond the scope of this
-document.
-
-The two LAPB specific structures are the LAPB initialisation structure and
-the LAPB parameter structure. These will be defined in a standard header
-file, <linux/lapb.h>. The header file <net/lapb.h> is internal to the LAPB
-module and is not for use.
-
-LAPB Initialisation Structure
------------------------------
-
-This structure is used only once, in the call to lapb_register (see below).
-It contains information about the device driver that requires the services
-of the LAPB module.
-
-struct lapb_register_struct {
-       void (*connect_confirmation)(int token, int reason);
-       void (*connect_indication)(int token, int reason);
-       void (*disconnect_confirmation)(int token, int reason);
-       void (*disconnect_indication)(int token, int reason);
-       int  (*data_indication)(int token, struct sk_buff *skb);
-       void (*data_transmit)(int token, struct sk_buff *skb);
-};
-
-Each member of this structure corresponds to a function in the device driver
-that is called when a particular event in the LAPB module occurs. These will
-be described in detail below. If a callback is not required (!!) then a NULL
-may be substituted.
-
-
-LAPB Parameter Structure
-------------------------
-
-This structure is used with the lapb_getparms and lapb_setparms functions
-(see below). They are used to allow the device driver to get and set the
-operational parameters of the LAPB implementation for a given connection.
-
-struct lapb_parms_struct {
-       unsigned int t1;
-       unsigned int t1timer;
-       unsigned int t2;
-       unsigned int t2timer;
-       unsigned int n2;
-       unsigned int n2count;
-       unsigned int window;
-       unsigned int state;
-       unsigned int mode;
-};
-
-T1 and T2 are protocol timing parameters and are given in units of 100ms. N2
-is the maximum number of tries on the link before it is declared a failure.
-The window size is the maximum number of outstanding data packets allowed to
-be unacknowledged by the remote end, the value of the window is between 1
-and 7 for a standard LAPB link, and between 1 and 127 for an extended LAPB
-link.
-
-The mode variable is a bit field used for setting (at present) three values.
-The bit fields have the following meanings:
-
-Bit    Meaning
-0      LAPB operation (0=LAPB_STANDARD 1=LAPB_EXTENDED).
-1      [SM]LP operation (0=LAPB_SLP 1=LAPB=MLP).
-2      DTE/DCE operation (0=LAPB_DTE 1=LAPB_DCE)
-3-31   Reserved, must be 0.
-
-Extended LAPB operation indicates the use of extended sequence numbers and
-consequently larger window sizes, the default is standard LAPB operation.
-MLP operation is the same as SLP operation except that the addresses used by
-LAPB are different to indicate the mode of operation, the default is Single
-Link Procedure. The difference between DCE and DTE operation is (i) the
-addresses used for commands and responses, and (ii) when the DCE is not
-connected, it sends DM without polls set, every T1. The upper case constant
-names will be defined in the public LAPB header file.
-
-
-Functions
----------
-
-The LAPB module provides a number of function entry points.
-
-
-int lapb_register(void *token, struct lapb_register_struct);
-
-This must be called before the LAPB module may be used. If the call is
-successful then LAPB_OK is returned. The token must be a unique identifier
-generated by the device driver to allow for the unique identification of the
-instance of the LAPB link. It is returned by the LAPB module in all of the
-callbacks, and is used by the device driver in all calls to the LAPB module.
-For multiple LAPB links in a single device driver, multiple calls to
-lapb_register must be made. The format of the lapb_register_struct is given
-above. The return values are:
-
-LAPB_OK                        LAPB registered successfully.
-LAPB_BADTOKEN          Token is already registered.
-LAPB_NOMEM             Out of memory
-
-
-int lapb_unregister(void *token);
-
-This releases all the resources associated with a LAPB link. Any current
-LAPB link will be abandoned without further messages being passed. After
-this call, the value of token is no longer valid for any calls to the LAPB
-function. The valid return values are:
-
-LAPB_OK                        LAPB unregistered successfully.
-LAPB_BADTOKEN          Invalid/unknown LAPB token.
-
-
-int lapb_getparms(void *token, struct lapb_parms_struct *parms);
-
-This allows the device driver to get the values of the current LAPB
-variables, the lapb_parms_struct is described above. The valid return values
-are:
-
-LAPB_OK                        LAPB getparms was successful.
-LAPB_BADTOKEN          Invalid/unknown LAPB token.
-
-
-int lapb_setparms(void *token, struct lapb_parms_struct *parms);
-
-This allows the device driver to set the values of the current LAPB
-variables, the lapb_parms_struct is described above. The values of t1timer,
-t2timer and n2count are ignored, likewise changing the mode bits when
-connected will be ignored. An error implies that none of the values have
-been changed. The valid return values are:
-
-LAPB_OK                        LAPB getparms was successful.
-LAPB_BADTOKEN          Invalid/unknown LAPB token.
-LAPB_INVALUE           One of the values was out of its allowable range.
-
-
-int lapb_connect_request(void *token);
-
-Initiate a connect using the current parameter settings. The valid return
-values are:
-
-LAPB_OK                        LAPB is starting to connect.
-LAPB_BADTOKEN          Invalid/unknown LAPB token.
-LAPB_CONNECTED         LAPB module is already connected.
-
-
-int lapb_disconnect_request(void *token);
-
-Initiate a disconnect. The valid return values are:
-
-LAPB_OK                        LAPB is starting to disconnect.
-LAPB_BADTOKEN          Invalid/unknown LAPB token.
-LAPB_NOTCONNECTED      LAPB module is not connected.
-
-
-int lapb_data_request(void *token, struct sk_buff *skb);
-
-Queue data with the LAPB module for transmitting over the link. If the call
-is successful then the skbuff is owned by the LAPB module and may not be
-used by the device driver again. The valid return values are:
-
-LAPB_OK                        LAPB has accepted the data.
-LAPB_BADTOKEN          Invalid/unknown LAPB token.
-LAPB_NOTCONNECTED      LAPB module is not connected.
-
-
-int lapb_data_received(void *token, struct sk_buff *skb);
-
-Queue data with the LAPB module which has been received from the device. It
-is expected that the data passed to the LAPB module has skb->data pointing
-to the beginning of the LAPB data. If the call is successful then the skbuff
-is owned by the LAPB module and may not be used by the device driver again.
-The valid return values are:
-
-LAPB_OK                        LAPB has accepted the data.
-LAPB_BADTOKEN          Invalid/unknown LAPB token.
-
-
-Callbacks
----------
-
-These callbacks are functions provided by the device driver for the LAPB
-module to call when an event occurs. They are registered with the LAPB
-module with lapb_register (see above) in the structure lapb_register_struct
-(see above).
-
-
-void (*connect_confirmation)(void *token, int reason);
-
-This is called by the LAPB module when a connection is established after
-being requested by a call to lapb_connect_request (see above). The reason is
-always LAPB_OK.
-
-
-void (*connect_indication)(void *token, int reason);
-
-This is called by the LAPB module when the link is established by the remote
-system. The value of reason is always LAPB_OK.
-
-
-void (*disconnect_confirmation)(void *token, int reason);
-
-This is called by the LAPB module when an event occurs after the device
-driver has called lapb_disconnect_request (see above). The reason indicates
-what has happened. In all cases the LAPB link can be regarded as being
-terminated. The values for reason are:
-
-LAPB_OK                        The LAPB link was terminated normally.
-LAPB_NOTCONNECTED      The remote system was not connected.
-LAPB_TIMEDOUT          No response was received in N2 tries from the remote
-                       system.
-
-
-void (*disconnect_indication)(void *token, int reason);
-
-This is called by the LAPB module when the link is terminated by the remote
-system or another event has occurred to terminate the link. This may be
-returned in response to a lapb_connect_request (see above) if the remote
-system refused the request. The values for reason are:
-
-LAPB_OK                        The LAPB link was terminated normally by the remote
-                       system.
-LAPB_REFUSED           The remote system refused the connect request.
-LAPB_NOTCONNECTED      The remote system was not connected.
-LAPB_TIMEDOUT          No response was received in N2 tries from the remote
-                       system.
-
-
-int (*data_indication)(void *token, struct sk_buff *skb);
-
-This is called by the LAPB module when data has been received from the
-remote system that should be passed onto the next layer in the protocol
-stack. The skbuff becomes the property of the device driver and the LAPB
-module will not perform any more actions on it. The skb->data pointer will
-be pointing to the first byte of data after the LAPB header.
-
-This method should return NET_RX_DROP (as defined in the header
-file include/linux/netdevice.h) if and only if the frame was dropped
-before it could be delivered to the upper layer.
-
-
-void (*data_transmit)(void *token, struct sk_buff *skb);
-
-This is called by the LAPB module when data is to be transmitted to the
-remote system by the device driver. The skbuff becomes the property of the
-device driver and the LAPB module will not perform any more actions on it.
-The skb->data pointer will be pointing to the first byte of the LAPB header.
diff --git a/Documentation/networking/ltpc.rst b/Documentation/networking/ltpc.rst
new file mode 100644 (file)
index 0000000..0ad197f
--- /dev/null
@@ -0,0 +1,144 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========
+LTPC Driver
+===========
+
+This is the ALPHA version of the ltpc driver.
+
+In order to use it, you will need at least version 1.3.3 of the
+netatalk package, and the Apple or Farallon LocalTalk PC card.
+There are a number of different LocalTalk cards for the PC; this
+driver applies only to the one with the 65c02 processor chip on it.
+
+To include it in the kernel, select the CONFIG_LTPC switch in the
+configuration dialog.  You can also compile it as a module.
+
+While the driver will attempt to autoprobe the I/O port address, IRQ
+line, and DMA channel of the card, this does not always work.  For
+this reason, you should be prepared to supply these parameters
+yourself.  (see "Card Configuration" below for how to determine or
+change the settings on your card)
+
+When the driver is compiled into the kernel, you can add a line such
+as the following to your /etc/lilo.conf::
+
+ append="ltpc=0x240,9,1"
+
+where the parameters (in order) are the port address, IRQ, and DMA
+channel.  The second and third values can be omitted, in which case
+the driver will try to determine them itself.
+
+If you load the driver as a module, you can pass the parameters "io=",
+"irq=", and "dma=" on the command line with insmod or modprobe, or add
+them as options in a configuration file in /etc/modprobe.d/ directory::
+
+ alias lt0 ltpc # autoload the module when the interface is configured
+ options ltpc io=0x240 irq=9 dma=1
+
+Before starting up the netatalk demons (perhaps in rc.local), you
+need to add a line such as::
+
+ /sbin/ifconfig lt0 127.0.0.42
+
+The address is unimportant - however, the card needs to be configured
+with ifconfig so that Netatalk can find it.
+
+The appropriate netatalk configuration depends on whether you are
+attached to a network that includes AppleTalk routers or not.  If,
+like me, you are simply connecting to your home Macintoshes and
+printers, you need to set up netatalk to "seed".  The way I do this
+is to have the lines::
+
+ dummy -seed -phase 2 -net 2000 -addr 2000.26 -zone "1033"
+ lt0 -seed -phase 1 -net 1033 -addr 1033.27 -zone "1033"
+
+in my atalkd.conf.  What is going on here is that I need to fool
+netatalk into thinking that there are two AppleTalk interfaces
+present; otherwise, it refuses to seed.  This is a hack, and a more
+permanent solution would be to alter the netatalk code.  Also, make
+sure you have the correct name for the dummy interface - If it's
+compiled as a module, you will need to refer to it as "dummy0" or some
+such.
+
+If you are attached to an extended AppleTalk network, with routers on
+it, then you don't need to fool around with this -- the appropriate
+line in atalkd.conf is::
+
+ lt0 -phase 1
+
+
+Card Configuration
+==================
+
+The interrupts and so forth are configured via the dipswitch on the
+board.  Set the switches so as not to conflict with other hardware.
+
+       Interrupts -- set at most one.  If none are set, the driver uses
+       polled mode.  Because the card was developed in the XT era, the
+       original documentation refers to IRQ2.  Since you'll be running
+       this on an AT (or later) class machine, that really means IRQ9.
+
+       ===     ===========================================================
+       SW1     IRQ 4
+       SW2     IRQ 3
+       SW3     IRQ 9 (2 in original card documentation only applies to XT)
+       ===     ===========================================================
+
+
+       DMA -- choose DMA 1 or 3, and set both corresponding switches.
+
+       ===     =====
+       SW4     DMA 3
+       SW5     DMA 1
+       SW6     DMA 3
+       SW7     DMA 1
+       ===     =====
+
+
+       I/O address -- choose one.
+
+       ===     =========
+       SW8     220 / 240
+       ===     =========
+
+
+IP
+==
+
+Yes, it is possible to do IP over LocalTalk.  However, you can't just
+treat the LocalTalk device like an ordinary Ethernet device, even if
+that's what it looks like to Netatalk.
+
+Instead, you follow the same procedure as for doing IP in EtherTalk.
+See Documentation/networking/ipddp.rst for more information about the
+kernel driver and userspace tools needed.
+
+
+Bugs
+====
+
+IRQ autoprobing often doesn't work on a cold boot.  To get around
+this, either compile the driver as a module, or pass the parameters
+for the card to the kernel as described above.
+
+Also, as usual, autoprobing is not recommended when you use the driver
+as a module. (though it usually works at boot time, at least)
+
+Polled mode is *really* slow sometimes, but this seems to depend on
+the configuration of the network.
+
+It may theoretically be possible to use two LTPC cards in the same
+machine, but this is unsupported, so if you really want to do this,
+you'll probably have to hack the initialization code a bit.
+
+
+Thanks
+======
+
+Thanks to Alan Cox for helpful discussions early on in this
+work, and to Denis Hainsworth for doing the bleeding-edge testing.
+
+Bradford Johnson <bradford@math.umn.edu>
+
+Updated 11/09/1998 by David Huggins-Daines <dhd@debian.org>
diff --git a/Documentation/networking/ltpc.txt b/Documentation/networking/ltpc.txt
deleted file mode 100644 (file)
index 0bf3220..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-This is the ALPHA version of the ltpc driver.
-
-In order to use it, you will need at least version 1.3.3 of the
-netatalk package, and the Apple or Farallon LocalTalk PC card.
-There are a number of different LocalTalk cards for the PC; this
-driver applies only to the one with the 65c02 processor chip on it.
-
-To include it in the kernel, select the CONFIG_LTPC switch in the
-configuration dialog.  You can also compile it as a module.
-
-While the driver will attempt to autoprobe the I/O port address, IRQ
-line, and DMA channel of the card, this does not always work.  For
-this reason, you should be prepared to supply these parameters
-yourself.  (see "Card Configuration" below for how to determine or
-change the settings on your card)
-
-When the driver is compiled into the kernel, you can add a line such
-as the following to your /etc/lilo.conf:
-
- append="ltpc=0x240,9,1"
-
-where the parameters (in order) are the port address, IRQ, and DMA
-channel.  The second and third values can be omitted, in which case
-the driver will try to determine them itself.
-
-If you load the driver as a module, you can pass the parameters "io=",
-"irq=", and "dma=" on the command line with insmod or modprobe, or add
-them as options in a configuration file in /etc/modprobe.d/ directory:
-
- alias lt0 ltpc # autoload the module when the interface is configured
- options ltpc io=0x240 irq=9 dma=1
-
-Before starting up the netatalk demons (perhaps in rc.local), you
-need to add a line such as:
-
- /sbin/ifconfig lt0 127.0.0.42
-
-The address is unimportant - however, the card needs to be configured
-with ifconfig so that Netatalk can find it.
-
-The appropriate netatalk configuration depends on whether you are
-attached to a network that includes AppleTalk routers or not.  If,
-like me, you are simply connecting to your home Macintoshes and
-printers, you need to set up netatalk to "seed".  The way I do this
-is to have the lines
-
- dummy -seed -phase 2 -net 2000 -addr 2000.26 -zone "1033"
- lt0 -seed -phase 1 -net 1033 -addr 1033.27 -zone "1033"
-
-in my atalkd.conf.  What is going on here is that I need to fool
-netatalk into thinking that there are two AppleTalk interfaces
-present; otherwise, it refuses to seed.  This is a hack, and a more
-permanent solution would be to alter the netatalk code.  Also, make
-sure you have the correct name for the dummy interface - If it's
-compiled as a module, you will need to refer to it as "dummy0" or some
-such.
-
-If you are attached to an extended AppleTalk network, with routers on
-it, then you don't need to fool around with this -- the appropriate
-line in atalkd.conf is
-
- lt0 -phase 1
-
---------------------------------------
-
-Card Configuration:
-
-The interrupts and so forth are configured via the dipswitch on the
-board.  Set the switches so as not to conflict with other hardware.
-
-       Interrupts -- set at most one.  If none are set, the driver uses
-       polled mode.  Because the card was developed in the XT era, the
-       original documentation refers to IRQ2.  Since you'll be running
-       this on an AT (or later) class machine, that really means IRQ9.
-
-       SW1     IRQ 4
-       SW2     IRQ 3
-       SW3     IRQ 9 (2 in original card documentation only applies to XT)
-
-
-       DMA -- choose DMA 1 or 3, and set both corresponding switches.
-
-       SW4     DMA 3
-       SW5     DMA 1
-       SW6     DMA 3
-       SW7     DMA 1
-
-
-       I/O address -- choose one.
-
-       SW8     220 / 240
-
---------------------------------------
-
-IP:
-
-Yes, it is possible to do IP over LocalTalk.  However, you can't just
-treat the LocalTalk device like an ordinary Ethernet device, even if
-that's what it looks like to Netatalk.
-
-Instead, you follow the same procedure as for doing IP in EtherTalk.
-See Documentation/networking/ipddp.txt for more information about the
-kernel driver and userspace tools needed.
-
---------------------------------------
-
-BUGS:
-
-IRQ autoprobing often doesn't work on a cold boot.  To get around
-this, either compile the driver as a module, or pass the parameters
-for the card to the kernel as described above.
-
-Also, as usual, autoprobing is not recommended when you use the driver
-as a module. (though it usually works at boot time, at least)
-
-Polled mode is *really* slow sometimes, but this seems to depend on
-the configuration of the network.
-
-It may theoretically be possible to use two LTPC cards in the same
-machine, but this is unsupported, so if you really want to do this,
-you'll probably have to hack the initialization code a bit.
-
-______________________________________
-
-THANKS:
-       Thanks to Alan Cox for helpful discussions early on in this
-work, and to Denis Hainsworth for doing the bleeding-edge testing.
-
--- Bradford Johnson <bradford@math.umn.edu>
-
--- Updated 11/09/1998 by David Huggins-Daines <dhd@debian.org>
diff --git a/Documentation/networking/mac80211-injection.rst b/Documentation/networking/mac80211-injection.rst
new file mode 100644 (file)
index 0000000..be65f88
--- /dev/null
@@ -0,0 +1,106 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================================
+How to use packet injection with mac80211
+=========================================
+
+mac80211 now allows arbitrary packets to be injected down any Monitor Mode
+interface from userland.  The packet you inject needs to be composed in the
+following format::
+
+ [ radiotap header  ]
+ [ ieee80211 header ]
+ [ payload ]
+
+The radiotap format is discussed in
+./Documentation/networking/radiotap-headers.rst.
+
+Despite many radiotap parameters being currently defined, most only make sense
+to appear on received packets.  The following information is parsed from the
+radiotap headers and used to control injection:
+
+ * IEEE80211_RADIOTAP_FLAGS
+
+   =========================  ===========================================
+   IEEE80211_RADIOTAP_F_FCS   FCS will be removed and recalculated
+   IEEE80211_RADIOTAP_F_WEP   frame will be encrypted if key available
+   IEEE80211_RADIOTAP_F_FRAG  frame will be fragmented if longer than the
+                             current fragmentation threshold.
+   =========================  ===========================================
+
+ * IEEE80211_RADIOTAP_TX_FLAGS
+
+   =============================  ========================================
+   IEEE80211_RADIOTAP_F_TX_NOACK  frame should be sent without waiting for
+                                 an ACK even if it is a unicast frame
+   =============================  ========================================
+
+ * IEEE80211_RADIOTAP_RATE
+
+   legacy rate for the transmission (only for devices without own rate control)
+
+ * IEEE80211_RADIOTAP_MCS
+
+   HT rate for the transmission (only for devices without own rate control).
+   Also some flags are parsed
+
+   ============================  ========================
+   IEEE80211_RADIOTAP_MCS_SGI    use short guard interval
+   IEEE80211_RADIOTAP_MCS_BW_40  send in HT40 mode
+   ============================  ========================
+
+ * IEEE80211_RADIOTAP_DATA_RETRIES
+
+   number of retries when either IEEE80211_RADIOTAP_RATE or
+   IEEE80211_RADIOTAP_MCS was used
+
+ * IEEE80211_RADIOTAP_VHT
+
+   VHT mcs and number of streams used in the transmission (only for devices
+   without own rate control). Also other fields are parsed
+
+   flags field
+       IEEE80211_RADIOTAP_VHT_FLAG_SGI: use short guard interval
+
+   bandwidth field
+       * 1: send using 40MHz channel width
+       * 4: send using 80MHz channel width
+       * 11: send using 160MHz channel width
+
+The injection code can also skip all other currently defined radiotap fields
+facilitating replay of captured radiotap headers directly.
+
+Here is an example valid radiotap header defining some parameters::
+
+       0x00, 0x00, // <-- radiotap version
+       0x0b, 0x00, // <- radiotap header length
+       0x04, 0x0c, 0x00, 0x00, // <-- bitmap
+       0x6c, // <-- rate
+       0x0c, //<-- tx power
+       0x01 //<-- antenna
+
+The ieee80211 header follows immediately afterwards, looking for example like
+this::
+
+       0x08, 0x01, 0x00, 0x00,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0x13, 0x22, 0x33, 0x44, 0x55, 0x66,
+       0x13, 0x22, 0x33, 0x44, 0x55, 0x66,
+       0x10, 0x86
+
+Then lastly there is the payload.
+
+After composing the packet contents, it is sent by send()-ing it to a logical
+mac80211 interface that is in Monitor mode.  Libpcap can also be used,
+(which is easier than doing the work to bind the socket to the right
+interface), along the following lines:::
+
+       ppcap = pcap_open_live(szInterfaceName, 800, 1, 20, szErrbuf);
+       ...
+       r = pcap_inject(ppcap, u8aSendBuffer, nLength);
+
+You can also find a link to a complete inject application here:
+
+http://wireless.kernel.org/en/users/Documentation/packetspammer
+
+Andy Green <andy@warmcat.com>
diff --git a/Documentation/networking/mac80211-injection.txt b/Documentation/networking/mac80211-injection.txt
deleted file mode 100644 (file)
index d58d78d..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-How to use packet injection with mac80211
-=========================================
-
-mac80211 now allows arbitrary packets to be injected down any Monitor Mode
-interface from userland.  The packet you inject needs to be composed in the
-following format:
-
- [ radiotap header  ]
- [ ieee80211 header ]
- [ payload ]
-
-The radiotap format is discussed in
-./Documentation/networking/radiotap-headers.txt.
-
-Despite many radiotap parameters being currently defined, most only make sense
-to appear on received packets.  The following information is parsed from the
-radiotap headers and used to control injection:
-
- * IEEE80211_RADIOTAP_FLAGS
-
-   IEEE80211_RADIOTAP_F_FCS: FCS will be removed and recalculated
-   IEEE80211_RADIOTAP_F_WEP: frame will be encrypted if key available
-   IEEE80211_RADIOTAP_F_FRAG: frame will be fragmented if longer than the
-                             current fragmentation threshold.
-
- * IEEE80211_RADIOTAP_TX_FLAGS
-
-   IEEE80211_RADIOTAP_F_TX_NOACK: frame should be sent without waiting for
-                                 an ACK even if it is a unicast frame
-
- * IEEE80211_RADIOTAP_RATE
-
-   legacy rate for the transmission (only for devices without own rate control)
-
- * IEEE80211_RADIOTAP_MCS
-
-   HT rate for the transmission (only for devices without own rate control).
-   Also some flags are parsed
-
-   IEEE80211_RADIOTAP_MCS_SGI: use short guard interval
-   IEEE80211_RADIOTAP_MCS_BW_40: send in HT40 mode
-
- * IEEE80211_RADIOTAP_DATA_RETRIES
-
-   number of retries when either IEEE80211_RADIOTAP_RATE or
-   IEEE80211_RADIOTAP_MCS was used
-
- * IEEE80211_RADIOTAP_VHT
-
-   VHT mcs and number of streams used in the transmission (only for devices
-   without own rate control). Also other fields are parsed
-
-   flags field
-   IEEE80211_RADIOTAP_VHT_FLAG_SGI: use short guard interval
-
-   bandwidth field
-   1: send using 40MHz channel width
-   4: send using 80MHz channel width
-   11: send using 160MHz channel width
-
-The injection code can also skip all other currently defined radiotap fields
-facilitating replay of captured radiotap headers directly.
-
-Here is an example valid radiotap header defining some parameters
-
-       0x00, 0x00, // <-- radiotap version
-       0x0b, 0x00, // <- radiotap header length
-       0x04, 0x0c, 0x00, 0x00, // <-- bitmap
-       0x6c, // <-- rate
-       0x0c, //<-- tx power
-       0x01 //<-- antenna
-
-The ieee80211 header follows immediately afterwards, looking for example like
-this:
-
-       0x08, 0x01, 0x00, 0x00,
-       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-       0x13, 0x22, 0x33, 0x44, 0x55, 0x66,
-       0x13, 0x22, 0x33, 0x44, 0x55, 0x66,
-       0x10, 0x86
-
-Then lastly there is the payload.
-
-After composing the packet contents, it is sent by send()-ing it to a logical
-mac80211 interface that is in Monitor mode.  Libpcap can also be used,
-(which is easier than doing the work to bind the socket to the right
-interface), along the following lines:
-
-       ppcap = pcap_open_live(szInterfaceName, 800, 1, 20, szErrbuf);
-...
-       r = pcap_inject(ppcap, u8aSendBuffer, nLength);
-
-You can also find a link to a complete inject application here:
-
-http://wireless.kernel.org/en/users/Documentation/packetspammer
-
-Andy Green <andy@warmcat.com>
diff --git a/Documentation/networking/mpls-sysctl.rst b/Documentation/networking/mpls-sysctl.rst
new file mode 100644 (file)
index 0000000..0a2ac88
--- /dev/null
@@ -0,0 +1,57 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====================
+MPLS Sysfs variables
+====================
+
+/proc/sys/net/mpls/* Variables:
+===============================
+
+platform_labels - INTEGER
+       Number of entries in the platform label table.  It is not
+       possible to configure forwarding for label values equal to or
+       greater than the number of platform labels.
+
+       A dense utilization of the entries in the platform label table
+       is possible and expected as the platform labels are locally
+       allocated.
+
+       If the number of platform label table entries is set to 0 no
+       label will be recognized by the kernel and mpls forwarding
+       will be disabled.
+
+       Reducing this value will remove all label routing entries that
+       no longer fit in the table.
+
+       Possible values: 0 - 1048575
+
+       Default: 0
+
+ip_ttl_propagate - BOOL
+       Control whether TTL is propagated from the IPv4/IPv6 header to
+       the MPLS header on imposing labels and propagated from the
+       MPLS header to the IPv4/IPv6 header on popping the last label.
+
+       If disabled, the MPLS transport network will appear as a
+       single hop to transit traffic.
+
+       * 0 - disabled / RFC 3443 [Short] Pipe Model
+       * 1 - enabled / RFC 3443 Uniform Model (default)
+
+default_ttl - INTEGER
+       Default TTL value to use for MPLS packets where it cannot be
+       propagated from an IP header, either because one isn't present
+       or ip_ttl_propagate has been disabled.
+
+       Possible values: 1 - 255
+
+       Default: 255
+
+conf/<interface>/input - BOOL
+       Control whether packets can be input on this interface.
+
+       If disabled, packets will be discarded without further
+       processing.
+
+       * 0 - disabled (default)
+       * not 0 - enabled
diff --git a/Documentation/networking/mpls-sysctl.txt b/Documentation/networking/mpls-sysctl.txt
deleted file mode 100644 (file)
index 025cc9b..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/proc/sys/net/mpls/* Variables:
-
-platform_labels - INTEGER
-       Number of entries in the platform label table.  It is not
-       possible to configure forwarding for label values equal to or
-       greater than the number of platform labels.
-
-       A dense utilization of the entries in the platform label table
-       is possible and expected as the platform labels are locally
-       allocated.
-
-       If the number of platform label table entries is set to 0 no
-       label will be recognized by the kernel and mpls forwarding
-       will be disabled.
-
-       Reducing this value will remove all label routing entries that
-       no longer fit in the table.
-
-       Possible values: 0 - 1048575
-       Default: 0
-
-ip_ttl_propagate - BOOL
-       Control whether TTL is propagated from the IPv4/IPv6 header to
-       the MPLS header on imposing labels and propagated from the
-       MPLS header to the IPv4/IPv6 header on popping the last label.
-
-       If disabled, the MPLS transport network will appear as a
-       single hop to transit traffic.
-
-       0 - disabled / RFC 3443 [Short] Pipe Model
-       1 - enabled / RFC 3443 Uniform Model (default)
-
-default_ttl - INTEGER
-       Default TTL value to use for MPLS packets where it cannot be
-       propagated from an IP header, either because one isn't present
-       or ip_ttl_propagate has been disabled.
-
-       Possible values: 1 - 255
-       Default: 255
-
-conf/<interface>/input - BOOL
-       Control whether packets can be input on this interface.
-
-       If disabled, packets will be discarded without further
-       processing.
-
-       0 - disabled (default)
-       not 0 - enabled
diff --git a/Documentation/networking/multiqueue.rst b/Documentation/networking/multiqueue.rst
new file mode 100644 (file)
index 0000000..0a57616
--- /dev/null
@@ -0,0 +1,78 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================================
+HOWTO for multiqueue network device support
+===========================================
+
+Section 1: Base driver requirements for implementing multiqueue support
+=======================================================================
+
+Intro: Kernel support for multiqueue devices
+---------------------------------------------------------
+
+Kernel support for multiqueue devices is always present.
+
+Base drivers are required to use the new alloc_etherdev_mq() or
+alloc_netdev_mq() functions to allocate the subqueues for the device.  The
+underlying kernel API will take care of the allocation and deallocation of
+the subqueue memory, as well as netdev configuration of where the queues
+exist in memory.
+
+The base driver will also need to manage the queues as it does the global
+netdev->queue_lock today.  Therefore base drivers should use the
+netif_{start|stop|wake}_subqueue() functions to manage each queue while the
+device is still operational.  netdev->queue_lock is still used when the device
+comes online or when it's completely shut down (unregister_netdev(), etc.).
+
+
+Section 2: Qdisc support for multiqueue devices
+===============================================
+
+Currently two qdiscs are optimized for multiqueue devices.  The first is the
+default pfifo_fast qdisc.  This qdisc supports one qdisc per hardware queue.
+A new round-robin qdisc, sch_multiq also supports multiple hardware queues. The
+qdisc is responsible for classifying the skb's and then directing the skb's to
+bands and queues based on the value in skb->queue_mapping.  Use this field in
+the base driver to determine which queue to send the skb to.
+
+sch_multiq has been added for hardware that wishes to avoid head-of-line
+blocking.  It will cycle though the bands and verify that the hardware queue
+associated with the band is not stopped prior to dequeuing a packet.
+
+On qdisc load, the number of bands is based on the number of queues on the
+hardware.  Once the association is made, any skb with skb->queue_mapping set,
+will be queued to the band associated with the hardware queue.
+
+
+Section 3: Brief howto using MULTIQ for multiqueue devices
+==========================================================
+
+The userspace command 'tc,' part of the iproute2 package, is used to configure
+qdiscs.  To add the MULTIQ qdisc to your network device, assuming the device
+is called eth0, run the following command::
+
+    # tc qdisc add dev eth0 root handle 1: multiq
+
+The qdisc will allocate the number of bands to equal the number of queues that
+the device reports, and bring the qdisc online.  Assuming eth0 has 4 Tx
+queues, the band mapping would look like::
+
+    band 0 => queue 0
+    band 1 => queue 1
+    band 2 => queue 2
+    band 3 => queue 3
+
+Traffic will begin flowing through each queue based on either the simple_tx_hash
+function or based on netdev->select_queue() if you have it defined.
+
+The behavior of tc filters remains the same.  However a new tc action,
+skbedit, has been added.  Assuming you wanted to route all traffic to a
+specific host, for example 192.168.0.3, through a specific queue you could use
+this action and establish a filter such as::
+
+    tc filter add dev eth0 parent 1: protocol ip prio 1 u32 \
+           match ip dst 192.168.0.3 \
+           action skbedit queue_mapping 3
+
+:Author: Alexander Duyck <alexander.h.duyck@intel.com>
+:Original Author: Peter P. Waskiewicz Jr. <peter.p.waskiewicz.jr@intel.com>
diff --git a/Documentation/networking/multiqueue.txt b/Documentation/networking/multiqueue.txt
deleted file mode 100644 (file)
index 4caa0e3..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-
-               HOWTO for multiqueue network device support
-               ===========================================
-
-Section 1: Base driver requirements for implementing multiqueue support
-
-Intro: Kernel support for multiqueue devices
----------------------------------------------------------
-
-Kernel support for multiqueue devices is always present.
-
-Section 1: Base driver requirements for implementing multiqueue support
------------------------------------------------------------------------
-
-Base drivers are required to use the new alloc_etherdev_mq() or
-alloc_netdev_mq() functions to allocate the subqueues for the device.  The
-underlying kernel API will take care of the allocation and deallocation of
-the subqueue memory, as well as netdev configuration of where the queues
-exist in memory.
-
-The base driver will also need to manage the queues as it does the global
-netdev->queue_lock today.  Therefore base drivers should use the
-netif_{start|stop|wake}_subqueue() functions to manage each queue while the
-device is still operational.  netdev->queue_lock is still used when the device
-comes online or when it's completely shut down (unregister_netdev(), etc.).
-
-
-Section 2: Qdisc support for multiqueue devices
-
------------------------------------------------
-
-Currently two qdiscs are optimized for multiqueue devices.  The first is the
-default pfifo_fast qdisc.  This qdisc supports one qdisc per hardware queue.
-A new round-robin qdisc, sch_multiq also supports multiple hardware queues. The
-qdisc is responsible for classifying the skb's and then directing the skb's to
-bands and queues based on the value in skb->queue_mapping.  Use this field in
-the base driver to determine which queue to send the skb to.
-
-sch_multiq has been added for hardware that wishes to avoid head-of-line
-blocking.  It will cycle though the bands and verify that the hardware queue
-associated with the band is not stopped prior to dequeuing a packet.
-
-On qdisc load, the number of bands is based on the number of queues on the
-hardware.  Once the association is made, any skb with skb->queue_mapping set,
-will be queued to the band associated with the hardware queue.
-
-
-Section 3: Brief howto using MULTIQ for multiqueue devices
----------------------------------------------------------------
-
-The userspace command 'tc,' part of the iproute2 package, is used to configure
-qdiscs.  To add the MULTIQ qdisc to your network device, assuming the device
-is called eth0, run the following command:
-
-# tc qdisc add dev eth0 root handle 1: multiq
-
-The qdisc will allocate the number of bands to equal the number of queues that
-the device reports, and bring the qdisc online.  Assuming eth0 has 4 Tx
-queues, the band mapping would look like:
-
-band 0 => queue 0
-band 1 => queue 1
-band 2 => queue 2
-band 3 => queue 3
-
-Traffic will begin flowing through each queue based on either the simple_tx_hash
-function or based on netdev->select_queue() if you have it defined.
-
-The behavior of tc filters remains the same.  However a new tc action,
-skbedit, has been added.  Assuming you wanted to route all traffic to a
-specific host, for example 192.168.0.3, through a specific queue you could use
-this action and establish a filter such as:
-
-tc filter add dev eth0 parent 1: protocol ip prio 1 u32 \
-       match ip dst 192.168.0.3 \
-       action skbedit queue_mapping 3
-
-Author: Alexander Duyck <alexander.h.duyck@intel.com>
-Original Author: Peter P. Waskiewicz Jr. <peter.p.waskiewicz.jr@intel.com>
diff --git a/Documentation/networking/net_dim.rst b/Documentation/networking/net_dim.rst
new file mode 100644 (file)
index 0000000..3bed9fd
--- /dev/null
@@ -0,0 +1,176 @@
+======================================================
+Net DIM - Generic Network Dynamic Interrupt Moderation
+======================================================
+
+:Author: Tal Gilboa <talgi@mellanox.com>
+
+.. contents:: :depth: 2
+
+Assumptions
+===========
+
+This document assumes the reader has basic knowledge in network drivers
+and in general interrupt moderation.
+
+
+Introduction
+============
+
+Dynamic Interrupt Moderation (DIM) (in networking) refers to changing the
+interrupt moderation configuration of a channel in order to optimize packet
+processing. The mechanism includes an algorithm which decides if and how to
+change moderation parameters for a channel, usually by performing an analysis on
+runtime data sampled from the system. Net DIM is such a mechanism. In each
+iteration of the algorithm, it analyses a given sample of the data, compares it
+to the previous sample and if required, it can decide to change some of the
+interrupt moderation configuration fields. The data sample is composed of data
+bandwidth, the number of packets and the number of events. The time between
+samples is also measured. Net DIM compares the current and the previous data and
+returns an adjusted interrupt moderation configuration object. In some cases,
+the algorithm might decide not to change anything. The configuration fields are
+the minimum duration (microseconds) allowed between events and the maximum
+number of wanted packets per event. The Net DIM algorithm ascribes importance to
+increase bandwidth over reducing interrupt rate.
+
+
+Net DIM Algorithm
+=================
+
+Each iteration of the Net DIM algorithm follows these steps:
+
+#. Calculates new data sample.
+#. Compares it to previous sample.
+#. Makes a decision - suggests interrupt moderation configuration fields.
+#. Applies a schedule work function, which applies suggested configuration.
+
+The first two steps are straightforward, both the new and the previous data are
+supplied by the driver registered to Net DIM. The previous data is the new data
+supplied to the previous iteration. The comparison step checks the difference
+between the new and previous data and decides on the result of the last step.
+A step would result as "better" if bandwidth increases and as "worse" if
+bandwidth reduces. If there is no change in bandwidth, the packet rate is
+compared in a similar fashion - increase == "better" and decrease == "worse".
+In case there is no change in the packet rate as well, the interrupt rate is
+compared. Here the algorithm tries to optimize for lower interrupt rate so an
+increase in the interrupt rate is considered "worse" and a decrease is
+considered "better". Step #2 has an optimization for avoiding false results: it
+only considers a difference between samples as valid if it is greater than a
+certain percentage. Also, since Net DIM does not measure anything by itself, it
+assumes the data provided by the driver is valid.
+
+Step #3 decides on the suggested configuration based on the result from step #2
+and the internal state of the algorithm. The states reflect the "direction" of
+the algorithm: is it going left (reducing moderation), right (increasing
+moderation) or standing still. Another optimization is that if a decision
+to stay still is made multiple times, the interval between iterations of the
+algorithm would increase in order to reduce calculation overhead. Also, after
+"parking" on one of the most left or most right decisions, the algorithm may
+decide to verify this decision by taking a step in the other direction. This is
+done in order to avoid getting stuck in a "deep sleep" scenario. Once a
+decision is made, an interrupt moderation configuration is selected from
+the predefined profiles.
+
+The last step is to notify the registered driver that it should apply the
+suggested configuration. This is done by scheduling a work function, defined by
+the Net DIM API and provided by the registered driver.
+
+As you can see, Net DIM itself does not actively interact with the system. It
+would have trouble making the correct decisions if the wrong data is supplied to
+it and it would be useless if the work function would not apply the suggested
+configuration. This does, however, allow the registered driver some room for
+manoeuvre as it may provide partial data or ignore the algorithm suggestion
+under some conditions.
+
+
+Registering a Network Device to DIM
+===================================
+
+Net DIM API exposes the main function net_dim().
+This function is the entry point to the Net
+DIM algorithm and has to be called every time the driver would like to check if
+it should change interrupt moderation parameters. The driver should provide two
+data structures: :c:type:`struct dim <dim>` and
+:c:type:`struct dim_sample <dim_sample>`. :c:type:`struct dim <dim>`
+describes the state of DIM for a specific object (RX queue, TX queue,
+other queues, etc.). This includes the current selected profile, previous data
+samples, the callback function provided by the driver and more.
+:c:type:`struct dim_sample <dim_sample>` describes a data sample,
+which will be compared to the data sample stored in :c:type:`struct dim <dim>`
+in order to decide on the algorithm's next
+step. The sample should include bytes, packets and interrupts, measured by
+the driver.
+
+In order to use Net DIM from a networking driver, the driver needs to call the
+main net_dim() function. The recommended method is to call net_dim() on each
+interrupt. Since Net DIM has a built-in moderation and it might decide to skip
+iterations under certain conditions, there is no need to moderate the net_dim()
+calls as well. As mentioned above, the driver needs to provide an object of type
+:c:type:`struct dim <dim>` to the net_dim() function call. It is advised for
+each entity using Net DIM to hold a :c:type:`struct dim <dim>` as part of its
+data structure and use it as the main Net DIM API object.
+The :c:type:`struct dim_sample <dim_sample>` should hold the latest
+bytes, packets and interrupts count. No need to perform any calculations, just
+include the raw data.
+
+The net_dim() call itself does not return anything. Instead Net DIM relies on
+the driver to provide a callback function, which is called when the algorithm
+decides to make a change in the interrupt moderation parameters. This callback
+will be scheduled and run in a separate thread in order not to add overhead to
+the data flow. After the work is done, Net DIM algorithm needs to be set to
+the proper state in order to move to the next iteration.
+
+
+Example
+=======
+
+The following code demonstrates how to register a driver to Net DIM. The actual
+usage is not complete but it should make the outline of the usage clear.
+
+.. code-block:: c
+
+  #include <linux/dim.h>
+
+  /* Callback for net DIM to schedule on a decision to change moderation */
+  void my_driver_do_dim_work(struct work_struct *work)
+  {
+       /* Get struct dim from struct work_struct */
+       struct dim *dim = container_of(work, struct dim,
+                                      work);
+       /* Do interrupt moderation related stuff */
+       ...
+
+       /* Signal net DIM work is done and it should move to next iteration */
+       dim->state = DIM_START_MEASURE;
+  }
+
+  /* My driver's interrupt handler */
+  int my_driver_handle_interrupt(struct my_driver_entity *my_entity, ...)
+  {
+       ...
+       /* A struct to hold current measured data */
+       struct dim_sample dim_sample;
+       ...
+       /* Initiate data sample struct with current data */
+       dim_update_sample(my_entity->events,
+                         my_entity->packets,
+                         my_entity->bytes,
+                         &dim_sample);
+       /* Call net DIM */
+       net_dim(&my_entity->dim, dim_sample);
+       ...
+  }
+
+  /* My entity's initialization function (my_entity was already allocated) */
+  int my_driver_init_my_entity(struct my_driver_entity *my_entity, ...)
+  {
+       ...
+       /* Initiate struct work_struct with my driver's callback function */
+       INIT_WORK(&my_entity->dim.work, my_driver_do_dim_work);
+       ...
+  }
+
+Dynamic Interrupt Moderation (DIM) library API
+==============================================
+
+.. kernel-doc:: include/linux/dim.h
+    :internal:
diff --git a/Documentation/networking/net_dim.txt b/Documentation/networking/net_dim.txt
deleted file mode 100644 (file)
index 9bdb7d5..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-Net DIM - Generic Network Dynamic Interrupt Moderation
-======================================================
-
-Author:
-       Tal Gilboa <talgi@mellanox.com>
-
-
-Contents
-=========
-
-- Assumptions
-- Introduction
-- The Net DIM Algorithm
-- Registering a Network Device to DIM
-- Example
-
-Part 0: Assumptions
-======================
-
-This document assumes the reader has basic knowledge in network drivers
-and in general interrupt moderation.
-
-
-Part I: Introduction
-======================
-
-Dynamic Interrupt Moderation (DIM) (in networking) refers to changing the
-interrupt moderation configuration of a channel in order to optimize packet
-processing. The mechanism includes an algorithm which decides if and how to
-change moderation parameters for a channel, usually by performing an analysis on
-runtime data sampled from the system. Net DIM is such a mechanism. In each
-iteration of the algorithm, it analyses a given sample of the data, compares it
-to the previous sample and if required, it can decide to change some of the
-interrupt moderation configuration fields. The data sample is composed of data
-bandwidth, the number of packets and the number of events. The time between
-samples is also measured. Net DIM compares the current and the previous data and
-returns an adjusted interrupt moderation configuration object. In some cases,
-the algorithm might decide not to change anything. The configuration fields are
-the minimum duration (microseconds) allowed between events and the maximum
-number of wanted packets per event. The Net DIM algorithm ascribes importance to
-increase bandwidth over reducing interrupt rate.
-
-
-Part II: The Net DIM Algorithm
-===============================
-
-Each iteration of the Net DIM algorithm follows these steps:
-1. Calculates new data sample.
-2. Compares it to previous sample.
-3. Makes a decision - suggests interrupt moderation configuration fields.
-4. Applies a schedule work function, which applies suggested configuration.
-
-The first two steps are straightforward, both the new and the previous data are
-supplied by the driver registered to Net DIM. The previous data is the new data
-supplied to the previous iteration. The comparison step checks the difference
-between the new and previous data and decides on the result of the last step.
-A step would result as "better" if bandwidth increases and as "worse" if
-bandwidth reduces. If there is no change in bandwidth, the packet rate is
-compared in a similar fashion - increase == "better" and decrease == "worse".
-In case there is no change in the packet rate as well, the interrupt rate is
-compared. Here the algorithm tries to optimize for lower interrupt rate so an
-increase in the interrupt rate is considered "worse" and a decrease is
-considered "better". Step #2 has an optimization for avoiding false results: it
-only considers a difference between samples as valid if it is greater than a
-certain percentage. Also, since Net DIM does not measure anything by itself, it
-assumes the data provided by the driver is valid.
-
-Step #3 decides on the suggested configuration based on the result from step #2
-and the internal state of the algorithm. The states reflect the "direction" of
-the algorithm: is it going left (reducing moderation), right (increasing
-moderation) or standing still. Another optimization is that if a decision
-to stay still is made multiple times, the interval between iterations of the
-algorithm would increase in order to reduce calculation overhead. Also, after
-"parking" on one of the most left or most right decisions, the algorithm may
-decide to verify this decision by taking a step in the other direction. This is
-done in order to avoid getting stuck in a "deep sleep" scenario. Once a
-decision is made, an interrupt moderation configuration is selected from
-the predefined profiles.
-
-The last step is to notify the registered driver that it should apply the
-suggested configuration. This is done by scheduling a work function, defined by
-the Net DIM API and provided by the registered driver.
-
-As you can see, Net DIM itself does not actively interact with the system. It
-would have trouble making the correct decisions if the wrong data is supplied to
-it and it would be useless if the work function would not apply the suggested
-configuration. This does, however, allow the registered driver some room for
-manoeuvre as it may provide partial data or ignore the algorithm suggestion
-under some conditions.
-
-
-Part III: Registering a Network Device to DIM
-==============================================
-
-Net DIM API exposes the main function net_dim(struct dim *dim,
-struct dim_sample end_sample). This function is the entry point to the Net
-DIM algorithm and has to be called every time the driver would like to check if
-it should change interrupt moderation parameters. The driver should provide two
-data structures: struct dim and struct dim_sample. Struct dim
-describes the state of DIM for a specific object (RX queue, TX queue,
-other queues, etc.). This includes the current selected profile, previous data
-samples, the callback function provided by the driver and more.
-Struct dim_sample describes a data sample, which will be compared to the
-data sample stored in struct dim in order to decide on the algorithm's next
-step. The sample should include bytes, packets and interrupts, measured by
-the driver.
-
-In order to use Net DIM from a networking driver, the driver needs to call the
-main net_dim() function. The recommended method is to call net_dim() on each
-interrupt. Since Net DIM has a built-in moderation and it might decide to skip
-iterations under certain conditions, there is no need to moderate the net_dim()
-calls as well. As mentioned above, the driver needs to provide an object of type
-struct dim to the net_dim() function call. It is advised for each entity
-using Net DIM to hold a struct dim as part of its data structure and use it
-as the main Net DIM API object. The struct dim_sample should hold the latest
-bytes, packets and interrupts count. No need to perform any calculations, just
-include the raw data.
-
-The net_dim() call itself does not return anything. Instead Net DIM relies on
-the driver to provide a callback function, which is called when the algorithm
-decides to make a change in the interrupt moderation parameters. This callback
-will be scheduled and run in a separate thread in order not to add overhead to
-the data flow. After the work is done, Net DIM algorithm needs to be set to
-the proper state in order to move to the next iteration.
-
-
-Part IV: Example
-=================
-
-The following code demonstrates how to register a driver to Net DIM. The actual
-usage is not complete but it should make the outline of the usage clear.
-
-my_driver.c:
-
-#include <linux/dim.h>
-
-/* Callback for net DIM to schedule on a decision to change moderation */
-void my_driver_do_dim_work(struct work_struct *work)
-{
-       /* Get struct dim from struct work_struct */
-       struct dim *dim = container_of(work, struct dim,
-                                      work);
-       /* Do interrupt moderation related stuff */
-       ...
-
-       /* Signal net DIM work is done and it should move to next iteration */
-       dim->state = DIM_START_MEASURE;
-}
-
-/* My driver's interrupt handler */
-int my_driver_handle_interrupt(struct my_driver_entity *my_entity, ...)
-{
-       ...
-       /* A struct to hold current measured data */
-       struct dim_sample dim_sample;
-       ...
-       /* Initiate data sample struct with current data */
-       dim_update_sample(my_entity->events,
-                         my_entity->packets,
-                         my_entity->bytes,
-                         &dim_sample);
-       /* Call net DIM */
-       net_dim(&my_entity->dim, dim_sample);
-       ...
-}
-
-/* My entity's initialization function (my_entity was already allocated) */
-int my_driver_init_my_entity(struct my_driver_entity *my_entity, ...)
-{
-       ...
-       /* Initiate struct work_struct with my driver's callback function */
-       INIT_WORK(&my_entity->dim.work, my_driver_do_dim_work);
-       ...
-}
diff --git a/Documentation/networking/netconsole.rst b/Documentation/networking/netconsole.rst
new file mode 100644 (file)
index 0000000..1f5c4a0
--- /dev/null
@@ -0,0 +1,239 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==========
+Netconsole
+==========
+
+
+started by Ingo Molnar <mingo@redhat.com>, 2001.09.17
+
+2.6 port and netpoll api by Matt Mackall <mpm@selenic.com>, Sep 9 2003
+
+IPv6 support by Cong Wang <xiyou.wangcong@gmail.com>, Jan 1 2013
+
+Extended console support by Tejun Heo <tj@kernel.org>, May 1 2015
+
+Please send bug reports to Matt Mackall <mpm@selenic.com>
+Satyam Sharma <satyam.sharma@gmail.com>, and Cong Wang <xiyou.wangcong@gmail.com>
+
+Introduction:
+=============
+
+This module logs kernel printk messages over UDP allowing debugging of
+problem where disk logging fails and serial consoles are impractical.
+
+It can be used either built-in or as a module. As a built-in,
+netconsole initializes immediately after NIC cards and will bring up
+the specified interface as soon as possible. While this doesn't allow
+capture of early kernel panics, it does capture most of the boot
+process.
+
+Sender and receiver configuration:
+==================================
+
+It takes a string configuration parameter "netconsole" in the
+following format::
+
+ netconsole=[+][src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr]
+
+   where
+       +             if present, enable extended console support
+       src-port      source for UDP packets (defaults to 6665)
+       src-ip        source IP to use (interface address)
+       dev           network interface (eth0)
+       tgt-port      port for logging agent (6666)
+       tgt-ip        IP address for logging agent
+       tgt-macaddr   ethernet MAC address for logging agent (broadcast)
+
+Examples::
+
+ linux netconsole=4444@10.0.0.1/eth1,9353@10.0.0.2/12:34:56:78:9a:bc
+
+or::
+
+ insmod netconsole netconsole=@/,@10.0.0.2/
+
+or using IPv6::
+
+ insmod netconsole netconsole=@/,@fd00:1:2:3::1/
+
+It also supports logging to multiple remote agents by specifying
+parameters for the multiple agents separated by semicolons and the
+complete string enclosed in "quotes", thusly::
+
+ modprobe netconsole netconsole="@/,@10.0.0.2/;@/eth1,6892@10.0.0.3/"
+
+Built-in netconsole starts immediately after the TCP stack is
+initialized and attempts to bring up the supplied dev at the supplied
+address.
+
+The remote host has several options to receive the kernel messages,
+for example:
+
+1) syslogd
+
+2) netcat
+
+   On distributions using a BSD-based netcat version (e.g. Fedora,
+   openSUSE and Ubuntu) the listening port must be specified without
+   the -p switch::
+
+       nc -u -l -p <port>' / 'nc -u -l <port>
+
+    or::
+
+       netcat -u -l -p <port>' / 'netcat -u -l <port>
+
+3) socat
+
+::
+
+   socat udp-recv:<port> -
+
+Dynamic reconfiguration:
+========================
+
+Dynamic reconfigurability is a useful addition to netconsole that enables
+remote logging targets to be dynamically added, removed, or have their
+parameters reconfigured at runtime from a configfs-based userspace interface.
+[ Note that the parameters of netconsole targets that were specified/created
+from the boot/module option are not exposed via this interface, and hence
+cannot be modified dynamically. ]
+
+To include this feature, select CONFIG_NETCONSOLE_DYNAMIC when building the
+netconsole module (or kernel, if netconsole is built-in).
+
+Some examples follow (where configfs is mounted at the /sys/kernel/config
+mountpoint).
+
+To add a remote logging target (target names can be arbitrary)::
+
+ cd /sys/kernel/config/netconsole/
+ mkdir target1
+
+Note that newly created targets have default parameter values (as mentioned
+above) and are disabled by default -- they must first be enabled by writing
+"1" to the "enabled" attribute (usually after setting parameters accordingly)
+as described below.
+
+To remove a target::
+
+ rmdir /sys/kernel/config/netconsole/othertarget/
+
+The interface exposes these parameters of a netconsole target to userspace:
+
+       ==============  =================================       ============
+       enabled         Is this target currently enabled?       (read-write)
+       extended        Extended mode enabled                   (read-write)
+       dev_name        Local network interface name            (read-write)
+       local_port      Source UDP port to use                  (read-write)
+       remote_port     Remote agent's UDP port                 (read-write)
+       local_ip        Source IP address to use                (read-write)
+       remote_ip       Remote agent's IP address               (read-write)
+       local_mac       Local interface's MAC address           (read-only)
+       remote_mac      Remote agent's MAC address              (read-write)
+       ==============  =================================       ============
+
+The "enabled" attribute is also used to control whether the parameters of
+a target can be updated or not -- you can modify the parameters of only
+disabled targets (i.e. if "enabled" is 0).
+
+To update a target's parameters::
+
+ cat enabled                           # check if enabled is 1
+ echo 0 > enabled                      # disable the target (if required)
+ echo eth2 > dev_name                  # set local interface
+ echo 10.0.0.4 > remote_ip             # update some parameter
+ echo cb:a9:87:65:43:21 > remote_mac   # update more parameters
+ echo 1 > enabled                      # enable target again
+
+You can also update the local interface dynamically. This is especially
+useful if you want to use interfaces that have newly come up (and may not
+have existed when netconsole was loaded / initialized).
+
+Extended console:
+=================
+
+If '+' is prefixed to the configuration line or "extended" config file
+is set to 1, extended console support is enabled. An example boot
+param follows::
+
+ linux netconsole=+4444@10.0.0.1/eth1,9353@10.0.0.2/12:34:56:78:9a:bc
+
+Log messages are transmitted with extended metadata header in the
+following format which is the same as /dev/kmsg::
+
+ <level>,<sequnum>,<timestamp>,<contflag>;<message text>
+
+Non printable characters in <message text> are escaped using "\xff"
+notation. If the message contains optional dictionary, verbatim
+newline is used as the delimeter.
+
+If a message doesn't fit in certain number of bytes (currently 1000),
+the message is split into multiple fragments by netconsole. These
+fragments are transmitted with "ncfrag" header field added::
+
+ ncfrag=<byte-offset>/<total-bytes>
+
+For example, assuming a lot smaller chunk size, a message "the first
+chunk, the 2nd chunk." may be split as follows::
+
+ 6,416,1758426,-,ncfrag=0/31;the first chunk,
+ 6,416,1758426,-,ncfrag=16/31; the 2nd chunk.
+
+Miscellaneous notes:
+====================
+
+.. Warning::
+
+   the default target ethernet setting uses the broadcast
+   ethernet address to send packets, which can cause increased load on
+   other systems on the same ethernet segment.
+
+.. Tip::
+
+   some LAN switches may be configured to suppress ethernet broadcasts
+   so it is advised to explicitly specify the remote agents' MAC addresses
+   from the config parameters passed to netconsole.
+
+.. Tip::
+
+   to find out the MAC address of, say, 10.0.0.2, you may try using::
+
+       ping -c 1 10.0.0.2 ; /sbin/arp -n | grep 10.0.0.2
+
+.. Tip::
+
+   in case the remote logging agent is on a separate LAN subnet than
+   the sender, it is suggested to try specifying the MAC address of the
+   default gateway (you may use /sbin/route -n to find it out) as the
+   remote MAC address instead.
+
+.. note::
+
+   the network device (eth1 in the above case) can run any kind
+   of other network traffic, netconsole is not intrusive. Netconsole
+   might cause slight delays in other traffic if the volume of kernel
+   messages is high, but should have no other impact.
+
+.. note::
+
+   if you find that the remote logging agent is not receiving or
+   printing all messages from the sender, it is likely that you have set
+   the "console_loglevel" parameter (on the sender) to only send high
+   priority messages to the console. You can change this at runtime using::
+
+       dmesg -n 8
+
+   or by specifying "debug" on the kernel command line at boot, to send
+   all kernel messages to the console. A specific value for this parameter
+   can also be set using the "loglevel" kernel boot option. See the
+   dmesg(8) man page and Documentation/admin-guide/kernel-parameters.rst
+   for details.
+
+Netconsole was designed to be as instantaneous as possible, to
+enable the logging of even the most critical kernel bugs. It works
+from IRQ contexts as well, and does not enable interrupts while
+sending packets. Due to these unique needs, configuration cannot
+be more automatic, and some fundamental limitations will remain:
+only IP networks, UDP packets and ethernet devices are supported.
diff --git a/Documentation/networking/netconsole.txt b/Documentation/networking/netconsole.txt
deleted file mode 100644 (file)
index 296ea00..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-
-started by Ingo Molnar <mingo@redhat.com>, 2001.09.17
-2.6 port and netpoll api by Matt Mackall <mpm@selenic.com>, Sep 9 2003
-IPv6 support by Cong Wang <xiyou.wangcong@gmail.com>, Jan 1 2013
-Extended console support by Tejun Heo <tj@kernel.org>, May 1 2015
-
-Please send bug reports to Matt Mackall <mpm@selenic.com>
-Satyam Sharma <satyam.sharma@gmail.com>, and Cong Wang <xiyou.wangcong@gmail.com>
-
-Introduction:
-=============
-
-This module logs kernel printk messages over UDP allowing debugging of
-problem where disk logging fails and serial consoles are impractical.
-
-It can be used either built-in or as a module. As a built-in,
-netconsole initializes immediately after NIC cards and will bring up
-the specified interface as soon as possible. While this doesn't allow
-capture of early kernel panics, it does capture most of the boot
-process.
-
-Sender and receiver configuration:
-==================================
-
-It takes a string configuration parameter "netconsole" in the
-following format:
-
- netconsole=[+][src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr]
-
-   where
-        +             if present, enable extended console support
-        src-port      source for UDP packets (defaults to 6665)
-        src-ip        source IP to use (interface address)
-        dev           network interface (eth0)
-        tgt-port      port for logging agent (6666)
-        tgt-ip        IP address for logging agent
-        tgt-macaddr   ethernet MAC address for logging agent (broadcast)
-
-Examples:
-
- linux netconsole=4444@10.0.0.1/eth1,9353@10.0.0.2/12:34:56:78:9a:bc
-
-  or
-
- insmod netconsole netconsole=@/,@10.0.0.2/
-
-  or using IPv6
-
- insmod netconsole netconsole=@/,@fd00:1:2:3::1/
-
-It also supports logging to multiple remote agents by specifying
-parameters for the multiple agents separated by semicolons and the
-complete string enclosed in "quotes", thusly:
-
- modprobe netconsole netconsole="@/,@10.0.0.2/;@/eth1,6892@10.0.0.3/"
-
-Built-in netconsole starts immediately after the TCP stack is
-initialized and attempts to bring up the supplied dev at the supplied
-address.
-
-The remote host has several options to receive the kernel messages,
-for example:
-
-1) syslogd
-
-2) netcat
-
-   On distributions using a BSD-based netcat version (e.g. Fedora,
-   openSUSE and Ubuntu) the listening port must be specified without
-   the -p switch:
-
-   'nc -u -l -p <port>' / 'nc -u -l <port>' or
-   'netcat -u -l -p <port>' / 'netcat -u -l <port>'
-
-3) socat
-
-   'socat udp-recv:<port> -'
-
-Dynamic reconfiguration:
-========================
-
-Dynamic reconfigurability is a useful addition to netconsole that enables
-remote logging targets to be dynamically added, removed, or have their
-parameters reconfigured at runtime from a configfs-based userspace interface.
-[ Note that the parameters of netconsole targets that were specified/created
-from the boot/module option are not exposed via this interface, and hence
-cannot be modified dynamically. ]
-
-To include this feature, select CONFIG_NETCONSOLE_DYNAMIC when building the
-netconsole module (or kernel, if netconsole is built-in).
-
-Some examples follow (where configfs is mounted at the /sys/kernel/config
-mountpoint).
-
-To add a remote logging target (target names can be arbitrary):
-
- cd /sys/kernel/config/netconsole/
- mkdir target1
-
-Note that newly created targets have default parameter values (as mentioned
-above) and are disabled by default -- they must first be enabled by writing
-"1" to the "enabled" attribute (usually after setting parameters accordingly)
-as described below.
-
-To remove a target:
-
- rmdir /sys/kernel/config/netconsole/othertarget/
-
-The interface exposes these parameters of a netconsole target to userspace:
-
-       enabled         Is this target currently enabled?       (read-write)
-       extended        Extended mode enabled                   (read-write)
-       dev_name        Local network interface name            (read-write)
-       local_port      Source UDP port to use                  (read-write)
-       remote_port     Remote agent's UDP port                 (read-write)
-       local_ip        Source IP address to use                (read-write)
-       remote_ip       Remote agent's IP address               (read-write)
-       local_mac       Local interface's MAC address           (read-only)
-       remote_mac      Remote agent's MAC address              (read-write)
-
-The "enabled" attribute is also used to control whether the parameters of
-a target can be updated or not -- you can modify the parameters of only
-disabled targets (i.e. if "enabled" is 0).
-
-To update a target's parameters:
-
- cat enabled                           # check if enabled is 1
- echo 0 > enabled                      # disable the target (if required)
- echo eth2 > dev_name                  # set local interface
- echo 10.0.0.4 > remote_ip             # update some parameter
- echo cb:a9:87:65:43:21 > remote_mac   # update more parameters
- echo 1 > enabled                      # enable target again
-
-You can also update the local interface dynamically. This is especially
-useful if you want to use interfaces that have newly come up (and may not
-have existed when netconsole was loaded / initialized).
-
-Extended console:
-=================
-
-If '+' is prefixed to the configuration line or "extended" config file
-is set to 1, extended console support is enabled. An example boot
-param follows.
-
- linux netconsole=+4444@10.0.0.1/eth1,9353@10.0.0.2/12:34:56:78:9a:bc
-
-Log messages are transmitted with extended metadata header in the
-following format which is the same as /dev/kmsg.
-
- <level>,<sequnum>,<timestamp>,<contflag>;<message text>
-
-Non printable characters in <message text> are escaped using "\xff"
-notation. If the message contains optional dictionary, verbatim
-newline is used as the delimeter.
-
-If a message doesn't fit in certain number of bytes (currently 1000),
-the message is split into multiple fragments by netconsole. These
-fragments are transmitted with "ncfrag" header field added.
-
- ncfrag=<byte-offset>/<total-bytes>
-
-For example, assuming a lot smaller chunk size, a message "the first
-chunk, the 2nd chunk." may be split as follows.
-
- 6,416,1758426,-,ncfrag=0/31;the first chunk,
- 6,416,1758426,-,ncfrag=16/31; the 2nd chunk.
-
-Miscellaneous notes:
-====================
-
-WARNING: the default target ethernet setting uses the broadcast
-ethernet address to send packets, which can cause increased load on
-other systems on the same ethernet segment.
-
-TIP: some LAN switches may be configured to suppress ethernet broadcasts
-so it is advised to explicitly specify the remote agents' MAC addresses
-from the config parameters passed to netconsole.
-
-TIP: to find out the MAC address of, say, 10.0.0.2, you may try using:
-
- ping -c 1 10.0.0.2 ; /sbin/arp -n | grep 10.0.0.2
-
-TIP: in case the remote logging agent is on a separate LAN subnet than
-the sender, it is suggested to try specifying the MAC address of the
-default gateway (you may use /sbin/route -n to find it out) as the
-remote MAC address instead.
-
-NOTE: the network device (eth1 in the above case) can run any kind
-of other network traffic, netconsole is not intrusive. Netconsole
-might cause slight delays in other traffic if the volume of kernel
-messages is high, but should have no other impact.
-
-NOTE: if you find that the remote logging agent is not receiving or
-printing all messages from the sender, it is likely that you have set
-the "console_loglevel" parameter (on the sender) to only send high
-priority messages to the console. You can change this at runtime using:
-
- dmesg -n 8
-
-or by specifying "debug" on the kernel command line at boot, to send
-all kernel messages to the console. A specific value for this parameter
-can also be set using the "loglevel" kernel boot option. See the
-dmesg(8) man page and Documentation/admin-guide/kernel-parameters.rst for details.
-
-Netconsole was designed to be as instantaneous as possible, to
-enable the logging of even the most critical kernel bugs. It works
-from IRQ contexts as well, and does not enable interrupts while
-sending packets. Due to these unique needs, configuration cannot
-be more automatic, and some fundamental limitations will remain:
-only IP networks, UDP packets and ethernet devices are supported.
diff --git a/Documentation/networking/netdev-features.rst b/Documentation/networking/netdev-features.rst
new file mode 100644 (file)
index 0000000..a2d7d71
--- /dev/null
@@ -0,0 +1,184 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================================================
+Netdev features mess and how to get out from it alive
+=====================================================
+
+Author:
+       Michał Mirosław <mirq-linux@rere.qmqm.pl>
+
+
+
+Part I: Feature sets
+====================
+
+Long gone are the days when a network card would just take and give packets
+verbatim.  Today's devices add multiple features and bugs (read: offloads)
+that relieve an OS of various tasks like generating and checking checksums,
+splitting packets, classifying them.  Those capabilities and their state
+are commonly referred to as netdev features in Linux kernel world.
+
+There are currently three sets of features relevant to the driver, and
+one used internally by network core:
+
+ 1. netdev->hw_features set contains features whose state may possibly
+    be changed (enabled or disabled) for a particular device by user's
+    request.  This set should be initialized in ndo_init callback and not
+    changed later.
+
+ 2. netdev->features set contains features which are currently enabled
+    for a device.  This should be changed only by network core or in
+    error paths of ndo_set_features callback.
+
+ 3. netdev->vlan_features set contains features whose state is inherited
+    by child VLAN devices (limits netdev->features set).  This is currently
+    used for all VLAN devices whether tags are stripped or inserted in
+    hardware or software.
+
+ 4. netdev->wanted_features set contains feature set requested by user.
+    This set is filtered by ndo_fix_features callback whenever it or
+    some device-specific conditions change. This set is internal to
+    networking core and should not be referenced in drivers.
+
+
+
+Part II: Controlling enabled features
+=====================================
+
+When current feature set (netdev->features) is to be changed, new set
+is calculated and filtered by calling ndo_fix_features callback
+and netdev_fix_features(). If the resulting set differs from current
+set, it is passed to ndo_set_features callback and (if the callback
+returns success) replaces value stored in netdev->features.
+NETDEV_FEAT_CHANGE notification is issued after that whenever current
+set might have changed.
+
+The following events trigger recalculation:
+ 1. device's registration, after ndo_init returned success
+ 2. user requested changes in features state
+ 3. netdev_update_features() is called
+
+ndo_*_features callbacks are called with rtnl_lock held. Missing callbacks
+are treated as always returning success.
+
+A driver that wants to trigger recalculation must do so by calling
+netdev_update_features() while holding rtnl_lock. This should not be done
+from ndo_*_features callbacks. netdev->features should not be modified by
+driver except by means of ndo_fix_features callback.
+
+
+
+Part III: Implementation hints
+==============================
+
+ * ndo_fix_features:
+
+All dependencies between features should be resolved here. The resulting
+set can be reduced further by networking core imposed limitations (as coded
+in netdev_fix_features()). For this reason it is safer to disable a feature
+when its dependencies are not met instead of forcing the dependency on.
+
+This callback should not modify hardware nor driver state (should be
+stateless).  It can be called multiple times between successive
+ndo_set_features calls.
+
+Callback must not alter features contained in NETIF_F_SOFT_FEATURES or
+NETIF_F_NEVER_CHANGE sets. The exception is NETIF_F_VLAN_CHALLENGED but
+care must be taken as the change won't affect already configured VLANs.
+
+ * ndo_set_features:
+
+Hardware should be reconfigured to match passed feature set. The set
+should not be altered unless some error condition happens that can't
+be reliably detected in ndo_fix_features. In this case, the callback
+should update netdev->features to match resulting hardware state.
+Errors returned are not (and cannot be) propagated anywhere except dmesg.
+(Note: successful return is zero, >0 means silent error.)
+
+
+
+Part IV: Features
+=================
+
+For current list of features, see include/linux/netdev_features.h.
+This section describes semantics of some of them.
+
+ * Transmit checksumming
+
+For complete description, see comments near the top of include/linux/skbuff.h.
+
+Note: NETIF_F_HW_CSUM is a superset of NETIF_F_IP_CSUM + NETIF_F_IPV6_CSUM.
+It means that device can fill TCP/UDP-like checksum anywhere in the packets
+whatever headers there might be.
+
+ * Transmit TCP segmentation offload
+
+NETIF_F_TSO_ECN means that hardware can properly split packets with CWR bit
+set, be it TCPv4 (when NETIF_F_TSO is enabled) or TCPv6 (NETIF_F_TSO6).
+
+ * Transmit UDP segmentation offload
+
+NETIF_F_GSO_UDP_L4 accepts a single UDP header with a payload that exceeds
+gso_size. On segmentation, it segments the payload on gso_size boundaries and
+replicates the network and UDP headers (fixing up the last one if less than
+gso_size).
+
+ * Transmit DMA from high memory
+
+On platforms where this is relevant, NETIF_F_HIGHDMA signals that
+ndo_start_xmit can handle skbs with frags in high memory.
+
+ * Transmit scatter-gather
+
+Those features say that ndo_start_xmit can handle fragmented skbs:
+NETIF_F_SG --- paged skbs (skb_shinfo()->frags), NETIF_F_FRAGLIST ---
+chained skbs (skb->next/prev list).
+
+ * Software features
+
+Features contained in NETIF_F_SOFT_FEATURES are features of networking
+stack. Driver should not change behaviour based on them.
+
+ * LLTX driver (deprecated for hardware drivers)
+
+NETIF_F_LLTX is meant to be used by drivers that don't need locking at all,
+e.g. software tunnels.
+
+This is also used in a few legacy drivers that implement their
+own locking, don't use it for new (hardware) drivers.
+
+ * netns-local device
+
+NETIF_F_NETNS_LOCAL is set for devices that are not allowed to move between
+network namespaces (e.g. loopback).
+
+Don't use it in drivers.
+
+ * VLAN challenged
+
+NETIF_F_VLAN_CHALLENGED should be set for devices which can't cope with VLAN
+headers. Some drivers set this because the cards can't handle the bigger MTU.
+[FIXME: Those cases could be fixed in VLAN code by allowing only reduced-MTU
+VLANs. This may be not useful, though.]
+
+*  rx-fcs
+
+This requests that the NIC append the Ethernet Frame Checksum (FCS)
+to the end of the skb data.  This allows sniffers and other tools to
+read the CRC recorded by the NIC on receipt of the packet.
+
+*  rx-all
+
+This requests that the NIC receive all possible frames, including errored
+frames (such as bad FCS, etc).  This can be helpful when sniffing a link with
+bad packets on it.  Some NICs may receive more packets if also put into normal
+PROMISC mode.
+
+*  rx-gro-hw
+
+This requests that the NIC enables Hardware GRO (generic receive offload).
+Hardware GRO is basically the exact reverse of TSO, and is generally
+stricter than Hardware LRO.  A packet stream merged by Hardware GRO must
+be re-segmentable by GSO or TSO back to the exact original packet stream.
+Hardware GRO is dependent on RXCSUM since every packet successfully merged
+by hardware must also have the checksum verified by hardware.
diff --git a/Documentation/networking/netdev-features.txt b/Documentation/networking/netdev-features.txt
deleted file mode 100644 (file)
index 58dd1c1..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-Netdev features mess and how to get out from it alive
-=====================================================
-
-Author:
-       Michał Mirosław <mirq-linux@rere.qmqm.pl>
-
-
-
- Part I: Feature sets
-======================
-
-Long gone are the days when a network card would just take and give packets
-verbatim.  Today's devices add multiple features and bugs (read: offloads)
-that relieve an OS of various tasks like generating and checking checksums,
-splitting packets, classifying them.  Those capabilities and their state
-are commonly referred to as netdev features in Linux kernel world.
-
-There are currently three sets of features relevant to the driver, and
-one used internally by network core:
-
- 1. netdev->hw_features set contains features whose state may possibly
-    be changed (enabled or disabled) for a particular device by user's
-    request.  This set should be initialized in ndo_init callback and not
-    changed later.
-
- 2. netdev->features set contains features which are currently enabled
-    for a device.  This should be changed only by network core or in
-    error paths of ndo_set_features callback.
-
- 3. netdev->vlan_features set contains features whose state is inherited
-    by child VLAN devices (limits netdev->features set).  This is currently
-    used for all VLAN devices whether tags are stripped or inserted in
-    hardware or software.
-
- 4. netdev->wanted_features set contains feature set requested by user.
-    This set is filtered by ndo_fix_features callback whenever it or
-    some device-specific conditions change. This set is internal to
-    networking core and should not be referenced in drivers.
-
-
-
- Part II: Controlling enabled features
-=======================================
-
-When current feature set (netdev->features) is to be changed, new set
-is calculated and filtered by calling ndo_fix_features callback
-and netdev_fix_features(). If the resulting set differs from current
-set, it is passed to ndo_set_features callback and (if the callback
-returns success) replaces value stored in netdev->features.
-NETDEV_FEAT_CHANGE notification is issued after that whenever current
-set might have changed.
-
-The following events trigger recalculation:
- 1. device's registration, after ndo_init returned success
- 2. user requested changes in features state
- 3. netdev_update_features() is called
-
-ndo_*_features callbacks are called with rtnl_lock held. Missing callbacks
-are treated as always returning success.
-
-A driver that wants to trigger recalculation must do so by calling
-netdev_update_features() while holding rtnl_lock. This should not be done
-from ndo_*_features callbacks. netdev->features should not be modified by
-driver except by means of ndo_fix_features callback.
-
-
-
- Part III: Implementation hints
-================================
-
- * ndo_fix_features:
-
-All dependencies between features should be resolved here. The resulting
-set can be reduced further by networking core imposed limitations (as coded
-in netdev_fix_features()). For this reason it is safer to disable a feature
-when its dependencies are not met instead of forcing the dependency on.
-
-This callback should not modify hardware nor driver state (should be
-stateless).  It can be called multiple times between successive
-ndo_set_features calls.
-
-Callback must not alter features contained in NETIF_F_SOFT_FEATURES or
-NETIF_F_NEVER_CHANGE sets. The exception is NETIF_F_VLAN_CHALLENGED but
-care must be taken as the change won't affect already configured VLANs.
-
- * ndo_set_features:
-
-Hardware should be reconfigured to match passed feature set. The set
-should not be altered unless some error condition happens that can't
-be reliably detected in ndo_fix_features. In this case, the callback
-should update netdev->features to match resulting hardware state.
-Errors returned are not (and cannot be) propagated anywhere except dmesg.
-(Note: successful return is zero, >0 means silent error.)
-
-
-
- Part IV: Features
-===================
-
-For current list of features, see include/linux/netdev_features.h.
-This section describes semantics of some of them.
-
- * Transmit checksumming
-
-For complete description, see comments near the top of include/linux/skbuff.h.
-
-Note: NETIF_F_HW_CSUM is a superset of NETIF_F_IP_CSUM + NETIF_F_IPV6_CSUM.
-It means that device can fill TCP/UDP-like checksum anywhere in the packets
-whatever headers there might be.
-
- * Transmit TCP segmentation offload
-
-NETIF_F_TSO_ECN means that hardware can properly split packets with CWR bit
-set, be it TCPv4 (when NETIF_F_TSO is enabled) or TCPv6 (NETIF_F_TSO6).
-
- * Transmit UDP segmentation offload
-
-NETIF_F_GSO_UDP_L4 accepts a single UDP header with a payload that exceeds
-gso_size. On segmentation, it segments the payload on gso_size boundaries and
-replicates the network and UDP headers (fixing up the last one if less than
-gso_size).
-
- * Transmit DMA from high memory
-
-On platforms where this is relevant, NETIF_F_HIGHDMA signals that
-ndo_start_xmit can handle skbs with frags in high memory.
-
- * Transmit scatter-gather
-
-Those features say that ndo_start_xmit can handle fragmented skbs:
-NETIF_F_SG --- paged skbs (skb_shinfo()->frags), NETIF_F_FRAGLIST ---
-chained skbs (skb->next/prev list).
-
- * Software features
-
-Features contained in NETIF_F_SOFT_FEATURES are features of networking
-stack. Driver should not change behaviour based on them.
-
- * LLTX driver (deprecated for hardware drivers)
-
-NETIF_F_LLTX is meant to be used by drivers that don't need locking at all,
-e.g. software tunnels.
-
-This is also used in a few legacy drivers that implement their
-own locking, don't use it for new (hardware) drivers.
-
- * netns-local device
-
-NETIF_F_NETNS_LOCAL is set for devices that are not allowed to move between
-network namespaces (e.g. loopback).
-
-Don't use it in drivers.
-
- * VLAN challenged
-
-NETIF_F_VLAN_CHALLENGED should be set for devices which can't cope with VLAN
-headers. Some drivers set this because the cards can't handle the bigger MTU.
-[FIXME: Those cases could be fixed in VLAN code by allowing only reduced-MTU
-VLANs. This may be not useful, though.]
-
-*  rx-fcs
-
-This requests that the NIC append the Ethernet Frame Checksum (FCS)
-to the end of the skb data.  This allows sniffers and other tools to
-read the CRC recorded by the NIC on receipt of the packet.
-
-*  rx-all
-
-This requests that the NIC receive all possible frames, including errored
-frames (such as bad FCS, etc).  This can be helpful when sniffing a link with
-bad packets on it.  Some NICs may receive more packets if also put into normal
-PROMISC mode.
-
-*  rx-gro-hw
-
-This requests that the NIC enables Hardware GRO (generic receive offload).
-Hardware GRO is basically the exact reverse of TSO, and is generally
-stricter than Hardware LRO.  A packet stream merged by Hardware GRO must
-be re-segmentable by GSO or TSO back to the exact original packet stream.
-Hardware GRO is dependent on RXCSUM since every packet successfully merged
-by hardware must also have the checksum verified by hardware.
diff --git a/Documentation/networking/netdevices.rst b/Documentation/networking/netdevices.rst
new file mode 100644 (file)
index 0000000..5a85fcc
--- /dev/null
@@ -0,0 +1,111 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================================
+Network Devices, the Kernel, and You!
+=====================================
+
+
+Introduction
+============
+The following is a random collection of documentation regarding
+network devices.
+
+struct net_device allocation rules
+==================================
+Network device structures need to persist even after module is unloaded and
+must be allocated with alloc_netdev_mqs() and friends.
+If device has registered successfully, it will be freed on last use
+by free_netdev(). This is required to handle the pathologic case cleanly
+(example: rmmod mydriver </sys/class/net/myeth/mtu )
+
+alloc_netdev_mqs()/alloc_netdev() reserve extra space for driver
+private data which gets freed when the network device is freed. If
+separately allocated data is attached to the network device
+(netdev_priv(dev)) then it is up to the module exit handler to free that.
+
+MTU
+===
+Each network device has a Maximum Transfer Unit. The MTU does not
+include any link layer protocol overhead. Upper layer protocols must
+not pass a socket buffer (skb) to a device to transmit with more data
+than the mtu. The MTU does not include link layer header overhead, so
+for example on Ethernet if the standard MTU is 1500 bytes used, the
+actual skb will contain up to 1514 bytes because of the Ethernet
+header. Devices should allow for the 4 byte VLAN header as well.
+
+Segmentation Offload (GSO, TSO) is an exception to this rule.  The
+upper layer protocol may pass a large socket buffer to the device
+transmit routine, and the device will break that up into separate
+packets based on the current MTU.
+
+MTU is symmetrical and applies both to receive and transmit. A device
+must be able to receive at least the maximum size packet allowed by
+the MTU. A network device may use the MTU as mechanism to size receive
+buffers, but the device should allow packets with VLAN header. With
+standard Ethernet mtu of 1500 bytes, the device should allow up to
+1518 byte packets (1500 + 14 header + 4 tag).  The device may either:
+drop, truncate, or pass up oversize packets, but dropping oversize
+packets is preferred.
+
+
+struct net_device synchronization rules
+=======================================
+ndo_open:
+       Synchronization: rtnl_lock() semaphore.
+       Context: process
+
+ndo_stop:
+       Synchronization: rtnl_lock() semaphore.
+       Context: process
+       Note: netif_running() is guaranteed false
+
+ndo_do_ioctl:
+       Synchronization: rtnl_lock() semaphore.
+       Context: process
+
+ndo_get_stats:
+       Synchronization: dev_base_lock rwlock.
+       Context: nominally process, but don't sleep inside an rwlock
+
+ndo_start_xmit:
+       Synchronization: __netif_tx_lock spinlock.
+
+       When the driver sets NETIF_F_LLTX in dev->features this will be
+       called without holding netif_tx_lock. In this case the driver
+       has to lock by itself when needed.
+       The locking there should also properly protect against
+       set_rx_mode. WARNING: use of NETIF_F_LLTX is deprecated.
+       Don't use it for new drivers.
+
+       Context: Process with BHs disabled or BH (timer),
+                will be called with interrupts disabled by netconsole.
+
+       Return codes:
+
+       * NETDEV_TX_OK everything ok.
+       * NETDEV_TX_BUSY Cannot transmit packet, try later
+         Usually a bug, means queue start/stop flow control is broken in
+         the driver. Note: the driver must NOT put the skb in its DMA ring.
+
+ndo_tx_timeout:
+       Synchronization: netif_tx_lock spinlock; all TX queues frozen.
+       Context: BHs disabled
+       Notes: netif_queue_stopped() is guaranteed true
+
+ndo_set_rx_mode:
+       Synchronization: netif_addr_lock spinlock.
+       Context: BHs disabled
+
+struct napi_struct synchronization rules
+========================================
+napi->poll:
+       Synchronization:
+               NAPI_STATE_SCHED bit in napi->state.  Device
+               driver's ndo_stop method will invoke napi_disable() on
+               all NAPI instances which will do a sleeping poll on the
+               NAPI_STATE_SCHED napi->state bit, waiting for all pending
+               NAPI activity to cease.
+
+       Context:
+                softirq
+                will be called with interrupts disabled by netconsole.
diff --git a/Documentation/networking/netdevices.txt b/Documentation/networking/netdevices.txt
deleted file mode 100644 (file)
index 7fec206..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-
-Network Devices, the Kernel, and You!
-
-
-Introduction
-============
-The following is a random collection of documentation regarding
-network devices.
-
-struct net_device allocation rules
-==================================
-Network device structures need to persist even after module is unloaded and
-must be allocated with alloc_netdev_mqs() and friends.
-If device has registered successfully, it will be freed on last use
-by free_netdev(). This is required to handle the pathologic case cleanly
-(example: rmmod mydriver </sys/class/net/myeth/mtu )
-
-alloc_netdev_mqs()/alloc_netdev() reserve extra space for driver
-private data which gets freed when the network device is freed. If
-separately allocated data is attached to the network device
-(netdev_priv(dev)) then it is up to the module exit handler to free that.
-
-MTU
-===
-Each network device has a Maximum Transfer Unit. The MTU does not
-include any link layer protocol overhead. Upper layer protocols must
-not pass a socket buffer (skb) to a device to transmit with more data
-than the mtu. The MTU does not include link layer header overhead, so
-for example on Ethernet if the standard MTU is 1500 bytes used, the
-actual skb will contain up to 1514 bytes because of the Ethernet
-header. Devices should allow for the 4 byte VLAN header as well.
-
-Segmentation Offload (GSO, TSO) is an exception to this rule.  The
-upper layer protocol may pass a large socket buffer to the device
-transmit routine, and the device will break that up into separate
-packets based on the current MTU.
-
-MTU is symmetrical and applies both to receive and transmit. A device
-must be able to receive at least the maximum size packet allowed by
-the MTU. A network device may use the MTU as mechanism to size receive
-buffers, but the device should allow packets with VLAN header. With
-standard Ethernet mtu of 1500 bytes, the device should allow up to
-1518 byte packets (1500 + 14 header + 4 tag).  The device may either:
-drop, truncate, or pass up oversize packets, but dropping oversize
-packets is preferred.
-
-
-struct net_device synchronization rules
-=======================================
-ndo_open:
-       Synchronization: rtnl_lock() semaphore.
-       Context: process
-
-ndo_stop:
-       Synchronization: rtnl_lock() semaphore.
-       Context: process
-       Note: netif_running() is guaranteed false
-
-ndo_do_ioctl:
-       Synchronization: rtnl_lock() semaphore.
-       Context: process
-
-ndo_get_stats:
-       Synchronization: dev_base_lock rwlock.
-       Context: nominally process, but don't sleep inside an rwlock
-
-ndo_start_xmit:
-       Synchronization: __netif_tx_lock spinlock.
-
-       When the driver sets NETIF_F_LLTX in dev->features this will be
-       called without holding netif_tx_lock. In this case the driver
-       has to lock by itself when needed.
-       The locking there should also properly protect against
-       set_rx_mode. WARNING: use of NETIF_F_LLTX is deprecated.
-       Don't use it for new drivers.
-
-       Context: Process with BHs disabled or BH (timer),
-                will be called with interrupts disabled by netconsole.
-
-       Return codes: 
-       o NETDEV_TX_OK everything ok. 
-       o NETDEV_TX_BUSY Cannot transmit packet, try later 
-         Usually a bug, means queue start/stop flow control is broken in
-         the driver. Note: the driver must NOT put the skb in its DMA ring.
-
-ndo_tx_timeout:
-       Synchronization: netif_tx_lock spinlock; all TX queues frozen.
-       Context: BHs disabled
-       Notes: netif_queue_stopped() is guaranteed true
-
-ndo_set_rx_mode:
-       Synchronization: netif_addr_lock spinlock.
-       Context: BHs disabled
-
-struct napi_struct synchronization rules
-========================================
-napi->poll:
-       Synchronization: NAPI_STATE_SCHED bit in napi->state.  Device
-               driver's ndo_stop method will invoke napi_disable() on
-               all NAPI instances which will do a sleeping poll on the
-               NAPI_STATE_SCHED napi->state bit, waiting for all pending
-               NAPI activity to cease.
-       Context: softirq
-                will be called with interrupts disabled by netconsole.
diff --git a/Documentation/networking/netfilter-sysctl.rst b/Documentation/networking/netfilter-sysctl.rst
new file mode 100644 (file)
index 0000000..beb6d7b
--- /dev/null
@@ -0,0 +1,17 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================
+Netfilter Sysfs variables
+=========================
+
+/proc/sys/net/netfilter/* Variables:
+====================================
+
+nf_log_all_netns - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       By default, only init_net namespace can log packets into kernel log
+       with LOG target; this aims to prevent containers from flooding host
+       kernel log. If enabled, this target also works in other network
+       namespaces. This variable is only accessible from init_net.
diff --git a/Documentation/networking/netfilter-sysctl.txt b/Documentation/networking/netfilter-sysctl.txt
deleted file mode 100644 (file)
index 55791e5..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-/proc/sys/net/netfilter/* Variables:
-
-nf_log_all_netns - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
-
-       By default, only init_net namespace can log packets into kernel log
-       with LOG target; this aims to prevent containers from flooding host
-       kernel log. If enabled, this target also works in other network
-       namespaces. This variable is only accessible from init_net.
diff --git a/Documentation/networking/netif-msg.rst b/Documentation/networking/netif-msg.rst
new file mode 100644 (file)
index 0000000..b20d265
--- /dev/null
@@ -0,0 +1,95 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============
+NETIF Msg Level
+===============
+
+The design of the network interface message level setting.
+
+History
+-------
+
+ The design of the debugging message interface was guided and
+ constrained by backwards compatibility previous practice.  It is useful
+ to understand the history and evolution in order to understand current
+ practice and relate it to older driver source code.
+
+ From the beginning of Linux, each network device driver has had a local
+ integer variable that controls the debug message level.  The message
+ level ranged from 0 to 7, and monotonically increased in verbosity.
+
+ The message level was not precisely defined past level 3, but were
+ always implemented within +-1 of the specified level.  Drivers tended
+ to shed the more verbose level messages as they matured.
+
+   - 0  Minimal messages, only essential information on fatal errors.
+   - 1  Standard messages, initialization status.  No run-time messages
+   - 2  Special media selection messages, generally timer-driver.
+   - 3  Interface starts and stops, including normal status messages
+   - 4  Tx and Rx frame error messages, and abnormal driver operation
+   - 5  Tx packet queue information, interrupt events.
+   - 6  Status on each completed Tx packet and received Rx packets
+   - 7  Initial contents of Tx and Rx packets
+
+ Initially this message level variable was uniquely named in each driver
+ e.g. "lance_debug", so that a kernel symbolic debugger could locate and
+ modify the setting.  When kernel modules became common, the variables
+ were consistently renamed to "debug" and allowed to be set as a module
+ parameter.
+
+ This approach worked well.  However there is always a demand for
+ additional features.  Over the years the following emerged as
+ reasonable and easily implemented enhancements
+
+   - Using an ioctl() call to modify the level.
+   - Per-interface rather than per-driver message level setting.
+   - More selective control over the type of messages emitted.
+
+ The netif_msg recommendation adds these features with only a minor
+ complexity and code size increase.
+
+ The recommendation is the following points
+
+  - Retaining the per-driver integer variable "debug" as a module
+    parameter with a default level of '1'.
+
+  - Adding a per-interface private variable named "msg_enable".  The
+    variable is a bit map rather than a level, and is initialized as::
+
+       1 << debug
+
+    Or more precisely::
+
+       debug < 0 ? 0 : 1 << min(sizeof(int)-1, debug)
+
+    Messages should changes from::
+
+      if (debug > 1)
+          printk(MSG_DEBUG "%s: ...
+
+    to::
+
+      if (np->msg_enable & NETIF_MSG_LINK)
+          printk(MSG_DEBUG "%s: ...
+
+
+The set of message levels is named
+
+
+  =========   ===================      ============
+  Old level   Name                     Bit position
+  =========   ===================      ============
+    0         NETIF_MSG_DRV            0x0001
+    1         NETIF_MSG_PROBE          0x0002
+    2         NETIF_MSG_LINK           0x0004
+    2         NETIF_MSG_TIMER          0x0004
+    3         NETIF_MSG_IFDOWN         0x0008
+    3         NETIF_MSG_IFUP           0x0008
+    4         NETIF_MSG_RX_ERR         0x0010
+    4         NETIF_MSG_TX_ERR         0x0010
+    5         NETIF_MSG_TX_QUEUED      0x0020
+    5         NETIF_MSG_INTR           0x0020
+    6         NETIF_MSG_TX_DONE                0x0040
+    6         NETIF_MSG_RX_STATUS      0x0040
+    7         NETIF_MSG_PKTDATA                0x0080
+  =========   ===================      ============
diff --git a/Documentation/networking/netif-msg.txt b/Documentation/networking/netif-msg.txt
deleted file mode 100644 (file)
index c967ddb..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-
-________________
-NETIF Msg Level
-
-The design of the network interface message level setting.
-
-History
-
- The design of the debugging message interface was guided and
- constrained by backwards compatibility previous practice.  It is useful
- to understand the history and evolution in order to understand current
- practice and relate it to older driver source code.
-
- From the beginning of Linux, each network device driver has had a local
- integer variable that controls the debug message level.  The message
- level ranged from 0 to 7, and monotonically increased in verbosity.
-
- The message level was not precisely defined past level 3, but were
- always implemented within +-1 of the specified level.  Drivers tended
- to shed the more verbose level messages as they matured.
-    0  Minimal messages, only essential information on fatal errors.
-    1  Standard messages, initialization status.  No run-time messages
-    2  Special media selection messages, generally timer-driver.
-    3  Interface starts and stops, including normal status messages
-    4  Tx and Rx frame error messages, and abnormal driver operation
-    5  Tx packet queue information, interrupt events.
-    6  Status on each completed Tx packet and received Rx packets
-    7  Initial contents of Tx and Rx packets
-
- Initially this message level variable was uniquely named in each driver
- e.g. "lance_debug", so that a kernel symbolic debugger could locate and
- modify the setting.  When kernel modules became common, the variables
- were consistently renamed to "debug" and allowed to be set as a module
- parameter.
-
- This approach worked well.  However there is always a demand for
- additional features.  Over the years the following emerged as
- reasonable and easily implemented enhancements
-   Using an ioctl() call to modify the level.
-   Per-interface rather than per-driver message level setting.
-   More selective control over the type of messages emitted.
-
- The netif_msg recommendation adds these features with only a minor
- complexity and code size increase.
-
- The recommendation is the following points
-    Retaining the per-driver integer variable "debug" as a module
-    parameter with a default level of '1'.
-
-    Adding a per-interface private variable named "msg_enable".  The
-    variable is a bit map rather than a level, and is initialized as
-       1 << debug
-    Or more precisely
-        debug < 0 ? 0 : 1 << min(sizeof(int)-1, debug)
-
-    Messages should changes from
-      if (debug > 1)
-           printk(MSG_DEBUG "%s: ...
-    to
-      if (np->msg_enable & NETIF_MSG_LINK)
-           printk(MSG_DEBUG "%s: ...
-
-
-The set of message levels is named
-  Old level   Name   Bit position
-    0    NETIF_MSG_DRV         0x0001
-    1    NETIF_MSG_PROBE       0x0002
-    2    NETIF_MSG_LINK                0x0004
-    2    NETIF_MSG_TIMER       0x0004
-    3    NETIF_MSG_IFDOWN      0x0008
-    3    NETIF_MSG_IFUP                0x0008
-    4    NETIF_MSG_RX_ERR      0x0010
-    4    NETIF_MSG_TX_ERR      0x0010
-    5    NETIF_MSG_TX_QUEUED   0x0020
-    5    NETIF_MSG_INTR                0x0020
-    6    NETIF_MSG_TX_DONE     0x0040
-    6    NETIF_MSG_RX_STATUS   0x0040
-    7    NETIF_MSG_PKTDATA     0x0080
-
diff --git a/Documentation/networking/nf_conntrack-sysctl.rst b/Documentation/networking/nf_conntrack-sysctl.rst
new file mode 100644 (file)
index 0000000..11a9b76
--- /dev/null
@@ -0,0 +1,179 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================================
+Netfilter Conntrack Sysfs variables
+===================================
+
+/proc/sys/net/netfilter/nf_conntrack_* Variables:
+=================================================
+
+nf_conntrack_acct - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       Enable connection tracking flow accounting. 64-bit byte and packet
+       counters per flow are added.
+
+nf_conntrack_buckets - INTEGER
+       Size of hash table. If not specified as parameter during module
+       loading, the default size is calculated by dividing total memory
+       by 16384 to determine the number of buckets but the hash table will
+       never have fewer than 32 and limited to 16384 buckets. For systems
+       with more than 4GB of memory it will be 65536 buckets.
+       This sysctl is only writeable in the initial net namespace.
+
+nf_conntrack_checksum - BOOLEAN
+       - 0 - disabled
+       - not 0 - enabled (default)
+
+       Verify checksum of incoming packets. Packets with bad checksums are
+       in INVALID state. If this is enabled, such packets will not be
+       considered for connection tracking.
+
+nf_conntrack_count - INTEGER (read-only)
+       Number of currently allocated flow entries.
+
+nf_conntrack_events - BOOLEAN
+       - 0 - disabled
+       - not 0 - enabled (default)
+
+       If this option is enabled, the connection tracking code will
+       provide userspace with connection tracking events via ctnetlink.
+
+nf_conntrack_expect_max - INTEGER
+       Maximum size of expectation table.  Default value is
+       nf_conntrack_buckets / 256. Minimum is 1.
+
+nf_conntrack_frag6_high_thresh - INTEGER
+       default 262144
+
+       Maximum memory used to reassemble IPv6 fragments.  When
+       nf_conntrack_frag6_high_thresh bytes of memory is allocated for this
+       purpose, the fragment handler will toss packets until
+       nf_conntrack_frag6_low_thresh is reached.
+
+nf_conntrack_frag6_low_thresh - INTEGER
+       default 196608
+
+       See nf_conntrack_frag6_low_thresh
+
+nf_conntrack_frag6_timeout - INTEGER (seconds)
+       default 60
+
+       Time to keep an IPv6 fragment in memory.
+
+nf_conntrack_generic_timeout - INTEGER (seconds)
+       default 600
+
+       Default for generic timeout.  This refers to layer 4 unknown/unsupported
+       protocols.
+
+nf_conntrack_helper - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       Enable automatic conntrack helper assignment.
+       If disabled it is required to set up iptables rules to assign
+       helpers to connections.  See the CT target description in the
+       iptables-extensions(8) man page for further information.
+
+nf_conntrack_icmp_timeout - INTEGER (seconds)
+       default 30
+
+       Default for ICMP timeout.
+
+nf_conntrack_icmpv6_timeout - INTEGER (seconds)
+       default 30
+
+       Default for ICMP6 timeout.
+
+nf_conntrack_log_invalid - INTEGER
+       - 0   - disable (default)
+       - 1   - log ICMP packets
+       - 6   - log TCP packets
+       - 17  - log UDP packets
+       - 33  - log DCCP packets
+       - 41  - log ICMPv6 packets
+       - 136 - log UDPLITE packets
+       - 255 - log packets of any protocol
+
+       Log invalid packets of a type specified by value.
+
+nf_conntrack_max - INTEGER
+       Size of connection tracking table.  Default value is
+       nf_conntrack_buckets value * 4.
+
+nf_conntrack_tcp_be_liberal - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       Be conservative in what you do, be liberal in what you accept from others.
+       If it's non-zero, we mark only out of window RST segments as INVALID.
+
+nf_conntrack_tcp_loose - BOOLEAN
+       - 0 - disabled
+       - not 0 - enabled (default)
+
+       If it is set to zero, we disable picking up already established
+       connections.
+
+nf_conntrack_tcp_max_retrans - INTEGER
+       default 3
+
+       Maximum number of packets that can be retransmitted without
+       received an (acceptable) ACK from the destination. If this number
+       is reached, a shorter timer will be started.
+
+nf_conntrack_tcp_timeout_close - INTEGER (seconds)
+       default 10
+
+nf_conntrack_tcp_timeout_close_wait - INTEGER (seconds)
+       default 60
+
+nf_conntrack_tcp_timeout_established - INTEGER (seconds)
+       default 432000 (5 days)
+
+nf_conntrack_tcp_timeout_fin_wait - INTEGER (seconds)
+       default 120
+
+nf_conntrack_tcp_timeout_last_ack - INTEGER (seconds)
+       default 30
+
+nf_conntrack_tcp_timeout_max_retrans - INTEGER (seconds)
+       default 300
+
+nf_conntrack_tcp_timeout_syn_recv - INTEGER (seconds)
+       default 60
+
+nf_conntrack_tcp_timeout_syn_sent - INTEGER (seconds)
+       default 120
+
+nf_conntrack_tcp_timeout_time_wait - INTEGER (seconds)
+       default 120
+
+nf_conntrack_tcp_timeout_unacknowledged - INTEGER (seconds)
+       default 300
+
+nf_conntrack_timestamp - BOOLEAN
+       - 0 - disabled (default)
+       - not 0 - enabled
+
+       Enable connection tracking flow timestamping.
+
+nf_conntrack_udp_timeout - INTEGER (seconds)
+       default 30
+
+nf_conntrack_udp_timeout_stream - INTEGER (seconds)
+       default 120
+
+       This extended timeout will be used in case there is an UDP stream
+       detected.
+
+nf_conntrack_gre_timeout - INTEGER (seconds)
+       default 30
+
+nf_conntrack_gre_timeout_stream - INTEGER (seconds)
+       default 180
+
+       This extended timeout will be used in case there is an GRE stream
+       detected.
diff --git a/Documentation/networking/nf_conntrack-sysctl.txt b/Documentation/networking/nf_conntrack-sysctl.txt
deleted file mode 100644 (file)
index f75c2ce..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-/proc/sys/net/netfilter/nf_conntrack_* Variables:
-
-nf_conntrack_acct - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
-
-       Enable connection tracking flow accounting. 64-bit byte and packet
-       counters per flow are added.
-
-nf_conntrack_buckets - INTEGER
-       Size of hash table. If not specified as parameter during module
-       loading, the default size is calculated by dividing total memory
-       by 16384 to determine the number of buckets but the hash table will
-       never have fewer than 32 and limited to 16384 buckets. For systems
-       with more than 4GB of memory it will be 65536 buckets.
-       This sysctl is only writeable in the initial net namespace.
-
-nf_conntrack_checksum - BOOLEAN
-       0 - disabled
-       not 0 - enabled (default)
-
-       Verify checksum of incoming packets. Packets with bad checksums are
-       in INVALID state. If this is enabled, such packets will not be
-       considered for connection tracking.
-
-nf_conntrack_count - INTEGER (read-only)
-       Number of currently allocated flow entries.
-
-nf_conntrack_events - BOOLEAN
-       0 - disabled
-       not 0 - enabled (default)
-
-       If this option is enabled, the connection tracking code will
-       provide userspace with connection tracking events via ctnetlink.
-
-nf_conntrack_expect_max - INTEGER
-       Maximum size of expectation table.  Default value is
-       nf_conntrack_buckets / 256. Minimum is 1.
-
-nf_conntrack_frag6_high_thresh - INTEGER
-       default 262144
-
-       Maximum memory used to reassemble IPv6 fragments.  When
-       nf_conntrack_frag6_high_thresh bytes of memory is allocated for this
-       purpose, the fragment handler will toss packets until
-       nf_conntrack_frag6_low_thresh is reached.
-
-nf_conntrack_frag6_low_thresh - INTEGER
-       default 196608
-
-       See nf_conntrack_frag6_low_thresh
-
-nf_conntrack_frag6_timeout - INTEGER (seconds)
-       default 60
-
-       Time to keep an IPv6 fragment in memory.
-
-nf_conntrack_generic_timeout - INTEGER (seconds)
-       default 600
-
-       Default for generic timeout.  This refers to layer 4 unknown/unsupported
-       protocols.
-
-nf_conntrack_helper - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
-
-       Enable automatic conntrack helper assignment.
-       If disabled it is required to set up iptables rules to assign
-       helpers to connections.  See the CT target description in the
-       iptables-extensions(8) man page for further information.
-
-nf_conntrack_icmp_timeout - INTEGER (seconds)
-       default 30
-
-       Default for ICMP timeout.
-
-nf_conntrack_icmpv6_timeout - INTEGER (seconds)
-       default 30
-
-       Default for ICMP6 timeout.
-
-nf_conntrack_log_invalid - INTEGER
-       0   - disable (default)
-       1   - log ICMP packets
-       6   - log TCP packets
-       17  - log UDP packets
-       33  - log DCCP packets
-       41  - log ICMPv6 packets
-       136 - log UDPLITE packets
-       255 - log packets of any protocol
-
-       Log invalid packets of a type specified by value.
-
-nf_conntrack_max - INTEGER
-       Size of connection tracking table.  Default value is
-       nf_conntrack_buckets value * 4.
-
-nf_conntrack_tcp_be_liberal - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
-
-       Be conservative in what you do, be liberal in what you accept from others.
-       If it's non-zero, we mark only out of window RST segments as INVALID.
-
-nf_conntrack_tcp_loose - BOOLEAN
-       0 - disabled
-       not 0 - enabled (default)
-
-       If it is set to zero, we disable picking up already established
-       connections.
-
-nf_conntrack_tcp_max_retrans - INTEGER
-       default 3
-
-       Maximum number of packets that can be retransmitted without
-       received an (acceptable) ACK from the destination. If this number
-       is reached, a shorter timer will be started.
-
-nf_conntrack_tcp_timeout_close - INTEGER (seconds)
-       default 10
-
-nf_conntrack_tcp_timeout_close_wait - INTEGER (seconds)
-       default 60
-
-nf_conntrack_tcp_timeout_established - INTEGER (seconds)
-       default 432000 (5 days)
-
-nf_conntrack_tcp_timeout_fin_wait - INTEGER (seconds)
-       default 120
-
-nf_conntrack_tcp_timeout_last_ack - INTEGER (seconds)
-       default 30
-
-nf_conntrack_tcp_timeout_max_retrans - INTEGER (seconds)
-       default 300
-
-nf_conntrack_tcp_timeout_syn_recv - INTEGER (seconds)
-       default 60
-
-nf_conntrack_tcp_timeout_syn_sent - INTEGER (seconds)
-       default 120
-
-nf_conntrack_tcp_timeout_time_wait - INTEGER (seconds)
-       default 120
-
-nf_conntrack_tcp_timeout_unacknowledged - INTEGER (seconds)
-       default 300
-
-nf_conntrack_timestamp - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
-
-       Enable connection tracking flow timestamping.
-
-nf_conntrack_udp_timeout - INTEGER (seconds)
-       default 30
-
-nf_conntrack_udp_timeout_stream - INTEGER (seconds)
-       default 120
-
-       This extended timeout will be used in case there is an UDP stream
-       detected.
-
-nf_conntrack_gre_timeout - INTEGER (seconds)
-       default 30
-
-nf_conntrack_gre_timeout_stream - INTEGER (seconds)
-       default 180
-
-       This extended timeout will be used in case there is an GRE stream
-       detected.
diff --git a/Documentation/networking/nf_flowtable.rst b/Documentation/networking/nf_flowtable.rst
new file mode 100644 (file)
index 0000000..b6e1fa1
--- /dev/null
@@ -0,0 +1,117 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====================================
+Netfilter's flowtable infrastructure
+====================================
+
+This documentation describes the software flowtable infrastructure available in
+Netfilter since Linux kernel 4.16.
+
+Overview
+--------
+
+Initial packets follow the classic forwarding path, once the flow enters the
+established state according to the conntrack semantics (ie. we have seen traffic
+in both directions), then you can decide to offload the flow to the flowtable
+from the forward chain via the 'flow offload' action available in nftables.
+
+Packets that find an entry in the flowtable (ie. flowtable hit) are sent to the
+output netdevice via neigh_xmit(), hence, they bypass the classic forwarding
+path (the visible effect is that you do not see these packets from any of the
+netfilter hooks coming after the ingress). In case of flowtable miss, the packet
+follows the classic forward path.
+
+The flowtable uses a resizable hashtable, lookups are based on the following
+7-tuple selectors: source, destination, layer 3 and layer 4 protocols, source
+and destination ports and the input interface (useful in case there are several
+conntrack zones in place).
+
+Flowtables are populated via the 'flow offload' nftables action, so the user can
+selectively specify what flows are placed into the flow table. Hence, packets
+follow the classic forwarding path unless the user explicitly instruct packets
+to use this new alternative forwarding path via nftables policy.
+
+This is represented in Fig.1, which describes the classic forwarding path
+including the Netfilter hooks and the flowtable fastpath bypass.
+
+::
+
+                                        userspace process
+                                         ^              |
+                                         |              |
+                                    _____|____     ____\/___
+                                   /          \   /         \
+                                   |   input   |  |  output  |
+                                   \__________/   \_________/
+                                        ^               |
+                                        |               |
+      _________      __________      ---------     _____\/_____
+     /         \    /          \     |Routing |   /            \
+  -->  ingress  ---> prerouting ---> |decision|   | postrouting |--> neigh_xmit
+     \_________/    \__________/     ----------   \____________/          ^
+       |      ^                          |               ^                |
+   flowtable  |                     ____\/___            |                |
+       |      |                    /         \           |                |
+    __\/___   |                    | forward |------------                |
+    |-----|   |                    \_________/                            |
+    |-----|   |                 'flow offload' rule                       |
+    |-----|   |                   adds entry to                           |
+    |_____|   |                     flowtable                             |
+       |      |                                                           |
+      / \     |                                                           |
+     /hit\_no_|                                                           |
+     \ ? /                                                                |
+      \ /                                                                 |
+       |__yes_________________fastpath bypass ____________________________|
+
+              Fig.1 Netfilter hooks and flowtable interactions
+
+The flowtable entry also stores the NAT configuration, so all packets are
+mangled according to the NAT policy that matches the initial packets that went
+through the classic forwarding path. The TTL is decremented before calling
+neigh_xmit(). Fragmented traffic is passed up to follow the classic forwarding
+path given that the transport selectors are missing, therefore flowtable lookup
+is not possible.
+
+Example configuration
+---------------------
+
+Enabling the flowtable bypass is relatively easy, you only need to create a
+flowtable and add one rule to your forward chain::
+
+       table inet x {
+               flowtable f {
+                       hook ingress priority 0; devices = { eth0, eth1 };
+               }
+               chain y {
+                       type filter hook forward priority 0; policy accept;
+                       ip protocol tcp flow offload @f
+                       counter packets 0 bytes 0
+               }
+       }
+
+This example adds the flowtable 'f' to the ingress hook of the eth0 and eth1
+netdevices. You can create as many flowtables as you want in case you need to
+perform resource partitioning. The flowtable priority defines the order in which
+hooks are run in the pipeline, this is convenient in case you already have a
+nftables ingress chain (make sure the flowtable priority is smaller than the
+nftables ingress chain hence the flowtable runs before in the pipeline).
+
+The 'flow offload' action from the forward chain 'y' adds an entry to the
+flowtable for the TCP syn-ack packet coming in the reply direction. Once the
+flow is offloaded, you will observe that the counter rule in the example above
+does not get updated for the packets that are being forwarded through the
+forwarding bypass.
+
+More reading
+------------
+
+This documentation is based on the LWN.net articles [1]_\ [2]_. Rafal Milecki
+also made a very complete and comprehensive summary called "A state of network
+acceleration" that describes how things were before this infrastructure was
+mailined [3]_ and it also makes a rough summary of this work [4]_.
+
+.. [1] https://lwn.net/Articles/738214/
+.. [2] https://lwn.net/Articles/742164/
+.. [3] http://lists.infradead.org/pipermail/lede-dev/2018-January/010830.html
+.. [4] http://lists.infradead.org/pipermail/lede-dev/2018-January/010829.html
diff --git a/Documentation/networking/nf_flowtable.txt b/Documentation/networking/nf_flowtable.txt
deleted file mode 100644 (file)
index 0bf32d1..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-Netfilter's flowtable infrastructure
-====================================
-
-This documentation describes the software flowtable infrastructure available in
-Netfilter since Linux kernel 4.16.
-
-Overview
---------
-
-Initial packets follow the classic forwarding path, once the flow enters the
-established state according to the conntrack semantics (ie. we have seen traffic
-in both directions), then you can decide to offload the flow to the flowtable
-from the forward chain via the 'flow offload' action available in nftables.
-
-Packets that find an entry in the flowtable (ie. flowtable hit) are sent to the
-output netdevice via neigh_xmit(), hence, they bypass the classic forwarding
-path (the visible effect is that you do not see these packets from any of the
-netfilter hooks coming after the ingress). In case of flowtable miss, the packet
-follows the classic forward path.
-
-The flowtable uses a resizable hashtable, lookups are based on the following
-7-tuple selectors: source, destination, layer 3 and layer 4 protocols, source
-and destination ports and the input interface (useful in case there are several
-conntrack zones in place).
-
-Flowtables are populated via the 'flow offload' nftables action, so the user can
-selectively specify what flows are placed into the flow table. Hence, packets
-follow the classic forwarding path unless the user explicitly instruct packets
-to use this new alternative forwarding path via nftables policy.
-
-This is represented in Fig.1, which describes the classic forwarding path
-including the Netfilter hooks and the flowtable fastpath bypass.
-
-                                         userspace process
-                                          ^              |
-                                          |              |
-                                     _____|____     ____\/___
-                                    /          \   /         \
-                                    |   input   |  |  output  |
-                                    \__________/   \_________/
-                                         ^               |
-                                         |               |
-      _________      __________      ---------     _____\/_____
-     /         \    /          \     |Routing |   /            \
-  -->  ingress  ---> prerouting ---> |decision|   | postrouting |--> neigh_xmit
-     \_________/    \__________/     ----------   \____________/          ^
-       |      ^                          |               ^                |
-   flowtable  |                     ____\/___            |                |
-       |      |                    /         \           |                |
-    __\/___   |                    | forward |------------                |
-    |-----|   |                    \_________/                            |
-    |-----|   |                 'flow offload' rule                       |
-    |-----|   |                   adds entry to                           |
-    |_____|   |                     flowtable                             |
-       |      |                                                           |
-      / \     |                                                           |
-     /hit\_no_|                                                           |
-     \ ? /                                                                |
-      \ /                                                                 |
-       |__yes_________________fastpath bypass ____________________________|
-
-               Fig.1 Netfilter hooks and flowtable interactions
-
-The flowtable entry also stores the NAT configuration, so all packets are
-mangled according to the NAT policy that matches the initial packets that went
-through the classic forwarding path. The TTL is decremented before calling
-neigh_xmit(). Fragmented traffic is passed up to follow the classic forwarding
-path given that the transport selectors are missing, therefore flowtable lookup
-is not possible.
-
-Example configuration
----------------------
-
-Enabling the flowtable bypass is relatively easy, you only need to create a
-flowtable and add one rule to your forward chain.
-
-        table inet x {
-               flowtable f {
-                       hook ingress priority 0; devices = { eth0, eth1 };
-               }
-                chain y {
-                        type filter hook forward priority 0; policy accept;
-                        ip protocol tcp flow offload @f
-                        counter packets 0 bytes 0
-                }
-        }
-
-This example adds the flowtable 'f' to the ingress hook of the eth0 and eth1
-netdevices. You can create as many flowtables as you want in case you need to
-perform resource partitioning. The flowtable priority defines the order in which
-hooks are run in the pipeline, this is convenient in case you already have a
-nftables ingress chain (make sure the flowtable priority is smaller than the
-nftables ingress chain hence the flowtable runs before in the pipeline).
-
-The 'flow offload' action from the forward chain 'y' adds an entry to the
-flowtable for the TCP syn-ack packet coming in the reply direction. Once the
-flow is offloaded, you will observe that the counter rule in the example above
-does not get updated for the packets that are being forwarded through the
-forwarding bypass.
-
-More reading
-------------
-
-This documentation is based on the LWN.net articles [1][2]. Rafal Milecki also
-made a very complete and comprehensive summary called "A state of network
-acceleration" that describes how things were before this infrastructure was
-mailined [3] and it also makes a rough summary of this work [4].
-
-[1] https://lwn.net/Articles/738214/
-[2] https://lwn.net/Articles/742164/
-[3] http://lists.infradead.org/pipermail/lede-dev/2018-January/010830.html
-[4] http://lists.infradead.org/pipermail/lede-dev/2018-January/010829.html
diff --git a/Documentation/networking/openvswitch.rst b/Documentation/networking/openvswitch.rst
new file mode 100644 (file)
index 0000000..1a8353d
--- /dev/null
@@ -0,0 +1,251 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============================================
+Open vSwitch datapath developer documentation
+=============================================
+
+The Open vSwitch kernel module allows flexible userspace control over
+flow-level packet processing on selected network devices.  It can be
+used to implement a plain Ethernet switch, network device bonding,
+VLAN processing, network access control, flow-based network control,
+and so on.
+
+The kernel module implements multiple "datapaths" (analogous to
+bridges), each of which can have multiple "vports" (analogous to ports
+within a bridge).  Each datapath also has associated with it a "flow
+table" that userspace populates with "flows" that map from keys based
+on packet headers and metadata to sets of actions.  The most common
+action forwards the packet to another vport; other actions are also
+implemented.
+
+When a packet arrives on a vport, the kernel module processes it by
+extracting its flow key and looking it up in the flow table.  If there
+is a matching flow, it executes the associated actions.  If there is
+no match, it queues the packet to userspace for processing (as part of
+its processing, userspace will likely set up a flow to handle further
+packets of the same type entirely in-kernel).
+
+
+Flow key compatibility
+----------------------
+
+Network protocols evolve over time.  New protocols become important
+and existing protocols lose their prominence.  For the Open vSwitch
+kernel module to remain relevant, it must be possible for newer
+versions to parse additional protocols as part of the flow key.  It
+might even be desirable, someday, to drop support for parsing
+protocols that have become obsolete.  Therefore, the Netlink interface
+to Open vSwitch is designed to allow carefully written userspace
+applications to work with any version of the flow key, past or future.
+
+To support this forward and backward compatibility, whenever the
+kernel module passes a packet to userspace, it also passes along the
+flow key that it parsed from the packet.  Userspace then extracts its
+own notion of a flow key from the packet and compares it against the
+kernel-provided version:
+
+    - If userspace's notion of the flow key for the packet matches the
+      kernel's, then nothing special is necessary.
+
+    - If the kernel's flow key includes more fields than the userspace
+      version of the flow key, for example if the kernel decoded IPv6
+      headers but userspace stopped at the Ethernet type (because it
+      does not understand IPv6), then again nothing special is
+      necessary.  Userspace can still set up a flow in the usual way,
+      as long as it uses the kernel-provided flow key to do it.
+
+    - If the userspace flow key includes more fields than the
+      kernel's, for example if userspace decoded an IPv6 header but
+      the kernel stopped at the Ethernet type, then userspace can
+      forward the packet manually, without setting up a flow in the
+      kernel.  This case is bad for performance because every packet
+      that the kernel considers part of the flow must go to userspace,
+      but the forwarding behavior is correct.  (If userspace can
+      determine that the values of the extra fields would not affect
+      forwarding behavior, then it could set up a flow anyway.)
+
+How flow keys evolve over time is important to making this work, so
+the following sections go into detail.
+
+
+Flow key format
+---------------
+
+A flow key is passed over a Netlink socket as a sequence of Netlink
+attributes.  Some attributes represent packet metadata, defined as any
+information about a packet that cannot be extracted from the packet
+itself, e.g. the vport on which the packet was received.  Most
+attributes, however, are extracted from headers within the packet,
+e.g. source and destination addresses from Ethernet, IP, or TCP
+headers.
+
+The <linux/openvswitch.h> header file defines the exact format of the
+flow key attributes.  For informal explanatory purposes here, we write
+them as comma-separated strings, with parentheses indicating arguments
+and nesting.  For example, the following could represent a flow key
+corresponding to a TCP packet that arrived on vport 1::
+
+    in_port(1), eth(src=e0:91:f5:21:d0:b2, dst=00:02:e3:0f:80:a4),
+    eth_type(0x0800), ipv4(src=172.16.0.20, dst=172.18.0.52, proto=17, tos=0,
+    frag=no), tcp(src=49163, dst=80)
+
+Often we ellipsize arguments not important to the discussion, e.g.::
+
+    in_port(1), eth(...), eth_type(0x0800), ipv4(...), tcp(...)
+
+
+Wildcarded flow key format
+--------------------------
+
+A wildcarded flow is described with two sequences of Netlink attributes
+passed over the Netlink socket. A flow key, exactly as described above, and an
+optional corresponding flow mask.
+
+A wildcarded flow can represent a group of exact match flows. Each '1' bit
+in the mask specifies a exact match with the corresponding bit in the flow key.
+A '0' bit specifies a don't care bit, which will match either a '1' or '0' bit
+of a incoming packet. Using wildcarded flow can improve the flow set up rate
+by reduce the number of new flows need to be processed by the user space program.
+
+Support for the mask Netlink attribute is optional for both the kernel and user
+space program. The kernel can ignore the mask attribute, installing an exact
+match flow, or reduce the number of don't care bits in the kernel to less than
+what was specified by the user space program. In this case, variations in bits
+that the kernel does not implement will simply result in additional flow setups.
+The kernel module will also work with user space programs that neither support
+nor supply flow mask attributes.
+
+Since the kernel may ignore or modify wildcard bits, it can be difficult for
+the userspace program to know exactly what matches are installed. There are
+two possible approaches: reactively install flows as they miss the kernel
+flow table (and therefore not attempt to determine wildcard changes at all)
+or use the kernel's response messages to determine the installed wildcards.
+
+When interacting with userspace, the kernel should maintain the match portion
+of the key exactly as originally installed. This will provides a handle to
+identify the flow for all future operations. However, when reporting the
+mask of an installed flow, the mask should include any restrictions imposed
+by the kernel.
+
+The behavior when using overlapping wildcarded flows is undefined. It is the
+responsibility of the user space program to ensure that any incoming packet
+can match at most one flow, wildcarded or not. The current implementation
+performs best-effort detection of overlapping wildcarded flows and may reject
+some but not all of them. However, this behavior may change in future versions.
+
+
+Unique flow identifiers
+-----------------------
+
+An alternative to using the original match portion of a key as the handle for
+flow identification is a unique flow identifier, or "UFID". UFIDs are optional
+for both the kernel and user space program.
+
+User space programs that support UFID are expected to provide it during flow
+setup in addition to the flow, then refer to the flow using the UFID for all
+future operations. The kernel is not required to index flows by the original
+flow key if a UFID is specified.
+
+
+Basic rule for evolving flow keys
+---------------------------------
+
+Some care is needed to really maintain forward and backward
+compatibility for applications that follow the rules listed under
+"Flow key compatibility" above.
+
+The basic rule is obvious::
+
+    ==================================================================
+    New network protocol support must only supplement existing flow
+    key attributes.  It must not change the meaning of already defined
+    flow key attributes.
+    ==================================================================
+
+This rule does have less-obvious consequences so it is worth working
+through a few examples.  Suppose, for example, that the kernel module
+did not already implement VLAN parsing.  Instead, it just interpreted
+the 802.1Q TPID (0x8100) as the Ethertype then stopped parsing the
+packet.  The flow key for any packet with an 802.1Q header would look
+essentially like this, ignoring metadata::
+
+    eth(...), eth_type(0x8100)
+
+Naively, to add VLAN support, it makes sense to add a new "vlan" flow
+key attribute to contain the VLAN tag, then continue to decode the
+encapsulated headers beyond the VLAN tag using the existing field
+definitions.  With this change, a TCP packet in VLAN 10 would have a
+flow key much like this::
+
+    eth(...), vlan(vid=10, pcp=0), eth_type(0x0800), ip(proto=6, ...), tcp(...)
+
+But this change would negatively affect a userspace application that
+has not been updated to understand the new "vlan" flow key attribute.
+The application could, following the flow compatibility rules above,
+ignore the "vlan" attribute that it does not understand and therefore
+assume that the flow contained IP packets.  This is a bad assumption
+(the flow only contains IP packets if one parses and skips over the
+802.1Q header) and it could cause the application's behavior to change
+across kernel versions even though it follows the compatibility rules.
+
+The solution is to use a set of nested attributes.  This is, for
+example, why 802.1Q support uses nested attributes.  A TCP packet in
+VLAN 10 is actually expressed as::
+
+    eth(...), eth_type(0x8100), vlan(vid=10, pcp=0), encap(eth_type(0x0800),
+    ip(proto=6, ...), tcp(...)))
+
+Notice how the "eth_type", "ip", and "tcp" flow key attributes are
+nested inside the "encap" attribute.  Thus, an application that does
+not understand the "vlan" key will not see either of those attributes
+and therefore will not misinterpret them.  (Also, the outer eth_type
+is still 0x8100, not changed to 0x0800.)
+
+Handling malformed packets
+--------------------------
+
+Don't drop packets in the kernel for malformed protocol headers, bad
+checksums, etc.  This would prevent userspace from implementing a
+simple Ethernet switch that forwards every packet.
+
+Instead, in such a case, include an attribute with "empty" content.
+It doesn't matter if the empty content could be valid protocol values,
+as long as those values are rarely seen in practice, because userspace
+can always forward all packets with those values to userspace and
+handle them individually.
+
+For example, consider a packet that contains an IP header that
+indicates protocol 6 for TCP, but which is truncated just after the IP
+header, so that the TCP header is missing.  The flow key for this
+packet would include a tcp attribute with all-zero src and dst, like
+this::
+
+    eth(...), eth_type(0x0800), ip(proto=6, ...), tcp(src=0, dst=0)
+
+As another example, consider a packet with an Ethernet type of 0x8100,
+indicating that a VLAN TCI should follow, but which is truncated just
+after the Ethernet type.  The flow key for this packet would include
+an all-zero-bits vlan and an empty encap attribute, like this::
+
+    eth(...), eth_type(0x8100), vlan(0), encap()
+
+Unlike a TCP packet with source and destination ports 0, an
+all-zero-bits VLAN TCI is not that rare, so the CFI bit (aka
+VLAN_TAG_PRESENT inside the kernel) is ordinarily set in a vlan
+attribute expressly to allow this situation to be distinguished.
+Thus, the flow key in this second example unambiguously indicates a
+missing or malformed VLAN TCI.
+
+Other rules
+-----------
+
+The other rules for flow keys are much less subtle:
+
+    - Duplicate attributes are not allowed at a given nesting level.
+
+    - Ordering of attributes is not significant.
+
+    - When the kernel sends a given flow key to userspace, it always
+      composes it the same way.  This allows userspace to hash and
+      compare entire flow keys that it may not be able to fully
+      interpret.
diff --git a/Documentation/networking/openvswitch.txt b/Documentation/networking/openvswitch.txt
deleted file mode 100644 (file)
index b3b9ac6..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-Open vSwitch datapath developer documentation
-=============================================
-
-The Open vSwitch kernel module allows flexible userspace control over
-flow-level packet processing on selected network devices.  It can be
-used to implement a plain Ethernet switch, network device bonding,
-VLAN processing, network access control, flow-based network control,
-and so on.
-
-The kernel module implements multiple "datapaths" (analogous to
-bridges), each of which can have multiple "vports" (analogous to ports
-within a bridge).  Each datapath also has associated with it a "flow
-table" that userspace populates with "flows" that map from keys based
-on packet headers and metadata to sets of actions.  The most common
-action forwards the packet to another vport; other actions are also
-implemented.
-
-When a packet arrives on a vport, the kernel module processes it by
-extracting its flow key and looking it up in the flow table.  If there
-is a matching flow, it executes the associated actions.  If there is
-no match, it queues the packet to userspace for processing (as part of
-its processing, userspace will likely set up a flow to handle further
-packets of the same type entirely in-kernel).
-
-
-Flow key compatibility
-----------------------
-
-Network protocols evolve over time.  New protocols become important
-and existing protocols lose their prominence.  For the Open vSwitch
-kernel module to remain relevant, it must be possible for newer
-versions to parse additional protocols as part of the flow key.  It
-might even be desirable, someday, to drop support for parsing
-protocols that have become obsolete.  Therefore, the Netlink interface
-to Open vSwitch is designed to allow carefully written userspace
-applications to work with any version of the flow key, past or future.
-
-To support this forward and backward compatibility, whenever the
-kernel module passes a packet to userspace, it also passes along the
-flow key that it parsed from the packet.  Userspace then extracts its
-own notion of a flow key from the packet and compares it against the
-kernel-provided version:
-
-    - If userspace's notion of the flow key for the packet matches the
-      kernel's, then nothing special is necessary.
-
-    - If the kernel's flow key includes more fields than the userspace
-      version of the flow key, for example if the kernel decoded IPv6
-      headers but userspace stopped at the Ethernet type (because it
-      does not understand IPv6), then again nothing special is
-      necessary.  Userspace can still set up a flow in the usual way,
-      as long as it uses the kernel-provided flow key to do it.
-
-    - If the userspace flow key includes more fields than the
-      kernel's, for example if userspace decoded an IPv6 header but
-      the kernel stopped at the Ethernet type, then userspace can
-      forward the packet manually, without setting up a flow in the
-      kernel.  This case is bad for performance because every packet
-      that the kernel considers part of the flow must go to userspace,
-      but the forwarding behavior is correct.  (If userspace can
-      determine that the values of the extra fields would not affect
-      forwarding behavior, then it could set up a flow anyway.)
-
-How flow keys evolve over time is important to making this work, so
-the following sections go into detail.
-
-
-Flow key format
----------------
-
-A flow key is passed over a Netlink socket as a sequence of Netlink
-attributes.  Some attributes represent packet metadata, defined as any
-information about a packet that cannot be extracted from the packet
-itself, e.g. the vport on which the packet was received.  Most
-attributes, however, are extracted from headers within the packet,
-e.g. source and destination addresses from Ethernet, IP, or TCP
-headers.
-
-The <linux/openvswitch.h> header file defines the exact format of the
-flow key attributes.  For informal explanatory purposes here, we write
-them as comma-separated strings, with parentheses indicating arguments
-and nesting.  For example, the following could represent a flow key
-corresponding to a TCP packet that arrived on vport 1:
-
-    in_port(1), eth(src=e0:91:f5:21:d0:b2, dst=00:02:e3:0f:80:a4),
-    eth_type(0x0800), ipv4(src=172.16.0.20, dst=172.18.0.52, proto=17, tos=0,
-    frag=no), tcp(src=49163, dst=80)
-
-Often we ellipsize arguments not important to the discussion, e.g.:
-
-    in_port(1), eth(...), eth_type(0x0800), ipv4(...), tcp(...)
-
-
-Wildcarded flow key format
---------------------------
-
-A wildcarded flow is described with two sequences of Netlink attributes
-passed over the Netlink socket. A flow key, exactly as described above, and an
-optional corresponding flow mask.
-
-A wildcarded flow can represent a group of exact match flows. Each '1' bit
-in the mask specifies a exact match with the corresponding bit in the flow key.
-A '0' bit specifies a don't care bit, which will match either a '1' or '0' bit
-of a incoming packet. Using wildcarded flow can improve the flow set up rate
-by reduce the number of new flows need to be processed by the user space program.
-
-Support for the mask Netlink attribute is optional for both the kernel and user
-space program. The kernel can ignore the mask attribute, installing an exact
-match flow, or reduce the number of don't care bits in the kernel to less than
-what was specified by the user space program. In this case, variations in bits
-that the kernel does not implement will simply result in additional flow setups.
-The kernel module will also work with user space programs that neither support
-nor supply flow mask attributes.
-
-Since the kernel may ignore or modify wildcard bits, it can be difficult for
-the userspace program to know exactly what matches are installed. There are
-two possible approaches: reactively install flows as they miss the kernel
-flow table (and therefore not attempt to determine wildcard changes at all)
-or use the kernel's response messages to determine the installed wildcards.
-
-When interacting with userspace, the kernel should maintain the match portion
-of the key exactly as originally installed. This will provides a handle to
-identify the flow for all future operations. However, when reporting the
-mask of an installed flow, the mask should include any restrictions imposed
-by the kernel.
-
-The behavior when using overlapping wildcarded flows is undefined. It is the
-responsibility of the user space program to ensure that any incoming packet
-can match at most one flow, wildcarded or not. The current implementation
-performs best-effort detection of overlapping wildcarded flows and may reject
-some but not all of them. However, this behavior may change in future versions.
-
-
-Unique flow identifiers
------------------------
-
-An alternative to using the original match portion of a key as the handle for
-flow identification is a unique flow identifier, or "UFID". UFIDs are optional
-for both the kernel and user space program.
-
-User space programs that support UFID are expected to provide it during flow
-setup in addition to the flow, then refer to the flow using the UFID for all
-future operations. The kernel is not required to index flows by the original
-flow key if a UFID is specified.
-
-
-Basic rule for evolving flow keys
----------------------------------
-
-Some care is needed to really maintain forward and backward
-compatibility for applications that follow the rules listed under
-"Flow key compatibility" above.
-
-The basic rule is obvious:
-
-    ------------------------------------------------------------------
-    New network protocol support must only supplement existing flow
-    key attributes.  It must not change the meaning of already defined
-    flow key attributes.
-    ------------------------------------------------------------------
-
-This rule does have less-obvious consequences so it is worth working
-through a few examples.  Suppose, for example, that the kernel module
-did not already implement VLAN parsing.  Instead, it just interpreted
-the 802.1Q TPID (0x8100) as the Ethertype then stopped parsing the
-packet.  The flow key for any packet with an 802.1Q header would look
-essentially like this, ignoring metadata:
-
-    eth(...), eth_type(0x8100)
-
-Naively, to add VLAN support, it makes sense to add a new "vlan" flow
-key attribute to contain the VLAN tag, then continue to decode the
-encapsulated headers beyond the VLAN tag using the existing field
-definitions.  With this change, a TCP packet in VLAN 10 would have a
-flow key much like this:
-
-    eth(...), vlan(vid=10, pcp=0), eth_type(0x0800), ip(proto=6, ...), tcp(...)
-
-But this change would negatively affect a userspace application that
-has not been updated to understand the new "vlan" flow key attribute.
-The application could, following the flow compatibility rules above,
-ignore the "vlan" attribute that it does not understand and therefore
-assume that the flow contained IP packets.  This is a bad assumption
-(the flow only contains IP packets if one parses and skips over the
-802.1Q header) and it could cause the application's behavior to change
-across kernel versions even though it follows the compatibility rules.
-
-The solution is to use a set of nested attributes.  This is, for
-example, why 802.1Q support uses nested attributes.  A TCP packet in
-VLAN 10 is actually expressed as:
-
-    eth(...), eth_type(0x8100), vlan(vid=10, pcp=0), encap(eth_type(0x0800),
-    ip(proto=6, ...), tcp(...)))
-
-Notice how the "eth_type", "ip", and "tcp" flow key attributes are
-nested inside the "encap" attribute.  Thus, an application that does
-not understand the "vlan" key will not see either of those attributes
-and therefore will not misinterpret them.  (Also, the outer eth_type
-is still 0x8100, not changed to 0x0800.)
-
-Handling malformed packets
---------------------------
-
-Don't drop packets in the kernel for malformed protocol headers, bad
-checksums, etc.  This would prevent userspace from implementing a
-simple Ethernet switch that forwards every packet.
-
-Instead, in such a case, include an attribute with "empty" content.
-It doesn't matter if the empty content could be valid protocol values,
-as long as those values are rarely seen in practice, because userspace
-can always forward all packets with those values to userspace and
-handle them individually.
-
-For example, consider a packet that contains an IP header that
-indicates protocol 6 for TCP, but which is truncated just after the IP
-header, so that the TCP header is missing.  The flow key for this
-packet would include a tcp attribute with all-zero src and dst, like
-this:
-
-    eth(...), eth_type(0x0800), ip(proto=6, ...), tcp(src=0, dst=0)
-
-As another example, consider a packet with an Ethernet type of 0x8100,
-indicating that a VLAN TCI should follow, but which is truncated just
-after the Ethernet type.  The flow key for this packet would include
-an all-zero-bits vlan and an empty encap attribute, like this:
-
-    eth(...), eth_type(0x8100), vlan(0), encap()
-
-Unlike a TCP packet with source and destination ports 0, an
-all-zero-bits VLAN TCI is not that rare, so the CFI bit (aka
-VLAN_TAG_PRESENT inside the kernel) is ordinarily set in a vlan
-attribute expressly to allow this situation to be distinguished.
-Thus, the flow key in this second example unambiguously indicates a
-missing or malformed VLAN TCI.
-
-Other rules
------------
-
-The other rules for flow keys are much less subtle:
-
-    - Duplicate attributes are not allowed at a given nesting level.
-
-    - Ordering of attributes is not significant.
-
-    - When the kernel sends a given flow key to userspace, it always
-      composes it the same way.  This allows userspace to hash and
-      compare entire flow keys that it may not be able to fully
-      interpret.
diff --git a/Documentation/networking/operstates.rst b/Documentation/networking/operstates.rst
new file mode 100644 (file)
index 0000000..9c918f7
--- /dev/null
@@ -0,0 +1,185 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================
+Operational States
+==================
+
+
+1. Introduction
+===============
+
+Linux distinguishes between administrative and operational state of an
+interface. Administrative state is the result of "ip link set dev
+<dev> up or down" and reflects whether the administrator wants to use
+the device for traffic.
+
+However, an interface is not usable just because the admin enabled it
+- ethernet requires to be plugged into the switch and, depending on
+a site's networking policy and configuration, an 802.1X authentication
+to be performed before user data can be transferred. Operational state
+shows the ability of an interface to transmit this user data.
+
+Thanks to 802.1X, userspace must be granted the possibility to
+influence operational state. To accommodate this, operational state is
+split into two parts: Two flags that can be set by the driver only, and
+a RFC2863 compatible state that is derived from these flags, a policy,
+and changeable from userspace under certain rules.
+
+
+2. Querying from userspace
+==========================
+
+Both admin and operational state can be queried via the netlink
+operation RTM_GETLINK. It is also possible to subscribe to RTNLGRP_LINK
+to be notified of updates while the interface is admin up. This is
+important for setting from userspace.
+
+These values contain interface state:
+
+ifinfomsg::if_flags & IFF_UP:
+ Interface is admin up
+
+ifinfomsg::if_flags & IFF_RUNNING:
+ Interface is in RFC2863 operational state UP or UNKNOWN. This is for
+ backward compatibility, routing daemons, dhcp clients can use this
+ flag to determine whether they should use the interface.
+
+ifinfomsg::if_flags & IFF_LOWER_UP:
+ Driver has signaled netif_carrier_on()
+
+ifinfomsg::if_flags & IFF_DORMANT:
+ Driver has signaled netif_dormant_on()
+
+TLV IFLA_OPERSTATE
+------------------
+
+contains RFC2863 state of the interface in numeric representation:
+
+IF_OPER_UNKNOWN (0):
+ Interface is in unknown state, neither driver nor userspace has set
+ operational state. Interface must be considered for user data as
+ setting operational state has not been implemented in every driver.
+
+IF_OPER_NOTPRESENT (1):
+ Unused in current kernel (notpresent interfaces normally disappear),
+ just a numerical placeholder.
+
+IF_OPER_DOWN (2):
+ Interface is unable to transfer data on L1, f.e. ethernet is not
+ plugged or interface is ADMIN down.
+
+IF_OPER_LOWERLAYERDOWN (3):
+ Interfaces stacked on an interface that is IF_OPER_DOWN show this
+ state (f.e. VLAN).
+
+IF_OPER_TESTING (4):
+ Unused in current kernel.
+
+IF_OPER_DORMANT (5):
+ Interface is L1 up, but waiting for an external event, f.e. for a
+ protocol to establish. (802.1X)
+
+IF_OPER_UP (6):
+ Interface is operational up and can be used.
+
+This TLV can also be queried via sysfs.
+
+TLV IFLA_LINKMODE
+-----------------
+
+contains link policy. This is needed for userspace interaction
+described below.
+
+This TLV can also be queried via sysfs.
+
+
+3. Kernel driver API
+====================
+
+Kernel drivers have access to two flags that map to IFF_LOWER_UP and
+IFF_DORMANT. These flags can be set from everywhere, even from
+interrupts. It is guaranteed that only the driver has write access,
+however, if different layers of the driver manipulate the same flag,
+the driver has to provide the synchronisation needed.
+
+__LINK_STATE_NOCARRIER, maps to !IFF_LOWER_UP:
+
+The driver uses netif_carrier_on() to clear and netif_carrier_off() to
+set this flag. On netif_carrier_off(), the scheduler stops sending
+packets. The name 'carrier' and the inversion are historical, think of
+it as lower layer.
+
+Note that for certain kind of soft-devices, which are not managing any
+real hardware, it is possible to set this bit from userspace.  One
+should use TVL IFLA_CARRIER to do so.
+
+netif_carrier_ok() can be used to query that bit.
+
+__LINK_STATE_DORMANT, maps to IFF_DORMANT:
+
+Set by the driver to express that the device cannot yet be used
+because some driver controlled protocol establishment has to
+complete. Corresponding functions are netif_dormant_on() to set the
+flag, netif_dormant_off() to clear it and netif_dormant() to query.
+
+On device allocation, both flags __LINK_STATE_NOCARRIER and
+__LINK_STATE_DORMANT are cleared, so the effective state is equivalent
+to netif_carrier_ok() and !netif_dormant().
+
+
+Whenever the driver CHANGES one of these flags, a workqueue event is
+scheduled to translate the flag combination to IFLA_OPERSTATE as
+follows:
+
+!netif_carrier_ok():
+ IF_OPER_LOWERLAYERDOWN if the interface is stacked, IF_OPER_DOWN
+ otherwise. Kernel can recognise stacked interfaces because their
+ ifindex != iflink.
+
+netif_carrier_ok() && netif_dormant():
+ IF_OPER_DORMANT
+
+netif_carrier_ok() && !netif_dormant():
+ IF_OPER_UP if userspace interaction is disabled. Otherwise
+ IF_OPER_DORMANT with the possibility for userspace to initiate the
+ IF_OPER_UP transition afterwards.
+
+
+4. Setting from userspace
+=========================
+
+Applications have to use the netlink interface to influence the
+RFC2863 operational state of an interface. Setting IFLA_LINKMODE to 1
+via RTM_SETLINK instructs the kernel that an interface should go to
+IF_OPER_DORMANT instead of IF_OPER_UP when the combination
+netif_carrier_ok() && !netif_dormant() is set by the
+driver. Afterwards, the userspace application can set IFLA_OPERSTATE
+to IF_OPER_DORMANT or IF_OPER_UP as long as the driver does not set
+netif_carrier_off() or netif_dormant_on(). Changes made by userspace
+are multicasted on the netlink group RTNLGRP_LINK.
+
+So basically a 802.1X supplicant interacts with the kernel like this:
+
+- subscribe to RTNLGRP_LINK
+- set IFLA_LINKMODE to 1 via RTM_SETLINK
+- query RTM_GETLINK once to get initial state
+- if initial flags are not (IFF_LOWER_UP && !IFF_DORMANT), wait until
+  netlink multicast signals this state
+- do 802.1X, eventually abort if flags go down again
+- send RTM_SETLINK to set operstate to IF_OPER_UP if authentication
+  succeeds, IF_OPER_DORMANT otherwise
+- see how operstate and IFF_RUNNING is echoed via netlink multicast
+- set interface back to IF_OPER_DORMANT if 802.1X reauthentication
+  fails
+- restart if kernel changes IFF_LOWER_UP or IFF_DORMANT flag
+
+if supplicant goes down, bring back IFLA_LINKMODE to 0 and
+IFLA_OPERSTATE to a sane value.
+
+A routing daemon or dhcp client just needs to care for IFF_RUNNING or
+waiting for operstate to go IF_OPER_UP/IF_OPER_UNKNOWN before
+considering the interface / querying a DHCP address.
+
+
+For technical questions and/or comments please e-mail to Stefan Rompf
+(stefan at loplof.de).
diff --git a/Documentation/networking/operstates.txt b/Documentation/networking/operstates.txt
deleted file mode 100644 (file)
index b203d13..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-
-1. Introduction
-
-Linux distinguishes between administrative and operational state of an
-interface. Administrative state is the result of "ip link set dev
-<dev> up or down" and reflects whether the administrator wants to use
-the device for traffic.
-
-However, an interface is not usable just because the admin enabled it
-- ethernet requires to be plugged into the switch and, depending on
-a site's networking policy and configuration, an 802.1X authentication
-to be performed before user data can be transferred. Operational state
-shows the ability of an interface to transmit this user data.
-
-Thanks to 802.1X, userspace must be granted the possibility to
-influence operational state. To accommodate this, operational state is
-split into two parts: Two flags that can be set by the driver only, and
-a RFC2863 compatible state that is derived from these flags, a policy,
-and changeable from userspace under certain rules.
-
-
-2. Querying from userspace
-
-Both admin and operational state can be queried via the netlink
-operation RTM_GETLINK. It is also possible to subscribe to RTNLGRP_LINK
-to be notified of updates while the interface is admin up. This is
-important for setting from userspace.
-
-These values contain interface state:
-
-ifinfomsg::if_flags & IFF_UP:
- Interface is admin up
-ifinfomsg::if_flags & IFF_RUNNING:
- Interface is in RFC2863 operational state UP or UNKNOWN. This is for
- backward compatibility, routing daemons, dhcp clients can use this
- flag to determine whether they should use the interface.
-ifinfomsg::if_flags & IFF_LOWER_UP:
- Driver has signaled netif_carrier_on()
-ifinfomsg::if_flags & IFF_DORMANT:
- Driver has signaled netif_dormant_on()
-
-TLV IFLA_OPERSTATE
-
-contains RFC2863 state of the interface in numeric representation:
-
-IF_OPER_UNKNOWN (0):
- Interface is in unknown state, neither driver nor userspace has set
- operational state. Interface must be considered for user data as
- setting operational state has not been implemented in every driver.
-IF_OPER_NOTPRESENT (1):
- Unused in current kernel (notpresent interfaces normally disappear),
- just a numerical placeholder.
-IF_OPER_DOWN (2):
- Interface is unable to transfer data on L1, f.e. ethernet is not
- plugged or interface is ADMIN down.
-IF_OPER_LOWERLAYERDOWN (3):
- Interfaces stacked on an interface that is IF_OPER_DOWN show this
- state (f.e. VLAN).
-IF_OPER_TESTING (4):
- Unused in current kernel.
-IF_OPER_DORMANT (5):
- Interface is L1 up, but waiting for an external event, f.e. for a
- protocol to establish. (802.1X)
-IF_OPER_UP (6):
- Interface is operational up and can be used.
-
-This TLV can also be queried via sysfs.
-
-TLV IFLA_LINKMODE
-
-contains link policy. This is needed for userspace interaction
-described below.
-
-This TLV can also be queried via sysfs.
-
-
-3. Kernel driver API
-
-Kernel drivers have access to two flags that map to IFF_LOWER_UP and
-IFF_DORMANT. These flags can be set from everywhere, even from
-interrupts. It is guaranteed that only the driver has write access,
-however, if different layers of the driver manipulate the same flag,
-the driver has to provide the synchronisation needed.
-
-__LINK_STATE_NOCARRIER, maps to !IFF_LOWER_UP:
-
-The driver uses netif_carrier_on() to clear and netif_carrier_off() to
-set this flag. On netif_carrier_off(), the scheduler stops sending
-packets. The name 'carrier' and the inversion are historical, think of
-it as lower layer.
-
-Note that for certain kind of soft-devices, which are not managing any
-real hardware, it is possible to set this bit from userspace.  One
-should use TVL IFLA_CARRIER to do so.
-
-netif_carrier_ok() can be used to query that bit.
-
-__LINK_STATE_DORMANT, maps to IFF_DORMANT:
-
-Set by the driver to express that the device cannot yet be used
-because some driver controlled protocol establishment has to
-complete. Corresponding functions are netif_dormant_on() to set the
-flag, netif_dormant_off() to clear it and netif_dormant() to query.
-
-On device allocation, both flags __LINK_STATE_NOCARRIER and
-__LINK_STATE_DORMANT are cleared, so the effective state is equivalent
-to netif_carrier_ok() and !netif_dormant().
-
-
-Whenever the driver CHANGES one of these flags, a workqueue event is
-scheduled to translate the flag combination to IFLA_OPERSTATE as
-follows:
-
-!netif_carrier_ok():
- IF_OPER_LOWERLAYERDOWN if the interface is stacked, IF_OPER_DOWN
- otherwise. Kernel can recognise stacked interfaces because their
- ifindex != iflink.
-
-netif_carrier_ok() && netif_dormant():
- IF_OPER_DORMANT
-
-netif_carrier_ok() && !netif_dormant():
- IF_OPER_UP if userspace interaction is disabled. Otherwise
- IF_OPER_DORMANT with the possibility for userspace to initiate the
- IF_OPER_UP transition afterwards.
-
-
-4. Setting from userspace
-
-Applications have to use the netlink interface to influence the
-RFC2863 operational state of an interface. Setting IFLA_LINKMODE to 1
-via RTM_SETLINK instructs the kernel that an interface should go to
-IF_OPER_DORMANT instead of IF_OPER_UP when the combination
-netif_carrier_ok() && !netif_dormant() is set by the
-driver. Afterwards, the userspace application can set IFLA_OPERSTATE
-to IF_OPER_DORMANT or IF_OPER_UP as long as the driver does not set
-netif_carrier_off() or netif_dormant_on(). Changes made by userspace
-are multicasted on the netlink group RTNLGRP_LINK.
-
-So basically a 802.1X supplicant interacts with the kernel like this:
-
--subscribe to RTNLGRP_LINK
--set IFLA_LINKMODE to 1 via RTM_SETLINK
--query RTM_GETLINK once to get initial state
--if initial flags are not (IFF_LOWER_UP && !IFF_DORMANT), wait until
- netlink multicast signals this state
--do 802.1X, eventually abort if flags go down again
--send RTM_SETLINK to set operstate to IF_OPER_UP if authentication
- succeeds, IF_OPER_DORMANT otherwise
--see how operstate and IFF_RUNNING is echoed via netlink multicast
--set interface back to IF_OPER_DORMANT if 802.1X reauthentication
- fails
--restart if kernel changes IFF_LOWER_UP or IFF_DORMANT flag
-
-if supplicant goes down, bring back IFLA_LINKMODE to 0 and
-IFLA_OPERSTATE to a sane value.
-
-A routing daemon or dhcp client just needs to care for IFF_RUNNING or
-waiting for operstate to go IF_OPER_UP/IF_OPER_UNKNOWN before
-considering the interface / querying a DHCP address.
-
-
-For technical questions and/or comments please e-mail to Stefan Rompf
-(stefan at loplof.de).
diff --git a/Documentation/networking/packet_mmap.rst b/Documentation/networking/packet_mmap.rst
new file mode 100644 (file)
index 0000000..6c009ce
--- /dev/null
@@ -0,0 +1,1084 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========
+Packet MMAP
+===========
+
+Abstract
+========
+
+This file documents the mmap() facility available with the PACKET
+socket interface on 2.4/2.6/3.x kernels. This type of sockets is used for
+
+i) capture network traffic with utilities like tcpdump,
+ii) transmit network traffic, or any other that needs raw
+    access to network interface.
+
+Howto can be found at:
+
+    https://sites.google.com/site/packetmmap/
+
+Please send your comments to
+    - Ulisses Alonso Camaró <uaca@i.hate.spam.alumni.uv.es>
+    - Johann Baudy
+
+Why use PACKET_MMAP
+===================
+
+In Linux 2.4/2.6/3.x if PACKET_MMAP is not enabled, the capture process is very
+inefficient. It uses very limited buffers and requires one system call to
+capture each packet, it requires two if you want to get packet's timestamp
+(like libpcap always does).
+
+In the other hand PACKET_MMAP is very efficient. PACKET_MMAP provides a size
+configurable circular buffer mapped in user space that can be used to either
+send or receive packets. This way reading packets just needs to wait for them,
+most of the time there is no need to issue a single system call. Concerning
+transmission, multiple packets can be sent through one system call to get the
+highest bandwidth. By using a shared buffer between the kernel and the user
+also has the benefit of minimizing packet copies.
+
+It's fine to use PACKET_MMAP to improve the performance of the capture and
+transmission process, but it isn't everything. At least, if you are capturing
+at high speeds (this is relative to the cpu speed), you should check if the
+device driver of your network interface card supports some sort of interrupt
+load mitigation or (even better) if it supports NAPI, also make sure it is
+enabled. For transmission, check the MTU (Maximum Transmission Unit) used and
+supported by devices of your network. CPU IRQ pinning of your network interface
+card can also be an advantage.
+
+How to use mmap() to improve capture process
+============================================
+
+From the user standpoint, you should use the higher level libpcap library, which
+is a de facto standard, portable across nearly all operating systems
+including Win32.
+
+Packet MMAP support was integrated into libpcap around the time of version 1.3.0;
+TPACKET_V3 support was added in version 1.5.0
+
+How to use mmap() directly to improve capture process
+=====================================================
+
+From the system calls stand point, the use of PACKET_MMAP involves
+the following process::
+
+
+    [setup]     socket() -------> creation of the capture socket
+               setsockopt() ---> allocation of the circular buffer (ring)
+                                 option: PACKET_RX_RING
+               mmap() ---------> mapping of the allocated buffer to the
+                                 user process
+
+    [capture]   poll() ---------> to wait for incoming packets
+
+    [shutdown]  close() --------> destruction of the capture socket and
+                                 deallocation of all associated
+                                 resources.
+
+
+socket creation and destruction is straight forward, and is done
+the same way with or without PACKET_MMAP::
+
+ int fd = socket(PF_PACKET, mode, htons(ETH_P_ALL));
+
+where mode is SOCK_RAW for the raw interface were link level
+information can be captured or SOCK_DGRAM for the cooked
+interface where link level information capture is not
+supported and a link level pseudo-header is provided
+by the kernel.
+
+The destruction of the socket and all associated resources
+is done by a simple call to close(fd).
+
+Similarly as without PACKET_MMAP, it is possible to use one socket
+for capture and transmission. This can be done by mapping the
+allocated RX and TX buffer ring with a single mmap() call.
+See "Mapping and use of the circular buffer (ring)".
+
+Next I will describe PACKET_MMAP settings and its constraints,
+also the mapping of the circular buffer in the user process and
+the use of this buffer.
+
+How to use mmap() directly to improve transmission process
+==========================================================
+Transmission process is similar to capture as shown below::
+
+    [setup]         socket() -------> creation of the transmission socket
+                   setsockopt() ---> allocation of the circular buffer (ring)
+                                     option: PACKET_TX_RING
+                   bind() ---------> bind transmission socket with a network interface
+                   mmap() ---------> mapping of the allocated buffer to the
+                                     user process
+
+    [transmission]  poll() ---------> wait for free packets (optional)
+                   send() ---------> send all packets that are set as ready in
+                                     the ring
+                                     The flag MSG_DONTWAIT can be used to return
+                                     before end of transfer.
+
+    [shutdown]      close() --------> destruction of the transmission socket and
+                                     deallocation of all associated resources.
+
+Socket creation and destruction is also straight forward, and is done
+the same way as in capturing described in the previous paragraph::
+
+ int fd = socket(PF_PACKET, mode, 0);
+
+The protocol can optionally be 0 in case we only want to transmit
+via this socket, which avoids an expensive call to packet_rcv().
+In this case, you also need to bind(2) the TX_RING with sll_protocol = 0
+set. Otherwise, htons(ETH_P_ALL) or any other protocol, for example.
+
+Binding the socket to your network interface is mandatory (with zero copy) to
+know the header size of frames used in the circular buffer.
+
+As capture, each frame contains two parts::
+
+    --------------------
+    | struct tpacket_hdr | Header. It contains the status of
+    |                    | of this frame
+    |--------------------|
+    | data buffer        |
+    .                    .  Data that will be sent over the network interface.
+    .                    .
+    --------------------
+
+ bind() associates the socket to your network interface thanks to
+ sll_ifindex parameter of struct sockaddr_ll.
+
+ Initialization example::
+
+    struct sockaddr_ll my_addr;
+    struct ifreq s_ifr;
+    ...
+
+    strncpy (s_ifr.ifr_name, "eth0", sizeof(s_ifr.ifr_name));
+
+    /* get interface index of eth0 */
+    ioctl(this->socket, SIOCGIFINDEX, &s_ifr);
+
+    /* fill sockaddr_ll struct to prepare binding */
+    my_addr.sll_family = AF_PACKET;
+    my_addr.sll_protocol = htons(ETH_P_ALL);
+    my_addr.sll_ifindex =  s_ifr.ifr_ifindex;
+
+    /* bind socket to eth0 */
+    bind(this->socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll));
+
+ A complete tutorial is available at: https://sites.google.com/site/packetmmap/
+
+By default, the user should put data at::
+
+ frame base + TPACKET_HDRLEN - sizeof(struct sockaddr_ll)
+
+So, whatever you choose for the socket mode (SOCK_DGRAM or SOCK_RAW),
+the beginning of the user data will be at::
+
+ frame base + TPACKET_ALIGN(sizeof(struct tpacket_hdr))
+
+If you wish to put user data at a custom offset from the beginning of
+the frame (for payload alignment with SOCK_RAW mode for instance) you
+can set tp_net (with SOCK_DGRAM) or tp_mac (with SOCK_RAW). In order
+to make this work it must be enabled previously with setsockopt()
+and the PACKET_TX_HAS_OFF option.
+
+PACKET_MMAP settings
+====================
+
+To setup PACKET_MMAP from user level code is done with a call like
+
+ - Capture process::
+
+     setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req))
+
+ - Transmission process::
+
+     setsockopt(fd, SOL_PACKET, PACKET_TX_RING, (void *) &req, sizeof(req))
+
+The most significant argument in the previous call is the req parameter,
+this parameter must to have the following structure::
+
+    struct tpacket_req
+    {
+       unsigned int    tp_block_size;  /* Minimal size of contiguous block */
+       unsigned int    tp_block_nr;    /* Number of blocks */
+       unsigned int    tp_frame_size;  /* Size of frame */
+       unsigned int    tp_frame_nr;    /* Total number of frames */
+    };
+
+This structure is defined in /usr/include/linux/if_packet.h and establishes a
+circular buffer (ring) of unswappable memory.
+Being mapped in the capture process allows reading the captured frames and
+related meta-information like timestamps without requiring a system call.
+
+Frames are grouped in blocks. Each block is a physically contiguous
+region of memory and holds tp_block_size/tp_frame_size frames. The total number
+of blocks is tp_block_nr. Note that tp_frame_nr is a redundant parameter because::
+
+    frames_per_block = tp_block_size/tp_frame_size
+
+indeed, packet_set_ring checks that the following condition is true::
+
+    frames_per_block * tp_block_nr == tp_frame_nr
+
+Lets see an example, with the following values::
+
+     tp_block_size= 4096
+     tp_frame_size= 2048
+     tp_block_nr  = 4
+     tp_frame_nr  = 8
+
+we will get the following buffer structure::
+
+           block #1                 block #2
+    +---------+---------+    +---------+---------+
+    | frame 1 | frame 2 |    | frame 3 | frame 4 |
+    +---------+---------+    +---------+---------+
+
+           block #3                 block #4
+    +---------+---------+    +---------+---------+
+    | frame 5 | frame 6 |    | frame 7 | frame 8 |
+    +---------+---------+    +---------+---------+
+
+A frame can be of any size with the only condition it can fit in a block. A block
+can only hold an integer number of frames, or in other words, a frame cannot
+be spawned across two blocks, so there are some details you have to take into
+account when choosing the frame_size. See "Mapping and use of the circular
+buffer (ring)".
+
+PACKET_MMAP setting constraints
+===============================
+
+In kernel versions prior to 2.4.26 (for the 2.4 branch) and 2.6.5 (2.6 branch),
+the PACKET_MMAP buffer could hold only 32768 frames in a 32 bit architecture or
+16384 in a 64 bit architecture. For information on these kernel versions
+see http://pusa.uv.es/~ulisses/packet_mmap/packet_mmap.pre-2.4.26_2.6.5.txt
+
+Block size limit
+----------------
+
+As stated earlier, each block is a contiguous physical region of memory. These
+memory regions are allocated with calls to the __get_free_pages() function. As
+the name indicates, this function allocates pages of memory, and the second
+argument is "order" or a power of two number of pages, that is
+(for PAGE_SIZE == 4096) order=0 ==> 4096 bytes, order=1 ==> 8192 bytes,
+order=2 ==> 16384 bytes, etc. The maximum size of a
+region allocated by __get_free_pages is determined by the MAX_ORDER macro. More
+precisely the limit can be calculated as::
+
+   PAGE_SIZE << MAX_ORDER
+
+   In a i386 architecture PAGE_SIZE is 4096 bytes
+   In a 2.4/i386 kernel MAX_ORDER is 10
+   In a 2.6/i386 kernel MAX_ORDER is 11
+
+So get_free_pages can allocate as much as 4MB or 8MB in a 2.4/2.6 kernel
+respectively, with an i386 architecture.
+
+User space programs can include /usr/include/sys/user.h and
+/usr/include/linux/mmzone.h to get PAGE_SIZE MAX_ORDER declarations.
+
+The pagesize can also be determined dynamically with the getpagesize (2)
+system call.
+
+Block number limit
+------------------
+
+To understand the constraints of PACKET_MMAP, we have to see the structure
+used to hold the pointers to each block.
+
+Currently, this structure is a dynamically allocated vector with kmalloc
+called pg_vec, its size limits the number of blocks that can be allocated::
+
+    +---+---+---+---+
+    | x | x | x | x |
+    +---+---+---+---+
+      |   |   |   |
+      |   |   |   v
+      |   |   v  block #4
+      |   v  block #3
+      v  block #2
+     block #1
+
+kmalloc allocates any number of bytes of physically contiguous memory from
+a pool of pre-determined sizes. This pool of memory is maintained by the slab
+allocator which is at the end the responsible for doing the allocation and
+hence which imposes the maximum memory that kmalloc can allocate.
+
+In a 2.4/2.6 kernel and the i386 architecture, the limit is 131072 bytes. The
+predetermined sizes that kmalloc uses can be checked in the "size-<bytes>"
+entries of /proc/slabinfo
+
+In a 32 bit architecture, pointers are 4 bytes long, so the total number of
+pointers to blocks is::
+
+     131072/4 = 32768 blocks
+
+PACKET_MMAP buffer size calculator
+==================================
+
+Definitions:
+
+==============  ================================================================
+<size-max>      is the maximum size of allocable with kmalloc
+               (see /proc/slabinfo)
+<pointer size>  depends on the architecture -- ``sizeof(void *)``
+<page size>     depends on the architecture -- PAGE_SIZE or getpagesize (2)
+<max-order>     is the value defined with MAX_ORDER
+<frame size>    it's an upper bound of frame's capture size (more on this later)
+==============  ================================================================
+
+from these definitions we will derive::
+
+       <block number> = <size-max>/<pointer size>
+       <block size> = <pagesize> << <max-order>
+
+so, the max buffer size is::
+
+       <block number> * <block size>
+
+and, the number of frames be::
+
+       <block number> * <block size> / <frame size>
+
+Suppose the following parameters, which apply for 2.6 kernel and an
+i386 architecture::
+
+       <size-max> = 131072 bytes
+       <pointer size> = 4 bytes
+       <pagesize> = 4096 bytes
+       <max-order> = 11
+
+and a value for <frame size> of 2048 bytes. These parameters will yield::
+
+       <block number> = 131072/4 = 32768 blocks
+       <block size> = 4096 << 11 = 8 MiB.
+
+and hence the buffer will have a 262144 MiB size. So it can hold
+262144 MiB / 2048 bytes = 134217728 frames
+
+Actually, this buffer size is not possible with an i386 architecture.
+Remember that the memory is allocated in kernel space, in the case of
+an i386 kernel's memory size is limited to 1GiB.
+
+All memory allocations are not freed until the socket is closed. The memory
+allocations are done with GFP_KERNEL priority, this basically means that
+the allocation can wait and swap other process' memory in order to allocate
+the necessary memory, so normally limits can be reached.
+
+Other constraints
+-----------------
+
+If you check the source code you will see that what I draw here as a frame
+is not only the link level frame. At the beginning of each frame there is a
+header called struct tpacket_hdr used in PACKET_MMAP to hold link level's frame
+meta information like timestamp. So what we draw here a frame it's really
+the following (from include/linux/if_packet.h)::
+
+ /*
+   Frame structure:
+
+   - Start. Frame must be aligned to TPACKET_ALIGNMENT=16
+   - struct tpacket_hdr
+   - pad to TPACKET_ALIGNMENT=16
+   - struct sockaddr_ll
+   - Gap, chosen so that packet data (Start+tp_net) aligns to
+     TPACKET_ALIGNMENT=16
+   - Start+tp_mac: [ Optional MAC header ]
+   - Start+tp_net: Packet data, aligned to TPACKET_ALIGNMENT=16.
+   - Pad to align to TPACKET_ALIGNMENT=16
+ */
+
+The following are conditions that are checked in packet_set_ring
+
+   - tp_block_size must be a multiple of PAGE_SIZE (1)
+   - tp_frame_size must be greater than TPACKET_HDRLEN (obvious)
+   - tp_frame_size must be a multiple of TPACKET_ALIGNMENT
+   - tp_frame_nr   must be exactly frames_per_block*tp_block_nr
+
+Note that tp_block_size should be chosen to be a power of two or there will
+be a waste of memory.
+
+Mapping and use of the circular buffer (ring)
+---------------------------------------------
+
+The mapping of the buffer in the user process is done with the conventional
+mmap function. Even the circular buffer is compound of several physically
+discontiguous blocks of memory, they are contiguous to the user space, hence
+just one call to mmap is needed::
+
+    mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+
+If tp_frame_size is a divisor of tp_block_size frames will be
+contiguously spaced by tp_frame_size bytes. If not, each
+tp_block_size/tp_frame_size frames there will be a gap between
+the frames. This is because a frame cannot be spawn across two
+blocks.
+
+To use one socket for capture and transmission, the mapping of both the
+RX and TX buffer ring has to be done with one call to mmap::
+
+    ...
+    setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &foo, sizeof(foo));
+    setsockopt(fd, SOL_PACKET, PACKET_TX_RING, &bar, sizeof(bar));
+    ...
+    rx_ring = mmap(0, size * 2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+    tx_ring = rx_ring + size;
+
+RX must be the first as the kernel maps the TX ring memory right
+after the RX one.
+
+At the beginning of each frame there is an status field (see
+struct tpacket_hdr). If this field is 0 means that the frame is ready
+to be used for the kernel, If not, there is a frame the user can read
+and the following flags apply:
+
+Capture process
+^^^^^^^^^^^^^^^
+
+     from include/linux/if_packet.h
+
+     #define TP_STATUS_COPY          (1 << 1)
+     #define TP_STATUS_LOSING        (1 << 2)
+     #define TP_STATUS_CSUMNOTREADY  (1 << 3)
+     #define TP_STATUS_CSUM_VALID    (1 << 7)
+
+======================  =======================================================
+TP_STATUS_COPY         This flag indicates that the frame (and associated
+                       meta information) has been truncated because it's
+                       larger than tp_frame_size. This packet can be
+                       read entirely with recvfrom().
+
+                       In order to make this work it must to be
+                       enabled previously with setsockopt() and
+                       the PACKET_COPY_THRESH option.
+
+                       The number of frames that can be buffered to
+                       be read with recvfrom is limited like a normal socket.
+                       See the SO_RCVBUF option in the socket (7) man page.
+
+TP_STATUS_LOSING       indicates there were packet drops from last time
+                       statistics where checked with getsockopt() and
+                       the PACKET_STATISTICS option.
+
+TP_STATUS_CSUMNOTREADY currently it's used for outgoing IP packets which
+                       its checksum will be done in hardware. So while
+                       reading the packet we should not try to check the
+                       checksum.
+
+TP_STATUS_CSUM_VALID   This flag indicates that at least the transport
+                       header checksum of the packet has been already
+                       validated on the kernel side. If the flag is not set
+                       then we are free to check the checksum by ourselves
+                       provided that TP_STATUS_CSUMNOTREADY is also not set.
+======================  =======================================================
+
+for convenience there are also the following defines::
+
+     #define TP_STATUS_KERNEL        0
+     #define TP_STATUS_USER          1
+
+The kernel initializes all frames to TP_STATUS_KERNEL, when the kernel
+receives a packet it puts in the buffer and updates the status with
+at least the TP_STATUS_USER flag. Then the user can read the packet,
+once the packet is read the user must zero the status field, so the kernel
+can use again that frame buffer.
+
+The user can use poll (any other variant should apply too) to check if new
+packets are in the ring::
+
+    struct pollfd pfd;
+
+    pfd.fd = fd;
+    pfd.revents = 0;
+    pfd.events = POLLIN|POLLRDNORM|POLLERR;
+
+    if (status == TP_STATUS_KERNEL)
+       retval = poll(&pfd, 1, timeout);
+
+It doesn't incur in a race condition to first check the status value and
+then poll for frames.
+
+Transmission process
+^^^^^^^^^^^^^^^^^^^^
+
+Those defines are also used for transmission::
+
+     #define TP_STATUS_AVAILABLE        0 // Frame is available
+     #define TP_STATUS_SEND_REQUEST     1 // Frame will be sent on next send()
+     #define TP_STATUS_SENDING          2 // Frame is currently in transmission
+     #define TP_STATUS_WRONG_FORMAT     4 // Frame format is not correct
+
+First, the kernel initializes all frames to TP_STATUS_AVAILABLE. To send a
+packet, the user fills a data buffer of an available frame, sets tp_len to
+current data buffer size and sets its status field to TP_STATUS_SEND_REQUEST.
+This can be done on multiple frames. Once the user is ready to transmit, it
+calls send(). Then all buffers with status equal to TP_STATUS_SEND_REQUEST are
+forwarded to the network device. The kernel updates each status of sent
+frames with TP_STATUS_SENDING until the end of transfer.
+
+At the end of each transfer, buffer status returns to TP_STATUS_AVAILABLE.
+
+::
+
+    header->tp_len = in_i_size;
+    header->tp_status = TP_STATUS_SEND_REQUEST;
+    retval = send(this->socket, NULL, 0, 0);
+
+The user can also use poll() to check if a buffer is available:
+
+(status == TP_STATUS_SENDING)
+
+::
+
+    struct pollfd pfd;
+    pfd.fd = fd;
+    pfd.revents = 0;
+    pfd.events = POLLOUT;
+    retval = poll(&pfd, 1, timeout);
+
+What TPACKET versions are available and when to use them?
+=========================================================
+
+::
+
+ int val = tpacket_version;
+ setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));
+ getsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));
+
+where 'tpacket_version' can be TPACKET_V1 (default), TPACKET_V2, TPACKET_V3.
+
+TPACKET_V1:
+       - Default if not otherwise specified by setsockopt(2)
+       - RX_RING, TX_RING available
+
+TPACKET_V1 --> TPACKET_V2:
+       - Made 64 bit clean due to unsigned long usage in TPACKET_V1
+         structures, thus this also works on 64 bit kernel with 32 bit
+         userspace and the like
+       - Timestamp resolution in nanoseconds instead of microseconds
+       - RX_RING, TX_RING available
+       - VLAN metadata information available for packets
+         (TP_STATUS_VLAN_VALID, TP_STATUS_VLAN_TPID_VALID),
+         in the tpacket2_hdr structure:
+
+               - TP_STATUS_VLAN_VALID bit being set into the tp_status field indicates
+                 that the tp_vlan_tci field has valid VLAN TCI value
+               - TP_STATUS_VLAN_TPID_VALID bit being set into the tp_status field
+                 indicates that the tp_vlan_tpid field has valid VLAN TPID value
+
+       - How to switch to TPACKET_V2:
+
+               1. Replace struct tpacket_hdr by struct tpacket2_hdr
+               2. Query header len and save
+               3. Set protocol version to 2, set up ring as usual
+               4. For getting the sockaddr_ll,
+                  use ``(void *)hdr + TPACKET_ALIGN(hdrlen)`` instead of
+                  ``(void *)hdr + TPACKET_ALIGN(sizeof(struct tpacket_hdr))``
+
+TPACKET_V2 --> TPACKET_V3:
+       - Flexible buffer implementation for RX_RING:
+               1. Blocks can be configured with non-static frame-size
+               2. Read/poll is at a block-level (as opposed to packet-level)
+               3. Added poll timeout to avoid indefinite user-space wait
+                  on idle links
+               4. Added user-configurable knobs:
+
+                       4.1 block::timeout
+                       4.2 tpkt_hdr::sk_rxhash
+
+       - RX Hash data available in user space
+       - TX_RING semantics are conceptually similar to TPACKET_V2;
+         use tpacket3_hdr instead of tpacket2_hdr, and TPACKET3_HDRLEN
+         instead of TPACKET2_HDRLEN. In the current implementation,
+         the tp_next_offset field in the tpacket3_hdr MUST be set to
+         zero, indicating that the ring does not hold variable sized frames.
+         Packets with non-zero values of tp_next_offset will be dropped.
+
+AF_PACKET fanout mode
+=====================
+
+In the AF_PACKET fanout mode, packet reception can be load balanced among
+processes. This also works in combination with mmap(2) on packet sockets.
+
+Currently implemented fanout policies are:
+
+  - PACKET_FANOUT_HASH: schedule to socket by skb's packet hash
+  - PACKET_FANOUT_LB: schedule to socket by round-robin
+  - PACKET_FANOUT_CPU: schedule to socket by CPU packet arrives on
+  - PACKET_FANOUT_RND: schedule to socket by random selection
+  - PACKET_FANOUT_ROLLOVER: if one socket is full, rollover to another
+  - PACKET_FANOUT_QM: schedule to socket by skbs recorded queue_mapping
+
+Minimal example code by David S. Miller (try things like "./test eth0 hash",
+"./test eth0 lb", etc.)::
+
+    #include <stddef.h>
+    #include <stdlib.h>
+    #include <stdio.h>
+    #include <string.h>
+
+    #include <sys/types.h>
+    #include <sys/wait.h>
+    #include <sys/socket.h>
+    #include <sys/ioctl.h>
+
+    #include <unistd.h>
+
+    #include <linux/if_ether.h>
+    #include <linux/if_packet.h>
+
+    #include <net/if.h>
+
+    static const char *device_name;
+    static int fanout_type;
+    static int fanout_id;
+
+    #ifndef PACKET_FANOUT
+    # define PACKET_FANOUT                     18
+    # define PACKET_FANOUT_HASH                0
+    # define PACKET_FANOUT_LB          1
+    #endif
+
+    static int setup_socket(void)
+    {
+           int err, fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP));
+           struct sockaddr_ll ll;
+           struct ifreq ifr;
+           int fanout_arg;
+
+           if (fd < 0) {
+                   perror("socket");
+                   return EXIT_FAILURE;
+           }
+
+           memset(&ifr, 0, sizeof(ifr));
+           strcpy(ifr.ifr_name, device_name);
+           err = ioctl(fd, SIOCGIFINDEX, &ifr);
+           if (err < 0) {
+                   perror("SIOCGIFINDEX");
+                   return EXIT_FAILURE;
+           }
+
+           memset(&ll, 0, sizeof(ll));
+           ll.sll_family = AF_PACKET;
+           ll.sll_ifindex = ifr.ifr_ifindex;
+           err = bind(fd, (struct sockaddr *) &ll, sizeof(ll));
+           if (err < 0) {
+                   perror("bind");
+                   return EXIT_FAILURE;
+           }
+
+           fanout_arg = (fanout_id | (fanout_type << 16));
+           err = setsockopt(fd, SOL_PACKET, PACKET_FANOUT,
+                           &fanout_arg, sizeof(fanout_arg));
+           if (err) {
+                   perror("setsockopt");
+                   return EXIT_FAILURE;
+           }
+
+           return fd;
+    }
+
+    static void fanout_thread(void)
+    {
+           int fd = setup_socket();
+           int limit = 10000;
+
+           if (fd < 0)
+                   exit(fd);
+
+           while (limit-- > 0) {
+                   char buf[1600];
+                   int err;
+
+                   err = read(fd, buf, sizeof(buf));
+                   if (err < 0) {
+                           perror("read");
+                           exit(EXIT_FAILURE);
+                   }
+                   if ((limit % 10) == 0)
+                           fprintf(stdout, "(%d) \n", getpid());
+           }
+
+           fprintf(stdout, "%d: Received 10000 packets\n", getpid());
+
+           close(fd);
+           exit(0);
+    }
+
+    int main(int argc, char **argp)
+    {
+           int fd, err;
+           int i;
+
+           if (argc != 3) {
+                   fprintf(stderr, "Usage: %s INTERFACE {hash|lb}\n", argp[0]);
+                   return EXIT_FAILURE;
+           }
+
+           if (!strcmp(argp[2], "hash"))
+                   fanout_type = PACKET_FANOUT_HASH;
+           else if (!strcmp(argp[2], "lb"))
+                   fanout_type = PACKET_FANOUT_LB;
+           else {
+                   fprintf(stderr, "Unknown fanout type [%s]\n", argp[2]);
+                   exit(EXIT_FAILURE);
+           }
+
+           device_name = argp[1];
+           fanout_id = getpid() & 0xffff;
+
+           for (i = 0; i < 4; i++) {
+                   pid_t pid = fork();
+
+                   switch (pid) {
+                   case 0:
+                           fanout_thread();
+
+                   case -1:
+                           perror("fork");
+                           exit(EXIT_FAILURE);
+                   }
+           }
+
+           for (i = 0; i < 4; i++) {
+                   int status;
+
+                   wait(&status);
+           }
+
+           return 0;
+    }
+
+AF_PACKET TPACKET_V3 example
+============================
+
+AF_PACKET's TPACKET_V3 ring buffer can be configured to use non-static frame
+sizes by doing it's own memory management. It is based on blocks where polling
+works on a per block basis instead of per ring as in TPACKET_V2 and predecessor.
+
+It is said that TPACKET_V3 brings the following benefits:
+
+ * ~15% - 20% reduction in CPU-usage
+ * ~20% increase in packet capture rate
+ * ~2x increase in packet density
+ * Port aggregation analysis
+ * Non static frame size to capture entire packet payload
+
+So it seems to be a good candidate to be used with packet fanout.
+
+Minimal example code by Daniel Borkmann based on Chetan Loke's lolpcap (compile
+it with gcc -Wall -O2 blob.c, and try things like "./a.out eth0", etc.)::
+
+    /* Written from scratch, but kernel-to-user space API usage
+    * dissected from lolpcap:
+    *  Copyright 2011, Chetan Loke <loke.chetan@gmail.com>
+    *  License: GPL, version 2.0
+    */
+
+    #include <stdio.h>
+    #include <stdlib.h>
+    #include <stdint.h>
+    #include <string.h>
+    #include <assert.h>
+    #include <net/if.h>
+    #include <arpa/inet.h>
+    #include <netdb.h>
+    #include <poll.h>
+    #include <unistd.h>
+    #include <signal.h>
+    #include <inttypes.h>
+    #include <sys/socket.h>
+    #include <sys/mman.h>
+    #include <linux/if_packet.h>
+    #include <linux/if_ether.h>
+    #include <linux/ip.h>
+
+    #ifndef likely
+    # define likely(x)         __builtin_expect(!!(x), 1)
+    #endif
+    #ifndef unlikely
+    # define unlikely(x)               __builtin_expect(!!(x), 0)
+    #endif
+
+    struct block_desc {
+           uint32_t version;
+           uint32_t offset_to_priv;
+           struct tpacket_hdr_v1 h1;
+    };
+
+    struct ring {
+           struct iovec *rd;
+           uint8_t *map;
+           struct tpacket_req3 req;
+    };
+
+    static unsigned long packets_total = 0, bytes_total = 0;
+    static sig_atomic_t sigint = 0;
+
+    static void sighandler(int num)
+    {
+           sigint = 1;
+    }
+
+    static int setup_socket(struct ring *ring, char *netdev)
+    {
+           int err, i, fd, v = TPACKET_V3;
+           struct sockaddr_ll ll;
+           unsigned int blocksiz = 1 << 22, framesiz = 1 << 11;
+           unsigned int blocknum = 64;
+
+           fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+           if (fd < 0) {
+                   perror("socket");
+                   exit(1);
+           }
+
+           err = setsockopt(fd, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
+           if (err < 0) {
+                   perror("setsockopt");
+                   exit(1);
+           }
+
+           memset(&ring->req, 0, sizeof(ring->req));
+           ring->req.tp_block_size = blocksiz;
+           ring->req.tp_frame_size = framesiz;
+           ring->req.tp_block_nr = blocknum;
+           ring->req.tp_frame_nr = (blocksiz * blocknum) / framesiz;
+           ring->req.tp_retire_blk_tov = 60;
+           ring->req.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH;
+
+           err = setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &ring->req,
+                           sizeof(ring->req));
+           if (err < 0) {
+                   perror("setsockopt");
+                   exit(1);
+           }
+
+           ring->map = mmap(NULL, ring->req.tp_block_size * ring->req.tp_block_nr,
+                           PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, 0);
+           if (ring->map == MAP_FAILED) {
+                   perror("mmap");
+                   exit(1);
+           }
+
+           ring->rd = malloc(ring->req.tp_block_nr * sizeof(*ring->rd));
+           assert(ring->rd);
+           for (i = 0; i < ring->req.tp_block_nr; ++i) {
+                   ring->rd[i].iov_base = ring->map + (i * ring->req.tp_block_size);
+                   ring->rd[i].iov_len = ring->req.tp_block_size;
+           }
+
+           memset(&ll, 0, sizeof(ll));
+           ll.sll_family = PF_PACKET;
+           ll.sll_protocol = htons(ETH_P_ALL);
+           ll.sll_ifindex = if_nametoindex(netdev);
+           ll.sll_hatype = 0;
+           ll.sll_pkttype = 0;
+           ll.sll_halen = 0;
+
+           err = bind(fd, (struct sockaddr *) &ll, sizeof(ll));
+           if (err < 0) {
+                   perror("bind");
+                   exit(1);
+           }
+
+           return fd;
+    }
+
+    static void display(struct tpacket3_hdr *ppd)
+    {
+           struct ethhdr *eth = (struct ethhdr *) ((uint8_t *) ppd + ppd->tp_mac);
+           struct iphdr *ip = (struct iphdr *) ((uint8_t *) eth + ETH_HLEN);
+
+           if (eth->h_proto == htons(ETH_P_IP)) {
+                   struct sockaddr_in ss, sd;
+                   char sbuff[NI_MAXHOST], dbuff[NI_MAXHOST];
+
+                   memset(&ss, 0, sizeof(ss));
+                   ss.sin_family = PF_INET;
+                   ss.sin_addr.s_addr = ip->saddr;
+                   getnameinfo((struct sockaddr *) &ss, sizeof(ss),
+                               sbuff, sizeof(sbuff), NULL, 0, NI_NUMERICHOST);
+
+                   memset(&sd, 0, sizeof(sd));
+                   sd.sin_family = PF_INET;
+                   sd.sin_addr.s_addr = ip->daddr;
+                   getnameinfo((struct sockaddr *) &sd, sizeof(sd),
+                               dbuff, sizeof(dbuff), NULL, 0, NI_NUMERICHOST);
+
+                   printf("%s -> %s, ", sbuff, dbuff);
+           }
+
+           printf("rxhash: 0x%x\n", ppd->hv1.tp_rxhash);
+    }
+
+    static void walk_block(struct block_desc *pbd, const int block_num)
+    {
+           int num_pkts = pbd->h1.num_pkts, i;
+           unsigned long bytes = 0;
+           struct tpacket3_hdr *ppd;
+
+           ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd +
+                                       pbd->h1.offset_to_first_pkt);
+           for (i = 0; i < num_pkts; ++i) {
+                   bytes += ppd->tp_snaplen;
+                   display(ppd);
+
+                   ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd +
+                                               ppd->tp_next_offset);
+           }
+
+           packets_total += num_pkts;
+           bytes_total += bytes;
+    }
+
+    static void flush_block(struct block_desc *pbd)
+    {
+           pbd->h1.block_status = TP_STATUS_KERNEL;
+    }
+
+    static void teardown_socket(struct ring *ring, int fd)
+    {
+           munmap(ring->map, ring->req.tp_block_size * ring->req.tp_block_nr);
+           free(ring->rd);
+           close(fd);
+    }
+
+    int main(int argc, char **argp)
+    {
+           int fd, err;
+           socklen_t len;
+           struct ring ring;
+           struct pollfd pfd;
+           unsigned int block_num = 0, blocks = 64;
+           struct block_desc *pbd;
+           struct tpacket_stats_v3 stats;
+
+           if (argc != 2) {
+                   fprintf(stderr, "Usage: %s INTERFACE\n", argp[0]);
+                   return EXIT_FAILURE;
+           }
+
+           signal(SIGINT, sighandler);
+
+           memset(&ring, 0, sizeof(ring));
+           fd = setup_socket(&ring, argp[argc - 1]);
+           assert(fd > 0);
+
+           memset(&pfd, 0, sizeof(pfd));
+           pfd.fd = fd;
+           pfd.events = POLLIN | POLLERR;
+           pfd.revents = 0;
+
+           while (likely(!sigint)) {
+                   pbd = (struct block_desc *) ring.rd[block_num].iov_base;
+
+                   if ((pbd->h1.block_status & TP_STATUS_USER) == 0) {
+                           poll(&pfd, 1, -1);
+                           continue;
+                   }
+
+                   walk_block(pbd, block_num);
+                   flush_block(pbd);
+                   block_num = (block_num + 1) % blocks;
+           }
+
+           len = sizeof(stats);
+           err = getsockopt(fd, SOL_PACKET, PACKET_STATISTICS, &stats, &len);
+           if (err < 0) {
+                   perror("getsockopt");
+                   exit(1);
+           }
+
+           fflush(stdout);
+           printf("\nReceived %u packets, %lu bytes, %u dropped, freeze_q_cnt: %u\n",
+               stats.tp_packets, bytes_total, stats.tp_drops,
+               stats.tp_freeze_q_cnt);
+
+           teardown_socket(&ring, fd);
+           return 0;
+    }
+
+PACKET_QDISC_BYPASS
+===================
+
+If there is a requirement to load the network with many packets in a similar
+fashion as pktgen does, you might set the following option after socket
+creation::
+
+    int one = 1;
+    setsockopt(fd, SOL_PACKET, PACKET_QDISC_BYPASS, &one, sizeof(one));
+
+This has the side-effect, that packets sent through PF_PACKET will bypass the
+kernel's qdisc layer and are forcedly pushed to the driver directly. Meaning,
+packet are not buffered, tc disciplines are ignored, increased loss can occur
+and such packets are also not visible to other PF_PACKET sockets anymore. So,
+you have been warned; generally, this can be useful for stress testing various
+components of a system.
+
+On default, PACKET_QDISC_BYPASS is disabled and needs to be explicitly enabled
+on PF_PACKET sockets.
+
+PACKET_TIMESTAMP
+================
+
+The PACKET_TIMESTAMP setting determines the source of the timestamp in
+the packet meta information for mmap(2)ed RX_RING and TX_RINGs.  If your
+NIC is capable of timestamping packets in hardware, you can request those
+hardware timestamps to be used. Note: you may need to enable the generation
+of hardware timestamps with SIOCSHWTSTAMP (see related information from
+Documentation/networking/timestamping.rst).
+
+PACKET_TIMESTAMP accepts the same integer bit field as SO_TIMESTAMPING::
+
+    int req = SOF_TIMESTAMPING_RAW_HARDWARE;
+    setsockopt(fd, SOL_PACKET, PACKET_TIMESTAMP, (void *) &req, sizeof(req))
+
+For the mmap(2)ed ring buffers, such timestamps are stored in the
+``tpacket{,2,3}_hdr`` structure's tp_sec and ``tp_{n,u}sec`` members.
+To determine what kind of timestamp has been reported, the tp_status field
+is binary or'ed with the following possible bits ...
+
+::
+
+    TP_STATUS_TS_RAW_HARDWARE
+    TP_STATUS_TS_SOFTWARE
+
+... that are equivalent to its ``SOF_TIMESTAMPING_*`` counterparts. For the
+RX_RING, if neither is set (i.e. PACKET_TIMESTAMP is not set), then a
+software fallback was invoked *within* PF_PACKET's processing code (less
+precise).
+
+Getting timestamps for the TX_RING works as follows: i) fill the ring frames,
+ii) call sendto() e.g. in blocking mode, iii) wait for status of relevant
+frames to be updated resp. the frame handed over to the application, iv) walk
+through the frames to pick up the individual hw/sw timestamps.
+
+Only (!) if transmit timestamping is enabled, then these bits are combined
+with binary | with TP_STATUS_AVAILABLE, so you must check for that in your
+application (e.g. !(tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING))
+in a first step to see if the frame belongs to the application, and then
+one can extract the type of timestamp in a second step from tp_status)!
+
+If you don't care about them, thus having it disabled, checking for
+TP_STATUS_AVAILABLE resp. TP_STATUS_WRONG_FORMAT is sufficient. If in the
+TX_RING part only TP_STATUS_AVAILABLE is set, then the tp_sec and tp_{n,u}sec
+members do not contain a valid value. For TX_RINGs, by default no timestamp
+is generated!
+
+See include/linux/net_tstamp.h and Documentation/networking/timestamping.rst
+for more information on hardware timestamps.
+
+Miscellaneous bits
+==================
+
+- Packet sockets work well together with Linux socket filters, thus you also
+  might want to have a look at Documentation/networking/filter.rst
+
+THANKS
+======
+
+   Jesse Brandeburg, for fixing my grammathical/spelling errors
diff --git a/Documentation/networking/packet_mmap.txt b/Documentation/networking/packet_mmap.txt
deleted file mode 100644 (file)
index 999eb41..0000000
+++ /dev/null
@@ -1,1061 +0,0 @@
---------------------------------------------------------------------------------
-+ ABSTRACT
---------------------------------------------------------------------------------
-
-This file documents the mmap() facility available with the PACKET
-socket interface on 2.4/2.6/3.x kernels. This type of sockets is used for
-i) capture network traffic with utilities like tcpdump, ii) transmit network
-traffic, or any other that needs raw access to network interface.
-
-Howto can be found at:
-    https://sites.google.com/site/packetmmap/
-
-Please send your comments to
-    Ulisses Alonso Camaró <uaca@i.hate.spam.alumni.uv.es>
-    Johann Baudy
-
--------------------------------------------------------------------------------
-+ Why use PACKET_MMAP
---------------------------------------------------------------------------------
-
-In Linux 2.4/2.6/3.x if PACKET_MMAP is not enabled, the capture process is very
-inefficient. It uses very limited buffers and requires one system call to
-capture each packet, it requires two if you want to get packet's timestamp
-(like libpcap always does).
-
-In the other hand PACKET_MMAP is very efficient. PACKET_MMAP provides a size 
-configurable circular buffer mapped in user space that can be used to either
-send or receive packets. This way reading packets just needs to wait for them,
-most of the time there is no need to issue a single system call. Concerning
-transmission, multiple packets can be sent through one system call to get the
-highest bandwidth. By using a shared buffer between the kernel and the user
-also has the benefit of minimizing packet copies.
-
-It's fine to use PACKET_MMAP to improve the performance of the capture and
-transmission process, but it isn't everything. At least, if you are capturing
-at high speeds (this is relative to the cpu speed), you should check if the
-device driver of your network interface card supports some sort of interrupt
-load mitigation or (even better) if it supports NAPI, also make sure it is
-enabled. For transmission, check the MTU (Maximum Transmission Unit) used and
-supported by devices of your network. CPU IRQ pinning of your network interface
-card can also be an advantage.
-
---------------------------------------------------------------------------------
-+ How to use mmap() to improve capture process
---------------------------------------------------------------------------------
-
-From the user standpoint, you should use the higher level libpcap library, which
-is a de facto standard, portable across nearly all operating systems
-including Win32. 
-
-Packet MMAP support was integrated into libpcap around the time of version 1.3.0;
-TPACKET_V3 support was added in version 1.5.0
-
---------------------------------------------------------------------------------
-+ How to use mmap() directly to improve capture process
---------------------------------------------------------------------------------
-
-From the system calls stand point, the use of PACKET_MMAP involves
-the following process:
-
-
-[setup]     socket() -------> creation of the capture socket
-            setsockopt() ---> allocation of the circular buffer (ring)
-                              option: PACKET_RX_RING
-            mmap() ---------> mapping of the allocated buffer to the
-                              user process
-
-[capture]   poll() ---------> to wait for incoming packets
-
-[shutdown]  close() --------> destruction of the capture socket and
-                              deallocation of all associated 
-                              resources.
-
-
-socket creation and destruction is straight forward, and is done 
-the same way with or without PACKET_MMAP:
-
- int fd = socket(PF_PACKET, mode, htons(ETH_P_ALL));
-
-where mode is SOCK_RAW for the raw interface were link level
-information can be captured or SOCK_DGRAM for the cooked
-interface where link level information capture is not 
-supported and a link level pseudo-header is provided 
-by the kernel.
-
-The destruction of the socket and all associated resources
-is done by a simple call to close(fd).
-
-Similarly as without PACKET_MMAP, it is possible to use one socket
-for capture and transmission. This can be done by mapping the
-allocated RX and TX buffer ring with a single mmap() call.
-See "Mapping and use of the circular buffer (ring)".
-
-Next I will describe PACKET_MMAP settings and its constraints,
-also the mapping of the circular buffer in the user process and 
-the use of this buffer.
-
---------------------------------------------------------------------------------
-+ How to use mmap() directly to improve transmission process
---------------------------------------------------------------------------------
-Transmission process is similar to capture as shown below.
-
-[setup]          socket() -------> creation of the transmission socket
-                 setsockopt() ---> allocation of the circular buffer (ring)
-                                   option: PACKET_TX_RING
-                 bind() ---------> bind transmission socket with a network interface
-                 mmap() ---------> mapping of the allocated buffer to the
-                                   user process
-
-[transmission]   poll() ---------> wait for free packets (optional)
-                 send() ---------> send all packets that are set as ready in
-                                   the ring
-                                   The flag MSG_DONTWAIT can be used to return
-                                   before end of transfer.
-
-[shutdown]  close() --------> destruction of the transmission socket and
-                              deallocation of all associated resources.
-
-Socket creation and destruction is also straight forward, and is done
-the same way as in capturing described in the previous paragraph:
-
- int fd = socket(PF_PACKET, mode, 0);
-
-The protocol can optionally be 0 in case we only want to transmit
-via this socket, which avoids an expensive call to packet_rcv().
-In this case, you also need to bind(2) the TX_RING with sll_protocol = 0
-set. Otherwise, htons(ETH_P_ALL) or any other protocol, for example.
-
-Binding the socket to your network interface is mandatory (with zero copy) to
-know the header size of frames used in the circular buffer.
-
-As capture, each frame contains two parts:
-
- --------------------
-| struct tpacket_hdr | Header. It contains the status of
-|                    | of this frame
-|--------------------|
-| data buffer        |
-.                    .  Data that will be sent over the network interface.
-.                    .
- --------------------
-
- bind() associates the socket to your network interface thanks to
- sll_ifindex parameter of struct sockaddr_ll.
-
- Initialization example:
-
- struct sockaddr_ll my_addr;
- struct ifreq s_ifr;
- ...
-
- strncpy (s_ifr.ifr_name, "eth0", sizeof(s_ifr.ifr_name));
-
- /* get interface index of eth0 */
- ioctl(this->socket, SIOCGIFINDEX, &s_ifr);
-
- /* fill sockaddr_ll struct to prepare binding */
- my_addr.sll_family = AF_PACKET;
- my_addr.sll_protocol = htons(ETH_P_ALL);
- my_addr.sll_ifindex =  s_ifr.ifr_ifindex;
-
- /* bind socket to eth0 */
- bind(this->socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll));
-
- A complete tutorial is available at: https://sites.google.com/site/packetmmap/
-
-By default, the user should put data at :
- frame base + TPACKET_HDRLEN - sizeof(struct sockaddr_ll)
-
-So, whatever you choose for the socket mode (SOCK_DGRAM or SOCK_RAW),
-the beginning of the user data will be at :
- frame base + TPACKET_ALIGN(sizeof(struct tpacket_hdr))
-
-If you wish to put user data at a custom offset from the beginning of
-the frame (for payload alignment with SOCK_RAW mode for instance) you
-can set tp_net (with SOCK_DGRAM) or tp_mac (with SOCK_RAW). In order
-to make this work it must be enabled previously with setsockopt()
-and the PACKET_TX_HAS_OFF option.
-
---------------------------------------------------------------------------------
-+ PACKET_MMAP settings
---------------------------------------------------------------------------------
-
-To setup PACKET_MMAP from user level code is done with a call like
-
- - Capture process
-     setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *) &req, sizeof(req))
- - Transmission process
-     setsockopt(fd, SOL_PACKET, PACKET_TX_RING, (void *) &req, sizeof(req))
-
-The most significant argument in the previous call is the req parameter, 
-this parameter must to have the following structure:
-
-    struct tpacket_req
-    {
-        unsigned int    tp_block_size;  /* Minimal size of contiguous block */
-        unsigned int    tp_block_nr;    /* Number of blocks */
-        unsigned int    tp_frame_size;  /* Size of frame */
-        unsigned int    tp_frame_nr;    /* Total number of frames */
-    };
-
-This structure is defined in /usr/include/linux/if_packet.h and establishes a 
-circular buffer (ring) of unswappable memory.
-Being mapped in the capture process allows reading the captured frames and 
-related meta-information like timestamps without requiring a system call.
-
-Frames are grouped in blocks. Each block is a physically contiguous
-region of memory and holds tp_block_size/tp_frame_size frames. The total number 
-of blocks is tp_block_nr. Note that tp_frame_nr is a redundant parameter because
-
-    frames_per_block = tp_block_size/tp_frame_size
-
-indeed, packet_set_ring checks that the following condition is true
-
-    frames_per_block * tp_block_nr == tp_frame_nr
-
-Lets see an example, with the following values:
-
-     tp_block_size= 4096
-     tp_frame_size= 2048
-     tp_block_nr  = 4
-     tp_frame_nr  = 8
-
-we will get the following buffer structure:
-
-        block #1                 block #2         
-+---------+---------+    +---------+---------+    
-| frame 1 | frame 2 |    | frame 3 | frame 4 |    
-+---------+---------+    +---------+---------+    
-
-        block #3                 block #4
-+---------+---------+    +---------+---------+
-| frame 5 | frame 6 |    | frame 7 | frame 8 |
-+---------+---------+    +---------+---------+
-
-A frame can be of any size with the only condition it can fit in a block. A block
-can only hold an integer number of frames, or in other words, a frame cannot 
-be spawned across two blocks, so there are some details you have to take into 
-account when choosing the frame_size. See "Mapping and use of the circular 
-buffer (ring)".
-
---------------------------------------------------------------------------------
-+ PACKET_MMAP setting constraints
---------------------------------------------------------------------------------
-
-In kernel versions prior to 2.4.26 (for the 2.4 branch) and 2.6.5 (2.6 branch),
-the PACKET_MMAP buffer could hold only 32768 frames in a 32 bit architecture or
-16384 in a 64 bit architecture. For information on these kernel versions
-see http://pusa.uv.es/~ulisses/packet_mmap/packet_mmap.pre-2.4.26_2.6.5.txt
-
- Block size limit
-------------------
-
-As stated earlier, each block is a contiguous physical region of memory. These 
-memory regions are allocated with calls to the __get_free_pages() function. As 
-the name indicates, this function allocates pages of memory, and the second
-argument is "order" or a power of two number of pages, that is 
-(for PAGE_SIZE == 4096) order=0 ==> 4096 bytes, order=1 ==> 8192 bytes, 
-order=2 ==> 16384 bytes, etc. The maximum size of a 
-region allocated by __get_free_pages is determined by the MAX_ORDER macro. More 
-precisely the limit can be calculated as:
-
-   PAGE_SIZE << MAX_ORDER
-
-   In a i386 architecture PAGE_SIZE is 4096 bytes 
-   In a 2.4/i386 kernel MAX_ORDER is 10
-   In a 2.6/i386 kernel MAX_ORDER is 11
-
-So get_free_pages can allocate as much as 4MB or 8MB in a 2.4/2.6 kernel 
-respectively, with an i386 architecture.
-
-User space programs can include /usr/include/sys/user.h and 
-/usr/include/linux/mmzone.h to get PAGE_SIZE MAX_ORDER declarations.
-
-The pagesize can also be determined dynamically with the getpagesize (2) 
-system call. 
-
- Block number limit
---------------------
-
-To understand the constraints of PACKET_MMAP, we have to see the structure 
-used to hold the pointers to each block.
-
-Currently, this structure is a dynamically allocated vector with kmalloc 
-called pg_vec, its size limits the number of blocks that can be allocated.
-
-    +---+---+---+---+
-    | x | x | x | x |
-    +---+---+---+---+
-      |   |   |   |
-      |   |   |   v
-      |   |   v  block #4
-      |   v  block #3
-      v  block #2
-     block #1
-
-kmalloc allocates any number of bytes of physically contiguous memory from 
-a pool of pre-determined sizes. This pool of memory is maintained by the slab 
-allocator which is at the end the responsible for doing the allocation and 
-hence which imposes the maximum memory that kmalloc can allocate. 
-
-In a 2.4/2.6 kernel and the i386 architecture, the limit is 131072 bytes. The 
-predetermined sizes that kmalloc uses can be checked in the "size-<bytes>" 
-entries of /proc/slabinfo
-
-In a 32 bit architecture, pointers are 4 bytes long, so the total number of 
-pointers to blocks is
-
-     131072/4 = 32768 blocks
-
- PACKET_MMAP buffer size calculator
-------------------------------------
-
-Definitions:
-
-<size-max>    : is the maximum size of allocable with kmalloc (see /proc/slabinfo)
-<pointer size>: depends on the architecture -- sizeof(void *)
-<page size>   : depends on the architecture -- PAGE_SIZE or getpagesize (2)
-<max-order>   : is the value defined with MAX_ORDER
-<frame size>  : it's an upper bound of frame's capture size (more on this later)
-
-from these definitions we will derive 
-
-       <block number> = <size-max>/<pointer size>
-       <block size> = <pagesize> << <max-order>
-
-so, the max buffer size is
-
-       <block number> * <block size>
-
-and, the number of frames be
-
-       <block number> * <block size> / <frame size>
-
-Suppose the following parameters, which apply for 2.6 kernel and an
-i386 architecture:
-
-       <size-max> = 131072 bytes
-       <pointer size> = 4 bytes
-       <pagesize> = 4096 bytes
-       <max-order> = 11
-
-and a value for <frame size> of 2048 bytes. These parameters will yield
-
-       <block number> = 131072/4 = 32768 blocks
-       <block size> = 4096 << 11 = 8 MiB.
-
-and hence the buffer will have a 262144 MiB size. So it can hold 
-262144 MiB / 2048 bytes = 134217728 frames
-
-Actually, this buffer size is not possible with an i386 architecture. 
-Remember that the memory is allocated in kernel space, in the case of 
-an i386 kernel's memory size is limited to 1GiB.
-
-All memory allocations are not freed until the socket is closed. The memory 
-allocations are done with GFP_KERNEL priority, this basically means that 
-the allocation can wait and swap other process' memory in order to allocate 
-the necessary memory, so normally limits can be reached.
-
- Other constraints
--------------------
-
-If you check the source code you will see that what I draw here as a frame
-is not only the link level frame. At the beginning of each frame there is a 
-header called struct tpacket_hdr used in PACKET_MMAP to hold link level's frame
-meta information like timestamp. So what we draw here a frame it's really 
-the following (from include/linux/if_packet.h):
-
-/*
-   Frame structure:
-
-   - Start. Frame must be aligned to TPACKET_ALIGNMENT=16
-   - struct tpacket_hdr
-   - pad to TPACKET_ALIGNMENT=16
-   - struct sockaddr_ll
-   - Gap, chosen so that packet data (Start+tp_net) aligns to 
-     TPACKET_ALIGNMENT=16
-   - Start+tp_mac: [ Optional MAC header ]
-   - Start+tp_net: Packet data, aligned to TPACKET_ALIGNMENT=16.
-   - Pad to align to TPACKET_ALIGNMENT=16
- */
- The following are conditions that are checked in packet_set_ring
-
-   tp_block_size must be a multiple of PAGE_SIZE (1)
-   tp_frame_size must be greater than TPACKET_HDRLEN (obvious)
-   tp_frame_size must be a multiple of TPACKET_ALIGNMENT
-   tp_frame_nr   must be exactly frames_per_block*tp_block_nr
-
-Note that tp_block_size should be chosen to be a power of two or there will
-be a waste of memory.
-
---------------------------------------------------------------------------------
-+ Mapping and use of the circular buffer (ring)
---------------------------------------------------------------------------------
-
-The mapping of the buffer in the user process is done with the conventional 
-mmap function. Even the circular buffer is compound of several physically
-discontiguous blocks of memory, they are contiguous to the user space, hence
-just one call to mmap is needed:
-
-    mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
-
-If tp_frame_size is a divisor of tp_block_size frames will be 
-contiguously spaced by tp_frame_size bytes. If not, each
-tp_block_size/tp_frame_size frames there will be a gap between 
-the frames. This is because a frame cannot be spawn across two
-blocks. 
-
-To use one socket for capture and transmission, the mapping of both the
-RX and TX buffer ring has to be done with one call to mmap:
-
-    ...
-    setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &foo, sizeof(foo));
-    setsockopt(fd, SOL_PACKET, PACKET_TX_RING, &bar, sizeof(bar));
-    ...
-    rx_ring = mmap(0, size * 2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
-    tx_ring = rx_ring + size;
-
-RX must be the first as the kernel maps the TX ring memory right
-after the RX one.
-
-At the beginning of each frame there is an status field (see 
-struct tpacket_hdr). If this field is 0 means that the frame is ready
-to be used for the kernel, If not, there is a frame the user can read 
-and the following flags apply:
-
-+++ Capture process:
-     from include/linux/if_packet.h
-
-     #define TP_STATUS_COPY          (1 << 1)
-     #define TP_STATUS_LOSING        (1 << 2)
-     #define TP_STATUS_CSUMNOTREADY  (1 << 3)
-     #define TP_STATUS_CSUM_VALID    (1 << 7)
-
-TP_STATUS_COPY        : This flag indicates that the frame (and associated
-                        meta information) has been truncated because it's 
-                        larger than tp_frame_size. This packet can be 
-                        read entirely with recvfrom().
-                        
-                        In order to make this work it must to be
-                        enabled previously with setsockopt() and 
-                        the PACKET_COPY_THRESH option. 
-
-                        The number of frames that can be buffered to
-                        be read with recvfrom is limited like a normal socket.
-                        See the SO_RCVBUF option in the socket (7) man page.
-
-TP_STATUS_LOSING      : indicates there were packet drops from last time 
-                        statistics where checked with getsockopt() and
-                        the PACKET_STATISTICS option.
-
-TP_STATUS_CSUMNOTREADY: currently it's used for outgoing IP packets which 
-                        its checksum will be done in hardware. So while
-                        reading the packet we should not try to check the 
-                        checksum. 
-
-TP_STATUS_CSUM_VALID  : This flag indicates that at least the transport
-                        header checksum of the packet has been already
-                        validated on the kernel side. If the flag is not set
-                        then we are free to check the checksum by ourselves
-                        provided that TP_STATUS_CSUMNOTREADY is also not set.
-
-for convenience there are also the following defines:
-
-     #define TP_STATUS_KERNEL        0
-     #define TP_STATUS_USER          1
-
-The kernel initializes all frames to TP_STATUS_KERNEL, when the kernel
-receives a packet it puts in the buffer and updates the status with
-at least the TP_STATUS_USER flag. Then the user can read the packet,
-once the packet is read the user must zero the status field, so the kernel 
-can use again that frame buffer.
-
-The user can use poll (any other variant should apply too) to check if new
-packets are in the ring:
-
-    struct pollfd pfd;
-
-    pfd.fd = fd;
-    pfd.revents = 0;
-    pfd.events = POLLIN|POLLRDNORM|POLLERR;
-
-    if (status == TP_STATUS_KERNEL)
-        retval = poll(&pfd, 1, timeout);
-
-It doesn't incur in a race condition to first check the status value and 
-then poll for frames.
-
-++ Transmission process
-Those defines are also used for transmission:
-
-     #define TP_STATUS_AVAILABLE        0 // Frame is available
-     #define TP_STATUS_SEND_REQUEST     1 // Frame will be sent on next send()
-     #define TP_STATUS_SENDING          2 // Frame is currently in transmission
-     #define TP_STATUS_WRONG_FORMAT     4 // Frame format is not correct
-
-First, the kernel initializes all frames to TP_STATUS_AVAILABLE. To send a
-packet, the user fills a data buffer of an available frame, sets tp_len to
-current data buffer size and sets its status field to TP_STATUS_SEND_REQUEST.
-This can be done on multiple frames. Once the user is ready to transmit, it
-calls send(). Then all buffers with status equal to TP_STATUS_SEND_REQUEST are
-forwarded to the network device. The kernel updates each status of sent
-frames with TP_STATUS_SENDING until the end of transfer.
-At the end of each transfer, buffer status returns to TP_STATUS_AVAILABLE.
-
-    header->tp_len = in_i_size;
-    header->tp_status = TP_STATUS_SEND_REQUEST;
-    retval = send(this->socket, NULL, 0, 0);
-
-The user can also use poll() to check if a buffer is available:
-(status == TP_STATUS_SENDING)
-
-    struct pollfd pfd;
-    pfd.fd = fd;
-    pfd.revents = 0;
-    pfd.events = POLLOUT;
-    retval = poll(&pfd, 1, timeout);
-
--------------------------------------------------------------------------------
-+ What TPACKET versions are available and when to use them?
--------------------------------------------------------------------------------
-
- int val = tpacket_version;
- setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));
- getsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));
-
-where 'tpacket_version' can be TPACKET_V1 (default), TPACKET_V2, TPACKET_V3.
-
-TPACKET_V1:
-       - Default if not otherwise specified by setsockopt(2)
-       - RX_RING, TX_RING available
-
-TPACKET_V1 --> TPACKET_V2:
-       - Made 64 bit clean due to unsigned long usage in TPACKET_V1
-         structures, thus this also works on 64 bit kernel with 32 bit
-         userspace and the like
-       - Timestamp resolution in nanoseconds instead of microseconds
-       - RX_RING, TX_RING available
-       - VLAN metadata information available for packets
-         (TP_STATUS_VLAN_VALID, TP_STATUS_VLAN_TPID_VALID),
-         in the tpacket2_hdr structure:
-               - TP_STATUS_VLAN_VALID bit being set into the tp_status field indicates
-                 that the tp_vlan_tci field has valid VLAN TCI value
-               - TP_STATUS_VLAN_TPID_VALID bit being set into the tp_status field
-                 indicates that the tp_vlan_tpid field has valid VLAN TPID value
-       - How to switch to TPACKET_V2:
-               1. Replace struct tpacket_hdr by struct tpacket2_hdr
-               2. Query header len and save
-               3. Set protocol version to 2, set up ring as usual
-               4. For getting the sockaddr_ll,
-                  use (void *)hdr + TPACKET_ALIGN(hdrlen) instead of
-                  (void *)hdr + TPACKET_ALIGN(sizeof(struct tpacket_hdr))
-
-TPACKET_V2 --> TPACKET_V3:
-       - Flexible buffer implementation for RX_RING:
-               1. Blocks can be configured with non-static frame-size
-               2. Read/poll is at a block-level (as opposed to packet-level)
-               3. Added poll timeout to avoid indefinite user-space wait
-                  on idle links
-               4. Added user-configurable knobs:
-                       4.1 block::timeout
-                       4.2 tpkt_hdr::sk_rxhash
-       - RX Hash data available in user space
-       - TX_RING semantics are conceptually similar to TPACKET_V2;
-         use tpacket3_hdr instead of tpacket2_hdr, and TPACKET3_HDRLEN
-         instead of TPACKET2_HDRLEN. In the current implementation,
-         the tp_next_offset field in the tpacket3_hdr MUST be set to
-         zero, indicating that the ring does not hold variable sized frames.
-         Packets with non-zero values of tp_next_offset will be dropped.
-
--------------------------------------------------------------------------------
-+ AF_PACKET fanout mode
--------------------------------------------------------------------------------
-
-In the AF_PACKET fanout mode, packet reception can be load balanced among
-processes. This also works in combination with mmap(2) on packet sockets.
-
-Currently implemented fanout policies are:
-
-  - PACKET_FANOUT_HASH: schedule to socket by skb's packet hash
-  - PACKET_FANOUT_LB: schedule to socket by round-robin
-  - PACKET_FANOUT_CPU: schedule to socket by CPU packet arrives on
-  - PACKET_FANOUT_RND: schedule to socket by random selection
-  - PACKET_FANOUT_ROLLOVER: if one socket is full, rollover to another
-  - PACKET_FANOUT_QM: schedule to socket by skbs recorded queue_mapping
-
-Minimal example code by David S. Miller (try things like "./test eth0 hash",
-"./test eth0 lb", etc.):
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-
-#include <unistd.h>
-
-#include <linux/if_ether.h>
-#include <linux/if_packet.h>
-
-#include <net/if.h>
-
-static const char *device_name;
-static int fanout_type;
-static int fanout_id;
-
-#ifndef PACKET_FANOUT
-# define PACKET_FANOUT                 18
-# define PACKET_FANOUT_HASH            0
-# define PACKET_FANOUT_LB              1
-#endif
-
-static int setup_socket(void)
-{
-       int err, fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP));
-       struct sockaddr_ll ll;
-       struct ifreq ifr;
-       int fanout_arg;
-
-       if (fd < 0) {
-               perror("socket");
-               return EXIT_FAILURE;
-       }
-
-       memset(&ifr, 0, sizeof(ifr));
-       strcpy(ifr.ifr_name, device_name);
-       err = ioctl(fd, SIOCGIFINDEX, &ifr);
-       if (err < 0) {
-               perror("SIOCGIFINDEX");
-               return EXIT_FAILURE;
-       }
-
-       memset(&ll, 0, sizeof(ll));
-       ll.sll_family = AF_PACKET;
-       ll.sll_ifindex = ifr.ifr_ifindex;
-       err = bind(fd, (struct sockaddr *) &ll, sizeof(ll));
-       if (err < 0) {
-               perror("bind");
-               return EXIT_FAILURE;
-       }
-
-       fanout_arg = (fanout_id | (fanout_type << 16));
-       err = setsockopt(fd, SOL_PACKET, PACKET_FANOUT,
-                        &fanout_arg, sizeof(fanout_arg));
-       if (err) {
-               perror("setsockopt");
-               return EXIT_FAILURE;
-       }
-
-       return fd;
-}
-
-static void fanout_thread(void)
-{
-       int fd = setup_socket();
-       int limit = 10000;
-
-       if (fd < 0)
-               exit(fd);
-
-       while (limit-- > 0) {
-               char buf[1600];
-               int err;
-
-               err = read(fd, buf, sizeof(buf));
-               if (err < 0) {
-                       perror("read");
-                       exit(EXIT_FAILURE);
-               }
-               if ((limit % 10) == 0)
-                       fprintf(stdout, "(%d) \n", getpid());
-       }
-
-       fprintf(stdout, "%d: Received 10000 packets\n", getpid());
-
-       close(fd);
-       exit(0);
-}
-
-int main(int argc, char **argp)
-{
-       int fd, err;
-       int i;
-
-       if (argc != 3) {
-               fprintf(stderr, "Usage: %s INTERFACE {hash|lb}\n", argp[0]);
-               return EXIT_FAILURE;
-       }
-
-       if (!strcmp(argp[2], "hash"))
-               fanout_type = PACKET_FANOUT_HASH;
-       else if (!strcmp(argp[2], "lb"))
-               fanout_type = PACKET_FANOUT_LB;
-       else {
-               fprintf(stderr, "Unknown fanout type [%s]\n", argp[2]);
-               exit(EXIT_FAILURE);
-       }
-
-       device_name = argp[1];
-       fanout_id = getpid() & 0xffff;
-
-       for (i = 0; i < 4; i++) {
-               pid_t pid = fork();
-
-               switch (pid) {
-               case 0:
-                       fanout_thread();
-
-               case -1:
-                       perror("fork");
-                       exit(EXIT_FAILURE);
-               }
-       }
-
-       for (i = 0; i < 4; i++) {
-               int status;
-
-               wait(&status);
-       }
-
-       return 0;
-}
-
--------------------------------------------------------------------------------
-+ AF_PACKET TPACKET_V3 example
--------------------------------------------------------------------------------
-
-AF_PACKET's TPACKET_V3 ring buffer can be configured to use non-static frame
-sizes by doing it's own memory management. It is based on blocks where polling
-works on a per block basis instead of per ring as in TPACKET_V2 and predecessor.
-
-It is said that TPACKET_V3 brings the following benefits:
- *) ~15 - 20% reduction in CPU-usage
- *) ~20% increase in packet capture rate
- *) ~2x increase in packet density
- *) Port aggregation analysis
- *) Non static frame size to capture entire packet payload
-
-So it seems to be a good candidate to be used with packet fanout.
-
-Minimal example code by Daniel Borkmann based on Chetan Loke's lolpcap (compile
-it with gcc -Wall -O2 blob.c, and try things like "./a.out eth0", etc.):
-
-/* Written from scratch, but kernel-to-user space API usage
- * dissected from lolpcap:
- *  Copyright 2011, Chetan Loke <loke.chetan@gmail.com>
- *  License: GPL, version 2.0
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <assert.h>
-#include <net/if.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <poll.h>
-#include <unistd.h>
-#include <signal.h>
-#include <inttypes.h>
-#include <sys/socket.h>
-#include <sys/mman.h>
-#include <linux/if_packet.h>
-#include <linux/if_ether.h>
-#include <linux/ip.h>
-
-#ifndef likely
-# define likely(x)             __builtin_expect(!!(x), 1)
-#endif
-#ifndef unlikely
-# define unlikely(x)           __builtin_expect(!!(x), 0)
-#endif
-
-struct block_desc {
-       uint32_t version;
-       uint32_t offset_to_priv;
-       struct tpacket_hdr_v1 h1;
-};
-
-struct ring {
-       struct iovec *rd;
-       uint8_t *map;
-       struct tpacket_req3 req;
-};
-
-static unsigned long packets_total = 0, bytes_total = 0;
-static sig_atomic_t sigint = 0;
-
-static void sighandler(int num)
-{
-       sigint = 1;
-}
-
-static int setup_socket(struct ring *ring, char *netdev)
-{
-       int err, i, fd, v = TPACKET_V3;
-       struct sockaddr_ll ll;
-       unsigned int blocksiz = 1 << 22, framesiz = 1 << 11;
-       unsigned int blocknum = 64;
-
-       fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
-       if (fd < 0) {
-               perror("socket");
-               exit(1);
-       }
-
-       err = setsockopt(fd, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
-       if (err < 0) {
-               perror("setsockopt");
-               exit(1);
-       }
-
-       memset(&ring->req, 0, sizeof(ring->req));
-       ring->req.tp_block_size = blocksiz;
-       ring->req.tp_frame_size = framesiz;
-       ring->req.tp_block_nr = blocknum;
-       ring->req.tp_frame_nr = (blocksiz * blocknum) / framesiz;
-       ring->req.tp_retire_blk_tov = 60;
-       ring->req.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH;
-
-       err = setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &ring->req,
-                        sizeof(ring->req));
-       if (err < 0) {
-               perror("setsockopt");
-               exit(1);
-       }
-
-       ring->map = mmap(NULL, ring->req.tp_block_size * ring->req.tp_block_nr,
-                        PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, 0);
-       if (ring->map == MAP_FAILED) {
-               perror("mmap");
-               exit(1);
-       }
-
-       ring->rd = malloc(ring->req.tp_block_nr * sizeof(*ring->rd));
-       assert(ring->rd);
-       for (i = 0; i < ring->req.tp_block_nr; ++i) {
-               ring->rd[i].iov_base = ring->map + (i * ring->req.tp_block_size);
-               ring->rd[i].iov_len = ring->req.tp_block_size;
-       }
-
-       memset(&ll, 0, sizeof(ll));
-       ll.sll_family = PF_PACKET;
-       ll.sll_protocol = htons(ETH_P_ALL);
-       ll.sll_ifindex = if_nametoindex(netdev);
-       ll.sll_hatype = 0;
-       ll.sll_pkttype = 0;
-       ll.sll_halen = 0;
-
-       err = bind(fd, (struct sockaddr *) &ll, sizeof(ll));
-       if (err < 0) {
-               perror("bind");
-               exit(1);
-       }
-
-       return fd;
-}
-
-static void display(struct tpacket3_hdr *ppd)
-{
-       struct ethhdr *eth = (struct ethhdr *) ((uint8_t *) ppd + ppd->tp_mac);
-       struct iphdr *ip = (struct iphdr *) ((uint8_t *) eth + ETH_HLEN);
-
-       if (eth->h_proto == htons(ETH_P_IP)) {
-               struct sockaddr_in ss, sd;
-               char sbuff[NI_MAXHOST], dbuff[NI_MAXHOST];
-
-               memset(&ss, 0, sizeof(ss));
-               ss.sin_family = PF_INET;
-               ss.sin_addr.s_addr = ip->saddr;
-               getnameinfo((struct sockaddr *) &ss, sizeof(ss),
-                           sbuff, sizeof(sbuff), NULL, 0, NI_NUMERICHOST);
-
-               memset(&sd, 0, sizeof(sd));
-               sd.sin_family = PF_INET;
-               sd.sin_addr.s_addr = ip->daddr;
-               getnameinfo((struct sockaddr *) &sd, sizeof(sd),
-                           dbuff, sizeof(dbuff), NULL, 0, NI_NUMERICHOST);
-
-               printf("%s -> %s, ", sbuff, dbuff);
-       }
-
-       printf("rxhash: 0x%x\n", ppd->hv1.tp_rxhash);
-}
-
-static void walk_block(struct block_desc *pbd, const int block_num)
-{
-       int num_pkts = pbd->h1.num_pkts, i;
-       unsigned long bytes = 0;
-       struct tpacket3_hdr *ppd;
-
-       ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd +
-                                      pbd->h1.offset_to_first_pkt);
-       for (i = 0; i < num_pkts; ++i) {
-               bytes += ppd->tp_snaplen;
-               display(ppd);
-
-               ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd +
-                                              ppd->tp_next_offset);
-       }
-
-       packets_total += num_pkts;
-       bytes_total += bytes;
-}
-
-static void flush_block(struct block_desc *pbd)
-{
-       pbd->h1.block_status = TP_STATUS_KERNEL;
-}
-
-static void teardown_socket(struct ring *ring, int fd)
-{
-       munmap(ring->map, ring->req.tp_block_size * ring->req.tp_block_nr);
-       free(ring->rd);
-       close(fd);
-}
-
-int main(int argc, char **argp)
-{
-       int fd, err;
-       socklen_t len;
-       struct ring ring;
-       struct pollfd pfd;
-       unsigned int block_num = 0, blocks = 64;
-       struct block_desc *pbd;
-       struct tpacket_stats_v3 stats;
-
-       if (argc != 2) {
-               fprintf(stderr, "Usage: %s INTERFACE\n", argp[0]);
-               return EXIT_FAILURE;
-       }
-
-       signal(SIGINT, sighandler);
-
-       memset(&ring, 0, sizeof(ring));
-       fd = setup_socket(&ring, argp[argc - 1]);
-       assert(fd > 0);
-
-       memset(&pfd, 0, sizeof(pfd));
-       pfd.fd = fd;
-       pfd.events = POLLIN | POLLERR;
-       pfd.revents = 0;
-
-       while (likely(!sigint)) {
-               pbd = (struct block_desc *) ring.rd[block_num].iov_base;
-
-               if ((pbd->h1.block_status & TP_STATUS_USER) == 0) {
-                       poll(&pfd, 1, -1);
-                       continue;
-               }
-
-               walk_block(pbd, block_num);
-               flush_block(pbd);
-               block_num = (block_num + 1) % blocks;
-       }
-
-       len = sizeof(stats);
-       err = getsockopt(fd, SOL_PACKET, PACKET_STATISTICS, &stats, &len);
-       if (err < 0) {
-               perror("getsockopt");
-               exit(1);
-       }
-
-       fflush(stdout);
-       printf("\nReceived %u packets, %lu bytes, %u dropped, freeze_q_cnt: %u\n",
-              stats.tp_packets, bytes_total, stats.tp_drops,
-              stats.tp_freeze_q_cnt);
-
-       teardown_socket(&ring, fd);
-       return 0;
-}
-
--------------------------------------------------------------------------------
-+ PACKET_QDISC_BYPASS
--------------------------------------------------------------------------------
-
-If there is a requirement to load the network with many packets in a similar
-fashion as pktgen does, you might set the following option after socket
-creation:
-
-    int one = 1;
-    setsockopt(fd, SOL_PACKET, PACKET_QDISC_BYPASS, &one, sizeof(one));
-
-This has the side-effect, that packets sent through PF_PACKET will bypass the
-kernel's qdisc layer and are forcedly pushed to the driver directly. Meaning,
-packet are not buffered, tc disciplines are ignored, increased loss can occur
-and such packets are also not visible to other PF_PACKET sockets anymore. So,
-you have been warned; generally, this can be useful for stress testing various
-components of a system.
-
-On default, PACKET_QDISC_BYPASS is disabled and needs to be explicitly enabled
-on PF_PACKET sockets.
-
--------------------------------------------------------------------------------
-+ PACKET_TIMESTAMP
--------------------------------------------------------------------------------
-
-The PACKET_TIMESTAMP setting determines the source of the timestamp in
-the packet meta information for mmap(2)ed RX_RING and TX_RINGs.  If your
-NIC is capable of timestamping packets in hardware, you can request those
-hardware timestamps to be used. Note: you may need to enable the generation
-of hardware timestamps with SIOCSHWTSTAMP (see related information from
-Documentation/networking/timestamping.txt).
-
-PACKET_TIMESTAMP accepts the same integer bit field as SO_TIMESTAMPING:
-
-    int req = SOF_TIMESTAMPING_RAW_HARDWARE;
-    setsockopt(fd, SOL_PACKET, PACKET_TIMESTAMP, (void *) &req, sizeof(req))
-
-For the mmap(2)ed ring buffers, such timestamps are stored in the
-tpacket{,2,3}_hdr structure's tp_sec and tp_{n,u}sec members. To determine
-what kind of timestamp has been reported, the tp_status field is binary |'ed
-with the following possible bits ...
-
-    TP_STATUS_TS_RAW_HARDWARE
-    TP_STATUS_TS_SOFTWARE
-
-... that are equivalent to its SOF_TIMESTAMPING_* counterparts. For the
-RX_RING, if neither is set (i.e. PACKET_TIMESTAMP is not set), then a
-software fallback was invoked *within* PF_PACKET's processing code (less
-precise).
-
-Getting timestamps for the TX_RING works as follows: i) fill the ring frames,
-ii) call sendto() e.g. in blocking mode, iii) wait for status of relevant
-frames to be updated resp. the frame handed over to the application, iv) walk
-through the frames to pick up the individual hw/sw timestamps.
-
-Only (!) if transmit timestamping is enabled, then these bits are combined
-with binary | with TP_STATUS_AVAILABLE, so you must check for that in your
-application (e.g. !(tp_status & (TP_STATUS_SEND_REQUEST | TP_STATUS_SENDING))
-in a first step to see if the frame belongs to the application, and then
-one can extract the type of timestamp in a second step from tp_status)!
-
-If you don't care about them, thus having it disabled, checking for
-TP_STATUS_AVAILABLE resp. TP_STATUS_WRONG_FORMAT is sufficient. If in the
-TX_RING part only TP_STATUS_AVAILABLE is set, then the tp_sec and tp_{n,u}sec
-members do not contain a valid value. For TX_RINGs, by default no timestamp
-is generated!
-
-See include/linux/net_tstamp.h and Documentation/networking/timestamping.txt
-for more information on hardware timestamps.
-
--------------------------------------------------------------------------------
-+ Miscellaneous bits
--------------------------------------------------------------------------------
-
-- Packet sockets work well together with Linux socket filters, thus you also
-  might want to have a look at Documentation/networking/filter.txt
-
---------------------------------------------------------------------------------
-+ THANKS
---------------------------------------------------------------------------------
-   
-   Jesse Brandeburg, for fixing my grammathical/spelling errors
-
diff --git a/Documentation/networking/phonet.rst b/Documentation/networking/phonet.rst
new file mode 100644 (file)
index 0000000..8668dcb
--- /dev/null
@@ -0,0 +1,230 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+============================
+Linux Phonet protocol family
+============================
+
+Introduction
+------------
+
+Phonet is a packet protocol used by Nokia cellular modems for both IPC
+and RPC. With the Linux Phonet socket family, Linux host processes can
+receive and send messages from/to the modem, or any other external
+device attached to the modem. The modem takes care of routing.
+
+Phonet packets can be exchanged through various hardware connections
+depending on the device, such as:
+
+  - USB with the CDC Phonet interface,
+  - infrared,
+  - Bluetooth,
+  - an RS232 serial port (with a dedicated "FBUS" line discipline),
+  - the SSI bus with some TI OMAP processors.
+
+
+Packets format
+--------------
+
+Phonet packets have a common header as follows::
+
+  struct phonethdr {
+    uint8_t  pn_media;  /* Media type (link-layer identifier) */
+    uint8_t  pn_rdev;   /* Receiver device ID */
+    uint8_t  pn_sdev;   /* Sender device ID */
+    uint8_t  pn_res;    /* Resource ID or function */
+    uint16_t pn_length; /* Big-endian message byte length (minus 6) */
+    uint8_t  pn_robj;   /* Receiver object ID */
+    uint8_t  pn_sobj;   /* Sender object ID */
+  };
+
+On Linux, the link-layer header includes the pn_media byte (see below).
+The next 7 bytes are part of the network-layer header.
+
+The device ID is split: the 6 higher-order bits constitute the device
+address, while the 2 lower-order bits are used for multiplexing, as are
+the 8-bit object identifiers. As such, Phonet can be considered as a
+network layer with 6 bits of address space and 10 bits for transport
+protocol (much like port numbers in IP world).
+
+The modem always has address number zero. All other device have a their
+own 6-bit address.
+
+
+Link layer
+----------
+
+Phonet links are always point-to-point links. The link layer header
+consists of a single Phonet media type byte. It uniquely identifies the
+link through which the packet is transmitted, from the modem's
+perspective. Each Phonet network device shall prepend and set the media
+type byte as appropriate. For convenience, a common phonet_header_ops
+link-layer header operations structure is provided. It sets the
+media type according to the network device hardware address.
+
+Linux Phonet network interfaces support a dedicated link layer packets
+type (ETH_P_PHONET) which is out of the Ethernet type range. They can
+only send and receive Phonet packets.
+
+The virtual TUN tunnel device driver can also be used for Phonet. This
+requires IFF_TUN mode, _without_ the IFF_NO_PI flag. In this case,
+there is no link-layer header, so there is no Phonet media type byte.
+
+Note that Phonet interfaces are not allowed to re-order packets, so
+only the (default) Linux FIFO qdisc should be used with them.
+
+
+Network layer
+-------------
+
+The Phonet socket address family maps the Phonet packet header::
+
+  struct sockaddr_pn {
+    sa_family_t spn_family;    /* AF_PHONET */
+    uint8_t     spn_obj;       /* Object ID */
+    uint8_t     spn_dev;       /* Device ID */
+    uint8_t     spn_resource;  /* Resource or function */
+    uint8_t     spn_zero[...]; /* Padding */
+  };
+
+The resource field is only used when sending and receiving;
+It is ignored by bind() and getsockname().
+
+
+Low-level datagram protocol
+---------------------------
+
+Applications can send Phonet messages using the Phonet datagram socket
+protocol from the PF_PHONET family. Each socket is bound to one of the
+2^10 object IDs available, and can send and receive packets with any
+other peer.
+
+::
+
+  struct sockaddr_pn addr = { .spn_family = AF_PHONET, };
+  ssize_t len;
+  socklen_t addrlen = sizeof(addr);
+  int fd;
+
+  fd = socket(PF_PHONET, SOCK_DGRAM, 0);
+  bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+  /* ... */
+
+  sendto(fd, msg, msglen, 0, (struct sockaddr *)&addr, sizeof(addr));
+  len = recvfrom(fd, buf, sizeof(buf), 0,
+                (struct sockaddr *)&addr, &addrlen);
+
+This protocol follows the SOCK_DGRAM connection-less semantics.
+However, connect() and getpeername() are not supported, as they did
+not seem useful with Phonet usages (could be added easily).
+
+
+Resource subscription
+---------------------
+
+A Phonet datagram socket can be subscribed to any number of 8-bits
+Phonet resources, as follow::
+
+  uint32_t res = 0xXX;
+  ioctl(fd, SIOCPNADDRESOURCE, &res);
+
+Subscription is similarly cancelled using the SIOCPNDELRESOURCE I/O
+control request, or when the socket is closed.
+
+Note that no more than one socket can be subcribed to any given
+resource at a time. If not, ioctl() will return EBUSY.
+
+
+Phonet Pipe protocol
+--------------------
+
+The Phonet Pipe protocol is a simple sequenced packets protocol
+with end-to-end congestion control. It uses the passive listening
+socket paradigm. The listening socket is bound to an unique free object
+ID. Each listening socket can handle up to 255 simultaneous
+connections, one per accept()'d socket.
+
+::
+
+  int lfd, cfd;
+
+  lfd = socket(PF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE);
+  listen (lfd, INT_MAX);
+
+  /* ... */
+  cfd = accept(lfd, NULL, NULL);
+  for (;;)
+  {
+    char buf[...];
+    ssize_t len = read(cfd, buf, sizeof(buf));
+
+    /* ... */
+
+    write(cfd, msg, msglen);
+  }
+
+Connections are traditionally established between two endpoints by a
+"third party" application. This means that both endpoints are passive.
+
+
+As of Linux kernel version 2.6.39, it is also possible to connect
+two endpoints directly, using connect() on the active side. This is
+intended to support the newer Nokia Wireless Modem API, as found in
+e.g. the Nokia Slim Modem in the ST-Ericsson U8500 platform::
+
+  struct sockaddr_spn spn;
+  int fd;
+
+  fd = socket(PF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE);
+  memset(&spn, 0, sizeof(spn));
+  spn.spn_family = AF_PHONET;
+  spn.spn_obj = ...;
+  spn.spn_dev = ...;
+  spn.spn_resource = 0xD9;
+  connect(fd, (struct sockaddr *)&spn, sizeof(spn));
+  /* normal I/O here ... */
+  close(fd);
+
+
+.. Warning:
+
+   When polling a connected pipe socket for writability, there is an
+   intrinsic race condition whereby writability might be lost between the
+   polling and the writing system calls. In this case, the socket will
+   block until write becomes possible again, unless non-blocking mode
+   is enabled.
+
+
+The pipe protocol provides two socket options at the SOL_PNPIPE level:
+
+  PNPIPE_ENCAP accepts one integer value (int) of:
+
+    PNPIPE_ENCAP_NONE:
+      The socket operates normally (default).
+
+    PNPIPE_ENCAP_IP:
+      The socket is used as a backend for a virtual IP
+      interface. This requires CAP_NET_ADMIN capability. GPRS data
+      support on Nokia modems can use this. Note that the socket cannot
+      be reliably poll()'d or read() from while in this mode.
+
+  PNPIPE_IFINDEX
+      is a read-only integer value. It contains the
+      interface index of the network interface created by PNPIPE_ENCAP,
+      or zero if encapsulation is off.
+
+  PNPIPE_HANDLE
+      is a read-only integer value. It contains the underlying
+      identifier ("pipe handle") of the pipe. This is only defined for
+      socket descriptors that are already connected or being connected.
+
+
+Authors
+-------
+
+Linux Phonet was initially written by Sakari Ailus.
+
+Other contributors include Mikä Liljeberg, Andras Domokos,
+Carlos Chinea and Rémi Denis-Courmont.
+
+Copyright |copy| 2008 Nokia Corporation.
diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt
deleted file mode 100644 (file)
index 8100358..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-Linux Phonet protocol family
-============================
-
-Introduction
-------------
-
-Phonet is a packet protocol used by Nokia cellular modems for both IPC
-and RPC. With the Linux Phonet socket family, Linux host processes can
-receive and send messages from/to the modem, or any other external
-device attached to the modem. The modem takes care of routing.
-
-Phonet packets can be exchanged through various hardware connections
-depending on the device, such as:
-  - USB with the CDC Phonet interface,
-  - infrared,
-  - Bluetooth,
-  - an RS232 serial port (with a dedicated "FBUS" line discipline),
-  - the SSI bus with some TI OMAP processors.
-
-
-Packets format
---------------
-
-Phonet packets have a common header as follows:
-
-  struct phonethdr {
-    uint8_t  pn_media;  /* Media type (link-layer identifier) */
-    uint8_t  pn_rdev;   /* Receiver device ID */
-    uint8_t  pn_sdev;   /* Sender device ID */
-    uint8_t  pn_res;    /* Resource ID or function */
-    uint16_t pn_length; /* Big-endian message byte length (minus 6) */
-    uint8_t  pn_robj;   /* Receiver object ID */
-    uint8_t  pn_sobj;   /* Sender object ID */
-  };
-
-On Linux, the link-layer header includes the pn_media byte (see below).
-The next 7 bytes are part of the network-layer header.
-
-The device ID is split: the 6 higher-order bits constitute the device
-address, while the 2 lower-order bits are used for multiplexing, as are
-the 8-bit object identifiers. As such, Phonet can be considered as a
-network layer with 6 bits of address space and 10 bits for transport
-protocol (much like port numbers in IP world).
-
-The modem always has address number zero. All other device have a their
-own 6-bit address.
-
-
-Link layer
-----------
-
-Phonet links are always point-to-point links. The link layer header
-consists of a single Phonet media type byte. It uniquely identifies the
-link through which the packet is transmitted, from the modem's
-perspective. Each Phonet network device shall prepend and set the media
-type byte as appropriate. For convenience, a common phonet_header_ops
-link-layer header operations structure is provided. It sets the
-media type according to the network device hardware address.
-
-Linux Phonet network interfaces support a dedicated link layer packets
-type (ETH_P_PHONET) which is out of the Ethernet type range. They can
-only send and receive Phonet packets.
-
-The virtual TUN tunnel device driver can also be used for Phonet. This
-requires IFF_TUN mode, _without_ the IFF_NO_PI flag. In this case,
-there is no link-layer header, so there is no Phonet media type byte.
-
-Note that Phonet interfaces are not allowed to re-order packets, so
-only the (default) Linux FIFO qdisc should be used with them.
-
-
-Network layer
--------------
-
-The Phonet socket address family maps the Phonet packet header:
-
-  struct sockaddr_pn {
-    sa_family_t spn_family;    /* AF_PHONET */
-    uint8_t     spn_obj;       /* Object ID */
-    uint8_t     spn_dev;       /* Device ID */
-    uint8_t     spn_resource;  /* Resource or function */
-    uint8_t     spn_zero[...]; /* Padding */
-  };
-
-The resource field is only used when sending and receiving;
-It is ignored by bind() and getsockname().
-
-
-Low-level datagram protocol
----------------------------
-
-Applications can send Phonet messages using the Phonet datagram socket
-protocol from the PF_PHONET family. Each socket is bound to one of the
-2^10 object IDs available, and can send and receive packets with any
-other peer.
-
-  struct sockaddr_pn addr = { .spn_family = AF_PHONET, };
-  ssize_t len;
-  socklen_t addrlen = sizeof(addr);
-  int fd;
-
-  fd = socket(PF_PHONET, SOCK_DGRAM, 0);
-  bind(fd, (struct sockaddr *)&addr, sizeof(addr));
-  /* ... */
-
-  sendto(fd, msg, msglen, 0, (struct sockaddr *)&addr, sizeof(addr));
-  len = recvfrom(fd, buf, sizeof(buf), 0,
-                 (struct sockaddr *)&addr, &addrlen);
-
-This protocol follows the SOCK_DGRAM connection-less semantics.
-However, connect() and getpeername() are not supported, as they did
-not seem useful with Phonet usages (could be added easily).
-
-
-Resource subscription
----------------------
-
-A Phonet datagram socket can be subscribed to any number of 8-bits
-Phonet resources, as follow:
-
-  uint32_t res = 0xXX;
-  ioctl(fd, SIOCPNADDRESOURCE, &res);
-
-Subscription is similarly cancelled using the SIOCPNDELRESOURCE I/O
-control request, or when the socket is closed.
-
-Note that no more than one socket can be subcribed to any given
-resource at a time. If not, ioctl() will return EBUSY.
-
-
-Phonet Pipe protocol
---------------------
-
-The Phonet Pipe protocol is a simple sequenced packets protocol
-with end-to-end congestion control. It uses the passive listening
-socket paradigm. The listening socket is bound to an unique free object
-ID. Each listening socket can handle up to 255 simultaneous
-connections, one per accept()'d socket.
-
-  int lfd, cfd;
-
-  lfd = socket(PF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE);
-  listen (lfd, INT_MAX);
-
-  /* ... */
-  cfd = accept(lfd, NULL, NULL);
-  for (;;)
-  {
-    char buf[...];
-    ssize_t len = read(cfd, buf, sizeof(buf));
-
-    /* ... */
-
-    write(cfd, msg, msglen);
-  }
-
-Connections are traditionally established between two endpoints by a
-"third party" application. This means that both endpoints are passive.
-
-
-As of Linux kernel version 2.6.39, it is also possible to connect
-two endpoints directly, using connect() on the active side. This is
-intended to support the newer Nokia Wireless Modem API, as found in
-e.g. the Nokia Slim Modem in the ST-Ericsson U8500 platform:
-
-  struct sockaddr_spn spn;
-  int fd;
-
-  fd = socket(PF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE);
-  memset(&spn, 0, sizeof(spn));
-  spn.spn_family = AF_PHONET;
-  spn.spn_obj = ...;
-  spn.spn_dev = ...;
-  spn.spn_resource = 0xD9;
-  connect(fd, (struct sockaddr *)&spn, sizeof(spn));
-  /* normal I/O here ... */
-  close(fd);
-
-
-WARNING:
-When polling a connected pipe socket for writability, there is an
-intrinsic race condition whereby writability might be lost between the
-polling and the writing system calls. In this case, the socket will
-block until write becomes possible again, unless non-blocking mode
-is enabled.
-
-
-The pipe protocol provides two socket options at the SOL_PNPIPE level:
-
-  PNPIPE_ENCAP accepts one integer value (int) of:
-
-    PNPIPE_ENCAP_NONE: The socket operates normally (default).
-
-    PNPIPE_ENCAP_IP: The socket is used as a backend for a virtual IP
-      interface. This requires CAP_NET_ADMIN capability. GPRS data
-      support on Nokia modems can use this. Note that the socket cannot
-      be reliably poll()'d or read() from while in this mode.
-
-  PNPIPE_IFINDEX is a read-only integer value. It contains the
-    interface index of the network interface created by PNPIPE_ENCAP,
-    or zero if encapsulation is off.
-
-  PNPIPE_HANDLE is a read-only integer value. It contains the underlying
-    identifier ("pipe handle") of the pipe. This is only defined for
-    socket descriptors that are already connected or being connected.
-
-
-Authors
--------
-
-Linux Phonet was initially written by Sakari Ailus.
-Other contributors include Mikä Liljeberg, Andras Domokos,
-Carlos Chinea and Rémi Denis-Courmont.
-Copyright (C) 2008 Nokia Corporation.
diff --git a/Documentation/networking/pktgen.rst b/Documentation/networking/pktgen.rst
new file mode 100644 (file)
index 0000000..7afa1c9
--- /dev/null
@@ -0,0 +1,412 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====================================
+HOWTO for the linux packet generator
+====================================
+
+Enable CONFIG_NET_PKTGEN to compile and build pktgen either in-kernel
+or as a module.  A module is preferred; modprobe pktgen if needed.  Once
+running, pktgen creates a thread for each CPU with affinity to that CPU.
+Monitoring and controlling is done via /proc.  It is easiest to select a
+suitable sample script and configure that.
+
+On a dual CPU::
+
+    ps aux | grep pkt
+    root       129  0.3  0.0     0    0 ?        SW    2003 523:20 [kpktgend_0]
+    root       130  0.3  0.0     0    0 ?        SW    2003 509:50 [kpktgend_1]
+
+
+For monitoring and control pktgen creates::
+
+       /proc/net/pktgen/pgctrl
+       /proc/net/pktgen/kpktgend_X
+       /proc/net/pktgen/ethX
+
+
+Tuning NIC for max performance
+==============================
+
+The default NIC settings are (likely) not tuned for pktgen's artificial
+overload type of benchmarking, as this could hurt the normal use-case.
+
+Specifically increasing the TX ring buffer in the NIC::
+
+ # ethtool -G ethX tx 1024
+
+A larger TX ring can improve pktgen's performance, while it can hurt
+in the general case, 1) because the TX ring buffer might get larger
+than the CPU's L1/L2 cache, 2) because it allows more queueing in the
+NIC HW layer (which is bad for bufferbloat).
+
+One should hesitate to conclude that packets/descriptors in the HW
+TX ring cause delay.  Drivers usually delay cleaning up the
+ring-buffers for various performance reasons, and packets stalling
+the TX ring might just be waiting for cleanup.
+
+This cleanup issue is specifically the case for the driver ixgbe
+(Intel 82599 chip).  This driver (ixgbe) combines TX+RX ring cleanups,
+and the cleanup interval is affected by the ethtool --coalesce setting
+of parameter "rx-usecs".
+
+For ixgbe use e.g. "30" resulting in approx 33K interrupts/sec (1/30*10^6)::
+
+ # ethtool -C ethX rx-usecs 30
+
+
+Kernel threads
+==============
+Pktgen creates a thread for each CPU with affinity to that CPU.
+Which is controlled through procfile /proc/net/pktgen/kpktgend_X.
+
+Example: /proc/net/pktgen/kpktgend_0::
+
+ Running:
+ Stopped: eth4@0
+ Result: OK: add_device=eth4@0
+
+Most important are the devices assigned to the thread.
+
+The two basic thread commands are:
+
+ * add_device DEVICE@NAME -- adds a single device
+ * rem_device_all         -- remove all associated devices
+
+When adding a device to a thread, a corresponding procfile is created
+which is used for configuring this device. Thus, device names need to
+be unique.
+
+To support adding the same device to multiple threads, which is useful
+with multi queue NICs, the device naming scheme is extended with "@":
+device@something
+
+The part after "@" can be anything, but it is custom to use the thread
+number.
+
+Viewing devices
+===============
+
+The Params section holds configured information.  The Current section
+holds running statistics.  The Result is printed after a run or after
+interruption.  Example::
+
+    /proc/net/pktgen/eth4@0
+
+    Params: count 100000  min_pkt_size: 60  max_pkt_size: 60
+       frags: 0  delay: 0  clone_skb: 64  ifname: eth4@0
+       flows: 0 flowlen: 0
+       queue_map_min: 0  queue_map_max: 0
+       dst_min: 192.168.81.2  dst_max:
+       src_min:   src_max:
+       src_mac: 90:e2:ba:0a:56:b4 dst_mac: 00:1b:21:3c:9d:f8
+       udp_src_min: 9  udp_src_max: 109  udp_dst_min: 9  udp_dst_max: 9
+       src_mac_count: 0  dst_mac_count: 0
+       Flags: UDPSRC_RND  NO_TIMESTAMP  QUEUE_MAP_CPU
+    Current:
+       pkts-sofar: 100000  errors: 0
+       started: 623913381008us  stopped: 623913396439us idle: 25us
+       seq_num: 100001  cur_dst_mac_offset: 0  cur_src_mac_offset: 0
+       cur_saddr: 192.168.8.3  cur_daddr: 192.168.81.2
+       cur_udp_dst: 9  cur_udp_src: 42
+       cur_queue_map: 0
+       flows: 0
+    Result: OK: 15430(c15405+d25) usec, 100000 (60byte,0frags)
+    6480562pps 3110Mb/sec (3110669760bps) errors: 0
+
+
+Configuring devices
+===================
+This is done via the /proc interface, and most easily done via pgset
+as defined in the sample scripts.
+You need to specify PGDEV environment variable to use functions from sample
+scripts, i.e.::
+
+    export PGDEV=/proc/net/pktgen/eth4@0
+    source samples/pktgen/functions.sh
+
+Examples::
+
+ pg_ctrl start           starts injection.
+ pg_ctrl stop            aborts injection. Also, ^C aborts generator.
+
+ pgset "clone_skb 1"     sets the number of copies of the same packet
+ pgset "clone_skb 0"     use single SKB for all transmits
+ pgset "burst 8"         uses xmit_more API to queue 8 copies of the same
+                        packet and update HW tx queue tail pointer once.
+                        "burst 1" is the default
+ pgset "pkt_size 9014"   sets packet size to 9014
+ pgset "frags 5"         packet will consist of 5 fragments
+ pgset "count 200000"    sets number of packets to send, set to zero
+                        for continuous sends until explicitly stopped.
+
+ pgset "delay 5000"      adds delay to hard_start_xmit(). nanoseconds
+
+ pgset "dst 10.0.0.1"    sets IP destination address
+                        (BEWARE! This generator is very aggressive!)
+
+ pgset "dst_min 10.0.0.1"            Same as dst
+ pgset "dst_max 10.0.0.254"          Set the maximum destination IP.
+ pgset "src_min 10.0.0.1"            Set the minimum (or only) source IP.
+ pgset "src_max 10.0.0.254"          Set the maximum source IP.
+ pgset "dst6 fec0::1"     IPV6 destination address
+ pgset "src6 fec0::2"     IPV6 source address
+ pgset "dstmac 00:00:00:00:00:00"    sets MAC destination address
+ pgset "srcmac 00:00:00:00:00:00"    sets MAC source address
+
+ pgset "queue_map_min 0" Sets the min value of tx queue interval
+ pgset "queue_map_max 7" Sets the max value of tx queue interval, for multiqueue devices
+                        To select queue 1 of a given device,
+                        use queue_map_min=1 and queue_map_max=1
+
+ pgset "src_mac_count 1" Sets the number of MACs we'll range through.
+                        The 'minimum' MAC is what you set with srcmac.
+
+ pgset "dst_mac_count 1" Sets the number of MACs we'll range through.
+                        The 'minimum' MAC is what you set with dstmac.
+
+ pgset "flag [name]"     Set a flag to determine behaviour.  Current flags
+                        are: IPSRC_RND # IP source is random (between min/max)
+                             IPDST_RND # IP destination is random
+                             UDPSRC_RND, UDPDST_RND,
+                             MACSRC_RND, MACDST_RND
+                             TXSIZE_RND, IPV6,
+                             MPLS_RND, VID_RND, SVID_RND
+                             FLOW_SEQ,
+                             QUEUE_MAP_RND # queue map random
+                             QUEUE_MAP_CPU # queue map mirrors smp_processor_id()
+                             UDPCSUM,
+                             IPSEC # IPsec encapsulation (needs CONFIG_XFRM)
+                             NODE_ALLOC # node specific memory allocation
+                             NO_TIMESTAMP # disable timestamping
+ pgset 'flag ![name]'    Clear a flag to determine behaviour.
+                        Note that you might need to use single quote in
+                        interactive mode, so that your shell wouldn't expand
+                        the specified flag as a history command.
+
+ pgset "spi [SPI_VALUE]" Set specific SA used to transform packet.
+
+ pgset "udp_src_min 9"   set UDP source port min, If < udp_src_max, then
+                        cycle through the port range.
+
+ pgset "udp_src_max 9"   set UDP source port max.
+ pgset "udp_dst_min 9"   set UDP destination port min, If < udp_dst_max, then
+                        cycle through the port range.
+ pgset "udp_dst_max 9"   set UDP destination port max.
+
+ pgset "mpls 0001000a,0002000a,0000000a" set MPLS labels (in this example
+                                        outer label=16,middle label=32,
+                                        inner label=0 (IPv4 NULL)) Note that
+                                        there must be no spaces between the
+                                        arguments. Leading zeros are required.
+                                        Do not set the bottom of stack bit,
+                                        that's done automatically. If you do
+                                        set the bottom of stack bit, that
+                                        indicates that you want to randomly
+                                        generate that address and the flag
+                                        MPLS_RND will be turned on. You
+                                        can have any mix of random and fixed
+                                        labels in the label stack.
+
+ pgset "mpls 0"                  turn off mpls (or any invalid argument works too!)
+
+ pgset "vlan_id 77"       set VLAN ID 0-4095
+ pgset "vlan_p 3"         set priority bit 0-7 (default 0)
+ pgset "vlan_cfi 0"       set canonical format identifier 0-1 (default 0)
+
+ pgset "svlan_id 22"      set SVLAN ID 0-4095
+ pgset "svlan_p 3"        set priority bit 0-7 (default 0)
+ pgset "svlan_cfi 0"      set canonical format identifier 0-1 (default 0)
+
+ pgset "vlan_id 9999"     > 4095 remove vlan and svlan tags
+ pgset "svlan 9999"       > 4095 remove svlan tag
+
+
+ pgset "tos XX"           set former IPv4 TOS field (e.g. "tos 28" for AF11 no ECN, default 00)
+ pgset "traffic_class XX" set former IPv6 TRAFFIC CLASS (e.g. "traffic_class B8" for EF no ECN, default 00)
+
+ pgset "rate 300M"        set rate to 300 Mb/s
+ pgset "ratep 1000000"    set rate to 1Mpps
+
+ pgset "xmit_mode netif_receive"  RX inject into stack netif_receive_skb()
+                                 Works with "burst" but not with "clone_skb".
+                                 Default xmit_mode is "start_xmit".
+
+Sample scripts
+==============
+
+A collection of tutorial scripts and helpers for pktgen is in the
+samples/pktgen directory. The helper parameters.sh file support easy
+and consistent parameter parsing across the sample scripts.
+
+Usage example and help::
+
+ ./pktgen_sample01_simple.sh -i eth4 -m 00:1B:21:3C:9D:F8 -d 192.168.8.2
+
+Usage:::
+
+  ./pktgen_sample01_simple.sh [-vx] -i ethX
+
+  -i : ($DEV)       output interface/device (required)
+  -s : ($PKT_SIZE)  packet size
+  -d : ($DEST_IP)   destination IP
+  -m : ($DST_MAC)   destination MAC-addr
+  -t : ($THREADS)   threads to start
+  -c : ($SKB_CLONE) SKB clones send before alloc new SKB
+  -b : ($BURST)     HW level bursting of SKBs
+  -v : ($VERBOSE)   verbose
+  -x : ($DEBUG)     debug
+
+The global variables being set are also listed.  E.g. the required
+interface/device parameter "-i" sets variable $DEV.  Copy the
+pktgen_sampleXX scripts and modify them to fit your own needs.
+
+The old scripts::
+
+    pktgen.conf-1-2                  # 1 CPU 2 dev
+    pktgen.conf-1-1-rdos             # 1 CPU 1 dev w. route DoS
+    pktgen.conf-1-1-ip6              # 1 CPU 1 dev ipv6
+    pktgen.conf-1-1-ip6-rdos         # 1 CPU 1 dev ipv6  w. route DoS
+    pktgen.conf-1-1-flows            # 1 CPU 1 dev multiple flows.
+
+
+Interrupt affinity
+===================
+Note that when adding devices to a specific CPU it is a good idea to
+also assign /proc/irq/XX/smp_affinity so that the TX interrupts are bound
+to the same CPU.  This reduces cache bouncing when freeing skbs.
+
+Plus using the device flag QUEUE_MAP_CPU, which maps the SKBs TX queue
+to the running threads CPU (directly from smp_processor_id()).
+
+Enable IPsec
+============
+Default IPsec transformation with ESP encapsulation plus transport mode
+can be enabled by simply setting::
+
+    pgset "flag IPSEC"
+    pgset "flows 1"
+
+To avoid breaking existing testbed scripts for using AH type and tunnel mode,
+you can use "pgset spi SPI_VALUE" to specify which transformation mode
+to employ.
+
+
+Current commands and configuration options
+==========================================
+
+**Pgcontrol commands**::
+
+    start
+    stop
+    reset
+
+**Thread commands**::
+
+    add_device
+    rem_device_all
+
+
+**Device commands**::
+
+    count
+    clone_skb
+    burst
+    debug
+
+    frags
+    delay
+
+    src_mac_count
+    dst_mac_count
+
+    pkt_size
+    min_pkt_size
+    max_pkt_size
+
+    queue_map_min
+    queue_map_max
+    skb_priority
+
+    tos           (ipv4)
+    traffic_class (ipv6)
+
+    mpls
+
+    udp_src_min
+    udp_src_max
+
+    udp_dst_min
+    udp_dst_max
+
+    node
+
+    flag
+    IPSRC_RND
+    IPDST_RND
+    UDPSRC_RND
+    UDPDST_RND
+    MACSRC_RND
+    MACDST_RND
+    TXSIZE_RND
+    IPV6
+    MPLS_RND
+    VID_RND
+    SVID_RND
+    FLOW_SEQ
+    QUEUE_MAP_RND
+    QUEUE_MAP_CPU
+    UDPCSUM
+    IPSEC
+    NODE_ALLOC
+    NO_TIMESTAMP
+
+    spi (ipsec)
+
+    dst_min
+    dst_max
+
+    src_min
+    src_max
+
+    dst_mac
+    src_mac
+
+    clear_counters
+
+    src6
+    dst6
+    dst6_max
+    dst6_min
+
+    flows
+    flowlen
+
+    rate
+    ratep
+
+    xmit_mode <start_xmit|netif_receive>
+
+    vlan_cfi
+    vlan_id
+    vlan_p
+
+    svlan_cfi
+    svlan_id
+    svlan_p
+
+
+References:
+
+- ftp://robur.slu.se/pub/Linux/net-development/pktgen-testing/
+- tp://robur.slu.se/pub/Linux/net-development/pktgen-testing/examples/
+
+Paper from Linux-Kongress in Erlangen 2004.
+- ftp://robur.slu.se/pub/Linux/net-development/pktgen-testing/pktgen_paper.pdf
+
+Thanks to:
+
+Grant Grundler for testing on IA-64 and parisc, Harald Welte,  Lennert Buytenhek
+Stephen Hemminger, Andi Kleen, Dave Miller and many others.
+
+
+Good luck with the linux net-development.
diff --git a/Documentation/networking/pktgen.txt b/Documentation/networking/pktgen.txt
deleted file mode 100644 (file)
index d2fd78f..0000000
+++ /dev/null
@@ -1,400 +0,0 @@
-
-
-                  HOWTO for the linux packet generator
-                  ------------------------------------
-
-Enable CONFIG_NET_PKTGEN to compile and build pktgen either in-kernel
-or as a module.  A module is preferred; modprobe pktgen if needed.  Once
-running, pktgen creates a thread for each CPU with affinity to that CPU.
-Monitoring and controlling is done via /proc.  It is easiest to select a
-suitable sample script and configure that.
-
-On a dual CPU:
-
-ps aux | grep pkt
-root       129  0.3  0.0     0    0 ?        SW    2003 523:20 [kpktgend_0]
-root       130  0.3  0.0     0    0 ?        SW    2003 509:50 [kpktgend_1]
-
-
-For monitoring and control pktgen creates:
-       /proc/net/pktgen/pgctrl
-       /proc/net/pktgen/kpktgend_X
-        /proc/net/pktgen/ethX
-
-
-Tuning NIC for max performance
-==============================
-
-The default NIC settings are (likely) not tuned for pktgen's artificial
-overload type of benchmarking, as this could hurt the normal use-case.
-
-Specifically increasing the TX ring buffer in the NIC:
- # ethtool -G ethX tx 1024
-
-A larger TX ring can improve pktgen's performance, while it can hurt
-in the general case, 1) because the TX ring buffer might get larger
-than the CPU's L1/L2 cache, 2) because it allows more queueing in the
-NIC HW layer (which is bad for bufferbloat).
-
-One should hesitate to conclude that packets/descriptors in the HW
-TX ring cause delay.  Drivers usually delay cleaning up the
-ring-buffers for various performance reasons, and packets stalling
-the TX ring might just be waiting for cleanup.
-
-This cleanup issue is specifically the case for the driver ixgbe
-(Intel 82599 chip).  This driver (ixgbe) combines TX+RX ring cleanups,
-and the cleanup interval is affected by the ethtool --coalesce setting
-of parameter "rx-usecs".
-
-For ixgbe use e.g. "30" resulting in approx 33K interrupts/sec (1/30*10^6):
- # ethtool -C ethX rx-usecs 30
-
-
-Kernel threads
-==============
-Pktgen creates a thread for each CPU with affinity to that CPU.
-Which is controlled through procfile /proc/net/pktgen/kpktgend_X.
-
-Example: /proc/net/pktgen/kpktgend_0
-
- Running:
- Stopped: eth4@0
- Result: OK: add_device=eth4@0
-
-Most important are the devices assigned to the thread.
-
-The two basic thread commands are:
- * add_device DEVICE@NAME -- adds a single device
- * rem_device_all         -- remove all associated devices
-
-When adding a device to a thread, a corresponding procfile is created
-which is used for configuring this device. Thus, device names need to
-be unique.
-
-To support adding the same device to multiple threads, which is useful
-with multi queue NICs, the device naming scheme is extended with "@":
- device@something
-
-The part after "@" can be anything, but it is custom to use the thread
-number.
-
-Viewing devices
-===============
-
-The Params section holds configured information.  The Current section
-holds running statistics.  The Result is printed after a run or after
-interruption.  Example:
-
-/proc/net/pktgen/eth4@0
-
- Params: count 100000  min_pkt_size: 60  max_pkt_size: 60
-     frags: 0  delay: 0  clone_skb: 64  ifname: eth4@0
-     flows: 0 flowlen: 0
-     queue_map_min: 0  queue_map_max: 0
-     dst_min: 192.168.81.2  dst_max:
-     src_min:   src_max:
-     src_mac: 90:e2:ba:0a:56:b4 dst_mac: 00:1b:21:3c:9d:f8
-     udp_src_min: 9  udp_src_max: 109  udp_dst_min: 9  udp_dst_max: 9
-     src_mac_count: 0  dst_mac_count: 0
-     Flags: UDPSRC_RND  NO_TIMESTAMP  QUEUE_MAP_CPU
- Current:
-     pkts-sofar: 100000  errors: 0
-     started: 623913381008us  stopped: 623913396439us idle: 25us
-     seq_num: 100001  cur_dst_mac_offset: 0  cur_src_mac_offset: 0
-     cur_saddr: 192.168.8.3  cur_daddr: 192.168.81.2
-     cur_udp_dst: 9  cur_udp_src: 42
-     cur_queue_map: 0
-     flows: 0
- Result: OK: 15430(c15405+d25) usec, 100000 (60byte,0frags)
-  6480562pps 3110Mb/sec (3110669760bps) errors: 0
-
-
-Configuring devices
-===================
-This is done via the /proc interface, and most easily done via pgset
-as defined in the sample scripts.
-You need to specify PGDEV environment variable to use functions from sample
-scripts, i.e.:
-export PGDEV=/proc/net/pktgen/eth4@0
-source samples/pktgen/functions.sh
-
-Examples:
-
- pg_ctrl start           starts injection.
- pg_ctrl stop            aborts injection. Also, ^C aborts generator.
-
- pgset "clone_skb 1"     sets the number of copies of the same packet
- pgset "clone_skb 0"     use single SKB for all transmits
- pgset "burst 8"         uses xmit_more API to queue 8 copies of the same
-                         packet and update HW tx queue tail pointer once.
-                         "burst 1" is the default
- pgset "pkt_size 9014"   sets packet size to 9014
- pgset "frags 5"         packet will consist of 5 fragments
- pgset "count 200000"    sets number of packets to send, set to zero
-                         for continuous sends until explicitly stopped.
-
- pgset "delay 5000"      adds delay to hard_start_xmit(). nanoseconds
-
- pgset "dst 10.0.0.1"    sets IP destination address
-                         (BEWARE! This generator is very aggressive!)
-
- pgset "dst_min 10.0.0.1"            Same as dst
- pgset "dst_max 10.0.0.254"          Set the maximum destination IP.
- pgset "src_min 10.0.0.1"            Set the minimum (or only) source IP.
- pgset "src_max 10.0.0.254"          Set the maximum source IP.
- pgset "dst6 fec0::1"     IPV6 destination address
- pgset "src6 fec0::2"     IPV6 source address
- pgset "dstmac 00:00:00:00:00:00"    sets MAC destination address
- pgset "srcmac 00:00:00:00:00:00"    sets MAC source address
-
- pgset "queue_map_min 0" Sets the min value of tx queue interval
- pgset "queue_map_max 7" Sets the max value of tx queue interval, for multiqueue devices
-                         To select queue 1 of a given device,
-                         use queue_map_min=1 and queue_map_max=1
-
- pgset "src_mac_count 1" Sets the number of MACs we'll range through.
-                         The 'minimum' MAC is what you set with srcmac.
-
- pgset "dst_mac_count 1" Sets the number of MACs we'll range through.
-                         The 'minimum' MAC is what you set with dstmac.
-
- pgset "flag [name]"     Set a flag to determine behaviour.  Current flags
-                         are: IPSRC_RND # IP source is random (between min/max)
-                              IPDST_RND # IP destination is random
-                              UDPSRC_RND, UDPDST_RND,
-                              MACSRC_RND, MACDST_RND
-                              TXSIZE_RND, IPV6,
-                              MPLS_RND, VID_RND, SVID_RND
-                              FLOW_SEQ,
-                              QUEUE_MAP_RND # queue map random
-                              QUEUE_MAP_CPU # queue map mirrors smp_processor_id()
-                              UDPCSUM,
-                              IPSEC # IPsec encapsulation (needs CONFIG_XFRM)
-                              NODE_ALLOC # node specific memory allocation
-                              NO_TIMESTAMP # disable timestamping
- pgset 'flag ![name]'    Clear a flag to determine behaviour.
-                         Note that you might need to use single quote in
-                         interactive mode, so that your shell wouldn't expand
-                         the specified flag as a history command.
-
- pgset "spi [SPI_VALUE]" Set specific SA used to transform packet.
-
- pgset "udp_src_min 9"   set UDP source port min, If < udp_src_max, then
-                         cycle through the port range.
-
- pgset "udp_src_max 9"   set UDP source port max.
- pgset "udp_dst_min 9"   set UDP destination port min, If < udp_dst_max, then
-                         cycle through the port range.
- pgset "udp_dst_max 9"   set UDP destination port max.
-
- pgset "mpls 0001000a,0002000a,0000000a" set MPLS labels (in this example
-                                         outer label=16,middle label=32,
-                                        inner label=0 (IPv4 NULL)) Note that
-                                        there must be no spaces between the
-                                        arguments. Leading zeros are required.
-                                        Do not set the bottom of stack bit,
-                                        that's done automatically. If you do
-                                        set the bottom of stack bit, that
-                                        indicates that you want to randomly
-                                        generate that address and the flag
-                                        MPLS_RND will be turned on. You
-                                        can have any mix of random and fixed
-                                        labels in the label stack.
-
- pgset "mpls 0"                  turn off mpls (or any invalid argument works too!)
-
- pgset "vlan_id 77"       set VLAN ID 0-4095
- pgset "vlan_p 3"         set priority bit 0-7 (default 0)
- pgset "vlan_cfi 0"       set canonical format identifier 0-1 (default 0)
-
- pgset "svlan_id 22"      set SVLAN ID 0-4095
- pgset "svlan_p 3"        set priority bit 0-7 (default 0)
- pgset "svlan_cfi 0"      set canonical format identifier 0-1 (default 0)
-
- pgset "vlan_id 9999"     > 4095 remove vlan and svlan tags
- pgset "svlan 9999"       > 4095 remove svlan tag
-
-
- pgset "tos XX"           set former IPv4 TOS field (e.g. "tos 28" for AF11 no ECN, default 00)
- pgset "traffic_class XX" set former IPv6 TRAFFIC CLASS (e.g. "traffic_class B8" for EF no ECN, default 00)
-
- pgset "rate 300M"        set rate to 300 Mb/s
- pgset "ratep 1000000"    set rate to 1Mpps
-
- pgset "xmit_mode netif_receive"  RX inject into stack netif_receive_skb()
-                                 Works with "burst" but not with "clone_skb".
-                                 Default xmit_mode is "start_xmit".
-
-Sample scripts
-==============
-
-A collection of tutorial scripts and helpers for pktgen is in the
-samples/pktgen directory. The helper parameters.sh file support easy
-and consistent parameter parsing across the sample scripts.
-
-Usage example and help:
- ./pktgen_sample01_simple.sh -i eth4 -m 00:1B:21:3C:9D:F8 -d 192.168.8.2
-
-Usage: ./pktgen_sample01_simple.sh [-vx] -i ethX
-  -i : ($DEV)       output interface/device (required)
-  -s : ($PKT_SIZE)  packet size
-  -d : ($DEST_IP)   destination IP
-  -m : ($DST_MAC)   destination MAC-addr
-  -t : ($THREADS)   threads to start
-  -c : ($SKB_CLONE) SKB clones send before alloc new SKB
-  -b : ($BURST)     HW level bursting of SKBs
-  -v : ($VERBOSE)   verbose
-  -x : ($DEBUG)     debug
-
-The global variables being set are also listed.  E.g. the required
-interface/device parameter "-i" sets variable $DEV.  Copy the
-pktgen_sampleXX scripts and modify them to fit your own needs.
-
-The old scripts:
-
-pktgen.conf-1-2                  # 1 CPU 2 dev
-pktgen.conf-1-1-rdos             # 1 CPU 1 dev w. route DoS 
-pktgen.conf-1-1-ip6              # 1 CPU 1 dev ipv6
-pktgen.conf-1-1-ip6-rdos         # 1 CPU 1 dev ipv6  w. route DoS
-pktgen.conf-1-1-flows            # 1 CPU 1 dev multiple flows.
-
-
-Interrupt affinity
-===================
-Note that when adding devices to a specific CPU it is a good idea to
-also assign /proc/irq/XX/smp_affinity so that the TX interrupts are bound
-to the same CPU.  This reduces cache bouncing when freeing skbs.
-
-Plus using the device flag QUEUE_MAP_CPU, which maps the SKBs TX queue
-to the running threads CPU (directly from smp_processor_id()).
-
-Enable IPsec
-============
-Default IPsec transformation with ESP encapsulation plus transport mode
-can be enabled by simply setting:
-
-pgset "flag IPSEC"
-pgset "flows 1"
-
-To avoid breaking existing testbed scripts for using AH type and tunnel mode,
-you can use "pgset spi SPI_VALUE" to specify which transformation mode
-to employ.
-
-
-Current commands and configuration options
-==========================================
-
-** Pgcontrol commands:
-
-start
-stop
-reset
-
-** Thread commands:
-
-add_device
-rem_device_all
-
-
-** Device commands:
-
-count
-clone_skb
-burst
-debug
-
-frags
-delay
-
-src_mac_count
-dst_mac_count
-
-pkt_size
-min_pkt_size
-max_pkt_size
-
-queue_map_min
-queue_map_max
-skb_priority
-
-tos           (ipv4)
-traffic_class (ipv6)
-
-mpls
-
-udp_src_min
-udp_src_max
-
-udp_dst_min
-udp_dst_max
-
-node
-
-flag
-  IPSRC_RND
-  IPDST_RND
-  UDPSRC_RND
-  UDPDST_RND
-  MACSRC_RND
-  MACDST_RND
-  TXSIZE_RND
-  IPV6
-  MPLS_RND
-  VID_RND
-  SVID_RND
-  FLOW_SEQ
-  QUEUE_MAP_RND
-  QUEUE_MAP_CPU
-  UDPCSUM
-  IPSEC
-  NODE_ALLOC
-  NO_TIMESTAMP
-
-spi (ipsec)
-
-dst_min
-dst_max
-
-src_min
-src_max
-
-dst_mac
-src_mac
-
-clear_counters
-
-src6
-dst6
-dst6_max
-dst6_min
-
-flows
-flowlen
-
-rate
-ratep
-
-xmit_mode <start_xmit|netif_receive>
-
-vlan_cfi
-vlan_id
-vlan_p
-
-svlan_cfi
-svlan_id
-svlan_p
-
-
-References:
-ftp://robur.slu.se/pub/Linux/net-development/pktgen-testing/
-ftp://robur.slu.se/pub/Linux/net-development/pktgen-testing/examples/
-
-Paper from Linux-Kongress in Erlangen 2004.
-ftp://robur.slu.se/pub/Linux/net-development/pktgen-testing/pktgen_paper.pdf
-
-Thanks to:
-Grant Grundler for testing on IA-64 and parisc, Harald Welte,  Lennert Buytenhek
-Stephen Hemminger, Andi Kleen, Dave Miller and many others.
-
-
-Good luck with the linux net-development.
diff --git a/Documentation/networking/plip.rst b/Documentation/networking/plip.rst
new file mode 100644 (file)
index 0000000..0eda745
--- /dev/null
@@ -0,0 +1,222 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+================================================
+PLIP: The Parallel Line Internet Protocol Device
+================================================
+
+Donald Becker (becker@super.org)
+I.D.A. Supercomputing Research Center, Bowie MD 20715
+
+At some point T. Thorn will probably contribute text,
+Tommy Thorn (tthorn@daimi.aau.dk)
+
+PLIP Introduction
+-----------------
+
+This document describes the parallel port packet pusher for Net/LGX.
+This device interface allows a point-to-point connection between two
+parallel ports to appear as a IP network interface.
+
+What is PLIP?
+=============
+
+PLIP is Parallel Line IP, that is, the transportation of IP packages
+over a parallel port. In the case of a PC, the obvious choice is the
+printer port.  PLIP is a non-standard, but [can use] uses the standard
+LapLink null-printer cable [can also work in turbo mode, with a PLIP
+cable]. [The protocol used to pack IP packages, is a simple one
+initiated by Crynwr.]
+
+Advantages of PLIP
+==================
+
+It's cheap, it's available everywhere, and it's easy.
+
+The PLIP cable is all that's needed to connect two Linux boxes, and it
+can be built for very few bucks.
+
+Connecting two Linux boxes takes only a second's decision and a few
+minutes' work, no need to search for a [supported] netcard. This might
+even be especially important in the case of notebooks, where netcards
+are not easily available.
+
+Not requiring a netcard also means that apart from connecting the
+cables, everything else is software configuration [which in principle
+could be made very easy.]
+
+Disadvantages of PLIP
+=====================
+
+Doesn't work over a modem, like SLIP and PPP. Limited range, 15 m.
+Can only be used to connect three (?) Linux boxes. Doesn't connect to
+an existing Ethernet. Isn't standard (not even de facto standard, like
+SLIP).
+
+Performance
+===========
+
+PLIP easily outperforms Ethernet cards....(ups, I was dreaming, but
+it *is* getting late. EOB)
+
+PLIP driver details
+-------------------
+
+The Linux PLIP driver is an implementation of the original Crynwr protocol,
+that uses the parallel port subsystem of the kernel in order to properly
+share parallel ports between PLIP and other services.
+
+IRQs and trigger timeouts
+=========================
+
+When a parallel port used for a PLIP driver has an IRQ configured to it, the
+PLIP driver is signaled whenever data is sent to it via the cable, such that
+when no data is available, the driver isn't being used.
+
+However, on some machines it is hard, if not impossible, to configure an IRQ
+to a certain parallel port, mainly because it is used by some other device.
+On these machines, the PLIP driver can be used in IRQ-less mode, where
+the PLIP driver would constantly poll the parallel port for data waiting,
+and if such data is available, process it. This mode is less efficient than
+the IRQ mode, because the driver has to check the parallel port many times
+per second, even when no data at all is sent. Some rough measurements
+indicate that there isn't a noticeable performance drop when using IRQ-less
+mode as compared to IRQ mode as far as the data transfer speed is involved.
+There is a performance drop on the machine hosting the driver.
+
+When the PLIP driver is used in IRQ mode, the timeout used for triggering a
+data transfer (the maximal time the PLIP driver would allow the other side
+before announcing a timeout, when trying to handshake a transfer of some
+data) is, by default, 500usec. As IRQ delivery is more or less immediate,
+this timeout is quite sufficient.
+
+When in IRQ-less mode, the PLIP driver polls the parallel port HZ times
+per second (where HZ is typically 100 on most platforms, and 1024 on an
+Alpha, as of this writing). Between two such polls, there are 10^6/HZ usecs.
+On an i386, for example, 10^6/100 = 10000usec. It is easy to see that it is
+quite possible for the trigger timeout to expire between two such polls, as
+the timeout is only 500usec long. As a result, it is required to change the
+trigger timeout on the *other* side of a PLIP connection, to about
+10^6/HZ usecs. If both sides of a PLIP connection are used in IRQ-less mode,
+this timeout is required on both sides.
+
+It appears that in practice, the trigger timeout can be shorter than in the
+above calculation. It isn't an important issue, unless the wire is faulty,
+in which case a long timeout would stall the machine when, for whatever
+reason, bits are dropped.
+
+A utility that can perform this change in Linux is plipconfig, which is part
+of the net-tools package (its location can be found in the
+Documentation/Changes file). An example command would be
+'plipconfig plipX trigger 10000', where plipX is the appropriate
+PLIP device.
+
+PLIP hardware interconnection
+-----------------------------
+
+PLIP uses several different data transfer methods.  The first (and the
+only one implemented in the early version of the code) uses a standard
+printer "null" cable to transfer data four bits at a time using
+data bit outputs connected to status bit inputs.
+
+The second data transfer method relies on both machines having
+bi-directional parallel ports, rather than output-only ``printer``
+ports.  This allows byte-wide transfers and avoids reconstructing
+nibbles into bytes, leading to much faster transfers.
+
+Parallel Transfer Mode 0 Cable
+==============================
+
+The cable for the first transfer mode is a standard
+printer "null" cable which transfers data four bits at a time using
+data bit outputs of the first port (machine T) connected to the
+status bit inputs of the second port (machine R).  There are five
+status inputs, and they are used as four data inputs and a clock (data
+strobe) input, arranged so that the data input bits appear as contiguous
+bits with standard status register implementation.
+
+A cable that implements this protocol is available commercially as a
+"Null Printer" or "Turbo Laplink" cable.  It can be constructed with
+two DB-25 male connectors symmetrically connected as follows::
+
+    STROBE output      1*
+    D0->ERROR  2 - 15          15 - 2
+    D1->SLCT   3 - 13          13 - 3
+    D2->PAPOUT 4 - 12          12 - 4
+    D3->ACK    5 - 10          10 - 5
+    D4->BUSY   6 - 11          11 - 6
+    D5,D6,D7 are   7*, 8*, 9*
+    AUTOFD output 14*
+    INIT   output 16*
+    SLCTIN     17 - 17
+    extra grounds are 18*,19*,20*,21*,22*,23*,24*
+    GROUND     25 - 25
+
+    * Do not connect these pins on either end
+
+If the cable you are using has a metallic shield it should be
+connected to the metallic DB-25 shell at one end only.
+
+Parallel Transfer Mode 1
+========================
+
+The second data transfer method relies on both machines having
+bi-directional parallel ports, rather than output-only ``printer``
+ports.  This allows byte-wide transfers, and avoids reconstructing
+nibbles into bytes.  This cable should not be used on unidirectional
+``printer`` (as opposed to ``parallel``) ports or when the machine
+isn't configured for PLIP, as it will result in output driver
+conflicts and the (unlikely) possibility of damage.
+
+The cable for this transfer mode should be constructed as follows::
+
+    STROBE->BUSY 1 - 11
+    D0->D0     2 - 2
+    D1->D1     3 - 3
+    D2->D2     4 - 4
+    D3->D3     5 - 5
+    D4->D4     6 - 6
+    D5->D5     7 - 7
+    D6->D6     8 - 8
+    D7->D7     9 - 9
+    INIT -> ACK  16 - 10
+    AUTOFD->PAPOUT 14 - 12
+    SLCT->SLCTIN 13 - 17
+    GND->ERROR 18 - 15
+    extra grounds are 19*,20*,21*,22*,23*,24*
+    GROUND     25 - 25
+
+    * Do not connect these pins on either end
+
+Once again, if the cable you are using has a metallic shield it should
+be connected to the metallic DB-25 shell at one end only.
+
+PLIP Mode 0 transfer protocol
+=============================
+
+The PLIP driver is compatible with the "Crynwr" parallel port transfer
+standard in Mode 0.  That standard specifies the following protocol::
+
+   send header nibble '0x8'
+   count-low octet
+   count-high octet
+   ... data octets
+   checksum octet
+
+Each octet is sent as::
+
+       <wait for rx. '0x1?'>   <send 0x10+(octet&0x0F)>
+       <wait for rx. '0x0?'>   <send 0x00+((octet>>4)&0x0F)>
+
+To start a transfer the transmitting machine outputs a nibble 0x08.
+That raises the ACK line, triggering an interrupt in the receiving
+machine.  The receiving machine disables interrupts and raises its own ACK
+line.
+
+Restated::
+
+  (OUT is bit 0-4, OUT.j is bit j from OUT. IN likewise)
+  Send_Byte:
+     OUT := low nibble, OUT.4 := 1
+     WAIT FOR IN.4 = 1
+     OUT := high nibble, OUT.4 := 0
+     WAIT FOR IN.4 = 0
diff --git a/Documentation/networking/ppp_generic.rst b/Documentation/networking/ppp_generic.rst
new file mode 100644 (file)
index 0000000..e605043
--- /dev/null
@@ -0,0 +1,440 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+========================================
+PPP Generic Driver and Channel Interface
+========================================
+
+                          Paul Mackerras
+                          paulus@samba.org
+
+                             7 Feb 2002
+
+The generic PPP driver in linux-2.4 provides an implementation of the
+functionality which is of use in any PPP implementation, including:
+
+* the network interface unit (ppp0 etc.)
+* the interface to the networking code
+* PPP multilink: splitting datagrams between multiple links, and
+  ordering and combining received fragments
+* the interface to pppd, via a /dev/ppp character device
+* packet compression and decompression
+* TCP/IP header compression and decompression
+* detecting network traffic for demand dialling and for idle timeouts
+* simple packet filtering
+
+For sending and receiving PPP frames, the generic PPP driver calls on
+the services of PPP ``channels``.  A PPP channel encapsulates a
+mechanism for transporting PPP frames from one machine to another.  A
+PPP channel implementation can be arbitrarily complex internally but
+has a very simple interface with the generic PPP code: it merely has
+to be able to send PPP frames, receive PPP frames, and optionally
+handle ioctl requests.  Currently there are PPP channel
+implementations for asynchronous serial ports, synchronous serial
+ports, and for PPP over ethernet.
+
+This architecture makes it possible to implement PPP multilink in a
+natural and straightforward way, by allowing more than one channel to
+be linked to each ppp network interface unit.  The generic layer is
+responsible for splitting datagrams on transmit and recombining them
+on receive.
+
+
+PPP channel API
+---------------
+
+See include/linux/ppp_channel.h for the declaration of the types and
+functions used to communicate between the generic PPP layer and PPP
+channels.
+
+Each channel has to provide two functions to the generic PPP layer,
+via the ppp_channel.ops pointer:
+
+* start_xmit() is called by the generic layer when it has a frame to
+  send.  The channel has the option of rejecting the frame for
+  flow-control reasons.  In this case, start_xmit() should return 0
+  and the channel should call the ppp_output_wakeup() function at a
+  later time when it can accept frames again, and the generic layer
+  will then attempt to retransmit the rejected frame(s).  If the frame
+  is accepted, the start_xmit() function should return 1.
+
+* ioctl() provides an interface which can be used by a user-space
+  program to control aspects of the channel's behaviour.  This
+  procedure will be called when a user-space program does an ioctl
+  system call on an instance of /dev/ppp which is bound to the
+  channel.  (Usually it would only be pppd which would do this.)
+
+The generic PPP layer provides seven functions to channels:
+
+* ppp_register_channel() is called when a channel has been created, to
+  notify the PPP generic layer of its presence.  For example, setting
+  a serial port to the PPPDISC line discipline causes the ppp_async
+  channel code to call this function.
+
+* ppp_unregister_channel() is called when a channel is to be
+  destroyed.  For example, the ppp_async channel code calls this when
+  a hangup is detected on the serial port.
+
+* ppp_output_wakeup() is called by a channel when it has previously
+  rejected a call to its start_xmit function, and can now accept more
+  packets.
+
+* ppp_input() is called by a channel when it has received a complete
+  PPP frame.
+
+* ppp_input_error() is called by a channel when it has detected that a
+  frame has been lost or dropped (for example, because of a FCS (frame
+  check sequence) error).
+
+* ppp_channel_index() returns the channel index assigned by the PPP
+  generic layer to this channel.  The channel should provide some way
+  (e.g. an ioctl) to transmit this back to user-space, as user-space
+  will need it to attach an instance of /dev/ppp to this channel.
+
+* ppp_unit_number() returns the unit number of the ppp network
+  interface to which this channel is connected, or -1 if the channel
+  is not connected.
+
+Connecting a channel to the ppp generic layer is initiated from the
+channel code, rather than from the generic layer.  The channel is
+expected to have some way for a user-level process to control it
+independently of the ppp generic layer.  For example, with the
+ppp_async channel, this is provided by the file descriptor to the
+serial port.
+
+Generally a user-level process will initialize the underlying
+communications medium and prepare it to do PPP.  For example, with an
+async tty, this can involve setting the tty speed and modes, issuing
+modem commands, and then going through some sort of dialog with the
+remote system to invoke PPP service there.  We refer to this process
+as ``discovery``.  Then the user-level process tells the medium to
+become a PPP channel and register itself with the generic PPP layer.
+The channel then has to report the channel number assigned to it back
+to the user-level process.  From that point, the PPP negotiation code
+in the PPP daemon (pppd) can take over and perform the PPP
+negotiation, accessing the channel through the /dev/ppp interface.
+
+At the interface to the PPP generic layer, PPP frames are stored in
+skbuff structures and start with the two-byte PPP protocol number.
+The frame does *not* include the 0xff ``address`` byte or the 0x03
+``control`` byte that are optionally used in async PPP.  Nor is there
+any escaping of control characters, nor are there any FCS or framing
+characters included.  That is all the responsibility of the channel
+code, if it is needed for the particular medium.  That is, the skbuffs
+presented to the start_xmit() function contain only the 2-byte
+protocol number and the data, and the skbuffs presented to ppp_input()
+must be in the same format.
+
+The channel must provide an instance of a ppp_channel struct to
+represent the channel.  The channel is free to use the ``private`` field
+however it wishes.  The channel should initialize the ``mtu`` and
+``hdrlen`` fields before calling ppp_register_channel() and not change
+them until after ppp_unregister_channel() returns.  The ``mtu`` field
+represents the maximum size of the data part of the PPP frames, that
+is, it does not include the 2-byte protocol number.
+
+If the channel needs some headroom in the skbuffs presented to it for
+transmission (i.e., some space free in the skbuff data area before the
+start of the PPP frame), it should set the ``hdrlen`` field of the
+ppp_channel struct to the amount of headroom required.  The generic
+PPP layer will attempt to provide that much headroom but the channel
+should still check if there is sufficient headroom and copy the skbuff
+if there isn't.
+
+On the input side, channels should ideally provide at least 2 bytes of
+headroom in the skbuffs presented to ppp_input().  The generic PPP
+code does not require this but will be more efficient if this is done.
+
+
+Buffering and flow control
+--------------------------
+
+The generic PPP layer has been designed to minimize the amount of data
+that it buffers in the transmit direction.  It maintains a queue of
+transmit packets for the PPP unit (network interface device) plus a
+queue of transmit packets for each attached channel.  Normally the
+transmit queue for the unit will contain at most one packet; the
+exceptions are when pppd sends packets by writing to /dev/ppp, and
+when the core networking code calls the generic layer's start_xmit()
+function with the queue stopped, i.e. when the generic layer has
+called netif_stop_queue(), which only happens on a transmit timeout.
+The start_xmit function always accepts and queues the packet which it
+is asked to transmit.
+
+Transmit packets are dequeued from the PPP unit transmit queue and
+then subjected to TCP/IP header compression and packet compression
+(Deflate or BSD-Compress compression), as appropriate.  After this
+point the packets can no longer be reordered, as the decompression
+algorithms rely on receiving compressed packets in the same order that
+they were generated.
+
+If multilink is not in use, this packet is then passed to the attached
+channel's start_xmit() function.  If the channel refuses to take
+the packet, the generic layer saves it for later transmission.  The
+generic layer will call the channel's start_xmit() function again
+when the channel calls  ppp_output_wakeup() or when the core
+networking code calls the generic layer's start_xmit() function
+again.  The generic layer contains no timeout and retransmission
+logic; it relies on the core networking code for that.
+
+If multilink is in use, the generic layer divides the packet into one
+or more fragments and puts a multilink header on each fragment.  It
+decides how many fragments to use based on the length of the packet
+and the number of channels which are potentially able to accept a
+fragment at the moment.  A channel is potentially able to accept a
+fragment if it doesn't have any fragments currently queued up for it
+to transmit.  The channel may still refuse a fragment; in this case
+the fragment is queued up for the channel to transmit later.  This
+scheme has the effect that more fragments are given to higher-
+bandwidth channels.  It also means that under light load, the generic
+layer will tend to fragment large packets across all the channels,
+thus reducing latency, while under heavy load, packets will tend to be
+transmitted as single fragments, thus reducing the overhead of
+fragmentation.
+
+
+SMP safety
+----------
+
+The PPP generic layer has been designed to be SMP-safe.  Locks are
+used around accesses to the internal data structures where necessary
+to ensure their integrity.  As part of this, the generic layer
+requires that the channels adhere to certain requirements and in turn
+provides certain guarantees to the channels.  Essentially the channels
+are required to provide the appropriate locking on the ppp_channel
+structures that form the basis of the communication between the
+channel and the generic layer.  This is because the channel provides
+the storage for the ppp_channel structure, and so the channel is
+required to provide the guarantee that this storage exists and is
+valid at the appropriate times.
+
+The generic layer requires these guarantees from the channel:
+
+* The ppp_channel object must exist from the time that
+  ppp_register_channel() is called until after the call to
+  ppp_unregister_channel() returns.
+
+* No thread may be in a call to any of ppp_input(), ppp_input_error(),
+  ppp_output_wakeup(), ppp_channel_index() or ppp_unit_number() for a
+  channel at the time that ppp_unregister_channel() is called for that
+  channel.
+
+* ppp_register_channel() and ppp_unregister_channel() must be called
+  from process context, not interrupt or softirq/BH context.
+
+* The remaining generic layer functions may be called at softirq/BH
+  level but must not be called from a hardware interrupt handler.
+
+* The generic layer may call the channel start_xmit() function at
+  softirq/BH level but will not call it at interrupt level.  Thus the
+  start_xmit() function may not block.
+
+* The generic layer will only call the channel ioctl() function in
+  process context.
+
+The generic layer provides these guarantees to the channels:
+
+* The generic layer will not call the start_xmit() function for a
+  channel while any thread is already executing in that function for
+  that channel.
+
+* The generic layer will not call the ioctl() function for a channel
+  while any thread is already executing in that function for that
+  channel.
+
+* By the time a call to ppp_unregister_channel() returns, no thread
+  will be executing in a call from the generic layer to that channel's
+  start_xmit() or ioctl() function, and the generic layer will not
+  call either of those functions subsequently.
+
+
+Interface to pppd
+-----------------
+
+The PPP generic layer exports a character device interface called
+/dev/ppp.  This is used by pppd to control PPP interface units and
+channels.  Although there is only one /dev/ppp, each open instance of
+/dev/ppp acts independently and can be attached either to a PPP unit
+or a PPP channel.  This is achieved using the file->private_data field
+to point to a separate object for each open instance of /dev/ppp.  In
+this way an effect similar to Solaris' clone open is obtained,
+allowing us to control an arbitrary number of PPP interfaces and
+channels without having to fill up /dev with hundreds of device names.
+
+When /dev/ppp is opened, a new instance is created which is initially
+unattached.  Using an ioctl call, it can then be attached to an
+existing unit, attached to a newly-created unit, or attached to an
+existing channel.  An instance attached to a unit can be used to send
+and receive PPP control frames, using the read() and write() system
+calls, along with poll() if necessary.  Similarly, an instance
+attached to a channel can be used to send and receive PPP frames on
+that channel.
+
+In multilink terms, the unit represents the bundle, while the channels
+represent the individual physical links.  Thus, a PPP frame sent by a
+write to the unit (i.e., to an instance of /dev/ppp attached to the
+unit) will be subject to bundle-level compression and to fragmentation
+across the individual links (if multilink is in use).  In contrast, a
+PPP frame sent by a write to the channel will be sent as-is on that
+channel, without any multilink header.
+
+A channel is not initially attached to any unit.  In this state it can
+be used for PPP negotiation but not for the transfer of data packets.
+It can then be connected to a PPP unit with an ioctl call, which
+makes it available to send and receive data packets for that unit.
+
+The ioctl calls which are available on an instance of /dev/ppp depend
+on whether it is unattached, attached to a PPP interface, or attached
+to a PPP channel.  The ioctl calls which are available on an
+unattached instance are:
+
+* PPPIOCNEWUNIT creates a new PPP interface and makes this /dev/ppp
+  instance the "owner" of the interface.  The argument should point to
+  an int which is the desired unit number if >= 0, or -1 to assign the
+  lowest unused unit number.  Being the owner of the interface means
+  that the interface will be shut down if this instance of /dev/ppp is
+  closed.
+
+* PPPIOCATTACH attaches this instance to an existing PPP interface.
+  The argument should point to an int containing the unit number.
+  This does not make this instance the owner of the PPP interface.
+
+* PPPIOCATTCHAN attaches this instance to an existing PPP channel.
+  The argument should point to an int containing the channel number.
+
+The ioctl calls available on an instance of /dev/ppp attached to a
+channel are:
+
+* PPPIOCCONNECT connects this channel to a PPP interface.  The
+  argument should point to an int containing the interface unit
+  number.  It will return an EINVAL error if the channel is already
+  connected to an interface, or ENXIO if the requested interface does
+  not exist.
+
+* PPPIOCDISCONN disconnects this channel from the PPP interface that
+  it is connected to.  It will return an EINVAL error if the channel
+  is not connected to an interface.
+
+* All other ioctl commands are passed to the channel ioctl() function.
+
+The ioctl calls that are available on an instance that is attached to
+an interface unit are:
+
+* PPPIOCSMRU sets the MRU (maximum receive unit) for the interface.
+  The argument should point to an int containing the new MRU value.
+
+* PPPIOCSFLAGS sets flags which control the operation of the
+  interface.  The argument should be a pointer to an int containing
+  the new flags value.  The bits in the flags value that can be set
+  are:
+
+       ================        ========================================
+       SC_COMP_TCP             enable transmit TCP header compression
+       SC_NO_TCP_CCID          disable connection-id compression for
+                               TCP header compression
+       SC_REJ_COMP_TCP         disable receive TCP header decompression
+       SC_CCP_OPEN             Compression Control Protocol (CCP) is
+                               open, so inspect CCP packets
+       SC_CCP_UP               CCP is up, may (de)compress packets
+       SC_LOOP_TRAFFIC         send IP traffic to pppd
+       SC_MULTILINK            enable PPP multilink fragmentation on
+                               transmitted packets
+       SC_MP_SHORTSEQ          expect short multilink sequence
+                               numbers on received multilink fragments
+       SC_MP_XSHORTSEQ         transmit short multilink sequence nos.
+       ================        ========================================
+
+  The values of these flags are defined in <linux/ppp-ioctl.h>.  Note
+  that the values of the SC_MULTILINK, SC_MP_SHORTSEQ and
+  SC_MP_XSHORTSEQ bits are ignored if the CONFIG_PPP_MULTILINK option
+  is not selected.
+
+* PPPIOCGFLAGS returns the value of the status/control flags for the
+  interface unit.  The argument should point to an int where the ioctl
+  will store the flags value.  As well as the values listed above for
+  PPPIOCSFLAGS, the following bits may be set in the returned value:
+
+       ================        =========================================
+       SC_COMP_RUN             CCP compressor is running
+       SC_DECOMP_RUN           CCP decompressor is running
+       SC_DC_ERROR             CCP decompressor detected non-fatal error
+       SC_DC_FERROR            CCP decompressor detected fatal error
+       ================        =========================================
+
+* PPPIOCSCOMPRESS sets the parameters for packet compression or
+  decompression.  The argument should point to a ppp_option_data
+  structure (defined in <linux/ppp-ioctl.h>), which contains a
+  pointer/length pair which should describe a block of memory
+  containing a CCP option specifying a compression method and its
+  parameters.  The ppp_option_data struct also contains a ``transmit``
+  field.  If this is 0, the ioctl will affect the receive path,
+  otherwise the transmit path.
+
+* PPPIOCGUNIT returns, in the int pointed to by the argument, the unit
+  number of this interface unit.
+
+* PPPIOCSDEBUG sets the debug flags for the interface to the value in
+  the int pointed to by the argument.  Only the least significant bit
+  is used; if this is 1 the generic layer will print some debug
+  messages during its operation.  This is only intended for debugging
+  the generic PPP layer code; it is generally not helpful for working
+  out why a PPP connection is failing.
+
+* PPPIOCGDEBUG returns the debug flags for the interface in the int
+  pointed to by the argument.
+
+* PPPIOCGIDLE returns the time, in seconds, since the last data
+  packets were sent and received.  The argument should point to a
+  ppp_idle structure (defined in <linux/ppp_defs.h>).  If the
+  CONFIG_PPP_FILTER option is enabled, the set of packets which reset
+  the transmit and receive idle timers is restricted to those which
+  pass the ``active`` packet filter.
+  Two versions of this command exist, to deal with user space
+  expecting times as either 32-bit or 64-bit time_t seconds.
+
+* PPPIOCSMAXCID sets the maximum connection-ID parameter (and thus the
+  number of connection slots) for the TCP header compressor and
+  decompressor.  The lower 16 bits of the int pointed to by the
+  argument specify the maximum connection-ID for the compressor.  If
+  the upper 16 bits of that int are non-zero, they specify the maximum
+  connection-ID for the decompressor, otherwise the decompressor's
+  maximum connection-ID is set to 15.
+
+* PPPIOCSNPMODE sets the network-protocol mode for a given network
+  protocol.  The argument should point to an npioctl struct (defined
+  in <linux/ppp-ioctl.h>).  The ``protocol`` field gives the PPP protocol
+  number for the protocol to be affected, and the ``mode`` field
+  specifies what to do with packets for that protocol:
+
+       =============   ==============================================
+       NPMODE_PASS     normal operation, transmit and receive packets
+       NPMODE_DROP     silently drop packets for this protocol
+       NPMODE_ERROR    drop packets and return an error on transmit
+       NPMODE_QUEUE    queue up packets for transmit, drop received
+                       packets
+       =============   ==============================================
+
+  At present NPMODE_ERROR and NPMODE_QUEUE have the same effect as
+  NPMODE_DROP.
+
+* PPPIOCGNPMODE returns the network-protocol mode for a given
+  protocol.  The argument should point to an npioctl struct with the
+  ``protocol`` field set to the PPP protocol number for the protocol of
+  interest.  On return the ``mode`` field will be set to the network-
+  protocol mode for that protocol.
+
+* PPPIOCSPASS and PPPIOCSACTIVE set the ``pass`` and ``active`` packet
+  filters.  These ioctls are only available if the CONFIG_PPP_FILTER
+  option is selected.  The argument should point to a sock_fprog
+  structure (defined in <linux/filter.h>) containing the compiled BPF
+  instructions for the filter.  Packets are dropped if they fail the
+  ``pass`` filter; otherwise, if they fail the ``active`` filter they are
+  passed but they do not reset the transmit or receive idle timer.
+
+* PPPIOCSMRRU enables or disables multilink processing for received
+  packets and sets the multilink MRRU (maximum reconstructed receive
+  unit).  The argument should point to an int containing the new MRRU
+  value.  If the MRRU value is 0, processing of received multilink
+  fragments is disabled.  This ioctl is only available if the
+  CONFIG_PPP_MULTILINK option is selected.
+
+Last modified: 7-feb-2002
diff --git a/Documentation/networking/ppp_generic.txt b/Documentation/networking/ppp_generic.txt
deleted file mode 100644 (file)
index fd563af..0000000
+++ /dev/null
@@ -1,428 +0,0 @@
-               PPP Generic Driver and Channel Interface
-               ----------------------------------------
-
-                           Paul Mackerras
-                          paulus@samba.org
-                             7 Feb 2002
-
-The generic PPP driver in linux-2.4 provides an implementation of the
-functionality which is of use in any PPP implementation, including:
-
-* the network interface unit (ppp0 etc.)
-* the interface to the networking code
-* PPP multilink: splitting datagrams between multiple links, and
-  ordering and combining received fragments
-* the interface to pppd, via a /dev/ppp character device
-* packet compression and decompression
-* TCP/IP header compression and decompression
-* detecting network traffic for demand dialling and for idle timeouts
-* simple packet filtering
-
-For sending and receiving PPP frames, the generic PPP driver calls on
-the services of PPP `channels'.  A PPP channel encapsulates a
-mechanism for transporting PPP frames from one machine to another.  A
-PPP channel implementation can be arbitrarily complex internally but
-has a very simple interface with the generic PPP code: it merely has
-to be able to send PPP frames, receive PPP frames, and optionally
-handle ioctl requests.  Currently there are PPP channel
-implementations for asynchronous serial ports, synchronous serial
-ports, and for PPP over ethernet.
-
-This architecture makes it possible to implement PPP multilink in a
-natural and straightforward way, by allowing more than one channel to
-be linked to each ppp network interface unit.  The generic layer is
-responsible for splitting datagrams on transmit and recombining them
-on receive.
-
-
-PPP channel API
----------------
-
-See include/linux/ppp_channel.h for the declaration of the types and
-functions used to communicate between the generic PPP layer and PPP
-channels.
-
-Each channel has to provide two functions to the generic PPP layer,
-via the ppp_channel.ops pointer:
-
-* start_xmit() is called by the generic layer when it has a frame to
-  send.  The channel has the option of rejecting the frame for
-  flow-control reasons.  In this case, start_xmit() should return 0
-  and the channel should call the ppp_output_wakeup() function at a
-  later time when it can accept frames again, and the generic layer
-  will then attempt to retransmit the rejected frame(s).  If the frame
-  is accepted, the start_xmit() function should return 1.
-
-* ioctl() provides an interface which can be used by a user-space
-  program to control aspects of the channel's behaviour.  This
-  procedure will be called when a user-space program does an ioctl
-  system call on an instance of /dev/ppp which is bound to the
-  channel.  (Usually it would only be pppd which would do this.)
-
-The generic PPP layer provides seven functions to channels:
-
-* ppp_register_channel() is called when a channel has been created, to
-  notify the PPP generic layer of its presence.  For example, setting
-  a serial port to the PPPDISC line discipline causes the ppp_async
-  channel code to call this function.
-
-* ppp_unregister_channel() is called when a channel is to be
-  destroyed.  For example, the ppp_async channel code calls this when
-  a hangup is detected on the serial port.
-
-* ppp_output_wakeup() is called by a channel when it has previously
-  rejected a call to its start_xmit function, and can now accept more
-  packets.
-
-* ppp_input() is called by a channel when it has received a complete
-  PPP frame.
-
-* ppp_input_error() is called by a channel when it has detected that a
-  frame has been lost or dropped (for example, because of a FCS (frame
-  check sequence) error).
-
-* ppp_channel_index() returns the channel index assigned by the PPP
-  generic layer to this channel.  The channel should provide some way
-  (e.g. an ioctl) to transmit this back to user-space, as user-space
-  will need it to attach an instance of /dev/ppp to this channel.
-
-* ppp_unit_number() returns the unit number of the ppp network
-  interface to which this channel is connected, or -1 if the channel
-  is not connected.
-
-Connecting a channel to the ppp generic layer is initiated from the
-channel code, rather than from the generic layer.  The channel is
-expected to have some way for a user-level process to control it
-independently of the ppp generic layer.  For example, with the
-ppp_async channel, this is provided by the file descriptor to the
-serial port.
-
-Generally a user-level process will initialize the underlying
-communications medium and prepare it to do PPP.  For example, with an
-async tty, this can involve setting the tty speed and modes, issuing
-modem commands, and then going through some sort of dialog with the
-remote system to invoke PPP service there.  We refer to this process
-as `discovery'.  Then the user-level process tells the medium to
-become a PPP channel and register itself with the generic PPP layer.
-The channel then has to report the channel number assigned to it back
-to the user-level process.  From that point, the PPP negotiation code
-in the PPP daemon (pppd) can take over and perform the PPP
-negotiation, accessing the channel through the /dev/ppp interface.
-
-At the interface to the PPP generic layer, PPP frames are stored in
-skbuff structures and start with the two-byte PPP protocol number.
-The frame does *not* include the 0xff `address' byte or the 0x03
-`control' byte that are optionally used in async PPP.  Nor is there
-any escaping of control characters, nor are there any FCS or framing
-characters included.  That is all the responsibility of the channel
-code, if it is needed for the particular medium.  That is, the skbuffs
-presented to the start_xmit() function contain only the 2-byte
-protocol number and the data, and the skbuffs presented to ppp_input()
-must be in the same format.
-
-The channel must provide an instance of a ppp_channel struct to
-represent the channel.  The channel is free to use the `private' field
-however it wishes.  The channel should initialize the `mtu' and
-`hdrlen' fields before calling ppp_register_channel() and not change
-them until after ppp_unregister_channel() returns.  The `mtu' field
-represents the maximum size of the data part of the PPP frames, that
-is, it does not include the 2-byte protocol number.
-
-If the channel needs some headroom in the skbuffs presented to it for
-transmission (i.e., some space free in the skbuff data area before the
-start of the PPP frame), it should set the `hdrlen' field of the
-ppp_channel struct to the amount of headroom required.  The generic
-PPP layer will attempt to provide that much headroom but the channel
-should still check if there is sufficient headroom and copy the skbuff
-if there isn't.
-
-On the input side, channels should ideally provide at least 2 bytes of
-headroom in the skbuffs presented to ppp_input().  The generic PPP
-code does not require this but will be more efficient if this is done.
-
-
-Buffering and flow control
---------------------------
-
-The generic PPP layer has been designed to minimize the amount of data
-that it buffers in the transmit direction.  It maintains a queue of
-transmit packets for the PPP unit (network interface device) plus a
-queue of transmit packets for each attached channel.  Normally the
-transmit queue for the unit will contain at most one packet; the
-exceptions are when pppd sends packets by writing to /dev/ppp, and
-when the core networking code calls the generic layer's start_xmit()
-function with the queue stopped, i.e. when the generic layer has
-called netif_stop_queue(), which only happens on a transmit timeout.
-The start_xmit function always accepts and queues the packet which it
-is asked to transmit.
-
-Transmit packets are dequeued from the PPP unit transmit queue and
-then subjected to TCP/IP header compression and packet compression
-(Deflate or BSD-Compress compression), as appropriate.  After this
-point the packets can no longer be reordered, as the decompression
-algorithms rely on receiving compressed packets in the same order that
-they were generated.
-
-If multilink is not in use, this packet is then passed to the attached
-channel's start_xmit() function.  If the channel refuses to take
-the packet, the generic layer saves it for later transmission.  The
-generic layer will call the channel's start_xmit() function again
-when the channel calls  ppp_output_wakeup() or when the core
-networking code calls the generic layer's start_xmit() function
-again.  The generic layer contains no timeout and retransmission
-logic; it relies on the core networking code for that.
-
-If multilink is in use, the generic layer divides the packet into one
-or more fragments and puts a multilink header on each fragment.  It
-decides how many fragments to use based on the length of the packet
-and the number of channels which are potentially able to accept a
-fragment at the moment.  A channel is potentially able to accept a
-fragment if it doesn't have any fragments currently queued up for it
-to transmit.  The channel may still refuse a fragment; in this case
-the fragment is queued up for the channel to transmit later.  This
-scheme has the effect that more fragments are given to higher-
-bandwidth channels.  It also means that under light load, the generic
-layer will tend to fragment large packets across all the channels,
-thus reducing latency, while under heavy load, packets will tend to be
-transmitted as single fragments, thus reducing the overhead of
-fragmentation.
-
-
-SMP safety
-----------
-
-The PPP generic layer has been designed to be SMP-safe.  Locks are
-used around accesses to the internal data structures where necessary
-to ensure their integrity.  As part of this, the generic layer
-requires that the channels adhere to certain requirements and in turn
-provides certain guarantees to the channels.  Essentially the channels
-are required to provide the appropriate locking on the ppp_channel
-structures that form the basis of the communication between the
-channel and the generic layer.  This is because the channel provides
-the storage for the ppp_channel structure, and so the channel is
-required to provide the guarantee that this storage exists and is
-valid at the appropriate times.
-
-The generic layer requires these guarantees from the channel:
-
-* The ppp_channel object must exist from the time that
-  ppp_register_channel() is called until after the call to
-  ppp_unregister_channel() returns.
-
-* No thread may be in a call to any of ppp_input(), ppp_input_error(),
-  ppp_output_wakeup(), ppp_channel_index() or ppp_unit_number() for a
-  channel at the time that ppp_unregister_channel() is called for that
-  channel.
-
-* ppp_register_channel() and ppp_unregister_channel() must be called
-  from process context, not interrupt or softirq/BH context.
-
-* The remaining generic layer functions may be called at softirq/BH
-  level but must not be called from a hardware interrupt handler.
-
-* The generic layer may call the channel start_xmit() function at
-  softirq/BH level but will not call it at interrupt level.  Thus the
-  start_xmit() function may not block.
-
-* The generic layer will only call the channel ioctl() function in
-  process context.
-
-The generic layer provides these guarantees to the channels:
-
-* The generic layer will not call the start_xmit() function for a
-  channel while any thread is already executing in that function for
-  that channel.
-
-* The generic layer will not call the ioctl() function for a channel
-  while any thread is already executing in that function for that
-  channel.
-
-* By the time a call to ppp_unregister_channel() returns, no thread
-  will be executing in a call from the generic layer to that channel's
-  start_xmit() or ioctl() function, and the generic layer will not
-  call either of those functions subsequently.
-
-
-Interface to pppd
------------------
-
-The PPP generic layer exports a character device interface called
-/dev/ppp.  This is used by pppd to control PPP interface units and
-channels.  Although there is only one /dev/ppp, each open instance of
-/dev/ppp acts independently and can be attached either to a PPP unit
-or a PPP channel.  This is achieved using the file->private_data field
-to point to a separate object for each open instance of /dev/ppp.  In
-this way an effect similar to Solaris' clone open is obtained,
-allowing us to control an arbitrary number of PPP interfaces and
-channels without having to fill up /dev with hundreds of device names.
-
-When /dev/ppp is opened, a new instance is created which is initially
-unattached.  Using an ioctl call, it can then be attached to an
-existing unit, attached to a newly-created unit, or attached to an
-existing channel.  An instance attached to a unit can be used to send
-and receive PPP control frames, using the read() and write() system
-calls, along with poll() if necessary.  Similarly, an instance
-attached to a channel can be used to send and receive PPP frames on
-that channel.
-
-In multilink terms, the unit represents the bundle, while the channels
-represent the individual physical links.  Thus, a PPP frame sent by a
-write to the unit (i.e., to an instance of /dev/ppp attached to the
-unit) will be subject to bundle-level compression and to fragmentation
-across the individual links (if multilink is in use).  In contrast, a
-PPP frame sent by a write to the channel will be sent as-is on that
-channel, without any multilink header.
-
-A channel is not initially attached to any unit.  In this state it can
-be used for PPP negotiation but not for the transfer of data packets.
-It can then be connected to a PPP unit with an ioctl call, which
-makes it available to send and receive data packets for that unit.
-
-The ioctl calls which are available on an instance of /dev/ppp depend
-on whether it is unattached, attached to a PPP interface, or attached
-to a PPP channel.  The ioctl calls which are available on an
-unattached instance are:
-
-* PPPIOCNEWUNIT creates a new PPP interface and makes this /dev/ppp
-  instance the "owner" of the interface.  The argument should point to
-  an int which is the desired unit number if >= 0, or -1 to assign the
-  lowest unused unit number.  Being the owner of the interface means
-  that the interface will be shut down if this instance of /dev/ppp is
-  closed.
-
-* PPPIOCATTACH attaches this instance to an existing PPP interface.
-  The argument should point to an int containing the unit number.
-  This does not make this instance the owner of the PPP interface.
-
-* PPPIOCATTCHAN attaches this instance to an existing PPP channel.
-  The argument should point to an int containing the channel number.
-
-The ioctl calls available on an instance of /dev/ppp attached to a
-channel are:
-
-* PPPIOCCONNECT connects this channel to a PPP interface.  The
-  argument should point to an int containing the interface unit
-  number.  It will return an EINVAL error if the channel is already
-  connected to an interface, or ENXIO if the requested interface does
-  not exist.
-
-* PPPIOCDISCONN disconnects this channel from the PPP interface that
-  it is connected to.  It will return an EINVAL error if the channel
-  is not connected to an interface.
-
-* All other ioctl commands are passed to the channel ioctl() function.
-
-The ioctl calls that are available on an instance that is attached to
-an interface unit are:
-
-* PPPIOCSMRU sets the MRU (maximum receive unit) for the interface.
-  The argument should point to an int containing the new MRU value.
-
-* PPPIOCSFLAGS sets flags which control the operation of the
-  interface.  The argument should be a pointer to an int containing
-  the new flags value.  The bits in the flags value that can be set
-  are:
-       SC_COMP_TCP             enable transmit TCP header compression
-       SC_NO_TCP_CCID          disable connection-id compression for
-                               TCP header compression
-       SC_REJ_COMP_TCP         disable receive TCP header decompression
-       SC_CCP_OPEN             Compression Control Protocol (CCP) is
-                               open, so inspect CCP packets
-       SC_CCP_UP               CCP is up, may (de)compress packets
-       SC_LOOP_TRAFFIC         send IP traffic to pppd
-       SC_MULTILINK            enable PPP multilink fragmentation on
-                               transmitted packets
-       SC_MP_SHORTSEQ          expect short multilink sequence
-                               numbers on received multilink fragments
-       SC_MP_XSHORTSEQ         transmit short multilink sequence nos.
-
-  The values of these flags are defined in <linux/ppp-ioctl.h>.  Note
-  that the values of the SC_MULTILINK, SC_MP_SHORTSEQ and
-  SC_MP_XSHORTSEQ bits are ignored if the CONFIG_PPP_MULTILINK option
-  is not selected.
-
-* PPPIOCGFLAGS returns the value of the status/control flags for the
-  interface unit.  The argument should point to an int where the ioctl
-  will store the flags value.  As well as the values listed above for
-  PPPIOCSFLAGS, the following bits may be set in the returned value:
-       SC_COMP_RUN             CCP compressor is running
-       SC_DECOMP_RUN           CCP decompressor is running
-       SC_DC_ERROR             CCP decompressor detected non-fatal error
-       SC_DC_FERROR            CCP decompressor detected fatal error
-
-* PPPIOCSCOMPRESS sets the parameters for packet compression or
-  decompression.  The argument should point to a ppp_option_data
-  structure (defined in <linux/ppp-ioctl.h>), which contains a
-  pointer/length pair which should describe a block of memory
-  containing a CCP option specifying a compression method and its
-  parameters.  The ppp_option_data struct also contains a `transmit'
-  field.  If this is 0, the ioctl will affect the receive path,
-  otherwise the transmit path.
-
-* PPPIOCGUNIT returns, in the int pointed to by the argument, the unit
-  number of this interface unit.
-
-* PPPIOCSDEBUG sets the debug flags for the interface to the value in
-  the int pointed to by the argument.  Only the least significant bit
-  is used; if this is 1 the generic layer will print some debug
-  messages during its operation.  This is only intended for debugging
-  the generic PPP layer code; it is generally not helpful for working
-  out why a PPP connection is failing.
-
-* PPPIOCGDEBUG returns the debug flags for the interface in the int
-  pointed to by the argument.
-
-* PPPIOCGIDLE returns the time, in seconds, since the last data
-  packets were sent and received.  The argument should point to a
-  ppp_idle structure (defined in <linux/ppp_defs.h>).  If the
-  CONFIG_PPP_FILTER option is enabled, the set of packets which reset
-  the transmit and receive idle timers is restricted to those which
-  pass the `active' packet filter.
-  Two versions of this command exist, to deal with user space
-  expecting times as either 32-bit or 64-bit time_t seconds.
-
-* PPPIOCSMAXCID sets the maximum connection-ID parameter (and thus the
-  number of connection slots) for the TCP header compressor and
-  decompressor.  The lower 16 bits of the int pointed to by the
-  argument specify the maximum connection-ID for the compressor.  If
-  the upper 16 bits of that int are non-zero, they specify the maximum
-  connection-ID for the decompressor, otherwise the decompressor's
-  maximum connection-ID is set to 15.
-
-* PPPIOCSNPMODE sets the network-protocol mode for a given network
-  protocol.  The argument should point to an npioctl struct (defined
-  in <linux/ppp-ioctl.h>).  The `protocol' field gives the PPP protocol
-  number for the protocol to be affected, and the `mode' field
-  specifies what to do with packets for that protocol:
-
-       NPMODE_PASS     normal operation, transmit and receive packets
-       NPMODE_DROP     silently drop packets for this protocol
-       NPMODE_ERROR    drop packets and return an error on transmit
-       NPMODE_QUEUE    queue up packets for transmit, drop received
-                       packets
-
-  At present NPMODE_ERROR and NPMODE_QUEUE have the same effect as
-  NPMODE_DROP.
-
-* PPPIOCGNPMODE returns the network-protocol mode for a given
-  protocol.  The argument should point to an npioctl struct with the
-  `protocol' field set to the PPP protocol number for the protocol of
-  interest.  On return the `mode' field will be set to the network-
-  protocol mode for that protocol.
-
-* PPPIOCSPASS and PPPIOCSACTIVE set the `pass' and `active' packet
-  filters.  These ioctls are only available if the CONFIG_PPP_FILTER
-  option is selected.  The argument should point to a sock_fprog
-  structure (defined in <linux/filter.h>) containing the compiled BPF
-  instructions for the filter.  Packets are dropped if they fail the
-  `pass' filter; otherwise, if they fail the `active' filter they are
-  passed but they do not reset the transmit or receive idle timer.
-
-* PPPIOCSMRRU enables or disables multilink processing for received
-  packets and sets the multilink MRRU (maximum reconstructed receive
-  unit).  The argument should point to an int containing the new MRRU
-  value.  If the MRRU value is 0, processing of received multilink
-  fragments is disabled.  This ioctl is only available if the
-  CONFIG_PPP_MULTILINK option is selected.
-
-Last modified: 7-feb-2002
diff --git a/Documentation/networking/proc_net_tcp.rst b/Documentation/networking/proc_net_tcp.rst
new file mode 100644 (file)
index 0000000..7d9dfe3
--- /dev/null
@@ -0,0 +1,57 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================================
+The proc/net/tcp and proc/net/tcp6 variables
+============================================
+
+This document describes the interfaces /proc/net/tcp and /proc/net/tcp6.
+Note that these interfaces are deprecated in favor of tcp_diag.
+
+These /proc interfaces provide information about currently active TCP
+connections, and are implemented by tcp4_seq_show() in net/ipv4/tcp_ipv4.c
+and tcp6_seq_show() in net/ipv6/tcp_ipv6.c, respectively.
+
+It will first list all listening TCP sockets, and next list all established
+TCP connections. A typical entry of /proc/net/tcp would look like this (split
+up into 3 parts because of the length of the line)::
+
+   46: 010310AC:9C4C 030310AC:1770 01
+   |      |      |      |      |   |--> connection state
+   |      |      |      |      |------> remote TCP port number
+   |      |      |      |-------------> remote IPv4 address
+   |      |      |--------------------> local TCP port number
+   |      |---------------------------> local IPv4 address
+   |----------------------------------> number of entry
+
+   00000150:00000000 01:00000019 00000000
+      |        |     |     |       |--> number of unrecovered RTO timeouts
+      |        |     |     |----------> number of jiffies until timer expires
+      |        |     |----------------> timer_active (see below)
+      |        |----------------------> receive-queue
+      |-------------------------------> transmit-queue
+
+   1000        0 54165785 4 cd1e6040 25 4 27 3 -1
+    |          |    |     |    |     |  | |  | |--> slow start size threshold,
+    |          |    |     |    |     |  | |  |      or -1 if the threshold
+    |          |    |     |    |     |  | |  |      is >= 0xFFFF
+    |          |    |     |    |     |  | |  |----> sending congestion window
+    |          |    |     |    |     |  | |-------> (ack.quick<<1)|ack.pingpong
+    |          |    |     |    |     |  |---------> Predicted tick of soft clock
+    |          |    |     |    |     |              (delayed ACK control data)
+    |          |    |     |    |     |------------> retransmit timeout
+    |          |    |     |    |------------------> location of socket in memory
+    |          |    |     |-----------------------> socket reference count
+    |          |    |-----------------------------> inode
+    |          |----------------------------------> unanswered 0-window probes
+    |---------------------------------------------> uid
+
+timer_active:
+
+ ==  ================================================================
+  0  no timer is pending
+  1  retransmit-timer is pending
+  2  another timer (e.g. delayed ack or keepalive) is pending
+  3  this is a socket in TIME_WAIT state. Not all fields will contain
+     data (or even exist)
+  4  zero window probe timer is pending
+ ==  ================================================================
diff --git a/Documentation/networking/proc_net_tcp.txt b/Documentation/networking/proc_net_tcp.txt
deleted file mode 100644 (file)
index 4a79209..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-This document describes the interfaces /proc/net/tcp and /proc/net/tcp6.
-Note that these interfaces are deprecated in favor of tcp_diag.
-
-These /proc interfaces provide information about currently active TCP 
-connections, and are implemented by tcp4_seq_show() in net/ipv4/tcp_ipv4.c
-and tcp6_seq_show() in net/ipv6/tcp_ipv6.c, respectively.
-
-It will first list all listening TCP sockets, and next list all established
-TCP connections. A typical entry of /proc/net/tcp would look like this (split 
-up into 3 parts because of the length of the line):
-
-   46: 010310AC:9C4C 030310AC:1770 01 
-   |      |      |      |      |   |--> connection state
-   |      |      |      |      |------> remote TCP port number
-   |      |      |      |-------------> remote IPv4 address
-   |      |      |--------------------> local TCP port number
-   |      |---------------------------> local IPv4 address
-   |----------------------------------> number of entry
-
-   00000150:00000000 01:00000019 00000000  
-      |        |     |     |       |--> number of unrecovered RTO timeouts
-      |        |     |     |----------> number of jiffies until timer expires
-      |        |     |----------------> timer_active (see below)
-      |        |----------------------> receive-queue
-      |-------------------------------> transmit-queue
-
-   1000        0 54165785 4 cd1e6040 25 4 27 3 -1
-    |          |    |     |    |     |  | |  | |--> slow start size threshold, 
-    |          |    |     |    |     |  | |  |      or -1 if the threshold
-    |          |    |     |    |     |  | |  |      is >= 0xFFFF
-    |          |    |     |    |     |  | |  |----> sending congestion window
-    |          |    |     |    |     |  | |-------> (ack.quick<<1)|ack.pingpong
-    |          |    |     |    |     |  |---------> Predicted tick of soft clock
-    |          |    |     |    |     |              (delayed ACK control data)
-    |          |    |     |    |     |------------> retransmit timeout
-    |          |    |     |    |------------------> location of socket in memory
-    |          |    |     |-----------------------> socket reference count
-    |          |    |-----------------------------> inode
-    |          |----------------------------------> unanswered 0-window probes
-    |---------------------------------------------> uid
-
-timer_active:
-  0  no timer is pending
-  1  retransmit-timer is pending
-  2  another timer (e.g. delayed ack or keepalive) is pending
-  3  this is a socket in TIME_WAIT state. Not all fields will contain 
-     data (or even exist)
-  4  zero window probe timer is pending
diff --git a/Documentation/networking/radiotap-headers.rst b/Documentation/networking/radiotap-headers.rst
new file mode 100644 (file)
index 0000000..1a1bd1e
--- /dev/null
@@ -0,0 +1,159 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================
+How to use radiotap headers
+===========================
+
+Pointer to the radiotap include file
+------------------------------------
+
+Radiotap headers are variable-length and extensible, you can get most of the
+information you need to know on them from::
+
+    ./include/net/ieee80211_radiotap.h
+
+This document gives an overview and warns on some corner cases.
+
+
+Structure of the header
+-----------------------
+
+There is a fixed portion at the start which contains a u32 bitmap that defines
+if the possible argument associated with that bit is present or not.  So if b0
+of the it_present member of ieee80211_radiotap_header is set, it means that
+the header for argument index 0 (IEEE80211_RADIOTAP_TSFT) is present in the
+argument area.
+
+::
+
+   < 8-byte ieee80211_radiotap_header >
+   [ <possible argument bitmap extensions ... > ]
+   [ <argument> ... ]
+
+At the moment there are only 13 possible argument indexes defined, but in case
+we run out of space in the u32 it_present member, it is defined that b31 set
+indicates that there is another u32 bitmap following (shown as "possible
+argument bitmap extensions..." above), and the start of the arguments is moved
+forward 4 bytes each time.
+
+Note also that the it_len member __le16 is set to the total number of bytes
+covered by the ieee80211_radiotap_header and any arguments following.
+
+
+Requirements for arguments
+--------------------------
+
+After the fixed part of the header, the arguments follow for each argument
+index whose matching bit is set in the it_present member of
+ieee80211_radiotap_header.
+
+ - the arguments are all stored little-endian!
+
+ - the argument payload for a given argument index has a fixed size.  So
+   IEEE80211_RADIOTAP_TSFT being present always indicates an 8-byte argument is
+   present.  See the comments in ./include/net/ieee80211_radiotap.h for a nice
+   breakdown of all the argument sizes
+
+ - the arguments must be aligned to a boundary of the argument size using
+   padding.  So a u16 argument must start on the next u16 boundary if it isn't
+   already on one, a u32 must start on the next u32 boundary and so on.
+
+ - "alignment" is relative to the start of the ieee80211_radiotap_header, ie,
+   the first byte of the radiotap header.  The absolute alignment of that first
+   byte isn't defined.  So even if the whole radiotap header is starting at, eg,
+   address 0x00000003, still the first byte of the radiotap header is treated as
+   0 for alignment purposes.
+
+ - the above point that there may be no absolute alignment for multibyte
+   entities in the fixed radiotap header or the argument region means that you
+   have to take special evasive action when trying to access these multibyte
+   entities.  Some arches like Blackfin cannot deal with an attempt to
+   dereference, eg, a u16 pointer that is pointing to an odd address.  Instead
+   you have to use a kernel API get_unaligned() to dereference the pointer,
+   which will do it bytewise on the arches that require that.
+
+ - The arguments for a given argument index can be a compound of multiple types
+   together.  For example IEEE80211_RADIOTAP_CHANNEL has an argument payload
+   consisting of two u16s of total length 4.  When this happens, the padding
+   rule is applied dealing with a u16, NOT dealing with a 4-byte single entity.
+
+
+Example valid radiotap header
+-----------------------------
+
+::
+
+       0x00, 0x00, // <-- radiotap version + pad byte
+       0x0b, 0x00, // <- radiotap header length
+       0x04, 0x0c, 0x00, 0x00, // <-- bitmap
+       0x6c, // <-- rate (in 500kHz units)
+       0x0c, //<-- tx power
+       0x01 //<-- antenna
+
+
+Using the Radiotap Parser
+-------------------------
+
+If you are having to parse a radiotap struct, you can radically simplify the
+job by using the radiotap parser that lives in net/wireless/radiotap.c and has
+its prototypes available in include/net/cfg80211.h.  You use it like this::
+
+    #include <net/cfg80211.h>
+
+    /* buf points to the start of the radiotap header part */
+
+    int MyFunction(u8 * buf, int buflen)
+    {
+           int pkt_rate_100kHz = 0, antenna = 0, pwr = 0;
+           struct ieee80211_radiotap_iterator iterator;
+           int ret = ieee80211_radiotap_iterator_init(&iterator, buf, buflen);
+
+           while (!ret) {
+
+                   ret = ieee80211_radiotap_iterator_next(&iterator);
+
+                   if (ret)
+                           continue;
+
+                   /* see if this argument is something we can use */
+
+                   switch (iterator.this_arg_index) {
+                   /*
+                   * You must take care when dereferencing iterator.this_arg
+                   * for multibyte types... the pointer is not aligned.  Use
+                   * get_unaligned((type *)iterator.this_arg) to dereference
+                   * iterator.this_arg for type "type" safely on all arches.
+                   */
+                   case IEEE80211_RADIOTAP_RATE:
+                           /* radiotap "rate" u8 is in
+                           * 500kbps units, eg, 0x02=1Mbps
+                           */
+                           pkt_rate_100kHz = (*iterator.this_arg) * 5;
+                           break;
+
+                   case IEEE80211_RADIOTAP_ANTENNA:
+                           /* radiotap uses 0 for 1st ant */
+                           antenna = *iterator.this_arg);
+                           break;
+
+                   case IEEE80211_RADIOTAP_DBM_TX_POWER:
+                           pwr = *iterator.this_arg;
+                           break;
+
+                   default:
+                           break;
+                   }
+           }  /* while more rt headers */
+
+           if (ret != -ENOENT)
+                   return TXRX_DROP;
+
+           /* discard the radiotap header part */
+           buf += iterator.max_length;
+           buflen -= iterator.max_length;
+
+           ...
+
+    }
+
+Andy Green <andy@warmcat.com>
diff --git a/Documentation/networking/radiotap-headers.txt b/Documentation/networking/radiotap-headers.txt
deleted file mode 100644 (file)
index 953331c..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-How to use radiotap headers
-===========================
-
-Pointer to the radiotap include file
-------------------------------------
-
-Radiotap headers are variable-length and extensible, you can get most of the
-information you need to know on them from:
-
-./include/net/ieee80211_radiotap.h
-
-This document gives an overview and warns on some corner cases.
-
-
-Structure of the header
------------------------
-
-There is a fixed portion at the start which contains a u32 bitmap that defines
-if the possible argument associated with that bit is present or not.  So if b0
-of the it_present member of ieee80211_radiotap_header is set, it means that
-the header for argument index 0 (IEEE80211_RADIOTAP_TSFT) is present in the
-argument area.
-
-   < 8-byte ieee80211_radiotap_header >
-   [ <possible argument bitmap extensions ... > ]
-   [ <argument> ... ]
-
-At the moment there are only 13 possible argument indexes defined, but in case
-we run out of space in the u32 it_present member, it is defined that b31 set
-indicates that there is another u32 bitmap following (shown as "possible
-argument bitmap extensions..." above), and the start of the arguments is moved
-forward 4 bytes each time.
-
-Note also that the it_len member __le16 is set to the total number of bytes
-covered by the ieee80211_radiotap_header and any arguments following.
-
-
-Requirements for arguments
---------------------------
-
-After the fixed part of the header, the arguments follow for each argument
-index whose matching bit is set in the it_present member of
-ieee80211_radiotap_header.
-
- - the arguments are all stored little-endian!
-
- - the argument payload for a given argument index has a fixed size.  So
-   IEEE80211_RADIOTAP_TSFT being present always indicates an 8-byte argument is
-   present.  See the comments in ./include/net/ieee80211_radiotap.h for a nice
-   breakdown of all the argument sizes
-
- - the arguments must be aligned to a boundary of the argument size using
-   padding.  So a u16 argument must start on the next u16 boundary if it isn't
-   already on one, a u32 must start on the next u32 boundary and so on.
-
- - "alignment" is relative to the start of the ieee80211_radiotap_header, ie,
-   the first byte of the radiotap header.  The absolute alignment of that first
-   byte isn't defined.  So even if the whole radiotap header is starting at, eg,
-   address 0x00000003, still the first byte of the radiotap header is treated as
-   0 for alignment purposes.
-
- - the above point that there may be no absolute alignment for multibyte
-   entities in the fixed radiotap header or the argument region means that you
-   have to take special evasive action when trying to access these multibyte
-   entities.  Some arches like Blackfin cannot deal with an attempt to
-   dereference, eg, a u16 pointer that is pointing to an odd address.  Instead
-   you have to use a kernel API get_unaligned() to dereference the pointer,
-   which will do it bytewise on the arches that require that.
-
- - The arguments for a given argument index can be a compound of multiple types
-   together.  For example IEEE80211_RADIOTAP_CHANNEL has an argument payload
-   consisting of two u16s of total length 4.  When this happens, the padding
-   rule is applied dealing with a u16, NOT dealing with a 4-byte single entity.
-
-
-Example valid radiotap header
------------------------------
-
-       0x00, 0x00, // <-- radiotap version + pad byte
-       0x0b, 0x00, // <- radiotap header length
-       0x04, 0x0c, 0x00, 0x00, // <-- bitmap
-       0x6c, // <-- rate (in 500kHz units)
-       0x0c, //<-- tx power
-       0x01 //<-- antenna
-
-
-Using the Radiotap Parser
--------------------------
-
-If you are having to parse a radiotap struct, you can radically simplify the
-job by using the radiotap parser that lives in net/wireless/radiotap.c and has
-its prototypes available in include/net/cfg80211.h.  You use it like this:
-
-#include <net/cfg80211.h>
-
-/* buf points to the start of the radiotap header part */
-
-int MyFunction(u8 * buf, int buflen)
-{
-       int pkt_rate_100kHz = 0, antenna = 0, pwr = 0;
-       struct ieee80211_radiotap_iterator iterator;
-       int ret = ieee80211_radiotap_iterator_init(&iterator, buf, buflen);
-
-       while (!ret) {
-
-               ret = ieee80211_radiotap_iterator_next(&iterator);
-
-               if (ret)
-                       continue;
-
-               /* see if this argument is something we can use */
-
-               switch (iterator.this_arg_index) {
-               /*
-                * You must take care when dereferencing iterator.this_arg
-                * for multibyte types... the pointer is not aligned.  Use
-                * get_unaligned((type *)iterator.this_arg) to dereference
-                * iterator.this_arg for type "type" safely on all arches.
-                */
-               case IEEE80211_RADIOTAP_RATE:
-                       /* radiotap "rate" u8 is in
-                        * 500kbps units, eg, 0x02=1Mbps
-                        */
-                       pkt_rate_100kHz = (*iterator.this_arg) * 5;
-                       break;
-
-               case IEEE80211_RADIOTAP_ANTENNA:
-                       /* radiotap uses 0 for 1st ant */
-                       antenna = *iterator.this_arg);
-                       break;
-
-               case IEEE80211_RADIOTAP_DBM_TX_POWER:
-                       pwr = *iterator.this_arg;
-                       break;
-
-               default:
-                       break;
-               }
-       }  /* while more rt headers */
-
-       if (ret != -ENOENT)
-               return TXRX_DROP;
-
-       /* discard the radiotap header part */
-       buf += iterator.max_length;
-       buflen -= iterator.max_length;
-
-       ...
-
-}
-
-Andy Green <andy@warmcat.com>
diff --git a/Documentation/networking/ray_cs.rst b/Documentation/networking/ray_cs.rst
new file mode 100644 (file)
index 0000000..9a46d1a
--- /dev/null
@@ -0,0 +1,165 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: <isonum.txt>
+
+=========================
+Raylink wireless LAN card
+=========================
+
+September 21, 1999
+
+Copyright |copy| 1998  Corey Thomas (corey@world.std.com)
+
+This file is the documentation for the Raylink Wireless LAN card driver for
+Linux.  The Raylink wireless LAN card is a PCMCIA card which provides IEEE
+802.11 compatible wireless network connectivity at 1 and 2 megabits/second.
+See http://www.raytheon.com/micro/raylink/ for more information on the Raylink
+card.  This driver is in early development and does have bugs.  See the known
+bugs and limitations at the end of this document for more information.
+This driver also works with WebGear's Aviator 2.4 and Aviator Pro
+wireless LAN cards.
+
+As of kernel 2.3.18, the ray_cs driver is part of the Linux kernel
+source.  My web page for the development of ray_cs is at
+http://web.ralinktech.com/ralink/Home/Support/Linux.html
+and I can be emailed at corey@world.std.com
+
+The kernel driver is based on ray_cs-1.62.tgz
+
+The driver at my web page is intended to be used as an add on to
+David Hinds pcmcia package.  All the command line parameters are
+available when compiled as a module.  When built into the kernel, only
+the essid= string parameter is available via the kernel command line.
+This will change after the method of sorting out parameters for all
+the PCMCIA drivers is agreed upon.  If you must have a built in driver
+with nondefault parameters, they can be edited in
+/usr/src/linux/drivers/net/pcmcia/ray_cs.c.  Searching for module_param
+will find them all.
+
+Information on card services is available at:
+
+       http://pcmcia-cs.sourceforge.net/
+
+
+Card services user programs are still required for PCMCIA devices.
+pcmcia-cs-3.1.1 or greater is required for the kernel version of
+the driver.
+
+Currently, ray_cs is not part of David Hinds card services package,
+so the following magic is required.
+
+At the end of the /etc/pcmcia/config.opts file, add the line:
+source ./ray_cs.opts
+This will make card services read the ray_cs.opts file
+when starting.  Create the file /etc/pcmcia/ray_cs.opts containing the
+following::
+
+  #### start of /etc/pcmcia/ray_cs.opts ###################
+  # Configuration options for Raylink Wireless LAN PCMCIA card
+  device "ray_cs"
+    class "network" module "misc/ray_cs"
+
+  card "RayLink PC Card WLAN Adapter"
+    manfid 0x01a6, 0x0000
+    bind "ray_cs"
+
+  module "misc/ray_cs" opts ""
+  #### end of /etc/pcmcia/ray_cs.opts #####################
+
+
+To join an existing network with
+different parameters, contact the network administrator for the
+configuration information, and edit /etc/pcmcia/ray_cs.opts.
+Add the parameters below between the empty quotes.
+
+Parameters for ray_cs driver which may be specified in ray_cs.opts:
+
+=============== =============== =============================================
+bc              integer         0 = normal mode (802.11 timing),
+                               1 = slow down inter frame timing to allow
+                               operation with older breezecom access
+                               points.
+
+beacon_period  integer         beacon period in Kilo-microseconds,
+
+                               legal values = must be integer multiple
+                               of hop dwell
+
+                               default = 256
+
+country         integer         1 = USA (default),
+                               2 = Europe,
+                               3 = Japan,
+                               4 = Korea,
+                               5 = Spain,
+                               6 = France,
+                               7 = Israel,
+                               8 = Australia
+
+essid          string          ESS ID - network name to join
+
+                               string with maximum length of 32 chars
+                               default value = "ADHOC_ESSID"
+
+hop_dwell      integer         hop dwell time in Kilo-microseconds
+
+                               legal values = 16,32,64,128(default),256
+
+irq_mask       integer         linux standard 16 bit value 1bit/IRQ
+
+                               lsb is IRQ 0, bit 1 is IRQ 1 etc.
+                               Used to restrict choice of IRQ's to use.
+                               Recommended method for controlling
+                               interrupts is in /etc/pcmcia/config.opts
+
+net_type       integer         0 (default) = adhoc network,
+                               1 = infrastructure
+
+phy_addr       string          string containing new MAC address in
+                               hex, must start with x eg
+                               x00008f123456
+
+psm            integer         0 = continuously active,
+                               1 = power save mode (not useful yet)
+
+pc_debug       integer         (0-5) larger values for more verbose
+                               logging.  Replaces ray_debug.
+
+ray_debug      integer         Replaced with pc_debug
+
+ray_mem_speed   integer         defaults to 500
+
+sniffer         integer         0 = not sniffer (default),
+                               1 = sniffer which can be used to record all
+                               network traffic using tcpdump or similar,
+                               but no normal network use is allowed.
+
+translate      integer         0 = no translation (encapsulate frames),
+                               1 = translation    (RFC1042/802.1)
+=============== =============== =============================================
+
+More on sniffer mode:
+
+tcpdump does not understand 802.11 headers, so it can't
+interpret the contents, but it can record to a file.  This is only
+useful for debugging 802.11 lowlevel protocols that are not visible to
+linux.  If you want to watch ftp xfers, or do similar things, you
+don't need to use sniffer mode.  Also, some packet types are never
+sent up by the card, so you will never see them (ack, rts, cts, probe
+etc.)  There is a simple program (showcap) included in the ray_cs
+package which parses the 802.11 headers.
+
+Known Problems and missing features
+
+       Does not work with non x86
+
+       Does not work with SMP
+
+       Support for defragmenting frames is not yet debugged, and in
+       fact is known to not work.  I have never encountered a net set
+       up to fragment, but still, it should be fixed.
+
+       The ioctl support is incomplete.  The hardware address cannot be set
+       using ifconfig yet.  If a different hardware address is needed, it may
+       be set using the phy_addr parameter in ray_cs.opts.  This requires
+       a card insertion to take effect.
diff --git a/Documentation/networking/ray_cs.txt b/Documentation/networking/ray_cs.txt
deleted file mode 100644 (file)
index c0c1230..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-September 21, 1999
-
-Copyright (c) 1998  Corey Thomas (corey@world.std.com)
-
-This file is the documentation for the Raylink Wireless LAN card driver for
-Linux.  The Raylink wireless LAN card is a PCMCIA card which provides IEEE
-802.11 compatible wireless network connectivity at 1 and 2 megabits/second.
-See http://www.raytheon.com/micro/raylink/ for more information on the Raylink
-card.  This driver is in early development and does have bugs.  See the known
-bugs and limitations at the end of this document for more information.
-This driver also works with WebGear's Aviator 2.4 and Aviator Pro
-wireless LAN cards.
-
-As of kernel 2.3.18, the ray_cs driver is part of the Linux kernel
-source.  My web page for the development of ray_cs is at
-http://web.ralinktech.com/ralink/Home/Support/Linux.html 
-and I can be emailed at corey@world.std.com
-
-The kernel driver is based on ray_cs-1.62.tgz
-
-The driver at my web page is intended to be used as an add on to
-David Hinds pcmcia package.  All the command line parameters are
-available when compiled as a module.  When built into the kernel, only
-the essid= string parameter is available via the kernel command line.
-This will change after the method of sorting out parameters for all
-the PCMCIA drivers is agreed upon.  If you must have a built in driver
-with nondefault parameters, they can be edited in
-/usr/src/linux/drivers/net/pcmcia/ray_cs.c.  Searching for module_param
-will find them all.
-
-Information on card services is available at:
-       http://pcmcia-cs.sourceforge.net/
-
-
-Card services user programs are still required for PCMCIA devices.
-pcmcia-cs-3.1.1 or greater is required for the kernel version of
-the driver.
-
-Currently, ray_cs is not part of David Hinds card services package,
-so the following magic is required.
-
-At the end of the /etc/pcmcia/config.opts file, add the line: 
-source ./ray_cs.opts 
-This will make card services read the ray_cs.opts file
-when starting.  Create the file /etc/pcmcia/ray_cs.opts containing the
-following:
-
-#### start of /etc/pcmcia/ray_cs.opts ###################
-# Configuration options for Raylink Wireless LAN PCMCIA card
-device "ray_cs"
-  class "network" module "misc/ray_cs"
-
-card "RayLink PC Card WLAN Adapter"
-  manfid 0x01a6, 0x0000
-  bind "ray_cs"
-
-module "misc/ray_cs" opts ""
-#### end of /etc/pcmcia/ray_cs.opts #####################
-
-
-To join an existing network with
-different parameters, contact the network administrator for the 
-configuration information, and edit /etc/pcmcia/ray_cs.opts.
-Add the parameters below between the empty quotes.
-
-Parameters for ray_cs driver which may be specified in ray_cs.opts:
-
-bc              integer         0 = normal mode (802.11 timing)
-                                1 = slow down inter frame timing to allow
-                                    operation with older breezecom access
-                                    points.
-
-beacon_period  integer         beacon period in Kilo-microseconds
-                               legal values = must be integer multiple 
-                                               of hop dwell
-                                default = 256
-
-country         integer         1 = USA (default)
-                                2 = Europe
-                                3 = Japan
-                                4 = Korea
-                                5 = Spain
-                                6 = France
-                                7 = Israel
-                                8 = Australia
-
-essid          string          ESS ID - network name to join
-                               string with maximum length of 32 chars
-                               default value = "ADHOC_ESSID"
-
-hop_dwell      integer         hop dwell time in Kilo-microseconds 
-                               legal values = 16,32,64,128(default),256
-
-irq_mask       integer         linux standard 16 bit value 1bit/IRQ
-                               lsb is IRQ 0, bit 1 is IRQ 1 etc.
-                               Used to restrict choice of IRQ's to use.
-                                Recommended method for controlling
-                                interrupts is in /etc/pcmcia/config.opts
-
-net_type       integer         0 (default) = adhoc network, 
-                               1 = infrastructure
-
-phy_addr       string          string containing new MAC address in
-                               hex, must start with x eg
-                               x00008f123456
-
-psm            integer         0 = continuously active
-                               1 = power save mode (not useful yet)
-
-pc_debug       integer         (0-5) larger values for more verbose
-                               logging.  Replaces ray_debug.
-
-ray_debug      integer         Replaced with pc_debug
-
-ray_mem_speed   integer         defaults to 500
-
-sniffer         integer         0 = not sniffer (default)
-                                1 = sniffer which can be used to record all
-                                    network traffic using tcpdump or similar, 
-                                    but no normal network use is allowed.
-
-translate      integer         0 = no translation (encapsulate frames)
-                               1 = translation    (RFC1042/802.1)
-
-
-More on sniffer mode:
-
-tcpdump does not understand 802.11 headers, so it can't
-interpret the contents, but it can record to a file.  This is only
-useful for debugging 802.11 lowlevel protocols that are not visible to
-linux.  If you want to watch ftp xfers, or do similar things, you
-don't need to use sniffer mode.  Also, some packet types are never
-sent up by the card, so you will never see them (ack, rts, cts, probe
-etc.)  There is a simple program (showcap) included in the ray_cs
-package which parses the 802.11 headers.
-
-Known Problems and missing features
-
-        Does not work with non x86
-
-       Does not work with SMP
-
-       Support for defragmenting frames is not yet debugged, and in
-       fact is known to not work.  I have never encountered a net set
-       up to fragment, but still, it should be fixed.
-
-       The ioctl support is incomplete.  The hardware address cannot be set
-       using ifconfig yet.  If a different hardware address is needed, it may
-       be set using the phy_addr parameter in ray_cs.opts.  This requires
-       a card insertion to take effect.
diff --git a/Documentation/networking/rds.rst b/Documentation/networking/rds.rst
new file mode 100644 (file)
index 0000000..44936c2
--- /dev/null
@@ -0,0 +1,448 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==
+RDS
+===
+
+Overview
+========
+
+This readme tries to provide some background on the hows and whys of RDS,
+and will hopefully help you find your way around the code.
+
+In addition, please see this email about RDS origins:
+http://oss.oracle.com/pipermail/rds-devel/2007-November/000228.html
+
+RDS Architecture
+================
+
+RDS provides reliable, ordered datagram delivery by using a single
+reliable connection between any two nodes in the cluster. This allows
+applications to use a single socket to talk to any other process in the
+cluster - so in a cluster with N processes you need N sockets, in contrast
+to N*N if you use a connection-oriented socket transport like TCP.
+
+RDS is not Infiniband-specific; it was designed to support different
+transports.  The current implementation used to support RDS over TCP as well
+as IB.
+
+The high-level semantics of RDS from the application's point of view are
+
+ *     Addressing
+
+       RDS uses IPv4 addresses and 16bit port numbers to identify
+       the end point of a connection. All socket operations that involve
+       passing addresses between kernel and user space generally
+       use a struct sockaddr_in.
+
+       The fact that IPv4 addresses are used does not mean the underlying
+       transport has to be IP-based. In fact, RDS over IB uses a
+       reliable IB connection; the IP address is used exclusively to
+       locate the remote node's GID (by ARPing for the given IP).
+
+       The port space is entirely independent of UDP, TCP or any other
+       protocol.
+
+ *     Socket interface
+
+       RDS sockets work *mostly* as you would expect from a BSD
+       socket. The next section will cover the details. At any rate,
+       all I/O is performed through the standard BSD socket API.
+       Some additions like zerocopy support are implemented through
+       control messages, while other extensions use the getsockopt/
+       setsockopt calls.
+
+       Sockets must be bound before you can send or receive data.
+       This is needed because binding also selects a transport and
+       attaches it to the socket. Once bound, the transport assignment
+       does not change. RDS will tolerate IPs moving around (eg in
+       a active-active HA scenario), but only as long as the address
+       doesn't move to a different transport.
+
+ *     sysctls
+
+       RDS supports a number of sysctls in /proc/sys/net/rds
+
+
+Socket Interface
+================
+
+  AF_RDS, PF_RDS, SOL_RDS
+       AF_RDS and PF_RDS are the domain type to be used with socket(2)
+       to create RDS sockets. SOL_RDS is the socket-level to be used
+       with setsockopt(2) and getsockopt(2) for RDS specific socket
+       options.
+
+  fd = socket(PF_RDS, SOCK_SEQPACKET, 0);
+       This creates a new, unbound RDS socket.
+
+  setsockopt(SOL_SOCKET): send and receive buffer size
+       RDS honors the send and receive buffer size socket options.
+       You are not allowed to queue more than SO_SNDSIZE bytes to
+       a socket. A message is queued when sendmsg is called, and
+       it leaves the queue when the remote system acknowledges
+       its arrival.
+
+       The SO_RCVSIZE option controls the maximum receive queue length.
+       This is a soft limit rather than a hard limit - RDS will
+       continue to accept and queue incoming messages, even if that
+       takes the queue length over the limit. However, it will also
+       mark the port as "congested" and send a congestion update to
+       the source node. The source node is supposed to throttle any
+       processes sending to this congested port.
+
+  bind(fd, &sockaddr_in, ...)
+       This binds the socket to a local IP address and port, and a
+       transport, if one has not already been selected via the
+       SO_RDS_TRANSPORT socket option
+
+  sendmsg(fd, ...)
+       Sends a message to the indicated recipient. The kernel will
+       transparently establish the underlying reliable connection
+       if it isn't up yet.
+
+       An attempt to send a message that exceeds SO_SNDSIZE will
+       return with -EMSGSIZE
+
+       An attempt to send a message that would take the total number
+       of queued bytes over the SO_SNDSIZE threshold will return
+       EAGAIN.
+
+       An attempt to send a message to a destination that is marked
+       as "congested" will return ENOBUFS.
+
+  recvmsg(fd, ...)
+       Receives a message that was queued to this socket. The sockets
+       recv queue accounting is adjusted, and if the queue length
+       drops below SO_SNDSIZE, the port is marked uncongested, and
+       a congestion update is sent to all peers.
+
+       Applications can ask the RDS kernel module to receive
+       notifications via control messages (for instance, there is a
+       notification when a congestion update arrived, or when a RDMA
+       operation completes). These notifications are received through
+       the msg.msg_control buffer of struct msghdr. The format of the
+       messages is described in manpages.
+
+  poll(fd)
+       RDS supports the poll interface to allow the application
+       to implement async I/O.
+
+       POLLIN handling is pretty straightforward. When there's an
+       incoming message queued to the socket, or a pending notification,
+       we signal POLLIN.
+
+       POLLOUT is a little harder. Since you can essentially send
+       to any destination, RDS will always signal POLLOUT as long as
+       there's room on the send queue (ie the number of bytes queued
+       is less than the sendbuf size).
+
+       However, the kernel will refuse to accept messages to
+       a destination marked congested - in this case you will loop
+       forever if you rely on poll to tell you what to do.
+       This isn't a trivial problem, but applications can deal with
+       this - by using congestion notifications, and by checking for
+       ENOBUFS errors returned by sendmsg.
+
+  setsockopt(SOL_RDS, RDS_CANCEL_SENT_TO, &sockaddr_in)
+       This allows the application to discard all messages queued to a
+       specific destination on this particular socket.
+
+       This allows the application to cancel outstanding messages if
+       it detects a timeout. For instance, if it tried to send a message,
+       and the remote host is unreachable, RDS will keep trying forever.
+       The application may decide it's not worth it, and cancel the
+       operation. In this case, it would use RDS_CANCEL_SENT_TO to
+       nuke any pending messages.
+
+  ``setsockopt(fd, SOL_RDS, SO_RDS_TRANSPORT, (int *)&transport ..), getsockopt(fd, SOL_RDS, SO_RDS_TRANSPORT, (int *)&transport ..)``
+       Set or read an integer defining  the underlying
+       encapsulating transport to be used for RDS packets on the
+       socket. When setting the option, integer argument may be
+       one of RDS_TRANS_TCP or RDS_TRANS_IB. When retrieving the
+       value, RDS_TRANS_NONE will be returned on an unbound socket.
+       This socket option may only be set exactly once on the socket,
+       prior to binding it via the bind(2) system call. Attempts to
+       set SO_RDS_TRANSPORT on a socket for which the transport has
+       been previously attached explicitly (by SO_RDS_TRANSPORT) or
+       implicitly (via bind(2)) will return an error of EOPNOTSUPP.
+       An attempt to set SO_RDS_TRANSPORT to RDS_TRANS_NONE will
+       always return EINVAL.
+
+RDMA for RDS
+============
+
+  see rds-rdma(7) manpage (available in rds-tools)
+
+
+Congestion Notifications
+========================
+
+  see rds(7) manpage
+
+
+RDS Protocol
+============
+
+  Message header
+
+    The message header is a 'struct rds_header' (see rds.h):
+
+    Fields:
+
+      h_sequence:
+         per-packet sequence number
+      h_ack:
+         piggybacked acknowledgment of last packet received
+      h_len:
+         length of data, not including header
+      h_sport:
+         source port
+      h_dport:
+         destination port
+      h_flags:
+         Can be:
+
+         =============  ==================================
+         CONG_BITMAP    this is a congestion update bitmap
+         ACK_REQUIRED   receiver must ack this packet
+         RETRANSMITTED  packet has previously been sent
+         =============  ==================================
+
+      h_credit:
+         indicate to other end of connection that
+         it has more credits available (i.e. there is
+         more send room)
+      h_padding[4]:
+         unused, for future use
+      h_csum:
+         header checksum
+      h_exthdr:
+         optional data can be passed here. This is currently used for
+         passing RDMA-related information.
+
+  ACK and retransmit handling
+
+      One might think that with reliable IB connections you wouldn't need
+      to ack messages that have been received.  The problem is that IB
+      hardware generates an ack message before it has DMAed the message
+      into memory.  This creates a potential message loss if the HCA is
+      disabled for any reason between when it sends the ack and before
+      the message is DMAed and processed.  This is only a potential issue
+      if another HCA is available for fail-over.
+
+      Sending an ack immediately would allow the sender to free the sent
+      message from their send queue quickly, but could cause excessive
+      traffic to be used for acks. RDS piggybacks acks on sent data
+      packets.  Ack-only packets are reduced by only allowing one to be
+      in flight at a time, and by the sender only asking for acks when
+      its send buffers start to fill up. All retransmissions are also
+      acked.
+
+  Flow Control
+
+      RDS's IB transport uses a credit-based mechanism to verify that
+      there is space in the peer's receive buffers for more data. This
+      eliminates the need for hardware retries on the connection.
+
+  Congestion
+
+      Messages waiting in the receive queue on the receiving socket
+      are accounted against the sockets SO_RCVBUF option value.  Only
+      the payload bytes in the message are accounted for.  If the
+      number of bytes queued equals or exceeds rcvbuf then the socket
+      is congested.  All sends attempted to this socket's address
+      should return block or return -EWOULDBLOCK.
+
+      Applications are expected to be reasonably tuned such that this
+      situation very rarely occurs.  An application encountering this
+      "back-pressure" is considered a bug.
+
+      This is implemented by having each node maintain bitmaps which
+      indicate which ports on bound addresses are congested.  As the
+      bitmap changes it is sent through all the connections which
+      terminate in the local address of the bitmap which changed.
+
+      The bitmaps are allocated as connections are brought up.  This
+      avoids allocation in the interrupt handling path which queues
+      sages on sockets.  The dense bitmaps let transports send the
+      entire bitmap on any bitmap change reasonably efficiently.  This
+      is much easier to implement than some finer-grained
+      communication of per-port congestion.  The sender does a very
+      inexpensive bit test to test if the port it's about to send to
+      is congested or not.
+
+
+RDS Transport Layer
+===================
+
+  As mentioned above, RDS is not IB-specific. Its code is divided
+  into a general RDS layer and a transport layer.
+
+  The general layer handles the socket API, congestion handling,
+  loopback, stats, usermem pinning, and the connection state machine.
+
+  The transport layer handles the details of the transport. The IB
+  transport, for example, handles all the queue pairs, work requests,
+  CM event handlers, and other Infiniband details.
+
+
+RDS Kernel Structures
+=====================
+
+  struct rds_message
+    aka possibly "rds_outgoing", the generic RDS layer copies data to
+    be sent and sets header fields as needed, based on the socket API.
+    This is then queued for the individual connection and sent by the
+    connection's transport.
+
+  struct rds_incoming
+    a generic struct referring to incoming data that can be handed from
+    the transport to the general code and queued by the general code
+    while the socket is awoken. It is then passed back to the transport
+    code to handle the actual copy-to-user.
+
+  struct rds_socket
+    per-socket information
+
+  struct rds_connection
+    per-connection information
+
+  struct rds_transport
+    pointers to transport-specific functions
+
+  struct rds_statistics
+    non-transport-specific statistics
+
+  struct rds_cong_map
+    wraps the raw congestion bitmap, contains rbnode, waitq, etc.
+
+Connection management
+=====================
+
+  Connections may be in UP, DOWN, CONNECTING, DISCONNECTING, and
+  ERROR states.
+
+  The first time an attempt is made by an RDS socket to send data to
+  a node, a connection is allocated and connected. That connection is
+  then maintained forever -- if there are transport errors, the
+  connection will be dropped and re-established.
+
+  Dropping a connection while packets are queued will cause queued or
+  partially-sent datagrams to be retransmitted when the connection is
+  re-established.
+
+
+The send path
+=============
+
+  rds_sendmsg()
+    - struct rds_message built from incoming data
+    - CMSGs parsed (e.g. RDMA ops)
+    - transport connection alloced and connected if not already
+    - rds_message placed on send queue
+    - send worker awoken
+
+  rds_send_worker()
+    - calls rds_send_xmit() until queue is empty
+
+  rds_send_xmit()
+    - transmits congestion map if one is pending
+    - may set ACK_REQUIRED
+    - calls transport to send either non-RDMA or RDMA message
+      (RDMA ops never retransmitted)
+
+  rds_ib_xmit()
+    - allocs work requests from send ring
+    - adds any new send credits available to peer (h_credits)
+    - maps the rds_message's sg list
+    - piggybacks ack
+    - populates work requests
+    - post send to connection's queue pair
+
+The recv path
+=============
+
+  rds_ib_recv_cq_comp_handler()
+    - looks at write completions
+    - unmaps recv buffer from device
+    - no errors, call rds_ib_process_recv()
+    - refill recv ring
+
+  rds_ib_process_recv()
+    - validate header checksum
+    - copy header to rds_ib_incoming struct if start of a new datagram
+    - add to ibinc's fraglist
+    - if competed datagram:
+        - update cong map if datagram was cong update
+        - call rds_recv_incoming() otherwise
+        - note if ack is required
+
+  rds_recv_incoming()
+    - drop duplicate packets
+    - respond to pings
+    - find the sock associated with this datagram
+    - add to sock queue
+    - wake up sock
+    - do some congestion calculations
+  rds_recvmsg
+    - copy data into user iovec
+    - handle CMSGs
+    - return to application
+
+Multipath RDS (mprds)
+=====================
+  Mprds is multipathed-RDS, primarily intended for RDS-over-TCP
+  (though the concept can be extended to other transports). The classical
+  implementation of RDS-over-TCP is implemented by demultiplexing multiple
+  PF_RDS sockets between any 2 endpoints (where endpoint == [IP address,
+  port]) over a single TCP socket between the 2 IP addresses involved. This
+  has the limitation that it ends up funneling multiple RDS flows over a
+  single TCP flow, thus it is
+  (a) upper-bounded to the single-flow bandwidth,
+  (b) suffers from head-of-line blocking for all the RDS sockets.
+
+  Better throughput (for a fixed small packet size, MTU) can be achieved
+  by having multiple TCP/IP flows per rds/tcp connection, i.e., multipathed
+  RDS (mprds).  Each such TCP/IP flow constitutes a path for the rds/tcp
+  connection. RDS sockets will be attached to a path based on some hash
+  (e.g., of local address and RDS port number) and packets for that RDS
+  socket will be sent over the attached path using TCP to segment/reassemble
+  RDS datagrams on that path.
+
+  Multipathed RDS is implemented by splitting the struct rds_connection into
+  a common (to all paths) part, and a per-path struct rds_conn_path. All
+  I/O workqs and reconnect threads are driven from the rds_conn_path.
+  Transports such as TCP that are multipath capable may then set up a
+  TCP socket per rds_conn_path, and this is managed by the transport via
+  the transport privatee cp_transport_data pointer.
+
+  Transports announce themselves as multipath capable by setting the
+  t_mp_capable bit during registration with the rds core module. When the
+  transport is multipath-capable, rds_sendmsg() hashes outgoing traffic
+  across multiple paths. The outgoing hash is computed based on the
+  local address and port that the PF_RDS socket is bound to.
+
+  Additionally, even if the transport is MP capable, we may be
+  peering with some node that does not support mprds, or supports
+  a different number of paths. As a result, the peering nodes need
+  to agree on the number of paths to be used for the connection.
+  This is done by sending out a control packet exchange before the
+  first data packet. The control packet exchange must have completed
+  prior to outgoing hash completion in rds_sendmsg() when the transport
+  is mutlipath capable.
+
+  The control packet is an RDS ping packet (i.e., packet to rds dest
+  port 0) with the ping packet having a rds extension header option  of
+  type RDS_EXTHDR_NPATHS, length 2 bytes, and the value is the
+  number of paths supported by the sender. The "probe" ping packet will
+  get sent from some reserved port, RDS_FLAG_PROBE_PORT (in <linux/rds.h>)
+  The receiver of a ping from RDS_FLAG_PROBE_PORT will thus immediately
+  be able to compute the min(sender_paths, rcvr_paths). The pong
+  sent in response to a probe-ping should contain the rcvr's npaths
+  when the rcvr is mprds-capable.
+
+  If the rcvr is not mprds-capable, the exthdr in the ping will be
+  ignored.  In this case the pong will not have any exthdrs, so the sender
+  of the probe-ping can default to single-path mprds.
+
diff --git a/Documentation/networking/rds.txt b/Documentation/networking/rds.txt
deleted file mode 100644 (file)
index eec6169..0000000
+++ /dev/null
@@ -1,423 +0,0 @@
-
-Overview
-========
-
-This readme tries to provide some background on the hows and whys of RDS,
-and will hopefully help you find your way around the code.
-
-In addition, please see this email about RDS origins:
-http://oss.oracle.com/pipermail/rds-devel/2007-November/000228.html
-
-RDS Architecture
-================
-
-RDS provides reliable, ordered datagram delivery by using a single
-reliable connection between any two nodes in the cluster. This allows
-applications to use a single socket to talk to any other process in the
-cluster - so in a cluster with N processes you need N sockets, in contrast
-to N*N if you use a connection-oriented socket transport like TCP.
-
-RDS is not Infiniband-specific; it was designed to support different
-transports.  The current implementation used to support RDS over TCP as well
-as IB.
-
-The high-level semantics of RDS from the application's point of view are
-
- *     Addressing
-        RDS uses IPv4 addresses and 16bit port numbers to identify
-        the end point of a connection. All socket operations that involve
-        passing addresses between kernel and user space generally
-        use a struct sockaddr_in.
-
-        The fact that IPv4 addresses are used does not mean the underlying
-        transport has to be IP-based. In fact, RDS over IB uses a
-        reliable IB connection; the IP address is used exclusively to
-        locate the remote node's GID (by ARPing for the given IP).
-
-        The port space is entirely independent of UDP, TCP or any other
-        protocol.
-
- *     Socket interface
-        RDS sockets work *mostly* as you would expect from a BSD
-        socket. The next section will cover the details. At any rate,
-        all I/O is performed through the standard BSD socket API.
-        Some additions like zerocopy support are implemented through
-        control messages, while other extensions use the getsockopt/
-        setsockopt calls.
-
-        Sockets must be bound before you can send or receive data.
-        This is needed because binding also selects a transport and
-        attaches it to the socket. Once bound, the transport assignment
-        does not change. RDS will tolerate IPs moving around (eg in
-        a active-active HA scenario), but only as long as the address
-        doesn't move to a different transport.
-
- *     sysctls
-        RDS supports a number of sysctls in /proc/sys/net/rds
-
-
-Socket Interface
-================
-
-  AF_RDS, PF_RDS, SOL_RDS
-       AF_RDS and PF_RDS are the domain type to be used with socket(2)
-       to create RDS sockets. SOL_RDS is the socket-level to be used
-       with setsockopt(2) and getsockopt(2) for RDS specific socket
-       options.
-
-  fd = socket(PF_RDS, SOCK_SEQPACKET, 0);
-        This creates a new, unbound RDS socket.
-
-  setsockopt(SOL_SOCKET): send and receive buffer size
-        RDS honors the send and receive buffer size socket options.
-        You are not allowed to queue more than SO_SNDSIZE bytes to
-        a socket. A message is queued when sendmsg is called, and
-        it leaves the queue when the remote system acknowledges
-        its arrival.
-
-        The SO_RCVSIZE option controls the maximum receive queue length.
-        This is a soft limit rather than a hard limit - RDS will
-        continue to accept and queue incoming messages, even if that
-        takes the queue length over the limit. However, it will also
-        mark the port as "congested" and send a congestion update to
-        the source node. The source node is supposed to throttle any
-        processes sending to this congested port.
-
-  bind(fd, &sockaddr_in, ...)
-        This binds the socket to a local IP address and port, and a
-        transport, if one has not already been selected via the
-       SO_RDS_TRANSPORT socket option
-
-  sendmsg(fd, ...)
-        Sends a message to the indicated recipient. The kernel will
-        transparently establish the underlying reliable connection
-        if it isn't up yet.
-
-        An attempt to send a message that exceeds SO_SNDSIZE will
-        return with -EMSGSIZE
-
-        An attempt to send a message that would take the total number
-        of queued bytes over the SO_SNDSIZE threshold will return
-        EAGAIN.
-
-        An attempt to send a message to a destination that is marked
-        as "congested" will return ENOBUFS.
-
-  recvmsg(fd, ...)
-        Receives a message that was queued to this socket. The sockets
-        recv queue accounting is adjusted, and if the queue length
-        drops below SO_SNDSIZE, the port is marked uncongested, and
-        a congestion update is sent to all peers.
-
-        Applications can ask the RDS kernel module to receive
-        notifications via control messages (for instance, there is a
-        notification when a congestion update arrived, or when a RDMA
-        operation completes). These notifications are received through
-        the msg.msg_control buffer of struct msghdr. The format of the
-        messages is described in manpages.
-
-  poll(fd)
-        RDS supports the poll interface to allow the application
-        to implement async I/O.
-
-        POLLIN handling is pretty straightforward. When there's an
-        incoming message queued to the socket, or a pending notification,
-        we signal POLLIN.
-
-        POLLOUT is a little harder. Since you can essentially send
-        to any destination, RDS will always signal POLLOUT as long as
-        there's room on the send queue (ie the number of bytes queued
-        is less than the sendbuf size).
-
-        However, the kernel will refuse to accept messages to
-        a destination marked congested - in this case you will loop
-        forever if you rely on poll to tell you what to do.
-        This isn't a trivial problem, but applications can deal with
-        this - by using congestion notifications, and by checking for
-        ENOBUFS errors returned by sendmsg.
-
-  setsockopt(SOL_RDS, RDS_CANCEL_SENT_TO, &sockaddr_in)
-        This allows the application to discard all messages queued to a
-        specific destination on this particular socket.
-
-        This allows the application to cancel outstanding messages if
-        it detects a timeout. For instance, if it tried to send a message,
-        and the remote host is unreachable, RDS will keep trying forever.
-        The application may decide it's not worth it, and cancel the
-        operation. In this case, it would use RDS_CANCEL_SENT_TO to
-        nuke any pending messages.
-
-  setsockopt(fd, SOL_RDS, SO_RDS_TRANSPORT, (int *)&transport ..)
-  getsockopt(fd, SOL_RDS, SO_RDS_TRANSPORT, (int *)&transport ..)
-       Set or read an integer defining  the underlying
-       encapsulating transport to be used for RDS packets on the
-       socket. When setting the option, integer argument may be
-       one of RDS_TRANS_TCP or RDS_TRANS_IB. When retrieving the
-       value, RDS_TRANS_NONE will be returned on an unbound socket.
-       This socket option may only be set exactly once on the socket,
-       prior to binding it via the bind(2) system call. Attempts to
-       set SO_RDS_TRANSPORT on a socket for which the transport has
-       been previously attached explicitly (by SO_RDS_TRANSPORT) or
-       implicitly (via bind(2)) will return an error of EOPNOTSUPP.
-       An attempt to set SO_RDS_TRANSPORT to RDS_TRANS_NONE will
-       always return EINVAL.
-
-RDMA for RDS
-============
-
-  see rds-rdma(7) manpage (available in rds-tools)
-
-
-Congestion Notifications
-========================
-
-  see rds(7) manpage
-
-
-RDS Protocol
-============
-
-  Message header
-
-    The message header is a 'struct rds_header' (see rds.h):
-    Fields:
-      h_sequence:
-          per-packet sequence number
-      h_ack:
-          piggybacked acknowledgment of last packet received
-      h_len:
-          length of data, not including header
-      h_sport:
-          source port
-      h_dport:
-          destination port
-      h_flags:
-          CONG_BITMAP - this is a congestion update bitmap
-          ACK_REQUIRED - receiver must ack this packet
-          RETRANSMITTED - packet has previously been sent
-      h_credit:
-          indicate to other end of connection that
-          it has more credits available (i.e. there is
-          more send room)
-      h_padding[4]:
-          unused, for future use
-      h_csum:
-          header checksum
-      h_exthdr:
-          optional data can be passed here. This is currently used for
-          passing RDMA-related information.
-
-  ACK and retransmit handling
-
-      One might think that with reliable IB connections you wouldn't need
-      to ack messages that have been received.  The problem is that IB
-      hardware generates an ack message before it has DMAed the message
-      into memory.  This creates a potential message loss if the HCA is
-      disabled for any reason between when it sends the ack and before
-      the message is DMAed and processed.  This is only a potential issue
-      if another HCA is available for fail-over.
-
-      Sending an ack immediately would allow the sender to free the sent
-      message from their send queue quickly, but could cause excessive
-      traffic to be used for acks. RDS piggybacks acks on sent data
-      packets.  Ack-only packets are reduced by only allowing one to be
-      in flight at a time, and by the sender only asking for acks when
-      its send buffers start to fill up. All retransmissions are also
-      acked.
-
-  Flow Control
-
-      RDS's IB transport uses a credit-based mechanism to verify that
-      there is space in the peer's receive buffers for more data. This
-      eliminates the need for hardware retries on the connection.
-
-  Congestion
-
-      Messages waiting in the receive queue on the receiving socket
-      are accounted against the sockets SO_RCVBUF option value.  Only
-      the payload bytes in the message are accounted for.  If the
-      number of bytes queued equals or exceeds rcvbuf then the socket
-      is congested.  All sends attempted to this socket's address
-      should return block or return -EWOULDBLOCK.
-
-      Applications are expected to be reasonably tuned such that this
-      situation very rarely occurs.  An application encountering this
-      "back-pressure" is considered a bug.
-
-      This is implemented by having each node maintain bitmaps which
-      indicate which ports on bound addresses are congested.  As the
-      bitmap changes it is sent through all the connections which
-      terminate in the local address of the bitmap which changed.
-
-      The bitmaps are allocated as connections are brought up.  This
-      avoids allocation in the interrupt handling path which queues
-      sages on sockets.  The dense bitmaps let transports send the
-      entire bitmap on any bitmap change reasonably efficiently.  This
-      is much easier to implement than some finer-grained
-      communication of per-port congestion.  The sender does a very
-      inexpensive bit test to test if the port it's about to send to
-      is congested or not.
-
-
-RDS Transport Layer
-==================
-
-  As mentioned above, RDS is not IB-specific. Its code is divided
-  into a general RDS layer and a transport layer.
-
-  The general layer handles the socket API, congestion handling,
-  loopback, stats, usermem pinning, and the connection state machine.
-
-  The transport layer handles the details of the transport. The IB
-  transport, for example, handles all the queue pairs, work requests,
-  CM event handlers, and other Infiniband details.
-
-
-RDS Kernel Structures
-=====================
-
-  struct rds_message
-    aka possibly "rds_outgoing", the generic RDS layer copies data to
-    be sent and sets header fields as needed, based on the socket API.
-    This is then queued for the individual connection and sent by the
-    connection's transport.
-  struct rds_incoming
-    a generic struct referring to incoming data that can be handed from
-    the transport to the general code and queued by the general code
-    while the socket is awoken. It is then passed back to the transport
-    code to handle the actual copy-to-user.
-  struct rds_socket
-    per-socket information
-  struct rds_connection
-    per-connection information
-  struct rds_transport
-    pointers to transport-specific functions
-  struct rds_statistics
-    non-transport-specific statistics
-  struct rds_cong_map
-    wraps the raw congestion bitmap, contains rbnode, waitq, etc.
-
-Connection management
-=====================
-
-  Connections may be in UP, DOWN, CONNECTING, DISCONNECTING, and
-  ERROR states.
-
-  The first time an attempt is made by an RDS socket to send data to
-  a node, a connection is allocated and connected. That connection is
-  then maintained forever -- if there are transport errors, the
-  connection will be dropped and re-established.
-
-  Dropping a connection while packets are queued will cause queued or
-  partially-sent datagrams to be retransmitted when the connection is
-  re-established.
-
-
-The send path
-=============
-
-  rds_sendmsg()
-    struct rds_message built from incoming data
-    CMSGs parsed (e.g. RDMA ops)
-    transport connection alloced and connected if not already
-    rds_message placed on send queue
-    send worker awoken
-  rds_send_worker()
-    calls rds_send_xmit() until queue is empty
-  rds_send_xmit()
-    transmits congestion map if one is pending
-    may set ACK_REQUIRED
-    calls transport to send either non-RDMA or RDMA message
-    (RDMA ops never retransmitted)
-  rds_ib_xmit()
-    allocs work requests from send ring
-    adds any new send credits available to peer (h_credits)
-    maps the rds_message's sg list
-    piggybacks ack
-    populates work requests
-    post send to connection's queue pair
-
-The recv path
-=============
-
-  rds_ib_recv_cq_comp_handler()
-    looks at write completions
-    unmaps recv buffer from device
-    no errors, call rds_ib_process_recv()
-    refill recv ring
-  rds_ib_process_recv()
-    validate header checksum
-    copy header to rds_ib_incoming struct if start of a new datagram
-    add to ibinc's fraglist
-    if competed datagram:
-      update cong map if datagram was cong update
-      call rds_recv_incoming() otherwise
-      note if ack is required
-  rds_recv_incoming()
-    drop duplicate packets
-    respond to pings
-    find the sock associated with this datagram
-    add to sock queue
-    wake up sock
-    do some congestion calculations
-  rds_recvmsg
-    copy data into user iovec
-    handle CMSGs
-    return to application
-
-Multipath RDS (mprds)
-=====================
-  Mprds is multipathed-RDS, primarily intended for RDS-over-TCP
-  (though the concept can be extended to other transports). The classical
-  implementation of RDS-over-TCP is implemented by demultiplexing multiple
-  PF_RDS sockets between any 2 endpoints (where endpoint == [IP address,
-  port]) over a single TCP socket between the 2 IP addresses involved. This
-  has the limitation that it ends up funneling multiple RDS flows over a
-  single TCP flow, thus it is
-  (a) upper-bounded to the single-flow bandwidth,
-  (b) suffers from head-of-line blocking for all the RDS sockets.
-
-  Better throughput (for a fixed small packet size, MTU) can be achieved
-  by having multiple TCP/IP flows per rds/tcp connection, i.e., multipathed
-  RDS (mprds).  Each such TCP/IP flow constitutes a path for the rds/tcp
-  connection. RDS sockets will be attached to a path based on some hash
-  (e.g., of local address and RDS port number) and packets for that RDS
-  socket will be sent over the attached path using TCP to segment/reassemble
-  RDS datagrams on that path.
-
-  Multipathed RDS is implemented by splitting the struct rds_connection into
-  a common (to all paths) part, and a per-path struct rds_conn_path. All
-  I/O workqs and reconnect threads are driven from the rds_conn_path.
-  Transports such as TCP that are multipath capable may then set up a
-  TCP socket per rds_conn_path, and this is managed by the transport via
-  the transport privatee cp_transport_data pointer.
-
-  Transports announce themselves as multipath capable by setting the
-  t_mp_capable bit during registration with the rds core module. When the
-  transport is multipath-capable, rds_sendmsg() hashes outgoing traffic
-  across multiple paths. The outgoing hash is computed based on the
-  local address and port that the PF_RDS socket is bound to.
-
-  Additionally, even if the transport is MP capable, we may be
-  peering with some node that does not support mprds, or supports
-  a different number of paths. As a result, the peering nodes need
-  to agree on the number of paths to be used for the connection.
-  This is done by sending out a control packet exchange before the
-  first data packet. The control packet exchange must have completed
-  prior to outgoing hash completion in rds_sendmsg() when the transport
-  is mutlipath capable.
-
-  The control packet is an RDS ping packet (i.e., packet to rds dest
-  port 0) with the ping packet having a rds extension header option  of
-  type RDS_EXTHDR_NPATHS, length 2 bytes, and the value is the
-  number of paths supported by the sender. The "probe" ping packet will
-  get sent from some reserved port, RDS_FLAG_PROBE_PORT (in <linux/rds.h>)
-  The receiver of a ping from RDS_FLAG_PROBE_PORT will thus immediately
-  be able to compute the min(sender_paths, rcvr_paths). The pong
-  sent in response to a probe-ping should contain the rcvr's npaths
-  when the rcvr is mprds-capable.
-
-  If the rcvr is not mprds-capable, the exthdr in the ping will be
-  ignored.  In this case the pong will not have any exthdrs, so the sender
-  of the probe-ping can default to single-path mprds.
-
diff --git a/Documentation/networking/regulatory.rst b/Documentation/networking/regulatory.rst
new file mode 100644 (file)
index 0000000..8701b91
--- /dev/null
@@ -0,0 +1,209 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======================================
+Linux wireless regulatory documentation
+=======================================
+
+This document gives a brief review over how the Linux wireless
+regulatory infrastructure works.
+
+More up to date information can be obtained at the project's web page:
+
+http://wireless.kernel.org/en/developers/Regulatory
+
+Keeping regulatory domains in userspace
+---------------------------------------
+
+Due to the dynamic nature of regulatory domains we keep them
+in userspace and provide a framework for userspace to upload
+to the kernel one regulatory domain to be used as the central
+core regulatory domain all wireless devices should adhere to.
+
+How to get regulatory domains to the kernel
+-------------------------------------------
+
+When the regulatory domain is first set up, the kernel will request a
+database file (regulatory.db) containing all the regulatory rules. It
+will then use that database when it needs to look up the rules for a
+given country.
+
+How to get regulatory domains to the kernel (old CRDA solution)
+---------------------------------------------------------------
+
+Userspace gets a regulatory domain in the kernel by having
+a userspace agent build it and send it via nl80211. Only
+expected regulatory domains will be respected by the kernel.
+
+A currently available userspace agent which can accomplish this
+is CRDA - central regulatory domain agent. Its documented here:
+
+http://wireless.kernel.org/en/developers/Regulatory/CRDA
+
+Essentially the kernel will send a udev event when it knows
+it needs a new regulatory domain. A udev rule can be put in place
+to trigger crda to send the respective regulatory domain for a
+specific ISO/IEC 3166 alpha2.
+
+Below is an example udev rule which can be used:
+
+# Example file, should be put in /etc/udev/rules.d/regulatory.rules
+KERNEL=="regulatory*", ACTION=="change", SUBSYSTEM=="platform", RUN+="/sbin/crda"
+
+The alpha2 is passed as an environment variable under the variable COUNTRY.
+
+Who asks for regulatory domains?
+--------------------------------
+
+* Users
+
+Users can use iw:
+
+http://wireless.kernel.org/en/users/Documentation/iw
+
+An example::
+
+  # set regulatory domain to "Costa Rica"
+  iw reg set CR
+
+This will request the kernel to set the regulatory domain to
+the specificied alpha2. The kernel in turn will then ask userspace
+to provide a regulatory domain for the alpha2 specified by the user
+by sending a uevent.
+
+* Wireless subsystems for Country Information elements
+
+The kernel will send a uevent to inform userspace a new
+regulatory domain is required. More on this to be added
+as its integration is added.
+
+* Drivers
+
+If drivers determine they need a specific regulatory domain
+set they can inform the wireless core using regulatory_hint().
+They have two options -- they either provide an alpha2 so that
+crda can provide back a regulatory domain for that country or
+they can build their own regulatory domain based on internal
+custom knowledge so the wireless core can respect it.
+
+*Most* drivers will rely on the first mechanism of providing a
+regulatory hint with an alpha2. For these drivers there is an additional
+check that can be used to ensure compliance based on custom EEPROM
+regulatory data. This additional check can be used by drivers by
+registering on its struct wiphy a reg_notifier() callback. This notifier
+is called when the core's regulatory domain has been changed. The driver
+can use this to review the changes made and also review who made them
+(driver, user, country IE) and determine what to allow based on its
+internal EEPROM data. Devices drivers wishing to be capable of world
+roaming should use this callback. More on world roaming will be
+added to this document when its support is enabled.
+
+Device drivers who provide their own built regulatory domain
+do not need a callback as the channels registered by them are
+the only ones that will be allowed and therefore *additional*
+channels cannot be enabled.
+
+Example code - drivers hinting an alpha2:
+------------------------------------------
+
+This example comes from the zd1211rw device driver. You can start
+by having a mapping of your device's EEPROM country/regulatory
+domain value to a specific alpha2 as follows::
+
+  static struct zd_reg_alpha2_map reg_alpha2_map[] = {
+       { ZD_REGDOMAIN_FCC, "US" },
+       { ZD_REGDOMAIN_IC, "CA" },
+       { ZD_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
+       { ZD_REGDOMAIN_JAPAN, "JP" },
+       { ZD_REGDOMAIN_JAPAN_ADD, "JP" },
+       { ZD_REGDOMAIN_SPAIN, "ES" },
+       { ZD_REGDOMAIN_FRANCE, "FR" },
+
+Then you can define a routine to map your read EEPROM value to an alpha2,
+as follows::
+
+  static int zd_reg2alpha2(u8 regdomain, char *alpha2)
+  {
+       unsigned int i;
+       struct zd_reg_alpha2_map *reg_map;
+               for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {
+                       reg_map = &reg_alpha2_map[i];
+                       if (regdomain == reg_map->reg) {
+                       alpha2[0] = reg_map->alpha2[0];
+                       alpha2[1] = reg_map->alpha2[1];
+                       return 0;
+               }
+       }
+       return 1;
+  }
+
+Lastly, you can then hint to the core of your discovered alpha2, if a match
+was found. You need to do this after you have registered your wiphy. You
+are expected to do this during initialization.
+
+::
+
+       r = zd_reg2alpha2(mac->regdomain, alpha2);
+       if (!r)
+               regulatory_hint(hw->wiphy, alpha2);
+
+Example code - drivers providing a built in regulatory domain:
+--------------------------------------------------------------
+
+[NOTE: This API is not currently available, it can be added when required]
+
+If you have regulatory information you can obtain from your
+driver and you *need* to use this we let you build a regulatory domain
+structure and pass it to the wireless core. To do this you should
+kmalloc() a structure big enough to hold your regulatory domain
+structure and you should then fill it with your data. Finally you simply
+call regulatory_hint() with the regulatory domain structure in it.
+
+Bellow is a simple example, with a regulatory domain cached using the stack.
+Your implementation may vary (read EEPROM cache instead, for example).
+
+Example cache of some regulatory domain::
+
+  struct ieee80211_regdomain mydriver_jp_regdom = {
+       .n_reg_rules = 3,
+       .alpha2 =  "JP",
+       //.alpha2 =  "99", /* If I have no alpha2 to map it to */
+       .reg_rules = {
+               /* IEEE 802.11b/g, channels 1..14 */
+               REG_RULE(2412-10, 2484+10, 40, 6, 20, 0),
+               /* IEEE 802.11a, channels 34..48 */
+               REG_RULE(5170-10, 5240+10, 40, 6, 20,
+                       NL80211_RRF_NO_IR),
+               /* IEEE 802.11a, channels 52..64 */
+               REG_RULE(5260-10, 5320+10, 40, 6, 20,
+                       NL80211_RRF_NO_IR|
+                       NL80211_RRF_DFS),
+       }
+  };
+
+Then in some part of your code after your wiphy has been registered::
+
+       struct ieee80211_regdomain *rd;
+       int size_of_regd;
+       int num_rules = mydriver_jp_regdom.n_reg_rules;
+       unsigned int i;
+
+       size_of_regd = sizeof(struct ieee80211_regdomain) +
+               (num_rules * sizeof(struct ieee80211_reg_rule));
+
+       rd = kzalloc(size_of_regd, GFP_KERNEL);
+       if (!rd)
+               return -ENOMEM;
+
+       memcpy(rd, &mydriver_jp_regdom, sizeof(struct ieee80211_regdomain));
+
+       for (i=0; i < num_rules; i++)
+               memcpy(&rd->reg_rules[i],
+                      &mydriver_jp_regdom.reg_rules[i],
+                      sizeof(struct ieee80211_reg_rule));
+       regulatory_struct_hint(rd);
+
+Statically compiled regulatory database
+---------------------------------------
+
+When a database should be fixed into the kernel, it can be provided as a
+firmware file at build time that is then linked into the kernel.
diff --git a/Documentation/networking/regulatory.txt b/Documentation/networking/regulatory.txt
deleted file mode 100644 (file)
index 381e5b2..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-Linux wireless regulatory documentation
----------------------------------------
-
-This document gives a brief review over how the Linux wireless
-regulatory infrastructure works.
-
-More up to date information can be obtained at the project's web page:
-
-http://wireless.kernel.org/en/developers/Regulatory
-
-Keeping regulatory domains in userspace
----------------------------------------
-
-Due to the dynamic nature of regulatory domains we keep them
-in userspace and provide a framework for userspace to upload
-to the kernel one regulatory domain to be used as the central
-core regulatory domain all wireless devices should adhere to.
-
-How to get regulatory domains to the kernel
--------------------------------------------
-
-When the regulatory domain is first set up, the kernel will request a
-database file (regulatory.db) containing all the regulatory rules. It
-will then use that database when it needs to look up the rules for a
-given country.
-
-How to get regulatory domains to the kernel (old CRDA solution)
----------------------------------------------------------------
-
-Userspace gets a regulatory domain in the kernel by having
-a userspace agent build it and send it via nl80211. Only
-expected regulatory domains will be respected by the kernel.
-
-A currently available userspace agent which can accomplish this
-is CRDA - central regulatory domain agent. Its documented here:
-
-http://wireless.kernel.org/en/developers/Regulatory/CRDA
-
-Essentially the kernel will send a udev event when it knows
-it needs a new regulatory domain. A udev rule can be put in place
-to trigger crda to send the respective regulatory domain for a
-specific ISO/IEC 3166 alpha2.
-
-Below is an example udev rule which can be used:
-
-# Example file, should be put in /etc/udev/rules.d/regulatory.rules
-KERNEL=="regulatory*", ACTION=="change", SUBSYSTEM=="platform", RUN+="/sbin/crda"
-
-The alpha2 is passed as an environment variable under the variable COUNTRY.
-
-Who asks for regulatory domains?
---------------------------------
-
-* Users
-
-Users can use iw:
-
-http://wireless.kernel.org/en/users/Documentation/iw
-
-An example:
-
-  # set regulatory domain to "Costa Rica"
-  iw reg set CR
-
-This will request the kernel to set the regulatory domain to
-the specificied alpha2. The kernel in turn will then ask userspace
-to provide a regulatory domain for the alpha2 specified by the user
-by sending a uevent.
-
-* Wireless subsystems for Country Information elements
-
-The kernel will send a uevent to inform userspace a new
-regulatory domain is required. More on this to be added
-as its integration is added.
-
-* Drivers
-
-If drivers determine they need a specific regulatory domain
-set they can inform the wireless core using regulatory_hint().
-They have two options -- they either provide an alpha2 so that
-crda can provide back a regulatory domain for that country or
-they can build their own regulatory domain based on internal
-custom knowledge so the wireless core can respect it.
-
-*Most* drivers will rely on the first mechanism of providing a
-regulatory hint with an alpha2. For these drivers there is an additional
-check that can be used to ensure compliance based on custom EEPROM
-regulatory data. This additional check can be used by drivers by
-registering on its struct wiphy a reg_notifier() callback. This notifier
-is called when the core's regulatory domain has been changed. The driver
-can use this to review the changes made and also review who made them
-(driver, user, country IE) and determine what to allow based on its
-internal EEPROM data. Devices drivers wishing to be capable of world
-roaming should use this callback. More on world roaming will be
-added to this document when its support is enabled.
-
-Device drivers who provide their own built regulatory domain
-do not need a callback as the channels registered by them are
-the only ones that will be allowed and therefore *additional*
-channels cannot be enabled.
-
-Example code - drivers hinting an alpha2:
-------------------------------------------
-
-This example comes from the zd1211rw device driver. You can start
-by having a mapping of your device's EEPROM country/regulatory
-domain value to a specific alpha2 as follows:
-
-static struct zd_reg_alpha2_map reg_alpha2_map[] = {
-       { ZD_REGDOMAIN_FCC, "US" },
-       { ZD_REGDOMAIN_IC, "CA" },
-       { ZD_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
-       { ZD_REGDOMAIN_JAPAN, "JP" },
-       { ZD_REGDOMAIN_JAPAN_ADD, "JP" },
-       { ZD_REGDOMAIN_SPAIN, "ES" },
-       { ZD_REGDOMAIN_FRANCE, "FR" },
-
-Then you can define a routine to map your read EEPROM value to an alpha2,
-as follows:
-
-static int zd_reg2alpha2(u8 regdomain, char *alpha2)
-{
-       unsigned int i;
-       struct zd_reg_alpha2_map *reg_map;
-               for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {
-                       reg_map = &reg_alpha2_map[i];
-                       if (regdomain == reg_map->reg) {
-                       alpha2[0] = reg_map->alpha2[0];
-                       alpha2[1] = reg_map->alpha2[1];
-                       return 0;
-               }
-       }
-       return 1;
-}
-
-Lastly, you can then hint to the core of your discovered alpha2, if a match
-was found. You need to do this after you have registered your wiphy. You
-are expected to do this during initialization.
-
-       r = zd_reg2alpha2(mac->regdomain, alpha2);
-       if (!r)
-               regulatory_hint(hw->wiphy, alpha2);
-
-Example code - drivers providing a built in regulatory domain:
---------------------------------------------------------------
-
-[NOTE: This API is not currently available, it can be added when required]
-
-If you have regulatory information you can obtain from your
-driver and you *need* to use this we let you build a regulatory domain
-structure and pass it to the wireless core. To do this you should
-kmalloc() a structure big enough to hold your regulatory domain
-structure and you should then fill it with your data. Finally you simply
-call regulatory_hint() with the regulatory domain structure in it.
-
-Bellow is a simple example, with a regulatory domain cached using the stack.
-Your implementation may vary (read EEPROM cache instead, for example).
-
-Example cache of some regulatory domain
-
-struct ieee80211_regdomain mydriver_jp_regdom = {
-       .n_reg_rules = 3,
-       .alpha2 =  "JP",
-       //.alpha2 =  "99", /* If I have no alpha2 to map it to */
-       .reg_rules = {
-               /* IEEE 802.11b/g, channels 1..14 */
-               REG_RULE(2412-10, 2484+10, 40, 6, 20, 0),
-               /* IEEE 802.11a, channels 34..48 */
-               REG_RULE(5170-10, 5240+10, 40, 6, 20,
-                       NL80211_RRF_NO_IR),
-               /* IEEE 802.11a, channels 52..64 */
-               REG_RULE(5260-10, 5320+10, 40, 6, 20,
-                       NL80211_RRF_NO_IR|
-                       NL80211_RRF_DFS),
-       }
-};
-
-Then in some part of your code after your wiphy has been registered:
-
-       struct ieee80211_regdomain *rd;
-       int size_of_regd;
-       int num_rules = mydriver_jp_regdom.n_reg_rules;
-       unsigned int i;
-
-       size_of_regd = sizeof(struct ieee80211_regdomain) +
-               (num_rules * sizeof(struct ieee80211_reg_rule));
-
-       rd = kzalloc(size_of_regd, GFP_KERNEL);
-       if (!rd)
-               return -ENOMEM;
-
-       memcpy(rd, &mydriver_jp_regdom, sizeof(struct ieee80211_regdomain));
-
-       for (i=0; i < num_rules; i++)
-               memcpy(&rd->reg_rules[i],
-                      &mydriver_jp_regdom.reg_rules[i],
-                      sizeof(struct ieee80211_reg_rule));
-       regulatory_struct_hint(rd);
-
-Statically compiled regulatory database
----------------------------------------
-
-When a database should be fixed into the kernel, it can be provided as a
-firmware file at build time that is then linked into the kernel.
diff --git a/Documentation/networking/rxrpc.rst b/Documentation/networking/rxrpc.rst
new file mode 100644 (file)
index 0000000..5ad3511
--- /dev/null
@@ -0,0 +1,1169 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================
+RxRPC Network Protocol
+======================
+
+The RxRPC protocol driver provides a reliable two-phase transport on top of UDP
+that can be used to perform RxRPC remote operations.  This is done over sockets
+of AF_RXRPC family, using sendmsg() and recvmsg() with control data to send and
+receive data, aborts and errors.
+
+Contents of this document:
+
+ (#) Overview.
+
+ (#) RxRPC protocol summary.
+
+ (#) AF_RXRPC driver model.
+
+ (#) Control messages.
+
+ (#) Socket options.
+
+ (#) Security.
+
+ (#) Example client usage.
+
+ (#) Example server usage.
+
+ (#) AF_RXRPC kernel interface.
+
+ (#) Configurable parameters.
+
+
+Overview
+========
+
+RxRPC is a two-layer protocol.  There is a session layer which provides
+reliable virtual connections using UDP over IPv4 (or IPv6) as the transport
+layer, but implements a real network protocol; and there's the presentation
+layer which renders structured data to binary blobs and back again using XDR
+(as does SunRPC)::
+
+               +-------------+
+               | Application |
+               +-------------+
+               |     XDR     |         Presentation
+               +-------------+
+               |    RxRPC    |         Session
+               +-------------+
+               |     UDP     |         Transport
+               +-------------+
+
+
+AF_RXRPC provides:
+
+ (1) Part of an RxRPC facility for both kernel and userspace applications by
+     making the session part of it a Linux network protocol (AF_RXRPC).
+
+ (2) A two-phase protocol.  The client transmits a blob (the request) and then
+     receives a blob (the reply), and the server receives the request and then
+     transmits the reply.
+
+ (3) Retention of the reusable bits of the transport system set up for one call
+     to speed up subsequent calls.
+
+ (4) A secure protocol, using the Linux kernel's key retention facility to
+     manage security on the client end.  The server end must of necessity be
+     more active in security negotiations.
+
+AF_RXRPC does not provide XDR marshalling/presentation facilities.  That is
+left to the application.  AF_RXRPC only deals in blobs.  Even the operation ID
+is just the first four bytes of the request blob, and as such is beyond the
+kernel's interest.
+
+
+Sockets of AF_RXRPC family are:
+
+ (1) created as type SOCK_DGRAM;
+
+ (2) provided with a protocol of the type of underlying transport they're going
+     to use - currently only PF_INET is supported.
+
+
+The Andrew File System (AFS) is an example of an application that uses this and
+that has both kernel (filesystem) and userspace (utility) components.
+
+
+RxRPC Protocol Summary
+======================
+
+An overview of the RxRPC protocol:
+
+ (#) RxRPC sits on top of another networking protocol (UDP is the only option
+     currently), and uses this to provide network transport.  UDP ports, for
+     example, provide transport endpoints.
+
+ (#) RxRPC supports multiple virtual "connections" from any given transport
+     endpoint, thus allowing the endpoints to be shared, even to the same
+     remote endpoint.
+
+ (#) Each connection goes to a particular "service".  A connection may not go
+     to multiple services.  A service may be considered the RxRPC equivalent of
+     a port number.  AF_RXRPC permits multiple services to share an endpoint.
+
+ (#) Client-originating packets are marked, thus a transport endpoint can be
+     shared between client and server connections (connections have a
+     direction).
+
+ (#) Up to a billion connections may be supported concurrently between one
+     local transport endpoint and one service on one remote endpoint.  An RxRPC
+     connection is described by seven numbers::
+
+       Local address   }
+       Local port      } Transport (UDP) address
+       Remote address  }
+       Remote port     }
+       Direction
+       Connection ID
+       Service ID
+
+ (#) Each RxRPC operation is a "call".  A connection may make up to four
+     billion calls, but only up to four calls may be in progress on a
+     connection at any one time.
+
+ (#) Calls are two-phase and asymmetric: the client sends its request data,
+     which the service receives; then the service transmits the reply data
+     which the client receives.
+
+ (#) The data blobs are of indefinite size, the end of a phase is marked with a
+     flag in the packet.  The number of packets of data making up one blob may
+     not exceed 4 billion, however, as this would cause the sequence number to
+     wrap.
+
+ (#) The first four bytes of the request data are the service operation ID.
+
+ (#) Security is negotiated on a per-connection basis.  The connection is
+     initiated by the first data packet on it arriving.  If security is
+     requested, the server then issues a "challenge" and then the client
+     replies with a "response".  If the response is successful, the security is
+     set for the lifetime of that connection, and all subsequent calls made
+     upon it use that same security.  In the event that the server lets a
+     connection lapse before the client, the security will be renegotiated if
+     the client uses the connection again.
+
+ (#) Calls use ACK packets to handle reliability.  Data packets are also
+     explicitly sequenced per call.
+
+ (#) There are two types of positive acknowledgment: hard-ACKs and soft-ACKs.
+     A hard-ACK indicates to the far side that all the data received to a point
+     has been received and processed; a soft-ACK indicates that the data has
+     been received but may yet be discarded and re-requested.  The sender may
+     not discard any transmittable packets until they've been hard-ACK'd.
+
+ (#) Reception of a reply data packet implicitly hard-ACK's all the data
+     packets that make up the request.
+
+ (#) An call is complete when the request has been sent, the reply has been
+     received and the final hard-ACK on the last packet of the reply has
+     reached the server.
+
+ (#) An call may be aborted by either end at any time up to its completion.
+
+
+AF_RXRPC Driver Model
+=====================
+
+About the AF_RXRPC driver:
+
+ (#) The AF_RXRPC protocol transparently uses internal sockets of the transport
+     protocol to represent transport endpoints.
+
+ (#) AF_RXRPC sockets map onto RxRPC connection bundles.  Actual RxRPC
+     connections are handled transparently.  One client socket may be used to
+     make multiple simultaneous calls to the same service.  One server socket
+     may handle calls from many clients.
+
+ (#) Additional parallel client connections will be initiated to support extra
+     concurrent calls, up to a tunable limit.
+
+ (#) Each connection is retained for a certain amount of time [tunable] after
+     the last call currently using it has completed in case a new call is made
+     that could reuse it.
+
+ (#) Each internal UDP socket is retained [tunable] for a certain amount of
+     time [tunable] after the last connection using it discarded, in case a new
+     connection is made that could use it.
+
+ (#) A client-side connection is only shared between calls if they have have
+     the same key struct describing their security (and assuming the calls
+     would otherwise share the connection).  Non-secured calls would also be
+     able to share connections with each other.
+
+ (#) A server-side connection is shared if the client says it is.
+
+ (#) ACK'ing is handled by the protocol driver automatically, including ping
+     replying.
+
+ (#) SO_KEEPALIVE automatically pings the other side to keep the connection
+     alive [TODO].
+
+ (#) If an ICMP error is received, all calls affected by that error will be
+     aborted with an appropriate network error passed through recvmsg().
+
+
+Interaction with the user of the RxRPC socket:
+
+ (#) A socket is made into a server socket by binding an address with a
+     non-zero service ID.
+
+ (#) In the client, sending a request is achieved with one or more sendmsgs,
+     followed by the reply being received with one or more recvmsgs.
+
+ (#) The first sendmsg for a request to be sent from a client contains a tag to
+     be used in all other sendmsgs or recvmsgs associated with that call.  The
+     tag is carried in the control data.
+
+ (#) connect() is used to supply a default destination address for a client
+     socket.  This may be overridden by supplying an alternate address to the
+     first sendmsg() of a call (struct msghdr::msg_name).
+
+ (#) If connect() is called on an unbound client, a random local port will
+     bound before the operation takes place.
+
+ (#) A server socket may also be used to make client calls.  To do this, the
+     first sendmsg() of the call must specify the target address.  The server's
+     transport endpoint is used to send the packets.
+
+ (#) Once the application has received the last message associated with a call,
+     the tag is guaranteed not to be seen again, and so it can be used to pin
+     client resources.  A new call can then be initiated with the same tag
+     without fear of interference.
+
+ (#) In the server, a request is received with one or more recvmsgs, then the
+     the reply is transmitted with one or more sendmsgs, and then the final ACK
+     is received with a last recvmsg.
+
+ (#) When sending data for a call, sendmsg is given MSG_MORE if there's more
+     data to come on that call.
+
+ (#) When receiving data for a call, recvmsg flags MSG_MORE if there's more
+     data to come for that call.
+
+ (#) When receiving data or messages for a call, MSG_EOR is flagged by recvmsg
+     to indicate the terminal message for that call.
+
+ (#) A call may be aborted by adding an abort control message to the control
+     data.  Issuing an abort terminates the kernel's use of that call's tag.
+     Any messages waiting in the receive queue for that call will be discarded.
+
+ (#) Aborts, busy notifications and challenge packets are delivered by recvmsg,
+     and control data messages will be set to indicate the context.  Receiving
+     an abort or a busy message terminates the kernel's use of that call's tag.
+
+ (#) The control data part of the msghdr struct is used for a number of things:
+
+     (#) The tag of the intended or affected call.
+
+     (#) Sending or receiving errors, aborts and busy notifications.
+
+     (#) Notifications of incoming calls.
+
+     (#) Sending debug requests and receiving debug replies [TODO].
+
+ (#) When the kernel has received and set up an incoming call, it sends a
+     message to server application to let it know there's a new call awaiting
+     its acceptance [recvmsg reports a special control message].  The server
+     application then uses sendmsg to assign a tag to the new call.  Once that
+     is done, the first part of the request data will be delivered by recvmsg.
+
+ (#) The server application has to provide the server socket with a keyring of
+     secret keys corresponding to the security types it permits.  When a secure
+     connection is being set up, the kernel looks up the appropriate secret key
+     in the keyring and then sends a challenge packet to the client and
+     receives a response packet.  The kernel then checks the authorisation of
+     the packet and either aborts the connection or sets up the security.
+
+ (#) The name of the key a client will use to secure its communications is
+     nominated by a socket option.
+
+
+Notes on sendmsg:
+
+ (#) MSG_WAITALL can be set to tell sendmsg to ignore signals if the peer is
+     making progress at accepting packets within a reasonable time such that we
+     manage to queue up all the data for transmission.  This requires the
+     client to accept at least one packet per 2*RTT time period.
+
+     If this isn't set, sendmsg() will return immediately, either returning
+     EINTR/ERESTARTSYS if nothing was consumed or returning the amount of data
+     consumed.
+
+
+Notes on recvmsg:
+
+ (#) If there's a sequence of data messages belonging to a particular call on
+     the receive queue, then recvmsg will keep working through them until:
+
+     (a) it meets the end of that call's received data,
+
+     (b) it meets a non-data message,
+
+     (c) it meets a message belonging to a different call, or
+
+     (d) it fills the user buffer.
+
+     If recvmsg is called in blocking mode, it will keep sleeping, awaiting the
+     reception of further data, until one of the above four conditions is met.
+
+ (2) MSG_PEEK operates similarly, but will return immediately if it has put any
+     data in the buffer rather than sleeping until it can fill the buffer.
+
+ (3) If a data message is only partially consumed in filling a user buffer,
+     then the remainder of that message will be left on the front of the queue
+     for the next taker.  MSG_TRUNC will never be flagged.
+
+ (4) If there is more data to be had on a call (it hasn't copied the last byte
+     of the last data message in that phase yet), then MSG_MORE will be
+     flagged.
+
+
+Control Messages
+================
+
+AF_RXRPC makes use of control messages in sendmsg() and recvmsg() to multiplex
+calls, to invoke certain actions and to report certain conditions.  These are:
+
+       ======================= === =========== ===============================
+       MESSAGE ID              SRT DATA        MEANING
+       ======================= === =========== ===============================
+       RXRPC_USER_CALL_ID      sr- User ID     App's call specifier
+       RXRPC_ABORT             srt Abort code  Abort code to issue/received
+       RXRPC_ACK               -rt n/a         Final ACK received
+       RXRPC_NET_ERROR         -rt error num   Network error on call
+       RXRPC_BUSY              -rt n/a         Call rejected (server busy)
+       RXRPC_LOCAL_ERROR       -rt error num   Local error encountered
+       RXRPC_NEW_CALL          -r- n/a         New call received
+       RXRPC_ACCEPT            s-- n/a         Accept new call
+       RXRPC_EXCLUSIVE_CALL    s-- n/a         Make an exclusive client call
+       RXRPC_UPGRADE_SERVICE   s-- n/a         Client call can be upgraded
+       RXRPC_TX_LENGTH         s-- data len    Total length of Tx data
+       ======================= === =========== ===============================
+
+       (SRT = usable in Sendmsg / delivered by Recvmsg / Terminal message)
+
+ (#) RXRPC_USER_CALL_ID
+
+     This is used to indicate the application's call ID.  It's an unsigned long
+     that the app specifies in the client by attaching it to the first data
+     message or in the server by passing it in association with an RXRPC_ACCEPT
+     message.  recvmsg() passes it in conjunction with all messages except
+     those of the RXRPC_NEW_CALL message.
+
+ (#) RXRPC_ABORT
+
+     This is can be used by an application to abort a call by passing it to
+     sendmsg, or it can be delivered by recvmsg to indicate a remote abort was
+     received.  Either way, it must be associated with an RXRPC_USER_CALL_ID to
+     specify the call affected.  If an abort is being sent, then error EBADSLT
+     will be returned if there is no call with that user ID.
+
+ (#) RXRPC_ACK
+
+     This is delivered to a server application to indicate that the final ACK
+     of a call was received from the client.  It will be associated with an
+     RXRPC_USER_CALL_ID to indicate the call that's now complete.
+
+ (#) RXRPC_NET_ERROR
+
+     This is delivered to an application to indicate that an ICMP error message
+     was encountered in the process of trying to talk to the peer.  An
+     errno-class integer value will be included in the control message data
+     indicating the problem, and an RXRPC_USER_CALL_ID will indicate the call
+     affected.
+
+ (#) RXRPC_BUSY
+
+     This is delivered to a client application to indicate that a call was
+     rejected by the server due to the server being busy.  It will be
+     associated with an RXRPC_USER_CALL_ID to indicate the rejected call.
+
+ (#) RXRPC_LOCAL_ERROR
+
+     This is delivered to an application to indicate that a local error was
+     encountered and that a call has been aborted because of it.  An
+     errno-class integer value will be included in the control message data
+     indicating the problem, and an RXRPC_USER_CALL_ID will indicate the call
+     affected.
+
+ (#) RXRPC_NEW_CALL
+
+     This is delivered to indicate to a server application that a new call has
+     arrived and is awaiting acceptance.  No user ID is associated with this,
+     as a user ID must subsequently be assigned by doing an RXRPC_ACCEPT.
+
+ (#) RXRPC_ACCEPT
+
+     This is used by a server application to attempt to accept a call and
+     assign it a user ID.  It should be associated with an RXRPC_USER_CALL_ID
+     to indicate the user ID to be assigned.  If there is no call to be
+     accepted (it may have timed out, been aborted, etc.), then sendmsg will
+     return error ENODATA.  If the user ID is already in use by another call,
+     then error EBADSLT will be returned.
+
+ (#) RXRPC_EXCLUSIVE_CALL
+
+     This is used to indicate that a client call should be made on a one-off
+     connection.  The connection is discarded once the call has terminated.
+
+ (#) RXRPC_UPGRADE_SERVICE
+
+     This is used to make a client call to probe if the specified service ID
+     may be upgraded by the server.  The caller must check msg_name returned to
+     recvmsg() for the service ID actually in use.  The operation probed must
+     be one that takes the same arguments in both services.
+
+     Once this has been used to establish the upgrade capability (or lack
+     thereof) of the server, the service ID returned should be used for all
+     future communication to that server and RXRPC_UPGRADE_SERVICE should no
+     longer be set.
+
+ (#) RXRPC_TX_LENGTH
+
+     This is used to inform the kernel of the total amount of data that is
+     going to be transmitted by a call (whether in a client request or a
+     service response).  If given, it allows the kernel to encrypt from the
+     userspace buffer directly to the packet buffers, rather than copying into
+     the buffer and then encrypting in place.  This may only be given with the
+     first sendmsg() providing data for a call.  EMSGSIZE will be generated if
+     the amount of data actually given is different.
+
+     This takes a parameter of __s64 type that indicates how much will be
+     transmitted.  This may not be less than zero.
+
+The symbol RXRPC__SUPPORTED is defined as one more than the highest control
+message type supported.  At run time this can be queried by means of the
+RXRPC_SUPPORTED_CMSG socket option (see below).
+
+
+==============
+SOCKET OPTIONS
+==============
+
+AF_RXRPC sockets support a few socket options at the SOL_RXRPC level:
+
+ (#) RXRPC_SECURITY_KEY
+
+     This is used to specify the description of the key to be used.  The key is
+     extracted from the calling process's keyrings with request_key() and
+     should be of "rxrpc" type.
+
+     The optval pointer points to the description string, and optlen indicates
+     how long the string is, without the NUL terminator.
+
+ (#) RXRPC_SECURITY_KEYRING
+
+     Similar to above but specifies a keyring of server secret keys to use (key
+     type "keyring").  See the "Security" section.
+
+ (#) RXRPC_EXCLUSIVE_CONNECTION
+
+     This is used to request that new connections should be used for each call
+     made subsequently on this socket.  optval should be NULL and optlen 0.
+
+ (#) RXRPC_MIN_SECURITY_LEVEL
+
+     This is used to specify the minimum security level required for calls on
+     this socket.  optval must point to an int containing one of the following
+     values:
+
+     (a) RXRPC_SECURITY_PLAIN
+
+        Encrypted checksum only.
+
+     (b) RXRPC_SECURITY_AUTH
+
+        Encrypted checksum plus packet padded and first eight bytes of packet
+        encrypted - which includes the actual packet length.
+
+     (c) RXRPC_SECURITY_ENCRYPTED
+
+        Encrypted checksum plus entire packet padded and encrypted, including
+        actual packet length.
+
+ (#) RXRPC_UPGRADEABLE_SERVICE
+
+     This is used to indicate that a service socket with two bindings may
+     upgrade one bound service to the other if requested by the client.  optval
+     must point to an array of two unsigned short ints.  The first is the
+     service ID to upgrade from and the second the service ID to upgrade to.
+
+ (#) RXRPC_SUPPORTED_CMSG
+
+     This is a read-only option that writes an int into the buffer indicating
+     the highest control message type supported.
+
+
+========
+SECURITY
+========
+
+Currently, only the kerberos 4 equivalent protocol has been implemented
+(security index 2 - rxkad).  This requires the rxkad module to be loaded and,
+on the client, tickets of the appropriate type to be obtained from the AFS
+kaserver or the kerberos server and installed as "rxrpc" type keys.  This is
+normally done using the klog program.  An example simple klog program can be
+found at:
+
+       http://people.redhat.com/~dhowells/rxrpc/klog.c
+
+The payload provided to add_key() on the client should be of the following
+form::
+
+       struct rxrpc_key_sec2_v1 {
+               uint16_t        security_index; /* 2 */
+               uint16_t        ticket_length;  /* length of ticket[] */
+               uint32_t        expiry;         /* time at which expires */
+               uint8_t         kvno;           /* key version number */
+               uint8_t         __pad[3];
+               uint8_t         session_key[8]; /* DES session key */
+               uint8_t         ticket[0];      /* the encrypted ticket */
+       };
+
+Where the ticket blob is just appended to the above structure.
+
+
+For the server, keys of type "rxrpc_s" must be made available to the server.
+They have a description of "<serviceID>:<securityIndex>" (eg: "52:2" for an
+rxkad key for the AFS VL service).  When such a key is created, it should be
+given the server's secret key as the instantiation data (see the example
+below).
+
+       add_key("rxrpc_s", "52:2", secret_key, 8, keyring);
+
+A keyring is passed to the server socket by naming it in a sockopt.  The server
+socket then looks the server secret keys up in this keyring when secure
+incoming connections are made.  This can be seen in an example program that can
+be found at:
+
+       http://people.redhat.com/~dhowells/rxrpc/listen.c
+
+
+====================
+EXAMPLE CLIENT USAGE
+====================
+
+A client would issue an operation by:
+
+ (1) An RxRPC socket is set up by::
+
+       client = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);
+
+     Where the third parameter indicates the protocol family of the transport
+     socket used - usually IPv4 but it can also be IPv6 [TODO].
+
+ (2) A local address can optionally be bound::
+
+       struct sockaddr_rxrpc srx = {
+               .srx_family     = AF_RXRPC,
+               .srx_service    = 0,  /* we're a client */
+               .transport_type = SOCK_DGRAM,   /* type of transport socket */
+               .transport.sin_family   = AF_INET,
+               .transport.sin_port     = htons(7000), /* AFS callback */
+               .transport.sin_address  = 0,  /* all local interfaces */
+       };
+       bind(client, &srx, sizeof(srx));
+
+     This specifies the local UDP port to be used.  If not given, a random
+     non-privileged port will be used.  A UDP port may be shared between
+     several unrelated RxRPC sockets.  Security is handled on a basis of
+     per-RxRPC virtual connection.
+
+ (3) The security is set::
+
+       const char *key = "AFS:cambridge.redhat.com";
+       setsockopt(client, SOL_RXRPC, RXRPC_SECURITY_KEY, key, strlen(key));
+
+     This issues a request_key() to get the key representing the security
+     context.  The minimum security level can be set::
+
+       unsigned int sec = RXRPC_SECURITY_ENCRYPTED;
+       setsockopt(client, SOL_RXRPC, RXRPC_MIN_SECURITY_LEVEL,
+                  &sec, sizeof(sec));
+
+ (4) The server to be contacted can then be specified (alternatively this can
+     be done through sendmsg)::
+
+       struct sockaddr_rxrpc srx = {
+               .srx_family     = AF_RXRPC,
+               .srx_service    = VL_SERVICE_ID,
+               .transport_type = SOCK_DGRAM,   /* type of transport socket */
+               .transport.sin_family   = AF_INET,
+               .transport.sin_port     = htons(7005), /* AFS volume manager */
+               .transport.sin_address  = ...,
+       };
+       connect(client, &srx, sizeof(srx));
+
+ (5) The request data should then be posted to the server socket using a series
+     of sendmsg() calls, each with the following control message attached:
+
+       ==================      ===================================
+       RXRPC_USER_CALL_ID      specifies the user ID for this call
+       ==================      ===================================
+
+     MSG_MORE should be set in msghdr::msg_flags on all but the last part of
+     the request.  Multiple requests may be made simultaneously.
+
+     An RXRPC_TX_LENGTH control message can also be specified on the first
+     sendmsg() call.
+
+     If a call is intended to go to a destination other than the default
+     specified through connect(), then msghdr::msg_name should be set on the
+     first request message of that call.
+
+ (6) The reply data will then be posted to the server socket for recvmsg() to
+     pick up.  MSG_MORE will be flagged by recvmsg() if there's more reply data
+     for a particular call to be read.  MSG_EOR will be set on the terminal
+     read for a call.
+
+     All data will be delivered with the following control message attached:
+
+       RXRPC_USER_CALL_ID      - specifies the user ID for this call
+
+     If an abort or error occurred, this will be returned in the control data
+     buffer instead, and MSG_EOR will be flagged to indicate the end of that
+     call.
+
+A client may ask for a service ID it knows and ask that this be upgraded to a
+better service if one is available by supplying RXRPC_UPGRADE_SERVICE on the
+first sendmsg() of a call.  The client should then check srx_service in the
+msg_name filled in by recvmsg() when collecting the result.  srx_service will
+hold the same value as given to sendmsg() if the upgrade request was ignored by
+the service - otherwise it will be altered to indicate the service ID the
+server upgraded to.  Note that the upgraded service ID is chosen by the server.
+The caller has to wait until it sees the service ID in the reply before sending
+any more calls (further calls to the same destination will be blocked until the
+probe is concluded).
+
+
+Example Server Usage
+====================
+
+A server would be set up to accept operations in the following manner:
+
+ (1) An RxRPC socket is created by::
+
+       server = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);
+
+     Where the third parameter indicates the address type of the transport
+     socket used - usually IPv4.
+
+ (2) Security is set up if desired by giving the socket a keyring with server
+     secret keys in it::
+
+       keyring = add_key("keyring", "AFSkeys", NULL, 0,
+                         KEY_SPEC_PROCESS_KEYRING);
+
+       const char secret_key[8] = {
+               0xa7, 0x83, 0x8a, 0xcb, 0xc7, 0x83, 0xec, 0x94 };
+       add_key("rxrpc_s", "52:2", secret_key, 8, keyring);
+
+       setsockopt(server, SOL_RXRPC, RXRPC_SECURITY_KEYRING, "AFSkeys", 7);
+
+     The keyring can be manipulated after it has been given to the socket. This
+     permits the server to add more keys, replace keys, etc. while it is live.
+
+ (3) A local address must then be bound::
+
+       struct sockaddr_rxrpc srx = {
+               .srx_family     = AF_RXRPC,
+               .srx_service    = VL_SERVICE_ID, /* RxRPC service ID */
+               .transport_type = SOCK_DGRAM,   /* type of transport socket */
+               .transport.sin_family   = AF_INET,
+               .transport.sin_port     = htons(7000), /* AFS callback */
+               .transport.sin_address  = 0,  /* all local interfaces */
+       };
+       bind(server, &srx, sizeof(srx));
+
+     More than one service ID may be bound to a socket, provided the transport
+     parameters are the same.  The limit is currently two.  To do this, bind()
+     should be called twice.
+
+ (4) If service upgrading is required, first two service IDs must have been
+     bound and then the following option must be set::
+
+       unsigned short service_ids[2] = { from_ID, to_ID };
+       setsockopt(server, SOL_RXRPC, RXRPC_UPGRADEABLE_SERVICE,
+                  service_ids, sizeof(service_ids));
+
+     This will automatically upgrade connections on service from_ID to service
+     to_ID if they request it.  This will be reflected in msg_name obtained
+     through recvmsg() when the request data is delivered to userspace.
+
+ (5) The server is then set to listen out for incoming calls::
+
+       listen(server, 100);
+
+ (6) The kernel notifies the server of pending incoming connections by sending
+     it a message for each.  This is received with recvmsg() on the server
+     socket.  It has no data, and has a single dataless control message
+     attached::
+
+       RXRPC_NEW_CALL
+
+     The address that can be passed back by recvmsg() at this point should be
+     ignored since the call for which the message was posted may have gone by
+     the time it is accepted - in which case the first call still on the queue
+     will be accepted.
+
+ (7) The server then accepts the new call by issuing a sendmsg() with two
+     pieces of control data and no actual data:
+
+       ==================      ==============================
+       RXRPC_ACCEPT            indicate connection acceptance
+       RXRPC_USER_CALL_ID      specify user ID for this call
+       ==================      ==============================
+
+ (8) The first request data packet will then be posted to the server socket for
+     recvmsg() to pick up.  At that point, the RxRPC address for the call can
+     be read from the address fields in the msghdr struct.
+
+     Subsequent request data will be posted to the server socket for recvmsg()
+     to collect as it arrives.  All but the last piece of the request data will
+     be delivered with MSG_MORE flagged.
+
+     All data will be delivered with the following control message attached:
+
+
+       ==================      ===================================
+       RXRPC_USER_CALL_ID      specifies the user ID for this call
+       ==================      ===================================
+
+ (9) The reply data should then be posted to the server socket using a series
+     of sendmsg() calls, each with the following control messages attached:
+
+       ==================      ===================================
+       RXRPC_USER_CALL_ID      specifies the user ID for this call
+       ==================      ===================================
+
+     MSG_MORE should be set in msghdr::msg_flags on all but the last message
+     for a particular call.
+
+(10) The final ACK from the client will be posted for retrieval by recvmsg()
+     when it is received.  It will take the form of a dataless message with two
+     control messages attached:
+
+       ==================      ===================================
+       RXRPC_USER_CALL_ID      specifies the user ID for this call
+       RXRPC_ACK               indicates final ACK (no data)
+       ==================      ===================================
+
+     MSG_EOR will be flagged to indicate that this is the final message for
+     this call.
+
+(11) Up to the point the final packet of reply data is sent, the call can be
+     aborted by calling sendmsg() with a dataless message with the following
+     control messages attached:
+
+       ==================      ===================================
+       RXRPC_USER_CALL_ID      specifies the user ID for this call
+       RXRPC_ABORT             indicates abort code (4 byte data)
+       ==================      ===================================
+
+     Any packets waiting in the socket's receive queue will be discarded if
+     this is issued.
+
+Note that all the communications for a particular service take place through
+the one server socket, using control messages on sendmsg() and recvmsg() to
+determine the call affected.
+
+
+AF_RXRPC Kernel Interface
+=========================
+
+The AF_RXRPC module also provides an interface for use by in-kernel utilities
+such as the AFS filesystem.  This permits such a utility to:
+
+ (1) Use different keys directly on individual client calls on one socket
+     rather than having to open a whole slew of sockets, one for each key it
+     might want to use.
+
+ (2) Avoid having RxRPC call request_key() at the point of issue of a call or
+     opening of a socket.  Instead the utility is responsible for requesting a
+     key at the appropriate point.  AFS, for instance, would do this during VFS
+     operations such as open() or unlink().  The key is then handed through
+     when the call is initiated.
+
+ (3) Request the use of something other than GFP_KERNEL to allocate memory.
+
+ (4) Avoid the overhead of using the recvmsg() call.  RxRPC messages can be
+     intercepted before they get put into the socket Rx queue and the socket
+     buffers manipulated directly.
+
+To use the RxRPC facility, a kernel utility must still open an AF_RXRPC socket,
+bind an address as appropriate and listen if it's to be a server socket, but
+then it passes this to the kernel interface functions.
+
+The kernel interface functions are as follows:
+
+ (#) Begin a new client call::
+
+       struct rxrpc_call *
+       rxrpc_kernel_begin_call(struct socket *sock,
+                               struct sockaddr_rxrpc *srx,
+                               struct key *key,
+                               unsigned long user_call_ID,
+                               s64 tx_total_len,
+                               gfp_t gfp,
+                               rxrpc_notify_rx_t notify_rx,
+                               bool upgrade,
+                               bool intr,
+                               unsigned int debug_id);
+
+     This allocates the infrastructure to make a new RxRPC call and assigns
+     call and connection numbers.  The call will be made on the UDP port that
+     the socket is bound to.  The call will go to the destination address of a
+     connected client socket unless an alternative is supplied (srx is
+     non-NULL).
+
+     If a key is supplied then this will be used to secure the call instead of
+     the key bound to the socket with the RXRPC_SECURITY_KEY sockopt.  Calls
+     secured in this way will still share connections if at all possible.
+
+     The user_call_ID is equivalent to that supplied to sendmsg() in the
+     control data buffer.  It is entirely feasible to use this to point to a
+     kernel data structure.
+
+     tx_total_len is the amount of data the caller is intending to transmit
+     with this call (or -1 if unknown at this point).  Setting the data size
+     allows the kernel to encrypt directly to the packet buffers, thereby
+     saving a copy.  The value may not be less than -1.
+
+     notify_rx is a pointer to a function to be called when events such as
+     incoming data packets or remote aborts happen.
+
+     upgrade should be set to true if a client operation should request that
+     the server upgrade the service to a better one.  The resultant service ID
+     is returned by rxrpc_kernel_recv_data().
+
+     intr should be set to true if the call should be interruptible.  If this
+     is not set, this function may not return until a channel has been
+     allocated; if it is set, the function may return -ERESTARTSYS.
+
+     debug_id is the call debugging ID to be used for tracing.  This can be
+     obtained by atomically incrementing rxrpc_debug_id.
+
+     If this function is successful, an opaque reference to the RxRPC call is
+     returned.  The caller now holds a reference on this and it must be
+     properly ended.
+
+ (#) End a client call::
+
+       void rxrpc_kernel_end_call(struct socket *sock,
+                                  struct rxrpc_call *call);
+
+     This is used to end a previously begun call.  The user_call_ID is expunged
+     from AF_RXRPC's knowledge and will not be seen again in association with
+     the specified call.
+
+ (#) Send data through a call::
+
+       typedef void (*rxrpc_notify_end_tx_t)(struct sock *sk,
+                                             unsigned long user_call_ID,
+                                             struct sk_buff *skb);
+
+       int rxrpc_kernel_send_data(struct socket *sock,
+                                  struct rxrpc_call *call,
+                                  struct msghdr *msg,
+                                  size_t len,
+                                  rxrpc_notify_end_tx_t notify_end_rx);
+
+     This is used to supply either the request part of a client call or the
+     reply part of a server call.  msg.msg_iovlen and msg.msg_iov specify the
+     data buffers to be used.  msg_iov may not be NULL and must point
+     exclusively to in-kernel virtual addresses.  msg.msg_flags may be given
+     MSG_MORE if there will be subsequent data sends for this call.
+
+     The msg must not specify a destination address, control data or any flags
+     other than MSG_MORE.  len is the total amount of data to transmit.
+
+     notify_end_rx can be NULL or it can be used to specify a function to be
+     called when the call changes state to end the Tx phase.  This function is
+     called with the call-state spinlock held to prevent any reply or final ACK
+     from being delivered first.
+
+ (#) Receive data from a call::
+
+       int rxrpc_kernel_recv_data(struct socket *sock,
+                                  struct rxrpc_call *call,
+                                  void *buf,
+                                  size_t size,
+                                  size_t *_offset,
+                                  bool want_more,
+                                  u32 *_abort,
+                                  u16 *_service)
+
+      This is used to receive data from either the reply part of a client call
+      or the request part of a service call.  buf and size specify how much
+      data is desired and where to store it.  *_offset is added on to buf and
+      subtracted from size internally; the amount copied into the buffer is
+      added to *_offset before returning.
+
+      want_more should be true if further data will be required after this is
+      satisfied and false if this is the last item of the receive phase.
+
+      There are three normal returns: 0 if the buffer was filled and want_more
+      was true; 1 if the buffer was filled, the last DATA packet has been
+      emptied and want_more was false; and -EAGAIN if the function needs to be
+      called again.
+
+      If the last DATA packet is processed but the buffer contains less than
+      the amount requested, EBADMSG is returned.  If want_more wasn't set, but
+      more data was available, EMSGSIZE is returned.
+
+      If a remote ABORT is detected, the abort code received will be stored in
+      ``*_abort`` and ECONNABORTED will be returned.
+
+      The service ID that the call ended up with is returned into *_service.
+      This can be used to see if a call got a service upgrade.
+
+ (#) Abort a call??
+
+     ::
+
+       void rxrpc_kernel_abort_call(struct socket *sock,
+                                    struct rxrpc_call *call,
+                                    u32 abort_code);
+
+     This is used to abort a call if it's still in an abortable state.  The
+     abort code specified will be placed in the ABORT message sent.
+
+ (#) Intercept received RxRPC messages::
+
+       typedef void (*rxrpc_interceptor_t)(struct sock *sk,
+                                           unsigned long user_call_ID,
+                                           struct sk_buff *skb);
+
+       void
+       rxrpc_kernel_intercept_rx_messages(struct socket *sock,
+                                          rxrpc_interceptor_t interceptor);
+
+     This installs an interceptor function on the specified AF_RXRPC socket.
+     All messages that would otherwise wind up in the socket's Rx queue are
+     then diverted to this function.  Note that care must be taken to process
+     the messages in the right order to maintain DATA message sequentiality.
+
+     The interceptor function itself is provided with the address of the socket
+     and handling the incoming message, the ID assigned by the kernel utility
+     to the call and the socket buffer containing the message.
+
+     The skb->mark field indicates the type of message:
+
+       =============================== =======================================
+       Mark                            Meaning
+       =============================== =======================================
+       RXRPC_SKB_MARK_DATA             Data message
+       RXRPC_SKB_MARK_FINAL_ACK        Final ACK received for an incoming call
+       RXRPC_SKB_MARK_BUSY             Client call rejected as server busy
+       RXRPC_SKB_MARK_REMOTE_ABORT     Call aborted by peer
+       RXRPC_SKB_MARK_NET_ERROR        Network error detected
+       RXRPC_SKB_MARK_LOCAL_ERROR      Local error encountered
+       RXRPC_SKB_MARK_NEW_CALL         New incoming call awaiting acceptance
+       =============================== =======================================
+
+     The remote abort message can be probed with rxrpc_kernel_get_abort_code().
+     The two error messages can be probed with rxrpc_kernel_get_error_number().
+     A new call can be accepted with rxrpc_kernel_accept_call().
+
+     Data messages can have their contents extracted with the usual bunch of
+     socket buffer manipulation functions.  A data message can be determined to
+     be the last one in a sequence with rxrpc_kernel_is_data_last().  When a
+     data message has been used up, rxrpc_kernel_data_consumed() should be
+     called on it.
+
+     Messages should be handled to rxrpc_kernel_free_skb() to dispose of.  It
+     is possible to get extra refs on all types of message for later freeing,
+     but this may pin the state of a call until the message is finally freed.
+
+ (#) Accept an incoming call::
+
+       struct rxrpc_call *
+       rxrpc_kernel_accept_call(struct socket *sock,
+                                unsigned long user_call_ID);
+
+     This is used to accept an incoming call and to assign it a call ID.  This
+     function is similar to rxrpc_kernel_begin_call() and calls accepted must
+     be ended in the same way.
+
+     If this function is successful, an opaque reference to the RxRPC call is
+     returned.  The caller now holds a reference on this and it must be
+     properly ended.
+
+ (#) Reject an incoming call::
+
+       int rxrpc_kernel_reject_call(struct socket *sock);
+
+     This is used to reject the first incoming call on the socket's queue with
+     a BUSY message.  -ENODATA is returned if there were no incoming calls.
+     Other errors may be returned if the call had been aborted (-ECONNABORTED)
+     or had timed out (-ETIME).
+
+ (#) Allocate a null key for doing anonymous security::
+
+       struct key *rxrpc_get_null_key(const char *keyname);
+
+     This is used to allocate a null RxRPC key that can be used to indicate
+     anonymous security for a particular domain.
+
+ (#) Get the peer address of a call::
+
+       void rxrpc_kernel_get_peer(struct socket *sock, struct rxrpc_call *call,
+                                  struct sockaddr_rxrpc *_srx);
+
+     This is used to find the remote peer address of a call.
+
+ (#) Set the total transmit data size on a call::
+
+       void rxrpc_kernel_set_tx_length(struct socket *sock,
+                                       struct rxrpc_call *call,
+                                       s64 tx_total_len);
+
+     This sets the amount of data that the caller is intending to transmit on a
+     call.  It's intended to be used for setting the reply size as the request
+     size should be set when the call is begun.  tx_total_len may not be less
+     than zero.
+
+ (#) Get call RTT::
+
+       u64 rxrpc_kernel_get_rtt(struct socket *sock, struct rxrpc_call *call);
+
+     Get the RTT time to the peer in use by a call.  The value returned is in
+     nanoseconds.
+
+ (#) Check call still alive::
+
+       bool rxrpc_kernel_check_life(struct socket *sock,
+                                    struct rxrpc_call *call,
+                                    u32 *_life);
+       void rxrpc_kernel_probe_life(struct socket *sock,
+                                    struct rxrpc_call *call);
+
+     The first function passes back in ``*_life`` a number that is updated when
+     ACKs are received from the peer (notably including PING RESPONSE ACKs
+     which we can elicit by sending PING ACKs to see if the call still exists
+     on the server).  The caller should compare the numbers of two calls to see
+     if the call is still alive after waiting for a suitable interval.  It also
+     returns true as long as the call hasn't yet reached the completed state.
+
+     This allows the caller to work out if the server is still contactable and
+     if the call is still alive on the server while waiting for the server to
+     process a client operation.
+
+     The second function causes a ping ACK to be transmitted to try to provoke
+     the peer into responding, which would then cause the value returned by the
+     first function to change.  Note that this must be called in TASK_RUNNING
+     state.
+
+ (#) Get reply timestamp::
+
+       bool rxrpc_kernel_get_reply_time(struct socket *sock,
+                                        struct rxrpc_call *call,
+                                        ktime_t *_ts)
+
+     This allows the timestamp on the first DATA packet of the reply of a
+     client call to be queried, provided that it is still in the Rx ring.  If
+     successful, the timestamp will be stored into ``*_ts`` and true will be
+     returned; false will be returned otherwise.
+
+ (#) Get remote client epoch::
+
+       u32 rxrpc_kernel_get_epoch(struct socket *sock,
+                                  struct rxrpc_call *call)
+
+     This allows the epoch that's contained in packets of an incoming client
+     call to be queried.  This value is returned.  The function always
+     successful if the call is still in progress.  It shouldn't be called once
+     the call has expired.  Note that calling this on a local client call only
+     returns the local epoch.
+
+     This value can be used to determine if the remote client has been
+     restarted as it shouldn't change otherwise.
+
+ (#) Set the maxmimum lifespan on a call::
+
+       void rxrpc_kernel_set_max_life(struct socket *sock,
+                                      struct rxrpc_call *call,
+                                      unsigned long hard_timeout)
+
+     This sets the maximum lifespan on a call to hard_timeout (which is in
+     jiffies).  In the event of the timeout occurring, the call will be
+     aborted and -ETIME or -ETIMEDOUT will be returned.
+
+
+Configurable Parameters
+=======================
+
+The RxRPC protocol driver has a number of configurable parameters that can be
+adjusted through sysctls in /proc/net/rxrpc/:
+
+ (#) req_ack_delay
+
+     The amount of time in milliseconds after receiving a packet with the
+     request-ack flag set before we honour the flag and actually send the
+     requested ack.
+
+     Usually the other side won't stop sending packets until the advertised
+     reception window is full (to a maximum of 255 packets), so delaying the
+     ACK permits several packets to be ACK'd in one go.
+
+ (#) soft_ack_delay
+
+     The amount of time in milliseconds after receiving a new packet before we
+     generate a soft-ACK to tell the sender that it doesn't need to resend.
+
+ (#) idle_ack_delay
+
+     The amount of time in milliseconds after all the packets currently in the
+     received queue have been consumed before we generate a hard-ACK to tell
+     the sender it can free its buffers, assuming no other reason occurs that
+     we would send an ACK.
+
+ (#) resend_timeout
+
+     The amount of time in milliseconds after transmitting a packet before we
+     transmit it again, assuming no ACK is received from the receiver telling
+     us they got it.
+
+ (#) max_call_lifetime
+
+     The maximum amount of time in seconds that a call may be in progress
+     before we preemptively kill it.
+
+ (#) dead_call_expiry
+
+     The amount of time in seconds before we remove a dead call from the call
+     list.  Dead calls are kept around for a little while for the purpose of
+     repeating ACK and ABORT packets.
+
+ (#) connection_expiry
+
+     The amount of time in seconds after a connection was last used before we
+     remove it from the connection list.  While a connection is in existence,
+     it serves as a placeholder for negotiated security; when it is deleted,
+     the security must be renegotiated.
+
+ (#) transport_expiry
+
+     The amount of time in seconds after a transport was last used before we
+     remove it from the transport list.  While a transport is in existence, it
+     serves to anchor the peer data and keeps the connection ID counter.
+
+ (#) rxrpc_rx_window_size
+
+     The size of the receive window in packets.  This is the maximum number of
+     unconsumed received packets we're willing to hold in memory for any
+     particular call.
+
+ (#) rxrpc_rx_mtu
+
+     The maximum packet MTU size that we're willing to receive in bytes.  This
+     indicates to the peer whether we're willing to accept jumbo packets.
+
+ (#) rxrpc_rx_jumbo_max
+
+     The maximum number of packets that we're willing to accept in a jumbo
+     packet.  Non-terminal packets in a jumbo packet must contain a four byte
+     header plus exactly 1412 bytes of data.  The terminal packet must contain
+     a four byte header plus any amount of data.  In any event, a jumbo packet
+     may not exceed rxrpc_rx_mtu in size.
diff --git a/Documentation/networking/rxrpc.txt b/Documentation/networking/rxrpc.txt
deleted file mode 100644 (file)
index 180e07d..0000000
+++ /dev/null
@@ -1,1155 +0,0 @@
-                           ======================
-                           RxRPC NETWORK PROTOCOL
-                           ======================
-
-The RxRPC protocol driver provides a reliable two-phase transport on top of UDP
-that can be used to perform RxRPC remote operations.  This is done over sockets
-of AF_RXRPC family, using sendmsg() and recvmsg() with control data to send and
-receive data, aborts and errors.
-
-Contents of this document:
-
- (*) Overview.
-
- (*) RxRPC protocol summary.
-
- (*) AF_RXRPC driver model.
-
- (*) Control messages.
-
- (*) Socket options.
-
- (*) Security.
-
- (*) Example client usage.
-
- (*) Example server usage.
-
- (*) AF_RXRPC kernel interface.
-
- (*) Configurable parameters.
-
-
-========
-OVERVIEW
-========
-
-RxRPC is a two-layer protocol.  There is a session layer which provides
-reliable virtual connections using UDP over IPv4 (or IPv6) as the transport
-layer, but implements a real network protocol; and there's the presentation
-layer which renders structured data to binary blobs and back again using XDR
-(as does SunRPC):
-
-               +-------------+
-               | Application |
-               +-------------+
-               |     XDR     |         Presentation
-               +-------------+
-               |    RxRPC    |         Session
-               +-------------+
-               |     UDP     |         Transport
-               +-------------+
-
-
-AF_RXRPC provides:
-
- (1) Part of an RxRPC facility for both kernel and userspace applications by
-     making the session part of it a Linux network protocol (AF_RXRPC).
-
- (2) A two-phase protocol.  The client transmits a blob (the request) and then
-     receives a blob (the reply), and the server receives the request and then
-     transmits the reply.
-
- (3) Retention of the reusable bits of the transport system set up for one call
-     to speed up subsequent calls.
-
- (4) A secure protocol, using the Linux kernel's key retention facility to
-     manage security on the client end.  The server end must of necessity be
-     more active in security negotiations.
-
-AF_RXRPC does not provide XDR marshalling/presentation facilities.  That is
-left to the application.  AF_RXRPC only deals in blobs.  Even the operation ID
-is just the first four bytes of the request blob, and as such is beyond the
-kernel's interest.
-
-
-Sockets of AF_RXRPC family are:
-
- (1) created as type SOCK_DGRAM;
-
- (2) provided with a protocol of the type of underlying transport they're going
-     to use - currently only PF_INET is supported.
-
-
-The Andrew File System (AFS) is an example of an application that uses this and
-that has both kernel (filesystem) and userspace (utility) components.
-
-
-======================
-RXRPC PROTOCOL SUMMARY
-======================
-
-An overview of the RxRPC protocol:
-
- (*) RxRPC sits on top of another networking protocol (UDP is the only option
-     currently), and uses this to provide network transport.  UDP ports, for
-     example, provide transport endpoints.
-
- (*) RxRPC supports multiple virtual "connections" from any given transport
-     endpoint, thus allowing the endpoints to be shared, even to the same
-     remote endpoint.
-
- (*) Each connection goes to a particular "service".  A connection may not go
-     to multiple services.  A service may be considered the RxRPC equivalent of
-     a port number.  AF_RXRPC permits multiple services to share an endpoint.
-
- (*) Client-originating packets are marked, thus a transport endpoint can be
-     shared between client and server connections (connections have a
-     direction).
-
- (*) Up to a billion connections may be supported concurrently between one
-     local transport endpoint and one service on one remote endpoint.  An RxRPC
-     connection is described by seven numbers:
-
-       Local address   }
-       Local port      } Transport (UDP) address
-       Remote address  }
-       Remote port     }
-       Direction
-       Connection ID
-       Service ID
-
- (*) Each RxRPC operation is a "call".  A connection may make up to four
-     billion calls, but only up to four calls may be in progress on a
-     connection at any one time.
-
- (*) Calls are two-phase and asymmetric: the client sends its request data,
-     which the service receives; then the service transmits the reply data
-     which the client receives.
-
- (*) The data blobs are of indefinite size, the end of a phase is marked with a
-     flag in the packet.  The number of packets of data making up one blob may
-     not exceed 4 billion, however, as this would cause the sequence number to
-     wrap.
-
- (*) The first four bytes of the request data are the service operation ID.
-
- (*) Security is negotiated on a per-connection basis.  The connection is
-     initiated by the first data packet on it arriving.  If security is
-     requested, the server then issues a "challenge" and then the client
-     replies with a "response".  If the response is successful, the security is
-     set for the lifetime of that connection, and all subsequent calls made
-     upon it use that same security.  In the event that the server lets a
-     connection lapse before the client, the security will be renegotiated if
-     the client uses the connection again.
-
- (*) Calls use ACK packets to handle reliability.  Data packets are also
-     explicitly sequenced per call.
-
- (*) There are two types of positive acknowledgment: hard-ACKs and soft-ACKs.
-     A hard-ACK indicates to the far side that all the data received to a point
-     has been received and processed; a soft-ACK indicates that the data has
-     been received but may yet be discarded and re-requested.  The sender may
-     not discard any transmittable packets until they've been hard-ACK'd.
-
- (*) Reception of a reply data packet implicitly hard-ACK's all the data
-     packets that make up the request.
-
- (*) An call is complete when the request has been sent, the reply has been
-     received and the final hard-ACK on the last packet of the reply has
-     reached the server.
-
- (*) An call may be aborted by either end at any time up to its completion.
-
-
-=====================
-AF_RXRPC DRIVER MODEL
-=====================
-
-About the AF_RXRPC driver:
-
- (*) The AF_RXRPC protocol transparently uses internal sockets of the transport
-     protocol to represent transport endpoints.
-
- (*) AF_RXRPC sockets map onto RxRPC connection bundles.  Actual RxRPC
-     connections are handled transparently.  One client socket may be used to
-     make multiple simultaneous calls to the same service.  One server socket
-     may handle calls from many clients.
-
- (*) Additional parallel client connections will be initiated to support extra
-     concurrent calls, up to a tunable limit.
-
- (*) Each connection is retained for a certain amount of time [tunable] after
-     the last call currently using it has completed in case a new call is made
-     that could reuse it.
-
- (*) Each internal UDP socket is retained [tunable] for a certain amount of
-     time [tunable] after the last connection using it discarded, in case a new
-     connection is made that could use it.
-
- (*) A client-side connection is only shared between calls if they have have
-     the same key struct describing their security (and assuming the calls
-     would otherwise share the connection).  Non-secured calls would also be
-     able to share connections with each other.
-
- (*) A server-side connection is shared if the client says it is.
-
- (*) ACK'ing is handled by the protocol driver automatically, including ping
-     replying.
-
- (*) SO_KEEPALIVE automatically pings the other side to keep the connection
-     alive [TODO].
-
- (*) If an ICMP error is received, all calls affected by that error will be
-     aborted with an appropriate network error passed through recvmsg().
-
-
-Interaction with the user of the RxRPC socket:
-
- (*) A socket is made into a server socket by binding an address with a
-     non-zero service ID.
-
- (*) In the client, sending a request is achieved with one or more sendmsgs,
-     followed by the reply being received with one or more recvmsgs.
-
- (*) The first sendmsg for a request to be sent from a client contains a tag to
-     be used in all other sendmsgs or recvmsgs associated with that call.  The
-     tag is carried in the control data.
-
- (*) connect() is used to supply a default destination address for a client
-     socket.  This may be overridden by supplying an alternate address to the
-     first sendmsg() of a call (struct msghdr::msg_name).
-
- (*) If connect() is called on an unbound client, a random local port will
-     bound before the operation takes place.
-
- (*) A server socket may also be used to make client calls.  To do this, the
-     first sendmsg() of the call must specify the target address.  The server's
-     transport endpoint is used to send the packets.
-
- (*) Once the application has received the last message associated with a call,
-     the tag is guaranteed not to be seen again, and so it can be used to pin
-     client resources.  A new call can then be initiated with the same tag
-     without fear of interference.
-
- (*) In the server, a request is received with one or more recvmsgs, then the
-     the reply is transmitted with one or more sendmsgs, and then the final ACK
-     is received with a last recvmsg.
-
- (*) When sending data for a call, sendmsg is given MSG_MORE if there's more
-     data to come on that call.
-
- (*) When receiving data for a call, recvmsg flags MSG_MORE if there's more
-     data to come for that call.
-
- (*) When receiving data or messages for a call, MSG_EOR is flagged by recvmsg
-     to indicate the terminal message for that call.
-
- (*) A call may be aborted by adding an abort control message to the control
-     data.  Issuing an abort terminates the kernel's use of that call's tag.
-     Any messages waiting in the receive queue for that call will be discarded.
-
- (*) Aborts, busy notifications and challenge packets are delivered by recvmsg,
-     and control data messages will be set to indicate the context.  Receiving
-     an abort or a busy message terminates the kernel's use of that call's tag.
-
- (*) The control data part of the msghdr struct is used for a number of things:
-
-     (*) The tag of the intended or affected call.
-
-     (*) Sending or receiving errors, aborts and busy notifications.
-
-     (*) Notifications of incoming calls.
-
-     (*) Sending debug requests and receiving debug replies [TODO].
-
- (*) When the kernel has received and set up an incoming call, it sends a
-     message to server application to let it know there's a new call awaiting
-     its acceptance [recvmsg reports a special control message].  The server
-     application then uses sendmsg to assign a tag to the new call.  Once that
-     is done, the first part of the request data will be delivered by recvmsg.
-
- (*) The server application has to provide the server socket with a keyring of
-     secret keys corresponding to the security types it permits.  When a secure
-     connection is being set up, the kernel looks up the appropriate secret key
-     in the keyring and then sends a challenge packet to the client and
-     receives a response packet.  The kernel then checks the authorisation of
-     the packet and either aborts the connection or sets up the security.
-
- (*) The name of the key a client will use to secure its communications is
-     nominated by a socket option.
-
-
-Notes on sendmsg:
-
- (*) MSG_WAITALL can be set to tell sendmsg to ignore signals if the peer is
-     making progress at accepting packets within a reasonable time such that we
-     manage to queue up all the data for transmission.  This requires the
-     client to accept at least one packet per 2*RTT time period.
-
-     If this isn't set, sendmsg() will return immediately, either returning
-     EINTR/ERESTARTSYS if nothing was consumed or returning the amount of data
-     consumed.
-
-
-Notes on recvmsg:
-
- (*) If there's a sequence of data messages belonging to a particular call on
-     the receive queue, then recvmsg will keep working through them until:
-
-     (a) it meets the end of that call's received data,
-
-     (b) it meets a non-data message,
-
-     (c) it meets a message belonging to a different call, or
-
-     (d) it fills the user buffer.
-
-     If recvmsg is called in blocking mode, it will keep sleeping, awaiting the
-     reception of further data, until one of the above four conditions is met.
-
- (2) MSG_PEEK operates similarly, but will return immediately if it has put any
-     data in the buffer rather than sleeping until it can fill the buffer.
-
- (3) If a data message is only partially consumed in filling a user buffer,
-     then the remainder of that message will be left on the front of the queue
-     for the next taker.  MSG_TRUNC will never be flagged.
-
- (4) If there is more data to be had on a call (it hasn't copied the last byte
-     of the last data message in that phase yet), then MSG_MORE will be
-     flagged.
-
-
-================
-CONTROL MESSAGES
-================
-
-AF_RXRPC makes use of control messages in sendmsg() and recvmsg() to multiplex
-calls, to invoke certain actions and to report certain conditions.  These are:
-
-       MESSAGE ID              SRT DATA        MEANING
-       ======================= === =========== ===============================
-       RXRPC_USER_CALL_ID      sr- User ID     App's call specifier
-       RXRPC_ABORT             srt Abort code  Abort code to issue/received
-       RXRPC_ACK               -rt n/a         Final ACK received
-       RXRPC_NET_ERROR         -rt error num   Network error on call
-       RXRPC_BUSY              -rt n/a         Call rejected (server busy)
-       RXRPC_LOCAL_ERROR       -rt error num   Local error encountered
-       RXRPC_NEW_CALL          -r- n/a         New call received
-       RXRPC_ACCEPT            s-- n/a         Accept new call
-       RXRPC_EXCLUSIVE_CALL    s-- n/a         Make an exclusive client call
-       RXRPC_UPGRADE_SERVICE   s-- n/a         Client call can be upgraded
-       RXRPC_TX_LENGTH         s-- data len    Total length of Tx data
-
-       (SRT = usable in Sendmsg / delivered by Recvmsg / Terminal message)
-
- (*) RXRPC_USER_CALL_ID
-
-     This is used to indicate the application's call ID.  It's an unsigned long
-     that the app specifies in the client by attaching it to the first data
-     message or in the server by passing it in association with an RXRPC_ACCEPT
-     message.  recvmsg() passes it in conjunction with all messages except
-     those of the RXRPC_NEW_CALL message.
-
- (*) RXRPC_ABORT
-
-     This is can be used by an application to abort a call by passing it to
-     sendmsg, or it can be delivered by recvmsg to indicate a remote abort was
-     received.  Either way, it must be associated with an RXRPC_USER_CALL_ID to
-     specify the call affected.  If an abort is being sent, then error EBADSLT
-     will be returned if there is no call with that user ID.
-
- (*) RXRPC_ACK
-
-     This is delivered to a server application to indicate that the final ACK
-     of a call was received from the client.  It will be associated with an
-     RXRPC_USER_CALL_ID to indicate the call that's now complete.
-
- (*) RXRPC_NET_ERROR
-
-     This is delivered to an application to indicate that an ICMP error message
-     was encountered in the process of trying to talk to the peer.  An
-     errno-class integer value will be included in the control message data
-     indicating the problem, and an RXRPC_USER_CALL_ID will indicate the call
-     affected.
-
- (*) RXRPC_BUSY
-
-     This is delivered to a client application to indicate that a call was
-     rejected by the server due to the server being busy.  It will be
-     associated with an RXRPC_USER_CALL_ID to indicate the rejected call.
-
- (*) RXRPC_LOCAL_ERROR
-
-     This is delivered to an application to indicate that a local error was
-     encountered and that a call has been aborted because of it.  An
-     errno-class integer value will be included in the control message data
-     indicating the problem, and an RXRPC_USER_CALL_ID will indicate the call
-     affected.
-
- (*) RXRPC_NEW_CALL
-
-     This is delivered to indicate to a server application that a new call has
-     arrived and is awaiting acceptance.  No user ID is associated with this,
-     as a user ID must subsequently be assigned by doing an RXRPC_ACCEPT.
-
- (*) RXRPC_ACCEPT
-
-     This is used by a server application to attempt to accept a call and
-     assign it a user ID.  It should be associated with an RXRPC_USER_CALL_ID
-     to indicate the user ID to be assigned.  If there is no call to be
-     accepted (it may have timed out, been aborted, etc.), then sendmsg will
-     return error ENODATA.  If the user ID is already in use by another call,
-     then error EBADSLT will be returned.
-
- (*) RXRPC_EXCLUSIVE_CALL
-
-     This is used to indicate that a client call should be made on a one-off
-     connection.  The connection is discarded once the call has terminated.
-
- (*) RXRPC_UPGRADE_SERVICE
-
-     This is used to make a client call to probe if the specified service ID
-     may be upgraded by the server.  The caller must check msg_name returned to
-     recvmsg() for the service ID actually in use.  The operation probed must
-     be one that takes the same arguments in both services.
-
-     Once this has been used to establish the upgrade capability (or lack
-     thereof) of the server, the service ID returned should be used for all
-     future communication to that server and RXRPC_UPGRADE_SERVICE should no
-     longer be set.
-
- (*) RXRPC_TX_LENGTH
-
-     This is used to inform the kernel of the total amount of data that is
-     going to be transmitted by a call (whether in a client request or a
-     service response).  If given, it allows the kernel to encrypt from the
-     userspace buffer directly to the packet buffers, rather than copying into
-     the buffer and then encrypting in place.  This may only be given with the
-     first sendmsg() providing data for a call.  EMSGSIZE will be generated if
-     the amount of data actually given is different.
-
-     This takes a parameter of __s64 type that indicates how much will be
-     transmitted.  This may not be less than zero.
-
-The symbol RXRPC__SUPPORTED is defined as one more than the highest control
-message type supported.  At run time this can be queried by means of the
-RXRPC_SUPPORTED_CMSG socket option (see below).
-
-
-==============
-SOCKET OPTIONS
-==============
-
-AF_RXRPC sockets support a few socket options at the SOL_RXRPC level:
-
- (*) RXRPC_SECURITY_KEY
-
-     This is used to specify the description of the key to be used.  The key is
-     extracted from the calling process's keyrings with request_key() and
-     should be of "rxrpc" type.
-
-     The optval pointer points to the description string, and optlen indicates
-     how long the string is, without the NUL terminator.
-
- (*) RXRPC_SECURITY_KEYRING
-
-     Similar to above but specifies a keyring of server secret keys to use (key
-     type "keyring").  See the "Security" section.
-
- (*) RXRPC_EXCLUSIVE_CONNECTION
-
-     This is used to request that new connections should be used for each call
-     made subsequently on this socket.  optval should be NULL and optlen 0.
-
- (*) RXRPC_MIN_SECURITY_LEVEL
-
-     This is used to specify the minimum security level required for calls on
-     this socket.  optval must point to an int containing one of the following
-     values:
-
-     (a) RXRPC_SECURITY_PLAIN
-
-        Encrypted checksum only.
-
-     (b) RXRPC_SECURITY_AUTH
-
-        Encrypted checksum plus packet padded and first eight bytes of packet
-        encrypted - which includes the actual packet length.
-
-     (c) RXRPC_SECURITY_ENCRYPTED
-
-        Encrypted checksum plus entire packet padded and encrypted, including
-        actual packet length.
-
- (*) RXRPC_UPGRADEABLE_SERVICE
-
-     This is used to indicate that a service socket with two bindings may
-     upgrade one bound service to the other if requested by the client.  optval
-     must point to an array of two unsigned short ints.  The first is the
-     service ID to upgrade from and the second the service ID to upgrade to.
-
- (*) RXRPC_SUPPORTED_CMSG
-
-     This is a read-only option that writes an int into the buffer indicating
-     the highest control message type supported.
-
-
-========
-SECURITY
-========
-
-Currently, only the kerberos 4 equivalent protocol has been implemented
-(security index 2 - rxkad).  This requires the rxkad module to be loaded and,
-on the client, tickets of the appropriate type to be obtained from the AFS
-kaserver or the kerberos server and installed as "rxrpc" type keys.  This is
-normally done using the klog program.  An example simple klog program can be
-found at:
-
-       http://people.redhat.com/~dhowells/rxrpc/klog.c
-
-The payload provided to add_key() on the client should be of the following
-form:
-
-       struct rxrpc_key_sec2_v1 {
-               uint16_t        security_index; /* 2 */
-               uint16_t        ticket_length;  /* length of ticket[] */
-               uint32_t        expiry;         /* time at which expires */
-               uint8_t         kvno;           /* key version number */
-               uint8_t         __pad[3];
-               uint8_t         session_key[8]; /* DES session key */
-               uint8_t         ticket[0];      /* the encrypted ticket */
-       };
-
-Where the ticket blob is just appended to the above structure.
-
-
-For the server, keys of type "rxrpc_s" must be made available to the server.
-They have a description of "<serviceID>:<securityIndex>" (eg: "52:2" for an
-rxkad key for the AFS VL service).  When such a key is created, it should be
-given the server's secret key as the instantiation data (see the example
-below).
-
-       add_key("rxrpc_s", "52:2", secret_key, 8, keyring);
-
-A keyring is passed to the server socket by naming it in a sockopt.  The server
-socket then looks the server secret keys up in this keyring when secure
-incoming connections are made.  This can be seen in an example program that can
-be found at:
-
-       http://people.redhat.com/~dhowells/rxrpc/listen.c
-
-
-====================
-EXAMPLE CLIENT USAGE
-====================
-
-A client would issue an operation by:
-
- (1) An RxRPC socket is set up by:
-
-       client = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);
-
-     Where the third parameter indicates the protocol family of the transport
-     socket used - usually IPv4 but it can also be IPv6 [TODO].
-
- (2) A local address can optionally be bound:
-
-       struct sockaddr_rxrpc srx = {
-               .srx_family     = AF_RXRPC,
-               .srx_service    = 0,  /* we're a client */
-               .transport_type = SOCK_DGRAM,   /* type of transport socket */
-               .transport.sin_family   = AF_INET,
-               .transport.sin_port     = htons(7000), /* AFS callback */
-               .transport.sin_address  = 0,  /* all local interfaces */
-       };
-       bind(client, &srx, sizeof(srx));
-
-     This specifies the local UDP port to be used.  If not given, a random
-     non-privileged port will be used.  A UDP port may be shared between
-     several unrelated RxRPC sockets.  Security is handled on a basis of
-     per-RxRPC virtual connection.
-
- (3) The security is set:
-
-       const char *key = "AFS:cambridge.redhat.com";
-       setsockopt(client, SOL_RXRPC, RXRPC_SECURITY_KEY, key, strlen(key));
-
-     This issues a request_key() to get the key representing the security
-     context.  The minimum security level can be set:
-
-       unsigned int sec = RXRPC_SECURITY_ENCRYPTED;
-       setsockopt(client, SOL_RXRPC, RXRPC_MIN_SECURITY_LEVEL,
-                  &sec, sizeof(sec));
-
- (4) The server to be contacted can then be specified (alternatively this can
-     be done through sendmsg):
-
-       struct sockaddr_rxrpc srx = {
-               .srx_family     = AF_RXRPC,
-               .srx_service    = VL_SERVICE_ID,
-               .transport_type = SOCK_DGRAM,   /* type of transport socket */
-               .transport.sin_family   = AF_INET,
-               .transport.sin_port     = htons(7005), /* AFS volume manager */
-               .transport.sin_address  = ...,
-       };
-       connect(client, &srx, sizeof(srx));
-
- (5) The request data should then be posted to the server socket using a series
-     of sendmsg() calls, each with the following control message attached:
-
-       RXRPC_USER_CALL_ID      - specifies the user ID for this call
-
-     MSG_MORE should be set in msghdr::msg_flags on all but the last part of
-     the request.  Multiple requests may be made simultaneously.
-
-     An RXRPC_TX_LENGTH control message can also be specified on the first
-     sendmsg() call.
-
-     If a call is intended to go to a destination other than the default
-     specified through connect(), then msghdr::msg_name should be set on the
-     first request message of that call.
-
- (6) The reply data will then be posted to the server socket for recvmsg() to
-     pick up.  MSG_MORE will be flagged by recvmsg() if there's more reply data
-     for a particular call to be read.  MSG_EOR will be set on the terminal
-     read for a call.
-
-     All data will be delivered with the following control message attached:
-
-       RXRPC_USER_CALL_ID      - specifies the user ID for this call
-
-     If an abort or error occurred, this will be returned in the control data
-     buffer instead, and MSG_EOR will be flagged to indicate the end of that
-     call.
-
-A client may ask for a service ID it knows and ask that this be upgraded to a
-better service if one is available by supplying RXRPC_UPGRADE_SERVICE on the
-first sendmsg() of a call.  The client should then check srx_service in the
-msg_name filled in by recvmsg() when collecting the result.  srx_service will
-hold the same value as given to sendmsg() if the upgrade request was ignored by
-the service - otherwise it will be altered to indicate the service ID the
-server upgraded to.  Note that the upgraded service ID is chosen by the server.
-The caller has to wait until it sees the service ID in the reply before sending
-any more calls (further calls to the same destination will be blocked until the
-probe is concluded).
-
-
-====================
-EXAMPLE SERVER USAGE
-====================
-
-A server would be set up to accept operations in the following manner:
-
- (1) An RxRPC socket is created by:
-
-       server = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);
-
-     Where the third parameter indicates the address type of the transport
-     socket used - usually IPv4.
-
- (2) Security is set up if desired by giving the socket a keyring with server
-     secret keys in it:
-
-       keyring = add_key("keyring", "AFSkeys", NULL, 0,
-                         KEY_SPEC_PROCESS_KEYRING);
-
-       const char secret_key[8] = {
-               0xa7, 0x83, 0x8a, 0xcb, 0xc7, 0x83, 0xec, 0x94 };
-       add_key("rxrpc_s", "52:2", secret_key, 8, keyring);
-
-       setsockopt(server, SOL_RXRPC, RXRPC_SECURITY_KEYRING, "AFSkeys", 7);
-
-     The keyring can be manipulated after it has been given to the socket. This
-     permits the server to add more keys, replace keys, etc. while it is live.
-
- (3) A local address must then be bound:
-
-       struct sockaddr_rxrpc srx = {
-               .srx_family     = AF_RXRPC,
-               .srx_service    = VL_SERVICE_ID, /* RxRPC service ID */
-               .transport_type = SOCK_DGRAM,   /* type of transport socket */
-               .transport.sin_family   = AF_INET,
-               .transport.sin_port     = htons(7000), /* AFS callback */
-               .transport.sin_address  = 0,  /* all local interfaces */
-       };
-       bind(server, &srx, sizeof(srx));
-
-     More than one service ID may be bound to a socket, provided the transport
-     parameters are the same.  The limit is currently two.  To do this, bind()
-     should be called twice.
-
- (4) If service upgrading is required, first two service IDs must have been
-     bound and then the following option must be set:
-
-       unsigned short service_ids[2] = { from_ID, to_ID };
-       setsockopt(server, SOL_RXRPC, RXRPC_UPGRADEABLE_SERVICE,
-                  service_ids, sizeof(service_ids));
-
-     This will automatically upgrade connections on service from_ID to service
-     to_ID if they request it.  This will be reflected in msg_name obtained
-     through recvmsg() when the request data is delivered to userspace.
-
- (5) The server is then set to listen out for incoming calls:
-
-       listen(server, 100);
-
- (6) The kernel notifies the server of pending incoming connections by sending
-     it a message for each.  This is received with recvmsg() on the server
-     socket.  It has no data, and has a single dataless control message
-     attached:
-
-       RXRPC_NEW_CALL
-
-     The address that can be passed back by recvmsg() at this point should be
-     ignored since the call for which the message was posted may have gone by
-     the time it is accepted - in which case the first call still on the queue
-     will be accepted.
-
- (7) The server then accepts the new call by issuing a sendmsg() with two
-     pieces of control data and no actual data:
-
-       RXRPC_ACCEPT            - indicate connection acceptance
-       RXRPC_USER_CALL_ID      - specify user ID for this call
-
- (8) The first request data packet will then be posted to the server socket for
-     recvmsg() to pick up.  At that point, the RxRPC address for the call can
-     be read from the address fields in the msghdr struct.
-
-     Subsequent request data will be posted to the server socket for recvmsg()
-     to collect as it arrives.  All but the last piece of the request data will
-     be delivered with MSG_MORE flagged.
-
-     All data will be delivered with the following control message attached:
-
-       RXRPC_USER_CALL_ID      - specifies the user ID for this call
-
- (9) The reply data should then be posted to the server socket using a series
-     of sendmsg() calls, each with the following control messages attached:
-
-       RXRPC_USER_CALL_ID      - specifies the user ID for this call
-
-     MSG_MORE should be set in msghdr::msg_flags on all but the last message
-     for a particular call.
-
-(10) The final ACK from the client will be posted for retrieval by recvmsg()
-     when it is received.  It will take the form of a dataless message with two
-     control messages attached:
-
-       RXRPC_USER_CALL_ID      - specifies the user ID for this call
-       RXRPC_ACK               - indicates final ACK (no data)
-
-     MSG_EOR will be flagged to indicate that this is the final message for
-     this call.
-
-(11) Up to the point the final packet of reply data is sent, the call can be
-     aborted by calling sendmsg() with a dataless message with the following
-     control messages attached:
-
-       RXRPC_USER_CALL_ID      - specifies the user ID for this call
-       RXRPC_ABORT             - indicates abort code (4 byte data)
-
-     Any packets waiting in the socket's receive queue will be discarded if
-     this is issued.
-
-Note that all the communications for a particular service take place through
-the one server socket, using control messages on sendmsg() and recvmsg() to
-determine the call affected.
-
-
-=========================
-AF_RXRPC KERNEL INTERFACE
-=========================
-
-The AF_RXRPC module also provides an interface for use by in-kernel utilities
-such as the AFS filesystem.  This permits such a utility to:
-
- (1) Use different keys directly on individual client calls on one socket
-     rather than having to open a whole slew of sockets, one for each key it
-     might want to use.
-
- (2) Avoid having RxRPC call request_key() at the point of issue of a call or
-     opening of a socket.  Instead the utility is responsible for requesting a
-     key at the appropriate point.  AFS, for instance, would do this during VFS
-     operations such as open() or unlink().  The key is then handed through
-     when the call is initiated.
-
- (3) Request the use of something other than GFP_KERNEL to allocate memory.
-
- (4) Avoid the overhead of using the recvmsg() call.  RxRPC messages can be
-     intercepted before they get put into the socket Rx queue and the socket
-     buffers manipulated directly.
-
-To use the RxRPC facility, a kernel utility must still open an AF_RXRPC socket,
-bind an address as appropriate and listen if it's to be a server socket, but
-then it passes this to the kernel interface functions.
-
-The kernel interface functions are as follows:
-
- (*) Begin a new client call.
-
-       struct rxrpc_call *
-       rxrpc_kernel_begin_call(struct socket *sock,
-                               struct sockaddr_rxrpc *srx,
-                               struct key *key,
-                               unsigned long user_call_ID,
-                               s64 tx_total_len,
-                               gfp_t gfp,
-                               rxrpc_notify_rx_t notify_rx,
-                               bool upgrade,
-                               bool intr,
-                               unsigned int debug_id);
-
-     This allocates the infrastructure to make a new RxRPC call and assigns
-     call and connection numbers.  The call will be made on the UDP port that
-     the socket is bound to.  The call will go to the destination address of a
-     connected client socket unless an alternative is supplied (srx is
-     non-NULL).
-
-     If a key is supplied then this will be used to secure the call instead of
-     the key bound to the socket with the RXRPC_SECURITY_KEY sockopt.  Calls
-     secured in this way will still share connections if at all possible.
-
-     The user_call_ID is equivalent to that supplied to sendmsg() in the
-     control data buffer.  It is entirely feasible to use this to point to a
-     kernel data structure.
-
-     tx_total_len is the amount of data the caller is intending to transmit
-     with this call (or -1 if unknown at this point).  Setting the data size
-     allows the kernel to encrypt directly to the packet buffers, thereby
-     saving a copy.  The value may not be less than -1.
-
-     notify_rx is a pointer to a function to be called when events such as
-     incoming data packets or remote aborts happen.
-
-     upgrade should be set to true if a client operation should request that
-     the server upgrade the service to a better one.  The resultant service ID
-     is returned by rxrpc_kernel_recv_data().
-
-     intr should be set to true if the call should be interruptible.  If this
-     is not set, this function may not return until a channel has been
-     allocated; if it is set, the function may return -ERESTARTSYS.
-
-     debug_id is the call debugging ID to be used for tracing.  This can be
-     obtained by atomically incrementing rxrpc_debug_id.
-
-     If this function is successful, an opaque reference to the RxRPC call is
-     returned.  The caller now holds a reference on this and it must be
-     properly ended.
-
- (*) End a client call.
-
-       void rxrpc_kernel_end_call(struct socket *sock,
-                                  struct rxrpc_call *call);
-
-     This is used to end a previously begun call.  The user_call_ID is expunged
-     from AF_RXRPC's knowledge and will not be seen again in association with
-     the specified call.
-
- (*) Send data through a call.
-
-       typedef void (*rxrpc_notify_end_tx_t)(struct sock *sk,
-                                             unsigned long user_call_ID,
-                                             struct sk_buff *skb);
-
-       int rxrpc_kernel_send_data(struct socket *sock,
-                                  struct rxrpc_call *call,
-                                  struct msghdr *msg,
-                                  size_t len,
-                                  rxrpc_notify_end_tx_t notify_end_rx);
-
-     This is used to supply either the request part of a client call or the
-     reply part of a server call.  msg.msg_iovlen and msg.msg_iov specify the
-     data buffers to be used.  msg_iov may not be NULL and must point
-     exclusively to in-kernel virtual addresses.  msg.msg_flags may be given
-     MSG_MORE if there will be subsequent data sends for this call.
-
-     The msg must not specify a destination address, control data or any flags
-     other than MSG_MORE.  len is the total amount of data to transmit.
-
-     notify_end_rx can be NULL or it can be used to specify a function to be
-     called when the call changes state to end the Tx phase.  This function is
-     called with the call-state spinlock held to prevent any reply or final ACK
-     from being delivered first.
-
- (*) Receive data from a call.
-
-       int rxrpc_kernel_recv_data(struct socket *sock,
-                                  struct rxrpc_call *call,
-                                  void *buf,
-                                  size_t size,
-                                  size_t *_offset,
-                                  bool want_more,
-                                  u32 *_abort,
-                                  u16 *_service)
-
-      This is used to receive data from either the reply part of a client call
-      or the request part of a service call.  buf and size specify how much
-      data is desired and where to store it.  *_offset is added on to buf and
-      subtracted from size internally; the amount copied into the buffer is
-      added to *_offset before returning.
-
-      want_more should be true if further data will be required after this is
-      satisfied and false if this is the last item of the receive phase.
-
-      There are three normal returns: 0 if the buffer was filled and want_more
-      was true; 1 if the buffer was filled, the last DATA packet has been
-      emptied and want_more was false; and -EAGAIN if the function needs to be
-      called again.
-
-      If the last DATA packet is processed but the buffer contains less than
-      the amount requested, EBADMSG is returned.  If want_more wasn't set, but
-      more data was available, EMSGSIZE is returned.
-
-      If a remote ABORT is detected, the abort code received will be stored in
-      *_abort and ECONNABORTED will be returned.
-
-      The service ID that the call ended up with is returned into *_service.
-      This can be used to see if a call got a service upgrade.
-
- (*) Abort a call.
-
-       void rxrpc_kernel_abort_call(struct socket *sock,
-                                    struct rxrpc_call *call,
-                                    u32 abort_code);
-
-     This is used to abort a call if it's still in an abortable state.  The
-     abort code specified will be placed in the ABORT message sent.
-
- (*) Intercept received RxRPC messages.
-
-       typedef void (*rxrpc_interceptor_t)(struct sock *sk,
-                                           unsigned long user_call_ID,
-                                           struct sk_buff *skb);
-
-       void
-       rxrpc_kernel_intercept_rx_messages(struct socket *sock,
-                                          rxrpc_interceptor_t interceptor);
-
-     This installs an interceptor function on the specified AF_RXRPC socket.
-     All messages that would otherwise wind up in the socket's Rx queue are
-     then diverted to this function.  Note that care must be taken to process
-     the messages in the right order to maintain DATA message sequentiality.
-
-     The interceptor function itself is provided with the address of the socket
-     and handling the incoming message, the ID assigned by the kernel utility
-     to the call and the socket buffer containing the message.
-
-     The skb->mark field indicates the type of message:
-
-       MARK                            MEANING
-       =============================== =======================================
-       RXRPC_SKB_MARK_DATA             Data message
-       RXRPC_SKB_MARK_FINAL_ACK        Final ACK received for an incoming call
-       RXRPC_SKB_MARK_BUSY             Client call rejected as server busy
-       RXRPC_SKB_MARK_REMOTE_ABORT     Call aborted by peer
-       RXRPC_SKB_MARK_NET_ERROR        Network error detected
-       RXRPC_SKB_MARK_LOCAL_ERROR      Local error encountered
-       RXRPC_SKB_MARK_NEW_CALL         New incoming call awaiting acceptance
-
-     The remote abort message can be probed with rxrpc_kernel_get_abort_code().
-     The two error messages can be probed with rxrpc_kernel_get_error_number().
-     A new call can be accepted with rxrpc_kernel_accept_call().
-
-     Data messages can have their contents extracted with the usual bunch of
-     socket buffer manipulation functions.  A data message can be determined to
-     be the last one in a sequence with rxrpc_kernel_is_data_last().  When a
-     data message has been used up, rxrpc_kernel_data_consumed() should be
-     called on it.
-
-     Messages should be handled to rxrpc_kernel_free_skb() to dispose of.  It
-     is possible to get extra refs on all types of message for later freeing,
-     but this may pin the state of a call until the message is finally freed.
-
- (*) Accept an incoming call.
-
-       struct rxrpc_call *
-       rxrpc_kernel_accept_call(struct socket *sock,
-                                unsigned long user_call_ID);
-
-     This is used to accept an incoming call and to assign it a call ID.  This
-     function is similar to rxrpc_kernel_begin_call() and calls accepted must
-     be ended in the same way.
-
-     If this function is successful, an opaque reference to the RxRPC call is
-     returned.  The caller now holds a reference on this and it must be
-     properly ended.
-
- (*) Reject an incoming call.
-
-       int rxrpc_kernel_reject_call(struct socket *sock);
-
-     This is used to reject the first incoming call on the socket's queue with
-     a BUSY message.  -ENODATA is returned if there were no incoming calls.
-     Other errors may be returned if the call had been aborted (-ECONNABORTED)
-     or had timed out (-ETIME).
-
- (*) Allocate a null key for doing anonymous security.
-
-       struct key *rxrpc_get_null_key(const char *keyname);
-
-     This is used to allocate a null RxRPC key that can be used to indicate
-     anonymous security for a particular domain.
-
- (*) Get the peer address of a call.
-
-       void rxrpc_kernel_get_peer(struct socket *sock, struct rxrpc_call *call,
-                                  struct sockaddr_rxrpc *_srx);
-
-     This is used to find the remote peer address of a call.
-
- (*) Set the total transmit data size on a call.
-
-       void rxrpc_kernel_set_tx_length(struct socket *sock,
-                                       struct rxrpc_call *call,
-                                       s64 tx_total_len);
-
-     This sets the amount of data that the caller is intending to transmit on a
-     call.  It's intended to be used for setting the reply size as the request
-     size should be set when the call is begun.  tx_total_len may not be less
-     than zero.
-
- (*) Get call RTT.
-
-       u64 rxrpc_kernel_get_rtt(struct socket *sock, struct rxrpc_call *call);
-
-     Get the RTT time to the peer in use by a call.  The value returned is in
-     nanoseconds.
-
- (*) Check call still alive.
-
-       bool rxrpc_kernel_check_life(struct socket *sock,
-                                    struct rxrpc_call *call,
-                                    u32 *_life);
-       void rxrpc_kernel_probe_life(struct socket *sock,
-                                    struct rxrpc_call *call);
-
-     The first function passes back in *_life a number that is updated when
-     ACKs are received from the peer (notably including PING RESPONSE ACKs
-     which we can elicit by sending PING ACKs to see if the call still exists
-     on the server).  The caller should compare the numbers of two calls to see
-     if the call is still alive after waiting for a suitable interval.  It also
-     returns true as long as the call hasn't yet reached the completed state.
-
-     This allows the caller to work out if the server is still contactable and
-     if the call is still alive on the server while waiting for the server to
-     process a client operation.
-
-     The second function causes a ping ACK to be transmitted to try to provoke
-     the peer into responding, which would then cause the value returned by the
-     first function to change.  Note that this must be called in TASK_RUNNING
-     state.
-
- (*) Get reply timestamp.
-
-       bool rxrpc_kernel_get_reply_time(struct socket *sock,
-                                        struct rxrpc_call *call,
-                                        ktime_t *_ts)
-
-     This allows the timestamp on the first DATA packet of the reply of a
-     client call to be queried, provided that it is still in the Rx ring.  If
-     successful, the timestamp will be stored into *_ts and true will be
-     returned; false will be returned otherwise.
-
- (*) Get remote client epoch.
-
-       u32 rxrpc_kernel_get_epoch(struct socket *sock,
-                                  struct rxrpc_call *call)
-
-     This allows the epoch that's contained in packets of an incoming client
-     call to be queried.  This value is returned.  The function always
-     successful if the call is still in progress.  It shouldn't be called once
-     the call has expired.  Note that calling this on a local client call only
-     returns the local epoch.
-
-     This value can be used to determine if the remote client has been
-     restarted as it shouldn't change otherwise.
-
- (*) Set the maxmimum lifespan on a call.
-
-       void rxrpc_kernel_set_max_life(struct socket *sock,
-                                      struct rxrpc_call *call,
-                                      unsigned long hard_timeout)
-
-     This sets the maximum lifespan on a call to hard_timeout (which is in
-     jiffies).  In the event of the timeout occurring, the call will be
-     aborted and -ETIME or -ETIMEDOUT will be returned.
-
-
-=======================
-CONFIGURABLE PARAMETERS
-=======================
-
-The RxRPC protocol driver has a number of configurable parameters that can be
-adjusted through sysctls in /proc/net/rxrpc/:
-
- (*) req_ack_delay
-
-     The amount of time in milliseconds after receiving a packet with the
-     request-ack flag set before we honour the flag and actually send the
-     requested ack.
-
-     Usually the other side won't stop sending packets until the advertised
-     reception window is full (to a maximum of 255 packets), so delaying the
-     ACK permits several packets to be ACK'd in one go.
-
- (*) soft_ack_delay
-
-     The amount of time in milliseconds after receiving a new packet before we
-     generate a soft-ACK to tell the sender that it doesn't need to resend.
-
- (*) idle_ack_delay
-
-     The amount of time in milliseconds after all the packets currently in the
-     received queue have been consumed before we generate a hard-ACK to tell
-     the sender it can free its buffers, assuming no other reason occurs that
-     we would send an ACK.
-
- (*) resend_timeout
-
-     The amount of time in milliseconds after transmitting a packet before we
-     transmit it again, assuming no ACK is received from the receiver telling
-     us they got it.
-
- (*) max_call_lifetime
-
-     The maximum amount of time in seconds that a call may be in progress
-     before we preemptively kill it.
-
- (*) dead_call_expiry
-
-     The amount of time in seconds before we remove a dead call from the call
-     list.  Dead calls are kept around for a little while for the purpose of
-     repeating ACK and ABORT packets.
-
- (*) connection_expiry
-
-     The amount of time in seconds after a connection was last used before we
-     remove it from the connection list.  While a connection is in existence,
-     it serves as a placeholder for negotiated security; when it is deleted,
-     the security must be renegotiated.
-
- (*) transport_expiry
-
-     The amount of time in seconds after a transport was last used before we
-     remove it from the transport list.  While a transport is in existence, it
-     serves to anchor the peer data and keeps the connection ID counter.
-
- (*) rxrpc_rx_window_size
-
-     The size of the receive window in packets.  This is the maximum number of
-     unconsumed received packets we're willing to hold in memory for any
-     particular call.
-
- (*) rxrpc_rx_mtu
-
-     The maximum packet MTU size that we're willing to receive in bytes.  This
-     indicates to the peer whether we're willing to accept jumbo packets.
-
- (*) rxrpc_rx_jumbo_max
-
-     The maximum number of packets that we're willing to accept in a jumbo
-     packet.  Non-terminal packets in a jumbo packet must contain a four byte
-     header plus exactly 1412 bytes of data.  The terminal packet must contain
-     a four byte header plus any amount of data.  In any event, a jumbo packet
-     may not exceed rxrpc_rx_mtu in size.
diff --git a/Documentation/networking/sctp.rst b/Documentation/networking/sctp.rst
new file mode 100644 (file)
index 0000000..9f4d9c8
--- /dev/null
@@ -0,0 +1,42 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================
+Linux Kernel SCTP
+=================
+
+This is the current BETA release of the Linux Kernel SCTP reference
+implementation.
+
+SCTP (Stream Control Transmission Protocol) is a IP based, message oriented,
+reliable transport protocol, with congestion control, support for
+transparent multi-homing, and multiple ordered streams of messages.
+RFC2960 defines the core protocol.  The IETF SIGTRAN working group originally
+developed the SCTP protocol and later handed the protocol over to the
+Transport Area (TSVWG) working group for the continued evolvement of SCTP as a
+general purpose transport.
+
+See the IETF website (http://www.ietf.org) for further documents on SCTP.
+See http://www.ietf.org/rfc/rfc2960.txt
+
+The initial project goal is to create an Linux kernel reference implementation
+of SCTP that is RFC 2960 compliant and provides an programming interface
+referred to as the  UDP-style API of the Sockets Extensions for SCTP, as
+proposed in IETF Internet-Drafts.
+
+Caveats
+=======
+
+- lksctp can be built as statically or as a module.  However, be aware that
+  module removal of lksctp is not yet a safe activity.
+
+- There is tentative support for IPv6, but most work has gone towards
+  implementation and testing lksctp on IPv4.
+
+
+For more information, please visit the lksctp project website:
+
+   http://www.sf.net/projects/lksctp
+
+Or contact the lksctp developers through the mailing list:
+
+   <linux-sctp@vger.kernel.org>
diff --git a/Documentation/networking/sctp.txt b/Documentation/networking/sctp.txt
deleted file mode 100644 (file)
index 97b810c..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-Linux Kernel SCTP 
-
-This is the current BETA release of the Linux Kernel SCTP reference
-implementation.  
-
-SCTP (Stream Control Transmission Protocol) is a IP based, message oriented,
-reliable transport protocol, with congestion control, support for
-transparent multi-homing, and multiple ordered streams of messages.
-RFC2960 defines the core protocol.  The IETF SIGTRAN working group originally
-developed the SCTP protocol and later handed the protocol over to the 
-Transport Area (TSVWG) working group for the continued evolvement of SCTP as a 
-general purpose transport.  
-
-See the IETF website (http://www.ietf.org) for further documents on SCTP. 
-See http://www.ietf.org/rfc/rfc2960.txt 
-
-The initial project goal is to create an Linux kernel reference implementation
-of SCTP that is RFC 2960 compliant and provides an programming interface 
-referred to as the  UDP-style API of the Sockets Extensions for SCTP, as 
-proposed in IETF Internet-Drafts.    
-
-Caveats:  
-
--lksctp can be built as statically or as a module.  However, be aware that 
-module removal of lksctp is not yet a safe activity.   
-
--There is tentative support for IPv6, but most work has gone towards 
-implementation and testing lksctp on IPv4.   
-
-
-For more information, please visit the lksctp project website:
-   http://www.sf.net/projects/lksctp
-
-Or contact the lksctp developers through the mailing list:
-   <linux-sctp@vger.kernel.org>
diff --git a/Documentation/networking/secid.rst b/Documentation/networking/secid.rst
new file mode 100644 (file)
index 0000000..b45141a
--- /dev/null
@@ -0,0 +1,20 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=================
+LSM/SeLinux secid
+=================
+
+flowi structure:
+
+The secid member in the flow structure is used in LSMs (e.g. SELinux) to indicate
+the label of the flow. This label of the flow is currently used in selecting
+matching labeled xfrm(s).
+
+If this is an outbound flow, the label is derived from the socket, if any, or
+the incoming packet this flow is being generated as a response to (e.g. tcp
+resets, timewait ack, etc.). It is also conceivable that the label could be
+derived from other sources such as process context, device, etc., in special
+cases, as may be appropriate.
+
+If this is an inbound flow, the label is derived from the IPSec security
+associations, if any, used by the packet.
diff --git a/Documentation/networking/secid.txt b/Documentation/networking/secid.txt
deleted file mode 100644 (file)
index 95ea067..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-flowi structure:
-
-The secid member in the flow structure is used in LSMs (e.g. SELinux) to indicate
-the label of the flow. This label of the flow is currently used in selecting
-matching labeled xfrm(s).
-
-If this is an outbound flow, the label is derived from the socket, if any, or
-the incoming packet this flow is being generated as a response to (e.g. tcp
-resets, timewait ack, etc.). It is also conceivable that the label could be
-derived from other sources such as process context, device, etc., in special
-cases, as may be appropriate.
-
-If this is an inbound flow, the label is derived from the IPSec security
-associations, if any, used by the packet.
diff --git a/Documentation/networking/seg6-sysctl.rst b/Documentation/networking/seg6-sysctl.rst
new file mode 100644 (file)
index 0000000..ec73e14
--- /dev/null
@@ -0,0 +1,26 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====================
+Seg6 Sysfs variables
+====================
+
+
+/proc/sys/net/conf/<iface>/seg6_* variables:
+============================================
+
+seg6_enabled - BOOL
+       Accept or drop SR-enabled IPv6 packets on this interface.
+
+       Relevant packets are those with SRH present and DA = local.
+
+       * 0 - disabled (default)
+       * not 0 - enabled
+
+seg6_require_hmac - INTEGER
+       Define HMAC policy for ingress SR-enabled packets on this interface.
+
+       * -1 - Ignore HMAC field
+       * 0 - Accept SR packets without HMAC, validate SR packets with HMAC
+       * 1 - Drop SR packets without HMAC, validate SR packets with HMAC
+
+       Default is 0.
diff --git a/Documentation/networking/seg6-sysctl.txt b/Documentation/networking/seg6-sysctl.txt
deleted file mode 100644 (file)
index bdbde23..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-/proc/sys/net/conf/<iface>/seg6_* variables:
-
-seg6_enabled - BOOL
-       Accept or drop SR-enabled IPv6 packets on this interface.
-
-       Relevant packets are those with SRH present and DA = local.
-
-       0 - disabled (default)
-       not 0 - enabled
-
-seg6_require_hmac - INTEGER
-       Define HMAC policy for ingress SR-enabled packets on this interface.
-
-       -1 - Ignore HMAC field
-       0 - Accept SR packets without HMAC, validate SR packets with HMAC
-       1 - Drop SR packets without HMAC, validate SR packets with HMAC
-
-       Default is 0.
diff --git a/Documentation/networking/skfp.rst b/Documentation/networking/skfp.rst
new file mode 100644 (file)
index 0000000..58f5481
--- /dev/null
@@ -0,0 +1,253 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: <isonum.txt>
+
+========================
+SysKonnect driver - SKFP
+========================
+
+|copy| Copyright 1998-2000 SysKonnect,
+
+skfp.txt created 11-May-2000
+
+Readme File for skfp.o v2.06
+
+
+.. This file contains
+
+   (1) OVERVIEW
+   (2) SUPPORTED ADAPTERS
+   (3) GENERAL INFORMATION
+   (4) INSTALLATION
+   (5) INCLUSION OF THE ADAPTER IN SYSTEM START
+   (6) TROUBLESHOOTING
+   (7) FUNCTION OF THE ADAPTER LEDS
+   (8) HISTORY
+
+
+1. Overview
+===========
+
+This README explains how to use the driver 'skfp' for Linux with your
+network adapter.
+
+Chapter 2: Contains a list of all network adapters that are supported by
+this driver.
+
+Chapter 3:
+          Gives some general information.
+
+Chapter 4: Describes common problems and solutions.
+
+Chapter 5: Shows the changed functionality of the adapter LEDs.
+
+Chapter 6: History of development.
+
+
+2. Supported adapters
+=====================
+
+The network driver 'skfp' supports the following network adapters:
+SysKonnect adapters:
+
+  - SK-5521 (SK-NET FDDI-UP)
+  - SK-5522 (SK-NET FDDI-UP DAS)
+  - SK-5541 (SK-NET FDDI-FP)
+  - SK-5543 (SK-NET FDDI-LP)
+  - SK-5544 (SK-NET FDDI-LP DAS)
+  - SK-5821 (SK-NET FDDI-UP64)
+  - SK-5822 (SK-NET FDDI-UP64 DAS)
+  - SK-5841 (SK-NET FDDI-FP64)
+  - SK-5843 (SK-NET FDDI-LP64)
+  - SK-5844 (SK-NET FDDI-LP64 DAS)
+
+Compaq adapters (not tested):
+
+  - Netelligent 100 FDDI DAS Fibre SC
+  - Netelligent 100 FDDI SAS Fibre SC
+  - Netelligent 100 FDDI DAS UTP
+  - Netelligent 100 FDDI SAS UTP
+  - Netelligent 100 FDDI SAS Fibre MIC
+
+
+3. General Information
+======================
+
+From v2.01 on, the driver is integrated in the linux kernel sources.
+Therefore, the installation is the same as for any other adapter
+supported by the kernel.
+
+Refer to the manual of your distribution about the installation
+of network adapters.
+
+Makes my life much easier :-)
+
+4. Troubleshooting
+==================
+
+If you run into problems during installation, check those items:
+
+Problem:
+         The FDDI adapter cannot be found by the driver.
+
+Reason:
+         Look in /proc/pci for the following entry:
+
+            'FDDI network controller: SysKonnect SK-FDDI-PCI ...'
+
+         If this entry exists, then the FDDI adapter has been
+         found by the system and should be able to be used.
+
+         If this entry does not exist or if the file '/proc/pci'
+         is not there, then you may have a hardware problem or PCI
+         support may not be enabled in your kernel.
+
+         The adapter can be checked using the diagnostic program
+         which is available from the SysKonnect web site:
+
+             www.syskonnect.de
+
+         Some COMPAQ machines have a problem with PCI under
+         Linux. This is described in the 'PCI howto' document
+         (included in some distributions or available from the
+         www, e.g. at 'www.linux.org') and no workaround is available.
+
+Problem:
+         You want to use your computer as a router between
+         multiple IP subnetworks (using multiple adapters), but
+         you cannot reach computers in other subnetworks.
+
+Reason:
+         Either the router's kernel is not configured for IP
+         forwarding or there is a problem with the routing table
+         and gateway configuration in at least one of the
+         computers.
+
+If your problem is not listed here, please contact our
+technical support for help.
+
+You can send email to: linux@syskonnect.de
+
+When contacting our technical support,
+please ensure that the following information is available:
+
+- System Manufacturer and Model
+- Boards in your system
+- Distribution
+- Kernel version
+
+
+5. Function of the Adapter LEDs
+===============================
+
+       The functionality of the LED's on the FDDI network adapters was
+       changed in SMT version v2.82. With this new SMT version, the yellow
+       LED works as a ring operational indicator. An active yellow LED
+       indicates that the ring is down. The green LED on the adapter now
+       works as a link indicator where an active GREEN LED indicates that
+       the respective port has a physical connection.
+
+       With versions of SMT prior to v2.82 a ring up was indicated if the
+       yellow LED was off while the green LED(s) showed the connection
+       status of the adapter. During a ring down the green LED was off and
+       the yellow LED was on.
+
+       All implementations indicate that a driver is not loaded if
+       all LEDs are off.
+
+
+6. History
+==========
+
+v2.06 (20000511) (In-Kernel version)
+    New features:
+
+       - 64 bit support
+       - new pci dma interface
+       - in kernel 2.3.99
+
+v2.05 (20000217) (In-Kernel version)
+    New features:
+
+       - Changes for 2.3.45 kernel
+
+v2.04 (20000207) (Standalone version)
+    New features:
+
+       - Added rx/tx byte counter
+
+v2.03 (20000111) (Standalone version)
+    Problems fixed:
+
+       - Fixed printk statements from v2.02
+
+v2.02 (991215) (Standalone version)
+    Problems fixed:
+
+       - Removed unnecessary output
+       - Fixed path for "printver.sh" in makefile
+
+v2.01 (991122) (In-Kernel version)
+    New features:
+
+       - Integration in Linux kernel sources
+       - Support for memory mapped I/O.
+
+v2.00 (991112)
+    New features:
+
+       - Full source released under GPL
+
+v1.05 (991023)
+    Problems fixed:
+
+       - Compilation with kernel version 2.2.13 failed
+
+v1.04 (990427)
+    Changes:
+
+       - New SMT module included, changing LED functionality
+
+    Problems fixed:
+
+       - Synchronization on SMP machines was buggy
+
+v1.03 (990325)
+    Problems fixed:
+
+       - Interrupt routing on SMP machines could be incorrect
+
+v1.02 (990310)
+    New features:
+
+       - Support for kernel versions 2.2.x added
+       - Kernel patch instead of private duplicate of kernel functions
+
+v1.01 (980812)
+    Problems fixed:
+
+       Connection hangup with telnet
+       Slow telnet connection
+
+v1.00 beta 01 (980507)
+    New features:
+
+       None.
+
+    Problems fixed:
+
+       None.
+
+    Known limitations:
+
+       - tar archive instead of standard package format (rpm).
+       - FDDI statistic is empty.
+       - not tested with 2.1.xx kernels
+       - integration in kernel not tested
+       - not tested simultaneously with FDDI adapters from other vendors.
+       - only X86 processors supported.
+       - SBA (Synchronous Bandwidth Allocator) parameters can
+         not be configured.
+       - does not work on some COMPAQ machines. See the PCI howto
+         document for details about this problem.
+       - data corruption with kernel versions below 2.0.33.
diff --git a/Documentation/networking/skfp.txt b/Documentation/networking/skfp.txt
deleted file mode 100644 (file)
index 203ec66..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-(C)Copyright 1998-2000 SysKonnect,
-===========================================================================
-
-skfp.txt created 11-May-2000
-
-Readme File for skfp.o v2.06
-
-
-This file contains
-(1) OVERVIEW
-(2) SUPPORTED ADAPTERS
-(3) GENERAL INFORMATION
-(4) INSTALLATION
-(5) INCLUSION OF THE ADAPTER IN SYSTEM START
-(6) TROUBLESHOOTING
-(7) FUNCTION OF THE ADAPTER LEDS
-(8) HISTORY
-
-===========================================================================
-
-
-
-(1) OVERVIEW
-============
-
-This README explains how to use the driver 'skfp' for Linux with your
-network adapter.
-
-Chapter 2: Contains a list of all network adapters that are supported by
-          this driver.
-
-Chapter 3: Gives some general information.
-
-Chapter 4: Describes common problems and solutions.
-
-Chapter 5: Shows the changed functionality of the adapter LEDs.
-
-Chapter 6: History of development.
-
-***
-
-
-(2) SUPPORTED ADAPTERS
-======================
-
-The network driver 'skfp' supports the following network adapters:
-SysKonnect adapters:
-  - SK-5521 (SK-NET FDDI-UP)
-  - SK-5522 (SK-NET FDDI-UP DAS)
-  - SK-5541 (SK-NET FDDI-FP)
-  - SK-5543 (SK-NET FDDI-LP)
-  - SK-5544 (SK-NET FDDI-LP DAS)
-  - SK-5821 (SK-NET FDDI-UP64)
-  - SK-5822 (SK-NET FDDI-UP64 DAS)
-  - SK-5841 (SK-NET FDDI-FP64)
-  - SK-5843 (SK-NET FDDI-LP64)
-  - SK-5844 (SK-NET FDDI-LP64 DAS)
-Compaq adapters (not tested):
-  - Netelligent 100 FDDI DAS Fibre SC
-  - Netelligent 100 FDDI SAS Fibre SC
-  - Netelligent 100 FDDI DAS UTP
-  - Netelligent 100 FDDI SAS UTP
-  - Netelligent 100 FDDI SAS Fibre MIC
-***
-
-
-(3) GENERAL INFORMATION
-=======================
-
-From v2.01 on, the driver is integrated in the linux kernel sources.
-Therefore, the installation is the same as for any other adapter
-supported by the kernel.
-Refer to the manual of your distribution about the installation
-of network adapters.
-Makes my life much easier :-)
-***
-
-
-(4) TROUBLESHOOTING
-===================
-
-If you run into problems during installation, check those items:
-
-Problem:  The FDDI adapter cannot be found by the driver.
-Reason:   Look in /proc/pci for the following entry:
-             'FDDI network controller: SysKonnect SK-FDDI-PCI ...'
-         If this entry exists, then the FDDI adapter has been
-         found by the system and should be able to be used.
-         If this entry does not exist or if the file '/proc/pci'
-         is not there, then you may have a hardware problem or PCI
-         support may not be enabled in your kernel.
-         The adapter can be checked using the diagnostic program
-         which is available from the SysKonnect web site:
-             www.syskonnect.de
-         Some COMPAQ machines have a problem with PCI under
-         Linux. This is described in the 'PCI howto' document
-         (included in some distributions or available from the
-         www, e.g. at 'www.linux.org') and no workaround is available.
-
-Problem:  You want to use your computer as a router between
-          multiple IP subnetworks (using multiple adapters), but
-         you cannot reach computers in other subnetworks.
-Reason:   Either the router's kernel is not configured for IP
-         forwarding or there is a problem with the routing table
-         and gateway configuration in at least one of the
-         computers.
-
-If your problem is not listed here, please contact our
-technical support for help. 
-You can send email to:
-  linux@syskonnect.de
-When contacting our technical support,
-please ensure that the following information is available:
-- System Manufacturer and Model
-- Boards in your system
-- Distribution
-- Kernel version
-
-***
-
-
-(5) FUNCTION OF THE ADAPTER LEDS
-================================
-
-        The functionality of the LED's on the FDDI network adapters was
-        changed in SMT version v2.82. With this new SMT version, the yellow
-        LED works as a ring operational indicator. An active yellow LED
-        indicates that the ring is down. The green LED on the adapter now
-        works as a link indicator where an active GREEN LED indicates that
-        the respective port has a physical connection.
-
-        With versions of SMT prior to v2.82 a ring up was indicated if the
-        yellow LED was off while the green LED(s) showed the connection
-        status of the adapter. During a ring down the green LED was off and
-        the yellow LED was on.
-
-        All implementations indicate that a driver is not loaded if
-        all LEDs are off.
-
-***
-
-
-(6) HISTORY
-===========
-
-v2.06 (20000511) (In-Kernel version)
-    New features:
-       - 64 bit support
-       - new pci dma interface
-       - in kernel 2.3.99
-
-v2.05 (20000217) (In-Kernel version)
-    New features:
-       - Changes for 2.3.45 kernel
-
-v2.04 (20000207) (Standalone version)
-    New features:
-       - Added rx/tx byte counter
-
-v2.03 (20000111) (Standalone version)
-    Problems fixed:
-       - Fixed printk statements from v2.02
-
-v2.02 (991215) (Standalone version)
-    Problems fixed:
-       - Removed unnecessary output
-       - Fixed path for "printver.sh" in makefile
-
-v2.01 (991122) (In-Kernel version)
-    New features:
-       - Integration in Linux kernel sources
-       - Support for memory mapped I/O.
-
-v2.00 (991112)
-    New features:
-       - Full source released under GPL
-
-v1.05 (991023)
-    Problems fixed:
-       - Compilation with kernel version 2.2.13 failed
-
-v1.04 (990427)
-    Changes:
-       - New SMT module included, changing LED functionality
-    Problems fixed:
-       - Synchronization on SMP machines was buggy
-
-v1.03 (990325)
-    Problems fixed:
-       - Interrupt routing on SMP machines could be incorrect
-
-v1.02 (990310)
-    New features:
-       - Support for kernel versions 2.2.x added
-       - Kernel patch instead of private duplicate of kernel functions
-
-v1.01 (980812)
-    Problems fixed:
-       Connection hangup with telnet
-       Slow telnet connection
-
-v1.00 beta 01 (980507)
-    New features:
-       None.
-    Problems fixed:
-       None.
-    Known limitations:
-        - tar archive instead of standard package format (rpm).
-       - FDDI statistic is empty.
-       - not tested with 2.1.xx kernels
-       - integration in kernel not tested
-       - not tested simultaneously with FDDI adapters from other vendors.
-       - only X86 processors supported.
-       - SBA (Synchronous Bandwidth Allocator) parameters can
-         not be configured.
-       - does not work on some COMPAQ machines. See the PCI howto
-         document for details about this problem.
-       - data corruption with kernel versions below 2.0.33.
-
-*** End of information file ***
index 10e1109..4edd0d3 100644 (file)
@@ -792,7 +792,7 @@ counters to indicate the ACK is skipped in which scenario. The ACK
 would only be skipped if the received packet is either a SYN packet or
 it has no data.
 
-.. _sysctl document: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
+.. _sysctl document: https://www.kernel.org/doc/Documentation/networking/ip-sysctl.rst
 
 * TcpExtTCPACKSkippedSynRecv
 
diff --git a/Documentation/networking/strparser.rst b/Documentation/networking/strparser.rst
new file mode 100644 (file)
index 0000000..6cab1f7
--- /dev/null
@@ -0,0 +1,240 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================
+Stream Parser (strparser)
+=========================
+
+Introduction
+============
+
+The stream parser (strparser) is a utility that parses messages of an
+application layer protocol running over a data stream. The stream
+parser works in conjunction with an upper layer in the kernel to provide
+kernel support for application layer messages. For instance, Kernel
+Connection Multiplexor (KCM) uses the Stream Parser to parse messages
+using a BPF program.
+
+The strparser works in one of two modes: receive callback or general
+mode.
+
+In receive callback mode, the strparser is called from the data_ready
+callback of a TCP socket. Messages are parsed and delivered as they are
+received on the socket.
+
+In general mode, a sequence of skbs are fed to strparser from an
+outside source. Message are parsed and delivered as the sequence is
+processed. This modes allows strparser to be applied to arbitrary
+streams of data.
+
+Interface
+=========
+
+The API includes a context structure, a set of callbacks, utility
+functions, and a data_ready function for receive callback mode. The
+callbacks include a parse_msg function that is called to perform
+parsing (e.g.  BPF parsing in case of KCM), and a rcv_msg function
+that is called when a full message has been completed.
+
+Functions
+=========
+
+     ::
+
+       strp_init(struct strparser *strp, struct sock *sk,
+               const struct strp_callbacks *cb)
+
+     Called to initialize a stream parser. strp is a struct of type
+     strparser that is allocated by the upper layer. sk is the TCP
+     socket associated with the stream parser for use with receive
+     callback mode; in general mode this is set to NULL. Callbacks
+     are called by the stream parser (the callbacks are listed below).
+
+     ::
+
+       void strp_pause(struct strparser *strp)
+
+     Temporarily pause a stream parser. Message parsing is suspended
+     and no new messages are delivered to the upper layer.
+
+     ::
+
+       void strp_unpause(struct strparser *strp)
+
+     Unpause a paused stream parser.
+
+     ::
+
+       void strp_stop(struct strparser *strp);
+
+     strp_stop is called to completely stop stream parser operations.
+     This is called internally when the stream parser encounters an
+     error, and it is called from the upper layer to stop parsing
+     operations.
+
+     ::
+
+       void strp_done(struct strparser *strp);
+
+     strp_done is called to release any resources held by the stream
+     parser instance. This must be called after the stream processor
+     has been stopped.
+
+     ::
+
+       int strp_process(struct strparser *strp, struct sk_buff *orig_skb,
+                        unsigned int orig_offset, size_t orig_len,
+                        size_t max_msg_size, long timeo)
+
+    strp_process is called in general mode for a stream parser to
+    parse an sk_buff. The number of bytes processed or a negative
+    error number is returned. Note that strp_process does not
+    consume the sk_buff. max_msg_size is maximum size the stream
+    parser will parse. timeo is timeout for completing a message.
+
+    ::
+
+       void strp_data_ready(struct strparser *strp);
+
+    The upper layer calls strp_tcp_data_ready when data is ready on
+    the lower socket for strparser to process. This should be called
+    from a data_ready callback that is set on the socket. Note that
+    maximum messages size is the limit of the receive socket
+    buffer and message timeout is the receive timeout for the socket.
+
+    ::
+
+       void strp_check_rcv(struct strparser *strp);
+
+    strp_check_rcv is called to check for new messages on the socket.
+    This is normally called at initialization of a stream parser
+    instance or after strp_unpause.
+
+Callbacks
+=========
+
+There are six callbacks:
+
+    ::
+
+       int (*parse_msg)(struct strparser *strp, struct sk_buff *skb);
+
+    parse_msg is called to determine the length of the next message
+    in the stream. The upper layer must implement this function. It
+    should parse the sk_buff as containing the headers for the
+    next application layer message in the stream.
+
+    The skb->cb in the input skb is a struct strp_msg. Only
+    the offset field is relevant in parse_msg and gives the offset
+    where the message starts in the skb.
+
+    The return values of this function are:
+
+    =========    ===========================================================
+    >0           indicates length of successfully parsed message
+    0            indicates more data must be received to parse the message
+    -ESTRPIPE    current message should not be processed by the
+                kernel, return control of the socket to userspace which
+                can proceed to read the messages itself
+    other < 0    Error in parsing, give control back to userspace
+                assuming that synchronization is lost and the stream
+                is unrecoverable (application expected to close TCP socket)
+    =========    ===========================================================
+
+    In the case that an error is returned (return value is less than
+    zero) and the parser is in receive callback mode, then it will set
+    the error on TCP socket and wake it up. If parse_msg returned
+    -ESTRPIPE and the stream parser had previously read some bytes for
+    the current message, then the error set on the attached socket is
+    ENODATA since the stream is unrecoverable in that case.
+
+    ::
+
+       void (*lock)(struct strparser *strp)
+
+    The lock callback is called to lock the strp structure when
+    the strparser is performing an asynchronous operation (such as
+    processing a timeout). In receive callback mode the default
+    function is to lock_sock for the associated socket. In general
+    mode the callback must be set appropriately.
+
+    ::
+
+       void (*unlock)(struct strparser *strp)
+
+    The unlock callback is called to release the lock obtained
+    by the lock callback. In receive callback mode the default
+    function is release_sock for the associated socket. In general
+    mode the callback must be set appropriately.
+
+    ::
+
+       void (*rcv_msg)(struct strparser *strp, struct sk_buff *skb);
+
+    rcv_msg is called when a full message has been received and
+    is queued. The callee must consume the sk_buff; it can
+    call strp_pause to prevent any further messages from being
+    received in rcv_msg (see strp_pause above). This callback
+    must be set.
+
+    The skb->cb in the input skb is a struct strp_msg. This
+    struct contains two fields: offset and full_len. Offset is
+    where the message starts in the skb, and full_len is the
+    the length of the message. skb->len - offset may be greater
+    then full_len since strparser does not trim the skb.
+
+    ::
+
+       int (*read_sock_done)(struct strparser *strp, int err);
+
+     read_sock_done is called when the stream parser is done reading
+     the TCP socket in receive callback mode. The stream parser may
+     read multiple messages in a loop and this function allows cleanup
+     to occur when exiting the loop. If the callback is not set (NULL
+     in strp_init) a default function is used.
+
+     ::
+
+       void (*abort_parser)(struct strparser *strp, int err);
+
+     This function is called when stream parser encounters an error
+     in parsing. The default function stops the stream parser and
+     sets the error in the socket if the parser is in receive callback
+     mode. The default function can be changed by setting the callback
+     to non-NULL in strp_init.
+
+Statistics
+==========
+
+Various counters are kept for each stream parser instance. These are in
+the strp_stats structure. strp_aggr_stats is a convenience structure for
+accumulating statistics for multiple stream parser instances.
+save_strp_stats and aggregate_strp_stats are helper functions to save
+and aggregate statistics.
+
+Message assembly limits
+=======================
+
+The stream parser provide mechanisms to limit the resources consumed by
+message assembly.
+
+A timer is set when assembly starts for a new message. In receive
+callback mode the message timeout is taken from rcvtime for the
+associated TCP socket. In general mode, the timeout is passed as an
+argument in strp_process. If the timer fires before assembly completes
+the stream parser is aborted and the ETIMEDOUT error is set on the TCP
+socket if in receive callback mode.
+
+In receive callback mode, message length is limited to the receive
+buffer size of the associated TCP socket. If the length returned by
+parse_msg is greater than the socket buffer size then the stream parser
+is aborted with EMSGSIZE error set on the TCP socket. Note that this
+makes the maximum size of receive skbuffs for a socket with a stream
+parser to be 2*sk_rcvbuf of the TCP socket.
+
+In general mode the message length limit is passed in as an argument
+to strp_process.
+
+Author
+======
+
+Tom Herbert (tom@quantonium.net)
diff --git a/Documentation/networking/strparser.txt b/Documentation/networking/strparser.txt
deleted file mode 100644 (file)
index a7d354d..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-Stream Parser (strparser)
-
-Introduction
-============
-
-The stream parser (strparser) is a utility that parses messages of an
-application layer protocol running over a data stream. The stream
-parser works in conjunction with an upper layer in the kernel to provide
-kernel support for application layer messages. For instance, Kernel
-Connection Multiplexor (KCM) uses the Stream Parser to parse messages
-using a BPF program.
-
-The strparser works in one of two modes: receive callback or general
-mode.
-
-In receive callback mode, the strparser is called from the data_ready
-callback of a TCP socket. Messages are parsed and delivered as they are
-received on the socket.
-
-In general mode, a sequence of skbs are fed to strparser from an
-outside source. Message are parsed and delivered as the sequence is
-processed. This modes allows strparser to be applied to arbitrary
-streams of data.
-
-Interface
-=========
-
-The API includes a context structure, a set of callbacks, utility
-functions, and a data_ready function for receive callback mode. The
-callbacks include a parse_msg function that is called to perform
-parsing (e.g.  BPF parsing in case of KCM), and a rcv_msg function
-that is called when a full message has been completed.
-
-Functions
-=========
-
-strp_init(struct strparser *strp, struct sock *sk,
-         const struct strp_callbacks *cb)
-
-     Called to initialize a stream parser. strp is a struct of type
-     strparser that is allocated by the upper layer. sk is the TCP
-     socket associated with the stream parser for use with receive
-     callback mode; in general mode this is set to NULL. Callbacks
-     are called by the stream parser (the callbacks are listed below).
-
-void strp_pause(struct strparser *strp)
-
-     Temporarily pause a stream parser. Message parsing is suspended
-     and no new messages are delivered to the upper layer.
-
-void strp_unpause(struct strparser *strp)
-
-     Unpause a paused stream parser.
-
-void strp_stop(struct strparser *strp);
-
-     strp_stop is called to completely stop stream parser operations.
-     This is called internally when the stream parser encounters an
-     error, and it is called from the upper layer to stop parsing
-     operations.
-
-void strp_done(struct strparser *strp);
-
-     strp_done is called to release any resources held by the stream
-     parser instance. This must be called after the stream processor
-     has been stopped.
-
-int strp_process(struct strparser *strp, struct sk_buff *orig_skb,
-                unsigned int orig_offset, size_t orig_len,
-                size_t max_msg_size, long timeo)
-
-    strp_process is called in general mode for a stream parser to
-    parse an sk_buff. The number of bytes processed or a negative
-    error number is returned. Note that strp_process does not
-    consume the sk_buff. max_msg_size is maximum size the stream
-    parser will parse. timeo is timeout for completing a message.
-
-void strp_data_ready(struct strparser *strp);
-
-    The upper layer calls strp_tcp_data_ready when data is ready on
-    the lower socket for strparser to process. This should be called
-    from a data_ready callback that is set on the socket. Note that
-    maximum messages size is the limit of the receive socket
-    buffer and message timeout is the receive timeout for the socket.
-
-void strp_check_rcv(struct strparser *strp);
-
-    strp_check_rcv is called to check for new messages on the socket.
-    This is normally called at initialization of a stream parser
-    instance or after strp_unpause.
-
-Callbacks
-=========
-
-There are six callbacks:
-
-int (*parse_msg)(struct strparser *strp, struct sk_buff *skb);
-
-    parse_msg is called to determine the length of the next message
-    in the stream. The upper layer must implement this function. It
-    should parse the sk_buff as containing the headers for the
-    next application layer message in the stream.
-
-    The skb->cb in the input skb is a struct strp_msg. Only
-    the offset field is relevant in parse_msg and gives the offset
-    where the message starts in the skb.
-
-    The return values of this function are:
-
-    >0 : indicates length of successfully parsed message
-    0  : indicates more data must be received to parse the message
-    -ESTRPIPE : current message should not be processed by the
-          kernel, return control of the socket to userspace which
-          can proceed to read the messages itself
-    other < 0 : Error in parsing, give control back to userspace
-          assuming that synchronization is lost and the stream
-          is unrecoverable (application expected to close TCP socket)
-
-    In the case that an error is returned (return value is less than
-    zero) and the parser is in receive callback mode, then it will set
-    the error on TCP socket and wake it up. If parse_msg returned
-    -ESTRPIPE and the stream parser had previously read some bytes for
-    the current message, then the error set on the attached socket is
-    ENODATA since the stream is unrecoverable in that case.
-
-void (*lock)(struct strparser *strp)
-
-    The lock callback is called to lock the strp structure when
-    the strparser is performing an asynchronous operation (such as
-    processing a timeout). In receive callback mode the default
-    function is to lock_sock for the associated socket. In general
-    mode the callback must be set appropriately.
-
-void (*unlock)(struct strparser *strp)
-
-    The unlock callback is called to release the lock obtained
-    by the lock callback. In receive callback mode the default
-    function is release_sock for the associated socket. In general
-    mode the callback must be set appropriately.
-
-void (*rcv_msg)(struct strparser *strp, struct sk_buff *skb);
-
-    rcv_msg is called when a full message has been received and
-    is queued. The callee must consume the sk_buff; it can
-    call strp_pause to prevent any further messages from being
-    received in rcv_msg (see strp_pause above). This callback
-    must be set.
-
-    The skb->cb in the input skb is a struct strp_msg. This
-    struct contains two fields: offset and full_len. Offset is
-    where the message starts in the skb, and full_len is the
-    the length of the message. skb->len - offset may be greater
-    then full_len since strparser does not trim the skb.
-
-int (*read_sock_done)(struct strparser *strp, int err);
-
-     read_sock_done is called when the stream parser is done reading
-     the TCP socket in receive callback mode. The stream parser may
-     read multiple messages in a loop and this function allows cleanup
-     to occur when exiting the loop. If the callback is not set (NULL
-     in strp_init) a default function is used.
-
-void (*abort_parser)(struct strparser *strp, int err);
-
-     This function is called when stream parser encounters an error
-     in parsing. The default function stops the stream parser and
-     sets the error in the socket if the parser is in receive callback
-     mode. The default function can be changed by setting the callback
-     to non-NULL in strp_init.
-
-Statistics
-==========
-
-Various counters are kept for each stream parser instance. These are in
-the strp_stats structure. strp_aggr_stats is a convenience structure for
-accumulating statistics for multiple stream parser instances.
-save_strp_stats and aggregate_strp_stats are helper functions to save
-and aggregate statistics.
-
-Message assembly limits
-=======================
-
-The stream parser provide mechanisms to limit the resources consumed by
-message assembly.
-
-A timer is set when assembly starts for a new message. In receive
-callback mode the message timeout is taken from rcvtime for the
-associated TCP socket. In general mode, the timeout is passed as an
-argument in strp_process. If the timer fires before assembly completes
-the stream parser is aborted and the ETIMEDOUT error is set on the TCP
-socket if in receive callback mode.
-
-In receive callback mode, message length is limited to the receive
-buffer size of the associated TCP socket. If the length returned by
-parse_msg is greater than the socket buffer size then the stream parser
-is aborted with EMSGSIZE error set on the TCP socket. Note that this
-makes the maximum size of receive skbuffs for a socket with a stream
-parser to be 2*sk_rcvbuf of the TCP socket.
-
-In general mode the message length limit is passed in as an argument
-to strp_process.
-
-Author
-======
-
-Tom Herbert (tom@quantonium.net)
-
diff --git a/Documentation/networking/switchdev.rst b/Documentation/networking/switchdev.rst
new file mode 100644 (file)
index 0000000..ddc3f35
--- /dev/null
@@ -0,0 +1,387 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+===============================================
+Ethernet switch device driver model (switchdev)
+===============================================
+
+Copyright |copy| 2014 Jiri Pirko <jiri@resnulli.us>
+
+Copyright |copy| 2014-2015 Scott Feldman <sfeldma@gmail.com>
+
+
+The Ethernet switch device driver model (switchdev) is an in-kernel driver
+model for switch devices which offload the forwarding (data) plane from the
+kernel.
+
+Figure 1 is a block diagram showing the components of the switchdev model for
+an example setup using a data-center-class switch ASIC chip.  Other setups
+with SR-IOV or soft switches, such as OVS, are possible.
+
+::
+
+
+                            User-space tools
+
+       user space                   |
+      +-------------------------------------------------------------------+
+       kernel                       | Netlink
+                                   |
+                    +--------------+-------------------------------+
+                    |         Network stack                        |
+                    |           (Linux)                            |
+                    |                                              |
+                    +----------------------------------------------+
+
+                          sw1p2     sw1p4     sw1p6
+                     sw1p1  +  sw1p3  +  sw1p5  +          eth1
+                       +    |    +    |    +    |            +
+                       |    |    |    |    |    |            |
+                    +--+----+----+----+----+----+---+  +-----+-----+
+                    |         Switch driver         |  |    mgmt   |
+                    |        (this document)        |  |   driver  |
+                    |                               |  |           |
+                    +--------------+----------------+  +-----------+
+                                   |
+       kernel                       | HW bus (eg PCI)
+      +-------------------------------------------------------------------+
+       hardware                     |
+                    +--------------+----------------+
+                    |         Switch device (sw1)   |
+                    |  +----+                       +--------+
+                    |  |    v offloaded data path   | mgmt port
+                    |  |    |                       |
+                    +--|----|----+----+----+----+---+
+                       |    |    |    |    |    |
+                       +    +    +    +    +    +
+                      p1   p2   p3   p4   p5   p6
+
+                            front-panel ports
+
+
+                                   Fig 1.
+
+
+Include Files
+-------------
+
+::
+
+    #include <linux/netdevice.h>
+    #include <net/switchdev.h>
+
+
+Configuration
+-------------
+
+Use "depends NET_SWITCHDEV" in driver's Kconfig to ensure switchdev model
+support is built for driver.
+
+
+Switch Ports
+------------
+
+On switchdev driver initialization, the driver will allocate and register a
+struct net_device (using register_netdev()) for each enumerated physical switch
+port, called the port netdev.  A port netdev is the software representation of
+the physical port and provides a conduit for control traffic to/from the
+controller (the kernel) and the network, as well as an anchor point for higher
+level constructs such as bridges, bonds, VLANs, tunnels, and L3 routers.  Using
+standard netdev tools (iproute2, ethtool, etc), the port netdev can also
+provide to the user access to the physical properties of the switch port such
+as PHY link state and I/O statistics.
+
+There is (currently) no higher-level kernel object for the switch beyond the
+port netdevs.  All of the switchdev driver ops are netdev ops or switchdev ops.
+
+A switch management port is outside the scope of the switchdev driver model.
+Typically, the management port is not participating in offloaded data plane and
+is loaded with a different driver, such as a NIC driver, on the management port
+device.
+
+Switch ID
+^^^^^^^^^
+
+The switchdev driver must implement the net_device operation
+ndo_get_port_parent_id for each port netdev, returning the same physical ID for
+each port of a switch. The ID must be unique between switches on the same
+system. The ID does not need to be unique between switches on different
+systems.
+
+The switch ID is used to locate ports on a switch and to know if aggregated
+ports belong to the same switch.
+
+Port Netdev Naming
+^^^^^^^^^^^^^^^^^^
+
+Udev rules should be used for port netdev naming, using some unique attribute
+of the port as a key, for example the port MAC address or the port PHYS name.
+Hard-coding of kernel netdev names within the driver is discouraged; let the
+kernel pick the default netdev name, and let udev set the final name based on a
+port attribute.
+
+Using port PHYS name (ndo_get_phys_port_name) for the key is particularly
+useful for dynamically-named ports where the device names its ports based on
+external configuration.  For example, if a physical 40G port is split logically
+into 4 10G ports, resulting in 4 port netdevs, the device can give a unique
+name for each port using port PHYS name.  The udev rule would be::
+
+    SUBSYSTEM=="net", ACTION=="add", ATTR{phys_switch_id}=="<phys_switch_id>", \
+           ATTR{phys_port_name}!="", NAME="swX$attr{phys_port_name}"
+
+Suggested naming convention is "swXpYsZ", where X is the switch name or ID, Y
+is the port name or ID, and Z is the sub-port name or ID.  For example, sw1p1s0
+would be sub-port 0 on port 1 on switch 1.
+
+Port Features
+^^^^^^^^^^^^^
+
+NETIF_F_NETNS_LOCAL
+
+If the switchdev driver (and device) only supports offloading of the default
+network namespace (netns), the driver should set this feature flag to prevent
+the port netdev from being moved out of the default netns.  A netns-aware
+driver/device would not set this flag and be responsible for partitioning
+hardware to preserve netns containment.  This means hardware cannot forward
+traffic from a port in one namespace to another port in another namespace.
+
+Port Topology
+^^^^^^^^^^^^^
+
+The port netdevs representing the physical switch ports can be organized into
+higher-level switching constructs.  The default construct is a standalone
+router port, used to offload L3 forwarding.  Two or more ports can be bonded
+together to form a LAG.  Two or more ports (or LAGs) can be bridged to bridge
+L2 networks.  VLANs can be applied to sub-divide L2 networks.  L2-over-L3
+tunnels can be built on ports.  These constructs are built using standard Linux
+tools such as the bridge driver, the bonding/team drivers, and netlink-based
+tools such as iproute2.
+
+The switchdev driver can know a particular port's position in the topology by
+monitoring NETDEV_CHANGEUPPER notifications.  For example, a port moved into a
+bond will see it's upper master change.  If that bond is moved into a bridge,
+the bond's upper master will change.  And so on.  The driver will track such
+movements to know what position a port is in in the overall topology by
+registering for netdevice events and acting on NETDEV_CHANGEUPPER.
+
+L2 Forwarding Offload
+---------------------
+
+The idea is to offload the L2 data forwarding (switching) path from the kernel
+to the switchdev device by mirroring bridge FDB entries down to the device.  An
+FDB entry is the {port, MAC, VLAN} tuple forwarding destination.
+
+To offloading L2 bridging, the switchdev driver/device should support:
+
+       - Static FDB entries installed on a bridge port
+       - Notification of learned/forgotten src mac/vlans from device
+       - STP state changes on the port
+       - VLAN flooding of multicast/broadcast and unknown unicast packets
+
+Static FDB Entries
+^^^^^^^^^^^^^^^^^^
+
+The switchdev driver should implement ndo_fdb_add, ndo_fdb_del and ndo_fdb_dump
+to support static FDB entries installed to the device.  Static bridge FDB
+entries are installed, for example, using iproute2 bridge cmd::
+
+       bridge fdb add ADDR dev DEV [vlan VID] [self]
+
+The driver should use the helper switchdev_port_fdb_xxx ops for ndo_fdb_xxx
+ops, and handle add/delete/dump of SWITCHDEV_OBJ_ID_PORT_FDB object using
+switchdev_port_obj_xxx ops.
+
+XXX: what should be done if offloading this rule to hardware fails (for
+example, due to full capacity in hardware tables) ?
+
+Note: by default, the bridge does not filter on VLAN and only bridges untagged
+traffic.  To enable VLAN support, turn on VLAN filtering::
+
+       echo 1 >/sys/class/net/<bridge>/bridge/vlan_filtering
+
+Notification of Learned/Forgotten Source MAC/VLANs
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The switch device will learn/forget source MAC address/VLAN on ingress packets
+and notify the switch driver of the mac/vlan/port tuples.  The switch driver,
+in turn, will notify the bridge driver using the switchdev notifier call::
+
+       err = call_switchdev_notifiers(val, dev, info, extack);
+
+Where val is SWITCHDEV_FDB_ADD when learning and SWITCHDEV_FDB_DEL when
+forgetting, and info points to a struct switchdev_notifier_fdb_info.  On
+SWITCHDEV_FDB_ADD, the bridge driver will install the FDB entry into the
+bridge's FDB and mark the entry as NTF_EXT_LEARNED.  The iproute2 bridge
+command will label these entries "offload"::
+
+       $ bridge fdb
+       52:54:00:12:35:01 dev sw1p1 master br0 permanent
+       00:02:00:00:02:00 dev sw1p1 master br0 offload
+       00:02:00:00:02:00 dev sw1p1 self
+       52:54:00:12:35:02 dev sw1p2 master br0 permanent
+       00:02:00:00:03:00 dev sw1p2 master br0 offload
+       00:02:00:00:03:00 dev sw1p2 self
+       33:33:00:00:00:01 dev eth0 self permanent
+       01:00:5e:00:00:01 dev eth0 self permanent
+       33:33:ff:00:00:00 dev eth0 self permanent
+       01:80:c2:00:00:0e dev eth0 self permanent
+       33:33:00:00:00:01 dev br0 self permanent
+       01:00:5e:00:00:01 dev br0 self permanent
+       33:33:ff:12:35:01 dev br0 self permanent
+
+Learning on the port should be disabled on the bridge using the bridge command::
+
+       bridge link set dev DEV learning off
+
+Learning on the device port should be enabled, as well as learning_sync::
+
+       bridge link set dev DEV learning on self
+       bridge link set dev DEV learning_sync on self
+
+Learning_sync attribute enables syncing of the learned/forgotten FDB entry to
+the bridge's FDB.  It's possible, but not optimal, to enable learning on the
+device port and on the bridge port, and disable learning_sync.
+
+To support learning, the driver implements switchdev op
+switchdev_port_attr_set for SWITCHDEV_ATTR_PORT_ID_{PRE}_BRIDGE_FLAGS.
+
+FDB Ageing
+^^^^^^^^^^
+
+The bridge will skip ageing FDB entries marked with NTF_EXT_LEARNED and it is
+the responsibility of the port driver/device to age out these entries.  If the
+port device supports ageing, when the FDB entry expires, it will notify the
+driver which in turn will notify the bridge with SWITCHDEV_FDB_DEL.  If the
+device does not support ageing, the driver can simulate ageing using a
+garbage collection timer to monitor FDB entries.  Expired entries will be
+notified to the bridge using SWITCHDEV_FDB_DEL.  See rocker driver for
+example of driver running ageing timer.
+
+To keep an NTF_EXT_LEARNED entry "alive", the driver should refresh the FDB
+entry by calling call_switchdev_notifiers(SWITCHDEV_FDB_ADD, ...).  The
+notification will reset the FDB entry's last-used time to now.  The driver
+should rate limit refresh notifications, for example, no more than once a
+second.  (The last-used time is visible using the bridge -s fdb option).
+
+STP State Change on Port
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+Internally or with a third-party STP protocol implementation (e.g. mstpd), the
+bridge driver maintains the STP state for ports, and will notify the switch
+driver of STP state change on a port using the switchdev op
+switchdev_attr_port_set for SWITCHDEV_ATTR_PORT_ID_STP_UPDATE.
+
+State is one of BR_STATE_*.  The switch driver can use STP state updates to
+update ingress packet filter list for the port.  For example, if port is
+DISABLED, no packets should pass, but if port moves to BLOCKED, then STP BPDUs
+and other IEEE 01:80:c2:xx:xx:xx link-local multicast packets can pass.
+
+Note that STP BDPUs are untagged and STP state applies to all VLANs on the port
+so packet filters should be applied consistently across untagged and tagged
+VLANs on the port.
+
+Flooding L2 domain
+^^^^^^^^^^^^^^^^^^
+
+For a given L2 VLAN domain, the switch device should flood multicast/broadcast
+and unknown unicast packets to all ports in domain, if allowed by port's
+current STP state.  The switch driver, knowing which ports are within which
+vlan L2 domain, can program the switch device for flooding.  The packet may
+be sent to the port netdev for processing by the bridge driver.  The
+bridge should not reflood the packet to the same ports the device flooded,
+otherwise there will be duplicate packets on the wire.
+
+To avoid duplicate packets, the switch driver should mark a packet as already
+forwarded by setting the skb->offload_fwd_mark bit. The bridge driver will mark
+the skb using the ingress bridge port's mark and prevent it from being forwarded
+through any bridge port with the same mark.
+
+It is possible for the switch device to not handle flooding and push the
+packets up to the bridge driver for flooding.  This is not ideal as the number
+of ports scale in the L2 domain as the device is much more efficient at
+flooding packets that software.
+
+If supported by the device, flood control can be offloaded to it, preventing
+certain netdevs from flooding unicast traffic for which there is no FDB entry.
+
+IGMP Snooping
+^^^^^^^^^^^^^
+
+In order to support IGMP snooping, the port netdevs should trap to the bridge
+driver all IGMP join and leave messages.
+The bridge multicast module will notify port netdevs on every multicast group
+changed whether it is static configured or dynamically joined/leave.
+The hardware implementation should be forwarding all registered multicast
+traffic groups only to the configured ports.
+
+L3 Routing Offload
+------------------
+
+Offloading L3 routing requires that device be programmed with FIB entries from
+the kernel, with the device doing the FIB lookup and forwarding.  The device
+does a longest prefix match (LPM) on FIB entries matching route prefix and
+forwards the packet to the matching FIB entry's nexthop(s) egress ports.
+
+To program the device, the driver has to register a FIB notifier handler
+using register_fib_notifier. The following events are available:
+
+===================  ===================================================
+FIB_EVENT_ENTRY_ADD  used for both adding a new FIB entry to the device,
+                    or modifying an existing entry on the device.
+FIB_EVENT_ENTRY_DEL  used for removing a FIB entry
+FIB_EVENT_RULE_ADD,
+FIB_EVENT_RULE_DEL   used to propagate FIB rule changes
+===================  ===================================================
+
+FIB_EVENT_ENTRY_ADD and FIB_EVENT_ENTRY_DEL events pass::
+
+       struct fib_entry_notifier_info {
+               struct fib_notifier_info info; /* must be first */
+               u32 dst;
+               int dst_len;
+               struct fib_info *fi;
+               u8 tos;
+               u8 type;
+               u32 tb_id;
+               u32 nlflags;
+       };
+
+to add/modify/delete IPv4 dst/dest_len prefix on table tb_id.  The ``*fi``
+structure holds details on the route and route's nexthops.  ``*dev`` is one
+of the port netdevs mentioned in the route's next hop list.
+
+Routes offloaded to the device are labeled with "offload" in the ip route
+listing::
+
+       $ ip route show
+       default via 192.168.0.2 dev eth0
+       11.0.0.0/30 dev sw1p1  proto kernel  scope link  src 11.0.0.2 offload
+       11.0.0.4/30 via 11.0.0.1 dev sw1p1  proto zebra  metric 20 offload
+       11.0.0.8/30 dev sw1p2  proto kernel  scope link  src 11.0.0.10 offload
+       11.0.0.12/30 via 11.0.0.9 dev sw1p2  proto zebra  metric 20 offload
+       12.0.0.2  proto zebra  metric 30 offload
+               nexthop via 11.0.0.1  dev sw1p1 weight 1
+               nexthop via 11.0.0.9  dev sw1p2 weight 1
+       12.0.0.3 via 11.0.0.1 dev sw1p1  proto zebra  metric 20 offload
+       12.0.0.4 via 11.0.0.9 dev sw1p2  proto zebra  metric 20 offload
+       192.168.0.0/24 dev eth0  proto kernel  scope link  src 192.168.0.15
+
+The "offload" flag is set in case at least one device offloads the FIB entry.
+
+XXX: add/mod/del IPv6 FIB API
+
+Nexthop Resolution
+^^^^^^^^^^^^^^^^^^
+
+The FIB entry's nexthop list contains the nexthop tuple (gateway, dev), but for
+the switch device to forward the packet with the correct dst mac address, the
+nexthop gateways must be resolved to the neighbor's mac address.  Neighbor mac
+address discovery comes via the ARP (or ND) process and is available via the
+arp_tbl neighbor table.  To resolve the routes nexthop gateways, the driver
+should trigger the kernel's neighbor resolution process.  See the rocker
+driver's rocker_port_ipv4_resolve() for an example.
+
+The driver can monitor for updates to arp_tbl using the netevent notifier
+NETEVENT_NEIGH_UPDATE.  The device can be programmed with resolved nexthops
+for the routes as arp_tbl updates.  The driver implements ndo_neigh_destroy
+to know when arp_tbl neighbor entries are purged from the port.
diff --git a/Documentation/networking/switchdev.txt b/Documentation/networking/switchdev.txt
deleted file mode 100644 (file)
index 86174ce..0000000
+++ /dev/null
@@ -1,373 +0,0 @@
-Ethernet switch device driver model (switchdev)
-===============================================
-Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
-Copyright (c) 2014-2015 Scott Feldman <sfeldma@gmail.com>
-
-
-The Ethernet switch device driver model (switchdev) is an in-kernel driver
-model for switch devices which offload the forwarding (data) plane from the
-kernel.
-
-Figure 1 is a block diagram showing the components of the switchdev model for
-an example setup using a data-center-class switch ASIC chip.  Other setups
-with SR-IOV or soft switches, such as OVS, are possible.
-
-
-                             User-space tools
-
-       user space                   |
-      +-------------------------------------------------------------------+
-       kernel                       | Netlink
-                                    |
-                     +--------------+-------------------------------+
-                     |         Network stack                        |
-                     |           (Linux)                            |
-                     |                                              |
-                     +----------------------------------------------+
-
-                           sw1p2     sw1p4     sw1p6
-                      sw1p1  +  sw1p3  +  sw1p5  +          eth1
-                        +    |    +    |    +    |            +
-                        |    |    |    |    |    |            |
-                     +--+----+----+----+----+----+---+  +-----+-----+
-                     |         Switch driver         |  |    mgmt   |
-                     |        (this document)        |  |   driver  |
-                     |                               |  |           |
-                     +--------------+----------------+  +-----------+
-                                    |
-       kernel                       | HW bus (eg PCI)
-      +-------------------------------------------------------------------+
-       hardware                     |
-                     +--------------+----------------+
-                     |         Switch device (sw1)   |
-                     |  +----+                       +--------+
-                     |  |    v offloaded data path   | mgmt port
-                     |  |    |                       |
-                     +--|----|----+----+----+----+---+
-                        |    |    |    |    |    |
-                        +    +    +    +    +    +
-                       p1   p2   p3   p4   p5   p6
-
-                             front-panel ports
-
-
-                                    Fig 1.
-
-
-Include Files
--------------
-
-#include <linux/netdevice.h>
-#include <net/switchdev.h>
-
-
-Configuration
--------------
-
-Use "depends NET_SWITCHDEV" in driver's Kconfig to ensure switchdev model
-support is built for driver.
-
-
-Switch Ports
-------------
-
-On switchdev driver initialization, the driver will allocate and register a
-struct net_device (using register_netdev()) for each enumerated physical switch
-port, called the port netdev.  A port netdev is the software representation of
-the physical port and provides a conduit for control traffic to/from the
-controller (the kernel) and the network, as well as an anchor point for higher
-level constructs such as bridges, bonds, VLANs, tunnels, and L3 routers.  Using
-standard netdev tools (iproute2, ethtool, etc), the port netdev can also
-provide to the user access to the physical properties of the switch port such
-as PHY link state and I/O statistics.
-
-There is (currently) no higher-level kernel object for the switch beyond the
-port netdevs.  All of the switchdev driver ops are netdev ops or switchdev ops.
-
-A switch management port is outside the scope of the switchdev driver model.
-Typically, the management port is not participating in offloaded data plane and
-is loaded with a different driver, such as a NIC driver, on the management port
-device.
-
-Switch ID
-^^^^^^^^^
-
-The switchdev driver must implement the net_device operation
-ndo_get_port_parent_id for each port netdev, returning the same physical ID for
-each port of a switch. The ID must be unique between switches on the same
-system. The ID does not need to be unique between switches on different
-systems.
-
-The switch ID is used to locate ports on a switch and to know if aggregated
-ports belong to the same switch.
-
-Port Netdev Naming
-^^^^^^^^^^^^^^^^^^
-
-Udev rules should be used for port netdev naming, using some unique attribute
-of the port as a key, for example the port MAC address or the port PHYS name.
-Hard-coding of kernel netdev names within the driver is discouraged; let the
-kernel pick the default netdev name, and let udev set the final name based on a
-port attribute.
-
-Using port PHYS name (ndo_get_phys_port_name) for the key is particularly
-useful for dynamically-named ports where the device names its ports based on
-external configuration.  For example, if a physical 40G port is split logically
-into 4 10G ports, resulting in 4 port netdevs, the device can give a unique
-name for each port using port PHYS name.  The udev rule would be:
-
-SUBSYSTEM=="net", ACTION=="add", ATTR{phys_switch_id}=="<phys_switch_id>", \
-       ATTR{phys_port_name}!="", NAME="swX$attr{phys_port_name}"
-
-Suggested naming convention is "swXpYsZ", where X is the switch name or ID, Y
-is the port name or ID, and Z is the sub-port name or ID.  For example, sw1p1s0
-would be sub-port 0 on port 1 on switch 1.
-
-Port Features
-^^^^^^^^^^^^^
-
-NETIF_F_NETNS_LOCAL
-
-If the switchdev driver (and device) only supports offloading of the default
-network namespace (netns), the driver should set this feature flag to prevent
-the port netdev from being moved out of the default netns.  A netns-aware
-driver/device would not set this flag and be responsible for partitioning
-hardware to preserve netns containment.  This means hardware cannot forward
-traffic from a port in one namespace to another port in another namespace.
-
-Port Topology
-^^^^^^^^^^^^^
-
-The port netdevs representing the physical switch ports can be organized into
-higher-level switching constructs.  The default construct is a standalone
-router port, used to offload L3 forwarding.  Two or more ports can be bonded
-together to form a LAG.  Two or more ports (or LAGs) can be bridged to bridge
-L2 networks.  VLANs can be applied to sub-divide L2 networks.  L2-over-L3
-tunnels can be built on ports.  These constructs are built using standard Linux
-tools such as the bridge driver, the bonding/team drivers, and netlink-based
-tools such as iproute2.
-
-The switchdev driver can know a particular port's position in the topology by
-monitoring NETDEV_CHANGEUPPER notifications.  For example, a port moved into a
-bond will see it's upper master change.  If that bond is moved into a bridge,
-the bond's upper master will change.  And so on.  The driver will track such
-movements to know what position a port is in in the overall topology by
-registering for netdevice events and acting on NETDEV_CHANGEUPPER.
-
-L2 Forwarding Offload
----------------------
-
-The idea is to offload the L2 data forwarding (switching) path from the kernel
-to the switchdev device by mirroring bridge FDB entries down to the device.  An
-FDB entry is the {port, MAC, VLAN} tuple forwarding destination.
-
-To offloading L2 bridging, the switchdev driver/device should support:
-
-       - Static FDB entries installed on a bridge port
-       - Notification of learned/forgotten src mac/vlans from device
-       - STP state changes on the port
-       - VLAN flooding of multicast/broadcast and unknown unicast packets
-
-Static FDB Entries
-^^^^^^^^^^^^^^^^^^
-
-The switchdev driver should implement ndo_fdb_add, ndo_fdb_del and ndo_fdb_dump
-to support static FDB entries installed to the device.  Static bridge FDB
-entries are installed, for example, using iproute2 bridge cmd:
-
-       bridge fdb add ADDR dev DEV [vlan VID] [self]
-
-The driver should use the helper switchdev_port_fdb_xxx ops for ndo_fdb_xxx
-ops, and handle add/delete/dump of SWITCHDEV_OBJ_ID_PORT_FDB object using
-switchdev_port_obj_xxx ops.
-
-XXX: what should be done if offloading this rule to hardware fails (for
-example, due to full capacity in hardware tables) ?
-
-Note: by default, the bridge does not filter on VLAN and only bridges untagged
-traffic.  To enable VLAN support, turn on VLAN filtering:
-
-       echo 1 >/sys/class/net/<bridge>/bridge/vlan_filtering
-
-Notification of Learned/Forgotten Source MAC/VLANs
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The switch device will learn/forget source MAC address/VLAN on ingress packets
-and notify the switch driver of the mac/vlan/port tuples.  The switch driver,
-in turn, will notify the bridge driver using the switchdev notifier call:
-
-       err = call_switchdev_notifiers(val, dev, info, extack);
-
-Where val is SWITCHDEV_FDB_ADD when learning and SWITCHDEV_FDB_DEL when
-forgetting, and info points to a struct switchdev_notifier_fdb_info.  On
-SWITCHDEV_FDB_ADD, the bridge driver will install the FDB entry into the
-bridge's FDB and mark the entry as NTF_EXT_LEARNED.  The iproute2 bridge
-command will label these entries "offload":
-
-       $ bridge fdb
-       52:54:00:12:35:01 dev sw1p1 master br0 permanent
-       00:02:00:00:02:00 dev sw1p1 master br0 offload
-       00:02:00:00:02:00 dev sw1p1 self
-       52:54:00:12:35:02 dev sw1p2 master br0 permanent
-       00:02:00:00:03:00 dev sw1p2 master br0 offload
-       00:02:00:00:03:00 dev sw1p2 self
-       33:33:00:00:00:01 dev eth0 self permanent
-       01:00:5e:00:00:01 dev eth0 self permanent
-       33:33:ff:00:00:00 dev eth0 self permanent
-       01:80:c2:00:00:0e dev eth0 self permanent
-       33:33:00:00:00:01 dev br0 self permanent
-       01:00:5e:00:00:01 dev br0 self permanent
-       33:33:ff:12:35:01 dev br0 self permanent
-
-Learning on the port should be disabled on the bridge using the bridge command:
-
-       bridge link set dev DEV learning off
-
-Learning on the device port should be enabled, as well as learning_sync:
-
-       bridge link set dev DEV learning on self
-       bridge link set dev DEV learning_sync on self
-
-Learning_sync attribute enables syncing of the learned/forgotten FDB entry to
-the bridge's FDB.  It's possible, but not optimal, to enable learning on the
-device port and on the bridge port, and disable learning_sync.
-
-To support learning, the driver implements switchdev op
-switchdev_port_attr_set for SWITCHDEV_ATTR_PORT_ID_{PRE}_BRIDGE_FLAGS.
-
-FDB Ageing
-^^^^^^^^^^
-
-The bridge will skip ageing FDB entries marked with NTF_EXT_LEARNED and it is
-the responsibility of the port driver/device to age out these entries.  If the
-port device supports ageing, when the FDB entry expires, it will notify the
-driver which in turn will notify the bridge with SWITCHDEV_FDB_DEL.  If the
-device does not support ageing, the driver can simulate ageing using a
-garbage collection timer to monitor FDB entries.  Expired entries will be
-notified to the bridge using SWITCHDEV_FDB_DEL.  See rocker driver for
-example of driver running ageing timer.
-
-To keep an NTF_EXT_LEARNED entry "alive", the driver should refresh the FDB
-entry by calling call_switchdev_notifiers(SWITCHDEV_FDB_ADD, ...).  The
-notification will reset the FDB entry's last-used time to now.  The driver
-should rate limit refresh notifications, for example, no more than once a
-second.  (The last-used time is visible using the bridge -s fdb option).
-
-STP State Change on Port
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-Internally or with a third-party STP protocol implementation (e.g. mstpd), the
-bridge driver maintains the STP state for ports, and will notify the switch
-driver of STP state change on a port using the switchdev op
-switchdev_attr_port_set for SWITCHDEV_ATTR_PORT_ID_STP_UPDATE.
-
-State is one of BR_STATE_*.  The switch driver can use STP state updates to
-update ingress packet filter list for the port.  For example, if port is
-DISABLED, no packets should pass, but if port moves to BLOCKED, then STP BPDUs
-and other IEEE 01:80:c2:xx:xx:xx link-local multicast packets can pass.
-
-Note that STP BDPUs are untagged and STP state applies to all VLANs on the port
-so packet filters should be applied consistently across untagged and tagged
-VLANs on the port.
-
-Flooding L2 domain
-^^^^^^^^^^^^^^^^^^
-
-For a given L2 VLAN domain, the switch device should flood multicast/broadcast
-and unknown unicast packets to all ports in domain, if allowed by port's
-current STP state.  The switch driver, knowing which ports are within which
-vlan L2 domain, can program the switch device for flooding.  The packet may
-be sent to the port netdev for processing by the bridge driver.  The
-bridge should not reflood the packet to the same ports the device flooded,
-otherwise there will be duplicate packets on the wire.
-
-To avoid duplicate packets, the switch driver should mark a packet as already
-forwarded by setting the skb->offload_fwd_mark bit. The bridge driver will mark
-the skb using the ingress bridge port's mark and prevent it from being forwarded
-through any bridge port with the same mark.
-
-It is possible for the switch device to not handle flooding and push the
-packets up to the bridge driver for flooding.  This is not ideal as the number
-of ports scale in the L2 domain as the device is much more efficient at
-flooding packets that software.
-
-If supported by the device, flood control can be offloaded to it, preventing
-certain netdevs from flooding unicast traffic for which there is no FDB entry.
-
-IGMP Snooping
-^^^^^^^^^^^^^
-
-In order to support IGMP snooping, the port netdevs should trap to the bridge
-driver all IGMP join and leave messages.
-The bridge multicast module will notify port netdevs on every multicast group
-changed whether it is static configured or dynamically joined/leave.
-The hardware implementation should be forwarding all registered multicast
-traffic groups only to the configured ports.
-
-L3 Routing Offload
-------------------
-
-Offloading L3 routing requires that device be programmed with FIB entries from
-the kernel, with the device doing the FIB lookup and forwarding.  The device
-does a longest prefix match (LPM) on FIB entries matching route prefix and
-forwards the packet to the matching FIB entry's nexthop(s) egress ports.
-
-To program the device, the driver has to register a FIB notifier handler
-using register_fib_notifier. The following events are available:
-FIB_EVENT_ENTRY_ADD: used for both adding a new FIB entry to the device,
-                     or modifying an existing entry on the device.
-FIB_EVENT_ENTRY_DEL: used for removing a FIB entry
-FIB_EVENT_RULE_ADD, FIB_EVENT_RULE_DEL: used to propagate FIB rule changes
-
-FIB_EVENT_ENTRY_ADD and FIB_EVENT_ENTRY_DEL events pass:
-
-       struct fib_entry_notifier_info {
-               struct fib_notifier_info info; /* must be first */
-               u32 dst;
-               int dst_len;
-               struct fib_info *fi;
-               u8 tos;
-               u8 type;
-               u32 tb_id;
-               u32 nlflags;
-       };
-
-to add/modify/delete IPv4 dst/dest_len prefix on table tb_id.  The *fi
-structure holds details on the route and route's nexthops.  *dev is one of the
-port netdevs mentioned in the route's next hop list.
-
-Routes offloaded to the device are labeled with "offload" in the ip route
-listing:
-
-       $ ip route show
-       default via 192.168.0.2 dev eth0
-       11.0.0.0/30 dev sw1p1  proto kernel  scope link  src 11.0.0.2 offload
-       11.0.0.4/30 via 11.0.0.1 dev sw1p1  proto zebra  metric 20 offload
-       11.0.0.8/30 dev sw1p2  proto kernel  scope link  src 11.0.0.10 offload
-       11.0.0.12/30 via 11.0.0.9 dev sw1p2  proto zebra  metric 20 offload
-       12.0.0.2  proto zebra  metric 30 offload
-               nexthop via 11.0.0.1  dev sw1p1 weight 1
-               nexthop via 11.0.0.9  dev sw1p2 weight 1
-       12.0.0.3 via 11.0.0.1 dev sw1p1  proto zebra  metric 20 offload
-       12.0.0.4 via 11.0.0.9 dev sw1p2  proto zebra  metric 20 offload
-       192.168.0.0/24 dev eth0  proto kernel  scope link  src 192.168.0.15
-
-The "offload" flag is set in case at least one device offloads the FIB entry.
-
-XXX: add/mod/del IPv6 FIB API
-
-Nexthop Resolution
-^^^^^^^^^^^^^^^^^^
-
-The FIB entry's nexthop list contains the nexthop tuple (gateway, dev), but for
-the switch device to forward the packet with the correct dst mac address, the
-nexthop gateways must be resolved to the neighbor's mac address.  Neighbor mac
-address discovery comes via the ARP (or ND) process and is available via the
-arp_tbl neighbor table.  To resolve the routes nexthop gateways, the driver
-should trigger the kernel's neighbor resolution process.  See the rocker
-driver's rocker_port_ipv4_resolve() for an example.
-
-The driver can monitor for updates to arp_tbl using the netevent notifier
-NETEVENT_NEIGH_UPDATE.  The device can be programmed with resolved nexthops
-for the routes as arp_tbl updates.  The driver implements ndo_neigh_destroy
-to know when arp_tbl neighbor entries are purged from the port.
diff --git a/Documentation/networking/tc-actions-env-rules.rst b/Documentation/networking/tc-actions-env-rules.rst
new file mode 100644 (file)
index 0000000..86884b8
--- /dev/null
@@ -0,0 +1,29 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+================================
+TC Actions - Environmental Rules
+================================
+
+
+The "environmental" rules for authors of any new tc actions are:
+
+1) If you stealeth or borroweth any packet thou shalt be branching
+   from the righteous path and thou shalt cloneth.
+
+   For example if your action queues a packet to be processed later,
+   or intentionally branches by redirecting a packet, then you need to
+   clone the packet.
+
+2) If you munge any packet thou shalt call pskb_expand_head in the case
+   someone else is referencing the skb. After that you "own" the skb.
+
+3) Dropping packets you don't own is a no-no. You simply return
+   TC_ACT_SHOT to the caller and they will drop it.
+
+The "environmental" rules for callers of actions (qdiscs etc) are:
+
+#) Thou art responsible for freeing anything returned as being
+   TC_ACT_SHOT/STOLEN/QUEUED. If none of TC_ACT_SHOT/STOLEN/QUEUED is
+   returned, then all is great and you don't need to do anything.
+
+Post on netdev if something is unclear.
diff --git a/Documentation/networking/tc-actions-env-rules.txt b/Documentation/networking/tc-actions-env-rules.txt
deleted file mode 100644 (file)
index f378146..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-
-The "environmental" rules for authors of any new tc actions are:
-
-1) If you stealeth or borroweth any packet thou shalt be branching
-from the righteous path and thou shalt cloneth.
-
-For example if your action queues a packet to be processed later,
-or intentionally branches by redirecting a packet, then you need to
-clone the packet.
-
-2) If you munge any packet thou shalt call pskb_expand_head in the case
-someone else is referencing the skb. After that you "own" the skb.
-
-3) Dropping packets you don't own is a no-no. You simply return
-TC_ACT_SHOT to the caller and they will drop it.
-
-The "environmental" rules for callers of actions (qdiscs etc) are:
-
-*) Thou art responsible for freeing anything returned as being
-TC_ACT_SHOT/STOLEN/QUEUED. If none of TC_ACT_SHOT/STOLEN/QUEUED is
-returned, then all is great and you don't need to do anything.
-
-Post on netdev if something is unclear.
-
diff --git a/Documentation/networking/tcp-thin.rst b/Documentation/networking/tcp-thin.rst
new file mode 100644 (file)
index 0000000..b06765c
--- /dev/null
@@ -0,0 +1,52 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====================
+Thin-streams and TCP
+====================
+
+A wide range of Internet-based services that use reliable transport
+protocols display what we call thin-stream properties. This means
+that the application sends data with such a low rate that the
+retransmission mechanisms of the transport protocol are not fully
+effective. In time-dependent scenarios (like online games, control
+systems, stock trading etc.) where the user experience depends
+on the data delivery latency, packet loss can be devastating for
+the service quality. Extreme latencies are caused by TCP's
+dependency on the arrival of new data from the application to trigger
+retransmissions effectively through fast retransmit instead of
+waiting for long timeouts.
+
+After analysing a large number of time-dependent interactive
+applications, we have seen that they often produce thin streams
+and also stay with this traffic pattern throughout its entire
+lifespan. The combination of time-dependency and the fact that the
+streams provoke high latencies when using TCP is unfortunate.
+
+In order to reduce application-layer latency when packets are lost,
+a set of mechanisms has been made, which address these latency issues
+for thin streams. In short, if the kernel detects a thin stream,
+the retransmission mechanisms are modified in the following manner:
+
+1) If the stream is thin, fast retransmit on the first dupACK.
+2) If the stream is thin, do not apply exponential backoff.
+
+These enhancements are applied only if the stream is detected as
+thin. This is accomplished by defining a threshold for the number
+of packets in flight. If there are less than 4 packets in flight,
+fast retransmissions can not be triggered, and the stream is prone
+to experience high retransmission latencies.
+
+Since these mechanisms are targeted at time-dependent applications,
+they must be specifically activated by the application using the
+TCP_THIN_LINEAR_TIMEOUTS and TCP_THIN_DUPACK IOCTLS or the
+tcp_thin_linear_timeouts and tcp_thin_dupack sysctls. Both
+modifications are turned off by default.
+
+References
+==========
+More information on the modifications, as well as a wide range of
+experimental data can be found here:
+
+"Improving latency for interactive, thin-stream applications over
+reliable transport"
+http://simula.no/research/nd/publications/Simula.nd.477/simula_pdf_file
diff --git a/Documentation/networking/tcp-thin.txt b/Documentation/networking/tcp-thin.txt
deleted file mode 100644 (file)
index 151e229..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-Thin-streams and TCP
-====================
-A wide range of Internet-based services that use reliable transport
-protocols display what we call thin-stream properties. This means
-that the application sends data with such a low rate that the
-retransmission mechanisms of the transport protocol are not fully
-effective. In time-dependent scenarios (like online games, control
-systems, stock trading etc.) where the user experience depends
-on the data delivery latency, packet loss can be devastating for
-the service quality. Extreme latencies are caused by TCP's
-dependency on the arrival of new data from the application to trigger
-retransmissions effectively through fast retransmit instead of
-waiting for long timeouts.
-
-After analysing a large number of time-dependent interactive
-applications, we have seen that they often produce thin streams
-and also stay with this traffic pattern throughout its entire
-lifespan. The combination of time-dependency and the fact that the
-streams provoke high latencies when using TCP is unfortunate.
-
-In order to reduce application-layer latency when packets are lost,
-a set of mechanisms has been made, which address these latency issues
-for thin streams. In short, if the kernel detects a thin stream,
-the retransmission mechanisms are modified in the following manner:
-
-1) If the stream is thin, fast retransmit on the first dupACK.
-2) If the stream is thin, do not apply exponential backoff.
-
-These enhancements are applied only if the stream is detected as
-thin. This is accomplished by defining a threshold for the number
-of packets in flight. If there are less than 4 packets in flight,
-fast retransmissions can not be triggered, and the stream is prone
-to experience high retransmission latencies.
-
-Since these mechanisms are targeted at time-dependent applications,
-they must be specifically activated by the application using the
-TCP_THIN_LINEAR_TIMEOUTS and TCP_THIN_DUPACK IOCTLS or the
-tcp_thin_linear_timeouts and tcp_thin_dupack sysctls. Both
-modifications are turned off by default.
-
-References
-==========
-More information on the modifications, as well as a wide range of
-experimental data can be found here:
-"Improving latency for interactive, thin-stream applications over
-reliable transport"
-http://simula.no/research/nd/publications/Simula.nd.477/simula_pdf_file
diff --git a/Documentation/networking/team.rst b/Documentation/networking/team.rst
new file mode 100644 (file)
index 0000000..0a7f3a0
--- /dev/null
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====
+Team
+====
+
+Team devices are driven from userspace via libteam library which is here:
+       https://github.com/jpirko/libteam
diff --git a/Documentation/networking/team.txt b/Documentation/networking/team.txt
deleted file mode 100644 (file)
index 5a01368..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-Team devices are driven from userspace via libteam library which is here:
-       https://github.com/jpirko/libteam
diff --git a/Documentation/networking/timestamping.rst b/Documentation/networking/timestamping.rst
new file mode 100644 (file)
index 0000000..1adead6
--- /dev/null
@@ -0,0 +1,591 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============
+Timestamping
+============
+
+
+1. Control Interfaces
+=====================
+
+The interfaces for receiving network packages timestamps are:
+
+SO_TIMESTAMP
+  Generates a timestamp for each incoming packet in (not necessarily
+  monotonic) system time. Reports the timestamp via recvmsg() in a
+  control message in usec resolution.
+  SO_TIMESTAMP is defined as SO_TIMESTAMP_NEW or SO_TIMESTAMP_OLD
+  based on the architecture type and time_t representation of libc.
+  Control message format is in struct __kernel_old_timeval for
+  SO_TIMESTAMP_OLD and in struct __kernel_sock_timeval for
+  SO_TIMESTAMP_NEW options respectively.
+
+SO_TIMESTAMPNS
+  Same timestamping mechanism as SO_TIMESTAMP, but reports the
+  timestamp as struct timespec in nsec resolution.
+  SO_TIMESTAMPNS is defined as SO_TIMESTAMPNS_NEW or SO_TIMESTAMPNS_OLD
+  based on the architecture type and time_t representation of libc.
+  Control message format is in struct timespec for SO_TIMESTAMPNS_OLD
+  and in struct __kernel_timespec for SO_TIMESTAMPNS_NEW options
+  respectively.
+
+IP_MULTICAST_LOOP + SO_TIMESTAMP[NS]
+  Only for multicast:approximate transmit timestamp obtained by
+  reading the looped packet receive timestamp.
+
+SO_TIMESTAMPING
+  Generates timestamps on reception, transmission or both. Supports
+  multiple timestamp sources, including hardware. Supports generating
+  timestamps for stream sockets.
+
+
+1.1 SO_TIMESTAMP (also SO_TIMESTAMP_OLD and SO_TIMESTAMP_NEW)
+-------------------------------------------------------------
+
+This socket option enables timestamping of datagrams on the reception
+path. Because the destination socket, if any, is not known early in
+the network stack, the feature has to be enabled for all packets. The
+same is true for all early receive timestamp options.
+
+For interface details, see `man 7 socket`.
+
+Always use SO_TIMESTAMP_NEW timestamp to always get timestamp in
+struct __kernel_sock_timeval format.
+
+SO_TIMESTAMP_OLD returns incorrect timestamps after the year 2038
+on 32 bit machines.
+
+1.2 SO_TIMESTAMPNS (also SO_TIMESTAMPNS_OLD and SO_TIMESTAMPNS_NEW):
+
+This option is identical to SO_TIMESTAMP except for the returned data type.
+Its struct timespec allows for higher resolution (ns) timestamps than the
+timeval of SO_TIMESTAMP (ms).
+
+Always use SO_TIMESTAMPNS_NEW timestamp to always get timestamp in
+struct __kernel_timespec format.
+
+SO_TIMESTAMPNS_OLD returns incorrect timestamps after the year 2038
+on 32 bit machines.
+
+1.3 SO_TIMESTAMPING (also SO_TIMESTAMPING_OLD and SO_TIMESTAMPING_NEW)
+----------------------------------------------------------------------
+
+Supports multiple types of timestamp requests. As a result, this
+socket option takes a bitmap of flags, not a boolean. In::
+
+  err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));
+
+val is an integer with any of the following bits set. Setting other
+bit returns EINVAL and does not change the current state.
+
+The socket option configures timestamp generation for individual
+sk_buffs (1.3.1), timestamp reporting to the socket's error
+queue (1.3.2) and options (1.3.3). Timestamp generation can also
+be enabled for individual sendmsg calls using cmsg (1.3.4).
+
+
+1.3.1 Timestamp Generation
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Some bits are requests to the stack to try to generate timestamps. Any
+combination of them is valid. Changes to these bits apply to newly
+created packets, not to packets already in the stack. As a result, it
+is possible to selectively request timestamps for a subset of packets
+(e.g., for sampling) by embedding an send() call within two setsockopt
+calls, one to enable timestamp generation and one to disable it.
+Timestamps may also be generated for reasons other than being
+requested by a particular socket, such as when receive timestamping is
+enabled system wide, as explained earlier.
+
+SOF_TIMESTAMPING_RX_HARDWARE:
+  Request rx timestamps generated by the network adapter.
+
+SOF_TIMESTAMPING_RX_SOFTWARE:
+  Request rx timestamps when data enters the kernel. These timestamps
+  are generated just after a device driver hands a packet to the
+  kernel receive stack.
+
+SOF_TIMESTAMPING_TX_HARDWARE:
+  Request tx timestamps generated by the network adapter. This flag
+  can be enabled via both socket options and control messages.
+
+SOF_TIMESTAMPING_TX_SOFTWARE:
+  Request tx timestamps when data leaves the kernel. These timestamps
+  are generated in the device driver as close as possible, but always
+  prior to, passing the packet to the network interface. Hence, they
+  require driver support and may not be available for all devices.
+  This flag can be enabled via both socket options and control messages.
+
+SOF_TIMESTAMPING_TX_SCHED:
+  Request tx timestamps prior to entering the packet scheduler. Kernel
+  transmit latency is, if long, often dominated by queuing delay. The
+  difference between this timestamp and one taken at
+  SOF_TIMESTAMPING_TX_SOFTWARE will expose this latency independent
+  of protocol processing. The latency incurred in protocol
+  processing, if any, can be computed by subtracting a userspace
+  timestamp taken immediately before send() from this timestamp. On
+  machines with virtual devices where a transmitted packet travels
+  through multiple devices and, hence, multiple packet schedulers,
+  a timestamp is generated at each layer. This allows for fine
+  grained measurement of queuing delay. This flag can be enabled
+  via both socket options and control messages.
+
+SOF_TIMESTAMPING_TX_ACK:
+  Request tx timestamps when all data in the send buffer has been
+  acknowledged. This only makes sense for reliable protocols. It is
+  currently only implemented for TCP. For that protocol, it may
+  over-report measurement, because the timestamp is generated when all
+  data up to and including the buffer at send() was acknowledged: the
+  cumulative acknowledgment. The mechanism ignores SACK and FACK.
+  This flag can be enabled via both socket options and control messages.
+
+
+1.3.2 Timestamp Reporting
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The other three bits control which timestamps will be reported in a
+generated control message. Changes to the bits take immediate
+effect at the timestamp reporting locations in the stack. Timestamps
+are only reported for packets that also have the relevant timestamp
+generation request set.
+
+SOF_TIMESTAMPING_SOFTWARE:
+  Report any software timestamps when available.
+
+SOF_TIMESTAMPING_SYS_HARDWARE:
+  This option is deprecated and ignored.
+
+SOF_TIMESTAMPING_RAW_HARDWARE:
+  Report hardware timestamps as generated by
+  SOF_TIMESTAMPING_TX_HARDWARE when available.
+
+
+1.3.3 Timestamp Options
+^^^^^^^^^^^^^^^^^^^^^^^
+
+The interface supports the options
+
+SOF_TIMESTAMPING_OPT_ID:
+  Generate a unique identifier along with each packet. A process can
+  have multiple concurrent timestamping requests outstanding. Packets
+  can be reordered in the transmit path, for instance in the packet
+  scheduler. In that case timestamps will be queued onto the error
+  queue out of order from the original send() calls. It is not always
+  possible to uniquely match timestamps to the original send() calls
+  based on timestamp order or payload inspection alone, then.
+
+  This option associates each packet at send() with a unique
+  identifier and returns that along with the timestamp. The identifier
+  is derived from a per-socket u32 counter (that wraps). For datagram
+  sockets, the counter increments with each sent packet. For stream
+  sockets, it increments with every byte.
+
+  The counter starts at zero. It is initialized the first time that
+  the socket option is enabled. It is reset each time the option is
+  enabled after having been disabled. Resetting the counter does not
+  change the identifiers of existing packets in the system.
+
+  This option is implemented only for transmit timestamps. There, the
+  timestamp is always looped along with a struct sock_extended_err.
+  The option modifies field ee_data to pass an id that is unique
+  among all possibly concurrently outstanding timestamp requests for
+  that socket.
+
+
+SOF_TIMESTAMPING_OPT_CMSG:
+  Support recv() cmsg for all timestamped packets. Control messages
+  are already supported unconditionally on all packets with receive
+  timestamps and on IPv6 packets with transmit timestamp. This option
+  extends them to IPv4 packets with transmit timestamp. One use case
+  is to correlate packets with their egress device, by enabling socket
+  option IP_PKTINFO simultaneously.
+
+
+SOF_TIMESTAMPING_OPT_TSONLY:
+  Applies to transmit timestamps only. Makes the kernel return the
+  timestamp as a cmsg alongside an empty packet, as opposed to
+  alongside the original packet. This reduces the amount of memory
+  charged to the socket's receive budget (SO_RCVBUF) and delivers
+  the timestamp even if sysctl net.core.tstamp_allow_data is 0.
+  This option disables SOF_TIMESTAMPING_OPT_CMSG.
+
+SOF_TIMESTAMPING_OPT_STATS:
+  Optional stats that are obtained along with the transmit timestamps.
+  It must be used together with SOF_TIMESTAMPING_OPT_TSONLY. When the
+  transmit timestamp is available, the stats are available in a
+  separate control message of type SCM_TIMESTAMPING_OPT_STATS, as a
+  list of TLVs (struct nlattr) of types. These stats allow the
+  application to associate various transport layer stats with
+  the transmit timestamps, such as how long a certain block of
+  data was limited by peer's receiver window.
+
+SOF_TIMESTAMPING_OPT_PKTINFO:
+  Enable the SCM_TIMESTAMPING_PKTINFO control message for incoming
+  packets with hardware timestamps. The message contains struct
+  scm_ts_pktinfo, which supplies the index of the real interface which
+  received the packet and its length at layer 2. A valid (non-zero)
+  interface index will be returned only if CONFIG_NET_RX_BUSY_POLL is
+  enabled and the driver is using NAPI. The struct contains also two
+  other fields, but they are reserved and undefined.
+
+SOF_TIMESTAMPING_OPT_TX_SWHW:
+  Request both hardware and software timestamps for outgoing packets
+  when SOF_TIMESTAMPING_TX_HARDWARE and SOF_TIMESTAMPING_TX_SOFTWARE
+  are enabled at the same time. If both timestamps are generated,
+  two separate messages will be looped to the socket's error queue,
+  each containing just one timestamp.
+
+New applications are encouraged to pass SOF_TIMESTAMPING_OPT_ID to
+disambiguate timestamps and SOF_TIMESTAMPING_OPT_TSONLY to operate
+regardless of the setting of sysctl net.core.tstamp_allow_data.
+
+An exception is when a process needs additional cmsg data, for
+instance SOL_IP/IP_PKTINFO to detect the egress network interface.
+Then pass option SOF_TIMESTAMPING_OPT_CMSG. This option depends on
+having access to the contents of the original packet, so cannot be
+combined with SOF_TIMESTAMPING_OPT_TSONLY.
+
+
+1.3.4. Enabling timestamps via control messages
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In addition to socket options, timestamp generation can be requested
+per write via cmsg, only for SOF_TIMESTAMPING_TX_* (see Section 1.3.1).
+Using this feature, applications can sample timestamps per sendmsg()
+without paying the overhead of enabling and disabling timestamps via
+setsockopt::
+
+  struct msghdr *msg;
+  ...
+  cmsg                        = CMSG_FIRSTHDR(msg);
+  cmsg->cmsg_level            = SOL_SOCKET;
+  cmsg->cmsg_type             = SO_TIMESTAMPING;
+  cmsg->cmsg_len              = CMSG_LEN(sizeof(__u32));
+  *((__u32 *) CMSG_DATA(cmsg)) = SOF_TIMESTAMPING_TX_SCHED |
+                                SOF_TIMESTAMPING_TX_SOFTWARE |
+                                SOF_TIMESTAMPING_TX_ACK;
+  err = sendmsg(fd, msg, 0);
+
+The SOF_TIMESTAMPING_TX_* flags set via cmsg will override
+the SOF_TIMESTAMPING_TX_* flags set via setsockopt.
+
+Moreover, applications must still enable timestamp reporting via
+setsockopt to receive timestamps::
+
+  __u32 val = SOF_TIMESTAMPING_SOFTWARE |
+             SOF_TIMESTAMPING_OPT_ID /* or any other flag */;
+  err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));
+
+
+1.4 Bytestream Timestamps
+-------------------------
+
+The SO_TIMESTAMPING interface supports timestamping of bytes in a
+bytestream. Each request is interpreted as a request for when the
+entire contents of the buffer has passed a timestamping point. That
+is, for streams option SOF_TIMESTAMPING_TX_SOFTWARE will record
+when all bytes have reached the device driver, regardless of how
+many packets the data has been converted into.
+
+In general, bytestreams have no natural delimiters and therefore
+correlating a timestamp with data is non-trivial. A range of bytes
+may be split across segments, any segments may be merged (possibly
+coalescing sections of previously segmented buffers associated with
+independent send() calls). Segments can be reordered and the same
+byte range can coexist in multiple segments for protocols that
+implement retransmissions.
+
+It is essential that all timestamps implement the same semantics,
+regardless of these possible transformations, as otherwise they are
+incomparable. Handling "rare" corner cases differently from the
+simple case (a 1:1 mapping from buffer to skb) is insufficient
+because performance debugging often needs to focus on such outliers.
+
+In practice, timestamps can be correlated with segments of a
+bytestream consistently, if both semantics of the timestamp and the
+timing of measurement are chosen correctly. This challenge is no
+different from deciding on a strategy for IP fragmentation. There, the
+definition is that only the first fragment is timestamped. For
+bytestreams, we chose that a timestamp is generated only when all
+bytes have passed a point. SOF_TIMESTAMPING_TX_ACK as defined is easy to
+implement and reason about. An implementation that has to take into
+account SACK would be more complex due to possible transmission holes
+and out of order arrival.
+
+On the host, TCP can also break the simple 1:1 mapping from buffer to
+skbuff as a result of Nagle, cork, autocork, segmentation and GSO. The
+implementation ensures correctness in all cases by tracking the
+individual last byte passed to send(), even if it is no longer the
+last byte after an skbuff extend or merge operation. It stores the
+relevant sequence number in skb_shinfo(skb)->tskey. Because an skbuff
+has only one such field, only one timestamp can be generated.
+
+In rare cases, a timestamp request can be missed if two requests are
+collapsed onto the same skb. A process can detect this situation by
+enabling SOF_TIMESTAMPING_OPT_ID and comparing the byte offset at
+send time with the value returned for each timestamp. It can prevent
+the situation by always flushing the TCP stack in between requests,
+for instance by enabling TCP_NODELAY and disabling TCP_CORK and
+autocork.
+
+These precautions ensure that the timestamp is generated only when all
+bytes have passed a timestamp point, assuming that the network stack
+itself does not reorder the segments. The stack indeed tries to avoid
+reordering. The one exception is under administrator control: it is
+possible to construct a packet scheduler configuration that delays
+segments from the same stream differently. Such a setup would be
+unusual.
+
+
+2 Data Interfaces
+==================
+
+Timestamps are read using the ancillary data feature of recvmsg().
+See `man 3 cmsg` for details of this interface. The socket manual
+page (`man 7 socket`) describes how timestamps generated with
+SO_TIMESTAMP and SO_TIMESTAMPNS records can be retrieved.
+
+
+2.1 SCM_TIMESTAMPING records
+----------------------------
+
+These timestamps are returned in a control message with cmsg_level
+SOL_SOCKET, cmsg_type SCM_TIMESTAMPING, and payload of type
+
+For SO_TIMESTAMPING_OLD::
+
+       struct scm_timestamping {
+               struct timespec ts[3];
+       };
+
+For SO_TIMESTAMPING_NEW::
+
+       struct scm_timestamping64 {
+               struct __kernel_timespec ts[3];
+
+Always use SO_TIMESTAMPING_NEW timestamp to always get timestamp in
+struct scm_timestamping64 format.
+
+SO_TIMESTAMPING_OLD returns incorrect timestamps after the year 2038
+on 32 bit machines.
+
+The structure can return up to three timestamps. This is a legacy
+feature. At least one field is non-zero at any time. Most timestamps
+are passed in ts[0]. Hardware timestamps are passed in ts[2].
+
+ts[1] used to hold hardware timestamps converted to system time.
+Instead, expose the hardware clock device on the NIC directly as
+a HW PTP clock source, to allow time conversion in userspace and
+optionally synchronize system time with a userspace PTP stack such
+as linuxptp. For the PTP clock API, see Documentation/driver-api/ptp.rst.
+
+Note that if the SO_TIMESTAMP or SO_TIMESTAMPNS option is enabled
+together with SO_TIMESTAMPING using SOF_TIMESTAMPING_SOFTWARE, a false
+software timestamp will be generated in the recvmsg() call and passed
+in ts[0] when a real software timestamp is missing. This happens also
+on hardware transmit timestamps.
+
+2.1.1 Transmit timestamps with MSG_ERRQUEUE
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For transmit timestamps the outgoing packet is looped back to the
+socket's error queue with the send timestamp(s) attached. A process
+receives the timestamps by calling recvmsg() with flag MSG_ERRQUEUE
+set and with a msg_control buffer sufficiently large to receive the
+relevant metadata structures. The recvmsg call returns the original
+outgoing data packet with two ancillary messages attached.
+
+A message of cm_level SOL_IP(V6) and cm_type IP(V6)_RECVERR
+embeds a struct sock_extended_err. This defines the error type. For
+timestamps, the ee_errno field is ENOMSG. The other ancillary message
+will have cm_level SOL_SOCKET and cm_type SCM_TIMESTAMPING. This
+embeds the struct scm_timestamping.
+
+
+2.1.1.2 Timestamp types
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The semantics of the three struct timespec are defined by field
+ee_info in the extended error structure. It contains a value of
+type SCM_TSTAMP_* to define the actual timestamp passed in
+scm_timestamping.
+
+The SCM_TSTAMP_* types are 1:1 matches to the SOF_TIMESTAMPING_*
+control fields discussed previously, with one exception. For legacy
+reasons, SCM_TSTAMP_SND is equal to zero and can be set for both
+SOF_TIMESTAMPING_TX_HARDWARE and SOF_TIMESTAMPING_TX_SOFTWARE. It
+is the first if ts[2] is non-zero, the second otherwise, in which
+case the timestamp is stored in ts[0].
+
+
+2.1.1.3 Fragmentation
+~~~~~~~~~~~~~~~~~~~~~
+
+Fragmentation of outgoing datagrams is rare, but is possible, e.g., by
+explicitly disabling PMTU discovery. If an outgoing packet is fragmented,
+then only the first fragment is timestamped and returned to the sending
+socket.
+
+
+2.1.1.4 Packet Payload
+~~~~~~~~~~~~~~~~~~~~~~
+
+The calling application is often not interested in receiving the whole
+packet payload that it passed to the stack originally: the socket
+error queue mechanism is just a method to piggyback the timestamp on.
+In this case, the application can choose to read datagrams with a
+smaller buffer, possibly even of length 0. The payload is truncated
+accordingly. Until the process calls recvmsg() on the error queue,
+however, the full packet is queued, taking up budget from SO_RCVBUF.
+
+
+2.1.1.5 Blocking Read
+~~~~~~~~~~~~~~~~~~~~~
+
+Reading from the error queue is always a non-blocking operation. To
+block waiting on a timestamp, use poll or select. poll() will return
+POLLERR in pollfd.revents if any data is ready on the error queue.
+There is no need to pass this flag in pollfd.events. This flag is
+ignored on request. See also `man 2 poll`.
+
+
+2.1.2 Receive timestamps
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+On reception, there is no reason to read from the socket error queue.
+The SCM_TIMESTAMPING ancillary data is sent along with the packet data
+on a normal recvmsg(). Since this is not a socket error, it is not
+accompanied by a message SOL_IP(V6)/IP(V6)_RECVERROR. In this case,
+the meaning of the three fields in struct scm_timestamping is
+implicitly defined. ts[0] holds a software timestamp if set, ts[1]
+is again deprecated and ts[2] holds a hardware timestamp if set.
+
+
+3. Hardware Timestamping configuration: SIOCSHWTSTAMP and SIOCGHWTSTAMP
+=======================================================================
+
+Hardware time stamping must also be initialized for each device driver
+that is expected to do hardware time stamping. The parameter is defined in
+include/uapi/linux/net_tstamp.h as::
+
+       struct hwtstamp_config {
+               int flags;      /* no flags defined right now, must be zero */
+               int tx_type;    /* HWTSTAMP_TX_* */
+               int rx_filter;  /* HWTSTAMP_FILTER_* */
+       };
+
+Desired behavior is passed into the kernel and to a specific device by
+calling ioctl(SIOCSHWTSTAMP) with a pointer to a struct ifreq whose
+ifr_data points to a struct hwtstamp_config. The tx_type and
+rx_filter are hints to the driver what it is expected to do. If
+the requested fine-grained filtering for incoming packets is not
+supported, the driver may time stamp more than just the requested types
+of packets.
+
+Drivers are free to use a more permissive configuration than the requested
+configuration. It is expected that drivers should only implement directly the
+most generic mode that can be supported. For example if the hardware can
+support HWTSTAMP_FILTER_V2_EVENT, then it should generally always upscale
+HWTSTAMP_FILTER_V2_L2_SYNC_MESSAGE, and so forth, as HWTSTAMP_FILTER_V2_EVENT
+is more generic (and more useful to applications).
+
+A driver which supports hardware time stamping shall update the struct
+with the actual, possibly more permissive configuration. If the
+requested packets cannot be time stamped, then nothing should be
+changed and ERANGE shall be returned (in contrast to EINVAL, which
+indicates that SIOCSHWTSTAMP is not supported at all).
+
+Only a processes with admin rights may change the configuration. User
+space is responsible to ensure that multiple processes don't interfere
+with each other and that the settings are reset.
+
+Any process can read the actual configuration by passing this
+structure to ioctl(SIOCGHWTSTAMP) in the same way.  However, this has
+not been implemented in all drivers.
+
+::
+
+    /* possible values for hwtstamp_config->tx_type */
+    enum {
+           /*
+           * no outgoing packet will need hardware time stamping;
+           * should a packet arrive which asks for it, no hardware
+           * time stamping will be done
+           */
+           HWTSTAMP_TX_OFF,
+
+           /*
+           * enables hardware time stamping for outgoing packets;
+           * the sender of the packet decides which are to be
+           * time stamped by setting SOF_TIMESTAMPING_TX_SOFTWARE
+           * before sending the packet
+           */
+           HWTSTAMP_TX_ON,
+    };
+
+    /* possible values for hwtstamp_config->rx_filter */
+    enum {
+           /* time stamp no incoming packet at all */
+           HWTSTAMP_FILTER_NONE,
+
+           /* time stamp any incoming packet */
+           HWTSTAMP_FILTER_ALL,
+
+           /* return value: time stamp all packets requested plus some others */
+           HWTSTAMP_FILTER_SOME,
+
+           /* PTP v1, UDP, any kind of event packet */
+           HWTSTAMP_FILTER_PTP_V1_L4_EVENT,
+
+           /* for the complete list of values, please check
+           * the include file include/uapi/linux/net_tstamp.h
+           */
+    };
+
+3.1 Hardware Timestamping Implementation: Device Drivers
+--------------------------------------------------------
+
+A driver which supports hardware time stamping must support the
+SIOCSHWTSTAMP ioctl and update the supplied struct hwtstamp_config with
+the actual values as described in the section on SIOCSHWTSTAMP.  It
+should also support SIOCGHWTSTAMP.
+
+Time stamps for received packets must be stored in the skb. To get a pointer
+to the shared time stamp structure of the skb call skb_hwtstamps(). Then
+set the time stamps in the structure::
+
+    struct skb_shared_hwtstamps {
+           /* hardware time stamp transformed into duration
+           * since arbitrary point in time
+           */
+           ktime_t     hwtstamp;
+    };
+
+Time stamps for outgoing packets are to be generated as follows:
+
+- In hard_start_xmit(), check if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
+  is set no-zero. If yes, then the driver is expected to do hardware time
+  stamping.
+- If this is possible for the skb and requested, then declare
+  that the driver is doing the time stamping by setting the flag
+  SKBTX_IN_PROGRESS in skb_shinfo(skb)->tx_flags , e.g. with::
+
+      skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+
+  You might want to keep a pointer to the associated skb for the next step
+  and not free the skb. A driver not supporting hardware time stamping doesn't
+  do that. A driver must never touch sk_buff::tstamp! It is used to store
+  software generated time stamps by the network subsystem.
+- Driver should call skb_tx_timestamp() as close to passing sk_buff to hardware
+  as possible. skb_tx_timestamp() provides a software time stamp if requested
+  and hardware timestamping is not possible (SKBTX_IN_PROGRESS not set).
+- As soon as the driver has sent the packet and/or obtained a
+  hardware time stamp for it, it passes the time stamp back by
+  calling skb_hwtstamp_tx() with the original skb, the raw
+  hardware time stamp. skb_hwtstamp_tx() clones the original skb and
+  adds the timestamps, therefore the original skb has to be freed now.
+  If obtaining the hardware time stamp somehow fails, then the driver
+  should not fall back to software time stamping. The rationale is that
+  this would occur at a later time in the processing pipeline than other
+  software time stamping and therefore could lead to unexpected deltas
+  between time stamps.
diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt
deleted file mode 100644 (file)
index 8dd6333..0000000
+++ /dev/null
@@ -1,571 +0,0 @@
-
-1. Control Interfaces
-
-The interfaces for receiving network packages timestamps are:
-
-* SO_TIMESTAMP
-  Generates a timestamp for each incoming packet in (not necessarily
-  monotonic) system time. Reports the timestamp via recvmsg() in a
-  control message in usec resolution.
-  SO_TIMESTAMP is defined as SO_TIMESTAMP_NEW or SO_TIMESTAMP_OLD
-  based on the architecture type and time_t representation of libc.
-  Control message format is in struct __kernel_old_timeval for
-  SO_TIMESTAMP_OLD and in struct __kernel_sock_timeval for
-  SO_TIMESTAMP_NEW options respectively.
-
-* SO_TIMESTAMPNS
-  Same timestamping mechanism as SO_TIMESTAMP, but reports the
-  timestamp as struct timespec in nsec resolution.
-  SO_TIMESTAMPNS is defined as SO_TIMESTAMPNS_NEW or SO_TIMESTAMPNS_OLD
-  based on the architecture type and time_t representation of libc.
-  Control message format is in struct timespec for SO_TIMESTAMPNS_OLD
-  and in struct __kernel_timespec for SO_TIMESTAMPNS_NEW options
-  respectively.
-
-* IP_MULTICAST_LOOP + SO_TIMESTAMP[NS]
-  Only for multicast:approximate transmit timestamp obtained by
-  reading the looped packet receive timestamp.
-
-* SO_TIMESTAMPING
-  Generates timestamps on reception, transmission or both. Supports
-  multiple timestamp sources, including hardware. Supports generating
-  timestamps for stream sockets.
-
-
-1.1 SO_TIMESTAMP (also SO_TIMESTAMP_OLD and SO_TIMESTAMP_NEW):
-
-This socket option enables timestamping of datagrams on the reception
-path. Because the destination socket, if any, is not known early in
-the network stack, the feature has to be enabled for all packets. The
-same is true for all early receive timestamp options.
-
-For interface details, see `man 7 socket`.
-
-Always use SO_TIMESTAMP_NEW timestamp to always get timestamp in
-struct __kernel_sock_timeval format.
-
-SO_TIMESTAMP_OLD returns incorrect timestamps after the year 2038
-on 32 bit machines.
-
-1.2 SO_TIMESTAMPNS (also SO_TIMESTAMPNS_OLD and SO_TIMESTAMPNS_NEW):
-
-This option is identical to SO_TIMESTAMP except for the returned data type.
-Its struct timespec allows for higher resolution (ns) timestamps than the
-timeval of SO_TIMESTAMP (ms).
-
-Always use SO_TIMESTAMPNS_NEW timestamp to always get timestamp in
-struct __kernel_timespec format.
-
-SO_TIMESTAMPNS_OLD returns incorrect timestamps after the year 2038
-on 32 bit machines.
-
-1.3 SO_TIMESTAMPING (also SO_TIMESTAMPING_OLD and SO_TIMESTAMPING_NEW):
-
-Supports multiple types of timestamp requests. As a result, this
-socket option takes a bitmap of flags, not a boolean. In
-
-  err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));
-
-val is an integer with any of the following bits set. Setting other
-bit returns EINVAL and does not change the current state.
-
-The socket option configures timestamp generation for individual
-sk_buffs (1.3.1), timestamp reporting to the socket's error
-queue (1.3.2) and options (1.3.3). Timestamp generation can also
-be enabled for individual sendmsg calls using cmsg (1.3.4).
-
-
-1.3.1 Timestamp Generation
-
-Some bits are requests to the stack to try to generate timestamps. Any
-combination of them is valid. Changes to these bits apply to newly
-created packets, not to packets already in the stack. As a result, it
-is possible to selectively request timestamps for a subset of packets
-(e.g., for sampling) by embedding an send() call within two setsockopt
-calls, one to enable timestamp generation and one to disable it.
-Timestamps may also be generated for reasons other than being
-requested by a particular socket, such as when receive timestamping is
-enabled system wide, as explained earlier.
-
-SOF_TIMESTAMPING_RX_HARDWARE:
-  Request rx timestamps generated by the network adapter.
-
-SOF_TIMESTAMPING_RX_SOFTWARE:
-  Request rx timestamps when data enters the kernel. These timestamps
-  are generated just after a device driver hands a packet to the
-  kernel receive stack.
-
-SOF_TIMESTAMPING_TX_HARDWARE:
-  Request tx timestamps generated by the network adapter. This flag
-  can be enabled via both socket options and control messages.
-
-SOF_TIMESTAMPING_TX_SOFTWARE:
-  Request tx timestamps when data leaves the kernel. These timestamps
-  are generated in the device driver as close as possible, but always
-  prior to, passing the packet to the network interface. Hence, they
-  require driver support and may not be available for all devices.
-  This flag can be enabled via both socket options and control messages.
-
-
-SOF_TIMESTAMPING_TX_SCHED:
-  Request tx timestamps prior to entering the packet scheduler. Kernel
-  transmit latency is, if long, often dominated by queuing delay. The
-  difference between this timestamp and one taken at
-  SOF_TIMESTAMPING_TX_SOFTWARE will expose this latency independent
-  of protocol processing. The latency incurred in protocol
-  processing, if any, can be computed by subtracting a userspace
-  timestamp taken immediately before send() from this timestamp. On
-  machines with virtual devices where a transmitted packet travels
-  through multiple devices and, hence, multiple packet schedulers,
-  a timestamp is generated at each layer. This allows for fine
-  grained measurement of queuing delay. This flag can be enabled
-  via both socket options and control messages.
-
-SOF_TIMESTAMPING_TX_ACK:
-  Request tx timestamps when all data in the send buffer has been
-  acknowledged. This only makes sense for reliable protocols. It is
-  currently only implemented for TCP. For that protocol, it may
-  over-report measurement, because the timestamp is generated when all
-  data up to and including the buffer at send() was acknowledged: the
-  cumulative acknowledgment. The mechanism ignores SACK and FACK.
-  This flag can be enabled via both socket options and control messages.
-
-
-1.3.2 Timestamp Reporting
-
-The other three bits control which timestamps will be reported in a
-generated control message. Changes to the bits take immediate
-effect at the timestamp reporting locations in the stack. Timestamps
-are only reported for packets that also have the relevant timestamp
-generation request set.
-
-SOF_TIMESTAMPING_SOFTWARE:
-  Report any software timestamps when available.
-
-SOF_TIMESTAMPING_SYS_HARDWARE:
-  This option is deprecated and ignored.
-
-SOF_TIMESTAMPING_RAW_HARDWARE:
-  Report hardware timestamps as generated by
-  SOF_TIMESTAMPING_TX_HARDWARE when available.
-
-
-1.3.3 Timestamp Options
-
-The interface supports the options
-
-SOF_TIMESTAMPING_OPT_ID:
-
-  Generate a unique identifier along with each packet. A process can
-  have multiple concurrent timestamping requests outstanding. Packets
-  can be reordered in the transmit path, for instance in the packet
-  scheduler. In that case timestamps will be queued onto the error
-  queue out of order from the original send() calls. It is not always
-  possible to uniquely match timestamps to the original send() calls
-  based on timestamp order or payload inspection alone, then.
-
-  This option associates each packet at send() with a unique
-  identifier and returns that along with the timestamp. The identifier
-  is derived from a per-socket u32 counter (that wraps). For datagram
-  sockets, the counter increments with each sent packet. For stream
-  sockets, it increments with every byte.
-
-  The counter starts at zero. It is initialized the first time that
-  the socket option is enabled. It is reset each time the option is
-  enabled after having been disabled. Resetting the counter does not
-  change the identifiers of existing packets in the system.
-
-  This option is implemented only for transmit timestamps. There, the
-  timestamp is always looped along with a struct sock_extended_err.
-  The option modifies field ee_data to pass an id that is unique
-  among all possibly concurrently outstanding timestamp requests for
-  that socket.
-
-
-SOF_TIMESTAMPING_OPT_CMSG:
-
-  Support recv() cmsg for all timestamped packets. Control messages
-  are already supported unconditionally on all packets with receive
-  timestamps and on IPv6 packets with transmit timestamp. This option
-  extends them to IPv4 packets with transmit timestamp. One use case
-  is to correlate packets with their egress device, by enabling socket
-  option IP_PKTINFO simultaneously.
-
-
-SOF_TIMESTAMPING_OPT_TSONLY:
-
-  Applies to transmit timestamps only. Makes the kernel return the
-  timestamp as a cmsg alongside an empty packet, as opposed to
-  alongside the original packet. This reduces the amount of memory
-  charged to the socket's receive budget (SO_RCVBUF) and delivers
-  the timestamp even if sysctl net.core.tstamp_allow_data is 0.
-  This option disables SOF_TIMESTAMPING_OPT_CMSG.
-
-SOF_TIMESTAMPING_OPT_STATS:
-
-  Optional stats that are obtained along with the transmit timestamps.
-  It must be used together with SOF_TIMESTAMPING_OPT_TSONLY. When the
-  transmit timestamp is available, the stats are available in a
-  separate control message of type SCM_TIMESTAMPING_OPT_STATS, as a
-  list of TLVs (struct nlattr) of types. These stats allow the
-  application to associate various transport layer stats with
-  the transmit timestamps, such as how long a certain block of
-  data was limited by peer's receiver window.
-
-SOF_TIMESTAMPING_OPT_PKTINFO:
-
-  Enable the SCM_TIMESTAMPING_PKTINFO control message for incoming
-  packets with hardware timestamps. The message contains struct
-  scm_ts_pktinfo, which supplies the index of the real interface which
-  received the packet and its length at layer 2. A valid (non-zero)
-  interface index will be returned only if CONFIG_NET_RX_BUSY_POLL is
-  enabled and the driver is using NAPI. The struct contains also two
-  other fields, but they are reserved and undefined.
-
-SOF_TIMESTAMPING_OPT_TX_SWHW:
-
-  Request both hardware and software timestamps for outgoing packets
-  when SOF_TIMESTAMPING_TX_HARDWARE and SOF_TIMESTAMPING_TX_SOFTWARE
-  are enabled at the same time. If both timestamps are generated,
-  two separate messages will be looped to the socket's error queue,
-  each containing just one timestamp.
-
-New applications are encouraged to pass SOF_TIMESTAMPING_OPT_ID to
-disambiguate timestamps and SOF_TIMESTAMPING_OPT_TSONLY to operate
-regardless of the setting of sysctl net.core.tstamp_allow_data.
-
-An exception is when a process needs additional cmsg data, for
-instance SOL_IP/IP_PKTINFO to detect the egress network interface.
-Then pass option SOF_TIMESTAMPING_OPT_CMSG. This option depends on
-having access to the contents of the original packet, so cannot be
-combined with SOF_TIMESTAMPING_OPT_TSONLY.
-
-
-1.3.4. Enabling timestamps via control messages
-
-In addition to socket options, timestamp generation can be requested
-per write via cmsg, only for SOF_TIMESTAMPING_TX_* (see Section 1.3.1).
-Using this feature, applications can sample timestamps per sendmsg()
-without paying the overhead of enabling and disabling timestamps via
-setsockopt:
-
-  struct msghdr *msg;
-  ...
-  cmsg                        = CMSG_FIRSTHDR(msg);
-  cmsg->cmsg_level            = SOL_SOCKET;
-  cmsg->cmsg_type             = SO_TIMESTAMPING;
-  cmsg->cmsg_len              = CMSG_LEN(sizeof(__u32));
-  *((__u32 *) CMSG_DATA(cmsg)) = SOF_TIMESTAMPING_TX_SCHED |
-                                SOF_TIMESTAMPING_TX_SOFTWARE |
-                                SOF_TIMESTAMPING_TX_ACK;
-  err = sendmsg(fd, msg, 0);
-
-The SOF_TIMESTAMPING_TX_* flags set via cmsg will override
-the SOF_TIMESTAMPING_TX_* flags set via setsockopt.
-
-Moreover, applications must still enable timestamp reporting via
-setsockopt to receive timestamps:
-
-  __u32 val = SOF_TIMESTAMPING_SOFTWARE |
-             SOF_TIMESTAMPING_OPT_ID /* or any other flag */;
-  err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));
-
-
-1.4 Bytestream Timestamps
-
-The SO_TIMESTAMPING interface supports timestamping of bytes in a
-bytestream. Each request is interpreted as a request for when the
-entire contents of the buffer has passed a timestamping point. That
-is, for streams option SOF_TIMESTAMPING_TX_SOFTWARE will record
-when all bytes have reached the device driver, regardless of how
-many packets the data has been converted into.
-
-In general, bytestreams have no natural delimiters and therefore
-correlating a timestamp with data is non-trivial. A range of bytes
-may be split across segments, any segments may be merged (possibly
-coalescing sections of previously segmented buffers associated with
-independent send() calls). Segments can be reordered and the same
-byte range can coexist in multiple segments for protocols that
-implement retransmissions.
-
-It is essential that all timestamps implement the same semantics,
-regardless of these possible transformations, as otherwise they are
-incomparable. Handling "rare" corner cases differently from the
-simple case (a 1:1 mapping from buffer to skb) is insufficient
-because performance debugging often needs to focus on such outliers.
-
-In practice, timestamps can be correlated with segments of a
-bytestream consistently, if both semantics of the timestamp and the
-timing of measurement are chosen correctly. This challenge is no
-different from deciding on a strategy for IP fragmentation. There, the
-definition is that only the first fragment is timestamped. For
-bytestreams, we chose that a timestamp is generated only when all
-bytes have passed a point. SOF_TIMESTAMPING_TX_ACK as defined is easy to
-implement and reason about. An implementation that has to take into
-account SACK would be more complex due to possible transmission holes
-and out of order arrival.
-
-On the host, TCP can also break the simple 1:1 mapping from buffer to
-skbuff as a result of Nagle, cork, autocork, segmentation and GSO. The
-implementation ensures correctness in all cases by tracking the
-individual last byte passed to send(), even if it is no longer the
-last byte after an skbuff extend or merge operation. It stores the
-relevant sequence number in skb_shinfo(skb)->tskey. Because an skbuff
-has only one such field, only one timestamp can be generated.
-
-In rare cases, a timestamp request can be missed if two requests are
-collapsed onto the same skb. A process can detect this situation by
-enabling SOF_TIMESTAMPING_OPT_ID and comparing the byte offset at
-send time with the value returned for each timestamp. It can prevent
-the situation by always flushing the TCP stack in between requests,
-for instance by enabling TCP_NODELAY and disabling TCP_CORK and
-autocork.
-
-These precautions ensure that the timestamp is generated only when all
-bytes have passed a timestamp point, assuming that the network stack
-itself does not reorder the segments. The stack indeed tries to avoid
-reordering. The one exception is under administrator control: it is
-possible to construct a packet scheduler configuration that delays
-segments from the same stream differently. Such a setup would be
-unusual.
-
-
-2 Data Interfaces
-
-Timestamps are read using the ancillary data feature of recvmsg().
-See `man 3 cmsg` for details of this interface. The socket manual
-page (`man 7 socket`) describes how timestamps generated with
-SO_TIMESTAMP and SO_TIMESTAMPNS records can be retrieved.
-
-
-2.1 SCM_TIMESTAMPING records
-
-These timestamps are returned in a control message with cmsg_level
-SOL_SOCKET, cmsg_type SCM_TIMESTAMPING, and payload of type
-
-For SO_TIMESTAMPING_OLD:
-
-struct scm_timestamping {
-       struct timespec ts[3];
-};
-
-For SO_TIMESTAMPING_NEW:
-
-struct scm_timestamping64 {
-       struct __kernel_timespec ts[3];
-
-Always use SO_TIMESTAMPING_NEW timestamp to always get timestamp in
-struct scm_timestamping64 format.
-
-SO_TIMESTAMPING_OLD returns incorrect timestamps after the year 2038
-on 32 bit machines.
-
-The structure can return up to three timestamps. This is a legacy
-feature. At least one field is non-zero at any time. Most timestamps
-are passed in ts[0]. Hardware timestamps are passed in ts[2].
-
-ts[1] used to hold hardware timestamps converted to system time.
-Instead, expose the hardware clock device on the NIC directly as
-a HW PTP clock source, to allow time conversion in userspace and
-optionally synchronize system time with a userspace PTP stack such
-as linuxptp. For the PTP clock API, see Documentation/driver-api/ptp.rst.
-
-Note that if the SO_TIMESTAMP or SO_TIMESTAMPNS option is enabled
-together with SO_TIMESTAMPING using SOF_TIMESTAMPING_SOFTWARE, a false
-software timestamp will be generated in the recvmsg() call and passed
-in ts[0] when a real software timestamp is missing. This happens also
-on hardware transmit timestamps.
-
-2.1.1 Transmit timestamps with MSG_ERRQUEUE
-
-For transmit timestamps the outgoing packet is looped back to the
-socket's error queue with the send timestamp(s) attached. A process
-receives the timestamps by calling recvmsg() with flag MSG_ERRQUEUE
-set and with a msg_control buffer sufficiently large to receive the
-relevant metadata structures. The recvmsg call returns the original
-outgoing data packet with two ancillary messages attached.
-
-A message of cm_level SOL_IP(V6) and cm_type IP(V6)_RECVERR
-embeds a struct sock_extended_err. This defines the error type. For
-timestamps, the ee_errno field is ENOMSG. The other ancillary message
-will have cm_level SOL_SOCKET and cm_type SCM_TIMESTAMPING. This
-embeds the struct scm_timestamping.
-
-
-2.1.1.2 Timestamp types
-
-The semantics of the three struct timespec are defined by field
-ee_info in the extended error structure. It contains a value of
-type SCM_TSTAMP_* to define the actual timestamp passed in
-scm_timestamping.
-
-The SCM_TSTAMP_* types are 1:1 matches to the SOF_TIMESTAMPING_*
-control fields discussed previously, with one exception. For legacy
-reasons, SCM_TSTAMP_SND is equal to zero and can be set for both
-SOF_TIMESTAMPING_TX_HARDWARE and SOF_TIMESTAMPING_TX_SOFTWARE. It
-is the first if ts[2] is non-zero, the second otherwise, in which
-case the timestamp is stored in ts[0].
-
-
-2.1.1.3 Fragmentation
-
-Fragmentation of outgoing datagrams is rare, but is possible, e.g., by
-explicitly disabling PMTU discovery. If an outgoing packet is fragmented,
-then only the first fragment is timestamped and returned to the sending
-socket.
-
-
-2.1.1.4 Packet Payload
-
-The calling application is often not interested in receiving the whole
-packet payload that it passed to the stack originally: the socket
-error queue mechanism is just a method to piggyback the timestamp on.
-In this case, the application can choose to read datagrams with a
-smaller buffer, possibly even of length 0. The payload is truncated
-accordingly. Until the process calls recvmsg() on the error queue,
-however, the full packet is queued, taking up budget from SO_RCVBUF.
-
-
-2.1.1.5 Blocking Read
-
-Reading from the error queue is always a non-blocking operation. To
-block waiting on a timestamp, use poll or select. poll() will return
-POLLERR in pollfd.revents if any data is ready on the error queue.
-There is no need to pass this flag in pollfd.events. This flag is
-ignored on request. See also `man 2 poll`.
-
-
-2.1.2 Receive timestamps
-
-On reception, there is no reason to read from the socket error queue.
-The SCM_TIMESTAMPING ancillary data is sent along with the packet data
-on a normal recvmsg(). Since this is not a socket error, it is not
-accompanied by a message SOL_IP(V6)/IP(V6)_RECVERROR. In this case,
-the meaning of the three fields in struct scm_timestamping is
-implicitly defined. ts[0] holds a software timestamp if set, ts[1]
-is again deprecated and ts[2] holds a hardware timestamp if set.
-
-
-3. Hardware Timestamping configuration: SIOCSHWTSTAMP and SIOCGHWTSTAMP
-
-Hardware time stamping must also be initialized for each device driver
-that is expected to do hardware time stamping. The parameter is defined in
-include/uapi/linux/net_tstamp.h as:
-
-struct hwtstamp_config {
-       int flags;      /* no flags defined right now, must be zero */
-       int tx_type;    /* HWTSTAMP_TX_* */
-       int rx_filter;  /* HWTSTAMP_FILTER_* */
-};
-
-Desired behavior is passed into the kernel and to a specific device by
-calling ioctl(SIOCSHWTSTAMP) with a pointer to a struct ifreq whose
-ifr_data points to a struct hwtstamp_config. The tx_type and
-rx_filter are hints to the driver what it is expected to do. If
-the requested fine-grained filtering for incoming packets is not
-supported, the driver may time stamp more than just the requested types
-of packets.
-
-Drivers are free to use a more permissive configuration than the requested
-configuration. It is expected that drivers should only implement directly the
-most generic mode that can be supported. For example if the hardware can
-support HWTSTAMP_FILTER_V2_EVENT, then it should generally always upscale
-HWTSTAMP_FILTER_V2_L2_SYNC_MESSAGE, and so forth, as HWTSTAMP_FILTER_V2_EVENT
-is more generic (and more useful to applications).
-
-A driver which supports hardware time stamping shall update the struct
-with the actual, possibly more permissive configuration. If the
-requested packets cannot be time stamped, then nothing should be
-changed and ERANGE shall be returned (in contrast to EINVAL, which
-indicates that SIOCSHWTSTAMP is not supported at all).
-
-Only a processes with admin rights may change the configuration. User
-space is responsible to ensure that multiple processes don't interfere
-with each other and that the settings are reset.
-
-Any process can read the actual configuration by passing this
-structure to ioctl(SIOCGHWTSTAMP) in the same way.  However, this has
-not been implemented in all drivers.
-
-/* possible values for hwtstamp_config->tx_type */
-enum {
-       /*
-        * no outgoing packet will need hardware time stamping;
-        * should a packet arrive which asks for it, no hardware
-        * time stamping will be done
-        */
-       HWTSTAMP_TX_OFF,
-
-       /*
-        * enables hardware time stamping for outgoing packets;
-        * the sender of the packet decides which are to be
-        * time stamped by setting SOF_TIMESTAMPING_TX_SOFTWARE
-        * before sending the packet
-        */
-       HWTSTAMP_TX_ON,
-};
-
-/* possible values for hwtstamp_config->rx_filter */
-enum {
-       /* time stamp no incoming packet at all */
-       HWTSTAMP_FILTER_NONE,
-
-       /* time stamp any incoming packet */
-       HWTSTAMP_FILTER_ALL,
-
-       /* return value: time stamp all packets requested plus some others */
-       HWTSTAMP_FILTER_SOME,
-
-       /* PTP v1, UDP, any kind of event packet */
-       HWTSTAMP_FILTER_PTP_V1_L4_EVENT,
-
-       /* for the complete list of values, please check
-        * the include file include/uapi/linux/net_tstamp.h
-        */
-};
-
-3.1 Hardware Timestamping Implementation: Device Drivers
-
-A driver which supports hardware time stamping must support the
-SIOCSHWTSTAMP ioctl and update the supplied struct hwtstamp_config with
-the actual values as described in the section on SIOCSHWTSTAMP.  It
-should also support SIOCGHWTSTAMP.
-
-Time stamps for received packets must be stored in the skb. To get a pointer
-to the shared time stamp structure of the skb call skb_hwtstamps(). Then
-set the time stamps in the structure:
-
-struct skb_shared_hwtstamps {
-       /* hardware time stamp transformed into duration
-        * since arbitrary point in time
-        */
-       ktime_t hwtstamp;
-};
-
-Time stamps for outgoing packets are to be generated as follows:
-- In hard_start_xmit(), check if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
-  is set no-zero. If yes, then the driver is expected to do hardware time
-  stamping.
-- If this is possible for the skb and requested, then declare
-  that the driver is doing the time stamping by setting the flag
-  SKBTX_IN_PROGRESS in skb_shinfo(skb)->tx_flags , e.g. with
-
-      skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
-
-  You might want to keep a pointer to the associated skb for the next step
-  and not free the skb. A driver not supporting hardware time stamping doesn't
-  do that. A driver must never touch sk_buff::tstamp! It is used to store
-  software generated time stamps by the network subsystem.
-- Driver should call skb_tx_timestamp() as close to passing sk_buff to hardware
-  as possible. skb_tx_timestamp() provides a software time stamp if requested
-  and hardware timestamping is not possible (SKBTX_IN_PROGRESS not set).
-- As soon as the driver has sent the packet and/or obtained a
-  hardware time stamp for it, it passes the time stamp back by
-  calling skb_hwtstamp_tx() with the original skb, the raw
-  hardware time stamp. skb_hwtstamp_tx() clones the original skb and
-  adds the timestamps, therefore the original skb has to be freed now.
-  If obtaining the hardware time stamp somehow fails, then the driver
-  should not fall back to software time stamping. The rationale is that
-  this would occur at a later time in the processing pipeline than other
-  software time stamping and therefore could lead to unexpected deltas
-  between time stamps.
diff --git a/Documentation/networking/tproxy.rst b/Documentation/networking/tproxy.rst
new file mode 100644 (file)
index 0000000..00dc3a1
--- /dev/null
@@ -0,0 +1,109 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================
+Transparent proxy support
+=========================
+
+This feature adds Linux 2.2-like transparent proxy support to current kernels.
+To use it, enable the socket match and the TPROXY target in your kernel config.
+You will need policy routing too, so be sure to enable that as well.
+
+From Linux 4.18 transparent proxy support is also available in nf_tables.
+
+1. Making non-local sockets work
+================================
+
+The idea is that you identify packets with destination address matching a local
+socket on your box, set the packet mark to a certain value::
+
+    # iptables -t mangle -N DIVERT
+    # iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
+    # iptables -t mangle -A DIVERT -j MARK --set-mark 1
+    # iptables -t mangle -A DIVERT -j ACCEPT
+
+Alternatively you can do this in nft with the following commands::
+
+    # nft add table filter
+    # nft add chain filter divert "{ type filter hook prerouting priority -150; }"
+    # nft add rule filter divert meta l4proto tcp socket transparent 1 meta mark set 1 accept
+
+And then match on that value using policy routing to have those packets
+delivered locally::
+
+    # ip rule add fwmark 1 lookup 100
+    # ip route add local 0.0.0.0/0 dev lo table 100
+
+Because of certain restrictions in the IPv4 routing output code you'll have to
+modify your application to allow it to send datagrams _from_ non-local IP
+addresses. All you have to do is enable the (SOL_IP, IP_TRANSPARENT) socket
+option before calling bind::
+
+    fd = socket(AF_INET, SOCK_STREAM, 0);
+    /* - 8< -*/
+    int value = 1;
+    setsockopt(fd, SOL_IP, IP_TRANSPARENT, &value, sizeof(value));
+    /* - 8< -*/
+    name.sin_family = AF_INET;
+    name.sin_port = htons(0xCAFE);
+    name.sin_addr.s_addr = htonl(0xDEADBEEF);
+    bind(fd, &name, sizeof(name));
+
+A trivial patch for netcat is available here:
+http://people.netfilter.org/hidden/tproxy/netcat-ip_transparent-support.patch
+
+
+2. Redirecting traffic
+======================
+
+Transparent proxying often involves "intercepting" traffic on a router. This is
+usually done with the iptables REDIRECT target; however, there are serious
+limitations of that method. One of the major issues is that it actually
+modifies the packets to change the destination address -- which might not be
+acceptable in certain situations. (Think of proxying UDP for example: you won't
+be able to find out the original destination address. Even in case of TCP
+getting the original destination address is racy.)
+
+The 'TPROXY' target provides similar functionality without relying on NAT. Simply
+add rules like this to the iptables ruleset above::
+
+    # iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TPROXY \
+      --tproxy-mark 0x1/0x1 --on-port 50080
+
+Or the following rule to nft:
+
+# nft add rule filter divert tcp dport 80 tproxy to :50080 meta mark set 1 accept
+
+Note that for this to work you'll have to modify the proxy to enable (SOL_IP,
+IP_TRANSPARENT) for the listening socket.
+
+As an example implementation, tcprdr is available here:
+https://git.breakpoint.cc/cgit/fw/tcprdr.git/
+This tool is written by Florian Westphal and it was used for testing during the
+nf_tables implementation.
+
+3. Iptables and nf_tables extensions
+====================================
+
+To use tproxy you'll need to have the following modules compiled for iptables:
+
+ - NETFILTER_XT_MATCH_SOCKET
+ - NETFILTER_XT_TARGET_TPROXY
+
+Or the floowing modules for nf_tables:
+
+ - NFT_SOCKET
+ - NFT_TPROXY
+
+4. Application support
+======================
+
+4.1. Squid
+----------
+
+Squid 3.HEAD has support built-in. To use it, pass
+'--enable-linux-netfilter' to configure and set the 'tproxy' option on
+the HTTP listener you redirect traffic to with the TPROXY iptables
+target.
+
+For more information please consult the following page on the Squid
+wiki: http://wiki.squid-cache.org/Features/Tproxy4
diff --git a/Documentation/networking/tproxy.txt b/Documentation/networking/tproxy.txt
deleted file mode 100644 (file)
index b9a1888..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-Transparent proxy support
-=========================
-
-This feature adds Linux 2.2-like transparent proxy support to current kernels.
-To use it, enable the socket match and the TPROXY target in your kernel config.
-You will need policy routing too, so be sure to enable that as well.
-
-From Linux 4.18 transparent proxy support is also available in nf_tables.
-
-1. Making non-local sockets work
-================================
-
-The idea is that you identify packets with destination address matching a local
-socket on your box, set the packet mark to a certain value:
-
-# iptables -t mangle -N DIVERT
-# iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
-# iptables -t mangle -A DIVERT -j MARK --set-mark 1
-# iptables -t mangle -A DIVERT -j ACCEPT
-
-Alternatively you can do this in nft with the following commands:
-
-# nft add table filter
-# nft add chain filter divert "{ type filter hook prerouting priority -150; }"
-# nft add rule filter divert meta l4proto tcp socket transparent 1 meta mark set 1 accept
-
-And then match on that value using policy routing to have those packets
-delivered locally:
-
-# ip rule add fwmark 1 lookup 100
-# ip route add local 0.0.0.0/0 dev lo table 100
-
-Because of certain restrictions in the IPv4 routing output code you'll have to
-modify your application to allow it to send datagrams _from_ non-local IP
-addresses. All you have to do is enable the (SOL_IP, IP_TRANSPARENT) socket
-option before calling bind:
-
-fd = socket(AF_INET, SOCK_STREAM, 0);
-/* - 8< -*/
-int value = 1;
-setsockopt(fd, SOL_IP, IP_TRANSPARENT, &value, sizeof(value));
-/* - 8< -*/
-name.sin_family = AF_INET;
-name.sin_port = htons(0xCAFE);
-name.sin_addr.s_addr = htonl(0xDEADBEEF);
-bind(fd, &name, sizeof(name));
-
-A trivial patch for netcat is available here:
-http://people.netfilter.org/hidden/tproxy/netcat-ip_transparent-support.patch
-
-
-2. Redirecting traffic
-======================
-
-Transparent proxying often involves "intercepting" traffic on a router. This is
-usually done with the iptables REDIRECT target; however, there are serious
-limitations of that method. One of the major issues is that it actually
-modifies the packets to change the destination address -- which might not be
-acceptable in certain situations. (Think of proxying UDP for example: you won't
-be able to find out the original destination address. Even in case of TCP
-getting the original destination address is racy.)
-
-The 'TPROXY' target provides similar functionality without relying on NAT. Simply
-add rules like this to the iptables ruleset above:
-
-# iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TPROXY \
-  --tproxy-mark 0x1/0x1 --on-port 50080
-
-Or the following rule to nft:
-
-# nft add rule filter divert tcp dport 80 tproxy to :50080 meta mark set 1 accept
-
-Note that for this to work you'll have to modify the proxy to enable (SOL_IP,
-IP_TRANSPARENT) for the listening socket.
-
-As an example implementation, tcprdr is available here:
-https://git.breakpoint.cc/cgit/fw/tcprdr.git/
-This tool is written by Florian Westphal and it was used for testing during the
-nf_tables implementation.
-
-3. Iptables and nf_tables extensions
-====================================
-
-To use tproxy you'll need to have the following modules compiled for iptables:
- - NETFILTER_XT_MATCH_SOCKET
- - NETFILTER_XT_TARGET_TPROXY
-
-Or the floowing modules for nf_tables:
- - NFT_SOCKET
- - NFT_TPROXY
-
-4. Application support
-======================
-
-4.1. Squid
-----------
-
-Squid 3.HEAD has support built-in. To use it, pass
-'--enable-linux-netfilter' to configure and set the 'tproxy' option on
-the HTTP listener you redirect traffic to with the TPROXY iptables
-target.
-
-For more information please consult the following page on the Squid
-wiki: http://wiki.squid-cache.org/Features/Tproxy4
diff --git a/Documentation/networking/tuntap.rst b/Documentation/networking/tuntap.rst
new file mode 100644 (file)
index 0000000..a59d1dd
--- /dev/null
@@ -0,0 +1,259 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+===============================
+Universal TUN/TAP device driver
+===============================
+
+Copyright |copy| 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
+
+  Linux, Solaris drivers
+  Copyright |copy| 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
+
+  FreeBSD TAP driver
+  Copyright |copy| 1999-2000 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+
+  Revision of this document 2002 by Florian Thiel <florian.thiel@gmx.net>
+
+1. Description
+==============
+
+  TUN/TAP provides packet reception and transmission for user space programs.
+  It can be seen as a simple Point-to-Point or Ethernet device, which,
+  instead of receiving packets from physical media, receives them from
+  user space program and instead of sending packets via physical media
+  writes them to the user space program.
+
+  In order to use the driver a program has to open /dev/net/tun and issue a
+  corresponding ioctl() to register a network device with the kernel. A network
+  device will appear as tunXX or tapXX, depending on the options chosen. When
+  the program closes the file descriptor, the network device and all
+  corresponding routes will disappear.
+
+  Depending on the type of device chosen the userspace program has to read/write
+  IP packets (with tun) or ethernet frames (with tap). Which one is being used
+  depends on the flags given with the ioctl().
+
+  The package from http://vtun.sourceforge.net/tun contains two simple examples
+  for how to use tun and tap devices. Both programs work like a bridge between
+  two network interfaces.
+  br_select.c - bridge based on select system call.
+  br_sigio.c  - bridge based on async io and SIGIO signal.
+  However, the best example is VTun http://vtun.sourceforge.net :))
+
+2. Configuration
+================
+
+  Create device node::
+
+     mkdir /dev/net (if it doesn't exist already)
+     mknod /dev/net/tun c 10 200
+
+  Set permissions::
+
+     e.g. chmod 0666 /dev/net/tun
+
+  There's no harm in allowing the device to be accessible by non-root users,
+  since CAP_NET_ADMIN is required for creating network devices or for
+  connecting to network devices which aren't owned by the user in question.
+  If you want to create persistent devices and give ownership of them to
+  unprivileged users, then you need the /dev/net/tun device to be usable by
+  those users.
+
+  Driver module autoloading
+
+     Make sure that "Kernel module loader" - module auto-loading
+     support is enabled in your kernel.  The kernel should load it on
+     first access.
+
+  Manual loading
+
+     insert the module by hand::
+
+       modprobe tun
+
+  If you do it the latter way, you have to load the module every time you
+  need it, if you do it the other way it will be automatically loaded when
+  /dev/net/tun is being opened.
+
+3. Program interface
+====================
+
+3.1 Network device allocation
+-----------------------------
+
+``char *dev`` should be the name of the device with a format string (e.g.
+"tun%d"), but (as far as I can see) this can be any valid network device name.
+Note that the character pointer becomes overwritten with the real device name
+(e.g. "tun0")::
+
+  #include <linux/if.h>
+  #include <linux/if_tun.h>
+
+  int tun_alloc(char *dev)
+  {
+      struct ifreq ifr;
+      int fd, err;
+
+      if( (fd = open("/dev/net/tun", O_RDWR)) < 0 )
+        return tun_alloc_old(dev);
+
+      memset(&ifr, 0, sizeof(ifr));
+
+      /* Flags: IFF_TUN   - TUN device (no Ethernet headers)
+       *        IFF_TAP   - TAP device
+       *
+       *        IFF_NO_PI - Do not provide packet information
+       */
+      ifr.ifr_flags = IFF_TUN;
+      if( *dev )
+        strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+
+      if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
+        close(fd);
+        return err;
+      }
+      strcpy(dev, ifr.ifr_name);
+      return fd;
+  }
+
+3.2 Frame format
+----------------
+
+If flag IFF_NO_PI is not set each frame format is::
+
+     Flags [2 bytes]
+     Proto [2 bytes]
+     Raw protocol(IP, IPv6, etc) frame.
+
+3.3 Multiqueue tuntap interface
+-------------------------------
+
+From version 3.8, Linux supports multiqueue tuntap which can uses multiple
+file descriptors (queues) to parallelize packets sending or receiving. The
+device allocation is the same as before, and if user wants to create multiple
+queues, TUNSETIFF with the same device name must be called many times with
+IFF_MULTI_QUEUE flag.
+
+``char *dev`` should be the name of the device, queues is the number of queues
+to be created, fds is used to store and return the file descriptors (queues)
+created to the caller. Each file descriptor were served as the interface of a
+queue which could be accessed by userspace.
+
+::
+
+  #include <linux/if.h>
+  #include <linux/if_tun.h>
+
+  int tun_alloc_mq(char *dev, int queues, int *fds)
+  {
+      struct ifreq ifr;
+      int fd, err, i;
+
+      if (!dev)
+         return -1;
+
+      memset(&ifr, 0, sizeof(ifr));
+      /* Flags: IFF_TUN   - TUN device (no Ethernet headers)
+       *        IFF_TAP   - TAP device
+       *
+       *        IFF_NO_PI - Do not provide packet information
+       *        IFF_MULTI_QUEUE - Create a queue of multiqueue device
+       */
+      ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_MULTI_QUEUE;
+      strcpy(ifr.ifr_name, dev);
+
+      for (i = 0; i < queues; i++) {
+         if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
+            goto err;
+         err = ioctl(fd, TUNSETIFF, (void *)&ifr);
+         if (err) {
+            close(fd);
+            goto err;
+         }
+         fds[i] = fd;
+      }
+
+      return 0;
+  err:
+      for (--i; i >= 0; i--)
+         close(fds[i]);
+      return err;
+  }
+
+A new ioctl(TUNSETQUEUE) were introduced to enable or disable a queue. When
+calling it with IFF_DETACH_QUEUE flag, the queue were disabled. And when
+calling it with IFF_ATTACH_QUEUE flag, the queue were enabled. The queue were
+enabled by default after it was created through TUNSETIFF.
+
+fd is the file descriptor (queue) that we want to enable or disable, when
+enable is true we enable it, otherwise we disable it::
+
+  #include <linux/if.h>
+  #include <linux/if_tun.h>
+
+  int tun_set_queue(int fd, int enable)
+  {
+      struct ifreq ifr;
+
+      memset(&ifr, 0, sizeof(ifr));
+
+      if (enable)
+        ifr.ifr_flags = IFF_ATTACH_QUEUE;
+      else
+        ifr.ifr_flags = IFF_DETACH_QUEUE;
+
+      return ioctl(fd, TUNSETQUEUE, (void *)&ifr);
+  }
+
+Universal TUN/TAP device driver Frequently Asked Question
+=========================================================
+
+1. What platforms are supported by TUN/TAP driver ?
+
+Currently driver has been written for 3 Unices:
+
+  - Linux kernels 2.2.x, 2.4.x
+  - FreeBSD 3.x, 4.x, 5.x
+  - Solaris 2.6, 7.0, 8.0
+
+2. What is TUN/TAP driver used for?
+
+As mentioned above, main purpose of TUN/TAP driver is tunneling.
+It is used by VTun (http://vtun.sourceforge.net).
+
+Another interesting application using TUN/TAP is pipsecd
+(http://perso.enst.fr/~beyssac/pipsec/), a userspace IPSec
+implementation that can use complete kernel routing (unlike FreeS/WAN).
+
+3. How does Virtual network device actually work ?
+
+Virtual network device can be viewed as a simple Point-to-Point or
+Ethernet device, which instead of receiving packets from a physical
+media, receives them from user space program and instead of sending
+packets via physical media sends them to the user space program.
+
+Let's say that you configured IPv6 on the tap0, then whenever
+the kernel sends an IPv6 packet to tap0, it is passed to the application
+(VTun for example). The application encrypts, compresses and sends it to
+the other side over TCP or UDP. The application on the other side decompresses
+and decrypts the data received and writes the packet to the TAP device,
+the kernel handles the packet like it came from real physical device.
+
+4. What is the difference between TUN driver and TAP driver?
+
+TUN works with IP frames. TAP works with Ethernet frames.
+
+This means that you have to read/write IP packets when you are using tun and
+ethernet frames when using tap.
+
+5. What is the difference between BPF and TUN/TAP driver?
+
+BPF is an advanced packet filter. It can be attached to existing
+network interface. It does not provide a virtual network interface.
+A TUN/TAP driver does provide a virtual network interface and it is possible
+to attach BPF to this interface.
+
+6. Does TAP driver support kernel Ethernet bridging?
+
+Yes. Linux and FreeBSD drivers support Ethernet bridging.
diff --git a/Documentation/networking/tuntap.txt b/Documentation/networking/tuntap.txt
deleted file mode 100644 (file)
index 0104830..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-Universal TUN/TAP device driver.
-Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
-
-  Linux, Solaris drivers 
-  Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
-
-  FreeBSD TAP driver 
-  Copyright (c) 1999-2000 Maksim Yevmenkin <m_evmenkin@yahoo.com>
-
-  Revision of this document 2002 by Florian Thiel <florian.thiel@gmx.net>
-
-1. Description
-  TUN/TAP provides packet reception and transmission for user space programs. 
-  It can be seen as a simple Point-to-Point or Ethernet device, which,
-  instead of receiving packets from physical media, receives them from 
-  user space program and instead of sending packets via physical media 
-  writes them to the user space program. 
-
-  In order to use the driver a program has to open /dev/net/tun and issue a
-  corresponding ioctl() to register a network device with the kernel. A network
-  device will appear as tunXX or tapXX, depending on the options chosen. When
-  the program closes the file descriptor, the network device and all
-  corresponding routes will disappear.
-
-  Depending on the type of device chosen the userspace program has to read/write
-  IP packets (with tun) or ethernet frames (with tap). Which one is being used
-  depends on the flags given with the ioctl().
-
-  The package from http://vtun.sourceforge.net/tun contains two simple examples
-  for how to use tun and tap devices. Both programs work like a bridge between
-  two network interfaces.
-  br_select.c - bridge based on select system call.
-  br_sigio.c  - bridge based on async io and SIGIO signal.
-  However, the best example is VTun http://vtun.sourceforge.net :))
-
-2. Configuration 
-  Create device node:
-     mkdir /dev/net (if it doesn't exist already)
-     mknod /dev/net/tun c 10 200
-  
-  Set permissions:
-     e.g. chmod 0666 /dev/net/tun
-     There's no harm in allowing the device to be accessible by non-root users,
-     since CAP_NET_ADMIN is required for creating network devices or for 
-     connecting to network devices which aren't owned by the user in question.
-     If you want to create persistent devices and give ownership of them to 
-     unprivileged users, then you need the /dev/net/tun device to be usable by
-     those users.
-
-  Driver module autoloading
-
-     Make sure that "Kernel module loader" - module auto-loading
-     support is enabled in your kernel.  The kernel should load it on
-     first access.
-  
-  Manual loading 
-     insert the module by hand:
-        modprobe tun
-
-  If you do it the latter way, you have to load the module every time you
-  need it, if you do it the other way it will be automatically loaded when
-  /dev/net/tun is being opened.
-
-3. Program interface 
-  3.1 Network device allocation:
-
-  char *dev should be the name of the device with a format string (e.g.
-  "tun%d"), but (as far as I can see) this can be any valid network device name.
-  Note that the character pointer becomes overwritten with the real device name
-  (e.g. "tun0")
-
-  #include <linux/if.h>
-  #include <linux/if_tun.h>
-
-  int tun_alloc(char *dev)
-  {
-      struct ifreq ifr;
-      int fd, err;
-
-      if( (fd = open("/dev/net/tun", O_RDWR)) < 0 )
-         return tun_alloc_old(dev);
-
-      memset(&ifr, 0, sizeof(ifr));
-
-      /* Flags: IFF_TUN   - TUN device (no Ethernet headers) 
-       *        IFF_TAP   - TAP device  
-       *
-       *        IFF_NO_PI - Do not provide packet information  
-       */ 
-      ifr.ifr_flags = IFF_TUN; 
-      if( *dev )
-         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
-
-      if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
-         close(fd);
-         return err;
-      }
-      strcpy(dev, ifr.ifr_name);
-      return fd;
-  }              
-  3.2 Frame format:
-  If flag IFF_NO_PI is not set each frame format is: 
-     Flags [2 bytes]
-     Proto [2 bytes]
-     Raw protocol(IP, IPv6, etc) frame.
-
-  3.3 Multiqueue tuntap interface:
-
-  From version 3.8, Linux supports multiqueue tuntap which can uses multiple
-  file descriptors (queues) to parallelize packets sending or receiving. The
-  device allocation is the same as before, and if user wants to create multiple
-  queues, TUNSETIFF with the same device name must be called many times with
-  IFF_MULTI_QUEUE flag.
-
-  char *dev should be the name of the device, queues is the number of queues to
-  be created, fds is used to store and return the file descriptors (queues)
-  created to the caller. Each file descriptor were served as the interface of a
-  queue which could be accessed by userspace.
-
-  #include <linux/if.h>
-  #include <linux/if_tun.h>
-
-  int tun_alloc_mq(char *dev, int queues, int *fds)
-  {
-      struct ifreq ifr;
-      int fd, err, i;
-
-      if (!dev)
-          return -1;
-
-      memset(&ifr, 0, sizeof(ifr));
-      /* Flags: IFF_TUN   - TUN device (no Ethernet headers)
-       *        IFF_TAP   - TAP device
-       *
-       *        IFF_NO_PI - Do not provide packet information
-       *        IFF_MULTI_QUEUE - Create a queue of multiqueue device
-       */
-      ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_MULTI_QUEUE;
-      strcpy(ifr.ifr_name, dev);
-
-      for (i = 0; i < queues; i++) {
-          if ((fd = open("/dev/net/tun", O_RDWR)) < 0)
-             goto err;
-          err = ioctl(fd, TUNSETIFF, (void *)&ifr);
-          if (err) {
-             close(fd);
-             goto err;
-          }
-          fds[i] = fd;
-      }
-
-      return 0;
-  err:
-      for (--i; i >= 0; i--)
-          close(fds[i]);
-      return err;
-  }
-
-  A new ioctl(TUNSETQUEUE) were introduced to enable or disable a queue. When
-  calling it with IFF_DETACH_QUEUE flag, the queue were disabled. And when
-  calling it with IFF_ATTACH_QUEUE flag, the queue were enabled. The queue were
-  enabled by default after it was created through TUNSETIFF.
-
-  fd is the file descriptor (queue) that we want to enable or disable, when
-  enable is true we enable it, otherwise we disable it
-
-  #include <linux/if.h>
-  #include <linux/if_tun.h>
-
-  int tun_set_queue(int fd, int enable)
-  {
-      struct ifreq ifr;
-
-      memset(&ifr, 0, sizeof(ifr));
-
-      if (enable)
-         ifr.ifr_flags = IFF_ATTACH_QUEUE;
-      else
-         ifr.ifr_flags = IFF_DETACH_QUEUE;
-
-      return ioctl(fd, TUNSETQUEUE, (void *)&ifr);
-  }
-
-Universal TUN/TAP device driver Frequently Asked Question.
-   
-1. What platforms are supported by TUN/TAP driver ?
-Currently driver has been written for 3 Unices:
-   Linux kernels 2.2.x, 2.4.x 
-   FreeBSD 3.x, 4.x, 5.x
-   Solaris 2.6, 7.0, 8.0
-
-2. What is TUN/TAP driver used for?
-As mentioned above, main purpose of TUN/TAP driver is tunneling. 
-It is used by VTun (http://vtun.sourceforge.net).
-
-Another interesting application using TUN/TAP is pipsecd
-(http://perso.enst.fr/~beyssac/pipsec/), a userspace IPSec
-implementation that can use complete kernel routing (unlike FreeS/WAN).
-
-3. How does Virtual network device actually work ? 
-Virtual network device can be viewed as a simple Point-to-Point or
-Ethernet device, which instead of receiving packets from a physical 
-media, receives them from user space program and instead of sending 
-packets via physical media sends them to the user space program. 
-
-Let's say that you configured IPv6 on the tap0, then whenever
-the kernel sends an IPv6 packet to tap0, it is passed to the application
-(VTun for example). The application encrypts, compresses and sends it to 
-the other side over TCP or UDP. The application on the other side decompresses
-and decrypts the data received and writes the packet to the TAP device, 
-the kernel handles the packet like it came from real physical device.
-
-4. What is the difference between TUN driver and TAP driver?
-TUN works with IP frames. TAP works with Ethernet frames.
-
-This means that you have to read/write IP packets when you are using tun and
-ethernet frames when using tap.
-
-5. What is the difference between BPF and TUN/TAP driver?
-BPF is an advanced packet filter. It can be attached to existing
-network interface. It does not provide a virtual network interface.
-A TUN/TAP driver does provide a virtual network interface and it is possible
-to attach BPF to this interface.
-
-6. Does TAP driver support kernel Ethernet bridging?
-Yes. Linux and FreeBSD drivers support Ethernet bridging. 
diff --git a/Documentation/networking/udplite.rst b/Documentation/networking/udplite.rst
new file mode 100644 (file)
index 0000000..2c225f2
--- /dev/null
@@ -0,0 +1,291 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+================================
+The UDP-Lite protocol (RFC 3828)
+================================
+
+
+  UDP-Lite is a Standards-Track IETF transport protocol whose characteristic
+  is a variable-length checksum. This has advantages for transport of multimedia
+  (video, VoIP) over wireless networks, as partly damaged packets can still be
+  fed into the codec instead of being discarded due to a failed checksum test.
+
+  This file briefly describes the existing kernel support and the socket API.
+  For in-depth information, you can consult:
+
+   - The UDP-Lite Homepage:
+     http://web.archive.org/web/%2E/http://www.erg.abdn.ac.uk/users/gerrit/udp-lite/
+
+     From here you can also download some example application source code.
+
+   - The UDP-Lite HOWTO on
+     http://web.archive.org/web/%2E/http://www.erg.abdn.ac.uk/users/gerrit/udp-lite/files/UDP-Lite-HOWTO.txt
+
+   - The Wireshark UDP-Lite WiKi (with capture files):
+     https://wiki.wireshark.org/Lightweight_User_Datagram_Protocol
+
+   - The Protocol Spec, RFC 3828, http://www.ietf.org/rfc/rfc3828.txt
+
+
+1. Applications
+===============
+
+  Several applications have been ported successfully to UDP-Lite. Ethereal
+  (now called wireshark) has UDP-Litev4/v6 support by default.
+
+  Porting applications to UDP-Lite is straightforward: only socket level and
+  IPPROTO need to be changed; senders additionally set the checksum coverage
+  length (default = header length = 8). Details are in the next section.
+
+2. Programming API
+==================
+
+  UDP-Lite provides a connectionless, unreliable datagram service and hence
+  uses the same socket type as UDP. In fact, porting from UDP to UDP-Lite is
+  very easy: simply add ``IPPROTO_UDPLITE`` as the last argument of the
+  socket(2) call so that the statement looks like::
+
+      s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDPLITE);
+
+  or, respectively,
+
+  ::
+
+      s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE);
+
+  With just the above change you are able to run UDP-Lite services or connect
+  to UDP-Lite servers. The kernel will assume that you are not interested in
+  using partial checksum coverage and so emulate UDP mode (full coverage).
+
+  To make use of the partial checksum coverage facilities requires setting a
+  single socket option, which takes an integer specifying the coverage length:
+
+    * Sender checksum coverage: UDPLITE_SEND_CSCOV
+
+      For example::
+
+       int val = 20;
+       setsockopt(s, SOL_UDPLITE, UDPLITE_SEND_CSCOV, &val, sizeof(int));
+
+      sets the checksum coverage length to 20 bytes (12b data + 8b header).
+      Of each packet only the first 20 bytes (plus the pseudo-header) will be
+      checksummed. This is useful for RTP applications which have a 12-byte
+      base header.
+
+
+    * Receiver checksum coverage: UDPLITE_RECV_CSCOV
+
+      This option is the receiver-side analogue. It is truly optional, i.e. not
+      required to enable traffic with partial checksum coverage. Its function is
+      that of a traffic filter: when enabled, it instructs the kernel to drop
+      all packets which have a coverage _less_ than this value. For example, if
+      RTP and UDP headers are to be protected, a receiver can enforce that only
+      packets with a minimum coverage of 20 are admitted::
+
+       int min = 20;
+       setsockopt(s, SOL_UDPLITE, UDPLITE_RECV_CSCOV, &min, sizeof(int));
+
+  The calls to getsockopt(2) are analogous. Being an extension and not a stand-
+  alone protocol, all socket options known from UDP can be used in exactly the
+  same manner as before, e.g. UDP_CORK or UDP_ENCAP.
+
+  A detailed discussion of UDP-Lite checksum coverage options is in section IV.
+
+3. Header Files
+===============
+
+  The socket API requires support through header files in /usr/include:
+
+    * /usr/include/netinet/in.h
+      to define IPPROTO_UDPLITE
+
+    * /usr/include/netinet/udplite.h
+      for UDP-Lite header fields and protocol constants
+
+  For testing purposes, the following can serve as a ``mini`` header file::
+
+    #define IPPROTO_UDPLITE       136
+    #define SOL_UDPLITE           136
+    #define UDPLITE_SEND_CSCOV     10
+    #define UDPLITE_RECV_CSCOV     11
+
+  Ready-made header files for various distros are in the UDP-Lite tarball.
+
+4. Kernel Behaviour with Regards to the Various Socket Options
+==============================================================
+
+
+  To enable debugging messages, the log level need to be set to 8, as most
+  messages use the KERN_DEBUG level (7).
+
+  1) Sender Socket Options
+
+  If the sender specifies a value of 0 as coverage length, the module
+  assumes full coverage, transmits a packet with coverage length of 0
+  and according checksum.  If the sender specifies a coverage < 8 and
+  different from 0, the kernel assumes 8 as default value.  Finally,
+  if the specified coverage length exceeds the packet length, the packet
+  length is used instead as coverage length.
+
+  2) Receiver Socket Options
+
+  The receiver specifies the minimum value of the coverage length it
+  is willing to accept.  A value of 0 here indicates that the receiver
+  always wants the whole of the packet covered. In this case, all
+  partially covered packets are dropped and an error is logged.
+
+  It is not possible to specify illegal values (<0 and <8); in these
+  cases the default of 8 is assumed.
+
+  All packets arriving with a coverage value less than the specified
+  threshold are discarded, these events are also logged.
+
+  3) Disabling the Checksum Computation
+
+  On both sender and receiver, checksumming will always be performed
+  and cannot be disabled using SO_NO_CHECK. Thus::
+
+       setsockopt(sockfd, SOL_SOCKET, SO_NO_CHECK,  ... );
+
+  will always will be ignored, while the value of::
+
+       getsockopt(sockfd, SOL_SOCKET, SO_NO_CHECK, &value, ...);
+
+  is meaningless (as in TCP). Packets with a zero checksum field are
+  illegal (cf. RFC 3828, sec. 3.1) and will be silently discarded.
+
+  4) Fragmentation
+
+  The checksum computation respects both buffersize and MTU. The size
+  of UDP-Lite packets is determined by the size of the send buffer. The
+  minimum size of the send buffer is 2048 (defined as SOCK_MIN_SNDBUF
+  in include/net/sock.h), the default value is configurable as
+  net.core.wmem_default or via setting the SO_SNDBUF socket(7)
+  option. The maximum upper bound for the send buffer is determined
+  by net.core.wmem_max.
+
+  Given a payload size larger than the send buffer size, UDP-Lite will
+  split the payload into several individual packets, filling up the
+  send buffer size in each case.
+
+  The precise value also depends on the interface MTU. The interface MTU,
+  in turn, may trigger IP fragmentation. In this case, the generated
+  UDP-Lite packet is split into several IP packets, of which only the
+  first one contains the L4 header.
+
+  The send buffer size has implications on the checksum coverage length.
+  Consider the following example::
+
+    Payload: 1536 bytes          Send Buffer:     1024 bytes
+    MTU:     1500 bytes          Coverage Length:  856 bytes
+
+  UDP-Lite will ship the 1536 bytes in two separate packets::
+
+    Packet 1: 1024 payload + 8 byte header + 20 byte IP header = 1052 bytes
+    Packet 2:  512 payload + 8 byte header + 20 byte IP header =  540 bytes
+
+  The coverage packet covers the UDP-Lite header and 848 bytes of the
+  payload in the first packet, the second packet is fully covered. Note
+  that for the second packet, the coverage length exceeds the packet
+  length. The kernel always re-adjusts the coverage length to the packet
+  length in such cases.
+
+  As an example of what happens when one UDP-Lite packet is split into
+  several tiny fragments, consider the following example::
+
+    Payload: 1024 bytes            Send buffer size: 1024 bytes
+    MTU:      300 bytes            Coverage length:   575 bytes
+
+    +-+-----------+--------------+--------------+--------------+
+    |8|    272    |      280     |     280      |     280      |
+    +-+-----------+--------------+--------------+--------------+
+               280            560            840           1032
+                                       ^
+    *****checksum coverage*************
+
+  The UDP-Lite module generates one 1032 byte packet (1024 + 8 byte
+  header). According to the interface MTU, these are split into 4 IP
+  packets (280 byte IP payload + 20 byte IP header). The kernel module
+  sums the contents of the entire first two packets, plus 15 bytes of
+  the last packet before releasing the fragments to the IP module.
+
+  To see the analogous case for IPv6 fragmentation, consider a link
+  MTU of 1280 bytes and a write buffer of 3356 bytes. If the checksum
+  coverage is less than 1232 bytes (MTU minus IPv6/fragment header
+  lengths), only the first fragment needs to be considered. When using
+  larger checksum coverage lengths, each eligible fragment needs to be
+  checksummed. Suppose we have a checksum coverage of 3062. The buffer
+  of 3356 bytes will be split into the following fragments::
+
+    Fragment 1: 1280 bytes carrying  1232 bytes of UDP-Lite data
+    Fragment 2: 1280 bytes carrying  1232 bytes of UDP-Lite data
+    Fragment 3:  948 bytes carrying   900 bytes of UDP-Lite data
+
+  The first two fragments have to be checksummed in full, of the last
+  fragment only 598 (= 3062 - 2*1232) bytes are checksummed.
+
+  While it is important that such cases are dealt with correctly, they
+  are (annoyingly) rare: UDP-Lite is designed for optimising multimedia
+  performance over wireless (or generally noisy) links and thus smaller
+  coverage lengths are likely to be expected.
+
+5. UDP-Lite Runtime Statistics and their Meaning
+================================================
+
+  Exceptional and error conditions are logged to syslog at the KERN_DEBUG
+  level.  Live statistics about UDP-Lite are available in /proc/net/snmp
+  and can (with newer versions of netstat) be viewed using::
+
+                           netstat -svu
+
+  This displays UDP-Lite statistics variables, whose meaning is as follows.
+
+   ============     =====================================================
+   InDatagrams      The total number of datagrams delivered to users.
+
+   NoPorts          Number of packets received to an unknown port.
+                   These cases are counted separately (not as InErrors).
+
+   InErrors         Number of erroneous UDP-Lite packets. Errors include:
+
+                     * internal socket queue receive errors
+                     * packet too short (less than 8 bytes or stated
+                       coverage length exceeds received length)
+                     * xfrm4_policy_check() returned with error
+                     * application has specified larger min. coverage
+                       length than that of incoming packet
+                     * checksum coverage violated
+                     * bad checksum
+
+   OutDatagrams     Total number of sent datagrams.
+   ============     =====================================================
+
+   These statistics derive from the UDP MIB (RFC 2013).
+
+6. IPtables
+===========
+
+  There is packet match support for UDP-Lite as well as support for the LOG target.
+  If you copy and paste the following line into /etc/protocols::
+
+    udplite 136     UDP-Lite        # UDP-Lite [RFC 3828]
+
+  then::
+
+             iptables -A INPUT -p udplite -j LOG
+
+  will produce logging output to syslog. Dropping and rejecting packets also works.
+
+7. Maintainer Address
+=====================
+
+  The UDP-Lite patch was developed at
+
+                   University of Aberdeen
+                   Electronics Research Group
+                   Department of Engineering
+                   Fraser Noble Building
+                   Aberdeen AB24 3UE; UK
+
+  The current maintainer is Gerrit Renker, <gerrit@erg.abdn.ac.uk>. Initial
+  code was developed by William  Stanislaus, <william@erg.abdn.ac.uk>.
diff --git a/Documentation/networking/udplite.txt b/Documentation/networking/udplite.txt
deleted file mode 100644 (file)
index 53a7268..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-  ===========================================================================
-                      The UDP-Lite protocol (RFC 3828)
-  ===========================================================================
-
-
-  UDP-Lite is a Standards-Track IETF transport protocol whose characteristic
-  is a variable-length checksum. This has advantages for transport of multimedia
-  (video, VoIP) over wireless networks, as partly damaged packets can still be
-  fed into the codec instead of being discarded due to a failed checksum test.
-
-  This file briefly describes the existing kernel support and the socket API.
-  For in-depth information, you can consult:
-
-   o The UDP-Lite Homepage:
-       http://web.archive.org/web/*/http://www.erg.abdn.ac.uk/users/gerrit/udp-lite/ 
-       From here you can also download some example application source code.
-
-   o The UDP-Lite HOWTO on
-       http://web.archive.org/web/*/http://www.erg.abdn.ac.uk/users/gerrit/udp-lite/
-       files/UDP-Lite-HOWTO.txt
-
-   o The Wireshark UDP-Lite WiKi (with capture files):
-       https://wiki.wireshark.org/Lightweight_User_Datagram_Protocol
-
-   o The Protocol Spec, RFC 3828, http://www.ietf.org/rfc/rfc3828.txt
-
-
-  I) APPLICATIONS
-
-  Several applications have been ported successfully to UDP-Lite. Ethereal
-  (now called wireshark) has UDP-Litev4/v6 support by default. 
-  Porting applications to UDP-Lite is straightforward: only socket level and
-  IPPROTO need to be changed; senders additionally set the checksum coverage
-  length (default = header length = 8). Details are in the next section.
-
-
-  II) PROGRAMMING API
-
-  UDP-Lite provides a connectionless, unreliable datagram service and hence
-  uses the same socket type as UDP. In fact, porting from UDP to UDP-Lite is
-  very easy: simply add `IPPROTO_UDPLITE' as the last argument of the socket(2)
-  call so that the statement looks like:
-
-      s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDPLITE);
-
-                      or, respectively,
-
-      s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE);
-
-  With just the above change you are able to run UDP-Lite services or connect
-  to UDP-Lite servers. The kernel will assume that you are not interested in
-  using partial checksum coverage and so emulate UDP mode (full coverage).
-
-  To make use of the partial checksum coverage facilities requires setting a
-  single socket option, which takes an integer specifying the coverage length:
-
-    * Sender checksum coverage: UDPLITE_SEND_CSCOV
-
-      For example,
-
-        int val = 20;
-        setsockopt(s, SOL_UDPLITE, UDPLITE_SEND_CSCOV, &val, sizeof(int));
-
-      sets the checksum coverage length to 20 bytes (12b data + 8b header).
-      Of each packet only the first 20 bytes (plus the pseudo-header) will be
-      checksummed. This is useful for RTP applications which have a 12-byte
-      base header.
-
-
-    * Receiver checksum coverage: UDPLITE_RECV_CSCOV
-
-      This option is the receiver-side analogue. It is truly optional, i.e. not
-      required to enable traffic with partial checksum coverage. Its function is
-      that of a traffic filter: when enabled, it instructs the kernel to drop
-      all packets which have a coverage _less_ than this value. For example, if
-      RTP and UDP headers are to be protected, a receiver can enforce that only
-      packets with a minimum coverage of 20 are admitted:
-
-        int min = 20;
-        setsockopt(s, SOL_UDPLITE, UDPLITE_RECV_CSCOV, &min, sizeof(int));
-
-  The calls to getsockopt(2) are analogous. Being an extension and not a stand-
-  alone protocol, all socket options known from UDP can be used in exactly the
-  same manner as before, e.g. UDP_CORK or UDP_ENCAP.
-
-  A detailed discussion of UDP-Lite checksum coverage options is in section IV.
-
-
-  III) HEADER FILES
-
-  The socket API requires support through header files in /usr/include:
-
-    * /usr/include/netinet/in.h
-        to define IPPROTO_UDPLITE
-
-    * /usr/include/netinet/udplite.h
-        for UDP-Lite header fields and protocol constants
-
-  For testing purposes, the following can serve as a `mini' header file:
-
-    #define IPPROTO_UDPLITE       136
-    #define SOL_UDPLITE           136
-    #define UDPLITE_SEND_CSCOV     10
-    #define UDPLITE_RECV_CSCOV     11
-
-  Ready-made header files for various distros are in the UDP-Lite tarball.
-
-
-  IV) KERNEL BEHAVIOUR WITH REGARD TO THE VARIOUS SOCKET OPTIONS
-
-  To enable debugging messages, the log level need to be set to 8, as most
-  messages use the KERN_DEBUG level (7).
-
-  1) Sender Socket Options
-
-  If the sender specifies a value of 0 as coverage length, the module
-  assumes full coverage, transmits a packet with coverage length of 0
-  and according checksum.  If the sender specifies a coverage < 8 and
-  different from 0, the kernel assumes 8 as default value.  Finally,
-  if the specified coverage length exceeds the packet length, the packet
-  length is used instead as coverage length.
-
-  2) Receiver Socket Options
-
-  The receiver specifies the minimum value of the coverage length it
-  is willing to accept.  A value of 0 here indicates that the receiver
-  always wants the whole of the packet covered. In this case, all
-  partially covered packets are dropped and an error is logged.
-
-  It is not possible to specify illegal values (<0 and <8); in these
-  cases the default of 8 is assumed.
-
-  All packets arriving with a coverage value less than the specified
-  threshold are discarded, these events are also logged.
-
-  3) Disabling the Checksum Computation
-
-  On both sender and receiver, checksumming will always be performed
-  and cannot be disabled using SO_NO_CHECK. Thus
-
-        setsockopt(sockfd, SOL_SOCKET, SO_NO_CHECK,  ... );
-
-  will always will be ignored, while the value of
-
-        getsockopt(sockfd, SOL_SOCKET, SO_NO_CHECK, &value, ...);
-
-  is meaningless (as in TCP). Packets with a zero checksum field are
-  illegal (cf. RFC 3828, sec. 3.1) and will be silently discarded.
-
-  4) Fragmentation
-
-  The checksum computation respects both buffersize and MTU. The size
-  of UDP-Lite packets is determined by the size of the send buffer. The
-  minimum size of the send buffer is 2048 (defined as SOCK_MIN_SNDBUF
-  in include/net/sock.h), the default value is configurable as
-  net.core.wmem_default or via setting the SO_SNDBUF socket(7)
-  option. The maximum upper bound for the send buffer is determined
-  by net.core.wmem_max.
-
-  Given a payload size larger than the send buffer size, UDP-Lite will
-  split the payload into several individual packets, filling up the
-  send buffer size in each case.
-
-  The precise value also depends on the interface MTU. The interface MTU,
-  in turn, may trigger IP fragmentation. In this case, the generated
-  UDP-Lite packet is split into several IP packets, of which only the
-  first one contains the L4 header.
-
-  The send buffer size has implications on the checksum coverage length.
-  Consider the following example:
-
-  Payload: 1536 bytes          Send Buffer:     1024 bytes
-  MTU:     1500 bytes          Coverage Length:  856 bytes
-
-  UDP-Lite will ship the 1536 bytes in two separate packets:
-
-  Packet 1: 1024 payload + 8 byte header + 20 byte IP header = 1052 bytes
-  Packet 2:  512 payload + 8 byte header + 20 byte IP header =  540 bytes
-
-  The coverage packet covers the UDP-Lite header and 848 bytes of the
-  payload in the first packet, the second packet is fully covered. Note
-  that for the second packet, the coverage length exceeds the packet
-  length. The kernel always re-adjusts the coverage length to the packet
-  length in such cases.
-
-  As an example of what happens when one UDP-Lite packet is split into
-  several tiny fragments, consider the following example.
-
-  Payload: 1024 bytes            Send buffer size: 1024 bytes
-  MTU:      300 bytes            Coverage length:   575 bytes
-
-  +-+-----------+--------------+--------------+--------------+
-  |8|    272    |      280     |     280      |     280      |
-  +-+-----------+--------------+--------------+--------------+
-               280            560            840           1032
-                                    ^
-  *****checksum coverage*************
-
-  The UDP-Lite module generates one 1032 byte packet (1024 + 8 byte
-  header). According to the interface MTU, these are split into 4 IP
-  packets (280 byte IP payload + 20 byte IP header). The kernel module
-  sums the contents of the entire first two packets, plus 15 bytes of
-  the last packet before releasing the fragments to the IP module.
-
-  To see the analogous case for IPv6 fragmentation, consider a link
-  MTU of 1280 bytes and a write buffer of 3356 bytes. If the checksum
-  coverage is less than 1232 bytes (MTU minus IPv6/fragment header
-  lengths), only the first fragment needs to be considered. When using
-  larger checksum coverage lengths, each eligible fragment needs to be
-  checksummed. Suppose we have a checksum coverage of 3062. The buffer
-  of 3356 bytes will be split into the following fragments:
-
-    Fragment 1: 1280 bytes carrying  1232 bytes of UDP-Lite data
-    Fragment 2: 1280 bytes carrying  1232 bytes of UDP-Lite data
-    Fragment 3:  948 bytes carrying   900 bytes of UDP-Lite data
-
-  The first two fragments have to be checksummed in full, of the last
-  fragment only 598 (= 3062 - 2*1232) bytes are checksummed.
-
-  While it is important that such cases are dealt with correctly, they
-  are (annoyingly) rare: UDP-Lite is designed for optimising multimedia
-  performance over wireless (or generally noisy) links and thus smaller
-  coverage lengths are likely to be expected.
-
-
-  V) UDP-LITE RUNTIME STATISTICS AND THEIR MEANING
-
-  Exceptional and error conditions are logged to syslog at the KERN_DEBUG
-  level.  Live statistics about UDP-Lite are available in /proc/net/snmp
-  and can (with newer versions of netstat) be viewed using
-
-                            netstat -svu
-
-  This displays UDP-Lite statistics variables, whose meaning is as follows.
-
-   InDatagrams:     The total number of datagrams delivered to users.
-
-   NoPorts:         Number of packets received to an unknown port.
-                    These cases are counted separately (not as InErrors).
-
-   InErrors:        Number of erroneous UDP-Lite packets. Errors include:
-                      * internal socket queue receive errors
-                      * packet too short (less than 8 bytes or stated
-                        coverage length exceeds received length)
-                      * xfrm4_policy_check() returned with error
-                      * application has specified larger min. coverage
-                        length than that of incoming packet
-                      * checksum coverage violated
-                      * bad checksum
-
-   OutDatagrams:    Total number of sent datagrams.
-
-   These statistics derive from the UDP MIB (RFC 2013).
-
-
-  VI) IPTABLES
-
-  There is packet match support for UDP-Lite as well as support for the LOG target.
-  If you copy and paste the following line into /etc/protocols,
-
-  udplite 136     UDP-Lite        # UDP-Lite [RFC 3828]
-
-  then
-              iptables -A INPUT -p udplite -j LOG
-
-  will produce logging output to syslog. Dropping and rejecting packets also works.
-
-
-  VII) MAINTAINER ADDRESS
-
-  The UDP-Lite patch was developed at
-                    University of Aberdeen
-                    Electronics Research Group
-                    Department of Engineering
-                    Fraser Noble Building
-                    Aberdeen AB24 3UE; UK
-  The current maintainer is Gerrit Renker, <gerrit@erg.abdn.ac.uk>. Initial
-  code was developed by William  Stanislaus, <william@erg.abdn.ac.uk>.
diff --git a/Documentation/networking/vrf.rst b/Documentation/networking/vrf.rst
new file mode 100644 (file)
index 0000000..0dde145
--- /dev/null
@@ -0,0 +1,451 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====================================
+Virtual Routing and Forwarding (VRF)
+====================================
+
+The VRF Device
+==============
+
+The VRF device combined with ip rules provides the ability to create virtual
+routing and forwarding domains (aka VRFs, VRF-lite to be specific) in the
+Linux network stack. One use case is the multi-tenancy problem where each
+tenant has their own unique routing tables and in the very least need
+different default gateways.
+
+Processes can be "VRF aware" by binding a socket to the VRF device. Packets
+through the socket then use the routing table associated with the VRF
+device. An important feature of the VRF device implementation is that it
+impacts only Layer 3 and above so L2 tools (e.g., LLDP) are not affected
+(ie., they do not need to be run in each VRF). The design also allows
+the use of higher priority ip rules (Policy Based Routing, PBR) to take
+precedence over the VRF device rules directing specific traffic as desired.
+
+In addition, VRF devices allow VRFs to be nested within namespaces. For
+example network namespaces provide separation of network interfaces at the
+device layer, VLANs on the interfaces within a namespace provide L2 separation
+and then VRF devices provide L3 separation.
+
+Design
+------
+A VRF device is created with an associated route table. Network interfaces
+are then enslaved to a VRF device::
+
+        +-----------------------------+
+        |           vrf-blue          |  ===> route table 10
+        +-----------------------------+
+           |        |            |
+        +------+ +------+     +-------------+
+        | eth1 | | eth2 | ... |    bond1    |
+        +------+ +------+     +-------------+
+                                 |       |
+                             +------+ +------+
+                             | eth8 | | eth9 |
+                             +------+ +------+
+
+Packets received on an enslaved device and are switched to the VRF device
+in the IPv4 and IPv6 processing stacks giving the impression that packets
+flow through the VRF device. Similarly on egress routing rules are used to
+send packets to the VRF device driver before getting sent out the actual
+interface. This allows tcpdump on a VRF device to capture all packets into
+and out of the VRF as a whole\ [1]_. Similarly, netfilter\ [2]_ and tc rules
+can be applied using the VRF device to specify rules that apply to the VRF
+domain as a whole.
+
+.. [1] Packets in the forwarded state do not flow through the device, so those
+       packets are not seen by tcpdump. Will revisit this limitation in a
+       future release.
+
+.. [2] Iptables on ingress supports PREROUTING with skb->dev set to the real
+       ingress device and both INPUT and PREROUTING rules with skb->dev set to
+       the VRF device. For egress POSTROUTING and OUTPUT rules can be written
+       using either the VRF device or real egress device.
+
+Setup
+-----
+1. VRF device is created with an association to a FIB table.
+   e.g,::
+
+       ip link add vrf-blue type vrf table 10
+       ip link set dev vrf-blue up
+
+2. An l3mdev FIB rule directs lookups to the table associated with the device.
+   A single l3mdev rule is sufficient for all VRFs. The VRF device adds the
+   l3mdev rule for IPv4 and IPv6 when the first device is created with a
+   default preference of 1000. Users may delete the rule if desired and add
+   with a different priority or install per-VRF rules.
+
+   Prior to the v4.8 kernel iif and oif rules are needed for each VRF device::
+
+       ip ru add oif vrf-blue table 10
+       ip ru add iif vrf-blue table 10
+
+3. Set the default route for the table (and hence default route for the VRF)::
+
+       ip route add table 10 unreachable default metric 4278198272
+
+   This high metric value ensures that the default unreachable route can
+   be overridden by a routing protocol suite.  FRRouting interprets
+   kernel metrics as a combined admin distance (upper byte) and priority
+   (lower 3 bytes).  Thus the above metric translates to [255/8192].
+
+4. Enslave L3 interfaces to a VRF device::
+
+       ip link set dev eth1 master vrf-blue
+
+   Local and connected routes for enslaved devices are automatically moved to
+   the table associated with VRF device. Any additional routes depending on
+   the enslaved device are dropped and will need to be reinserted to the VRF
+   FIB table following the enslavement.
+
+   The IPv6 sysctl option keep_addr_on_down can be enabled to keep IPv6 global
+   addresses as VRF enslavement changes::
+
+       sysctl -w net.ipv6.conf.all.keep_addr_on_down=1
+
+5. Additional VRF routes are added to associated table::
+
+       ip route add table 10 ...
+
+
+Applications
+------------
+Applications that are to work within a VRF need to bind their socket to the
+VRF device::
+
+    setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, dev, strlen(dev)+1);
+
+or to specify the output device using cmsg and IP_PKTINFO.
+
+By default the scope of the port bindings for unbound sockets is
+limited to the default VRF. That is, it will not be matched by packets
+arriving on interfaces enslaved to an l3mdev and processes may bind to
+the same port if they bind to an l3mdev.
+
+TCP & UDP services running in the default VRF context (ie., not bound
+to any VRF device) can work across all VRF domains by enabling the
+tcp_l3mdev_accept and udp_l3mdev_accept sysctl options::
+
+    sysctl -w net.ipv4.tcp_l3mdev_accept=1
+    sysctl -w net.ipv4.udp_l3mdev_accept=1
+
+These options are disabled by default so that a socket in a VRF is only
+selected for packets in that VRF. There is a similar option for RAW
+sockets, which is enabled by default for reasons of backwards compatibility.
+This is so as to specify the output device with cmsg and IP_PKTINFO, but
+using a socket not bound to the corresponding VRF. This allows e.g. older ping
+implementations to be run with specifying the device but without executing it
+in the VRF. This option can be disabled so that packets received in a VRF
+context are only handled by a raw socket bound to the VRF, and packets in the
+default VRF are only handled by a socket not bound to any VRF::
+
+    sysctl -w net.ipv4.raw_l3mdev_accept=0
+
+netfilter rules on the VRF device can be used to limit access to services
+running in the default VRF context as well.
+
+--------------------------------------------------------------------------------
+
+Using iproute2 for VRFs
+=======================
+iproute2 supports the vrf keyword as of v4.7. For backwards compatibility this
+section lists both commands where appropriate -- with the vrf keyword and the
+older form without it.
+
+1. Create a VRF
+
+   To instantiate a VRF device and associate it with a table::
+
+       $ ip link add dev NAME type vrf table ID
+
+   As of v4.8 the kernel supports the l3mdev FIB rule where a single rule
+   covers all VRFs. The l3mdev rule is created for IPv4 and IPv6 on first
+   device create.
+
+2. List VRFs
+
+   To list VRFs that have been created::
+
+       $ ip [-d] link show type vrf
+        NOTE: The -d option is needed to show the table id
+
+   For example::
+
+       $ ip -d link show type vrf
+       11: mgmt: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
+          link/ether 72:b3:ba:91:e2:24 brd ff:ff:ff:ff:ff:ff promiscuity 0
+          vrf table 1 addrgenmode eui64
+       12: red: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
+          link/ether b6:6f:6e:f6:da:73 brd ff:ff:ff:ff:ff:ff promiscuity 0
+          vrf table 10 addrgenmode eui64
+       13: blue: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
+          link/ether 36:62:e8:7d:bb:8c brd ff:ff:ff:ff:ff:ff promiscuity 0
+          vrf table 66 addrgenmode eui64
+       14: green: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
+          link/ether e6:28:b8:63:70:bb brd ff:ff:ff:ff:ff:ff promiscuity 0
+          vrf table 81 addrgenmode eui64
+
+
+   Or in brief output::
+
+       $ ip -br link show type vrf
+       mgmt         UP             72:b3:ba:91:e2:24 <NOARP,MASTER,UP,LOWER_UP>
+       red          UP             b6:6f:6e:f6:da:73 <NOARP,MASTER,UP,LOWER_UP>
+       blue         UP             36:62:e8:7d:bb:8c <NOARP,MASTER,UP,LOWER_UP>
+       green        UP             e6:28:b8:63:70:bb <NOARP,MASTER,UP,LOWER_UP>
+
+
+3. Assign a Network Interface to a VRF
+
+   Network interfaces are assigned to a VRF by enslaving the netdevice to a
+   VRF device::
+
+       $ ip link set dev NAME master NAME
+
+   On enslavement connected and local routes are automatically moved to the
+   table associated with the VRF device.
+
+   For example::
+
+       $ ip link set dev eth0 master mgmt
+
+
+4. Show Devices Assigned to a VRF
+
+   To show devices that have been assigned to a specific VRF add the master
+   option to the ip command::
+
+       $ ip link show vrf NAME
+       $ ip link show master NAME
+
+   For example::
+
+       $ ip link show vrf red
+       3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP mode DEFAULT group default qlen 1000
+          link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff
+       4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP mode DEFAULT group default qlen 1000
+          link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff
+       7: eth5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master red state DOWN mode DEFAULT group default qlen 1000
+          link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff
+
+
+   Or using the brief output::
+
+       $ ip -br link show vrf red
+       eth1             UP             02:00:00:00:02:02 <BROADCAST,MULTICAST,UP,LOWER_UP>
+       eth2             UP             02:00:00:00:02:03 <BROADCAST,MULTICAST,UP,LOWER_UP>
+       eth5             DOWN           02:00:00:00:02:06 <BROADCAST,MULTICAST>
+
+
+5. Show Neighbor Entries for a VRF
+
+   To list neighbor entries associated with devices enslaved to a VRF device
+   add the master option to the ip command::
+
+       $ ip [-6] neigh show vrf NAME
+       $ ip [-6] neigh show master NAME
+
+   For example::
+
+       $  ip neigh show vrf red
+       10.2.1.254 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE
+       10.2.2.254 dev eth2 lladdr 5e:54:01:6a:ee:80 REACHABLE
+
+       $ ip -6 neigh show vrf red
+       2002:1::64 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE
+
+
+6. Show Addresses for a VRF
+
+   To show addresses for interfaces associated with a VRF add the master
+   option to the ip command::
+
+       $ ip addr show vrf NAME
+       $ ip addr show master NAME
+
+   For example::
+
+       $ ip addr show vrf red
+       3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP group default qlen 1000
+           link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff
+           inet 10.2.1.2/24 brd 10.2.1.255 scope global eth1
+              valid_lft forever preferred_lft forever
+           inet6 2002:1::2/120 scope global
+              valid_lft forever preferred_lft forever
+           inet6 fe80::ff:fe00:202/64 scope link
+              valid_lft forever preferred_lft forever
+       4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP group default qlen 1000
+           link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff
+           inet 10.2.2.2/24 brd 10.2.2.255 scope global eth2
+              valid_lft forever preferred_lft forever
+           inet6 2002:2::2/120 scope global
+              valid_lft forever preferred_lft forever
+           inet6 fe80::ff:fe00:203/64 scope link
+              valid_lft forever preferred_lft forever
+       7: eth5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master red state DOWN group default qlen 1000
+           link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff
+
+   Or in brief format::
+
+       $ ip -br addr show vrf red
+       eth1             UP             10.2.1.2/24 2002:1::2/120 fe80::ff:fe00:202/64
+       eth2             UP             10.2.2.2/24 2002:2::2/120 fe80::ff:fe00:203/64
+       eth5             DOWN
+
+
+7. Show Routes for a VRF
+
+   To show routes for a VRF use the ip command to display the table associated
+   with the VRF device::
+
+       $ ip [-6] route show vrf NAME
+       $ ip [-6] route show table ID
+
+   For example::
+
+       $ ip route show vrf red
+       unreachable default  metric 4278198272
+       broadcast 10.2.1.0 dev eth1  proto kernel  scope link  src 10.2.1.2
+       10.2.1.0/24 dev eth1  proto kernel  scope link  src 10.2.1.2
+       local 10.2.1.2 dev eth1  proto kernel  scope host  src 10.2.1.2
+       broadcast 10.2.1.255 dev eth1  proto kernel  scope link  src 10.2.1.2
+       broadcast 10.2.2.0 dev eth2  proto kernel  scope link  src 10.2.2.2
+       10.2.2.0/24 dev eth2  proto kernel  scope link  src 10.2.2.2
+       local 10.2.2.2 dev eth2  proto kernel  scope host  src 10.2.2.2
+       broadcast 10.2.2.255 dev eth2  proto kernel  scope link  src 10.2.2.2
+
+       $ ip -6 route show vrf red
+       local 2002:1:: dev lo  proto none  metric 0  pref medium
+       local 2002:1::2 dev lo  proto none  metric 0  pref medium
+       2002:1::/120 dev eth1  proto kernel  metric 256  pref medium
+       local 2002:2:: dev lo  proto none  metric 0  pref medium
+       local 2002:2::2 dev lo  proto none  metric 0  pref medium
+       2002:2::/120 dev eth2  proto kernel  metric 256  pref medium
+       local fe80:: dev lo  proto none  metric 0  pref medium
+       local fe80:: dev lo  proto none  metric 0  pref medium
+       local fe80::ff:fe00:202 dev lo  proto none  metric 0  pref medium
+       local fe80::ff:fe00:203 dev lo  proto none  metric 0  pref medium
+       fe80::/64 dev eth1  proto kernel  metric 256  pref medium
+       fe80::/64 dev eth2  proto kernel  metric 256  pref medium
+       ff00::/8 dev red  metric 256  pref medium
+       ff00::/8 dev eth1  metric 256  pref medium
+       ff00::/8 dev eth2  metric 256  pref medium
+       unreachable default dev lo  metric 4278198272  error -101 pref medium
+
+8. Route Lookup for a VRF
+
+   A test route lookup can be done for a VRF::
+
+       $ ip [-6] route get vrf NAME ADDRESS
+       $ ip [-6] route get oif NAME ADDRESS
+
+   For example::
+
+       $ ip route get 10.2.1.40 vrf red
+       10.2.1.40 dev eth1  table red  src 10.2.1.2
+           cache
+
+       $ ip -6 route get 2002:1::32 vrf red
+       2002:1::32 from :: dev eth1  table red  proto kernel  src 2002:1::2  metric 256  pref medium
+
+
+9. Removing Network Interface from a VRF
+
+   Network interfaces are removed from a VRF by breaking the enslavement to
+   the VRF device::
+
+       $ ip link set dev NAME nomaster
+
+   Connected routes are moved back to the default table and local entries are
+   moved to the local table.
+
+   For example::
+
+    $ ip link set dev eth0 nomaster
+
+--------------------------------------------------------------------------------
+
+Commands used in this example::
+
+     cat >> /etc/iproute2/rt_tables.d/vrf.conf <<EOF
+     1  mgmt
+     10 red
+     66 blue
+     81 green
+     EOF
+
+     function vrf_create
+     {
+        VRF=$1
+        TBID=$2
+
+        # create VRF device
+        ip link add ${VRF} type vrf table ${TBID}
+
+        if [ "${VRF}" != "mgmt" ]; then
+            ip route add table ${TBID} unreachable default metric 4278198272
+        fi
+        ip link set dev ${VRF} up
+     }
+
+     vrf_create mgmt 1
+     ip link set dev eth0 master mgmt
+
+     vrf_create red 10
+     ip link set dev eth1 master red
+     ip link set dev eth2 master red
+     ip link set dev eth5 master red
+
+     vrf_create blue 66
+     ip link set dev eth3 master blue
+
+     vrf_create green 81
+     ip link set dev eth4 master green
+
+
+     Interface addresses from /etc/network/interfaces:
+     auto eth0
+     iface eth0 inet static
+          address 10.0.0.2
+          netmask 255.255.255.0
+          gateway 10.0.0.254
+
+     iface eth0 inet6 static
+          address 2000:1::2
+          netmask 120
+
+     auto eth1
+     iface eth1 inet static
+          address 10.2.1.2
+          netmask 255.255.255.0
+
+     iface eth1 inet6 static
+          address 2002:1::2
+          netmask 120
+
+     auto eth2
+     iface eth2 inet static
+          address 10.2.2.2
+          netmask 255.255.255.0
+
+     iface eth2 inet6 static
+          address 2002:2::2
+          netmask 120
+
+     auto eth3
+     iface eth3 inet static
+          address 10.2.3.2
+          netmask 255.255.255.0
+
+     iface eth3 inet6 static
+          address 2002:3::2
+          netmask 120
+
+     auto eth4
+     iface eth4 inet static
+          address 10.2.4.2
+          netmask 255.255.255.0
+
+     iface eth4 inet6 static
+          address 2002:4::2
+          netmask 120
diff --git a/Documentation/networking/vrf.txt b/Documentation/networking/vrf.txt
deleted file mode 100644 (file)
index a5f103b..0000000
+++ /dev/null
@@ -1,418 +0,0 @@
-Virtual Routing and Forwarding (VRF)
-====================================
-The VRF device combined with ip rules provides the ability to create virtual
-routing and forwarding domains (aka VRFs, VRF-lite to be specific) in the
-Linux network stack. One use case is the multi-tenancy problem where each
-tenant has their own unique routing tables and in the very least need
-different default gateways.
-
-Processes can be "VRF aware" by binding a socket to the VRF device. Packets
-through the socket then use the routing table associated with the VRF
-device. An important feature of the VRF device implementation is that it
-impacts only Layer 3 and above so L2 tools (e.g., LLDP) are not affected
-(ie., they do not need to be run in each VRF). The design also allows
-the use of higher priority ip rules (Policy Based Routing, PBR) to take
-precedence over the VRF device rules directing specific traffic as desired.
-
-In addition, VRF devices allow VRFs to be nested within namespaces. For
-example network namespaces provide separation of network interfaces at the
-device layer, VLANs on the interfaces within a namespace provide L2 separation
-and then VRF devices provide L3 separation.
-
-Design
-------
-A VRF device is created with an associated route table. Network interfaces
-are then enslaved to a VRF device:
-
-         +-----------------------------+
-         |           vrf-blue          |  ===> route table 10
-         +-----------------------------+
-            |        |            |
-         +------+ +------+     +-------------+
-         | eth1 | | eth2 | ... |    bond1    |
-         +------+ +------+     +-------------+
-                                  |       |
-                              +------+ +------+
-                              | eth8 | | eth9 |
-                              +------+ +------+
-
-Packets received on an enslaved device and are switched to the VRF device
-in the IPv4 and IPv6 processing stacks giving the impression that packets
-flow through the VRF device. Similarly on egress routing rules are used to
-send packets to the VRF device driver before getting sent out the actual
-interface. This allows tcpdump on a VRF device to capture all packets into
-and out of the VRF as a whole.[1] Similarly, netfilter[2] and tc rules can be
-applied using the VRF device to specify rules that apply to the VRF domain
-as a whole.
-
-[1] Packets in the forwarded state do not flow through the device, so those
-    packets are not seen by tcpdump. Will revisit this limitation in a
-    future release.
-
-[2] Iptables on ingress supports PREROUTING with skb->dev set to the real
-    ingress device and both INPUT and PREROUTING rules with skb->dev set to
-    the VRF device. For egress POSTROUTING and OUTPUT rules can be written
-    using either the VRF device or real egress device.
-
-Setup
------
-1. VRF device is created with an association to a FIB table.
-   e.g, ip link add vrf-blue type vrf table 10
-        ip link set dev vrf-blue up
-
-2. An l3mdev FIB rule directs lookups to the table associated with the device.
-   A single l3mdev rule is sufficient for all VRFs. The VRF device adds the
-   l3mdev rule for IPv4 and IPv6 when the first device is created with a
-   default preference of 1000. Users may delete the rule if desired and add
-   with a different priority or install per-VRF rules.
-
-   Prior to the v4.8 kernel iif and oif rules are needed for each VRF device:
-       ip ru add oif vrf-blue table 10
-       ip ru add iif vrf-blue table 10
-
-3. Set the default route for the table (and hence default route for the VRF).
-       ip route add table 10 unreachable default metric 4278198272
-
-   This high metric value ensures that the default unreachable route can
-   be overridden by a routing protocol suite.  FRRouting interprets
-   kernel metrics as a combined admin distance (upper byte) and priority
-   (lower 3 bytes).  Thus the above metric translates to [255/8192].
-
-4. Enslave L3 interfaces to a VRF device.
-       ip link set dev eth1 master vrf-blue
-
-   Local and connected routes for enslaved devices are automatically moved to
-   the table associated with VRF device. Any additional routes depending on
-   the enslaved device are dropped and will need to be reinserted to the VRF
-   FIB table following the enslavement.
-
-   The IPv6 sysctl option keep_addr_on_down can be enabled to keep IPv6 global
-   addresses as VRF enslavement changes.
-       sysctl -w net.ipv6.conf.all.keep_addr_on_down=1
-
-5. Additional VRF routes are added to associated table.
-       ip route add table 10 ...
-
-
-Applications
-------------
-Applications that are to work within a VRF need to bind their socket to the
-VRF device:
-
-    setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, dev, strlen(dev)+1);
-
-or to specify the output device using cmsg and IP_PKTINFO.
-
-By default the scope of the port bindings for unbound sockets is
-limited to the default VRF. That is, it will not be matched by packets
-arriving on interfaces enslaved to an l3mdev and processes may bind to
-the same port if they bind to an l3mdev.
-
-TCP & UDP services running in the default VRF context (ie., not bound
-to any VRF device) can work across all VRF domains by enabling the
-tcp_l3mdev_accept and udp_l3mdev_accept sysctl options:
-
-    sysctl -w net.ipv4.tcp_l3mdev_accept=1
-    sysctl -w net.ipv4.udp_l3mdev_accept=1
-
-These options are disabled by default so that a socket in a VRF is only
-selected for packets in that VRF. There is a similar option for RAW
-sockets, which is enabled by default for reasons of backwards compatibility.
-This is so as to specify the output device with cmsg and IP_PKTINFO, but
-using a socket not bound to the corresponding VRF. This allows e.g. older ping
-implementations to be run with specifying the device but without executing it
-in the VRF. This option can be disabled so that packets received in a VRF
-context are only handled by a raw socket bound to the VRF, and packets in the
-default VRF are only handled by a socket not bound to any VRF:
-
-    sysctl -w net.ipv4.raw_l3mdev_accept=0
-
-netfilter rules on the VRF device can be used to limit access to services
-running in the default VRF context as well.
-
-################################################################################
-
-Using iproute2 for VRFs
-=======================
-iproute2 supports the vrf keyword as of v4.7. For backwards compatibility this
-section lists both commands where appropriate -- with the vrf keyword and the
-older form without it.
-
-1. Create a VRF
-
-   To instantiate a VRF device and associate it with a table:
-       $ ip link add dev NAME type vrf table ID
-
-   As of v4.8 the kernel supports the l3mdev FIB rule where a single rule
-   covers all VRFs. The l3mdev rule is created for IPv4 and IPv6 on first
-   device create.
-
-2. List VRFs
-
-   To list VRFs that have been created:
-       $ ip [-d] link show type vrf
-         NOTE: The -d option is needed to show the table id
-
-   For example:
-   $ ip -d link show type vrf
-   11: mgmt: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
-       link/ether 72:b3:ba:91:e2:24 brd ff:ff:ff:ff:ff:ff promiscuity 0
-       vrf table 1 addrgenmode eui64
-   12: red: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
-       link/ether b6:6f:6e:f6:da:73 brd ff:ff:ff:ff:ff:ff promiscuity 0
-       vrf table 10 addrgenmode eui64
-   13: blue: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
-       link/ether 36:62:e8:7d:bb:8c brd ff:ff:ff:ff:ff:ff promiscuity 0
-       vrf table 66 addrgenmode eui64
-   14: green: <NOARP,MASTER,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
-       link/ether e6:28:b8:63:70:bb brd ff:ff:ff:ff:ff:ff promiscuity 0
-       vrf table 81 addrgenmode eui64
-
-
-   Or in brief output:
-
-   $ ip -br link show type vrf
-   mgmt         UP             72:b3:ba:91:e2:24 <NOARP,MASTER,UP,LOWER_UP>
-   red          UP             b6:6f:6e:f6:da:73 <NOARP,MASTER,UP,LOWER_UP>
-   blue         UP             36:62:e8:7d:bb:8c <NOARP,MASTER,UP,LOWER_UP>
-   green        UP             e6:28:b8:63:70:bb <NOARP,MASTER,UP,LOWER_UP>
-
-
-3. Assign a Network Interface to a VRF
-
-   Network interfaces are assigned to a VRF by enslaving the netdevice to a
-   VRF device:
-       $ ip link set dev NAME master NAME
-
-   On enslavement connected and local routes are automatically moved to the
-   table associated with the VRF device.
-
-   For example:
-   $ ip link set dev eth0 master mgmt
-
-
-4. Show Devices Assigned to a VRF
-
-   To show devices that have been assigned to a specific VRF add the master
-   option to the ip command:
-       $ ip link show vrf NAME
-       $ ip link show master NAME
-
-   For example:
-   $ ip link show vrf red
-   3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP mode DEFAULT group default qlen 1000
-       link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff
-   4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP mode DEFAULT group default qlen 1000
-       link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff
-   7: eth5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master red state DOWN mode DEFAULT group default qlen 1000
-       link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff
-
-
-   Or using the brief output:
-   $ ip -br link show vrf red
-   eth1             UP             02:00:00:00:02:02 <BROADCAST,MULTICAST,UP,LOWER_UP>
-   eth2             UP             02:00:00:00:02:03 <BROADCAST,MULTICAST,UP,LOWER_UP>
-   eth5             DOWN           02:00:00:00:02:06 <BROADCAST,MULTICAST>
-
-
-5. Show Neighbor Entries for a VRF
-
-   To list neighbor entries associated with devices enslaved to a VRF device
-   add the master option to the ip command:
-       $ ip [-6] neigh show vrf NAME
-       $ ip [-6] neigh show master NAME
-
-   For example:
-   $  ip neigh show vrf red
-   10.2.1.254 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE
-   10.2.2.254 dev eth2 lladdr 5e:54:01:6a:ee:80 REACHABLE
-
-   $ ip -6 neigh show vrf red
-   2002:1::64 dev eth1 lladdr a6:d9:c7:4f:06:23 REACHABLE
-
-
-6. Show Addresses for a VRF
-
-   To show addresses for interfaces associated with a VRF add the master
-   option to the ip command:
-       $ ip addr show vrf NAME
-       $ ip addr show master NAME
-
-   For example:
-   $ ip addr show vrf red
-   3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP group default qlen 1000
-       link/ether 02:00:00:00:02:02 brd ff:ff:ff:ff:ff:ff
-       inet 10.2.1.2/24 brd 10.2.1.255 scope global eth1
-          valid_lft forever preferred_lft forever
-       inet6 2002:1::2/120 scope global
-          valid_lft forever preferred_lft forever
-       inet6 fe80::ff:fe00:202/64 scope link
-          valid_lft forever preferred_lft forever
-   4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master red state UP group default qlen 1000
-       link/ether 02:00:00:00:02:03 brd ff:ff:ff:ff:ff:ff
-       inet 10.2.2.2/24 brd 10.2.2.255 scope global eth2
-          valid_lft forever preferred_lft forever
-       inet6 2002:2::2/120 scope global
-          valid_lft forever preferred_lft forever
-       inet6 fe80::ff:fe00:203/64 scope link
-          valid_lft forever preferred_lft forever
-   7: eth5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master red state DOWN group default qlen 1000
-       link/ether 02:00:00:00:02:06 brd ff:ff:ff:ff:ff:ff
-
-   Or in brief format:
-   $ ip -br addr show vrf red
-   eth1             UP             10.2.1.2/24 2002:1::2/120 fe80::ff:fe00:202/64
-   eth2             UP             10.2.2.2/24 2002:2::2/120 fe80::ff:fe00:203/64
-   eth5             DOWN
-
-
-7. Show Routes for a VRF
-
-   To show routes for a VRF use the ip command to display the table associated
-   with the VRF device:
-       $ ip [-6] route show vrf NAME
-       $ ip [-6] route show table ID
-
-   For example:
-   $ ip route show vrf red
-   unreachable default  metric 4278198272
-   broadcast 10.2.1.0 dev eth1  proto kernel  scope link  src 10.2.1.2
-   10.2.1.0/24 dev eth1  proto kernel  scope link  src 10.2.1.2
-   local 10.2.1.2 dev eth1  proto kernel  scope host  src 10.2.1.2
-   broadcast 10.2.1.255 dev eth1  proto kernel  scope link  src 10.2.1.2
-   broadcast 10.2.2.0 dev eth2  proto kernel  scope link  src 10.2.2.2
-   10.2.2.0/24 dev eth2  proto kernel  scope link  src 10.2.2.2
-   local 10.2.2.2 dev eth2  proto kernel  scope host  src 10.2.2.2
-   broadcast 10.2.2.255 dev eth2  proto kernel  scope link  src 10.2.2.2
-
-   $ ip -6 route show vrf red
-   local 2002:1:: dev lo  proto none  metric 0  pref medium
-   local 2002:1::2 dev lo  proto none  metric 0  pref medium
-   2002:1::/120 dev eth1  proto kernel  metric 256  pref medium
-   local 2002:2:: dev lo  proto none  metric 0  pref medium
-   local 2002:2::2 dev lo  proto none  metric 0  pref medium
-   2002:2::/120 dev eth2  proto kernel  metric 256  pref medium
-   local fe80:: dev lo  proto none  metric 0  pref medium
-   local fe80:: dev lo  proto none  metric 0  pref medium
-   local fe80::ff:fe00:202 dev lo  proto none  metric 0  pref medium
-   local fe80::ff:fe00:203 dev lo  proto none  metric 0  pref medium
-   fe80::/64 dev eth1  proto kernel  metric 256  pref medium
-   fe80::/64 dev eth2  proto kernel  metric 256  pref medium
-   ff00::/8 dev red  metric 256  pref medium
-   ff00::/8 dev eth1  metric 256  pref medium
-   ff00::/8 dev eth2  metric 256  pref medium
-   unreachable default dev lo  metric 4278198272  error -101 pref medium
-
-8. Route Lookup for a VRF
-
-   A test route lookup can be done for a VRF:
-       $ ip [-6] route get vrf NAME ADDRESS
-       $ ip [-6] route get oif NAME ADDRESS
-
-   For example:
-   $ ip route get 10.2.1.40 vrf red
-   10.2.1.40 dev eth1  table red  src 10.2.1.2
-       cache
-
-   $ ip -6 route get 2002:1::32 vrf red
-   2002:1::32 from :: dev eth1  table red  proto kernel  src 2002:1::2  metric 256  pref medium
-
-
-9. Removing Network Interface from a VRF
-
-   Network interfaces are removed from a VRF by breaking the enslavement to
-   the VRF device:
-       $ ip link set dev NAME nomaster
-
-   Connected routes are moved back to the default table and local entries are
-   moved to the local table.
-
-   For example:
-   $ ip link set dev eth0 nomaster
-
---------------------------------------------------------------------------------
-
-Commands used in this example:
-
-cat >> /etc/iproute2/rt_tables.d/vrf.conf <<EOF
-1  mgmt
-10 red
-66 blue
-81 green
-EOF
-
-function vrf_create
-{
-    VRF=$1
-    TBID=$2
-
-    # create VRF device
-    ip link add ${VRF} type vrf table ${TBID}
-
-    if [ "${VRF}" != "mgmt" ]; then
-        ip route add table ${TBID} unreachable default metric 4278198272
-    fi
-    ip link set dev ${VRF} up
-}
-
-vrf_create mgmt 1
-ip link set dev eth0 master mgmt
-
-vrf_create red 10
-ip link set dev eth1 master red
-ip link set dev eth2 master red
-ip link set dev eth5 master red
-
-vrf_create blue 66
-ip link set dev eth3 master blue
-
-vrf_create green 81
-ip link set dev eth4 master green
-
-
-Interface addresses from /etc/network/interfaces:
-auto eth0
-iface eth0 inet static
-      address 10.0.0.2
-      netmask 255.255.255.0
-      gateway 10.0.0.254
-
-iface eth0 inet6 static
-      address 2000:1::2
-      netmask 120
-
-auto eth1
-iface eth1 inet static
-      address 10.2.1.2
-      netmask 255.255.255.0
-
-iface eth1 inet6 static
-      address 2002:1::2
-      netmask 120
-
-auto eth2
-iface eth2 inet static
-      address 10.2.2.2
-      netmask 255.255.255.0
-
-iface eth2 inet6 static
-      address 2002:2::2
-      netmask 120
-
-auto eth3
-iface eth3 inet static
-      address 10.2.3.2
-      netmask 255.255.255.0
-
-iface eth3 inet6 static
-      address 2002:3::2
-      netmask 120
-
-auto eth4
-iface eth4 inet static
-      address 10.2.4.2
-      netmask 255.255.255.0
-
-iface eth4 inet6 static
-      address 2002:4::2
-      netmask 120
diff --git a/Documentation/networking/vxlan.rst b/Documentation/networking/vxlan.rst
new file mode 100644 (file)
index 0000000..ce239fa
--- /dev/null
@@ -0,0 +1,60 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================================
+Virtual eXtensible Local Area Networking documentation
+======================================================
+
+The VXLAN protocol is a tunnelling protocol designed to solve the
+problem of limited VLAN IDs (4096) in IEEE 802.1q.  With VXLAN the
+size of the identifier is expanded to 24 bits (16777216).
+
+VXLAN is described by IETF RFC 7348, and has been implemented by a
+number of vendors.  The protocol runs over UDP using a single
+destination port.  This document describes the Linux kernel tunnel
+device, there is also a separate implementation of VXLAN for
+Openvswitch.
+
+Unlike most tunnels, a VXLAN is a 1 to N network, not just point to
+point. A VXLAN device can learn the IP address of the other endpoint
+either dynamically in a manner similar to a learning bridge, or make
+use of statically-configured forwarding entries.
+
+The management of vxlan is done in a manner similar to its two closest
+neighbors GRE and VLAN. Configuring VXLAN requires the version of
+iproute2 that matches the kernel release where VXLAN was first merged
+upstream.
+
+1. Create vxlan device::
+
+    # ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev eth1 dstport 4789
+
+This creates a new device named vxlan0.  The device uses the multicast
+group 239.1.1.1 over eth1 to handle traffic for which there is no
+entry in the forwarding table.  The destination port number is set to
+the IANA-assigned value of 4789.  The Linux implementation of VXLAN
+pre-dates the IANA's selection of a standard destination port number
+and uses the Linux-selected value by default to maintain backwards
+compatibility.
+
+2. Delete vxlan device::
+
+    # ip link delete vxlan0
+
+3. Show vxlan info::
+
+    # ip -d link show vxlan0
+
+It is possible to create, destroy and display the vxlan
+forwarding table using the new bridge command.
+
+1. Create forwarding table entry::
+
+    # bridge fdb add to 00:17:42:8a:b4:05 dst 192.19.0.2 dev vxlan0
+
+2. Delete forwarding table entry::
+
+    # bridge fdb delete 00:17:42:8a:b4:05 dev vxlan0
+
+3. Show forwarding table::
+
+    # bridge fdb show dev vxlan0
diff --git a/Documentation/networking/vxlan.txt b/Documentation/networking/vxlan.txt
deleted file mode 100644 (file)
index c28f498..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-Virtual eXtensible Local Area Networking documentation
-======================================================
-
-The VXLAN protocol is a tunnelling protocol designed to solve the
-problem of limited VLAN IDs (4096) in IEEE 802.1q.  With VXLAN the
-size of the identifier is expanded to 24 bits (16777216).
-
-VXLAN is described by IETF RFC 7348, and has been implemented by a
-number of vendors.  The protocol runs over UDP using a single
-destination port.  This document describes the Linux kernel tunnel
-device, there is also a separate implementation of VXLAN for
-Openvswitch.
-
-Unlike most tunnels, a VXLAN is a 1 to N network, not just point to
-point. A VXLAN device can learn the IP address of the other endpoint
-either dynamically in a manner similar to a learning bridge, or make
-use of statically-configured forwarding entries.
-
-The management of vxlan is done in a manner similar to its two closest
-neighbors GRE and VLAN. Configuring VXLAN requires the version of
-iproute2 that matches the kernel release where VXLAN was first merged
-upstream.
-
-1. Create vxlan device
- # ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev eth1 dstport 4789
-
-This creates a new device named vxlan0.  The device uses the multicast
-group 239.1.1.1 over eth1 to handle traffic for which there is no
-entry in the forwarding table.  The destination port number is set to
-the IANA-assigned value of 4789.  The Linux implementation of VXLAN
-pre-dates the IANA's selection of a standard destination port number
-and uses the Linux-selected value by default to maintain backwards
-compatibility.
-
-2. Delete vxlan device
-  # ip link delete vxlan0
-
-3. Show vxlan info
-  # ip -d link show vxlan0
-
-It is possible to create, destroy and display the vxlan
-forwarding table using the new bridge command.
-
-1. Create forwarding table entry
-  # bridge fdb add to 00:17:42:8a:b4:05 dst 192.19.0.2 dev vxlan0
-
-2. Delete forwarding table entry
-  # bridge fdb delete 00:17:42:8a:b4:05 dev vxlan0
-
-3. Show forwarding table
-  # bridge fdb show dev vxlan0
diff --git a/Documentation/networking/x25-iface.rst b/Documentation/networking/x25-iface.rst
new file mode 100644 (file)
index 0000000..df40189
--- /dev/null
@@ -0,0 +1,129 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================-
+X.25 Device Driver Interface
+============================-
+
+Version 1.1
+
+                          Jonathan Naylor 26.12.96
+
+This is a description of the messages to be passed between the X.25 Packet
+Layer and the X.25 device driver. They are designed to allow for the easy
+setting of the LAPB mode from within the Packet Layer.
+
+The X.25 device driver will be coded normally as per the Linux device driver
+standards. Most X.25 device drivers will be moderately similar to the
+already existing Ethernet device drivers. However unlike those drivers, the
+X.25 device driver has a state associated with it, and this information
+needs to be passed to and from the Packet Layer for proper operation.
+
+All messages are held in sk_buff's just like real data to be transmitted
+over the LAPB link. The first byte of the skbuff indicates the meaning of
+the rest of the skbuff, if any more information does exist.
+
+
+Packet Layer to Device Driver
+-----------------------------
+
+First Byte = 0x00 (X25_IFACE_DATA)
+
+This indicates that the rest of the skbuff contains data to be transmitted
+over the LAPB link. The LAPB link should already exist before any data is
+passed down.
+
+First Byte = 0x01 (X25_IFACE_CONNECT)
+
+Establish the LAPB link. If the link is already established then the connect
+confirmation message should be returned as soon as possible.
+
+First Byte = 0x02 (X25_IFACE_DISCONNECT)
+
+Terminate the LAPB link. If it is already disconnected then the disconnect
+confirmation message should be returned as soon as possible.
+
+First Byte = 0x03 (X25_IFACE_PARAMS)
+
+LAPB parameters. To be defined.
+
+
+Device Driver to Packet Layer
+-----------------------------
+
+First Byte = 0x00 (X25_IFACE_DATA)
+
+This indicates that the rest of the skbuff contains data that has been
+received over the LAPB link.
+
+First Byte = 0x01 (X25_IFACE_CONNECT)
+
+LAPB link has been established. The same message is used for both a LAPB
+link connect_confirmation and a connect_indication.
+
+First Byte = 0x02 (X25_IFACE_DISCONNECT)
+
+LAPB link has been terminated. This same message is used for both a LAPB
+link disconnect_confirmation and a disconnect_indication.
+
+First Byte = 0x03 (X25_IFACE_PARAMS)
+
+LAPB parameters. To be defined.
+
+
+
+Possible Problems
+=================
+
+(Henner Eisen, 2000-10-28)
+
+The X.25 packet layer protocol depends on a reliable datalink service.
+The LAPB protocol provides such reliable service. But this reliability
+is not preserved by the Linux network device driver interface:
+
+- With Linux 2.4.x (and above) SMP kernels, packet ordering is not
+  preserved. Even if a device driver calls netif_rx(skb1) and later
+  netif_rx(skb2), skb2 might be delivered to the network layer
+  earlier that skb1.
+- Data passed upstream by means of netif_rx() might be dropped by the
+  kernel if the backlog queue is congested.
+
+The X.25 packet layer protocol will detect this and reset the virtual
+call in question. But many upper layer protocols are not designed to
+handle such N-Reset events gracefully. And frequent N-Reset events
+will always degrade performance.
+
+Thus, driver authors should make netif_rx() as reliable as possible:
+
+SMP re-ordering will not occur if the driver's interrupt handler is
+always executed on the same CPU. Thus,
+
+- Driver authors should use irq affinity for the interrupt handler.
+
+The probability of packet loss due to backlog congestion can be
+reduced by the following measures or a combination thereof:
+
+(1) Drivers for kernel versions 2.4.x and above should always check the
+    return value of netif_rx(). If it returns NET_RX_DROP, the
+    driver's LAPB protocol must not confirm reception of the frame
+    to the peer.
+    This will reliably suppress packet loss. The LAPB protocol will
+    automatically cause the peer to re-transmit the dropped packet
+    later.
+    The lapb module interface was modified to support this. Its
+    data_indication() method should now transparently pass the
+    netif_rx() return value to the (lapb module) caller.
+(2) Drivers for kernel versions 2.2.x should always check the global
+    variable netdev_dropping when a new frame is received. The driver
+    should only call netif_rx() if netdev_dropping is zero. Otherwise
+    the driver should not confirm delivery of the frame and drop it.
+    Alternatively, the driver can queue the frame internally and call
+    netif_rx() later when netif_dropping is 0 again. In that case, delivery
+    confirmation should also be deferred such that the internal queue
+    cannot grow to much.
+    This will not reliably avoid packet loss, but the probability
+    of packet loss in netif_rx() path will be significantly reduced.
+(3) Additionally, driver authors might consider to support
+    CONFIG_NET_HW_FLOWCONTROL. This allows the driver to be woken up
+    when a previously congested backlog queue becomes empty again.
+    The driver could uses this for flow-controlling the peer by means
+    of the LAPB protocol's flow-control service.
diff --git a/Documentation/networking/x25-iface.txt b/Documentation/networking/x25-iface.txt
deleted file mode 100644 (file)
index 7f213b5..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-                       X.25 Device Driver Interface 1.1
-
-                          Jonathan Naylor 26.12.96
-
-This is a description of the messages to be passed between the X.25 Packet
-Layer and the X.25 device driver. They are designed to allow for the easy
-setting of the LAPB mode from within the Packet Layer.
-
-The X.25 device driver will be coded normally as per the Linux device driver
-standards. Most X.25 device drivers will be moderately similar to the
-already existing Ethernet device drivers. However unlike those drivers, the
-X.25 device driver has a state associated with it, and this information
-needs to be passed to and from the Packet Layer for proper operation.
-
-All messages are held in sk_buff's just like real data to be transmitted
-over the LAPB link. The first byte of the skbuff indicates the meaning of
-the rest of the skbuff, if any more information does exist.
-
-
-Packet Layer to Device Driver
------------------------------
-
-First Byte = 0x00 (X25_IFACE_DATA)
-
-This indicates that the rest of the skbuff contains data to be transmitted
-over the LAPB link. The LAPB link should already exist before any data is
-passed down.
-
-First Byte = 0x01 (X25_IFACE_CONNECT)
-
-Establish the LAPB link. If the link is already established then the connect
-confirmation message should be returned as soon as possible.
-
-First Byte = 0x02 (X25_IFACE_DISCONNECT)
-
-Terminate the LAPB link. If it is already disconnected then the disconnect
-confirmation message should be returned as soon as possible.
-
-First Byte = 0x03 (X25_IFACE_PARAMS)
-
-LAPB parameters. To be defined.
-
-
-Device Driver to Packet Layer
------------------------------
-
-First Byte = 0x00 (X25_IFACE_DATA)
-
-This indicates that the rest of the skbuff contains data that has been
-received over the LAPB link.
-
-First Byte = 0x01 (X25_IFACE_CONNECT)
-
-LAPB link has been established. The same message is used for both a LAPB
-link connect_confirmation and a connect_indication.
-
-First Byte = 0x02 (X25_IFACE_DISCONNECT)
-
-LAPB link has been terminated. This same message is used for both a LAPB
-link disconnect_confirmation and a disconnect_indication.
-
-First Byte = 0x03 (X25_IFACE_PARAMS)
-
-LAPB parameters. To be defined.
-
-
-
-Possible Problems
-=================
-
-(Henner Eisen, 2000-10-28)
-
-The X.25 packet layer protocol depends on a reliable datalink service.
-The LAPB protocol provides such reliable service. But this reliability
-is not preserved by the Linux network device driver interface:
-
-- With Linux 2.4.x (and above) SMP kernels, packet ordering is not
-  preserved. Even if a device driver calls netif_rx(skb1) and later
-  netif_rx(skb2), skb2 might be delivered to the network layer
-  earlier that skb1.
-- Data passed upstream by means of netif_rx() might be dropped by the
-  kernel if the backlog queue is congested.
-
-The X.25 packet layer protocol will detect this and reset the virtual
-call in question. But many upper layer protocols are not designed to
-handle such N-Reset events gracefully. And frequent N-Reset events
-will always degrade performance.
-
-Thus, driver authors should make netif_rx() as reliable as possible:
-
-SMP re-ordering will not occur if the driver's interrupt handler is
-always executed on the same CPU. Thus,
-
-- Driver authors should use irq affinity for the interrupt handler.
-
-The probability of packet loss due to backlog congestion can be
-reduced by the following measures or a combination thereof:
-
-(1) Drivers for kernel versions 2.4.x and above should always check the
-    return value of netif_rx(). If it returns NET_RX_DROP, the
-    driver's LAPB protocol must not confirm reception of the frame
-    to the peer. 
-    This will reliably suppress packet loss. The LAPB protocol will
-    automatically cause the peer to re-transmit the dropped packet
-    later.
-    The lapb module interface was modified to support this. Its
-    data_indication() method should now transparently pass the
-    netif_rx() return value to the (lapb module) caller.
-(2) Drivers for kernel versions 2.2.x should always check the global
-    variable netdev_dropping when a new frame is received. The driver
-    should only call netif_rx() if netdev_dropping is zero. Otherwise
-    the driver should not confirm delivery of the frame and drop it.
-    Alternatively, the driver can queue the frame internally and call
-    netif_rx() later when netif_dropping is 0 again. In that case, delivery
-    confirmation should also be deferred such that the internal queue
-    cannot grow to much.
-    This will not reliably avoid packet loss, but the probability
-    of packet loss in netif_rx() path will be significantly reduced.
-(3) Additionally, driver authors might consider to support
-    CONFIG_NET_HW_FLOWCONTROL. This allows the driver to be woken up
-    when a previously congested backlog queue becomes empty again.
-    The driver could uses this for flow-controlling the peer by means
-    of the LAPB protocol's flow-control service.
diff --git a/Documentation/networking/x25.rst b/Documentation/networking/x25.rst
new file mode 100644 (file)
index 0000000..00e45d3
--- /dev/null
@@ -0,0 +1,48 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================
+Linux X.25 Project
+==================
+
+As my third year dissertation at University I have taken it upon myself to
+write an X.25 implementation for Linux. My aim is to provide a complete X.25
+Packet Layer and a LAPB module to allow for "normal" X.25 to be run using
+Linux. There are two sorts of X.25 cards available, intelligent ones that
+implement LAPB on the card itself, and unintelligent ones that simply do
+framing, bit-stuffing and checksumming. These both need to be handled by the
+system.
+
+I therefore decided to write the implementation such that as far as the
+Packet Layer is concerned, the link layer was being performed by a lower
+layer of the Linux kernel and therefore it did not concern itself with
+implementation of LAPB. Therefore the LAPB modules would be called by
+unintelligent X.25 card drivers and not by intelligent ones, this would
+provide a uniform device driver interface, and simplify configuration.
+
+To confuse matters a little, an 802.2 LLC implementation for Linux is being
+written which will allow X.25 to be run over an Ethernet (or Token Ring) and
+conform with the JNT "Pink Book", this will have a different interface to
+the Packet Layer but there will be no confusion since the class of device
+being served by the LLC will be completely separate from LAPB. The LLC
+implementation is being done as part of another protocol project (SNA) and
+by a different author.
+
+Just when you thought that it could not become more confusing, another
+option appeared, XOT. This allows X.25 Packet Layer frames to operate over
+the Internet using TCP/IP as a reliable link layer. RFC1613 specifies the
+format and behaviour of the protocol. If time permits this option will also
+be actively considered.
+
+A linux-x25 mailing list has been created at vger.kernel.org to support the
+development and use of Linux X.25. It is early days yet, but interested
+parties are welcome to subscribe to it. Just send a message to
+majordomo@vger.kernel.org with the following in the message body:
+
+subscribe linux-x25
+end
+
+The contents of the Subject line are ignored.
+
+Jonathan
+
+g4klx@g4klx.demon.co.uk
diff --git a/Documentation/networking/x25.txt b/Documentation/networking/x25.txt
deleted file mode 100644 (file)
index c91c6d7..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-Linux X.25 Project
-
-As my third year dissertation at University I have taken it upon myself to
-write an X.25 implementation for Linux. My aim is to provide a complete X.25
-Packet Layer and a LAPB module to allow for "normal" X.25 to be run using
-Linux. There are two sorts of X.25 cards available, intelligent ones that
-implement LAPB on the card itself, and unintelligent ones that simply do
-framing, bit-stuffing and checksumming. These both need to be handled by the
-system.
-
-I therefore decided to write the implementation such that as far as the
-Packet Layer is concerned, the link layer was being performed by a lower
-layer of the Linux kernel and therefore it did not concern itself with
-implementation of LAPB. Therefore the LAPB modules would be called by
-unintelligent X.25 card drivers and not by intelligent ones, this would
-provide a uniform device driver interface, and simplify configuration.
-
-To confuse matters a little, an 802.2 LLC implementation for Linux is being
-written which will allow X.25 to be run over an Ethernet (or Token Ring) and
-conform with the JNT "Pink Book", this will have a different interface to
-the Packet Layer but there will be no confusion since the class of device
-being served by the LLC will be completely separate from LAPB. The LLC
-implementation is being done as part of another protocol project (SNA) and
-by a different author.
-
-Just when you thought that it could not become more confusing, another
-option appeared, XOT. This allows X.25 Packet Layer frames to operate over
-the Internet using TCP/IP as a reliable link layer. RFC1613 specifies the
-format and behaviour of the protocol. If time permits this option will also
-be actively considered.
-
-A linux-x25 mailing list has been created at vger.kernel.org to support the
-development and use of Linux X.25. It is early days yet, but interested
-parties are welcome to subscribe to it. Just send a message to
-majordomo@vger.kernel.org with the following in the message body:
-
-subscribe linux-x25
-end
-
-The contents of the Subject line are ignored.
-
-Jonathan
-
-g4klx@g4klx.demon.co.uk
diff --git a/Documentation/networking/xfrm_device.rst b/Documentation/networking/xfrm_device.rst
new file mode 100644 (file)
index 0000000..da1073a
--- /dev/null
@@ -0,0 +1,151 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============================================
+XFRM device - offloading the IPsec computations
+===============================================
+
+Shannon Nelson <shannon.nelson@oracle.com>
+
+
+Overview
+========
+
+IPsec is a useful feature for securing network traffic, but the
+computational cost is high: a 10Gbps link can easily be brought down
+to under 1Gbps, depending on the traffic and link configuration.
+Luckily, there are NICs that offer a hardware based IPsec offload which
+can radically increase throughput and decrease CPU utilization.  The XFRM
+Device interface allows NIC drivers to offer to the stack access to the
+hardware offload.
+
+Userland access to the offload is typically through a system such as
+libreswan or KAME/raccoon, but the iproute2 'ip xfrm' command set can
+be handy when experimenting.  An example command might look something
+like this::
+
+  ip x s add proto esp dst 14.0.0.70 src 14.0.0.52 spi 0x07 mode transport \
+     reqid 0x07 replay-window 32 \
+     aead 'rfc4106(gcm(aes))' 0x44434241343332312423222114131211f4f3f2f1 128 \
+     sel src 14.0.0.52/24 dst 14.0.0.70/24 proto tcp \
+     offload dev eth4 dir in
+
+Yes, that's ugly, but that's what shell scripts and/or libreswan are for.
+
+
+
+Callbacks to implement
+======================
+
+::
+
+  /* from include/linux/netdevice.h */
+  struct xfrmdev_ops {
+       int     (*xdo_dev_state_add) (struct xfrm_state *x);
+       void    (*xdo_dev_state_delete) (struct xfrm_state *x);
+       void    (*xdo_dev_state_free) (struct xfrm_state *x);
+       bool    (*xdo_dev_offload_ok) (struct sk_buff *skb,
+                                      struct xfrm_state *x);
+       void    (*xdo_dev_state_advance_esn) (struct xfrm_state *x);
+  };
+
+The NIC driver offering ipsec offload will need to implement these
+callbacks to make the offload available to the network stack's
+XFRM subsytem.  Additionally, the feature bits NETIF_F_HW_ESP and
+NETIF_F_HW_ESP_TX_CSUM will signal the availability of the offload.
+
+
+
+Flow
+====
+
+At probe time and before the call to register_netdev(), the driver should
+set up local data structures and XFRM callbacks, and set the feature bits.
+The XFRM code's listener will finish the setup on NETDEV_REGISTER.
+
+::
+
+               adapter->netdev->xfrmdev_ops = &ixgbe_xfrmdev_ops;
+               adapter->netdev->features |= NETIF_F_HW_ESP;
+               adapter->netdev->hw_enc_features |= NETIF_F_HW_ESP;
+
+When new SAs are set up with a request for "offload" feature, the
+driver's xdo_dev_state_add() will be given the new SA to be offloaded
+and an indication of whether it is for Rx or Tx.  The driver should
+
+       - verify the algorithm is supported for offloads
+       - store the SA information (key, salt, target-ip, protocol, etc)
+       - enable the HW offload of the SA
+       - return status value:
+
+               ===========   ===================================
+               0             success
+               -EOPNETSUPP   offload not supported, try SW IPsec
+               other         fail the request
+               ===========   ===================================
+
+The driver can also set an offload_handle in the SA, an opaque void pointer
+that can be used to convey context into the fast-path offload requests::
+
+               xs->xso.offload_handle = context;
+
+
+When the network stack is preparing an IPsec packet for an SA that has
+been setup for offload, it first calls into xdo_dev_offload_ok() with
+the skb and the intended offload state to ask the driver if the offload
+will serviceable.  This can check the packet information to be sure the
+offload can be supported (e.g. IPv4 or IPv6, no IPv4 options, etc) and
+return true of false to signify its support.
+
+When ready to send, the driver needs to inspect the Tx packet for the
+offload information, including the opaque context, and set up the packet
+send accordingly::
+
+               xs = xfrm_input_state(skb);
+               context = xs->xso.offload_handle;
+               set up HW for send
+
+The stack has already inserted the appropriate IPsec headers in the
+packet data, the offload just needs to do the encryption and fix up the
+header values.
+
+
+When a packet is received and the HW has indicated that it offloaded a
+decryption, the driver needs to add a reference to the decoded SA into
+the packet's skb.  At this point the data should be decrypted but the
+IPsec headers are still in the packet data; they are removed later up
+the stack in xfrm_input().
+
+       find and hold the SA that was used to the Rx skb::
+
+               get spi, protocol, and destination IP from packet headers
+               xs = find xs from (spi, protocol, dest_IP)
+               xfrm_state_hold(xs);
+
+       store the state information into the skb::
+
+               sp = secpath_set(skb);
+               if (!sp) return;
+               sp->xvec[sp->len++] = xs;
+               sp->olen++;
+
+       indicate the success and/or error status of the offload::
+
+               xo = xfrm_offload(skb);
+               xo->flags = CRYPTO_DONE;
+               xo->status = crypto_status;
+
+       hand the packet to napi_gro_receive() as usual
+
+In ESN mode, xdo_dev_state_advance_esn() is called from xfrm_replay_advance_esn().
+Driver will check packet seq number and update HW ESN state machine if needed.
+
+When the SA is removed by the user, the driver's xdo_dev_state_delete()
+is asked to disable the offload.  Later, xdo_dev_state_free() is called
+from a garbage collection routine after all reference counts to the state
+have been removed and any remaining resources can be cleared for the
+offload state.  How these are used by the driver will depend on specific
+hardware needs.
+
+As a netdev is set to DOWN the XFRM stack's netdev listener will call
+xdo_dev_state_delete() and xdo_dev_state_free() on any remaining offloaded
+states.
diff --git a/Documentation/networking/xfrm_device.txt b/Documentation/networking/xfrm_device.txt
deleted file mode 100644 (file)
index a1c904d..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-
-===============================================
-XFRM device - offloading the IPsec computations
-===============================================
-Shannon Nelson <shannon.nelson@oracle.com>
-
-
-Overview
-========
-
-IPsec is a useful feature for securing network traffic, but the
-computational cost is high: a 10Gbps link can easily be brought down
-to under 1Gbps, depending on the traffic and link configuration.
-Luckily, there are NICs that offer a hardware based IPsec offload which
-can radically increase throughput and decrease CPU utilization.  The XFRM
-Device interface allows NIC drivers to offer to the stack access to the
-hardware offload.
-
-Userland access to the offload is typically through a system such as
-libreswan or KAME/raccoon, but the iproute2 'ip xfrm' command set can
-be handy when experimenting.  An example command might look something
-like this:
-
-  ip x s add proto esp dst 14.0.0.70 src 14.0.0.52 spi 0x07 mode transport \
-     reqid 0x07 replay-window 32 \
-     aead 'rfc4106(gcm(aes))' 0x44434241343332312423222114131211f4f3f2f1 128 \
-     sel src 14.0.0.52/24 dst 14.0.0.70/24 proto tcp \
-     offload dev eth4 dir in
-
-Yes, that's ugly, but that's what shell scripts and/or libreswan are for.
-
-
-
-Callbacks to implement
-======================
-
-/* from include/linux/netdevice.h */
-struct xfrmdev_ops {
-       int     (*xdo_dev_state_add) (struct xfrm_state *x);
-       void    (*xdo_dev_state_delete) (struct xfrm_state *x);
-       void    (*xdo_dev_state_free) (struct xfrm_state *x);
-       bool    (*xdo_dev_offload_ok) (struct sk_buff *skb,
-                                      struct xfrm_state *x);
-       void    (*xdo_dev_state_advance_esn) (struct xfrm_state *x);
-};
-
-The NIC driver offering ipsec offload will need to implement these
-callbacks to make the offload available to the network stack's
-XFRM subsytem.  Additionally, the feature bits NETIF_F_HW_ESP and
-NETIF_F_HW_ESP_TX_CSUM will signal the availability of the offload.
-
-
-
-Flow
-====
-
-At probe time and before the call to register_netdev(), the driver should
-set up local data structures and XFRM callbacks, and set the feature bits.
-The XFRM code's listener will finish the setup on NETDEV_REGISTER.
-
-               adapter->netdev->xfrmdev_ops = &ixgbe_xfrmdev_ops;
-               adapter->netdev->features |= NETIF_F_HW_ESP;
-               adapter->netdev->hw_enc_features |= NETIF_F_HW_ESP;
-
-When new SAs are set up with a request for "offload" feature, the
-driver's xdo_dev_state_add() will be given the new SA to be offloaded
-and an indication of whether it is for Rx or Tx.  The driver should
-       - verify the algorithm is supported for offloads
-       - store the SA information (key, salt, target-ip, protocol, etc)
-       - enable the HW offload of the SA
-       - return status value:
-               0             success
-               -EOPNETSUPP   offload not supported, try SW IPsec
-               other         fail the request
-
-The driver can also set an offload_handle in the SA, an opaque void pointer
-that can be used to convey context into the fast-path offload requests.
-
-               xs->xso.offload_handle = context;
-
-
-When the network stack is preparing an IPsec packet for an SA that has
-been setup for offload, it first calls into xdo_dev_offload_ok() with
-the skb and the intended offload state to ask the driver if the offload
-will serviceable.  This can check the packet information to be sure the
-offload can be supported (e.g. IPv4 or IPv6, no IPv4 options, etc) and
-return true of false to signify its support.
-
-When ready to send, the driver needs to inspect the Tx packet for the
-offload information, including the opaque context, and set up the packet
-send accordingly.
-
-               xs = xfrm_input_state(skb);
-               context = xs->xso.offload_handle;
-               set up HW for send
-
-The stack has already inserted the appropriate IPsec headers in the
-packet data, the offload just needs to do the encryption and fix up the
-header values.
-
-
-When a packet is received and the HW has indicated that it offloaded a
-decryption, the driver needs to add a reference to the decoded SA into
-the packet's skb.  At this point the data should be decrypted but the
-IPsec headers are still in the packet data; they are removed later up
-the stack in xfrm_input().
-
-       find and hold the SA that was used to the Rx skb
-               get spi, protocol, and destination IP from packet headers
-               xs = find xs from (spi, protocol, dest_IP)
-               xfrm_state_hold(xs);
-
-       store the state information into the skb
-               sp = secpath_set(skb);
-               if (!sp) return;
-               sp->xvec[sp->len++] = xs;
-               sp->olen++;
-
-       indicate the success and/or error status of the offload
-               xo = xfrm_offload(skb);
-               xo->flags = CRYPTO_DONE;
-               xo->status = crypto_status;
-
-       hand the packet to napi_gro_receive() as usual
-
-In ESN mode, xdo_dev_state_advance_esn() is called from xfrm_replay_advance_esn().
-Driver will check packet seq number and update HW ESN state machine if needed.
-
-When the SA is removed by the user, the driver's xdo_dev_state_delete()
-is asked to disable the offload.  Later, xdo_dev_state_free() is called
-from a garbage collection routine after all reference counts to the state
-have been removed and any remaining resources can be cleared for the
-offload state.  How these are used by the driver will depend on specific
-hardware needs.
-
-As a netdev is set to DOWN the XFRM stack's netdev listener will call
-xdo_dev_state_delete() and xdo_dev_state_free() on any remaining offloaded
-states.
-
-
diff --git a/Documentation/networking/xfrm_proc.rst b/Documentation/networking/xfrm_proc.rst
new file mode 100644 (file)
index 0000000..0a771c5
--- /dev/null
@@ -0,0 +1,113 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================================
+XFRM proc - /proc/net/xfrm_* files
+==================================
+
+Masahide NAKAMURA <nakam@linux-ipv6.org>
+
+
+Transformation Statistics
+-------------------------
+
+The xfrm_proc code is a set of statistics showing numbers of packets
+dropped by the transformation code and why.  These counters are defined
+as part of the linux private MIB.  These counters can be viewed in
+/proc/net/xfrm_stat.
+
+
+Inbound errors
+~~~~~~~~~~~~~~
+
+XfrmInError:
+       All errors which is not matched others
+
+XfrmInBufferError:
+       No buffer is left
+
+XfrmInHdrError:
+       Header error
+
+XfrmInNoStates:
+       No state is found
+       i.e. Either inbound SPI, address, or IPsec protocol at SA is wrong
+
+XfrmInStateProtoError:
+       Transformation protocol specific error
+       e.g. SA key is wrong
+
+XfrmInStateModeError:
+       Transformation mode specific error
+
+XfrmInStateSeqError:
+       Sequence error
+       i.e. Sequence number is out of window
+
+XfrmInStateExpired:
+       State is expired
+
+XfrmInStateMismatch:
+       State has mismatch option
+       e.g. UDP encapsulation type is mismatch
+
+XfrmInStateInvalid:
+       State is invalid
+
+XfrmInTmplMismatch:
+       No matching template for states
+       e.g. Inbound SAs are correct but SP rule is wrong
+
+XfrmInNoPols:
+       No policy is found for states
+       e.g. Inbound SAs are correct but no SP is found
+
+XfrmInPolBlock:
+       Policy discards
+
+XfrmInPolError:
+       Policy error
+
+XfrmAcquireError:
+       State hasn't been fully acquired before use
+
+XfrmFwdHdrError:
+       Forward routing of a packet is not allowed
+
+Outbound errors
+~~~~~~~~~~~~~~~
+XfrmOutError:
+       All errors which is not matched others
+
+XfrmOutBundleGenError:
+       Bundle generation error
+
+XfrmOutBundleCheckError:
+       Bundle check error
+
+XfrmOutNoStates:
+       No state is found
+
+XfrmOutStateProtoError:
+       Transformation protocol specific error
+
+XfrmOutStateModeError:
+       Transformation mode specific error
+
+XfrmOutStateSeqError:
+       Sequence error
+       i.e. Sequence number overflow
+
+XfrmOutStateExpired:
+       State is expired
+
+XfrmOutPolBlock:
+       Policy discards
+
+XfrmOutPolDead:
+       Policy is dead
+
+XfrmOutPolError:
+       Policy error
+
+XfrmOutStateInvalid:
+       State is invalid, perhaps expired
diff --git a/Documentation/networking/xfrm_proc.txt b/Documentation/networking/xfrm_proc.txt
deleted file mode 100644 (file)
index 2eae619..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-XFRM proc - /proc/net/xfrm_* files
-==================================
-Masahide NAKAMURA <nakam@linux-ipv6.org>
-
-
-Transformation Statistics
--------------------------
-
-The xfrm_proc code is a set of statistics showing numbers of packets
-dropped by the transformation code and why.  These counters are defined
-as part of the linux private MIB.  These counters can be viewed in
-/proc/net/xfrm_stat.
-
-
-Inbound errors
-~~~~~~~~~~~~~~
-XfrmInError:
-       All errors which is not matched others
-XfrmInBufferError:
-       No buffer is left
-XfrmInHdrError:
-       Header error
-XfrmInNoStates:
-       No state is found
-       i.e. Either inbound SPI, address, or IPsec protocol at SA is wrong
-XfrmInStateProtoError:
-       Transformation protocol specific error
-       e.g. SA key is wrong
-XfrmInStateModeError:
-       Transformation mode specific error
-XfrmInStateSeqError:
-       Sequence error
-       i.e. Sequence number is out of window
-XfrmInStateExpired:
-       State is expired
-XfrmInStateMismatch:
-       State has mismatch option
-       e.g. UDP encapsulation type is mismatch
-XfrmInStateInvalid:
-       State is invalid
-XfrmInTmplMismatch:
-       No matching template for states
-       e.g. Inbound SAs are correct but SP rule is wrong
-XfrmInNoPols:
-       No policy is found for states
-       e.g. Inbound SAs are correct but no SP is found
-XfrmInPolBlock:
-       Policy discards
-XfrmInPolError:
-       Policy error
-XfrmAcquireError:
-       State hasn't been fully acquired before use
-XfrmFwdHdrError:
-       Forward routing of a packet is not allowed
-
-Outbound errors
-~~~~~~~~~~~~~~~
-XfrmOutError:
-       All errors which is not matched others
-XfrmOutBundleGenError:
-       Bundle generation error
-XfrmOutBundleCheckError:
-       Bundle check error
-XfrmOutNoStates:
-       No state is found
-XfrmOutStateProtoError:
-       Transformation protocol specific error
-XfrmOutStateModeError:
-       Transformation mode specific error
-XfrmOutStateSeqError:
-       Sequence error
-       i.e. Sequence number overflow
-XfrmOutStateExpired:
-       State is expired
-XfrmOutPolBlock:
-       Policy discards
-XfrmOutPolDead:
-       Policy is dead
-XfrmOutPolError:
-       Policy error
-XfrmOutStateInvalid:
-       State is invalid, perhaps expired
diff --git a/Documentation/networking/xfrm_sync.rst b/Documentation/networking/xfrm_sync.rst
new file mode 100644 (file)
index 0000000..6246503
--- /dev/null
@@ -0,0 +1,189 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====
+XFRM
+====
+
+The sync patches work is based on initial patches from
+Krisztian <hidden@balabit.hu> and others and additional patches
+from Jamal <hadi@cyberus.ca>.
+
+The end goal for syncing is to be able to insert attributes + generate
+events so that the SA can be safely moved from one machine to another
+for HA purposes.
+The idea is to synchronize the SA so that the takeover machine can do
+the processing of the SA as accurate as possible if it has access to it.
+
+We already have the ability to generate SA add/del/upd events.
+These patches add ability to sync and have accurate lifetime byte (to
+ensure proper decay of SAs) and replay counters to avoid replay attacks
+with as minimal loss at failover time.
+This way a backup stays as closely up-to-date as an active member.
+
+Because the above items change for every packet the SA receives,
+it is possible for a lot of the events to be generated.
+For this reason, we also add a nagle-like algorithm to restrict
+the events. i.e we are going to set thresholds to say "let me
+know if the replay sequence threshold is reached or 10 secs have passed"
+These thresholds are set system-wide via sysctls or can be updated
+per SA.
+
+The identified items that need to be synchronized are:
+- the lifetime byte counter
+note that: lifetime time limit is not important if you assume the failover
+machine is known ahead of time since the decay of the time countdown
+is not driven by packet arrival.
+- the replay sequence for both inbound and outbound
+
+1) Message Structure
+----------------------
+
+nlmsghdr:aevent_id:optional-TLVs.
+
+The netlink message types are:
+
+XFRM_MSG_NEWAE and XFRM_MSG_GETAE.
+
+A XFRM_MSG_GETAE does not have TLVs.
+
+A XFRM_MSG_NEWAE will have at least two TLVs (as is
+discussed further below).
+
+aevent_id structure looks like::
+
+   struct xfrm_aevent_id {
+            struct xfrm_usersa_id           sa_id;
+            xfrm_address_t                  saddr;
+            __u32                           flags;
+            __u32                           reqid;
+   };
+
+The unique SA is identified by the combination of xfrm_usersa_id,
+reqid and saddr.
+
+flags are used to indicate different things. The possible
+flags are::
+
+       XFRM_AE_RTHR=1, /* replay threshold*/
+       XFRM_AE_RVAL=2, /* replay value */
+       XFRM_AE_LVAL=4, /* lifetime value */
+       XFRM_AE_ETHR=8, /* expiry timer threshold */
+       XFRM_AE_CR=16, /* Event cause is replay update */
+       XFRM_AE_CE=32, /* Event cause is timer expiry */
+       XFRM_AE_CU=64, /* Event cause is policy update */
+
+How these flags are used is dependent on the direction of the
+message (kernel<->user) as well the cause (config, query or event).
+This is described below in the different messages.
+
+The pid will be set appropriately in netlink to recognize direction
+(0 to the kernel and pid = processid that created the event
+when going from kernel to user space)
+
+A program needs to subscribe to multicast group XFRMNLGRP_AEVENTS
+to get notified of these events.
+
+2) TLVS reflect the different parameters:
+-----------------------------------------
+
+a) byte value (XFRMA_LTIME_VAL)
+
+This TLV carries the running/current counter for byte lifetime since
+last event.
+
+b)replay value (XFRMA_REPLAY_VAL)
+
+This TLV carries the running/current counter for replay sequence since
+last event.
+
+c)replay threshold (XFRMA_REPLAY_THRESH)
+
+This TLV carries the threshold being used by the kernel to trigger events
+when the replay sequence is exceeded.
+
+d) expiry timer (XFRMA_ETIMER_THRESH)
+
+This is a timer value in milliseconds which is used as the nagle
+value to rate limit the events.
+
+3) Default configurations for the parameters:
+---------------------------------------------
+
+By default these events should be turned off unless there is
+at least one listener registered to listen to the multicast
+group XFRMNLGRP_AEVENTS.
+
+Programs installing SAs will need to specify the two thresholds, however,
+in order to not change existing applications such as racoon
+we also provide default threshold values for these different parameters
+in case they are not specified.
+
+the two sysctls/proc entries are:
+
+a) /proc/sys/net/core/sysctl_xfrm_aevent_etime
+used to provide default values for the XFRMA_ETIMER_THRESH in incremental
+units of time of 100ms. The default is 10 (1 second)
+
+b) /proc/sys/net/core/sysctl_xfrm_aevent_rseqth
+used to provide default values for XFRMA_REPLAY_THRESH parameter
+in incremental packet count. The default is two packets.
+
+4) Message types
+----------------
+
+a) XFRM_MSG_GETAE issued by user-->kernel.
+   XFRM_MSG_GETAE does not carry any TLVs.
+
+The response is a XFRM_MSG_NEWAE which is formatted based on what
+XFRM_MSG_GETAE queried for.
+
+The response will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
+* if XFRM_AE_RTHR flag is set, then XFRMA_REPLAY_THRESH is also retrieved
+* if XFRM_AE_ETHR flag is set, then XFRMA_ETIMER_THRESH is also retrieved
+
+b) XFRM_MSG_NEWAE is issued by either user space to configure
+   or kernel to announce events or respond to a XFRM_MSG_GETAE.
+
+i) user --> kernel to configure a specific SA.
+
+any of the values or threshold parameters can be updated by passing the
+appropriate TLV.
+
+A response is issued back to the sender in user space to indicate success
+or failure.
+
+In the case of success, additionally an event with
+XFRM_MSG_NEWAE is also issued to any listeners as described in iii).
+
+ii) kernel->user direction as a response to XFRM_MSG_GETAE
+
+The response will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
+
+The threshold TLVs will be included if explicitly requested in
+the XFRM_MSG_GETAE message.
+
+iii) kernel->user to report as event if someone sets any values or
+     thresholds for an SA using XFRM_MSG_NEWAE (as described in #i above).
+     In such a case XFRM_AE_CU flag is set to inform the user that
+     the change happened as a result of an update.
+     The message will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
+
+iv) kernel->user to report event when replay threshold or a timeout
+    is exceeded.
+
+In such a case either XFRM_AE_CR (replay exceeded) or XFRM_AE_CE (timeout
+happened) is set to inform the user what happened.
+Note the two flags are mutually exclusive.
+The message will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
+
+Exceptions to threshold settings
+--------------------------------
+
+If you have an SA that is getting hit by traffic in bursts such that
+there is a period where the timer threshold expires with no packets
+seen, then an odd behavior is seen as follows:
+The first packet arrival after a timer expiry will trigger a timeout
+event; i.e we don't wait for a timeout period or a packet threshold
+to be reached. This is done for simplicity and efficiency reasons.
+
+-JHS
diff --git a/Documentation/networking/xfrm_sync.txt b/Documentation/networking/xfrm_sync.txt
deleted file mode 100644 (file)
index 8d88e0f..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-
-The sync patches work is based on initial patches from
-Krisztian <hidden@balabit.hu> and others and additional patches
-from Jamal <hadi@cyberus.ca>.
-
-The end goal for syncing is to be able to insert attributes + generate
-events so that the SA can be safely moved from one machine to another
-for HA purposes.
-The idea is to synchronize the SA so that the takeover machine can do
-the processing of the SA as accurate as possible if it has access to it.
-
-We already have the ability to generate SA add/del/upd events.
-These patches add ability to sync and have accurate lifetime byte (to
-ensure proper decay of SAs) and replay counters to avoid replay attacks
-with as minimal loss at failover time.
-This way a backup stays as closely up-to-date as an active member.
-
-Because the above items change for every packet the SA receives,
-it is possible for a lot of the events to be generated.
-For this reason, we also add a nagle-like algorithm to restrict
-the events. i.e we are going to set thresholds to say "let me
-know if the replay sequence threshold is reached or 10 secs have passed"
-These thresholds are set system-wide via sysctls or can be updated
-per SA.
-
-The identified items that need to be synchronized are:
-- the lifetime byte counter
-note that: lifetime time limit is not important if you assume the failover
-machine is known ahead of time since the decay of the time countdown
-is not driven by packet arrival.
-- the replay sequence for both inbound and outbound
-
-1) Message Structure
-----------------------
-
-nlmsghdr:aevent_id:optional-TLVs.
-
-The netlink message types are:
-
-XFRM_MSG_NEWAE and XFRM_MSG_GETAE.
-
-A XFRM_MSG_GETAE does not have TLVs.
-A XFRM_MSG_NEWAE will have at least two TLVs (as is
-discussed further below).
-
-aevent_id structure looks like:
-
-   struct xfrm_aevent_id {
-             struct xfrm_usersa_id           sa_id;
-             xfrm_address_t                  saddr;
-             __u32                           flags;
-             __u32                           reqid;
-   };
-
-The unique SA is identified by the combination of xfrm_usersa_id,
-reqid and saddr.
-
-flags are used to indicate different things. The possible
-flags are:
-        XFRM_AE_RTHR=1, /* replay threshold*/
-        XFRM_AE_RVAL=2, /* replay value */
-        XFRM_AE_LVAL=4, /* lifetime value */
-        XFRM_AE_ETHR=8, /* expiry timer threshold */
-        XFRM_AE_CR=16, /* Event cause is replay update */
-        XFRM_AE_CE=32, /* Event cause is timer expiry */
-        XFRM_AE_CU=64, /* Event cause is policy update */
-
-How these flags are used is dependent on the direction of the
-message (kernel<->user) as well the cause (config, query or event).
-This is described below in the different messages.
-
-The pid will be set appropriately in netlink to recognize direction
-(0 to the kernel and pid = processid that created the event
-when going from kernel to user space)
-
-A program needs to subscribe to multicast group XFRMNLGRP_AEVENTS
-to get notified of these events.
-
-2) TLVS reflect the different parameters:
------------------------------------------
-
-a) byte value (XFRMA_LTIME_VAL)
-This TLV carries the running/current counter for byte lifetime since
-last event.
-
-b)replay value (XFRMA_REPLAY_VAL)
-This TLV carries the running/current counter for replay sequence since
-last event.
-
-c)replay threshold (XFRMA_REPLAY_THRESH)
-This TLV carries the threshold being used by the kernel to trigger events
-when the replay sequence is exceeded.
-
-d) expiry timer (XFRMA_ETIMER_THRESH)
-This is a timer value in milliseconds which is used as the nagle
-value to rate limit the events.
-
-3) Default configurations for the parameters:
-----------------------------------------------
-
-By default these events should be turned off unless there is
-at least one listener registered to listen to the multicast
-group XFRMNLGRP_AEVENTS.
-
-Programs installing SAs will need to specify the two thresholds, however,
-in order to not change existing applications such as racoon
-we also provide default threshold values for these different parameters
-in case they are not specified.
-
-the two sysctls/proc entries are:
-a) /proc/sys/net/core/sysctl_xfrm_aevent_etime
-used to provide default values for the XFRMA_ETIMER_THRESH in incremental
-units of time of 100ms. The default is 10 (1 second)
-
-b) /proc/sys/net/core/sysctl_xfrm_aevent_rseqth
-used to provide default values for XFRMA_REPLAY_THRESH parameter
-in incremental packet count. The default is two packets.
-
-4) Message types
-----------------
-
-a) XFRM_MSG_GETAE issued by user-->kernel.
-XFRM_MSG_GETAE does not carry any TLVs.
-The response is a XFRM_MSG_NEWAE which is formatted based on what
-XFRM_MSG_GETAE queried for.
-The response will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
-*if XFRM_AE_RTHR flag is set, then XFRMA_REPLAY_THRESH is also retrieved
-*if XFRM_AE_ETHR flag is set, then XFRMA_ETIMER_THRESH is also retrieved
-
-b) XFRM_MSG_NEWAE is issued by either user space to configure
-or kernel to announce events or respond to a XFRM_MSG_GETAE.
-
-i) user --> kernel to configure a specific SA.
-any of the values or threshold parameters can be updated by passing the
-appropriate TLV.
-A response is issued back to the sender in user space to indicate success
-or failure.
-In the case of success, additionally an event with
-XFRM_MSG_NEWAE is also issued to any listeners as described in iii).
-
-ii) kernel->user direction as a response to XFRM_MSG_GETAE
-The response will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
-The threshold TLVs will be included if explicitly requested in
-the XFRM_MSG_GETAE message.
-
-iii) kernel->user to report as event if someone sets any values or
-thresholds for an SA using XFRM_MSG_NEWAE (as described in #i above).
-In such a case XFRM_AE_CU flag is set to inform the user that
-the change happened as a result of an update.
-The message will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
-
-iv) kernel->user to report event when replay threshold or a timeout
-is exceeded.
-In such a case either XFRM_AE_CR (replay exceeded) or XFRM_AE_CE (timeout
-happened) is set to inform the user what happened.
-Note the two flags are mutually exclusive.
-The message will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
-
-Exceptions to threshold settings
---------------------------------
-
-If you have an SA that is getting hit by traffic in bursts such that
-there is a period where the timer threshold expires with no packets
-seen, then an odd behavior is seen as follows:
-The first packet arrival after a timer expiry will trigger a timeout
-event; i.e we don't wait for a timeout period or a packet threshold
-to be reached. This is done for simplicity and efficiency reasons.
-
--JHS
diff --git a/Documentation/networking/xfrm_sysctl.rst b/Documentation/networking/xfrm_sysctl.rst
new file mode 100644 (file)
index 0000000..47b9bbd
--- /dev/null
@@ -0,0 +1,11 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============
+XFRM Syscall
+============
+
+/proc/sys/net/core/xfrm_* Variables:
+====================================
+
+xfrm_acq_expires - INTEGER
+       default 30 - hard timeout in seconds for acquire requests
diff --git a/Documentation/networking/xfrm_sysctl.txt b/Documentation/networking/xfrm_sysctl.txt
deleted file mode 100644 (file)
index 5bbd167..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-/proc/sys/net/core/xfrm_* Variables:
-
-xfrm_acq_expires - INTEGER
-       default 30 - hard timeout in seconds for acquire requests
diff --git a/Documentation/networking/z8530drv.rst b/Documentation/networking/z8530drv.rst
new file mode 100644 (file)
index 0000000..d294276
--- /dev/null
@@ -0,0 +1,686 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+=========================================================
+SCC.C - Linux driver for Z8530 based HDLC cards for AX.25
+=========================================================
+
+
+This is a subset of the documentation. To use this driver you MUST have the
+full package from:
+
+Internet:
+
+    1. ftp://ftp.ccac.rwth-aachen.de/pub/jr/z8530drv-utils_3.0-3.tar.gz
+
+    2. ftp://ftp.pspt.fi/pub/ham/linux/ax25/z8530drv-utils_3.0-3.tar.gz
+
+Please note that the information in this document may be hopelessly outdated.
+A new version of the documentation, along with links to other important
+Linux Kernel AX.25 documentation and programs, is available on
+http://yaina.de/jreuter
+
+Copyright |copy| 1993,2000 by Joerg Reuter DL1BKE <jreuter@yaina.de>
+
+portions Copyright |copy| 1993 Guido ten Dolle PE1NNZ
+
+for the complete copyright notice see >> Copying.Z8530DRV <<
+
+1. Initialization of the driver
+===============================
+
+To use the driver, 3 steps must be performed:
+
+     1. if compiled as module: loading the module
+     2. Setup of hardware, MODEM and KISS parameters with sccinit
+     3. Attach each channel to the Linux kernel AX.25 with "ifconfig"
+
+Unlike the versions below 2.4 this driver is a real network device
+driver. If you want to run xNOS instead of our fine kernel AX.25
+use a 2.x version (available from above sites) or read the
+AX.25-HOWTO on how to emulate a KISS TNC on network device drivers.
+
+
+1.1 Loading the module
+======================
+
+(If you're going to compile the driver as a part of the kernel image,
+ skip this chapter and continue with 1.2)
+
+Before you can use a module, you'll have to load it with::
+
+       insmod scc.o
+
+please read 'man insmod' that comes with module-init-tools.
+
+You should include the insmod in one of the /etc/rc.d/rc.* files,
+and don't forget to insert a call of sccinit after that. It
+will read your /etc/z8530drv.conf.
+
+1.2. /etc/z8530drv.conf
+=======================
+
+To setup all parameters you must run /sbin/sccinit from one
+of your rc.*-files. This has to be done BEFORE you can
+"ifconfig" an interface. Sccinit reads the file /etc/z8530drv.conf
+and sets the hardware, MODEM and KISS parameters. A sample file is
+delivered with this package. Change it to your needs.
+
+The file itself consists of two main sections.
+
+1.2.1 configuration of hardware parameters
+==========================================
+
+The hardware setup section defines the following parameters for each
+Z8530::
+
+    chip    1
+    data_a  0x300                   # data port A
+    ctrl_a  0x304                   # control port A
+    data_b  0x301                   # data port B
+    ctrl_b  0x305                   # control port B
+    irq     5                       # IRQ No. 5
+    pclock  4915200                 # clock
+    board   BAYCOM                  # hardware type
+    escc    no                      # enhanced SCC chip? (8580/85180/85280)
+    vector  0                       # latch for interrupt vector
+    special no                      # address of special function register
+    option  0                       # option to set via sfr
+
+
+chip
+       - this is just a delimiter to make sccinit a bit simpler to
+         program. A parameter has no effect.
+
+data_a
+       - the address of the data port A of this Z8530 (needed)
+ctrl_a
+       - the address of the control port A (needed)
+data_b
+       - the address of the data port B (needed)
+ctrl_b
+       - the address of the control port B (needed)
+
+irq
+       - the used IRQ for this chip. Different chips can use different
+         IRQs or the same. If they share an interrupt, it needs to be
+         specified within one chip-definition only.
+
+pclock  - the clock at the PCLK pin of the Z8530 (option, 4915200 is
+         default), measured in Hertz
+
+board
+       - the "type" of the board:
+
+          =======================  ========
+          SCC type                 value
+          =======================  ========
+          PA0HZP SCC card          PA0HZP
+          EAGLE card               EAGLE
+          PC100 card               PC100
+          PRIMUS-PC (DG9BL) card   PRIMUS
+          BayCom (U)SCC card       BAYCOM
+          =======================  ========
+
+escc
+       - if you want support for ESCC chips (8580, 85180, 85280), set
+         this to "yes" (option, defaults to "no")
+
+vector
+       - address of the vector latch (aka "intack port") for PA0HZP
+         cards. There can be only one vector latch for all chips!
+         (option, defaults to 0)
+
+special
+       - address of the special function register on several cards.
+         (option, defaults to 0)
+
+option  - The value you write into that register (option, default is 0)
+
+You can specify up to four chips (8 channels). If this is not enough,
+just change::
+
+       #define MAXSCC 4
+
+to a higher value.
+
+Example for the BAYCOM USCC:
+----------------------------
+
+::
+
+       chip    1
+       data_a  0x300                   # data port A
+       ctrl_a  0x304                   # control port A
+       data_b  0x301                   # data port B
+       ctrl_b  0x305                   # control port B
+       irq     5                       # IRQ No. 5 (#)
+       board   BAYCOM                  # hardware type (*)
+       #
+       # SCC chip 2
+       #
+       chip    2
+       data_a  0x302
+       ctrl_a  0x306
+       data_b  0x303
+       ctrl_b  0x307
+       board   BAYCOM
+
+An example for a PA0HZP card:
+-----------------------------
+
+::
+
+       chip 1
+       data_a 0x153
+       data_b 0x151
+       ctrl_a 0x152
+       ctrl_b 0x150
+       irq 9
+       pclock 4915200
+       board PA0HZP
+       vector 0x168
+       escc no
+       #
+       #
+       #
+       chip 2
+       data_a 0x157
+       data_b 0x155
+       ctrl_a 0x156
+       ctrl_b 0x154
+       irq 9
+       pclock 4915200
+       board PA0HZP
+       vector 0x168
+       escc no
+
+A DRSI would should probably work with this:
+--------------------------------------------
+(actually: two DRSI cards...)
+
+::
+
+       chip 1
+       data_a 0x303
+       data_b 0x301
+       ctrl_a 0x302
+       ctrl_b 0x300
+       irq 7
+       pclock 4915200
+       board DRSI
+       escc no
+       #
+       #
+       #
+       chip 2
+       data_a 0x313
+       data_b 0x311
+       ctrl_a 0x312
+       ctrl_b 0x310
+       irq 7
+       pclock 4915200
+       board DRSI
+       escc no
+
+Note that you cannot use the on-board baudrate generator off DRSI
+cards. Use "mode dpll" for clock source (see below).
+
+This is based on information provided by Mike Bilow (and verified
+by Paul Helay)
+
+The utility "gencfg"
+--------------------
+
+If you only know the parameters for the PE1CHL driver for DOS,
+run gencfg. It will generate the correct port addresses (I hope).
+Its parameters are exactly the same as the ones you use with
+the "attach scc" command in net, except that the string "init" must
+not appear. Example::
+
+       gencfg 2 0x150 4 2 0 1 0x168 9 4915200
+
+will print a skeleton z8530drv.conf for the OptoSCC to stdout.
+
+::
+
+       gencfg 2 0x300 2 4 5 -4 0 7 4915200 0x10
+
+does the same for the BAYCOM USCC card. In my opinion it is much easier
+to edit scc_config.h...
+
+
+1.2.2 channel configuration
+===========================
+
+The channel definition is divided into three sub sections for each
+channel:
+
+An example for scc0::
+
+       # DEVICE
+
+       device scc0     # the device for the following params
+
+       # MODEM / BUFFERS
+
+       speed 1200              # the default baudrate
+       clock dpll              # clock source:
+                               #       dpll     = normal half duplex operation
+                               #       external = MODEM provides own Rx/Tx clock
+                               #       divider  = use full duplex divider if
+                               #                  installed (1)
+       mode nrzi               # HDLC encoding mode
+                               #       nrzi = 1k2 MODEM, G3RUH 9k6 MODEM
+                               #       nrz  = DF9IC 9k6 MODEM
+                               #
+       bufsize 384             # size of buffers. Note that this must include
+                               # the AX.25 header, not only the data field!
+                               # (optional, defaults to 384)
+
+       # KISS (Layer 1)
+
+       txdelay 36              # (see chapter 1.4)
+       persist 64
+       slot    8
+       tail    8
+       fulldup 0
+       wait    12
+       min     3
+       maxkey  7
+       idle    3
+       maxdef  120
+       group   0
+       txoff   off
+       softdcd on
+       slip    off
+
+The order WITHIN these sections is unimportant. The order OF these
+sections IS important. The MODEM parameters are set with the first
+recognized KISS parameter...
+
+Please note that you can initialize the board only once after boot
+(or insmod). You can change all parameters but "mode" and "clock"
+later with the Sccparam program or through KISS. Just to avoid
+security holes...
+
+(1) this divider is usually mounted on the SCC-PBC (PA0HZP) or not
+    present at all (BayCom). It feeds back the output of the DPLL
+    (digital pll) as transmit clock. Using this mode without a divider
+    installed will normally result in keying the transceiver until
+    maxkey expires --- of course without sending anything (useful).
+
+2. Attachment of a channel by your AX.25 software
+=================================================
+
+2.1 Kernel AX.25
+================
+
+To set up an AX.25 device you can simply type::
+
+       ifconfig scc0 44.128.1.1 hw ax25 dl0tha-7
+
+This will create a network interface with the IP number 44.128.20.107
+and the callsign "dl0tha". If you do not have any IP number (yet) you
+can use any of the 44.128.0.0 network. Note that you do not need
+axattach. The purpose of axattach (like slattach) is to create a KISS
+network device linked to a TTY. Please read the documentation of the
+ax25-utils and the AX.25-HOWTO to learn how to set the parameters of
+the kernel AX.25.
+
+2.2 NOS, NET and TFKISS
+=======================
+
+Since the TTY driver (aka KISS TNC emulation) is gone you need
+to emulate the old behaviour. The cost of using these programs is
+that you probably need to compile the kernel AX.25, regardless of whether
+you actually use it or not. First setup your /etc/ax25/axports,
+for example::
+
+       9k6     dl0tha-9  9600  255 4 9600 baud port (scc3)
+       axlink  dl0tha-15 38400 255 4 Link to NOS
+
+Now "ifconfig" the scc device::
+
+       ifconfig scc3 44.128.1.1 hw ax25 dl0tha-9
+
+You can now axattach a pseudo-TTY::
+
+       axattach /dev/ptys0 axlink
+
+and start your NOS and attach /dev/ptys0 there. The problem is that
+NOS is reachable only via digipeating through the kernel AX.25
+(disastrous on a DAMA controlled channel). To solve this problem,
+configure "rxecho" to echo the incoming frames from "9k6" to "axlink"
+and outgoing frames from "axlink" to "9k6" and start::
+
+       rxecho
+
+Or simply use "kissbridge" coming with z8530drv-utils::
+
+       ifconfig scc3 hw ax25 dl0tha-9
+       kissbridge scc3 /dev/ptys0
+
+
+3. Adjustment and Display of parameters
+=======================================
+
+3.1 Displaying SCC Parameters:
+==============================
+
+Once a SCC channel has been attached, the parameter settings and
+some statistic information can be shown using the param program::
+
+       dl1bke-u:~$ sccstat scc0
+
+       Parameters:
+
+       speed       : 1200 baud
+       txdelay     : 36
+       persist     : 255
+       slottime    : 0
+       txtail      : 8
+       fulldup     : 1
+       waittime    : 12
+       mintime     : 3 sec
+       maxkeyup    : 7 sec
+       idletime    : 3 sec
+       maxdefer    : 120 sec
+       group       : 0x00
+       txoff       : off
+       softdcd     : on
+       SLIP        : off
+
+       Status:
+
+       HDLC                  Z8530           Interrupts         Buffers
+       -----------------------------------------------------------------------
+       Sent       :     273  RxOver :     0  RxInts :   125074  Size    :  384
+       Received   :    1095  TxUnder:     0  TxInts :     4684  NoSpace :    0
+       RxErrors   :    1591                  ExInts :    11776
+       TxErrors   :       0                  SpInts :     1503
+       Tx State   :    idle
+
+
+The status info shown is:
+
+============== ==============================================================
+Sent           number of frames transmitted
+Received       number of frames received
+RxErrors       number of receive errors (CRC, ABORT)
+TxErrors       number of discarded Tx frames (due to various reasons)
+Tx State       status of the Tx interrupt handler: idle/busy/active/tail (2)
+RxOver         number of receiver overruns
+TxUnder                number of transmitter underruns
+RxInts         number of receiver interrupts
+TxInts         number of transmitter interrupts
+EpInts         number of receiver special condition interrupts
+SpInts         number of external/status interrupts
+Size           maximum size of an AX.25 frame (*with* AX.25 headers!)
+NoSpace                number of times a buffer could not get allocated
+============== ==============================================================
+
+An overrun is abnormal. If lots of these occur, the product of
+baudrate and number of interfaces is too high for the processing
+power of your computer. NoSpace errors are unlikely to be caused by the
+driver or the kernel AX.25.
+
+
+3.2 Setting Parameters
+======================
+
+
+The setting of parameters of the emulated KISS TNC is done in the
+same way in the SCC driver. You can change parameters by using
+the kissparms program from the ax25-utils package or use the program
+"sccparam"::
+
+     sccparam <device> <paramname> <decimal-|hexadecimal value>
+
+You can change the following parameters:
+
+===========   =====
+param        value
+===========   =====
+speed         1200
+txdelay       36
+persist       255
+slottime      0
+txtail        8
+fulldup       1
+waittime      12
+mintime       3
+maxkeyup      7
+idletime      3
+maxdefer      120
+group         0x00
+txoff         off
+softdcd       on
+SLIP          off
+===========   =====
+
+
+The parameters have the following meaning:
+
+speed:
+     The baudrate on this channel in bits/sec
+
+     Example: sccparam /dev/scc3 speed 9600
+
+txdelay:
+     The delay (in units of 10 ms) after keying of the
+     transmitter, until the first byte is sent. This is usually
+     called "TXDELAY" in a TNC.  When 0 is specified, the driver
+     will just wait until the CTS signal is asserted. This
+     assumes the presence of a timer or other circuitry in the
+     MODEM and/or transmitter, that asserts CTS when the
+     transmitter is ready for data.
+     A normal value of this parameter is 30-36.
+
+     Example: sccparam /dev/scc0 txd 20
+
+persist:
+     This is the probability that the transmitter will be keyed
+     when the channel is found to be free.  It is a value from 0
+     to 255, and the probability is (value+1)/256.  The value
+     should be somewhere near 50-60, and should be lowered when
+     the channel is used more heavily.
+
+     Example: sccparam /dev/scc2 persist 20
+
+slottime:
+     This is the time between samples of the channel. It is
+     expressed in units of 10 ms.  About 200-300 ms (value 20-30)
+     seems to be a good value.
+
+     Example: sccparam /dev/scc0 slot 20
+
+tail:
+     The time the transmitter will remain keyed after the last
+     byte of a packet has been transferred to the SCC. This is
+     necessary because the CRC and a flag still have to leave the
+     SCC before the transmitter is keyed down. The value depends
+     on the baudrate selected.  A few character times should be
+     sufficient, e.g. 40ms at 1200 baud. (value 4)
+     The value of this parameter is in 10 ms units.
+
+     Example: sccparam /dev/scc2 4
+
+full:
+     The full-duplex mode switch. This can be one of the following
+     values:
+
+     0:   The interface will operate in CSMA mode (the normal
+         half-duplex packet radio operation)
+     1:   Fullduplex mode, i.e. the transmitter will be keyed at
+         any time, without checking the received carrier.  It
+         will be unkeyed when there are no packets to be sent.
+     2:   Like 1, but the transmitter will remain keyed, also
+         when there are no packets to be sent.  Flags will be
+         sent in that case, until a timeout (parameter 10)
+         occurs.
+
+     Example: sccparam /dev/scc0 fulldup off
+
+wait:
+     The initial waittime before any transmit attempt, after the
+     frame has been queue for transmit.  This is the length of
+     the first slot in CSMA mode.  In full duplex modes it is
+     set to 0 for maximum performance.
+     The value of this parameter is in 10 ms units.
+
+     Example: sccparam /dev/scc1 wait 4
+
+maxkey:
+     The maximal time the transmitter will be keyed to send
+     packets, in seconds.  This can be useful on busy CSMA
+     channels, to avoid "getting a bad reputation" when you are
+     generating a lot of traffic.  After the specified time has
+     elapsed, no new frame will be started. Instead, the trans-
+     mitter will be switched off for a specified time (parameter
+     min), and then the selected algorithm for keyup will be
+     started again.
+     The value 0 as well as "off" will disable this feature,
+     and allow infinite transmission time.
+
+     Example: sccparam /dev/scc0 maxk 20
+
+min:
+     This is the time the transmitter will be switched off when
+     the maximum transmission time is exceeded.
+
+     Example: sccparam /dev/scc3 min 10
+
+idle:
+     This parameter specifies the maximum idle time in full duplex
+     2 mode, in seconds.  When no frames have been sent for this
+     time, the transmitter will be keyed down.  A value of 0 is
+     has same result as the fullduplex mode 1. This parameter
+     can be disabled.
+
+     Example: sccparam /dev/scc2 idle off      # transmit forever
+
+maxdefer
+     This is the maximum time (in seconds) to wait for a free channel
+     to send. When this timer expires the transmitter will be keyed
+     IMMEDIATELY. If you love to get trouble with other users you
+     should set this to a very low value ;-)
+
+     Example: sccparam /dev/scc0 maxdefer 240  # 2 minutes
+
+
+txoff:
+     When this parameter has the value 0, the transmission of packets
+     is enable. Otherwise it is disabled.
+
+     Example: sccparam /dev/scc2 txoff on
+
+group:
+     It is possible to build special radio equipment to use more than
+     one frequency on the same band, e.g. using several receivers and
+     only one transmitter that can be switched between frequencies.
+     Also, you can connect several radios that are active on the same
+     band.  In these cases, it is not possible, or not a good idea, to
+     transmit on more than one frequency.  The SCC driver provides a
+     method to lock transmitters on different interfaces, using the
+     "param <interface> group <x>" command.  This will only work when
+     you are using CSMA mode (parameter full = 0).
+
+     The number <x> must be 0 if you want no group restrictions, and
+     can be computed as follows to create restricted groups:
+     <x> is the sum of some OCTAL numbers:
+
+
+     ===  =======================================================
+     200  This transmitter will only be keyed when all other
+         transmitters in the group are off.
+     100  This transmitter will only be keyed when the carrier
+         detect of all other interfaces in the group is off.
+     0xx  A byte that can be used to define different groups.
+         Interfaces are in the same group, when the logical AND
+         between their xx values is nonzero.
+     ===  =======================================================
+
+     Examples:
+
+     When 2 interfaces use group 201, their transmitters will never be
+     keyed at the same time.
+
+     When 2 interfaces use group 101, the transmitters will only key
+     when both channels are clear at the same time.  When group 301,
+     the transmitters will not be keyed at the same time.
+
+     Don't forget to convert the octal numbers into decimal before
+     you set the parameter.
+
+     Example: (to be written)
+
+softdcd:
+     use a software dcd instead of the real one... Useful for a very
+     slow squelch.
+
+     Example: sccparam /dev/scc0 soft on
+
+
+4. Problems
+===========
+
+If you have tx-problems with your BayCom USCC card please check
+the manufacturer of the 8530. SGS chips have a slightly
+different timing. Try Zilog...  A solution is to write to register 8
+instead to the data port, but this won't work with the ESCC chips.
+*SIGH!*
+
+A very common problem is that the PTT locks until the maxkeyup timer
+expires, although interrupts and clock source are correct. In most
+cases compiling the driver with CONFIG_SCC_DELAY (set with
+make config) solves the problems. For more hints read the (pseudo) FAQ
+and the documentation coming with z8530drv-utils.
+
+I got reports that the driver has problems on some 386-based systems.
+(i.e. Amstrad) Those systems have a bogus AT bus timing which will
+lead to delayed answers on interrupts. You can recognize these
+problems by looking at the output of Sccstat for the suspected
+port. If it shows under- and overruns you own such a system.
+
+Delayed processing of received data: This depends on
+
+- the kernel version
+
+- kernel profiling compiled or not
+
+- a high interrupt load
+
+- a high load of the machine --- running X, Xmorph, XV and Povray,
+  while compiling the kernel... hmm ... even with 32 MB RAM ...  ;-)
+  Or running a named for the whole .ampr.org domain on an 8 MB
+  box...
+
+- using information from rxecho or kissbridge.
+
+Kernel panics: please read /linux/README and find out if it
+really occurred within the scc driver.
+
+If you cannot solve a problem, send me
+
+- a description of the problem,
+- information on your hardware (computer system, scc board, modem)
+- your kernel version
+- the output of cat /proc/net/z8530
+
+4. Thor RLC100
+==============
+
+Mysteriously this board seems not to work with the driver. Anyone
+got it up-and-running?
+
+
+Many thanks to Linus Torvalds and Alan Cox for including the driver
+in the Linux standard distribution and their support.
+
+::
+
+       Joerg Reuter    ampr-net: dl1bke@db0pra.ampr.org
+                       AX-25   : DL1BKE @ DB0ABH.#BAY.DEU.EU
+                       Internet: jreuter@yaina.de
+                       WWW     : http://yaina.de/jreuter
diff --git a/Documentation/networking/z8530drv.txt b/Documentation/networking/z8530drv.txt
deleted file mode 100644 (file)
index 2206abb..0000000
+++ /dev/null
@@ -1,657 +0,0 @@
-This is a subset of the documentation. To use this driver you MUST have the
-full package from:
-
-Internet:
-=========
-
-1. ftp://ftp.ccac.rwth-aachen.de/pub/jr/z8530drv-utils_3.0-3.tar.gz
-
-2. ftp://ftp.pspt.fi/pub/ham/linux/ax25/z8530drv-utils_3.0-3.tar.gz
-
-Please note that the information in this document may be hopelessly outdated.
-A new version of the documentation, along with links to other important
-Linux Kernel AX.25 documentation and programs, is available on
-http://yaina.de/jreuter
-
------------------------------------------------------------------------------
-
-
-        SCC.C - Linux driver for Z8530 based HDLC cards for AX.25      
-
-   ********************************************************************
-
-        (c) 1993,2000 by Joerg Reuter DL1BKE <jreuter@yaina.de>
-
-        portions (c) 1993 Guido ten Dolle PE1NNZ
-
-        for the complete copyright notice see >> Copying.Z8530DRV <<
-
-   ******************************************************************** 
-
-
-1. Initialization of the driver
-===============================
-
-To use the driver, 3 steps must be performed:
-
-     1. if compiled as module: loading the module
-     2. Setup of hardware, MODEM and KISS parameters with sccinit
-     3. Attach each channel to the Linux kernel AX.25 with "ifconfig"
-
-Unlike the versions below 2.4 this driver is a real network device
-driver. If you want to run xNOS instead of our fine kernel AX.25
-use a 2.x version (available from above sites) or read the
-AX.25-HOWTO on how to emulate a KISS TNC on network device drivers.
-
-
-1.1 Loading the module
-======================
-
-(If you're going to compile the driver as a part of the kernel image,
- skip this chapter and continue with 1.2)
-
-Before you can use a module, you'll have to load it with
-
-       insmod scc.o
-
-please read 'man insmod' that comes with module-init-tools.
-
-You should include the insmod in one of the /etc/rc.d/rc.* files,
-and don't forget to insert a call of sccinit after that. It
-will read your /etc/z8530drv.conf.
-
-1.2. /etc/z8530drv.conf
-=======================
-
-To setup all parameters you must run /sbin/sccinit from one
-of your rc.*-files. This has to be done BEFORE you can
-"ifconfig" an interface. Sccinit reads the file /etc/z8530drv.conf
-and sets the hardware, MODEM and KISS parameters. A sample file is
-delivered with this package. Change it to your needs.
-
-The file itself consists of two main sections.
-
-1.2.1 configuration of hardware parameters
-==========================================
-
-The hardware setup section defines the following parameters for each
-Z8530:
-
-chip    1
-data_a  0x300                   # data port A
-ctrl_a  0x304                   # control port A
-data_b  0x301                   # data port B
-ctrl_b  0x305                   # control port B
-irq     5                       # IRQ No. 5
-pclock  4915200                 # clock
-board   BAYCOM                  # hardware type
-escc    no                      # enhanced SCC chip? (8580/85180/85280)
-vector  0                       # latch for interrupt vector
-special no                      # address of special function register
-option  0                       # option to set via sfr
-
-
-chip   - this is just a delimiter to make sccinit a bit simpler to
-         program. A parameter has no effect.
-
-data_a  - the address of the data port A of this Z8530 (needed)
-ctrl_a  - the address of the control port A (needed)
-data_b  - the address of the data port B (needed)
-ctrl_b  - the address of the control port B (needed)
-
-irq     - the used IRQ for this chip. Different chips can use different
-          IRQs or the same. If they share an interrupt, it needs to be
-         specified within one chip-definition only.
-
-pclock  - the clock at the PCLK pin of the Z8530 (option, 4915200 is
-          default), measured in Hertz
-
-board   - the "type" of the board:
-
-          SCC type                 value
-          ---------------------------------
-          PA0HZP SCC card          PA0HZP
-          EAGLE card               EAGLE
-          PC100 card               PC100
-          PRIMUS-PC (DG9BL) card   PRIMUS
-          BayCom (U)SCC card       BAYCOM
-
-escc    - if you want support for ESCC chips (8580, 85180, 85280), set
-          this to "yes" (option, defaults to "no")
-
-vector  - address of the vector latch (aka "intack port") for PA0HZP
-          cards. There can be only one vector latch for all chips!
-         (option, defaults to 0)
-
-special - address of the special function register on several cards.
-          (option, defaults to 0)
-
-option  - The value you write into that register (option, default is 0)
-
-You can specify up to four chips (8 channels). If this is not enough,
-just change
-
-       #define MAXSCC 4
-
-to a higher value.
-
-Example for the BAYCOM USCC:
-----------------------------
-
-chip    1
-data_a  0x300                   # data port A
-ctrl_a  0x304                   # control port A
-data_b  0x301                   # data port B
-ctrl_b  0x305                   # control port B
-irq     5                       # IRQ No. 5 (#)
-board   BAYCOM                  # hardware type (*)
-#
-# SCC chip 2
-#
-chip    2
-data_a  0x302
-ctrl_a  0x306
-data_b  0x303
-ctrl_b  0x307
-board   BAYCOM
-
-An example for a PA0HZP card:
------------------------------
-
-chip 1
-data_a 0x153
-data_b 0x151
-ctrl_a 0x152
-ctrl_b 0x150
-irq 9
-pclock 4915200
-board PA0HZP
-vector 0x168
-escc no
-#
-#
-#
-chip 2
-data_a 0x157
-data_b 0x155
-ctrl_a 0x156
-ctrl_b 0x154
-irq 9
-pclock 4915200
-board PA0HZP
-vector 0x168
-escc no
-
-A DRSI would should probably work with this:
---------------------------------------------
-(actually: two DRSI cards...)
-
-chip 1
-data_a 0x303
-data_b 0x301
-ctrl_a 0x302
-ctrl_b 0x300
-irq 7
-pclock 4915200
-board DRSI
-escc no
-#
-#
-#
-chip 2
-data_a 0x313
-data_b 0x311
-ctrl_a 0x312
-ctrl_b 0x310
-irq 7
-pclock 4915200
-board DRSI
-escc no
-
-Note that you cannot use the on-board baudrate generator off DRSI
-cards. Use "mode dpll" for clock source (see below).
-
-This is based on information provided by Mike Bilow (and verified
-by Paul Helay)
-
-The utility "gencfg"
---------------------
-
-If you only know the parameters for the PE1CHL driver for DOS,
-run gencfg. It will generate the correct port addresses (I hope).
-Its parameters are exactly the same as the ones you use with
-the "attach scc" command in net, except that the string "init" must 
-not appear. Example:
-
-gencfg 2 0x150 4 2 0 1 0x168 9 4915200 
-
-will print a skeleton z8530drv.conf for the OptoSCC to stdout.
-
-gencfg 2 0x300 2 4 5 -4 0 7 4915200 0x10
-
-does the same for the BAYCOM USCC card. In my opinion it is much easier
-to edit scc_config.h... 
-
-
-1.2.2 channel configuration
-===========================
-
-The channel definition is divided into three sub sections for each
-channel:
-
-An example for scc0:
-
-# DEVICE
-
-device scc0    # the device for the following params
-
-# MODEM / BUFFERS
-
-speed 1200             # the default baudrate
-clock dpll             # clock source: 
-                       #       dpll     = normal half duplex operation
-                       #       external = MODEM provides own Rx/Tx clock
-                       #       divider  = use full duplex divider if
-                       #                  installed (1)
-mode nrzi              # HDLC encoding mode
-                       #       nrzi = 1k2 MODEM, G3RUH 9k6 MODEM
-                       #       nrz  = DF9IC 9k6 MODEM
-                       #
-bufsize        384             # size of buffers. Note that this must include
-                       # the AX.25 header, not only the data field!
-                       # (optional, defaults to 384)
-
-# KISS (Layer 1)
-
-txdelay 36              # (see chapter 1.4)
-persist 64
-slot    8
-tail    8
-fulldup 0
-wait    12
-min     3
-maxkey  7
-idle    3
-maxdef  120
-group   0
-txoff   off
-softdcd on                   
-slip    off
-
-The order WITHIN these sections is unimportant. The order OF these
-sections IS important. The MODEM parameters are set with the first
-recognized KISS parameter...
-
-Please note that you can initialize the board only once after boot
-(or insmod). You can change all parameters but "mode" and "clock" 
-later with the Sccparam program or through KISS. Just to avoid 
-security holes... 
-
-(1) this divider is usually mounted on the SCC-PBC (PA0HZP) or not
-    present at all (BayCom). It feeds back the output of the DPLL 
-    (digital pll) as transmit clock. Using this mode without a divider 
-    installed will normally result in keying the transceiver until 
-    maxkey expires --- of course without sending anything (useful).
-
-2. Attachment of a channel by your AX.25 software
-=================================================
-
-2.1 Kernel AX.25
-================
-
-To set up an AX.25 device you can simply type:
-
-       ifconfig scc0 44.128.1.1 hw ax25 dl0tha-7
-
-This will create a network interface with the IP number 44.128.20.107 
-and the callsign "dl0tha". If you do not have any IP number (yet) you 
-can use any of the 44.128.0.0 network. Note that you do not need 
-axattach. The purpose of axattach (like slattach) is to create a KISS 
-network device linked to a TTY. Please read the documentation of the 
-ax25-utils and the AX.25-HOWTO to learn how to set the parameters of
-the kernel AX.25.
-
-2.2 NOS, NET and TFKISS
-=======================
-
-Since the TTY driver (aka KISS TNC emulation) is gone you need
-to emulate the old behaviour. The cost of using these programs is
-that you probably need to compile the kernel AX.25, regardless of whether
-you actually use it or not. First setup your /etc/ax25/axports,
-for example:
-
-       9k6     dl0tha-9  9600  255 4 9600 baud port (scc3)
-       axlink  dl0tha-15 38400 255 4 Link to NOS
-
-Now "ifconfig" the scc device:
-
-       ifconfig scc3 44.128.1.1 hw ax25 dl0tha-9
-
-You can now axattach a pseudo-TTY:
-
-       axattach /dev/ptys0 axlink
-
-and start your NOS and attach /dev/ptys0 there. The problem is that
-NOS is reachable only via digipeating through the kernel AX.25
-(disastrous on a DAMA controlled channel). To solve this problem,
-configure "rxecho" to echo the incoming frames from "9k6" to "axlink"
-and outgoing frames from "axlink" to "9k6" and start:
-
-       rxecho
-
-Or simply use "kissbridge" coming with z8530drv-utils:
-
-       ifconfig scc3 hw ax25 dl0tha-9
-       kissbridge scc3 /dev/ptys0
-
-
-3. Adjustment and Display of parameters
-=======================================
-
-3.1 Displaying SCC Parameters:
-==============================
-
-Once a SCC channel has been attached, the parameter settings and 
-some statistic information can be shown using the param program:
-
-dl1bke-u:~$ sccstat scc0
-
-Parameters:
-
-speed       : 1200 baud
-txdelay     : 36
-persist     : 255
-slottime    : 0
-txtail      : 8
-fulldup     : 1
-waittime    : 12
-mintime     : 3 sec
-maxkeyup    : 7 sec
-idletime    : 3 sec
-maxdefer    : 120 sec
-group       : 0x00
-txoff       : off
-softdcd     : on
-SLIP        : off
-
-Status:
-
-HDLC                  Z8530           Interrupts         Buffers
------------------------------------------------------------------------
-Sent       :     273  RxOver :     0  RxInts :   125074  Size    :  384
-Received   :    1095  TxUnder:     0  TxInts :     4684  NoSpace :    0
-RxErrors   :    1591                  ExInts :    11776
-TxErrors   :       0                  SpInts :     1503
-Tx State   :    idle
-
-
-The status info shown is:
-
-Sent           - number of frames transmitted
-Received       - number of frames received
-RxErrors       - number of receive errors (CRC, ABORT)
-TxErrors       - number of discarded Tx frames (due to various reasons) 
-Tx State       - status of the Tx interrupt handler: idle/busy/active/tail (2)
-RxOver         - number of receiver overruns
-TxUnder                - number of transmitter underruns
-RxInts         - number of receiver interrupts
-TxInts         - number of transmitter interrupts
-EpInts         - number of receiver special condition interrupts
-SpInts         - number of external/status interrupts
-Size           - maximum size of an AX.25 frame (*with* AX.25 headers!)
-NoSpace                - number of times a buffer could not get allocated
-
-An overrun is abnormal. If lots of these occur, the product of
-baudrate and number of interfaces is too high for the processing
-power of your computer. NoSpace errors are unlikely to be caused by the
-driver or the kernel AX.25.
-
-
-3.2 Setting Parameters
-======================
-
-
-The setting of parameters of the emulated KISS TNC is done in the 
-same way in the SCC driver. You can change parameters by using
-the kissparms program from the ax25-utils package or use the program 
-"sccparam":
-
-     sccparam <device> <paramname> <decimal-|hexadecimal value>
-
-You can change the following parameters:
-
-param      : value
-------------------------
-speed       : 1200
-txdelay     : 36
-persist     : 255
-slottime    : 0
-txtail      : 8
-fulldup     : 1
-waittime    : 12
-mintime     : 3
-maxkeyup    : 7
-idletime    : 3
-maxdefer    : 120
-group       : 0x00
-txoff       : off
-softdcd     : on
-SLIP        : off
-
-
-The parameters have the following meaning:
-
-speed:
-     The baudrate on this channel in bits/sec
-
-     Example: sccparam /dev/scc3 speed 9600
-
-txdelay:
-     The delay (in units of 10 ms) after keying of the 
-     transmitter, until the first byte is sent. This is usually 
-     called "TXDELAY" in a TNC.  When 0 is specified, the driver 
-     will just wait until the CTS signal is asserted. This 
-     assumes the presence of a timer or other circuitry in the 
-     MODEM and/or transmitter, that asserts CTS when the 
-     transmitter is ready for data.
-     A normal value of this parameter is 30-36.
-
-     Example: sccparam /dev/scc0 txd 20
-
-persist:
-     This is the probability that the transmitter will be keyed 
-     when the channel is found to be free.  It is a value from 0 
-     to 255, and the probability is (value+1)/256.  The value 
-     should be somewhere near 50-60, and should be lowered when 
-     the channel is used more heavily.
-
-     Example: sccparam /dev/scc2 persist 20
-
-slottime:
-     This is the time between samples of the channel. It is 
-     expressed in units of 10 ms.  About 200-300 ms (value 20-30) 
-     seems to be a good value.
-
-     Example: sccparam /dev/scc0 slot 20
-
-tail:
-     The time the transmitter will remain keyed after the last 
-     byte of a packet has been transferred to the SCC. This is 
-     necessary because the CRC and a flag still have to leave the 
-     SCC before the transmitter is keyed down. The value depends 
-     on the baudrate selected.  A few character times should be 
-     sufficient, e.g. 40ms at 1200 baud. (value 4)
-     The value of this parameter is in 10 ms units.
-
-     Example: sccparam /dev/scc2 4
-
-full:
-     The full-duplex mode switch. This can be one of the following 
-     values:
-
-     0:   The interface will operate in CSMA mode (the normal 
-          half-duplex packet radio operation)
-     1:   Fullduplex mode, i.e. the transmitter will be keyed at 
-          any time, without checking the received carrier.  It 
-          will be unkeyed when there are no packets to be sent.
-     2:   Like 1, but the transmitter will remain keyed, also 
-          when there are no packets to be sent.  Flags will be 
-          sent in that case, until a timeout (parameter 10) 
-          occurs.
-
-     Example: sccparam /dev/scc0 fulldup off
-
-wait:
-     The initial waittime before any transmit attempt, after the 
-     frame has been queue for transmit.  This is the length of 
-     the first slot in CSMA mode.  In full duplex modes it is
-     set to 0 for maximum performance.
-     The value of this parameter is in 10 ms units. 
-
-     Example: sccparam /dev/scc1 wait 4
-
-maxkey:
-     The maximal time the transmitter will be keyed to send 
-     packets, in seconds.  This can be useful on busy CSMA 
-     channels, to avoid "getting a bad reputation" when you are 
-     generating a lot of traffic.  After the specified time has 
-     elapsed, no new frame will be started. Instead, the trans-
-     mitter will be switched off for a specified time (parameter 
-     min), and then the selected algorithm for keyup will be 
-     started again.
-     The value 0 as well as "off" will disable this feature, 
-     and allow infinite transmission time. 
-
-     Example: sccparam /dev/scc0 maxk 20
-
-min:
-     This is the time the transmitter will be switched off when 
-     the maximum transmission time is exceeded.
-
-     Example: sccparam /dev/scc3 min 10
-
-idle
-     This parameter specifies the maximum idle time in full duplex 
-     2 mode, in seconds.  When no frames have been sent for this 
-     time, the transmitter will be keyed down.  A value of 0 is
-     has same result as the fullduplex mode 1. This parameter
-     can be disabled.
-
-     Example: sccparam /dev/scc2 idle off      # transmit forever
-
-maxdefer
-     This is the maximum time (in seconds) to wait for a free channel
-     to send. When this timer expires the transmitter will be keyed 
-     IMMEDIATELY. If you love to get trouble with other users you
-     should set this to a very low value ;-)
-
-     Example: sccparam /dev/scc0 maxdefer 240  # 2 minutes
-
-
-txoff:
-     When this parameter has the value 0, the transmission of packets
-     is enable. Otherwise it is disabled.
-
-     Example: sccparam /dev/scc2 txoff on
-
-group:
-     It is possible to build special radio equipment to use more than 
-     one frequency on the same band, e.g. using several receivers and 
-     only one transmitter that can be switched between frequencies.
-     Also, you can connect several radios that are active on the same 
-     band.  In these cases, it is not possible, or not a good idea, to 
-     transmit on more than one frequency.  The SCC driver provides a 
-     method to lock transmitters on different interfaces, using the 
-     "param <interface> group <x>" command.  This will only work when 
-     you are using CSMA mode (parameter full = 0).
-     The number <x> must be 0 if you want no group restrictions, and 
-     can be computed as follows to create restricted groups:
-     <x> is the sum of some OCTAL numbers:
-
-     200  This transmitter will only be keyed when all other 
-          transmitters in the group are off.
-     100  This transmitter will only be keyed when the carrier 
-          detect of all other interfaces in the group is off.
-     0xx  A byte that can be used to define different groups.  
-          Interfaces are in the same group, when the logical AND 
-          between their xx values is nonzero.
-
-     Examples:
-     When 2 interfaces use group 201, their transmitters will never be 
-     keyed at the same time.
-     When 2 interfaces use group 101, the transmitters will only key 
-     when both channels are clear at the same time.  When group 301, 
-     the transmitters will not be keyed at the same time.
-
-     Don't forget to convert the octal numbers into decimal before
-     you set the parameter.
-
-     Example: (to be written)
-
-softdcd:
-     use a software dcd instead of the real one... Useful for a very
-     slow squelch.
-
-     Example: sccparam /dev/scc0 soft on
-
-
-4. Problems 
-===========
-
-If you have tx-problems with your BayCom USCC card please check
-the manufacturer of the 8530. SGS chips have a slightly
-different timing. Try Zilog...  A solution is to write to register 8 
-instead to the data port, but this won't work with the ESCC chips. 
-*SIGH!*
-
-A very common problem is that the PTT locks until the maxkeyup timer
-expires, although interrupts and clock source are correct. In most
-cases compiling the driver with CONFIG_SCC_DELAY (set with
-make config) solves the problems. For more hints read the (pseudo) FAQ 
-and the documentation coming with z8530drv-utils.
-
-I got reports that the driver has problems on some 386-based systems.
-(i.e. Amstrad) Those systems have a bogus AT bus timing which will
-lead to delayed answers on interrupts. You can recognize these
-problems by looking at the output of Sccstat for the suspected
-port. If it shows under- and overruns you own such a system.
-
-Delayed processing of received data: This depends on
-
-- the kernel version
-
-- kernel profiling compiled or not
-
-- a high interrupt load
-
-- a high load of the machine --- running X, Xmorph, XV and Povray,
-  while compiling the kernel... hmm ... even with 32 MB RAM ...  ;-)
-  Or running a named for the whole .ampr.org domain on an 8 MB
-  box...
-
-- using information from rxecho or kissbridge.
-
-Kernel panics: please read /linux/README and find out if it
-really occurred within the scc driver.
-
-If you cannot solve a problem, send me
-
-- a description of the problem,
-- information on your hardware (computer system, scc board, modem)
-- your kernel version
-- the output of cat /proc/net/z8530
-
-4. Thor RLC100
-==============
-
-Mysteriously this board seems not to work with the driver. Anyone
-got it up-and-running?
-
-
-Many thanks to Linus Torvalds and Alan Cox for including the driver
-in the Linux standard distribution and their support.
-
-Joerg Reuter   ampr-net: dl1bke@db0pra.ampr.org
-               AX-25   : DL1BKE @ DB0ABH.#BAY.DEU.EU
-               Internet: jreuter@yaina.de
-               WWW     : http://yaina.de/jreuter
index 7e3167b..afb0a43 100644 (file)
@@ -110,3 +110,6 @@ NON-ATOMIC CONTEXT:
                        short, the difference is whether the sleep can be ended
                        early by a signal. In general, just use msleep unless
                        you know you have a need for the interruptible variant.
+
+       FLEXIBLE SLEEPING (any delay, uninterruptible)
+               * Use fsleep
index fa7ddc0..5325c71 100644 (file)
@@ -1399,8 +1399,8 @@ must have read/write permission; CS must be __BOOT_CS and DS, ES, SS
 must be __BOOT_DS; interrupt must be disabled; %rsi must hold the base
 address of the struct boot_params.
 
-EFI Handover Protocol
-=====================
+EFI Handover Protocol (deprecated)
+==================================
 
 This protocol allows boot loaders to defer initialisation to the EFI
 boot stub. The boot loader is required to load the kernel/initrd(s)
@@ -1408,6 +1408,12 @@ from the boot media and jump to the EFI handover protocol entry point
 which is hdr->handover_offset bytes from the beginning of
 startup_{32,64}.
 
+The boot loader MUST respect the kernel's PE/COFF metadata when it comes
+to section alignment, the memory footprint of the executable image beyond
+the size of the file itself, and any other aspect of the PE/COFF header
+that may affect correct operation of the image as a PE/COFF binary in the
+execution context provided by the EFI firmware.
+
 The function prototype for the handover entry point looks like this::
 
     efi_main(void *handle, efi_system_table_t *table, struct boot_params *bp)
@@ -1419,9 +1425,18 @@ UEFI specification. 'bp' is the boot loader-allocated boot params.
 
 The boot loader *must* fill out the following fields in bp::
 
-  - hdr.code32_start
   - hdr.cmd_line_ptr
   - hdr.ramdisk_image (if applicable)
   - hdr.ramdisk_size  (if applicable)
 
 All other fields should be zero.
+
+NOTE: The EFI Handover Protocol is deprecated in favour of the ordinary PE/COFF
+      entry point, combined with the LINUX_EFI_INITRD_MEDIA_GUID based initrd
+      loading protocol (refer to [0] for an example of the bootloader side of
+      this), which removes the need for any knowledge on the part of the EFI
+      bootloader regarding the internal representation of boot_params or any
+      requirements/limitations regarding the placement of the command line
+      and ramdisk in memory, or the placement of the kernel image itself.
+
+[0] https://github.com/u-boot/u-boot/commit/ec80b4735a593961fe701cc3a5d717d4739b0fd0
index e64e5db..e581ae4 100644 (file)
@@ -147,7 +147,7 @@ Maintainers List
 M:     Steffen Klassert <klassert@kernel.org>
 L:     netdev@vger.kernel.org
 S:     Odd Fixes
-F:     Documentation/networking/device_drivers/3com/vortex.txt
+F:     Documentation/networking/device_drivers/3com/vortex.rst
 F:     drivers/net/ethernet/3com/3c59x.c
 
 3CR990 NETWORK DRIVER
@@ -189,11 +189,11 @@ F:        drivers/net/hamradio/6pack.c
 M:     Johannes Berg <johannes@sipsolutions.net>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
-W:     http://wireless.kernel.org/
+W:     https://wireless.wiki.kernel.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
 F:     Documentation/driver-api/80211/cfg80211.rst
-F:     Documentation/networking/regulatory.txt
+F:     Documentation/networking/regulatory.rst
 F:     include/linux/ieee80211.h
 F:     include/net/cfg80211.h
 F:     include/net/ieee80211_radiotap.h
@@ -505,7 +505,7 @@ F:  drivers/hwmon/adm1029.c
 ADM8211 WIRELESS DRIVER
 L:     linux-wireless@vger.kernel.org
 S:     Orphan
-W:     http://wireless.kernel.org/
+W:     https://wireless.wiki.kernel.org/
 F:     drivers/net/wireless/admtek/adm8211.*
 
 ADP1653 FLASH CONTROLLER DRIVER
@@ -570,7 +570,7 @@ F:  Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
 F:     drivers/input/misc/adxl34x.c
 
 ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER
-M:     Stefan Popa <stefan.popa@analog.com>
+M:     Michael Hennerich <michael.hennerich@analog.com>
 S:     Supported
 W:     http://ez.analog.com/community/linux-device-drivers
 F:     Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml
@@ -815,7 +815,7 @@ R:  Saeed Bishara <saeedb@amazon.com>
 R:     Zorik Machulsky <zorik@amazon.com>
 L:     netdev@vger.kernel.org
 S:     Supported
-F:     Documentation/networking/device_drivers/amazon/ena.txt
+F:     Documentation/networking/device_drivers/amazon/ena.rst
 F:     drivers/net/ethernet/amazon/
 
 AMAZON RDMA EFA DRIVER
@@ -922,7 +922,7 @@ F:  arch/arm64/boot/dts/amd/amd-seattle-xgbe*.dtsi
 F:     drivers/net/ethernet/amd/xgbe/
 
 ANALOG DEVICES INC AD5686 DRIVER
-M:     Stefan Popa <stefan.popa@analog.com>
+M:     Michael Hennerich <Michael.Hennerich@analog.com>
 L:     linux-pm@vger.kernel.org
 S:     Supported
 W:     http://ez.analog.com/community/linux-device-drivers
@@ -930,7 +930,7 @@ F:  drivers/iio/dac/ad5686*
 F:     drivers/iio/dac/ad5696*
 
 ANALOG DEVICES INC AD5758 DRIVER
-M:     Stefan Popa <stefan.popa@analog.com>
+M:     Michael Hennerich <Michael.Hennerich@analog.com>
 L:     linux-iio@vger.kernel.org
 S:     Supported
 W:     http://ez.analog.com/community/linux-device-drivers
@@ -946,7 +946,7 @@ F:  Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml
 F:     drivers/iio/adc/ad7091r5.c
 
 ANALOG DEVICES INC AD7124 DRIVER
-M:     Stefan Popa <stefan.popa@analog.com>
+M:     Michael Hennerich <Michael.Hennerich@analog.com>
 L:     linux-iio@vger.kernel.org
 S:     Supported
 W:     http://ez.analog.com/community/linux-device-drivers
@@ -970,7 +970,7 @@ F:  Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml
 F:     drivers/iio/adc/ad7292.c
 
 ANALOG DEVICES INC AD7606 DRIVER
-M:     Stefan Popa <stefan.popa@analog.com>
+M:     Michael Hennerich <Michael.Hennerich@analog.com>
 M:     Beniamin Bia <beniamin.bia@analog.com>
 L:     linux-iio@vger.kernel.org
 S:     Supported
@@ -979,7 +979,7 @@ F:  Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml
 F:     drivers/iio/adc/ad7606.c
 
 ANALOG DEVICES INC AD7768-1 DRIVER
-M:     Stefan Popa <stefan.popa@analog.com>
+M:     Michael Hennerich <Michael.Hennerich@analog.com>
 L:     linux-iio@vger.kernel.org
 S:     Supported
 W:     http://ez.analog.com/community/linux-device-drivers
@@ -1040,7 +1040,7 @@ F:        Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml
 F:     drivers/hwmon/adm1177.c
 
 ANALOG DEVICES INC ADP5061 DRIVER
-M:     Stefan Popa <stefan.popa@analog.com>
+M:     Michael Hennerich <Michael.Hennerich@analog.com>
 L:     linux-pm@vger.kernel.org
 S:     Supported
 W:     http://ez.analog.com/community/linux-device-drivers
@@ -1109,7 +1109,6 @@ F:        drivers/iio/amplifiers/hmc425a.c
 ANALOG DEVICES INC IIO DRIVERS
 M:     Lars-Peter Clausen <lars@metafoo.de>
 M:     Michael Hennerich <Michael.Hennerich@analog.com>
-M:     Stefan Popa <stefan.popa@analog.com>
 S:     Supported
 W:     http://wiki.analog.com/
 W:     http://ez.analog.com/community/linux-device-drivers
@@ -1275,7 +1274,7 @@ L:        netdev@vger.kernel.org
 S:     Supported
 W:     https://www.marvell.com/
 Q:     http://patchwork.ozlabs.org/project/netdev/list/
-F:     Documentation/networking/device_drivers/aquantia/atlantic.txt
+F:     Documentation/networking/device_drivers/aquantia/atlantic.rst
 F:     drivers/net/ethernet/aquantia/atlantic/
 
 AQUANTIA ETHERNET DRIVER PTP SUBSYSTEM
@@ -1323,7 +1322,10 @@ ARM INTEGRATOR, VERSATILE AND REALVIEW SUPPORT
 M:     Linus Walleij <linus.walleij@linaro.org>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
-F:     Documentation/devicetree/bindings/arm/arm-boards
+F:     Documentation/devicetree/bindings/arm/arm,integrator.yaml
+F:     Documentation/devicetree/bindings/arm/arm,realview.yaml
+F:     Documentation/devicetree/bindings/arm/arm,versatile.yaml
+F:     Documentation/devicetree/bindings/arm/arm,vexpress-juno.yaml
 F:     Documentation/devicetree/bindings/auxdisplay/arm-charlcd.txt
 F:     Documentation/devicetree/bindings/clock/arm,syscon-icst.yaml
 F:     Documentation/devicetree/bindings/i2c/i2c-versatile.txt
@@ -2847,14 +2849,14 @@ M:      Nick Kossifidis <mickflemm@gmail.com>
 M:     Luis Chamberlain <mcgrof@kernel.org>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
-W:     http://wireless.kernel.org/en/users/Drivers/ath5k
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/ath5k
 F:     drivers/net/wireless/ath/ath5k/
 
 ATHEROS ATH6KL WIRELESS DRIVER
 M:     Kalle Valo <kvalo@codeaurora.org>
 L:     linux-wireless@vger.kernel.org
 S:     Supported
-W:     http://wireless.kernel.org/en/users/Drivers/ath6kl
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/ath6kl
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
 F:     drivers/net/wireless/ath/ath6kl/
 
@@ -3017,7 +3019,7 @@ B43 WIRELESS DRIVER
 L:     linux-wireless@vger.kernel.org
 L:     b43-dev@lists.infradead.org
 S:     Odd Fixes
-W:     http://wireless.kernel.org/en/users/Drivers/b43
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/b43
 F:     drivers/net/wireless/broadcom/b43/
 
 B43LEGACY WIRELESS DRIVER
@@ -3025,7 +3027,7 @@ M:        Larry Finger <Larry.Finger@lwfinger.net>
 L:     linux-wireless@vger.kernel.org
 L:     b43-dev@lists.infradead.org
 S:     Maintained
-W:     http://wireless.kernel.org/en/users/Drivers/b43
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/b43
 F:     drivers/net/wireless/broadcom/b43legacy/
 
 BACKLIGHT CLASS/SUBSYSTEM
@@ -3189,7 +3191,7 @@ Q:        https://patchwork.ozlabs.org/project/netdev/list/?delegate=77147
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git
 F:     Documentation/bpf/
-F:     Documentation/networking/filter.txt
+F:     Documentation/networking/filter.rst
 F:     arch/*/net/*
 F:     include/linux/bpf*
 F:     include/linux/filter.h
@@ -3655,7 +3657,7 @@ L:        linux-btrfs@vger.kernel.org
 S:     Maintained
 W:     http://btrfs.wiki.kernel.org/
 Q:     http://patchwork.kernel.org/project/linux-btrfs/list/
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs.git
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git
 F:     Documentation/filesystems/btrfs.rst
 F:     fs/btrfs/
 F:     include/linux/btrfs*
@@ -3840,7 +3842,7 @@ CARL9170 LINUX COMMUNITY WIRELESS DRIVER
 M:     Christian Lamparter <chunkeey@googlemail.com>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
-W:     http://wireless.kernel.org/en/users/Drivers/carl9170
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/carl9170
 F:     drivers/net/wireless/ath/carl9170/
 
 CAVIUM I2C DRIVER
@@ -4691,7 +4693,7 @@ F:        net/ax25/sysctl_net_ax25.c
 DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER
 L:     netdev@vger.kernel.org
 S:     Orphan
-F:     Documentation/networking/device_drivers/dec/dmfe.txt
+F:     Documentation/networking/device_drivers/dec/dmfe.rst
 F:     drivers/net/ethernet/dec/tulip/dmfe.c
 
 DC390/AM53C974 SCSI driver
@@ -4725,7 +4727,7 @@ DECnet NETWORK LAYER
 L:     linux-decnet-user@lists.sourceforge.net
 S:     Orphan
 W:     http://linux-decnet.sourceforge.net
-F:     Documentation/networking/decnet.txt
+F:     Documentation/networking/decnet.rst
 F:     net/decnet/
 
 DECSTATION PLATFORM SUPPORT
@@ -5173,6 +5175,7 @@ S:        Maintained
 F:     drivers/soc/fsl/dpio
 
 DPAA2 ETHERNET DRIVER
+M:     Ioana Ciornei <ioana.ciornei@nxp.com>
 M:     Ioana Radulescu <ruxandra.radulescu@nxp.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
@@ -5552,7 +5555,7 @@ M:        Chen-Yu Tsai <wens@csie.org>
 L:     dri-devel@lists.freedesktop.org
 S:     Supported
 T:     git git://anongit.freedesktop.org/drm/drm-misc
-F:     Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
+F:     Documentation/devicetree/bindings/display/allwinner*
 F:     drivers/gpu/drm/sun4i/
 
 DRM DRIVERS FOR AMLOGIC SOCS
@@ -5932,6 +5935,7 @@ F:        lib/dynamic_debug.c
 DYNAMIC INTERRUPT MODERATION
 M:     Tal Gilboa <talgi@mellanox.com>
 S:     Maintained
+F:     Documentation/networking/net_dim.rst
 F:     include/linux/dim.h
 F:     lib/dim/
 
@@ -7810,7 +7814,7 @@ HUAWEI ETHERNET DRIVER
 M:     Aviad Krawczyk <aviad.krawczyk@huawei.com>
 L:     netdev@vger.kernel.org
 S:     Supported
-F:     Documentation/networking/hinic.txt
+F:     Documentation/networking/hinic.rst
 F:     drivers/net/ethernet/huawei/hinic/
 
 HUGETLB FILESYSTEM
@@ -7862,7 +7866,7 @@ S:        Supported
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux.git
 F:     Documentation/ABI/stable/sysfs-bus-vmbus
 F:     Documentation/ABI/testing/debugfs-hyperv
-F:     Documentation/networking/device_drivers/microsoft/netvsc.txt
+F:     Documentation/networking/device_drivers/microsoft/netvsc.rst
 F:     arch/x86/hyperv
 F:     arch/x86/include/asm/hyperv-tlfs.h
 F:     arch/x86/include/asm/mshyperv.h
@@ -8737,8 +8741,8 @@ INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWORK CONNECTION SUPPORT
 M:     Stanislav Yakovlev <stas.yakovlev@gmail.com>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
-F:     Documentation/networking/device_drivers/intel/ipw2100.txt
-F:     Documentation/networking/device_drivers/intel/ipw2200.txt
+F:     Documentation/networking/device_drivers/intel/ipw2100.rst
+F:     Documentation/networking/device_drivers/intel/ipw2200.rst
 F:     drivers/net/wireless/intel/ipw2x00/
 
 INTEL PSTATE DRIVER
@@ -8929,7 +8933,7 @@ L:        lvs-devel@vger.kernel.org
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/horms/ipvs-next.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/horms/ipvs.git
-F:     Documentation/networking/ipvs-sysctl.txt
+F:     Documentation/networking/ipvs-sysctl.rst
 F:     include/net/ip_vs.h
 F:     include/uapi/linux/ip_vs.h
 F:     net/netfilter/ipvs/
@@ -9325,6 +9329,7 @@ M:        Christian Borntraeger <borntraeger@de.ibm.com>
 M:     Janosch Frank <frankja@linux.ibm.com>
 R:     David Hildenbrand <david@redhat.com>
 R:     Cornelia Huck <cohuck@redhat.com>
+R:     Claudio Imbrenda <imbrenda@linux.ibm.com>
 L:     kvm@vger.kernel.org
 S:     Supported
 W:     http://www.ibm.com/developerworks/linux/linux390/
@@ -9412,6 +9417,13 @@ F:       include/linux/keyctl.h
 F:     include/uapi/linux/keyctl.h
 F:     security/keys/
 
+KFIFO
+M:     Stefani Seibold <stefani@seibold.net>
+S:     Maintained
+F:     include/linux/kfifo.h
+F:     lib/kfifo.c
+F:     samples/kfifo/
+
 KGDB / KDB /debug_core
 M:     Jason Wessel <jason.wessel@windriver.com>
 M:     Daniel Thompson <daniel.thompson@linaro.org>
@@ -9502,7 +9514,7 @@ F:        drivers/soc/lantiq
 LAPB module
 L:     linux-x25@vger.kernel.org
 S:     Orphan
-F:     Documentation/networking/lapb-module.txt
+F:     Documentation/networking/lapb-module.rst
 F:     include/*/lapb.h
 F:     net/lapb/
 
@@ -10063,10 +10075,10 @@ MAC80211
 M:     Johannes Berg <johannes@sipsolutions.net>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
-W:     http://wireless.kernel.org/
+W:     https://wireless.wiki.kernel.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
-F:     Documentation/networking/mac80211-injection.txt
+F:     Documentation/networking/mac80211-injection.rst
 F:     Documentation/networking/mac80211_hwsim/mac80211_hwsim.rst
 F:     drivers/net/wireless/mac80211_hwsim.[ch]
 F:     include/net/mac80211.h
@@ -10693,7 +10705,6 @@ MEDIATEK MT76 WIRELESS LAN DRIVER
 M:     Felix Fietkau <nbd@nbd.name>
 M:     Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
 R:     Ryder Lee <ryder.lee@mediatek.com>
-R:     Roy Luo <royluo@google.com>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
 F:     drivers/net/wireless/mediatek/mt76/
@@ -11644,8 +11655,8 @@ NETERION 10GbE DRIVERS (s2io/vxge)
 M:     Jon Mason <jdmason@kudzu.us>
 L:     netdev@vger.kernel.org
 S:     Supported
-F:     Documentation/networking/device_drivers/neterion/s2io.txt
-F:     Documentation/networking/device_drivers/neterion/vxge.txt
+F:     Documentation/networking/device_drivers/neterion/s2io.rst
+F:     Documentation/networking/device_drivers/neterion/vxge.rst
 F:     drivers/net/ethernet/neterion/
 
 NETFILTER
@@ -12644,7 +12655,7 @@ F:      fs/orangefs/
 ORINOCO DRIVER
 L:     linux-wireless@vger.kernel.org
 S:     Orphan
-W:     http://wireless.kernel.org/en/users/Drivers/orinoco
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/orinoco
 W:     http://www.nongnu.org/orinoco/
 F:     drivers/net/wireless/intersil/orinoco/
 
@@ -12670,7 +12681,7 @@ P54 WIRELESS DRIVER
 M:     Christian Lamparter <chunkeey@googlemail.com>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
-W:     http://wireless.kernel.org/en/users/Drivers/p54
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/p54
 F:     drivers/net/wireless/intersil/p54/
 
 PACKING
@@ -13038,7 +13049,7 @@ F:      drivers/pci/controller/pci-xgene-msi.c
 
 PCI NATIVE HOST BRIDGE AND ENDPOINT DRIVERS
 M:     Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-R:     Andrew Murray <amurray@thegoodpenguin.co.uk>
+R:     Rob Herring <robh@kernel.org>
 L:     linux-pci@vger.kernel.org
 S:     Supported
 Q:     http://patchwork.ozlabs.org/project/linux-pci/list/
@@ -13250,7 +13261,7 @@ F:      drivers/input/joystick/pxrc.c
 PHONET PROTOCOL
 M:     Remi Denis-Courmont <courmisch@gmail.com>
 S:     Supported
-F:     Documentation/networking/phonet.txt
+F:     Documentation/networking/phonet.rst
 F:     include/linux/phonet.h
 F:     include/net/phonet/
 F:     include/uapi/linux/phonet.h
@@ -13591,7 +13602,7 @@ PRISM54 WIRELESS DRIVER
 M:     Luis Chamberlain <mcgrof@kernel.org>
 L:     linux-wireless@vger.kernel.org
 S:     Obsolete
-W:     http://wireless.kernel.org/en/users/Drivers/p54
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/p54
 F:     drivers/net/wireless/intersil/prism54/
 
 PROC FILESYSTEM
@@ -13853,7 +13864,8 @@ S:      Maintained
 F:     drivers/scsi/qla1280.[ch]
 
 QLOGIC QLA2XXX FC-SCSI DRIVER
-M:     hmadhani@marvell.com
+M:     Nilesh Javali <njavali@marvell.com>
+M:     GR-QLogic-Storage-Upstream@marvell.com
 L:     linux-scsi@vger.kernel.org
 S:     Supported
 F:     Documentation/scsi/LICENSE.qla2xxx
@@ -13931,7 +13943,7 @@ QUALCOMM ATHEROS ATH10K WIRELESS DRIVER
 M:     Kalle Valo <kvalo@codeaurora.org>
 L:     ath10k@lists.infradead.org
 S:     Supported
-W:     http://wireless.kernel.org/en/users/Drivers/ath10k
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/ath10k
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
 F:     drivers/net/wireless/ath/ath10k/
 
@@ -13946,7 +13958,7 @@ QUALCOMM ATHEROS ATH9K WIRELESS DRIVER
 M:     QCA ath9k Development <ath9k-devel@qca.qualcomm.com>
 L:     linux-wireless@vger.kernel.org
 S:     Supported
-W:     http://wireless.kernel.org/en/users/Drivers/ath9k
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/ath9k
 F:     drivers/net/wireless/ath/ath9k/
 
 QUALCOMM CAMERA SUBSYSTEM DRIVER
@@ -14018,7 +14030,7 @@ M:      Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
 M:     Sean Tranchetti <stranche@codeaurora.org>
 L:     netdev@vger.kernel.org
 S:     Maintained
-F:     Documentation/networking/device_drivers/qualcomm/rmnet.txt
+F:     Documentation/networking/device_drivers/qualcomm/rmnet.rst
 F:     drivers/net/ethernet/qualcomm/rmnet/
 F:     include/linux/if_rmnet.h
 
@@ -14043,13 +14055,12 @@ QUALCOMM WCN36XX WIRELESS DRIVER
 M:     Kalle Valo <kvalo@codeaurora.org>
 L:     wcn36xx@lists.infradead.org
 S:     Supported
-W:     http://wireless.kernel.org/en/users/Drivers/wcn36xx
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/wcn36xx
 T:     git git://github.com/KrasnikovEugene/wcn36xx.git
 F:     drivers/net/wireless/ath/wcn36xx/
 
 QUANTENNA QTNFMAC WIRELESS DRIVER
 M:     Igor Mitsyanko <imitsyanko@quantenna.com>
-M:     Avinash Patil <avinashp@quantenna.com>
 M:     Sergey Matyukevich <smatyukevich@quantenna.com>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
@@ -14207,7 +14218,7 @@ L:      linux-rdma@vger.kernel.org
 L:     rds-devel@oss.oracle.com (moderated for non-subscribers)
 S:     Supported
 W:     https://oss.oracle.com/projects/rds/
-F:     Documentation/networking/rds.txt
+F:     Documentation/networking/rds.rst
 F:     net/rds/
 
 RDT - RESOURCE ALLOCATION
@@ -14271,7 +14282,7 @@ REALTEK WIRELESS DRIVER (rtlwifi family)
 M:     Ping-Ke Shih <pkshih@realtek.com>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
-W:     http://wireless.kernel.org/
+W:     https://wireless.wiki.kernel.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
 F:     drivers/net/wireless/realtek/rtlwifi/
 
@@ -14406,7 +14417,7 @@ RFKILL
 M:     Johannes Berg <johannes@sipsolutions.net>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
-W:     http://wireless.kernel.org/
+W:     https://wireless.wiki.kernel.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
 F:     Documentation/ABI/stable/sysfs-class-rfkill
@@ -14555,7 +14566,7 @@ F:      drivers/media/dvb-frontends/rtl2832_sdr*
 RTL8180 WIRELESS DRIVER
 L:     linux-wireless@vger.kernel.org
 S:     Orphan
-W:     http://wireless.kernel.org/
+W:     https://wireless.wiki.kernel.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
 F:     drivers/net/wireless/realtek/rtl818x/rtl8180/
 
@@ -14565,7 +14576,7 @@ M:      Hin-Tak Leung <htl10@users.sourceforge.net>
 M:     Larry Finger <Larry.Finger@lwfinger.net>
 L:     linux-wireless@vger.kernel.org
 S:     Maintained
-W:     http://wireless.kernel.org/
+W:     https://wireless.wiki.kernel.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
 F:     drivers/net/wireless/realtek/rtl818x/rtl8187/
 
@@ -14581,7 +14592,7 @@ M:      David Howells <dhowells@redhat.com>
 L:     linux-afs@lists.infradead.org
 S:     Supported
 W:     https://www.infradead.org/~dhowells/kafs/
-F:     Documentation/networking/rxrpc.txt
+F:     Documentation/networking/rxrpc.rst
 F:     include/keys/rxrpc-type.h
 F:     include/net/af_rxrpc.h
 F:     include/trace/events/rxrpc.h
@@ -14987,7 +14998,7 @@ M:      Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
 L:     linux-sctp@vger.kernel.org
 S:     Maintained
 W:     http://lksctp.sourceforge.net
-F:     Documentation/networking/sctp.txt
+F:     Documentation/networking/sctp.rst
 F:     include/linux/sctp.h
 F:     include/net/sctp/
 F:     include/uapi/linux/sctp.h
@@ -15862,7 +15873,7 @@ SPIDERNET NETWORK DRIVER for CELL
 M:     Ishizaki Kou <kou.ishizaki@toshiba.co.jp>
 L:     netdev@vger.kernel.org
 S:     Supported
-F:     Documentation/networking/device_drivers/toshiba/spider_net.txt
+F:     Documentation/networking/device_drivers/toshiba/spider_net.rst
 F:     drivers/net/ethernet/toshiba/spider_net*
 
 SPMI SUBSYSTEM
@@ -16920,8 +16931,8 @@ F:      drivers/media/platform/ti-vpe/
 TI WILINK WIRELESS DRIVERS
 L:     linux-wireless@vger.kernel.org
 S:     Orphan
-W:     http://wireless.kernel.org/en/users/Drivers/wl12xx
-W:     http://wireless.kernel.org/en/users/Drivers/wl1251
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/wl12xx
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/wl1251
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx.git
 F:     drivers/net/wireless/ti/
 F:     include/linux/wl12xx.h
@@ -16959,7 +16970,7 @@ M:      Samuel Chessman <chessman@tux.org>
 L:     tlan-devel@lists.sourceforge.net (subscribers-only)
 S:     Maintained
 W:     http://sourceforge.net/projects/tlan/
-F:     Documentation/networking/device_drivers/ti/tlan.txt
+F:     Documentation/networking/device_drivers/ti/tlan.rst
 F:     drivers/net/ethernet/ti/tlan.*
 
 TM6000 VIDEO4LINUX DRIVER
@@ -17149,7 +17160,7 @@ TUN/TAP driver
 M:     Maxim Krasnyansky <maxk@qti.qualcomm.com>
 S:     Maintained
 W:     http://vtun.sourceforge.net/tun
-F:     Documentation/networking/tuntap.txt
+F:     Documentation/networking/tuntap.rst
 F:     arch/um/os-Linux/drivers/
 
 TURBOCHANNEL SUBSYSTEM
@@ -18094,7 +18105,7 @@ M:      David Ahern <dsahern@kernel.org>
 M:     Shrijeet Mukherjee <shrijeet@gmail.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
-F:     Documentation/networking/vrf.txt
+F:     Documentation/networking/vrf.rst
 F:     drivers/net/vrf.c
 
 VSPRINTF
@@ -18203,7 +18214,7 @@ M:      Maya Erez <merez@codeaurora.org>
 L:     linux-wireless@vger.kernel.org
 L:     wil6210@qti.qualcomm.com
 S:     Supported
-W:     http://wireless.kernel.org/en/users/Drivers/wil6210
+W:     https://wireless.wiki.kernel.org/en/users/Drivers/wil6210
 F:     drivers/net/wireless/ath/wil6210/
 
 WIMAX STACK
@@ -18632,7 +18643,7 @@ L:      linux-hams@vger.kernel.org
 S:     Maintained
 W:     http://yaina.de/jreuter/
 W:     http://www.qsl.net/dl1bke/
-F:     Documentation/networking/z8530drv.txt
+F:     Documentation/networking/z8530drv.rst
 F:     drivers/net/hamradio/*scc.c
 F:     drivers/net/hamradio/z8530.h
 
index 70def49..3512f7b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 VERSION = 5
 PATCHLEVEL = 7
 SUBLEVEL = 0
-EXTRAVERSION = -rc1
+EXTRAVERSION = -rc4
 NAME = Kleptomaniac Octopus
 
 # *DOCUMENTATION*
index 48f13a4..f534a1f 100644 (file)
@@ -3,7 +3,6 @@
  * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
  *
  * Amit Bhor, Sameer Dhavale: Codito Technologies 2004
-
  */
 
 #ifndef _ASM_ARC_MODULE_H
@@ -19,8 +18,4 @@ struct mod_arch_specific {
        const char *secstr;
 };
 
-#define MODULE_PROC_FAMILY "ARC700"
-
-#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY
-
 #endif /* _ASM_ARC_MODULE_H */
diff --git a/arch/arc/include/asm/vermagic.h b/arch/arc/include/asm/vermagic.h
new file mode 100644 (file)
index 0000000..a10257d
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#define MODULE_ARCH_VERMAGIC "ARC700"
+
+#endif /* _ASM_VERMAGIC_H */
index cabdd8f..e8e1c86 100644 (file)
@@ -1450,7 +1450,8 @@ ENTRY(efi_enter_kernel)
                @ running beyond the PoU, and so calling cache_off below from
                @ inside the PE/COFF loader allocated region is unsafe unless
                @ we explicitly clean it to the PoC.
-               adr     r0, call_cache_fn               @ region of code we will
+ ARM(          adrl    r0, call_cache_fn       )
+ THUMB(                adr     r0, call_cache_fn       )       @ region of code we will
                adr     r1, 0f                          @ run with MMU off
                bl      cache_clean_flush
                bl      cache_off
index fd2c766..f7ae5a4 100644 (file)
@@ -14,6 +14,9 @@
        soc {
                firmware: firmware {
                        compatible = "raspberrypi,bcm2835-firmware", "simple-bus";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
                        mboxes = <&mailbox>;
                        dma-ranges;
                };
index e1abe8c..b83a864 100644 (file)
                                             "dsi0_ddr2",
                                             "dsi0_ddr";
 
+                       status = "disabled";
                };
 
                aux: aux@7e215000 {
index 4798288..98da446 100644 (file)
                                compatible = "fsl,imx6q-fec";
                                reg = <0x02188000 0x4000>;
                                interrupt-names = "int0", "pps";
-                               interrupts-extended =
-                                       <&intc 0 118 IRQ_TYPE_LEVEL_HIGH>,
-                                       <&intc 0 119 IRQ_TYPE_LEVEL_HIGH>;
+                               interrupts = <0 118 IRQ_TYPE_LEVEL_HIGH>,
+                                            <0 119 IRQ_TYPE_LEVEL_HIGH>;
                                clocks = <&clks IMX6QDL_CLK_ENET>,
                                         <&clks IMX6QDL_CLK_ENET>,
                                         <&clks IMX6QDL_CLK_ENET_REF>;
                                clock-names = "ipg", "ahb", "ptp";
+                               gpr = <&gpr>;
                                status = "disabled";
                        };
 
index 93b89dc..b310f13 100644 (file)
@@ -77,7 +77,6 @@
 };
 
 &fec {
-       /delete-property/interrupts-extended;
        interrupts = <0 118 IRQ_TYPE_LEVEL_HIGH>,
                     <0 119 IRQ_TYPE_LEVEL_HIGH>;
 };
index a075b63..11d41e8 100644 (file)
        status = "disabled";
 };
 
+/* RNG not directly accessible on N950/N9. */
+&rng_target {
+       status = "disabled";
+};
+
 &usb_otg_hs {
        interface-type = <0>;
        usb-phy = <&usb2_phy>;
index bfa9ce4..b9839f8 100644 (file)
                                          "legacy";
                        status = "disabled";
                };
+
+               mdio: mdio@90000 {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       compatible = "qcom,ipq4019-mdio";
+                       reg = <0x90000 0x64>;
+                       status = "disabled";
+
+                       ethphy0: ethernet-phy@0 {
+                               reg = <0>;
+                       };
+
+                       ethphy1: ethernet-phy@1 {
+                               reg = <1>;
+                       };
+
+                       ethphy2: ethernet-phy@2 {
+                               reg = <2>;
+                       };
+
+                       ethphy3: ethernet-phy@3 {
+                               reg = <3>;
+                       };
+
+                       ethphy4: ethernet-phy@4 {
+                               reg = <4>;
+                       };
+               };
        };
 };
index 6fdb0ac..59da6c0 100644 (file)
@@ -91,9 +91,17 @@ void chacha_crypt_arch(u32 *state, u8 *dst, const u8 *src, unsigned int bytes,
                return;
        }
 
-       kernel_neon_begin();
-       chacha_doneon(state, dst, src, bytes, nrounds);
-       kernel_neon_end();
+       do {
+               unsigned int todo = min_t(unsigned int, bytes, SZ_4K);
+
+               kernel_neon_begin();
+               chacha_doneon(state, dst, src, todo, nrounds);
+               kernel_neon_end();
+
+               bytes -= todo;
+               src += todo;
+               dst += todo;
+       } while (bytes);
 }
 EXPORT_SYMBOL(chacha_crypt_arch);
 
index ae5aefc..ffa8d73 100644 (file)
@@ -30,7 +30,7 @@ static int nhpoly1305_neon_update(struct shash_desc *desc,
                return crypto_nhpoly1305_update(desc, src, srclen);
 
        do {
-               unsigned int n = min_t(unsigned int, srclen, PAGE_SIZE);
+               unsigned int n = min_t(unsigned int, srclen, SZ_4K);
 
                kernel_neon_begin();
                crypto_nhpoly1305_update_helper(desc, src, n, _nh_neon);
index ceec04e..13cfef4 100644 (file)
@@ -160,13 +160,20 @@ void poly1305_update_arch(struct poly1305_desc_ctx *dctx, const u8 *src,
                unsigned int len = round_down(nbytes, POLY1305_BLOCK_SIZE);
 
                if (static_branch_likely(&have_neon) && do_neon) {
-                       kernel_neon_begin();
-                       poly1305_blocks_neon(&dctx->h, src, len, 1);
-                       kernel_neon_end();
+                       do {
+                               unsigned int todo = min_t(unsigned int, len, SZ_4K);
+
+                               kernel_neon_begin();
+                               poly1305_blocks_neon(&dctx->h, src, todo, 1);
+                               kernel_neon_end();
+
+                               len -= todo;
+                               src += todo;
+                       } while (len);
                } else {
                        poly1305_blocks_arm(&dctx->h, src, len, 1);
+                       src += len;
                }
-               src += len;
                nbytes %= POLY1305_BLOCK_SIZE;
        }
 
index 182163b..4b0df09 100644 (file)
@@ -37,30 +37,6 @@ struct mod_arch_specific {
 struct module;
 u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val);
 
-/*
- * Add the ARM architecture version to the version magic string
- */
-#define MODULE_ARCH_VERMAGIC_ARMVSN "ARMv" __stringify(__LINUX_ARM_ARCH__) " "
-
-/* Add __virt_to_phys patching state as well */
-#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
-#define MODULE_ARCH_VERMAGIC_P2V "p2v8 "
-#else
-#define MODULE_ARCH_VERMAGIC_P2V ""
-#endif
-
-/* Add instruction set architecture tag to distinguish ARM/Thumb kernels */
-#ifdef CONFIG_THUMB2_KERNEL
-#define MODULE_ARCH_VERMAGIC_ARMTHUMB "thumb2 "
-#else
-#define MODULE_ARCH_VERMAGIC_ARMTHUMB ""
-#endif
-
-#define MODULE_ARCH_VERMAGIC \
-       MODULE_ARCH_VERMAGIC_ARMVSN \
-       MODULE_ARCH_VERMAGIC_ARMTHUMB \
-       MODULE_ARCH_VERMAGIC_P2V
-
 #ifdef CONFIG_THUMB2_KERNEL
 #define HAVE_ARCH_KALLSYMS_SYMBOL_VALUE
 static inline unsigned long kallsyms_symbol_value(const Elf_Sym *sym)
diff --git a/arch/arm/include/asm/vermagic.h b/arch/arm/include/asm/vermagic.h
new file mode 100644 (file)
index 0000000..62ce94e
--- /dev/null
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#include <linux/stringify.h>
+
+/*
+ * Add the ARM architecture version to the version magic string
+ */
+#define MODULE_ARCH_VERMAGIC_ARMVSN "ARMv" __stringify(__LINUX_ARM_ARCH__) " "
+
+/* Add __virt_to_phys patching state as well */
+#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
+#define MODULE_ARCH_VERMAGIC_P2V "p2v8 "
+#else
+#define MODULE_ARCH_VERMAGIC_P2V ""
+#endif
+
+/* Add instruction set architecture tag to distinguish ARM/Thumb kernels */
+#ifdef CONFIG_THUMB2_KERNEL
+#define MODULE_ARCH_VERMAGIC_ARMTHUMB "thumb2 "
+#else
+#define MODULE_ARCH_VERMAGIC_ARMTHUMB ""
+#endif
+
+#define MODULE_ARCH_VERMAGIC \
+       MODULE_ARCH_VERMAGIC_ARMVSN \
+       MODULE_ARCH_VERMAGIC_ARMTHUMB \
+       MODULE_ARCH_VERMAGIC_P2V
+
+#endif /* _ASM_VERMAGIC_H */
index 03506ce..e7364e6 100644 (file)
@@ -91,8 +91,10 @@ AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
 obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
 obj-$(CONFIG_SOC_IMX53) += suspend-imx53.o
 endif
+ifeq ($(CONFIG_ARM_CPU_SUSPEND),y)
 AFLAGS_resume-imx6.o :=-Wa,-march=armv7-a
 obj-$(CONFIG_SOC_IMX6) += resume-imx6.o
+endif
 obj-$(CONFIG_SOC_IMX6) += pm-imx6.o
 
 obj-$(CONFIG_SOC_IMX1) += mach-imx1.o
index cc29869..bf85d6d 100644 (file)
@@ -929,7 +929,11 @@ static inline void emit_a32_rsh_i64(const s8 dst[],
        rd = arm_bpf_get_reg64(dst, tmp, ctx);
 
        /* Do LSR operation */
-       if (val < 32) {
+       if (val == 0) {
+               /* An immediate value of 0 encodes a shift amount of 32
+                * for LSR. To shift by 0, don't do anything.
+                */
+       } else if (val < 32) {
                emit(ARM_MOV_SI(tmp2[1], rd[1], SRTYPE_LSR, val), ctx);
                emit(ARM_ORR_SI(rd[1], tmp2[1], rd[0], SRTYPE_ASL, 32 - val), ctx);
                emit(ARM_MOV_SI(rd[0], rd[0], SRTYPE_LSR, val), ctx);
@@ -955,7 +959,11 @@ static inline void emit_a32_arsh_i64(const s8 dst[],
        rd = arm_bpf_get_reg64(dst, tmp, ctx);
 
        /* Do ARSH operation */
-       if (val < 32) {
+       if (val == 0) {
+               /* An immediate value of 0 encodes a shift amount of 32
+                * for ASR. To shift by 0, don't do anything.
+                */
+       } else if (val < 32) {
                emit(ARM_MOV_SI(tmp2[1], rd[1], SRTYPE_LSR, val), ctx);
                emit(ARM_ORR_SI(rd[1], tmp2[1], rd[0], SRTYPE_ASL, 32 - val), ctx);
                emit(ARM_MOV_SI(rd[0], rd[0], SRTYPE_ASR, val), ctx);
@@ -992,21 +1000,35 @@ static inline void emit_a32_mul_r64(const s8 dst[], const s8 src[],
        arm_bpf_put_reg32(dst_hi, rd[0], ctx);
 }
 
+static bool is_ldst_imm(s16 off, const u8 size)
+{
+       s16 off_max = 0;
+
+       switch (size) {
+       case BPF_B:
+       case BPF_W:
+               off_max = 0xfff;
+               break;
+       case BPF_H:
+               off_max = 0xff;
+               break;
+       case BPF_DW:
+               /* Need to make sure off+4 does not overflow. */
+               off_max = 0xfff - 4;
+               break;
+       }
+       return -off_max <= off && off <= off_max;
+}
+
 /* *(size *)(dst + off) = src */
 static inline void emit_str_r(const s8 dst, const s8 src[],
-                             s32 off, struct jit_ctx *ctx, const u8 sz){
+                             s16 off, struct jit_ctx *ctx, const u8 sz){
        const s8 *tmp = bpf2a32[TMP_REG_1];
-       s32 off_max;
        s8 rd;
 
        rd = arm_bpf_get_reg32(dst, tmp[1], ctx);
 
-       if (sz == BPF_H)
-               off_max = 0xff;
-       else
-               off_max = 0xfff;
-
-       if (off < 0 || off > off_max) {
+       if (!is_ldst_imm(off, sz)) {
                emit_a32_mov_i(tmp[0], off, ctx);
                emit(ARM_ADD_R(tmp[0], tmp[0], rd), ctx);
                rd = tmp[0];
@@ -1035,18 +1057,12 @@ static inline void emit_str_r(const s8 dst, const s8 src[],
 
 /* dst = *(size*)(src + off) */
 static inline void emit_ldx_r(const s8 dst[], const s8 src,
-                             s32 off, struct jit_ctx *ctx, const u8 sz){
+                             s16 off, struct jit_ctx *ctx, const u8 sz){
        const s8 *tmp = bpf2a32[TMP_REG_1];
        const s8 *rd = is_stacked(dst_lo) ? tmp : dst;
        s8 rm = src;
-       s32 off_max;
-
-       if (sz == BPF_H)
-               off_max = 0xff;
-       else
-               off_max = 0xfff;
 
-       if (off < 0 || off > off_max) {
+       if (!is_ldst_imm(off, sz)) {
                emit_a32_mov_i(tmp[0], off, ctx);
                emit(ARM_ADD_R(tmp[0], tmp[0], src), ctx);
                rm = tmp[0];
index dd6804a..fd4e1ce 100644 (file)
@@ -36,7 +36,7 @@
 
 #include <linux/mm.h>
 
-struct start_info _xen_start_info;
+static struct start_info _xen_start_info;
 struct start_info *xen_start_info = &_xen_start_info;
 EXPORT_SYMBOL(xen_start_info);
 
index 8f926b5..de6bb86 100644 (file)
 
                ipa: ipa@1e40000 {
                        compatible = "qcom,sdm845-ipa";
+
+                       iommus = <&apps_smmu 0x720 0x3>;
                        reg = <0 0x1e40000 0 0x7000>,
                              <0 0x1e47000 0 0x2000>,
                              <0 0x1e04000 0 0x2c000>;
index 11887c7..0d533d5 100644 (file)
                                                <0x5>; /* RX_CHAN */
                        ti,sci-rm-range-rflow = <0x6>; /* GP RFLOW */
                };
+
+               cpts@310d0000 {
+                       compatible = "ti,am65-cpts";
+                       reg = <0x0 0x310d0000 0x0 0x400>;
+                       reg-names = "cpts";
+                       clocks = <&main_cpts_mux>;
+                       clock-names = "cpts";
+                       interrupts-extended = <&intr_main_navss 163 0>;
+                       interrupt-names = "cpts";
+                       ti,cpts-periodic-outputs = <6>;
+                       ti,cpts-ext-ts-inputs = <8>;
+
+                       main_cpts_mux: refclk-mux {
+                               #clock-cells = <0>;
+                               clocks = <&k3_clks 118 5>, <&k3_clks 118 11>,
+                                       <&k3_clks 118 6>, <&k3_clks 118 3>,
+                                       <&k3_clks 118 8>, <&k3_clks 118 14>,
+                                       <&k3_clks 120 3>, <&k3_clks 121 3>;
+                               assigned-clocks = <&main_cpts_mux>;
+                               assigned-clock-parents = <&k3_clks 118 5>;
+                       };
+               };
        };
 
        main_gpio0:  main_gpio0@600000 {
index 353d1e2..0e773e0 100644 (file)
                        clock-names = "fck";
                        bus_freq = <1000000>;
                };
+
+               cpts {
+                       clocks = <&mcu_cpsw_cpts_mux>;
+                       clock-names = "cpts";
+                       interrupts-extended = <&gic500 GIC_SPI 570 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "cpts";
+                       ti,cpts-ext-ts-inputs = <4>;
+                       ti,cpts-periodic-outputs = <2>;
+
+                       mcu_cpsw_cpts_mux: refclk-mux {
+                               #clock-cells = <0>;
+                               clocks = <&k3_clks 118 5>, <&k3_clks 118 11>,
+                                       <&k3_clks 118 6>, <&k3_clks 118 3>,
+                                       <&k3_clks 118 8>, <&k3_clks 118 14>,
+                                       <&k3_clks 120 3>, <&k3_clks 121 3>;
+                               assigned-clocks = <&mcu_cpsw_cpts_mux>;
+                               assigned-clock-parents = <&k3_clks 118 5>;
+                       };
+               };
        };
 };
index 0b9d14b..844a5b5 100644 (file)
                                                <0x0c>; /* RX_UHCHAN */
                        ti,sci-rm-range-rflow = <0x00>; /* GP RFLOW */
                };
+
+               cpts@310d0000 {
+                       compatible = "ti,j721e-cpts";
+                       reg = <0x0 0x310d0000 0x0 0x400>;
+                       reg-names = "cpts";
+                       clocks = <&k3_clks 201 1>;
+                       clock-names = "cpts";
+                       interrupts-extended = <&main_navss_intr 201 0>;
+                       interrupt-names = "cpts";
+                       ti,cpts-periodic-outputs = <6>;
+                       ti,cpts-ext-ts-inputs = <8>;
+               };
        };
 
        main_pmx0: pinmux@11c000 {
index 3d60641..37c355e 100644 (file)
                        clock-names = "fck";
                        bus_freq = <1000000>;
                };
+
+               cpts {
+                       clocks = <&k3_clks 18 2>;
+                       clock-names = "cpts";
+                       interrupts-extended = <&gic500 GIC_SPI 858 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "cpts";
+                       ti,cpts-ext-ts-inputs = <4>;
+                       ti,cpts-periodic-outputs = <2>;
+               };
        };
 };
index 37ca3e8..af2bbca 100644 (file)
@@ -87,9 +87,17 @@ void chacha_crypt_arch(u32 *state, u8 *dst, const u8 *src, unsigned int bytes,
            !crypto_simd_usable())
                return chacha_crypt_generic(state, dst, src, bytes, nrounds);
 
-       kernel_neon_begin();
-       chacha_doneon(state, dst, src, bytes, nrounds);
-       kernel_neon_end();
+       do {
+               unsigned int todo = min_t(unsigned int, bytes, SZ_4K);
+
+               kernel_neon_begin();
+               chacha_doneon(state, dst, src, todo, nrounds);
+               kernel_neon_end();
+
+               bytes -= todo;
+               src += todo;
+               dst += todo;
+       } while (bytes);
 }
 EXPORT_SYMBOL(chacha_crypt_arch);
 
index 895d372..c5405e6 100644 (file)
@@ -30,7 +30,7 @@ static int nhpoly1305_neon_update(struct shash_desc *desc,
                return crypto_nhpoly1305_update(desc, src, srclen);
 
        do {
-               unsigned int n = min_t(unsigned int, srclen, PAGE_SIZE);
+               unsigned int n = min_t(unsigned int, srclen, SZ_4K);
 
                kernel_neon_begin();
                crypto_nhpoly1305_update_helper(desc, src, n, _nh_neon);
index e97b092..f33ada7 100644 (file)
@@ -143,13 +143,20 @@ void poly1305_update_arch(struct poly1305_desc_ctx *dctx, const u8 *src,
                unsigned int len = round_down(nbytes, POLY1305_BLOCK_SIZE);
 
                if (static_branch_likely(&have_neon) && crypto_simd_usable()) {
-                       kernel_neon_begin();
-                       poly1305_blocks_neon(&dctx->h, src, len, 1);
-                       kernel_neon_end();
+                       do {
+                               unsigned int todo = min_t(unsigned int, len, SZ_4K);
+
+                               kernel_neon_begin();
+                               poly1305_blocks_neon(&dctx->h, src, todo, 1);
+                               kernel_neon_end();
+
+                               len -= todo;
+                               src += todo;
+                       } while (len);
                } else {
                        poly1305_blocks(&dctx->h, src, len, 1);
+                       src += len;
                }
-               src += len;
                nbytes %= POLY1305_BLOCK_SIZE;
        }
 
index 1e93de6..4e7fa26 100644 (file)
@@ -7,8 +7,6 @@
 
 #include <asm-generic/module.h>
 
-#define MODULE_ARCH_VERMAGIC   "aarch64"
-
 #ifdef CONFIG_ARM64_MODULE_PLTS
 struct mod_plt_sec {
        int                     plt_shndx;
index 70c4715..c6b4f06 100644 (file)
@@ -47,7 +47,7 @@ static inline void ptrauth_keys_init_user(struct ptrauth_keys_user *keys)
                get_random_bytes(&keys->apga, sizeof(keys->apga));
 }
 
-#define __ptrauth_key_install(k, v)                            \
+#define __ptrauth_key_install_nosync(k, v)                     \
 do {                                                           \
        struct ptrauth_key __pki_v = (v);                       \
        write_sysreg_s(__pki_v.lo, SYS_ ## k ## KEYLO_EL1);     \
@@ -62,8 +62,11 @@ static __always_inline void ptrauth_keys_init_kernel(struct ptrauth_keys_kernel
 
 static __always_inline void ptrauth_keys_switch_kernel(struct ptrauth_keys_kernel *keys)
 {
-       if (system_supports_address_auth())
-               __ptrauth_key_install(APIA, keys->apia);
+       if (!system_supports_address_auth())
+               return;
+
+       __ptrauth_key_install_nosync(APIA, keys->apia);
+       isb();
 }
 
 extern int ptrauth_prctl_reset_keys(struct task_struct *tsk, unsigned long arg);
index ebc6224..c4ac0ac 100644 (file)
@@ -49,7 +49,9 @@
 #ifndef CONFIG_BROKEN_GAS_INST
 
 #ifdef __ASSEMBLY__
-#define __emit_inst(x)                 .inst (x)
+// The space separator is omitted so that __emit_inst(x) can be parsed as
+// either an assembler directive or an assembler macro argument.
+#define __emit_inst(x)                 .inst(x)
 #else
 #define __emit_inst(x)                 ".inst " __stringify((x)) "\n\t"
 #endif
diff --git a/arch/arm64/include/asm/vermagic.h b/arch/arm64/include/asm/vermagic.h
new file mode 100644 (file)
index 0000000..a1eec6a
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ */
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#define MODULE_ARCH_VERMAGIC   "aarch64"
+
+#endif /* _ASM_VERMAGIC_H */
index c19aa81..7364de0 100644 (file)
@@ -203,7 +203,7 @@ static void __init register_insn_emulation(struct insn_emulation_ops *ops)
 }
 
 static int emulation_proc_handler(struct ctl_table *table, int write,
-                                 void __user *buffer, size_t *lenp,
+                                 void *buffer, size_t *lenp,
                                  loff_t *ppos)
 {
        int ret = 0;
index 94289d1..35cb5e6 100644 (file)
@@ -341,8 +341,7 @@ static unsigned int find_supported_vector_length(unsigned int vl)
 #ifdef CONFIG_SYSCTL
 
 static int sve_proc_do_default_vl(struct ctl_table *table, int write,
-                                 void __user *buffer, size_t *lenp,
-                                 loff_t *ppos)
+                                 void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
        int vl = sve_default_vl;
index 354b11e..033a48f 100644 (file)
@@ -260,18 +260,7 @@ static int __aarch32_alloc_vdso_pages(void)
        if (ret)
                return ret;
 
-       ret = aarch32_alloc_kuser_vdso_page();
-       if (ret) {
-               unsigned long c_vvar =
-                       (unsigned long)page_to_virt(aarch32_vdso_pages[C_VVAR]);
-               unsigned long c_vdso =
-                       (unsigned long)page_to_virt(aarch32_vdso_pages[C_VDSO]);
-
-               free_page(c_vvar);
-               free_page(c_vdso);
-       }
-
-       return ret;
+       return aarch32_alloc_kuser_vdso_page();
 }
 #else
 static int __aarch32_alloc_vdso_pages(void)
index dd2514b..3862cad 100644 (file)
@@ -32,7 +32,7 @@ UBSAN_SANITIZE                        := n
 OBJECT_FILES_NON_STANDARD      := y
 KCOV_INSTRUMENT                        := n
 
-CFLAGS_vgettimeofday.o = -O2 -mcmodel=tiny
+CFLAGS_vgettimeofday.o = -O2 -mcmodel=tiny -fasynchronous-unwind-tables
 
 ifneq ($(c-gettimeofday-y),)
   CFLAGS_vgettimeofday.o += -include $(c-gettimeofday-y)
diff --git a/arch/h8300/kernel/.gitignore b/arch/h8300/kernel/.gitignore
new file mode 100644 (file)
index 0000000..bbb90f9
--- /dev/null
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+vmlinux.lds
diff --git a/arch/hexagon/include/asm/module.h b/arch/hexagon/include/asm/module.h
deleted file mode 100644 (file)
index e8de4fe..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
- */
-
-#ifndef _ASM_MODULE_H
-#define _ASM_MODULE_H
-
-#include <asm-generic/module.h>
-
-#define MODULE_ARCH_VERMAGIC __stringify(PROCESSOR_MODEL_NAME) " "
-
-#endif
diff --git a/arch/hexagon/include/asm/vermagic.h b/arch/hexagon/include/asm/vermagic.h
new file mode 100644 (file)
index 0000000..0e8dedc
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#include <linux/stringify.h>
+
+#define MODULE_ARCH_VERMAGIC __stringify(PROCESSOR_MODEL_NAME) " "
+
+#endif /* _ASM_VERMAGIC_H */
index f319144..5a29652 100644 (file)
@@ -26,10 +26,6 @@ struct mod_arch_specific {
        unsigned int next_got_entry;    /* index of next available got entry */
 };
 
-#define MODULE_PROC_FAMILY     "ia64"
-#define MODULE_ARCH_VERMAGIC   MODULE_PROC_FAMILY \
-       "gcc-" __stringify(__GNUC__) "." __stringify(__GNUC_MINOR__)
-
 #define ARCH_SHF_SMALL SHF_IA_64_SHORT
 
 #endif /* _ASM_IA64_MODULE_H */
diff --git a/arch/ia64/include/asm/vermagic.h b/arch/ia64/include/asm/vermagic.h
new file mode 100644 (file)
index 0000000..29c7424
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2003 Hewlett-Packard Co
+ *     David Mosberger-Tang <davidm@hpl.hp.com>
+ */
+
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#include <linux/stringify.h>
+
+#define MODULE_ARCH_VERMAGIC   "ia64" \
+       "gcc-" __stringify(__GNUC__) "." __stringify(__GNUC_MINOR__)
+
+#endif /* _ASM_VERMAGIC_H */
index a0765aa..1bff55a 100644 (file)
@@ -1,7 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 generated-y += syscall_table.h
 generic-y += extable.h
-generic-y += hardirq.h
 generic-y += kvm_para.h
 generic-y += local64.h
 generic-y += mcs_spinlock.h
index 9846047..724a088 100644 (file)
@@ -83,65 +83,4 @@ search_module_dbetables(unsigned long addr)
 }
 #endif
 
-#ifdef CONFIG_CPU_BMIPS
-#define MODULE_PROC_FAMILY "BMIPS "
-#elif defined CONFIG_CPU_MIPS32_R1
-#define MODULE_PROC_FAMILY "MIPS32_R1 "
-#elif defined CONFIG_CPU_MIPS32_R2
-#define MODULE_PROC_FAMILY "MIPS32_R2 "
-#elif defined CONFIG_CPU_MIPS32_R6
-#define MODULE_PROC_FAMILY "MIPS32_R6 "
-#elif defined CONFIG_CPU_MIPS64_R1
-#define MODULE_PROC_FAMILY "MIPS64_R1 "
-#elif defined CONFIG_CPU_MIPS64_R2
-#define MODULE_PROC_FAMILY "MIPS64_R2 "
-#elif defined CONFIG_CPU_MIPS64_R6
-#define MODULE_PROC_FAMILY "MIPS64_R6 "
-#elif defined CONFIG_CPU_R3000
-#define MODULE_PROC_FAMILY "R3000 "
-#elif defined CONFIG_CPU_TX39XX
-#define MODULE_PROC_FAMILY "TX39XX "
-#elif defined CONFIG_CPU_VR41XX
-#define MODULE_PROC_FAMILY "VR41XX "
-#elif defined CONFIG_CPU_R4X00
-#define MODULE_PROC_FAMILY "R4X00 "
-#elif defined CONFIG_CPU_TX49XX
-#define MODULE_PROC_FAMILY "TX49XX "
-#elif defined CONFIG_CPU_R5000
-#define MODULE_PROC_FAMILY "R5000 "
-#elif defined CONFIG_CPU_R5500
-#define MODULE_PROC_FAMILY "R5500 "
-#elif defined CONFIG_CPU_NEVADA
-#define MODULE_PROC_FAMILY "NEVADA "
-#elif defined CONFIG_CPU_R10000
-#define MODULE_PROC_FAMILY "R10000 "
-#elif defined CONFIG_CPU_RM7000
-#define MODULE_PROC_FAMILY "RM7000 "
-#elif defined CONFIG_CPU_SB1
-#define MODULE_PROC_FAMILY "SB1 "
-#elif defined CONFIG_CPU_LOONGSON32
-#define MODULE_PROC_FAMILY "LOONGSON32 "
-#elif defined CONFIG_CPU_LOONGSON2EF
-#define MODULE_PROC_FAMILY "LOONGSON2EF "
-#elif defined CONFIG_CPU_LOONGSON64
-#define MODULE_PROC_FAMILY "LOONGSON64 "
-#elif defined CONFIG_CPU_CAVIUM_OCTEON
-#define MODULE_PROC_FAMILY "OCTEON "
-#elif defined CONFIG_CPU_XLR
-#define MODULE_PROC_FAMILY "XLR "
-#elif defined CONFIG_CPU_XLP
-#define MODULE_PROC_FAMILY "XLP "
-#else
-#error MODULE_PROC_FAMILY undefined for your processor configuration
-#endif
-
-#ifdef CONFIG_32BIT
-#define MODULE_KERNEL_TYPE "32BIT "
-#elif defined CONFIG_64BIT
-#define MODULE_KERNEL_TYPE "64BIT "
-#endif
-
-#define MODULE_ARCH_VERMAGIC \
-       MODULE_PROC_FAMILY MODULE_KERNEL_TYPE
-
 #endif /* _ASM_MODULE_H */
diff --git a/arch/mips/include/asm/vermagic.h b/arch/mips/include/asm/vermagic.h
new file mode 100644 (file)
index 0000000..24dc3d3
--- /dev/null
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#ifdef CONFIG_CPU_BMIPS
+#define MODULE_PROC_FAMILY "BMIPS "
+#elif defined CONFIG_CPU_MIPS32_R1
+#define MODULE_PROC_FAMILY "MIPS32_R1 "
+#elif defined CONFIG_CPU_MIPS32_R2
+#define MODULE_PROC_FAMILY "MIPS32_R2 "
+#elif defined CONFIG_CPU_MIPS32_R6
+#define MODULE_PROC_FAMILY "MIPS32_R6 "
+#elif defined CONFIG_CPU_MIPS64_R1
+#define MODULE_PROC_FAMILY "MIPS64_R1 "
+#elif defined CONFIG_CPU_MIPS64_R2
+#define MODULE_PROC_FAMILY "MIPS64_R2 "
+#elif defined CONFIG_CPU_MIPS64_R6
+#define MODULE_PROC_FAMILY "MIPS64_R6 "
+#elif defined CONFIG_CPU_R3000
+#define MODULE_PROC_FAMILY "R3000 "
+#elif defined CONFIG_CPU_TX39XX
+#define MODULE_PROC_FAMILY "TX39XX "
+#elif defined CONFIG_CPU_VR41XX
+#define MODULE_PROC_FAMILY "VR41XX "
+#elif defined CONFIG_CPU_R4X00
+#define MODULE_PROC_FAMILY "R4X00 "
+#elif defined CONFIG_CPU_TX49XX
+#define MODULE_PROC_FAMILY "TX49XX "
+#elif defined CONFIG_CPU_R5000
+#define MODULE_PROC_FAMILY "R5000 "
+#elif defined CONFIG_CPU_R5500
+#define MODULE_PROC_FAMILY "R5500 "
+#elif defined CONFIG_CPU_NEVADA
+#define MODULE_PROC_FAMILY "NEVADA "
+#elif defined CONFIG_CPU_R10000
+#define MODULE_PROC_FAMILY "R10000 "
+#elif defined CONFIG_CPU_RM7000
+#define MODULE_PROC_FAMILY "RM7000 "
+#elif defined CONFIG_CPU_SB1
+#define MODULE_PROC_FAMILY "SB1 "
+#elif defined CONFIG_CPU_LOONGSON32
+#define MODULE_PROC_FAMILY "LOONGSON32 "
+#elif defined CONFIG_CPU_LOONGSON2EF
+#define MODULE_PROC_FAMILY "LOONGSON2EF "
+#elif defined CONFIG_CPU_LOONGSON64
+#define MODULE_PROC_FAMILY "LOONGSON64 "
+#elif defined CONFIG_CPU_CAVIUM_OCTEON
+#define MODULE_PROC_FAMILY "OCTEON "
+#elif defined CONFIG_CPU_XLR
+#define MODULE_PROC_FAMILY "XLR "
+#elif defined CONFIG_CPU_XLP
+#define MODULE_PROC_FAMILY "XLP "
+#else
+#error MODULE_PROC_FAMILY undefined for your processor configuration
+#endif
+
+#ifdef CONFIG_32BIT
+#define MODULE_KERNEL_TYPE "32BIT "
+#elif defined CONFIG_64BIT
+#define MODULE_KERNEL_TYPE "64BIT "
+#endif
+
+#define MODULE_ARCH_VERMAGIC \
+       MODULE_PROC_FAMILY MODULE_KERNEL_TYPE
+
+#endif /* _ASM_VERMAGIC_H */
index e666fe2..2119541 100644 (file)
@@ -95,16 +95,15 @@ int proc_lasat_ip(struct ctl_table *table, int write,
                len = 0;
                p = buffer;
                while (len < *lenp) {
-                       if (get_user(c, p++))
-                               return -EFAULT;
+                       c = *p;
+                       p++;
                        if (c == 0 || c == '\n')
                                break;
                        len++;
                }
                if (len >= sizeof(ipbuf)-1)
                        len = sizeof(ipbuf) - 1;
-               if (copy_from_user(ipbuf, buffer, len))
-                       return -EFAULT;
+               memcpy(ipbuf, buffer, len);
                ipbuf[len] = 0;
                *ppos += *lenp;
                /* Now see if we can convert it to a valid IP */
@@ -122,11 +121,9 @@ int proc_lasat_ip(struct ctl_table *table, int write,
                if (len > *lenp)
                        len = *lenp;
                if (len)
-                       if (copy_to_user(buffer, ipbuf, len))
-                               return -EFAULT;
+                       memcpy(buffer, ipbuf, len);
                if (len < *lenp) {
-                       if (put_user('\n', ((char *) buffer) + len))
-                               return -EFAULT;
+                       *((char *)buffer + len) = '\n';
                        len++;
                }
                *lenp = len;
diff --git a/arch/nds32/include/asm/module.h b/arch/nds32/include/asm/module.h
deleted file mode 100644 (file)
index a3a08e9..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-// Copyright (C) 2005-2017 Andes Technology Corporation
-
-#ifndef _ASM_NDS32_MODULE_H
-#define _ASM_NDS32_MODULE_H
-
-#include <asm-generic/module.h>
-
-#define MODULE_ARCH_VERMAGIC   "NDS32v3"
-
-#endif /* _ASM_NDS32_MODULE_H */
diff --git a/arch/nds32/include/asm/vermagic.h b/arch/nds32/include/asm/vermagic.h
new file mode 100644 (file)
index 0000000..f772e7b
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (C) 2005-2017 Andes Technology Corporation
+
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#define MODULE_ARCH_VERMAGIC   "NDS32v3"
+
+#endif /* _ASM_VERMAGIC_H */
index 3566587..5398bfc 100644 (file)
@@ -3,28 +3,10 @@
 #define _ASM_POWERPC_MODULE_H
 #ifdef __KERNEL__
 
-/*
- */
-
 #include <linux/list.h>
 #include <asm/bug.h>
 #include <asm-generic/module.h>
 
-
-#ifdef CONFIG_MPROFILE_KERNEL
-#define MODULE_ARCH_VERMAGIC_FTRACE    "mprofile-kernel "
-#else
-#define MODULE_ARCH_VERMAGIC_FTRACE    ""
-#endif
-
-#ifdef CONFIG_RELOCATABLE
-#define MODULE_ARCH_VERMAGIC_RELOCATABLE       "relocatable "
-#else
-#define MODULE_ARCH_VERMAGIC_RELOCATABLE       ""
-#endif
-
-#define MODULE_ARCH_VERMAGIC MODULE_ARCH_VERMAGIC_FTRACE MODULE_ARCH_VERMAGIC_RELOCATABLE
-
 #ifndef __powerpc64__
 /*
  * Thanks to Paul M for explaining this.
diff --git a/arch/powerpc/include/asm/vermagic.h b/arch/powerpc/include/asm/vermagic.h
new file mode 100644 (file)
index 0000000..b054a85
--- /dev/null
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#ifdef CONFIG_MPROFILE_KERNEL
+#define MODULE_ARCH_VERMAGIC_FTRACE    "mprofile-kernel "
+#else
+#define MODULE_ARCH_VERMAGIC_FTRACE    ""
+#endif
+
+#ifdef CONFIG_RELOCATABLE
+#define MODULE_ARCH_VERMAGIC_RELOCATABLE       "relocatable "
+#else
+#define MODULE_ARCH_VERMAGIC_RELOCATABLE       ""
+#endif
+
+#define MODULE_ARCH_VERMAGIC \
+               MODULE_ARCH_VERMAGIC_FTRACE MODULE_ARCH_VERMAGIC_RELOCATABLE
+
+#endif /* _ASM_VERMAGIC_H */
index a6371fb..8420abd 100644 (file)
@@ -732,7 +732,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_SPE)
        stw     r10,_CCR(r1)
        stw     r1,KSP(r3)      /* Set old stack pointer */
 
-       kuap_check r2, r4
+       kuap_check r2, r0
 #ifdef CONFIG_SMP
        /* We need a sync somewhere here to make sure that if the
         * previous task gets rescheduled on another CPU, it sees all
index 438a9be..8105010 100644 (file)
@@ -534,6 +534,8 @@ static bool __init parse_cache_info(struct device_node *np,
        lsizep = of_get_property(np, propnames[3], NULL);
        if (bsizep == NULL)
                bsizep = lsizep;
+       if (lsizep == NULL)
+               lsizep = bsizep;
        if (lsizep != NULL)
                lsize = be32_to_cpu(*lsizep);
        if (bsizep != NULL)
index 6404df6..2b35f9b 100644 (file)
@@ -604,18 +604,19 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
         */
        local_irq_disable();
        ptep = __find_linux_pte(vcpu->arch.pgdir, hva, NULL, &shift);
+       pte = __pte(0);
+       if (ptep)
+               pte = *ptep;
+       local_irq_enable();
        /*
         * If the PTE disappeared temporarily due to a THP
         * collapse, just return and let the guest try again.
         */
-       if (!ptep) {
-               local_irq_enable();
+       if (!pte_present(pte)) {
                if (page)
                        put_page(page);
                return RESUME_GUEST;
        }
-       pte = *ptep;
-       local_irq_enable();
        hpa = pte_pfn(pte) << PAGE_SHIFT;
        pte_size = PAGE_SIZE;
        if (shift)
index 9f05006..aa12cd4 100644 (file)
@@ -815,18 +815,19 @@ int kvmppc_book3s_instantiate_page(struct kvm_vcpu *vcpu,
         */
        local_irq_disable();
        ptep = __find_linux_pte(vcpu->arch.pgdir, hva, NULL, &shift);
+       pte = __pte(0);
+       if (ptep)
+               pte = *ptep;
+       local_irq_enable();
        /*
         * If the PTE disappeared temporarily due to a THP
         * collapse, just return and let the guest try again.
         */
-       if (!ptep) {
-               local_irq_enable();
+       if (!pte_present(pte)) {
                if (page)
                        put_page(page);
                return RESUME_GUEST;
        }
-       pte = *ptep;
-       local_irq_enable();
 
        /* If we're logging dirty pages, always map single pages */
        large_enable = !(memslot->flags & KVM_MEM_LOG_DIRTY_PAGES);
index 3189308..d83a12c 100644 (file)
@@ -185,6 +185,7 @@ void mmu_mark_initmem_nx(void)
                        mmu_mapin_ram_chunk(etext8, einittext8, PAGE_KERNEL);
                }
        }
+       _tlbil_all();
 }
 
 #ifdef CONFIG_STRICT_KERNEL_RWX
@@ -199,6 +200,8 @@ void mmu_mark_rodata_ro(void)
                                      ~(LARGE_PAGE_SIZE_8M - 1)));
        mmu_patch_addis(&patch__dtlbmiss_romem_top, -__pa(_sinittext));
 
+       _tlbil_all();
+
        /* Update page tables for PTDUMP and BDI */
        mmu_mapin_ram_chunk(0, sinittext, __pgprot(0));
        mmu_mapin_ram_chunk(0, etext, PAGE_KERNEL_ROX);
index 0c3c190..27a81c2 100644 (file)
@@ -397,7 +397,7 @@ config PPC_KUAP
 
 config PPC_KUAP_DEBUG
        bool "Extra debugging for Kernel Userspace Access Protection"
-       depends on PPC_KUAP && (PPC_RADIX_MMU || PPC_32)
+       depends on PPC_KUAP && (PPC_RADIX_MMU || PPC32)
        help
          Add extra debugging for Kernel Userspace Access Protection (KUAP)
          If you're unsure, say N.
index a197258..74f82cf 100644 (file)
@@ -55,12 +55,12 @@ config RISCV
        select ARCH_HAS_PTE_SPECIAL
        select ARCH_HAS_MMIOWB
        select ARCH_HAS_DEBUG_VIRTUAL
-       select HAVE_EBPF_JIT
+       select HAVE_EBPF_JIT if MMU
        select EDAC_SUPPORT
        select ARCH_HAS_GIGANTIC_PAGE
        select ARCH_HAS_SET_DIRECT_MAP
        select ARCH_HAS_SET_MEMORY
-       select ARCH_HAS_STRICT_KERNEL_RWX
+       select ARCH_HAS_STRICT_KERNEL_RWX if MMU
        select ARCH_WANT_HUGE_PMD_SHARE if 64BIT
        select SPARSEMEM_STATIC if 32BIT
        select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU
index 46202da..76aa96a 100644 (file)
@@ -6,8 +6,6 @@
 
 #include <asm-generic/module.h>
 
-#define MODULE_ARCH_VERMAGIC    "riscv"
-
 struct module;
 unsigned long module_emit_got_entry(struct module *mod, unsigned long val);
 unsigned long module_emit_plt_entry(struct module *mod, unsigned long val);
diff --git a/arch/riscv/include/asm/vermagic.h b/arch/riscv/include/asm/vermagic.h
new file mode 100644 (file)
index 0000000..7b9441a
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2017 Andes Technology Corporation */
+
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#define MODULE_ARCH_VERMAGIC    "riscv"
+
+#endif /* _ASM_VERMAGIC_H */
index 7c24da5..f383ef5 100644 (file)
@@ -102,7 +102,7 @@ void sbi_shutdown(void)
 {
        sbi_ecall(SBI_EXT_0_1_SHUTDOWN, 0, 0, 0, 0, 0, 0, 0);
 }
-EXPORT_SYMBOL(sbi_set_timer);
+EXPORT_SYMBOL(sbi_shutdown);
 
 /**
  * sbi_clear_ipi() - Clear any pending IPIs for the calling hart.
@@ -113,7 +113,7 @@ void sbi_clear_ipi(void)
 {
        sbi_ecall(SBI_EXT_0_1_CLEAR_IPI, 0, 0, 0, 0, 0, 0, 0);
 }
-EXPORT_SYMBOL(sbi_shutdown);
+EXPORT_SYMBOL(sbi_clear_ipi);
 
 /**
  * sbi_set_timer_v01() - Program the timer for next timer event.
@@ -167,6 +167,11 @@ static int __sbi_rfence_v01(int fid, const unsigned long *hart_mask,
 
        return result;
 }
+
+static void sbi_set_power_off(void)
+{
+       pm_power_off = sbi_shutdown;
+}
 #else
 static void __sbi_set_timer_v01(uint64_t stime_value)
 {
@@ -191,6 +196,8 @@ static int __sbi_rfence_v01(int fid, const unsigned long *hart_mask,
 
        return 0;
 }
+
+static void sbi_set_power_off(void) {}
 #endif /* CONFIG_RISCV_SBI_V01 */
 
 static void __sbi_set_timer_v02(uint64_t stime_value)
@@ -540,16 +547,12 @@ static inline long sbi_get_firmware_version(void)
        return __sbi_base_ecall(SBI_EXT_BASE_GET_IMP_VERSION);
 }
 
-static void sbi_power_off(void)
-{
-       sbi_shutdown();
-}
 
 int __init sbi_init(void)
 {
        int ret;
 
-       pm_power_off = sbi_power_off;
+       sbi_set_power_off();
        ret = sbi_get_spec_version();
        if (ret > 0)
                sbi_spec_version = ret;
index 02087fe..6c85487 100644 (file)
@@ -12,6 +12,8 @@
 #include <linux/stacktrace.h>
 #include <linux/ftrace.h>
 
+register unsigned long sp_in_global __asm__("sp");
+
 #ifdef CONFIG_FRAME_POINTER
 
 struct stackframe {
@@ -19,8 +21,6 @@ struct stackframe {
        unsigned long ra;
 };
 
-register unsigned long sp_in_global __asm__("sp");
-
 void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
                             bool (*fn)(unsigned long, void *), void *arg)
 {
index 33b16f4..a4ee3a0 100644 (file)
@@ -33,15 +33,15 @@ $(obj)/vdso.so.dbg: $(src)/vdso.lds $(obj-vdso) FORCE
        $(call if_changed,vdsold)
 
 # We also create a special relocatable object that should mirror the symbol
-# table and layout of the linked DSO.  With ld -R we can then refer to
-# these symbols in the kernel code rather than hand-coded addresses.
+# table and layout of the linked DSO. With ld --just-symbols we can then
+# refer to these symbols in the kernel code rather than hand-coded addresses.
 
 SYSCFLAGS_vdso.so.dbg = -shared -s -Wl,-soname=linux-vdso.so.1 \
        -Wl,--build-id -Wl,--hash-style=both
 $(obj)/vdso-dummy.o: $(src)/vdso.lds $(obj)/rt_sigreturn.o FORCE
        $(call if_changed,vdsold)
 
-LDFLAGS_vdso-syms.o := -r -R
+LDFLAGS_vdso-syms.o := -r --just-symbols
 $(obj)/vdso-syms.o: $(obj)/vdso-dummy.o FORCE
        $(call if_changed,ld)
 
index 3029341..b198eaa 100644 (file)
 #include <linux/filter.h>
 #include "bpf_jit.h"
 
+/*
+ * Stack layout during BPF program execution:
+ *
+ *                     high
+ *     RV32 fp =>  +----------+
+ *                 | saved ra |
+ *                 | saved fp | RV32 callee-saved registers
+ *                 |   ...    |
+ *                 +----------+ <= (fp - 4 * NR_SAVED_REGISTERS)
+ *                 |  hi(R6)  |
+ *                 |  lo(R6)  |
+ *                 |  hi(R7)  | JIT scratch space for BPF registers
+ *                 |  lo(R7)  |
+ *                 |   ...    |
+ *  BPF_REG_FP =>  +----------+ <= (fp - 4 * NR_SAVED_REGISTERS
+ *                 |          |        - 4 * BPF_JIT_SCRATCH_REGS)
+ *                 |          |
+ *                 |   ...    | BPF program stack
+ *                 |          |
+ *     RV32 sp =>  +----------+
+ *                 |          |
+ *                 |   ...    | Function call stack
+ *                 |          |
+ *                 +----------+
+ *                     low
+ */
+
 enum {
-       /* Stack layout - these are offsets from (top of stack - 4). */
+       /* Stack layout - these are offsets from top of JIT scratch space. */
        BPF_R6_HI,
        BPF_R6_LO,
        BPF_R7_HI,
@@ -29,7 +56,11 @@ enum {
        BPF_JIT_SCRATCH_REGS,
 };
 
-#define STACK_OFFSET(k) (-4 - ((k) * 4))
+/* Number of callee-saved registers stored to stack: ra, fp, s1--s7. */
+#define NR_SAVED_REGISTERS     9
+
+/* Offset from fp for BPF registers stored on stack. */
+#define STACK_OFFSET(k)        (-4 - (4 * NR_SAVED_REGISTERS) - (4 * (k)))
 
 #define TMP_REG_1      (MAX_BPF_JIT_REG + 0)
 #define TMP_REG_2      (MAX_BPF_JIT_REG + 1)
@@ -111,11 +142,9 @@ static void emit_imm64(const s8 *rd, s32 imm_hi, s32 imm_lo,
 
 static void __build_epilogue(bool is_tail_call, struct rv_jit_context *ctx)
 {
-       int stack_adjust = ctx->stack_size, store_offset = stack_adjust - 4;
+       int stack_adjust = ctx->stack_size;
        const s8 *r0 = bpf2rv32[BPF_REG_0];
 
-       store_offset -= 4 * BPF_JIT_SCRATCH_REGS;
-
        /* Set return value if not tail call. */
        if (!is_tail_call) {
                emit(rv_addi(RV_REG_A0, lo(r0), 0), ctx);
@@ -123,15 +152,15 @@ static void __build_epilogue(bool is_tail_call, struct rv_jit_context *ctx)
        }
 
        /* Restore callee-saved registers. */
-       emit(rv_lw(RV_REG_RA, store_offset - 0, RV_REG_SP), ctx);
-       emit(rv_lw(RV_REG_FP, store_offset - 4, RV_REG_SP), ctx);
-       emit(rv_lw(RV_REG_S1, store_offset - 8, RV_REG_SP), ctx);
-       emit(rv_lw(RV_REG_S2, store_offset - 12, RV_REG_SP), ctx);
-       emit(rv_lw(RV_REG_S3, store_offset - 16, RV_REG_SP), ctx);
-       emit(rv_lw(RV_REG_S4, store_offset - 20, RV_REG_SP), ctx);
-       emit(rv_lw(RV_REG_S5, store_offset - 24, RV_REG_SP), ctx);
-       emit(rv_lw(RV_REG_S6, store_offset - 28, RV_REG_SP), ctx);
-       emit(rv_lw(RV_REG_S7, store_offset - 32, RV_REG_SP), ctx);
+       emit(rv_lw(RV_REG_RA, stack_adjust - 4, RV_REG_SP), ctx);
+       emit(rv_lw(RV_REG_FP, stack_adjust - 8, RV_REG_SP), ctx);
+       emit(rv_lw(RV_REG_S1, stack_adjust - 12, RV_REG_SP), ctx);
+       emit(rv_lw(RV_REG_S2, stack_adjust - 16, RV_REG_SP), ctx);
+       emit(rv_lw(RV_REG_S3, stack_adjust - 20, RV_REG_SP), ctx);
+       emit(rv_lw(RV_REG_S4, stack_adjust - 24, RV_REG_SP), ctx);
+       emit(rv_lw(RV_REG_S5, stack_adjust - 28, RV_REG_SP), ctx);
+       emit(rv_lw(RV_REG_S6, stack_adjust - 32, RV_REG_SP), ctx);
+       emit(rv_lw(RV_REG_S7, stack_adjust - 36, RV_REG_SP), ctx);
 
        emit(rv_addi(RV_REG_SP, RV_REG_SP, stack_adjust), ctx);
 
@@ -770,12 +799,13 @@ static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx)
        emit_bcc(BPF_JGE, lo(idx_reg), RV_REG_T1, off, ctx);
 
        /*
-        * if ((temp_tcc = tcc - 1) < 0)
+        * temp_tcc = tcc - 1;
+        * if (tcc < 0)
         *   goto out;
         */
        emit(rv_addi(RV_REG_T1, RV_REG_TCC, -1), ctx);
        off = (tc_ninsn - (ctx->ninsns - start_insn)) << 2;
-       emit_bcc(BPF_JSLT, RV_REG_T1, RV_REG_ZERO, off, ctx);
+       emit_bcc(BPF_JSLT, RV_REG_TCC, RV_REG_ZERO, off, ctx);
 
        /*
         * prog = array->ptrs[index];
@@ -1259,17 +1289,20 @@ notsupported:
 
 void bpf_jit_build_prologue(struct rv_jit_context *ctx)
 {
-       /* Make space to save 9 registers: ra, fp, s1--s7. */
-       int stack_adjust = 9 * sizeof(u32), store_offset, bpf_stack_adjust;
        const s8 *fp = bpf2rv32[BPF_REG_FP];
        const s8 *r1 = bpf2rv32[BPF_REG_1];
-
-       bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16);
+       int stack_adjust = 0;
+       int bpf_stack_adjust =
+               round_up(ctx->prog->aux->stack_depth, STACK_ALIGN);
+
+       /* Make space for callee-saved registers. */
+       stack_adjust += NR_SAVED_REGISTERS * sizeof(u32);
+       /* Make space for BPF registers on stack. */
+       stack_adjust += BPF_JIT_SCRATCH_REGS * sizeof(u32);
+       /* Make space for BPF stack. */
        stack_adjust += bpf_stack_adjust;
-
-       store_offset = stack_adjust - 4;
-
-       stack_adjust += 4 * BPF_JIT_SCRATCH_REGS;
+       /* Round up for stack alignment. */
+       stack_adjust = round_up(stack_adjust, STACK_ALIGN);
 
        /*
         * The first instruction sets the tail-call-counter (TCC) register.
@@ -1280,24 +1313,24 @@ void bpf_jit_build_prologue(struct rv_jit_context *ctx)
        emit(rv_addi(RV_REG_SP, RV_REG_SP, -stack_adjust), ctx);
 
        /* Save callee-save registers. */
-       emit(rv_sw(RV_REG_SP, store_offset - 0, RV_REG_RA), ctx);
-       emit(rv_sw(RV_REG_SP, store_offset - 4, RV_REG_FP), ctx);
-       emit(rv_sw(RV_REG_SP, store_offset - 8, RV_REG_S1), ctx);
-       emit(rv_sw(RV_REG_SP, store_offset - 12, RV_REG_S2), ctx);
-       emit(rv_sw(RV_REG_SP, store_offset - 16, RV_REG_S3), ctx);
-       emit(rv_sw(RV_REG_SP, store_offset - 20, RV_REG_S4), ctx);
-       emit(rv_sw(RV_REG_SP, store_offset - 24, RV_REG_S5), ctx);
-       emit(rv_sw(RV_REG_SP, store_offset - 28, RV_REG_S6), ctx);
-       emit(rv_sw(RV_REG_SP, store_offset - 32, RV_REG_S7), ctx);
+       emit(rv_sw(RV_REG_SP, stack_adjust - 4, RV_REG_RA), ctx);
+       emit(rv_sw(RV_REG_SP, stack_adjust - 8, RV_REG_FP), ctx);
+       emit(rv_sw(RV_REG_SP, stack_adjust - 12, RV_REG_S1), ctx);
+       emit(rv_sw(RV_REG_SP, stack_adjust - 16, RV_REG_S2), ctx);
+       emit(rv_sw(RV_REG_SP, stack_adjust - 20, RV_REG_S3), ctx);
+       emit(rv_sw(RV_REG_SP, stack_adjust - 24, RV_REG_S4), ctx);
+       emit(rv_sw(RV_REG_SP, stack_adjust - 28, RV_REG_S5), ctx);
+       emit(rv_sw(RV_REG_SP, stack_adjust - 32, RV_REG_S6), ctx);
+       emit(rv_sw(RV_REG_SP, stack_adjust - 36, RV_REG_S7), ctx);
 
        /* Set fp: used as the base address for stacked BPF registers. */
        emit(rv_addi(RV_REG_FP, RV_REG_SP, stack_adjust), ctx);
 
-       /* Set up BPF stack pointer. */
+       /* Set up BPF frame pointer. */
        emit(rv_addi(lo(fp), RV_REG_SP, bpf_stack_adjust), ctx);
        emit(rv_addi(hi(fp), RV_REG_ZERO, 0), ctx);
 
-       /* Set up context pointer. */
+       /* Set up BPF context pointer. */
        emit(rv_addi(lo(r1), RV_REG_A0, 0), ctx);
        emit(rv_addi(hi(r1), RV_REG_ZERO, 0), ctx);
 
index cc1985d..d208a9f 100644 (file)
@@ -110,6 +110,16 @@ static bool is_32b_int(s64 val)
        return -(1L << 31) <= val && val < (1L << 31);
 }
 
+static bool in_auipc_jalr_range(s64 val)
+{
+       /*
+        * auipc+jalr can reach any signed PC-relative offset in the range
+        * [-2^31 - 2^11, 2^31 - 2^11).
+        */
+       return (-(1L << 31) - (1L << 11)) <= val &&
+               val < ((1L << 31) - (1L << 11));
+}
+
 static void emit_imm(u8 rd, s64 val, struct rv_jit_context *ctx)
 {
        /* Note that the immediate from the add is sign-extended,
@@ -380,20 +390,24 @@ static void emit_sext_32_rd(u8 *rd, struct rv_jit_context *ctx)
        *rd = RV_REG_T2;
 }
 
-static void emit_jump_and_link(u8 rd, s64 rvoff, bool force_jalr,
-                              struct rv_jit_context *ctx)
+static int emit_jump_and_link(u8 rd, s64 rvoff, bool force_jalr,
+                             struct rv_jit_context *ctx)
 {
        s64 upper, lower;
 
        if (rvoff && is_21b_int(rvoff) && !force_jalr) {
                emit(rv_jal(rd, rvoff >> 1), ctx);
-               return;
+               return 0;
+       } else if (in_auipc_jalr_range(rvoff)) {
+               upper = (rvoff + (1 << 11)) >> 12;
+               lower = rvoff & 0xfff;
+               emit(rv_auipc(RV_REG_T1, upper), ctx);
+               emit(rv_jalr(rd, RV_REG_T1, lower), ctx);
+               return 0;
        }
 
-       upper = (rvoff + (1 << 11)) >> 12;
-       lower = rvoff & 0xfff;
-       emit(rv_auipc(RV_REG_T1, upper), ctx);
-       emit(rv_jalr(rd, RV_REG_T1, lower), ctx);
+       pr_err("bpf-jit: target offset 0x%llx is out of range\n", rvoff);
+       return -ERANGE;
 }
 
 static bool is_signed_bpf_cond(u8 cond)
@@ -407,18 +421,16 @@ static int emit_call(bool fixed, u64 addr, struct rv_jit_context *ctx)
        s64 off = 0;
        u64 ip;
        u8 rd;
+       int ret;
 
        if (addr && ctx->insns) {
                ip = (u64)(long)(ctx->insns + ctx->ninsns);
                off = addr - ip;
-               if (!is_32b_int(off)) {
-                       pr_err("bpf-jit: target call addr %pK is out of range\n",
-                              (void *)addr);
-                       return -ERANGE;
-               }
        }
 
-       emit_jump_and_link(RV_REG_RA, off, !fixed, ctx);
+       ret = emit_jump_and_link(RV_REG_RA, off, !fixed, ctx);
+       if (ret)
+               return ret;
        rd = bpf_to_rv_reg(BPF_REG_0, ctx);
        emit(rv_addi(rd, RV_REG_A0, 0), ctx);
        return 0;
@@ -429,7 +441,7 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
 {
        bool is64 = BPF_CLASS(insn->code) == BPF_ALU64 ||
                    BPF_CLASS(insn->code) == BPF_JMP;
-       int s, e, rvoff, i = insn - ctx->prog->insnsi;
+       int s, e, rvoff, ret, i = insn - ctx->prog->insnsi;
        struct bpf_prog_aux *aux = ctx->prog->aux;
        u8 rd = -1, rs = -1, code = insn->code;
        s16 off = insn->off;
@@ -699,7 +711,9 @@ out_be:
        /* JUMP off */
        case BPF_JMP | BPF_JA:
                rvoff = rv_offset(i, off, ctx);
-               emit_jump_and_link(RV_REG_ZERO, rvoff, false, ctx);
+               ret = emit_jump_and_link(RV_REG_ZERO, rvoff, false, ctx);
+               if (ret)
+                       return ret;
                break;
 
        /* IF (dst COND src) JUMP off */
@@ -801,7 +815,6 @@ out_be:
        case BPF_JMP | BPF_CALL:
        {
                bool fixed;
-               int ret;
                u64 addr;
 
                mark_call(ctx);
@@ -826,7 +839,9 @@ out_be:
                        break;
 
                rvoff = epilogue_offset(ctx);
-               emit_jump_and_link(RV_REG_ZERO, rvoff, false, ctx);
+               ret = emit_jump_and_link(RV_REG_ZERO, rvoff, false, ctx);
+               if (ret)
+                       return ret;
                break;
 
        /* dst = imm64 */
index aa738ca..d74a4c7 100644 (file)
@@ -51,10 +51,9 @@ static struct platform_device *appldata_pdev;
  */
 static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
 static int appldata_timer_handler(struct ctl_table *ctl, int write,
-                                 void __user *buffer, size_t *lenp, loff_t *ppos);
+                                 void *buffer, size_t *lenp, loff_t *ppos);
 static int appldata_interval_handler(struct ctl_table *ctl, int write,
-                                        void __user *buffer,
-                                        size_t *lenp, loff_t *ppos);
+                                    void *buffer, size_t *lenp, loff_t *ppos);
 
 static struct ctl_table_header *appldata_sysctl_header;
 static struct ctl_table appldata_table[] = {
@@ -217,7 +216,7 @@ static void __appldata_vtimer_setup(int cmd)
  */
 static int
 appldata_timer_handler(struct ctl_table *ctl, int write,
-                          void __user *buffer, size_t *lenp, loff_t *ppos)
+                          void *buffer, size_t *lenp, loff_t *ppos)
 {
        int timer_active = appldata_timer_active;
        int rc;
@@ -250,7 +249,7 @@ appldata_timer_handler(struct ctl_table *ctl, int write,
  */
 static int
 appldata_interval_handler(struct ctl_table *ctl, int write,
-                          void __user *buffer, size_t *lenp, loff_t *ppos)
+                          void *buffer, size_t *lenp, loff_t *ppos)
 {
        int interval = appldata_interval;
        int rc;
@@ -280,7 +279,7 @@ appldata_interval_handler(struct ctl_table *ctl, int write,
  */
 static int
 appldata_generic_handler(struct ctl_table *ctl, int write,
-                          void __user *buffer, size_t *lenp, loff_t *ppos)
+                          void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct appldata_ops *ops = NULL, *tmp_ops;
        struct list_head *lh;
index 8fde561..f887a47 100644 (file)
@@ -7,9 +7,7 @@
 #ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
 int __bootdata_preserved(prot_virt_guest);
 #endif
-#if IS_ENABLED(CONFIG_KVM)
 struct uv_info __bootdata_preserved(uv_info);
-#endif
 
 void uv_query_info(void)
 {
index 6d321f5..6364460 100644 (file)
@@ -867,7 +867,7 @@ static int debug_active = 1;
  * if debug_active is already off
  */
 static int s390dbf_procactive(struct ctl_table *table, int write,
-                             void __user *buffer, size_t *lenp, loff_t *ppos)
+                             void *buffer, size_t *lenp, loff_t *ppos)
 {
        if (!write || debug_stoppable || !debug_active)
                return proc_dointvec(table, write, buffer, lenp, ppos);
index 61f2b04..ccba63a 100644 (file)
@@ -133,7 +133,7 @@ void diag_stat_inc(enum diag_stat_enum nr)
 }
 EXPORT_SYMBOL(diag_stat_inc);
 
-void diag_stat_inc_norecursion(enum diag_stat_enum nr)
+void notrace diag_stat_inc_norecursion(enum diag_stat_enum nr)
 {
        this_cpu_inc(diag_stat.counter[nr]);
        trace_s390_diagnose_norecursion(diag_map[nr].code);
index 7eaabba..10dbb12 100644 (file)
@@ -403,7 +403,7 @@ int smp_find_processor_id(u16 address)
        return -1;
 }
 
-bool arch_vcpu_is_preempted(int cpu)
+bool notrace arch_vcpu_is_preempted(int cpu)
 {
        if (test_cpu_flag_of(CIF_ENABLED_WAIT, cpu))
                return false;
@@ -413,7 +413,7 @@ bool arch_vcpu_is_preempted(int cpu)
 }
 EXPORT_SYMBOL(arch_vcpu_is_preempted);
 
-void smp_yield_cpu(int cpu)
+void notrace smp_yield_cpu(int cpu)
 {
        if (!MACHINE_HAS_DIAG9C)
                return;
index 5f70cef..332b542 100644 (file)
@@ -594,7 +594,7 @@ static int __init topology_setup(char *str)
 early_param("topology", topology_setup);
 
 static int topology_ctl_handler(struct ctl_table *ctl, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int enabled = topology_is_enabled();
        int new_mode;
index 490b52e..11a669f 100644 (file)
@@ -14,7 +14,7 @@ EXPORT_TRACEPOINT_SYMBOL(s390_diagnose);
 
 static DEFINE_PER_CPU(unsigned int, diagnose_trace_depth);
 
-void trace_s390_diagnose_norecursion(int diag_nr)
+void notrace trace_s390_diagnose_norecursion(int diag_nr)
 {
        unsigned long flags;
        unsigned int *depth;
index c86d654..4c0677f 100644 (file)
 int __bootdata_preserved(prot_virt_guest);
 #endif
 
+struct uv_info __bootdata_preserved(uv_info);
+
 #if IS_ENABLED(CONFIG_KVM)
 int prot_virt_host;
 EXPORT_SYMBOL(prot_virt_host);
-struct uv_info __bootdata_preserved(uv_info);
 EXPORT_SYMBOL(uv_info);
 
 static int __init prot_virt_setup(char *val)
index 8191106..bfb4811 100644 (file)
@@ -393,7 +393,7 @@ static unsigned long deliverable_irqs(struct kvm_vcpu *vcpu)
        if (psw_mchk_disabled(vcpu))
                active_mask &= ~IRQ_PEND_MCHK_MASK;
        /* PV guest cpus can have a single interruption injected at a time. */
-       if (kvm_s390_pv_cpu_is_protected(vcpu) &&
+       if (kvm_s390_pv_cpu_get_handle(vcpu) &&
            vcpu->arch.sie_block->iictl != IICTL_CODE_NONE)
                active_mask &= ~(IRQ_PEND_EXT_II_MASK |
                                 IRQ_PEND_IO_MASK |
index 19a8102..5dcf9ff 100644 (file)
@@ -1939,6 +1939,9 @@ static int gfn_to_memslot_approx(struct kvm_memslots *slots, gfn_t gfn)
                        start = slot + 1;
        }
 
+       if (start >= slots->used_slots)
+               return slots->used_slots - 1;
+
        if (gfn >= memslots[start].base_gfn &&
            gfn < memslots[start].base_gfn + memslots[start].npages) {
                atomic_set(&slots->lru_slot, start);
index c4f8039..0267405 100644 (file)
@@ -64,10 +64,13 @@ mm_segment_t enable_sacf_uaccess(void)
 {
        mm_segment_t old_fs;
        unsigned long asce, cr;
+       unsigned long flags;
 
        old_fs = current->thread.mm_segment;
        if (old_fs & 1)
                return old_fs;
+       /* protect against a concurrent page table upgrade */
+       local_irq_save(flags);
        current->thread.mm_segment |= 1;
        asce = S390_lowcore.kernel_asce;
        if (likely(old_fs == USER_DS)) {
@@ -83,6 +86,7 @@ mm_segment_t enable_sacf_uaccess(void)
                __ctl_load(asce, 7, 7);
                set_cpu_flag(CIF_ASCE_SECONDARY);
        }
+       local_irq_restore(flags);
        return old_fs;
 }
 EXPORT_SYMBOL(enable_sacf_uaccess);
index ae989b7..36bce72 100644 (file)
@@ -245,7 +245,7 @@ static int cmm_skip_blanks(char *cp, char **endp)
 }
 
 static int cmm_pages_handler(struct ctl_table *ctl, int write,
-                            void __user *buffer, size_t *lenp, loff_t *ppos)
+                            void *buffer, size_t *lenp, loff_t *ppos)
 {
        long nr = cmm_get_pages();
        struct ctl_table ctl_entry = {
@@ -264,7 +264,7 @@ static int cmm_pages_handler(struct ctl_table *ctl, int write,
 }
 
 static int cmm_timed_pages_handler(struct ctl_table *ctl, int write,
-                                  void __user *buffer, size_t *lenp,
+                                  void *buffer, size_t *lenp,
                                   loff_t *ppos)
 {
        long nr = cmm_get_timed_pages();
@@ -284,7 +284,7 @@ static int cmm_timed_pages_handler(struct ctl_table *ctl, int write,
 }
 
 static int cmm_timeout_handler(struct ctl_table *ctl, int write,
-                              void __user *buffer, size_t *lenp, loff_t *ppos)
+                              void *buffer, size_t *lenp, loff_t *ppos)
 {
        char buf[64], *p;
        long nr, seconds;
@@ -297,8 +297,7 @@ static int cmm_timeout_handler(struct ctl_table *ctl, int write,
 
        if (write) {
                len = min(*lenp, sizeof(buf));
-               if (copy_from_user(buf, buffer, len))
-                       return -EFAULT;
+               memcpy(buf, buffer, len);
                buf[len - 1] = '\0';
                cmm_skip_blanks(buf, &p);
                nr = simple_strtoul(p, &p, 0);
@@ -311,8 +310,7 @@ static int cmm_timeout_handler(struct ctl_table *ctl, int write,
                              cmm_timeout_pages, cmm_timeout_seconds);
                if (len > *lenp)
                        len = *lenp;
-               if (copy_to_user(buffer, buf, len))
-                       return -EFAULT;
+               memcpy(buffer, buf, len);
                *lenp = len;
                *ppos += len;
        }
index 498c98a..fff169d 100644 (file)
@@ -70,8 +70,20 @@ static void __crst_table_upgrade(void *arg)
 {
        struct mm_struct *mm = arg;
 
-       if (current->active_mm == mm)
-               set_user_asce(mm);
+       /* we must change all active ASCEs to avoid the creation of new TLBs */
+       if (current->active_mm == mm) {
+               S390_lowcore.user_asce = mm->context.asce;
+               if (current->thread.mm_segment == USER_DS) {
+                       __ctl_load(S390_lowcore.user_asce, 1, 1);
+                       /* Mark user-ASCE present in CR1 */
+                       clear_cpu_flag(CIF_ASCE_PRIMARY);
+               }
+               if (current->thread.mm_segment == USER_DS_SACF) {
+                       __ctl_load(S390_lowcore.user_asce, 7, 7);
+                       /* enable_sacf_uaccess does all or nothing */
+                       WARN_ON(!test_cpu_flag(CIF_ASCE_SECONDARY));
+               }
+       }
        __tlb_flush_local();
 }
 
index fbe97ab..743f257 100644 (file)
@@ -115,7 +115,6 @@ static struct irq_chip zpci_irq_chip = {
        .name = "PCI-MSI",
        .irq_unmask = pci_msi_unmask_irq,
        .irq_mask = pci_msi_mask_irq,
-       .irq_set_affinity = zpci_set_irq_affinity,
 };
 
 static void zpci_handle_cpu_local_irq(bool rescan)
@@ -276,7 +275,9 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
                rc = -EIO;
                if (hwirq - bit >= msi_vecs)
                        break;
-               irq = __irq_alloc_descs(-1, 0, 1, 0, THIS_MODULE, msi->affinity);
+               irq = __irq_alloc_descs(-1, 0, 1, 0, THIS_MODULE,
+                               (irq_delivery == DIRECTED) ?
+                               msi->affinity : NULL);
                if (irq < 0)
                        return -ENOMEM;
                rc = irq_set_msi_desc(irq, msi);
index 9f38fb3..337663a 100644 (file)
@@ -11,32 +11,4 @@ struct mod_arch_specific {
 };
 #endif
 
-#ifdef CONFIG_CPU_LITTLE_ENDIAN
-# ifdef CONFIG_CPU_SH2
-#  define MODULE_PROC_FAMILY "SH2LE "
-# elif defined  CONFIG_CPU_SH3
-#  define MODULE_PROC_FAMILY "SH3LE "
-# elif defined  CONFIG_CPU_SH4
-#  define MODULE_PROC_FAMILY "SH4LE "
-# elif defined  CONFIG_CPU_SH5
-#  define MODULE_PROC_FAMILY "SH5LE "
-# else
-#  error unknown processor family
-# endif
-#else
-# ifdef CONFIG_CPU_SH2
-#  define MODULE_PROC_FAMILY "SH2BE "
-# elif defined  CONFIG_CPU_SH3
-#  define MODULE_PROC_FAMILY "SH3BE "
-# elif defined  CONFIG_CPU_SH4
-#  define MODULE_PROC_FAMILY "SH4BE "
-# elif defined  CONFIG_CPU_SH5
-#  define MODULE_PROC_FAMILY "SH5BE "
-# else
-#  error unknown processor family
-# endif
-#endif
-
-#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY
-
 #endif /* _ASM_SH_MODULE_H */
diff --git a/arch/sh/include/asm/vermagic.h b/arch/sh/include/asm/vermagic.h
new file mode 100644 (file)
index 0000000..13d8eaa
--- /dev/null
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+# ifdef CONFIG_CPU_SH2
+#  define MODULE_PROC_FAMILY "SH2LE "
+# elif defined  CONFIG_CPU_SH3
+#  define MODULE_PROC_FAMILY "SH3LE "
+# elif defined  CONFIG_CPU_SH4
+#  define MODULE_PROC_FAMILY "SH4LE "
+# elif defined  CONFIG_CPU_SH5
+#  define MODULE_PROC_FAMILY "SH5LE "
+# else
+#  error unknown processor family
+# endif
+#else
+# ifdef CONFIG_CPU_SH2
+#  define MODULE_PROC_FAMILY "SH2BE "
+# elif defined  CONFIG_CPU_SH3
+#  define MODULE_PROC_FAMILY "SH3BE "
+# elif defined  CONFIG_CPU_SH4
+#  define MODULE_PROC_FAMILY "SH4BE "
+# elif defined  CONFIG_CPU_SH5
+#  define MODULE_PROC_FAMILY "SH5BE "
+# else
+#  error unknown processor family
+# endif
+#endif
+
+#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY
+
+#endif /* _ASM_VERMAGIC_H */
index b9de2d4..8d2a68a 100644 (file)
@@ -412,7 +412,7 @@ int arch_add_memory(int nid, u64 start, u64 size,
        unsigned long nr_pages = size >> PAGE_SHIFT;
        int ret;
 
-       if (WARN_ON_ONCE(params->pgprot.pgprot != PAGE_KERNEL.pgprot)
+       if (WARN_ON_ONCE(params->pgprot.pgprot != PAGE_KERNEL.pgprot))
                return -EINVAL;
 
        /* We only have ZONE_NORMAL, so this is easy.. */
index d2daa20..275f5ff 100644 (file)
@@ -140,6 +140,7 @@ export CFLAGS_vmlinux := $(LINK-y) $(LINK_WRAPS) $(LD_FLAGS_CMDLINE)
 # When cleaning we don't include .config, so we don't include
 # TT or skas makefiles and don't clean skas_ptregs.h.
 CLEAN_FILES += linux x.i gmon.out
+MRPROPER_DIRS += arch/$(SUBARCH)/include/generated
 
 archclean:
        @find . \( -name '*.bb' -o -name '*.bbg' -o -name '*.da' \
index 1d6104e..1197b55 100644 (file)
@@ -149,7 +149,7 @@ config X86
        select HAVE_ARCH_TRACEHOOK
        select HAVE_ARCH_TRANSPARENT_HUGEPAGE
        select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64
-       select HAVE_ARCH_USERFAULTFD_WP         if USERFAULTFD
+       select HAVE_ARCH_USERFAULTFD_WP         if X86_64 && USERFAULTFD
        select HAVE_ARCH_VMAP_STACK             if X86_64
        select HAVE_ARCH_WITHIN_STACK_FRAMES
        select HAVE_ASM_MODVERSIONS
index 06ef2d4..6737bce 100644 (file)
@@ -32,16 +32,16 @@ void blake2s_compress_arch(struct blake2s_state *state,
                           const u32 inc)
 {
        /* SIMD disables preemption, so relax after processing each page. */
-       BUILD_BUG_ON(PAGE_SIZE / BLAKE2S_BLOCK_SIZE < 8);
+       BUILD_BUG_ON(SZ_4K / BLAKE2S_BLOCK_SIZE < 8);
 
        if (!static_branch_likely(&blake2s_use_ssse3) || !crypto_simd_usable()) {
                blake2s_compress_generic(state, block, nblocks, inc);
                return;
        }
 
-       for (;;) {
+       do {
                const size_t blocks = min_t(size_t, nblocks,
-                                           PAGE_SIZE / BLAKE2S_BLOCK_SIZE);
+                                           SZ_4K / BLAKE2S_BLOCK_SIZE);
 
                kernel_fpu_begin();
                if (IS_ENABLED(CONFIG_AS_AVX512) &&
@@ -52,10 +52,8 @@ void blake2s_compress_arch(struct blake2s_state *state,
                kernel_fpu_end();
 
                nblocks -= blocks;
-               if (!nblocks)
-                       break;
                block += blocks * BLAKE2S_BLOCK_SIZE;
-       }
+       } while (nblocks);
 }
 EXPORT_SYMBOL(blake2s_compress_arch);
 
index b412c21..2225009 100644 (file)
@@ -153,9 +153,17 @@ void chacha_crypt_arch(u32 *state, u8 *dst, const u8 *src, unsigned int bytes,
            bytes <= CHACHA_BLOCK_SIZE)
                return chacha_crypt_generic(state, dst, src, bytes, nrounds);
 
-       kernel_fpu_begin();
-       chacha_dosimd(state, dst, src, bytes, nrounds);
-       kernel_fpu_end();
+       do {
+               unsigned int todo = min_t(unsigned int, bytes, SZ_4K);
+
+               kernel_fpu_begin();
+               chacha_dosimd(state, dst, src, todo, nrounds);
+               kernel_fpu_end();
+
+               bytes -= todo;
+               src += todo;
+               dst += todo;
+       } while (bytes);
 }
 EXPORT_SYMBOL(chacha_crypt_arch);
 
index f7567cb..80fcb85 100644 (file)
@@ -29,7 +29,7 @@ static int nhpoly1305_avx2_update(struct shash_desc *desc,
                return crypto_nhpoly1305_update(desc, src, srclen);
 
        do {
-               unsigned int n = min_t(unsigned int, srclen, PAGE_SIZE);
+               unsigned int n = min_t(unsigned int, srclen, SZ_4K);
 
                kernel_fpu_begin();
                crypto_nhpoly1305_update_helper(desc, src, n, _nh_avx2);
index a661ede..cc6b7c1 100644 (file)
@@ -29,7 +29,7 @@ static int nhpoly1305_sse2_update(struct shash_desc *desc,
                return crypto_nhpoly1305_update(desc, src, srclen);
 
        do {
-               unsigned int n = min_t(unsigned int, srclen, PAGE_SIZE);
+               unsigned int n = min_t(unsigned int, srclen, SZ_4K);
 
                kernel_fpu_begin();
                crypto_nhpoly1305_update_helper(desc, src, n, _nh_sse2);
index 6dfec19..dfe921e 100644 (file)
@@ -91,8 +91,8 @@ static void poly1305_simd_blocks(void *ctx, const u8 *inp, size_t len,
        struct poly1305_arch_internal *state = ctx;
 
        /* SIMD disables preemption, so relax after processing each page. */
-       BUILD_BUG_ON(PAGE_SIZE < POLY1305_BLOCK_SIZE ||
-                    PAGE_SIZE % POLY1305_BLOCK_SIZE);
+       BUILD_BUG_ON(SZ_4K < POLY1305_BLOCK_SIZE ||
+                    SZ_4K % POLY1305_BLOCK_SIZE);
 
        if (!static_branch_likely(&poly1305_use_avx) ||
            (len < (POLY1305_BLOCK_SIZE * 18) && !state->is_base2_26) ||
@@ -102,8 +102,8 @@ static void poly1305_simd_blocks(void *ctx, const u8 *inp, size_t len,
                return;
        }
 
-       for (;;) {
-               const size_t bytes = min_t(size_t, len, PAGE_SIZE);
+       do {
+               const size_t bytes = min_t(size_t, len, SZ_4K);
 
                kernel_fpu_begin();
                if (IS_ENABLED(CONFIG_AS_AVX512) && static_branch_likely(&poly1305_use_avx512))
@@ -113,11 +113,10 @@ static void poly1305_simd_blocks(void *ctx, const u8 *inp, size_t len,
                else
                        poly1305_blocks_avx(ctx, inp, bytes, padbit);
                kernel_fpu_end();
+
                len -= bytes;
-               if (!len)
-                       break;
                inp += bytes;
-       }
+       } while (len);
 }
 
 static void poly1305_simd_emit(void *ctx, u8 mac[POLY1305_DIGEST_SIZE],
index e4aa20c..442e1ed 100644 (file)
@@ -643,6 +643,7 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = {
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_PLUS,  &glm_cstates),
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_D,      &glm_cstates),
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT,        &glm_cstates),
+       X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L,      &glm_cstates),
 
        X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L,           &icl_cstates),
        X86_MATCH_INTEL_FAM6_MODEL(ICELAKE,             &icl_cstates),
index b0da532..fd51bac 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/mm.h>
 #include <linux/hyperv.h>
 #include <linux/slab.h>
+#include <linux/kernel.h>
 #include <linux/cpuhotplug.h>
 #include <linux/syscore_ops.h>
 #include <clocksource/hyperv_timer.h>
@@ -72,7 +73,8 @@ static int hv_cpu_init(unsigned int cpu)
        struct page *pg;
 
        input_arg = (void **)this_cpu_ptr(hyperv_pcpu_input_arg);
-       pg = alloc_page(GFP_KERNEL);
+       /* hv_cpu_init() can be called with IRQs disabled from hv_resume() */
+       pg = alloc_page(irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL);
        if (unlikely(!pg))
                return -ENOMEM;
        *input_arg = page_address(pg);
@@ -253,6 +255,7 @@ static int __init hv_pci_init(void)
 static int hv_suspend(void)
 {
        union hv_x64_msr_hypercall_contents hypercall_msr;
+       int ret;
 
        /*
         * Reset the hypercall page as it is going to be invalidated
@@ -269,12 +272,17 @@ static int hv_suspend(void)
        hypercall_msr.enable = 0;
        wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
 
-       return 0;
+       ret = hv_cpu_die(0);
+       return ret;
 }
 
 static void hv_resume(void)
 {
        union hv_x64_msr_hypercall_contents hypercall_msr;
+       int ret;
+
+       ret = hv_cpu_init(0);
+       WARN_ON(ret);
 
        /* Re-enable the hypercall page */
        rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
@@ -287,6 +295,7 @@ static void hv_resume(void)
        hv_hypercall_pg_saved = NULL;
 }
 
+/* Note: when the ops are called, only CPU0 is online and IRQs are disabled. */
 static struct syscore_ops hv_syscore_ops = {
        .suspend        = hv_suspend,
        .resume         = hv_resume,
@@ -419,11 +428,14 @@ void hyperv_cleanup(void)
 }
 EXPORT_SYMBOL_GPL(hyperv_cleanup);
 
-void hyperv_report_panic(struct pt_regs *regs, long err)
+void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die)
 {
        static bool panic_reported;
        u64 guest_id;
 
+       if (in_die && !panic_on_oops)
+               return;
+
        /*
         * We prefer to report panic on 'die' chain as we have proper
         * registers to report, but if we miss it (e.g. on BUG()) we need
index cdcf48d..8391c11 100644 (file)
@@ -178,8 +178,10 @@ extern void efi_free_boot_services(void);
 extern pgd_t * __init efi_uv1_memmap_phys_prolog(void);
 extern void __init efi_uv1_memmap_phys_epilog(pgd_t *save_pgd);
 
+/* kexec external ABI */
 struct efi_setup_data {
        u64 fw_vendor;
+       u64 __unused;
        u64 tables;
        u64 smbios;
        u64 reserved[8];
index 6685e12..7063b5a 100644 (file)
@@ -41,7 +41,7 @@ struct microcode_amd {
        unsigned int                    mpb[0];
 };
 
-#define PATCH_MAX_SIZE PAGE_SIZE
+#define PATCH_MAX_SIZE (3 * PAGE_SIZE)
 
 #ifdef CONFIG_MICROCODE_AMD
 extern void __init load_ucode_amd_bsp(unsigned int family);
index c215d27..e988bac 100644 (file)
@@ -13,64 +13,4 @@ struct mod_arch_specific {
 #endif
 };
 
-#ifdef CONFIG_X86_64
-/* X86_64 does not define MODULE_PROC_FAMILY */
-#elif defined CONFIG_M486SX
-#define MODULE_PROC_FAMILY "486SX "
-#elif defined CONFIG_M486
-#define MODULE_PROC_FAMILY "486 "
-#elif defined CONFIG_M586
-#define MODULE_PROC_FAMILY "586 "
-#elif defined CONFIG_M586TSC
-#define MODULE_PROC_FAMILY "586TSC "
-#elif defined CONFIG_M586MMX
-#define MODULE_PROC_FAMILY "586MMX "
-#elif defined CONFIG_MCORE2
-#define MODULE_PROC_FAMILY "CORE2 "
-#elif defined CONFIG_MATOM
-#define MODULE_PROC_FAMILY "ATOM "
-#elif defined CONFIG_M686
-#define MODULE_PROC_FAMILY "686 "
-#elif defined CONFIG_MPENTIUMII
-#define MODULE_PROC_FAMILY "PENTIUMII "
-#elif defined CONFIG_MPENTIUMIII
-#define MODULE_PROC_FAMILY "PENTIUMIII "
-#elif defined CONFIG_MPENTIUMM
-#define MODULE_PROC_FAMILY "PENTIUMM "
-#elif defined CONFIG_MPENTIUM4
-#define MODULE_PROC_FAMILY "PENTIUM4 "
-#elif defined CONFIG_MK6
-#define MODULE_PROC_FAMILY "K6 "
-#elif defined CONFIG_MK7
-#define MODULE_PROC_FAMILY "K7 "
-#elif defined CONFIG_MK8
-#define MODULE_PROC_FAMILY "K8 "
-#elif defined CONFIG_MELAN
-#define MODULE_PROC_FAMILY "ELAN "
-#elif defined CONFIG_MCRUSOE
-#define MODULE_PROC_FAMILY "CRUSOE "
-#elif defined CONFIG_MEFFICEON
-#define MODULE_PROC_FAMILY "EFFICEON "
-#elif defined CONFIG_MWINCHIPC6
-#define MODULE_PROC_FAMILY "WINCHIPC6 "
-#elif defined CONFIG_MWINCHIP3D
-#define MODULE_PROC_FAMILY "WINCHIP3D "
-#elif defined CONFIG_MCYRIXIII
-#define MODULE_PROC_FAMILY "CYRIXIII "
-#elif defined CONFIG_MVIAC3_2
-#define MODULE_PROC_FAMILY "VIAC3-2 "
-#elif defined CONFIG_MVIAC7
-#define MODULE_PROC_FAMILY "VIAC7 "
-#elif defined CONFIG_MGEODEGX1
-#define MODULE_PROC_FAMILY "GEODEGX1 "
-#elif defined CONFIG_MGEODE_LX
-#define MODULE_PROC_FAMILY "GEODE "
-#else
-#error unknown processor family
-#endif
-
-#ifdef CONFIG_X86_32
-# define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY
-#endif
-
 #endif /* _ASM_X86_MODULE_H */
index 1c42ecb..d30805e 100644 (file)
@@ -35,6 +35,8 @@ typedef int (*hyperv_fill_flush_list_func)(
        rdmsrl(HV_X64_MSR_SINT0 + int_num, val)
 #define hv_set_synint_state(int_num, val) \
        wrmsrl(HV_X64_MSR_SINT0 + int_num, val)
+#define hv_recommend_using_aeoi() \
+       (!(ms_hyperv.hints & HV_DEPRECATING_AEOI_RECOMMENDED))
 
 #define hv_get_crash_ctl(val) \
        rdmsrl(HV_X64_MSR_CRASH_CTL, val)
index 07e95dc..7e9a281 100644 (file)
@@ -237,27 +237,6 @@ enum ssb_mitigation {
 extern char __indirect_thunk_start[];
 extern char __indirect_thunk_end[];
 
-/*
- * On VMEXIT we must ensure that no RSB predictions learned in the guest
- * can be followed in the host, by overwriting the RSB completely. Both
- * retpoline and IBRS mitigations for Spectre v2 need this; only on future
- * CPUs with IBRS_ALL *might* it be avoided.
- */
-static inline void vmexit_fill_RSB(void)
-{
-#ifdef CONFIG_RETPOLINE
-       unsigned long loops;
-
-       asm volatile (ANNOTATE_NOSPEC_ALTERNATIVE
-                     ALTERNATIVE("jmp 910f",
-                                 __stringify(__FILL_RETURN_BUFFER(%0, RSB_CLEAR_LOOPS, %1)),
-                                 X86_FEATURE_RETPOLINE)
-                     "910:"
-                     : "=r" (loops), ASM_CALL_CONSTRAINT
-                     : : "memory" );
-#endif
-}
-
 static __always_inline
 void alternative_msr_write(unsigned int msr, u64 val, unsigned int feature)
 {
diff --git a/arch/x86/include/asm/vermagic.h b/arch/x86/include/asm/vermagic.h
new file mode 100644 (file)
index 0000000..75884d2
--- /dev/null
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#ifdef CONFIG_X86_64
+/* X86_64 does not define MODULE_PROC_FAMILY */
+#elif defined CONFIG_M486SX
+#define MODULE_PROC_FAMILY "486SX "
+#elif defined CONFIG_M486
+#define MODULE_PROC_FAMILY "486 "
+#elif defined CONFIG_M586
+#define MODULE_PROC_FAMILY "586 "
+#elif defined CONFIG_M586TSC
+#define MODULE_PROC_FAMILY "586TSC "
+#elif defined CONFIG_M586MMX
+#define MODULE_PROC_FAMILY "586MMX "
+#elif defined CONFIG_MCORE2
+#define MODULE_PROC_FAMILY "CORE2 "
+#elif defined CONFIG_MATOM
+#define MODULE_PROC_FAMILY "ATOM "
+#elif defined CONFIG_M686
+#define MODULE_PROC_FAMILY "686 "
+#elif defined CONFIG_MPENTIUMII
+#define MODULE_PROC_FAMILY "PENTIUMII "
+#elif defined CONFIG_MPENTIUMIII
+#define MODULE_PROC_FAMILY "PENTIUMIII "
+#elif defined CONFIG_MPENTIUMM
+#define MODULE_PROC_FAMILY "PENTIUMM "
+#elif defined CONFIG_MPENTIUM4
+#define MODULE_PROC_FAMILY "PENTIUM4 "
+#elif defined CONFIG_MK6
+#define MODULE_PROC_FAMILY "K6 "
+#elif defined CONFIG_MK7
+#define MODULE_PROC_FAMILY "K7 "
+#elif defined CONFIG_MK8
+#define MODULE_PROC_FAMILY "K8 "
+#elif defined CONFIG_MELAN
+#define MODULE_PROC_FAMILY "ELAN "
+#elif defined CONFIG_MCRUSOE
+#define MODULE_PROC_FAMILY "CRUSOE "
+#elif defined CONFIG_MEFFICEON
+#define MODULE_PROC_FAMILY "EFFICEON "
+#elif defined CONFIG_MWINCHIPC6
+#define MODULE_PROC_FAMILY "WINCHIPC6 "
+#elif defined CONFIG_MWINCHIP3D
+#define MODULE_PROC_FAMILY "WINCHIP3D "
+#elif defined CONFIG_MCYRIXIII
+#define MODULE_PROC_FAMILY "CYRIXIII "
+#elif defined CONFIG_MVIAC3_2
+#define MODULE_PROC_FAMILY "VIAC3-2 "
+#elif defined CONFIG_MVIAC7
+#define MODULE_PROC_FAMILY "VIAC7 "
+#elif defined CONFIG_MGEODEGX1
+#define MODULE_PROC_FAMILY "GEODEGX1 "
+#elif defined CONFIG_MGEODE_LX
+#define MODULE_PROC_FAMILY "GEODE "
+#else
+#error unknown processor family
+#endif
+
+#ifdef CONFIG_X86_32
+# define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY
+#else
+# define MODULE_ARCH_VERMAGIC ""
+#endif
+
+#endif /* _ASM_VERMAGIC_H */
index bf08d45..a19a680 100644 (file)
@@ -1119,35 +1119,53 @@ void switch_to_sld(unsigned long tifn)
        sld_update_msr(!(tifn & _TIF_SLD));
 }
 
-#define SPLIT_LOCK_CPU(model) {X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY}
-
 /*
- * The following processors have the split lock detection feature. But
- * since they don't have the IA32_CORE_CAPABILITIES MSR, the feature cannot
- * be enumerated. Enable it by family and model matching on these
- * processors.
+ * Bits in the IA32_CORE_CAPABILITIES are not architectural, so they should
+ * only be trusted if it is confirmed that a CPU model implements a
+ * specific feature at a particular bit position.
+ *
+ * The possible driver data field values:
+ *
+ * - 0: CPU models that are known to have the per-core split-lock detection
+ *     feature even though they do not enumerate IA32_CORE_CAPABILITIES.
+ *
+ * - 1: CPU models which may enumerate IA32_CORE_CAPABILITIES and if so use
+ *      bit 5 to enumerate the per-core split-lock detection feature.
  */
 static const struct x86_cpu_id split_lock_cpu_ids[] __initconst = {
-       SPLIT_LOCK_CPU(INTEL_FAM6_ICELAKE_X),
-       SPLIT_LOCK_CPU(INTEL_FAM6_ICELAKE_L),
+       X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X,           0),
+       X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L,           0),
+       X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT,        1),
+       X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_D,      1),
+       X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L,      1),
        {}
 };
 
 void __init cpu_set_core_cap_bits(struct cpuinfo_x86 *c)
 {
-       u64 ia32_core_caps = 0;
+       const struct x86_cpu_id *m;
+       u64 ia32_core_caps;
+
+       if (boot_cpu_has(X86_FEATURE_HYPERVISOR))
+               return;
 
-       if (c->x86_vendor != X86_VENDOR_INTEL)
+       m = x86_match_cpu(split_lock_cpu_ids);
+       if (!m)
                return;
-       if (cpu_has(c, X86_FEATURE_CORE_CAPABILITIES)) {
-               /* Enumerate features reported in IA32_CORE_CAPABILITIES MSR. */
+
+       switch (m->driver_data) {
+       case 0:
+               break;
+       case 1:
+               if (!cpu_has(c, X86_FEATURE_CORE_CAPABILITIES))
+                       return;
                rdmsrl(MSR_IA32_CORE_CAPS, ia32_core_caps);
-       } else if (!boot_cpu_has(X86_FEATURE_HYPERVISOR)) {
-               /* Enumerate split lock detection by family and model. */
-               if (x86_match_cpu(split_lock_cpu_ids))
-                       ia32_core_caps |= MSR_IA32_CORE_CAPS_SPLIT_LOCK_DETECT;
+               if (!(ia32_core_caps & MSR_IA32_CORE_CAPS_SPLIT_LOCK_DETECT))
+                       return;
+               break;
+       default:
+               return;
        }
 
-       if (ia32_core_caps & MSR_IA32_CORE_CAPS_SPLIT_LOCK_DETECT)
-               split_lock_setup();
+       split_lock_setup();
 }
index caa032c..ebf34c7 100644 (file)
@@ -227,8 +227,8 @@ static void __init ms_hyperv_init_platform(void)
        ms_hyperv.misc_features = cpuid_edx(HYPERV_CPUID_FEATURES);
        ms_hyperv.hints    = cpuid_eax(HYPERV_CPUID_ENLIGHTMENT_INFO);
 
-       pr_info("Hyper-V: features 0x%x, hints 0x%x\n",
-               ms_hyperv.features, ms_hyperv.hints);
+       pr_info("Hyper-V: features 0x%x, hints 0x%x, misc 0x%x\n",
+               ms_hyperv.features, ms_hyperv.hints, ms_hyperv.misc_features);
 
        ms_hyperv.max_vp_index = cpuid_eax(HYPERV_CPUID_IMPLEMENT_LIMITS);
        ms_hyperv.max_lp_index = cpuid_ebx(HYPERV_CPUID_IMPLEMENT_LIMITS);
@@ -263,6 +263,16 @@ static void __init ms_hyperv_init_platform(void)
                        cpuid_eax(HYPERV_CPUID_NESTED_FEATURES);
        }
 
+       /*
+        * Hyper-V expects to get crash register data or kmsg when
+        * crash enlightment is available and system crashes. Set
+        * crash_kexec_post_notifiers to be true to make sure that
+        * calling crash enlightment interface before running kdump
+        * kernel.
+        */
+       if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE)
+               crash_kexec_post_notifiers = true;
+
 #ifdef CONFIG_X86_LOCAL_APIC
        if (ms_hyperv.features & HV_X64_ACCESS_FREQUENCY_MSRS &&
            ms_hyperv.misc_features & HV_FEATURE_FREQUENCY_MSRS_AVAILABLE) {
index 89049b3..d8cc522 100644 (file)
@@ -578,6 +578,8 @@ static void domain_add_cpu(int cpu, struct rdt_resource *r)
        d->id = id;
        cpumask_set_cpu(cpu, &d->cpu_mask);
 
+       rdt_domain_reconfigure_cdp(r);
+
        if (r->alloc_capable && domain_setup_ctrlval(r, d)) {
                kfree(d);
                return;
index 181c992..3dd13f3 100644 (file)
@@ -601,5 +601,6 @@ bool has_busy_rmid(struct rdt_resource *r, struct rdt_domain *d);
 void __check_limbo(struct rdt_domain *d, bool force_free);
 bool cbm_validate_intel(char *buf, u32 *data, struct rdt_resource *r);
 bool cbm_validate_amd(char *buf, u32 *data, struct rdt_resource *r);
+void rdt_domain_reconfigure_cdp(struct rdt_resource *r);
 
 #endif /* _ASM_X86_RESCTRL_INTERNAL_H */
index 064e9ef..5a359d9 100644 (file)
@@ -1859,6 +1859,19 @@ static int set_cache_qos_cfg(int level, bool enable)
        return 0;
 }
 
+/* Restore the qos cfg state when a domain comes online */
+void rdt_domain_reconfigure_cdp(struct rdt_resource *r)
+{
+       if (!r->alloc_capable)
+               return;
+
+       if (r == &rdt_resources_all[RDT_RESOURCE_L2DATA])
+               l2_qos_cfg_update(&r->alloc_enabled);
+
+       if (r == &rdt_resources_all[RDT_RESOURCE_L3DATA])
+               l3_qos_cfg_update(&r->alloc_enabled);
+}
+
 /*
  * Enable or disable the MBA software controller
  * which helps user specify bandwidth in MBps.
@@ -3072,7 +3085,8 @@ static int rdtgroup_rmdir(struct kernfs_node *kn)
         * If the rdtgroup is a mon group and parent directory
         * is a valid "mon_groups" directory, remove the mon group.
         */
-       if (rdtgrp->type == RDTCTRL_GROUP && parent_kn == rdtgroup_default.kn) {
+       if (rdtgrp->type == RDTCTRL_GROUP && parent_kn == rdtgroup_default.kn &&
+           rdtgrp != &rdtgroup_default) {
                if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP ||
                    rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
                        ret = rdtgroup_ctrl_remove(kn, rdtgrp);
index 1cb3ca9..1afbdd1 100644 (file)
@@ -39,8 +39,7 @@ static bool __read_mostly sched_itmt_capable;
 unsigned int __read_mostly sysctl_sched_itmt_enabled;
 
 static int sched_itmt_update_handler(struct ctl_table *table, int write,
-                                    void __user *buffer, size_t *lenp,
-                                    loff_t *ppos)
+                                    void *buffer, size_t *lenp, loff_t *ppos)
 {
        unsigned int old_sysctl;
        int ret;
index fe3ab96..8c89e4d 100644 (file)
@@ -147,7 +147,7 @@ static inline void smpboot_restore_warm_reset_vector(void)
        *((volatile u32 *)phys_to_virt(TRAMPOLINE_PHYS_LOW)) = 0;
 }
 
-static void init_freq_invariance(void);
+static void init_freq_invariance(bool secondary);
 
 /*
  * Report back to the Boot Processor during boot time or to the caller processor
@@ -185,7 +185,7 @@ static void smp_callin(void)
         */
        set_cpu_sibling_map(raw_smp_processor_id());
 
-       init_freq_invariance();
+       init_freq_invariance(true);
 
        /*
         * Get our bogomips.
@@ -1341,7 +1341,7 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus)
        set_sched_topology(x86_topology);
 
        set_cpu_sibling_map(0);
-       init_freq_invariance();
+       init_freq_invariance(false);
        smp_sanity_check();
 
        switch (apic_intr_mode) {
@@ -1877,9 +1877,6 @@ static bool knl_set_max_freq_ratio(u64 *base_freq, u64 *turbo_freq,
        int err, i;
        u64 msr;
 
-       if (!x86_match_cpu(has_knl_turbo_ratio_limits))
-               return false;
-
        err = rdmsrl_safe(MSR_PLATFORM_INFO, base_freq);
        if (err)
                return false;
@@ -1945,18 +1942,23 @@ static bool skx_set_max_freq_ratio(u64 *base_freq, u64 *turbo_freq, int size)
 
 static bool core_set_max_freq_ratio(u64 *base_freq, u64 *turbo_freq)
 {
+       u64 msr;
        int err;
 
        err = rdmsrl_safe(MSR_PLATFORM_INFO, base_freq);
        if (err)
                return false;
 
-       err = rdmsrl_safe(MSR_TURBO_RATIO_LIMIT, turbo_freq);
+       err = rdmsrl_safe(MSR_TURBO_RATIO_LIMIT, &msr);
        if (err)
                return false;
 
-       *base_freq = (*base_freq >> 8) & 0xFF;      /* max P state */
-       *turbo_freq = (*turbo_freq >> 24) & 0xFF;   /* 4C turbo    */
+       *base_freq = (*base_freq >> 8) & 0xFF;    /* max P state */
+       *turbo_freq = (msr >> 24) & 0xFF;         /* 4C turbo    */
+
+       /* The CPU may have less than 4 cores */
+       if (!*turbo_freq)
+               *turbo_freq = msr & 0xFF;         /* 1C turbo    */
 
        return true;
 }
@@ -1972,7 +1974,8 @@ static bool intel_set_max_freq_ratio(void)
            skx_set_max_freq_ratio(&base_freq, &turbo_freq, 1))
                goto out;
 
-       if (knl_set_max_freq_ratio(&base_freq, &turbo_freq, 1))
+       if (x86_match_cpu(has_knl_turbo_ratio_limits) &&
+           knl_set_max_freq_ratio(&base_freq, &turbo_freq, 1))
                goto out;
 
        if (x86_match_cpu(has_skx_turbo_ratio_limits) &&
@@ -1985,13 +1988,22 @@ static bool intel_set_max_freq_ratio(void)
        return false;
 
 out:
+       /*
+        * Some hypervisors advertise X86_FEATURE_APERFMPERF
+        * but then fill all MSR's with zeroes.
+        */
+       if (!base_freq) {
+               pr_debug("Couldn't determine cpu base frequency, necessary for scale-invariant accounting.\n");
+               return false;
+       }
+
        arch_turbo_freq_ratio = div_u64(turbo_freq * SCHED_CAPACITY_SCALE,
                                        base_freq);
        arch_set_max_freq_ratio(turbo_disabled());
        return true;
 }
 
-static void init_counter_refs(void *arg)
+static void init_counter_refs(void)
 {
        u64 aperf, mperf;
 
@@ -2002,18 +2014,25 @@ static void init_counter_refs(void *arg)
        this_cpu_write(arch_prev_mperf, mperf);
 }
 
-static void init_freq_invariance(void)
+static void init_freq_invariance(bool secondary)
 {
        bool ret = false;
 
-       if (smp_processor_id() != 0 || !boot_cpu_has(X86_FEATURE_APERFMPERF))
+       if (!boot_cpu_has(X86_FEATURE_APERFMPERF))
                return;
 
+       if (secondary) {
+               if (static_branch_likely(&arch_scale_freq_key)) {
+                       init_counter_refs();
+               }
+               return;
+       }
+
        if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
                ret = intel_set_max_freq_ratio();
 
        if (ret) {
-               on_each_cpu(init_counter_refs, NULL, 1);
+               init_counter_refs();
                static_branch_enable(&arch_scale_freq_key);
        } else {
                pr_debug("Couldn't determine max cpu frequency, necessary for scale-invariant accounting.\n");
index 4d732a4..8d5cbe1 100644 (file)
@@ -81,7 +81,7 @@
 #define        UMIP_INST_SLDT  3       /* 0F 00 /0 */
 #define        UMIP_INST_STR   4       /* 0F 00 /1 */
 
-const char * const umip_insns[5] = {
+static const char * const umip_insns[5] = {
        [UMIP_INST_SGDT] = "SGDT",
        [UMIP_INST_SIDT] = "SIDT",
        [UMIP_INST_SMSW] = "SMSW",
index a789759..4a3081e 100644 (file)
@@ -3,6 +3,10 @@
 ccflags-y += -Iarch/x86/kvm
 ccflags-$(CONFIG_KVM_WERROR) += -Werror
 
+ifeq ($(CONFIG_FRAME_POINTER),y)
+OBJECT_FILES_NON_STANDARD_vmenter.o := y
+endif
+
 KVM := ../../../virt/kvm
 
 kvm-y                  += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \
index 0e3fc31..cf912b4 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/kernel.h>
 #include <linux/highmem.h>
 #include <linux/psp-sev.h>
+#include <linux/pagemap.h>
 #include <linux/swap.h>
 
 #include "x86.h"
@@ -1117,7 +1118,7 @@ int __init sev_hardware_setup(void)
        /* Maximum number of encrypted guests supported simultaneously */
        max_sev_asid = cpuid_ecx(0x8000001F);
 
-       if (!max_sev_asid)
+       if (!svm_sev_enabled())
                return 1;
 
        /* Minimum ASID value that should be used for SEV guest */
@@ -1156,6 +1157,9 @@ err:
 
 void sev_hardware_teardown(void)
 {
+       if (!svm_sev_enabled())
+               return;
+
        bitmap_free(sev_asid_bitmap);
        bitmap_free(sev_reclaim_asid_bitmap);
 
index 2be5bba..2f379ba 100644 (file)
@@ -3276,7 +3276,7 @@ static void svm_cancel_injection(struct kvm_vcpu *vcpu)
        svm_complete_interrupts(svm);
 }
 
-bool __svm_vcpu_run(unsigned long vmcb_pa, unsigned long *regs);
+void __svm_vcpu_run(unsigned long vmcb_pa, unsigned long *regs);
 
 static void svm_vcpu_run(struct kvm_vcpu *vcpu)
 {
@@ -3330,13 +3330,8 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)
         */
        x86_spec_ctrl_set_guest(svm->spec_ctrl, svm->virt_spec_ctrl);
 
-       local_irq_enable();
-
        __svm_vcpu_run(svm->vmcb_pa, (unsigned long *)&svm->vcpu.arch.regs);
 
-       /* Eliminate branch target predictions from guest mode */
-       vmexit_fill_RSB();
-
 #ifdef CONFIG_X86_64
        wrmsrl(MSR_GS_BASE, svm->host.gs_base);
 #else
@@ -3366,8 +3361,6 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)
 
        reload_tss(vcpu);
 
-       local_irq_disable();
-
        x86_spec_ctrl_restore_host(svm->spec_ctrl, svm->virt_spec_ctrl);
 
        vcpu->arch.cr2 = svm->vmcb->save.cr2;
@@ -3411,7 +3404,6 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)
 
        mark_all_clean(svm->vmcb);
 }
-STACK_FRAME_NON_STANDARD(svm_vcpu_run);
 
 static void svm_load_mmu_pgd(struct kvm_vcpu *vcpu, unsigned long root)
 {
index fa1af90..bf94433 100644 (file)
@@ -3,6 +3,7 @@
 #include <asm/asm.h>
 #include <asm/bitsperlong.h>
 #include <asm/kvm_vcpu_regs.h>
+#include <asm/nospec-branch.h>
 
 #define WORD_SIZE (BITS_PER_LONG / 8)
 
@@ -35,7 +36,6 @@
  */
 SYM_FUNC_START(__svm_vcpu_run)
        push %_ASM_BP
-       mov  %_ASM_SP, %_ASM_BP
 #ifdef CONFIG_X86_64
        push %r15
        push %r14
@@ -78,6 +78,7 @@ SYM_FUNC_START(__svm_vcpu_run)
        pop %_ASM_AX
 
        /* Enter guest mode */
+       sti
 1:     vmload %_ASM_AX
        jmp 3f
 2:     cmpb $0, kvm_rebooting
@@ -99,6 +100,13 @@ SYM_FUNC_START(__svm_vcpu_run)
        ud2
        _ASM_EXTABLE(5b, 6b)
 7:
+       cli
+
+#ifdef CONFIG_RETPOLINE
+       /* IMPORTANT: Stuff the RSB immediately after VM-Exit, before RET! */
+       FILL_RETURN_BUFFER %_ASM_AX, RSB_CLEAR_LOOPS, X86_FEATURE_RETPOLINE
+#endif
+
        /* "POP" @regs to RAX. */
        pop %_ASM_AX
 
index cbc9ea2..fd78ffb 100644 (file)
@@ -5533,8 +5533,25 @@ static bool nested_vmx_exit_handled_vmcs_access(struct kvm_vcpu *vcpu,
        return 1 & (b >> (field & 7));
 }
 
+static bool nested_vmx_exit_handled_mtf(struct vmcs12 *vmcs12)
+{
+       u32 entry_intr_info = vmcs12->vm_entry_intr_info_field;
+
+       if (nested_cpu_has_mtf(vmcs12))
+               return true;
+
+       /*
+        * An MTF VM-exit may be injected into the guest by setting the
+        * interruption-type to 7 (other event) and the vector field to 0. Such
+        * is the case regardless of the 'monitor trap flag' VM-execution
+        * control.
+        */
+       return entry_intr_info == (INTR_INFO_VALID_MASK
+                                  | INTR_TYPE_OTHER_EVENT);
+}
+
 /*
- * Return 1 if we should exit from L2 to L1 to handle an exit, or 0 if we
+ * Return true if we should exit from L2 to L1 to handle an exit, or false if we
  * should handle it ourselves in L0 (and then continue L2). Only call this
  * when in is_guest_mode (L2).
  */
@@ -5633,7 +5650,7 @@ bool nested_vmx_exit_reflected(struct kvm_vcpu *vcpu, u32 exit_reason)
        case EXIT_REASON_MWAIT_INSTRUCTION:
                return nested_cpu_has(vmcs12, CPU_BASED_MWAIT_EXITING);
        case EXIT_REASON_MONITOR_TRAP_FLAG:
-               return nested_cpu_has_mtf(vmcs12);
+               return nested_vmx_exit_handled_mtf(vmcs12);
        case EXIT_REASON_MONITOR_INSTRUCTION:
                return nested_cpu_has(vmcs12, CPU_BASED_MONITOR_EXITING);
        case EXIT_REASON_PAUSE_INSTRUCTION:
index 8305097..c2c6335 100644 (file)
@@ -4572,7 +4572,7 @@ static int handle_rmode_exception(struct kvm_vcpu *vcpu,
  */
 static void kvm_machine_check(void)
 {
-#if defined(CONFIG_X86_MCE) && defined(CONFIG_X86_64)
+#if defined(CONFIG_X86_MCE)
        struct pt_regs regs = {
                .cs = 3, /* Fake ring 3 no matter what the guest ran on */
                .flags = X86_EFLAGS_IF,
index 3bf2eca..c5835f9 100644 (file)
@@ -3060,6 +3060,17 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
        case MSR_IA32_PERF_CTL:
        case MSR_AMD64_DC_CFG:
        case MSR_F15H_EX_CFG:
+       /*
+        * Intel Sandy Bridge CPUs must support the RAPL (running average power
+        * limit) MSRs. Just return 0, as we do not want to expose the host
+        * data here. Do not conditionalize this on CPUID, as KVM does not do
+        * so for existing CPU-specific MSRs.
+        */
+       case MSR_RAPL_POWER_UNIT:
+       case MSR_PP0_ENERGY_STATUS:     /* Power plane 0 (core) */
+       case MSR_PP1_ENERGY_STATUS:     /* Power plane 1 (graphics uncore) */
+       case MSR_PKG_ENERGY_STATUS:     /* Total package */
+       case MSR_DRAM_ENERGY_STATUS:    /* DRAM controller */
                msr_info->data = 0;
                break;
        case MSR_F15H_PERF_CTL0 ... MSR_F15H_PERF_CTR5:
@@ -5049,10 +5060,13 @@ set_identity_unlock:
                r = -EFAULT;
                if (copy_from_user(&u.ps, argp, sizeof(u.ps)))
                        goto out;
+               mutex_lock(&kvm->lock);
                r = -ENXIO;
                if (!kvm->arch.vpit)
-                       goto out;
+                       goto set_pit_out;
                r = kvm_vm_ioctl_set_pit(kvm, &u.ps);
+set_pit_out:
+               mutex_unlock(&kvm->lock);
                break;
        }
        case KVM_GET_PIT2: {
@@ -5072,10 +5086,13 @@ set_identity_unlock:
                r = -EFAULT;
                if (copy_from_user(&u.ps2, argp, sizeof(u.ps2)))
                        goto out;
+               mutex_lock(&kvm->lock);
                r = -ENXIO;
                if (!kvm->arch.vpit)
-                       goto out;
+                       goto set_pit2_out;
                r = kvm_vm_ioctl_set_pit2(kvm, &u.ps2);
+set_pit2_out:
+               mutex_unlock(&kvm->lock);
                break;
        }
        case KVM_REINJECT_CONTROL: {
index 5ea7c2c..42b6709 100644 (file)
@@ -158,6 +158,19 @@ static bool is_ereg(u32 reg)
                             BIT(BPF_REG_AX));
 }
 
+/*
+ * is_ereg_8l() == true if BPF register 'reg' is mapped to access x86-64
+ * lower 8-bit registers dil,sil,bpl,spl,r8b..r15b, which need extra byte
+ * of encoding. al,cl,dl,bl have simpler encoding.
+ */
+static bool is_ereg_8l(u32 reg)
+{
+       return is_ereg(reg) ||
+           (1 << reg) & (BIT(BPF_REG_1) |
+                         BIT(BPF_REG_2) |
+                         BIT(BPF_REG_FP));
+}
+
 static bool is_axreg(u32 reg)
 {
        return reg == BPF_REG_0;
@@ -598,9 +611,8 @@ static void emit_stx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
        switch (size) {
        case BPF_B:
                /* Emit 'mov byte ptr [rax + off], al' */
-               if (is_ereg(dst_reg) || is_ereg(src_reg) ||
-                   /* We have to add extra byte for x86 SIL, DIL regs */
-                   src_reg == BPF_REG_1 || src_reg == BPF_REG_2)
+               if (is_ereg(dst_reg) || is_ereg_8l(src_reg))
+                       /* Add extra byte for eregs or SIL,DIL,BPL in src_reg */
                        EMIT2(add_2mod(0x40, dst_reg, src_reg), 0x88);
                else
                        EMIT1(0x88);
index 4d2a7a7..66cd150 100644 (file)
@@ -1847,14 +1847,16 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
                        case BPF_B:
                        case BPF_H:
                        case BPF_W:
-                               if (!bpf_prog->aux->verifier_zext)
+                               if (bpf_prog->aux->verifier_zext)
                                        break;
                                if (dstk) {
                                        EMIT3(0xC7, add_1reg(0x40, IA32_EBP),
                                              STACK_VAR(dst_hi));
                                        EMIT(0x0, 4);
                                } else {
-                                       EMIT3(0xC7, add_1reg(0xC0, dst_hi), 0);
+                                       /* xor dst_hi,dst_hi */
+                                       EMIT2(0x33,
+                                             add_2reg(0xC0, dst_hi, dst_hi));
                                }
                                break;
                        case BPF_DW:
@@ -2013,8 +2015,8 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
                case BPF_JMP | BPF_JSET | BPF_X:
                case BPF_JMP32 | BPF_JSET | BPF_X: {
                        bool is_jmp64 = BPF_CLASS(insn->code) == BPF_JMP;
-                       u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
-                       u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
+                       u8 dreg_lo = IA32_EAX;
+                       u8 dreg_hi = IA32_EDX;
                        u8 sreg_lo = sstk ? IA32_ECX : src_lo;
                        u8 sreg_hi = sstk ? IA32_EBX : src_hi;
 
@@ -2026,6 +2028,13 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
                                              add_2reg(0x40, IA32_EBP,
                                                       IA32_EDX),
                                              STACK_VAR(dst_hi));
+                       } else {
+                               /* mov dreg_lo,dst_lo */
+                               EMIT2(0x89, add_2reg(0xC0, dreg_lo, dst_lo));
+                               if (is_jmp64)
+                                       /* mov dreg_hi,dst_hi */
+                                       EMIT2(0x89,
+                                             add_2reg(0xC0, dreg_hi, dst_hi));
                        }
 
                        if (sstk) {
@@ -2050,8 +2059,8 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
                case BPF_JMP | BPF_JSET | BPF_K:
                case BPF_JMP32 | BPF_JSET | BPF_K: {
                        bool is_jmp64 = BPF_CLASS(insn->code) == BPF_JMP;
-                       u8 dreg_lo = dstk ? IA32_EAX : dst_lo;
-                       u8 dreg_hi = dstk ? IA32_EDX : dst_hi;
+                       u8 dreg_lo = IA32_EAX;
+                       u8 dreg_hi = IA32_EDX;
                        u8 sreg_lo = IA32_ECX;
                        u8 sreg_hi = IA32_EBX;
                        u32 hi;
@@ -2064,6 +2073,13 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
                                              add_2reg(0x40, IA32_EBP,
                                                       IA32_EDX),
                                              STACK_VAR(dst_hi));
+                       } else {
+                               /* mov dreg_lo,dst_lo */
+                               EMIT2(0x89, add_2reg(0xC0, dreg_lo, dst_lo));
+                               if (is_jmp64)
+                                       /* mov dreg_hi,dst_hi */
+                                       EMIT2(0x89,
+                                             add_2reg(0xC0, dreg_hi, dst_hi));
                        }
 
                        /* mov ecx,imm32 */
index 211bb93..c5e393f 100644 (file)
@@ -202,7 +202,7 @@ virt_to_phys_or_null_size(void *va, unsigned long size)
 
 int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
 {
-       unsigned long pfn, text, pf;
+       unsigned long pfn, text, pf, rodata;
        struct page *page;
        unsigned npages;
        pgd_t *pgd = efi_mm.pgd;
@@ -256,7 +256,7 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
 
        efi_scratch.phys_stack = page_to_phys(page + 1); /* stack grows down */
 
-       npages = (__end_rodata_aligned - _text) >> PAGE_SHIFT;
+       npages = (_etext - _text) >> PAGE_SHIFT;
        text = __pa(_text);
        pfn = text >> PAGE_SHIFT;
 
@@ -266,6 +266,14 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
                return 1;
        }
 
+       npages = (__end_rodata - __start_rodata) >> PAGE_SHIFT;
+       rodata = __pa(__start_rodata);
+       pfn = rodata >> PAGE_SHIFT;
+       if (kernel_map_pages_in_pgd(pgd, pfn, rodata, npages, pf)) {
+               pr_err("Failed to map kernel rodata 1:1\n");
+               return 1;
+       }
+
        return 0;
 }
 
@@ -638,7 +646,7 @@ efi_thunk_set_variable(efi_char16_t *name, efi_guid_t *vendor,
        phys_vendor = virt_to_phys_or_null(vnd);
        phys_data = virt_to_phys_or_null_size(data, data_size);
 
-       if (!phys_name || !phys_data)
+       if (!phys_name || (data && !phys_data))
                status = EFI_INVALID_PARAMETER;
        else
                status = efi_thunk(set_variable, phys_name, phys_vendor,
@@ -669,7 +677,7 @@ efi_thunk_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
        phys_vendor = virt_to_phys_or_null(vnd);
        phys_data = virt_to_phys_or_null_size(data, data_size);
 
-       if (!phys_name || !phys_data)
+       if (!phys_name || (data && !phys_data))
                status = EFI_INVALID_PARAMETER;
        else
                status = efi_thunk(set_variable, phys_name, phys_vendor,
diff --git a/arch/xtensa/include/asm/module.h b/arch/xtensa/include/asm/module.h
deleted file mode 100644 (file)
index 488b40c..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * include/asm-xtensa/module.h
- *
- * This file contains the module code specific to the Xtensa architecture.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2001 - 2005 Tensilica Inc.
- */
-
-#ifndef _XTENSA_MODULE_H
-#define _XTENSA_MODULE_H
-
-#define MODULE_ARCH_VERMAGIC "xtensa-" __stringify(XCHAL_CORE_ID) " "
-
-#include <asm-generic/module.h>
-
-#endif /* _XTENSA_MODULE_H */
diff --git a/arch/xtensa/include/asm/vermagic.h b/arch/xtensa/include/asm/vermagic.h
new file mode 100644 (file)
index 0000000..6d9c670
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ */
+
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#include <linux/stringify.h>
+#include <variant/core.h>
+
+#define MODULE_ARCH_VERMAGIC "xtensa-" __stringify(XCHAL_CORE_ID) " "
+
+#endif /* _ASM_VERMAGIC_H */
index db35ee6..3ab0c1c 100644 (file)
@@ -1591,7 +1591,7 @@ skip_surplus_transfers:
                                      vrate_min, vrate_max);
                }
 
-               trace_iocost_ioc_vrate_adj(ioc, vrate, &missed_ppm, rq_wait_pct,
+               trace_iocost_ioc_vrate_adj(ioc, vrate, missed_ppm, rq_wait_pct,
                                           nr_lagging, nr_shortages,
                                           nr_surpluses);
 
@@ -1600,7 +1600,7 @@ skip_surplus_transfers:
                        ioc->period_us * vrate * INUSE_MARGIN_PCT, 100);
        } else if (ioc->busy_level != prev_busy_level || nr_lagging) {
                trace_iocost_ioc_vrate_adj(ioc, atomic64_read(&ioc->vtime_rate),
-                                          &missed_ppm, rq_wait_pct, nr_lagging,
+                                          missed_ppm, rq_wait_pct, nr_lagging,
                                           nr_shortages, nr_surpluses);
        }
 
index 8e56884..a7785df 100644 (file)
@@ -1222,8 +1222,10 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list,
                rq = list_first_entry(list, struct request, queuelist);
 
                hctx = rq->mq_hctx;
-               if (!got_budget && !blk_mq_get_dispatch_budget(hctx))
+               if (!got_budget && !blk_mq_get_dispatch_budget(hctx)) {
+                       blk_mq_put_driver_tag(rq);
                        break;
+               }
 
                if (!blk_mq_get_driver_tag(rq)) {
                        /*
index 8641ba9..9cb082f 100644 (file)
@@ -313,7 +313,7 @@ static void scale_up(struct rq_wb *rwb)
        calc_wb_limits(rwb);
        rwb->unknown_cnt = 0;
        rwb_wake_all(rwb);
-       rwb_trace_step(rwb, "scale up");
+       rwb_trace_step(rwb, tracepoint_string("scale up"));
 }
 
 static void scale_down(struct rq_wb *rwb, bool hard_throttle)
@@ -322,7 +322,7 @@ static void scale_down(struct rq_wb *rwb, bool hard_throttle)
                return;
        calc_wb_limits(rwb);
        rwb->unknown_cnt = 0;
-       rwb_trace_step(rwb, "scale down");
+       rwb_trace_step(rwb, tracepoint_string("scale down"));
 }
 
 static void rwb_arm_timer(struct rq_wb *rwb)
index bc1ded1..9ef48a8 100644 (file)
@@ -496,7 +496,7 @@ int blk_drop_partitions(struct gendisk *disk, struct block_device *bdev)
 
        if (!disk_part_scan_enabled(disk))
                return 0;
-       if (bdev->bd_part_count || bdev->bd_openers > 1)
+       if (bdev->bd_part_count)
                return -EBUSY;
        res = invalidate_partition(disk, 0);
        if (res)
index 00112cf..78cfc70 100644 (file)
@@ -87,18 +87,6 @@ static const struct dmi_system_id dmi_lid_quirks[] = {
                },
                .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
        },
-       {
-               /*
-                * Asus T200TA, _LID keeps reporting closed after every second
-                * openening of the lid. Causing immediate re-suspend after
-                * opening every other open. Using LID_INIT_OPEN fixes this.
-                */
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"),
-               },
-               .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
-       },
        {
                /* GP-electronic T701, _LID method points to a floating GPIO */
                .matches = {
index b2263ec..5832bc1 100644 (file)
@@ -273,13 +273,13 @@ int acpi_device_set_power(struct acpi_device *device, int state)
  end:
        if (result) {
                dev_warn(&device->dev, "Failed to change power state to %s\n",
-                        acpi_power_state_string(state));
+                        acpi_power_state_string(target_state));
        } else {
                device->power.state = target_state;
                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
                                  "Device [%s] transitioned to %s\n",
                                  device->pnp.bus_id,
-                                 acpi_power_state_string(state)));
+                                 acpi_power_state_string(target_state)));
        }
 
        return result;
index 00a6da2..ed3d218 100644 (file)
@@ -322,10 +322,10 @@ static int acpi_pci_link_set(struct acpi_pci_link *link, int irq)
                resource->res.data.extended_irq.polarity =
                    link->irq.polarity;
                if (link->irq.triggering == ACPI_EDGE_SENSITIVE)
-                       resource->res.data.irq.shareable =
+                       resource->res.data.extended_irq.shareable =
                            ACPI_EXCLUSIVE;
                else
-                       resource->res.data.irq.shareable = ACPI_SHARED;
+                       resource->res.data.extended_irq.shareable = ACPI_SHARED;
                resource->res.data.extended_irq.interrupt_count = 1;
                resource->res.data.extended_irq.interrupts[0] = irq;
                /* ignore resource_source, it's optional */
index 0101b65..0c0a736 100644 (file)
@@ -410,6 +410,7 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(INTEL, 0x22a3), board_ahci_mobile }, /* Cherry Tr. AHCI */
        { PCI_VDEVICE(INTEL, 0x5ae3), board_ahci_mobile }, /* ApolloLake AHCI */
        { PCI_VDEVICE(INTEL, 0x34d3), board_ahci_mobile }, /* Ice Lake LP AHCI */
+       { PCI_VDEVICE(INTEL, 0x02d3), board_ahci_mobile }, /* Comet Lake PCH-U AHCI */
        { PCI_VDEVICE(INTEL, 0x02d7), board_ahci_mobile }, /* Comet Lake PCH RAID */
 
        /* JMicron 360/1/3/5/6, match class to avoid IDE function */
index a6b76cc..e517bd8 100644 (file)
@@ -145,7 +145,7 @@ enum {
 
        /* PORT_IDMA_CTL bits */
        IDMA_CTL_RST_ATA        = (1 << 2),  /* hardreset ATA bus */
-       IDMA_CTL_RST_IDMA       = (1 << 5),  /* reset IDMA machinary */
+       IDMA_CTL_RST_IDMA       = (1 << 5),  /* reset IDMA machinery */
        IDMA_CTL_GO             = (1 << 7),  /* IDMA mode go */
        IDMA_CTL_ATA_NIEN       = (1 << 8),  /* ATA IRQ disable */
 
index 8c37294..cfb0d16 100644 (file)
@@ -306,7 +306,7 @@ config ATM_IA
          for more info about the cards. Say Y (or M to compile as a module
          named iphase) here if you have one of these cards.
 
-         See the file <file:Documentation/networking/iphase.txt> for further
+         See the file <file:Documentation/networking/iphase.rst> for further
          details.
 
 config ATM_IA_DEBUG
@@ -336,7 +336,7 @@ config ATM_FORE200E
          on PCI and SBUS hosts. Say Y (or M to compile as a module
          named fore_200e) here if you have one of these ATM adapters.
 
-         See the file <file:Documentation/networking/fore200e.txt> for
+         See the file <file:Documentation/networking/fore200e.rst> for
          further details.
 
 config ATM_FORE200E_USE_TASKLET
index ba9d30b..a182e31 100644 (file)
@@ -45,5 +45,4 @@ struct ctl_table firmware_config_table[] = {
        },
        { }
 };
-EXPORT_SYMBOL_GPL(firmware_config_table);
 #endif
index fdd508a..0e07e17 100644 (file)
@@ -730,7 +730,7 @@ static bool dpm_async_fn(struct device *dev, async_func_t func)
 
        if (is_async(dev)) {
                get_device(dev);
-               async_schedule(func, dev);
+               async_schedule_dev(func, dev);
                return true;
        }
 
index 62b6608..81b311c 100644 (file)
@@ -85,26 +85,35 @@ struct nullb {
        char disk_name[DISK_NAME_LEN];
 };
 
+blk_status_t null_process_cmd(struct nullb_cmd *cmd,
+                             enum req_opf op, sector_t sector,
+                             unsigned int nr_sectors);
+
 #ifdef CONFIG_BLK_DEV_ZONED
-int null_zone_init(struct nullb_device *dev);
-void null_zone_exit(struct nullb_device *dev);
+int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q);
+int null_register_zoned_dev(struct nullb *nullb);
+void null_free_zoned_dev(struct nullb_device *dev);
 int null_report_zones(struct gendisk *disk, sector_t sector,
                      unsigned int nr_zones, report_zones_cb cb, void *data);
-blk_status_t null_handle_zoned(struct nullb_cmd *cmd,
-                               enum req_opf op, sector_t sector,
-                               sector_t nr_sectors);
+blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd,
+                                   enum req_opf op, sector_t sector,
+                                   sector_t nr_sectors);
 size_t null_zone_valid_read_len(struct nullb *nullb,
                                sector_t sector, unsigned int len);
 #else
-static inline int null_zone_init(struct nullb_device *dev)
+static inline int null_init_zoned_dev(struct nullb_device *dev,
+                                     struct request_queue *q)
 {
        pr_err("CONFIG_BLK_DEV_ZONED not enabled\n");
        return -EINVAL;
 }
-static inline void null_zone_exit(struct nullb_device *dev) {}
-static inline blk_status_t null_handle_zoned(struct nullb_cmd *cmd,
-                                            enum req_opf op, sector_t sector,
-                                            sector_t nr_sectors)
+static inline int null_register_zoned_dev(struct nullb *nullb)
+{
+       return -ENODEV;
+}
+static inline void null_free_zoned_dev(struct nullb_device *dev) {}
+static inline blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd,
+                       enum req_opf op, sector_t sector, sector_t nr_sectors)
 {
        return BLK_STS_NOTSUPP;
 }
index 4e1c071..8efd877 100644 (file)
@@ -580,7 +580,7 @@ static void null_free_dev(struct nullb_device *dev)
        if (!dev)
                return;
 
-       null_zone_exit(dev);
+       null_free_zoned_dev(dev);
        badblocks_exit(&dev->badblocks);
        kfree(dev);
 }
@@ -1276,6 +1276,25 @@ static inline void nullb_complete_cmd(struct nullb_cmd *cmd)
        }
 }
 
+blk_status_t null_process_cmd(struct nullb_cmd *cmd,
+                             enum req_opf op, sector_t sector,
+                             unsigned int nr_sectors)
+{
+       struct nullb_device *dev = cmd->nq->dev;
+       blk_status_t ret;
+
+       if (dev->badblocks.shift != -1) {
+               ret = null_handle_badblocks(cmd, sector, nr_sectors);
+               if (ret != BLK_STS_OK)
+                       return ret;
+       }
+
+       if (dev->memory_backed)
+               return null_handle_memory_backed(cmd, op);
+
+       return BLK_STS_OK;
+}
+
 static blk_status_t null_handle_cmd(struct nullb_cmd *cmd, sector_t sector,
                                    sector_t nr_sectors, enum req_opf op)
 {
@@ -1294,17 +1313,11 @@ static blk_status_t null_handle_cmd(struct nullb_cmd *cmd, sector_t sector,
                goto out;
        }
 
-       if (nullb->dev->badblocks.shift != -1) {
-               cmd->error = null_handle_badblocks(cmd, sector, nr_sectors);
-               if (cmd->error != BLK_STS_OK)
-                       goto out;
-       }
-
-       if (dev->memory_backed)
-               cmd->error = null_handle_memory_backed(cmd, op);
-
-       if (!cmd->error && dev->zoned)
-               cmd->error = null_handle_zoned(cmd, op, sector, nr_sectors);
+       if (dev->zoned)
+               cmd->error = null_process_zoned_cmd(cmd, op,
+                                                   sector, nr_sectors);
+       else
+               cmd->error = null_process_cmd(cmd, op, sector, nr_sectors);
 
 out:
        nullb_complete_cmd(cmd);
@@ -1605,19 +1618,12 @@ static int null_gendisk_register(struct nullb *nullb)
        disk->queue             = nullb->q;
        strncpy(disk->disk_name, nullb->disk_name, DISK_NAME_LEN);
 
-#ifdef CONFIG_BLK_DEV_ZONED
        if (nullb->dev->zoned) {
-               if (queue_is_mq(nullb->q)) {
-                       int ret = blk_revalidate_disk_zones(disk);
-                       if (ret)
-                               return ret;
-               } else {
-                       blk_queue_chunk_sectors(nullb->q,
-                                       nullb->dev->zone_size_sects);
-                       nullb->q->nr_zones = blkdev_nr_zones(disk);
-               }
+               int ret = null_register_zoned_dev(nullb);
+
+               if (ret)
+                       return ret;
        }
-#endif
 
        add_disk(disk);
        return 0;
@@ -1773,14 +1779,9 @@ static int null_add_dev(struct nullb_device *dev)
        }
 
        if (dev->zoned) {
-               rv = null_zone_init(dev);
+               rv = null_init_zoned_dev(dev, nullb->q);
                if (rv)
                        goto out_cleanup_blk_queue;
-
-               nullb->q->limits.zoned = BLK_ZONED_HM;
-               blk_queue_flag_set(QUEUE_FLAG_ZONE_RESETALL, nullb->q);
-               blk_queue_required_elevator_features(nullb->q,
-                                               ELEVATOR_F_ZBD_SEQ_WRITE);
        }
 
        nullb->q->queuedata = nullb;
@@ -1809,8 +1810,7 @@ static int null_add_dev(struct nullb_device *dev)
 
        return 0;
 out_cleanup_zone:
-       if (dev->zoned)
-               null_zone_exit(dev);
+       null_free_zoned_dev(dev);
 out_cleanup_blk_queue:
        blk_cleanup_queue(nullb->q);
 out_cleanup_tags:
index 673618d..9e4bcda 100644 (file)
@@ -13,7 +13,7 @@ static inline unsigned int null_zone_no(struct nullb_device *dev, sector_t sect)
        return sect >> ilog2(dev->zone_size_sects);
 }
 
-int null_zone_init(struct nullb_device *dev)
+int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q)
 {
        sector_t dev_size = (sector_t)dev->size * 1024 * 1024;
        sector_t sector = 0;
@@ -61,10 +61,27 @@ int null_zone_init(struct nullb_device *dev)
                sector += dev->zone_size_sects;
        }
 
+       q->limits.zoned = BLK_ZONED_HM;
+       blk_queue_flag_set(QUEUE_FLAG_ZONE_RESETALL, q);
+       blk_queue_required_elevator_features(q, ELEVATOR_F_ZBD_SEQ_WRITE);
+
+       return 0;
+}
+
+int null_register_zoned_dev(struct nullb *nullb)
+{
+       struct request_queue *q = nullb->q;
+
+       if (queue_is_mq(q))
+               return blk_revalidate_disk_zones(nullb->disk);
+
+       blk_queue_chunk_sectors(q, nullb->dev->zone_size_sects);
+       q->nr_zones = blkdev_nr_zones(nullb->disk);
+
        return 0;
 }
 
-void null_zone_exit(struct nullb_device *dev)
+void null_free_zoned_dev(struct nullb_device *dev)
 {
        kvfree(dev->zones);
 }
@@ -126,11 +143,16 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
        struct nullb_device *dev = cmd->nq->dev;
        unsigned int zno = null_zone_no(dev, sector);
        struct blk_zone *zone = &dev->zones[zno];
+       blk_status_t ret;
+
+       trace_nullb_zone_op(cmd, zno, zone->cond);
+
+       if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL)
+               return null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors);
 
        switch (zone->cond) {
        case BLK_ZONE_COND_FULL:
                /* Cannot write to a full zone */
-               cmd->error = BLK_STS_IOERR;
                return BLK_STS_IOERR;
        case BLK_ZONE_COND_EMPTY:
        case BLK_ZONE_COND_IMP_OPEN:
@@ -143,19 +165,18 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector,
                if (zone->cond != BLK_ZONE_COND_EXP_OPEN)
                        zone->cond = BLK_ZONE_COND_IMP_OPEN;
 
+               ret = null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors);
+               if (ret != BLK_STS_OK)
+                       return ret;
+
                zone->wp += nr_sectors;
                if (zone->wp == zone->start + zone->len)
                        zone->cond = BLK_ZONE_COND_FULL;
-               break;
-       case BLK_ZONE_COND_NOT_WP:
-               break;
+               return BLK_STS_OK;
        default:
                /* Invalid zone condition */
                return BLK_STS_IOERR;
        }
-
-       trace_nullb_zone_op(cmd, zno, zone->cond);
-       return BLK_STS_OK;
 }
 
 static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op,
@@ -216,8 +237,8 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op,
        return BLK_STS_OK;
 }
 
-blk_status_t null_handle_zoned(struct nullb_cmd *cmd, enum req_opf op,
-                              sector_t sector, sector_t nr_sectors)
+blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_opf op,
+                                   sector_t sector, sector_t nr_sectors)
 {
        switch (op) {
        case REQ_OP_WRITE:
@@ -229,6 +250,6 @@ blk_status_t null_handle_zoned(struct nullb_cmd *cmd, enum req_opf op,
        case REQ_OP_ZONE_FINISH:
                return null_zone_mgmt(cmd, op, sector);
        default:
-               return BLK_STS_OK;
+               return null_process_cmd(cmd, op, sector, nr_sectors);
        }
 }
index 1e0a6b1..67d65ac 100644 (file)
@@ -3754,11 +3754,7 @@ static int __rbd_notify_op_lock(struct rbd_device *rbd_dev,
 static void rbd_notify_op_lock(struct rbd_device *rbd_dev,
                               enum rbd_notify_op notify_op)
 {
-       struct page **reply_pages;
-       size_t reply_len;
-
-       __rbd_notify_op_lock(rbd_dev, notify_op, &reply_pages, &reply_len);
-       ceph_release_page_vector(reply_pages, calc_pages_for(0, reply_len));
+       __rbd_notify_op_lock(rbd_dev, notify_op, NULL, NULL);
 }
 
 static void rbd_notify_acquired_lock(struct work_struct *work)
@@ -4527,6 +4523,10 @@ static void cancel_tasks_sync(struct rbd_device *rbd_dev)
        cancel_work_sync(&rbd_dev->unlock_work);
 }
 
+/*
+ * header_rwsem must not be held to avoid a deadlock with
+ * rbd_dev_refresh() when flushing notifies.
+ */
 static void rbd_unregister_watch(struct rbd_device *rbd_dev)
 {
        cancel_tasks_sync(rbd_dev);
@@ -6894,9 +6894,10 @@ static void rbd_print_dne(struct rbd_device *rbd_dev, bool is_snap)
 
 static void rbd_dev_image_release(struct rbd_device *rbd_dev)
 {
-       rbd_dev_unprobe(rbd_dev);
-       if (rbd_dev->opts)
+       if (!rbd_is_ro(rbd_dev))
                rbd_unregister_watch(rbd_dev);
+
+       rbd_dev_unprobe(rbd_dev);
        rbd_dev->image_format = 0;
        kfree(rbd_dev->spec->image_id);
        rbd_dev->spec->image_id = NULL;
@@ -6907,6 +6908,9 @@ static void rbd_dev_image_release(struct rbd_device *rbd_dev)
  * device.  If this image is the one being mapped (i.e., not a
  * parent), initiate a watch on its header object before using that
  * object to get detailed information about the rbd image.
+ *
+ * On success, returns with header_rwsem held for write if called
+ * with @depth == 0.
  */
 static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
 {
@@ -6936,11 +6940,14 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
                }
        }
 
+       if (!depth)
+               down_write(&rbd_dev->header_rwsem);
+
        ret = rbd_dev_header_info(rbd_dev);
        if (ret) {
                if (ret == -ENOENT && !need_watch)
                        rbd_print_dne(rbd_dev, false);
-               goto err_out_watch;
+               goto err_out_probe;
        }
 
        /*
@@ -6985,10 +6992,11 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth)
        return 0;
 
 err_out_probe:
-       rbd_dev_unprobe(rbd_dev);
-err_out_watch:
+       if (!depth)
+               up_write(&rbd_dev->header_rwsem);
        if (need_watch)
                rbd_unregister_watch(rbd_dev);
+       rbd_dev_unprobe(rbd_dev);
 err_out_format:
        rbd_dev->image_format = 0;
        kfree(rbd_dev->spec->image_id);
@@ -7050,12 +7058,9 @@ static ssize_t do_rbd_add(struct bus_type *bus,
                goto err_out_rbd_dev;
        }
 
-       down_write(&rbd_dev->header_rwsem);
        rc = rbd_dev_image_probe(rbd_dev, 0);
-       if (rc < 0) {
-               up_write(&rbd_dev->header_rwsem);
+       if (rc < 0)
                goto err_out_rbd_dev;
-       }
 
        if (rbd_dev->opts->alloc_size > rbd_dev->layout.object_size) {
                rbd_warn(rbd_dev, "alloc_size adjusted to %u",
index f9b1e70..9d21bf0 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/blk-mq.h>
 #include <linux/blk-mq-virtio.h>
 #include <linux/numa.h>
+#include <uapi/linux/virtio_ring.h>
 
 #define PART_BITS 4
 #define VQ_NAME_LEN 16
@@ -32,6 +33,15 @@ struct virtio_blk_vq {
 } ____cacheline_aligned_in_smp;
 
 struct virtio_blk {
+       /*
+        * This mutex must be held by anything that may run after
+        * virtblk_remove() sets vblk->vdev to NULL.
+        *
+        * blk-mq, virtqueue processing, and sysfs attribute code paths are
+        * shut down before vblk->vdev is set to NULL and therefore do not need
+        * to hold this mutex.
+        */
+       struct mutex vdev_mutex;
        struct virtio_device *vdev;
 
        /* The disk structure for the kernel. */
@@ -43,6 +53,13 @@ struct virtio_blk {
        /* Process context for config space updates */
        struct work_struct config_work;
 
+       /*
+        * Tracks references from block_device_operations open/release and
+        * virtio_driver probe/remove so this object can be freed once no
+        * longer in use.
+        */
+       refcount_t refs;
+
        /* What host tells us, plus 2 for header & tailer. */
        unsigned int sg_elems;
 
@@ -294,10 +311,55 @@ out:
        return err;
 }
 
+static void virtblk_get(struct virtio_blk *vblk)
+{
+       refcount_inc(&vblk->refs);
+}
+
+static void virtblk_put(struct virtio_blk *vblk)
+{
+       if (refcount_dec_and_test(&vblk->refs)) {
+               ida_simple_remove(&vd_index_ida, vblk->index);
+               mutex_destroy(&vblk->vdev_mutex);
+               kfree(vblk);
+       }
+}
+
+static int virtblk_open(struct block_device *bd, fmode_t mode)
+{
+       struct virtio_blk *vblk = bd->bd_disk->private_data;
+       int ret = 0;
+
+       mutex_lock(&vblk->vdev_mutex);
+
+       if (vblk->vdev)
+               virtblk_get(vblk);
+       else
+               ret = -ENXIO;
+
+       mutex_unlock(&vblk->vdev_mutex);
+       return ret;
+}
+
+static void virtblk_release(struct gendisk *disk, fmode_t mode)
+{
+       struct virtio_blk *vblk = disk->private_data;
+
+       virtblk_put(vblk);
+}
+
 /* We provide getgeo only to please some old bootloader/partitioning tools */
 static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
 {
        struct virtio_blk *vblk = bd->bd_disk->private_data;
+       int ret = 0;
+
+       mutex_lock(&vblk->vdev_mutex);
+
+       if (!vblk->vdev) {
+               ret = -ENXIO;
+               goto out;
+       }
 
        /* see if the host passed in geometry config */
        if (virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_GEOMETRY)) {
@@ -313,11 +375,15 @@ static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo)
                geo->sectors = 1 << 5;
                geo->cylinders = get_capacity(bd->bd_disk) >> 11;
        }
-       return 0;
+out:
+       mutex_unlock(&vblk->vdev_mutex);
+       return ret;
 }
 
 static const struct block_device_operations virtblk_fops = {
        .owner  = THIS_MODULE,
+       .open = virtblk_open,
+       .release = virtblk_release,
        .getgeo = virtblk_getgeo,
 };
 
@@ -654,6 +720,10 @@ static int virtblk_probe(struct virtio_device *vdev)
                goto out_free_index;
        }
 
+       /* This reference is dropped in virtblk_remove(). */
+       refcount_set(&vblk->refs, 1);
+       mutex_init(&vblk->vdev_mutex);
+
        vblk->vdev = vdev;
        vblk->sg_elems = sg_elems;
 
@@ -819,8 +889,6 @@ out:
 static void virtblk_remove(struct virtio_device *vdev)
 {
        struct virtio_blk *vblk = vdev->priv;
-       int index = vblk->index;
-       int refc;
 
        /* Make sure no work handler is accessing the device. */
        flush_work(&vblk->config_work);
@@ -830,18 +898,21 @@ static void virtblk_remove(struct virtio_device *vdev)
 
        blk_mq_free_tag_set(&vblk->tag_set);
 
+       mutex_lock(&vblk->vdev_mutex);
+
        /* Stop all the virtqueues. */
        vdev->config->reset(vdev);
 
-       refc = kref_read(&disk_to_dev(vblk->disk)->kobj.kref);
+       /* Virtqueues are stopped, nothing can use vblk->vdev anymore. */
+       vblk->vdev = NULL;
+
        put_disk(vblk->disk);
        vdev->config->del_vqs(vdev);
        kfree(vblk->vqs);
-       kfree(vblk);
 
-       /* Only free device id if we don't have any users */
-       if (refc == 1)
-               ida_simple_remove(&vd_index_ida, index);
+       mutex_unlock(&vblk->vdev_mutex);
+
+       virtblk_put(vblk);
 }
 
 #ifdef CONFIG_PM_SLEEP
index a16845c..3ea866d 100644 (file)
@@ -32,7 +32,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
         * VSE event. WCN3991 sends version command response as a payload to
         * command complete event.
         */
-       if (soc_type == QCA_WCN3991) {
+       if (soc_type >= QCA_WCN3991) {
                event_type = 0;
                rlen += 1;
                rtype = EDL_PATCH_VER_REQ_CMD;
@@ -69,7 +69,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version,
                goto out;
        }
 
-       if (soc_type == QCA_WCN3991)
+       if (soc_type >= QCA_WCN3991)
                memmove(&edl->data, &edl->data[1], sizeof(*ver));
 
        ver = (struct qca_btsoc_version *)(edl->data);
@@ -217,7 +217,7 @@ static void qca_tlv_check_data(struct qca_fw_config *config,
                                tlv_nvm->data[0] |= 0x80;
 
                                /* UART Baud Rate */
-                               if (soc_type == QCA_WCN3991)
+                               if (soc_type >= QCA_WCN3991)
                                        tlv_nvm->data[1] = nvm_baud_rate;
                                else
                                        tlv_nvm->data[2] = nvm_baud_rate;
@@ -268,7 +268,7 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
         * VSE event. WCN3991 sends version command response as a payload to
         * command complete event.
         */
-       if (soc_type == QCA_WCN3991) {
+       if (soc_type >= QCA_WCN3991) {
                event_type = 0;
                rlen = sizeof(*edl);
                rtype = EDL_PATCH_TLV_REQ_CMD;
@@ -301,7 +301,7 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
                err = -EIO;
        }
 
-       if (soc_type == QCA_WCN3991)
+       if (soc_type >= QCA_WCN3991)
                goto out;
 
        tlv_resp = (struct tlv_seg_resp *)(edl->data);
@@ -442,6 +442,11 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
                            (soc_ver & 0x0000000f);
                snprintf(config.fwname, sizeof(config.fwname),
                         "qca/crbtfw%02x.tlv", rom_ver);
+       } else if (soc_type == QCA_QCA6390) {
+               rom_ver = ((soc_ver & 0x00000f00) >> 0x04) |
+                           (soc_ver & 0x0000000f);
+               snprintf(config.fwname, sizeof(config.fwname),
+                        "qca/htbtfw%02x.tlv", rom_ver);
        } else {
                snprintf(config.fwname, sizeof(config.fwname),
                         "qca/rampatch_%08x.bin", soc_ver);
@@ -464,6 +469,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
        else if (qca_is_wcn399x(soc_type))
                snprintf(config.fwname, sizeof(config.fwname),
                         "qca/crnv%02x.bin", rom_ver);
+       else if (soc_type == QCA_QCA6390)
+               snprintf(config.fwname, sizeof(config.fwname),
+                        "qca/htnv%02x.bin", rom_ver);
        else
                snprintf(config.fwname, sizeof(config.fwname),
                         "qca/nvm_%08x.bin", soc_ver);
index e16a4d6..6e1e62d 100644 (file)
@@ -125,8 +125,9 @@ enum qca_btsoc_type {
        QCA_AR3002,
        QCA_ROME,
        QCA_WCN3990,
-       QCA_WCN3991,
        QCA_WCN3998,
+       QCA_WCN3991,
+       QCA_QCA6390,
 };
 
 #if IS_ENABLED(CONFIG_BT_QCA)
index 67f4bc2..3a9afc9 100644 (file)
@@ -130,12 +130,19 @@ static const struct id_table ic_id_table[] = {
          .cfg_name = "rtl_bt/rtl8821c_config" },
 
        /* 8761A */
-       { IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8761A, 0x0,
+       { IC_INFO(RTL_ROM_LMP_8761A, 0xa),
          .config_needed = false,
          .has_rom_version = true,
          .fw_name  = "rtl_bt/rtl8761a_fw.bin",
          .cfg_name = "rtl_bt/rtl8761a_config" },
 
+       /* 8761B */
+       { IC_INFO(RTL_ROM_LMP_8761A, 0xb),
+         .config_needed = false,
+         .has_rom_version = true,
+         .fw_name  = "rtl_bt/rtl8761b_fw.bin",
+         .cfg_name = "rtl_bt/rtl8761b_config" },
+
        /* 8822C with UART interface */
        { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV |
                         IC_MATCH_FL_HCIBUS,
@@ -267,6 +274,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
                { RTL_ROM_LMP_8723B, 9 },       /* 8723D */
                { RTL_ROM_LMP_8821A, 10 },      /* 8821C */
                { RTL_ROM_LMP_8822B, 13 },      /* 8822C */
+               { RTL_ROM_LMP_8761A, 14 },      /* 8761B */
        };
 
        min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
index 3bdec42..8711627 100644 (file)
@@ -492,6 +492,8 @@ struct btusb_data {
        __u8 cmdreq;
 
        unsigned int sco_num;
+       unsigned int air_mode;
+       bool usb_alt6_packet_flow;
        int isoc_altsetting;
        int suspend_count;
 
@@ -983,6 +985,42 @@ static void btusb_isoc_complete(struct urb *urb)
        }
 }
 
+static inline void __fill_isoc_descriptor_msbc(struct urb *urb, int len,
+                                              int mtu, struct btusb_data *data)
+{
+       int i, offset = 0;
+       unsigned int interval;
+
+       BT_DBG("len %d mtu %d", len, mtu);
+
+       /* For mSBC ALT 6 setting the host will send the packet at continuous
+        * flow. As per core spec 5, vol 4, part B, table 2.1. For ALT setting
+        * 6 the HCI PACKET INTERVAL should be 7.5ms for every usb packets.
+        * To maintain the rate we send 63bytes of usb packets alternatively for
+        * 7ms and 8ms to maintain the rate as 7.5ms.
+        */
+       if (data->usb_alt6_packet_flow) {
+               interval = 7;
+               data->usb_alt6_packet_flow = false;
+       } else {
+               interval = 6;
+               data->usb_alt6_packet_flow = true;
+       }
+
+       for (i = 0; i < interval; i++) {
+               urb->iso_frame_desc[i].offset = offset;
+               urb->iso_frame_desc[i].length = offset;
+       }
+
+       if (len && i < BTUSB_MAX_ISOC_FRAMES) {
+               urb->iso_frame_desc[i].offset = offset;
+               urb->iso_frame_desc[i].length = len;
+               i++;
+       }
+
+       urb->number_of_packets = i;
+}
+
 static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
 {
        int i, offset = 0;
@@ -1386,9 +1424,13 @@ static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb)
 
        urb->transfer_flags  = URB_ISO_ASAP;
 
-       __fill_isoc_descriptor(urb, skb->len,
-                              le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
-
+       if (data->isoc_altsetting == 6)
+               __fill_isoc_descriptor_msbc(urb, skb->len,
+                                           le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize),
+                                           data);
+       else
+               __fill_isoc_descriptor(urb, skb->len,
+                                      le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
        skb->dev = (void *)hdev;
 
        return urb;
@@ -1484,6 +1526,7 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
 
        if (hci_conn_num(hdev, SCO_LINK) != data->sco_num) {
                data->sco_num = hci_conn_num(hdev, SCO_LINK);
+               data->air_mode = evt;
                schedule_work(&data->work);
        }
 }
@@ -1531,11 +1574,70 @@ static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
        return 0;
 }
 
+static int btusb_switch_alt_setting(struct hci_dev *hdev, int new_alts)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       int err;
+
+       if (data->isoc_altsetting != new_alts) {
+               unsigned long flags;
+
+               clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+               usb_kill_anchored_urbs(&data->isoc_anchor);
+
+               /* When isochronous alternate setting needs to be
+                * changed, because SCO connection has been added
+                * or removed, a packet fragment may be left in the
+                * reassembling state. This could lead to wrongly
+                * assembled fragments.
+                *
+                * Clear outstanding fragment when selecting a new
+                * alternate setting.
+                */
+               spin_lock_irqsave(&data->rxlock, flags);
+               kfree_skb(data->sco_skb);
+               data->sco_skb = NULL;
+               spin_unlock_irqrestore(&data->rxlock, flags);
+
+               err = __set_isoc_interface(hdev, new_alts);
+               if (err < 0)
+                       return err;
+       }
+
+       if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+               if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
+                       clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+               else
+                       btusb_submit_isoc_urb(hdev, GFP_KERNEL);
+       }
+
+       return 0;
+}
+
+static struct usb_host_interface *btusb_find_altsetting(struct btusb_data *data,
+                                                       int alt)
+{
+       struct usb_interface *intf = data->isoc;
+       int i;
+
+       BT_DBG("Looking for Alt no :%d", alt);
+
+       if (!intf)
+               return NULL;
+
+       for (i = 0; i < intf->num_altsetting; i++) {
+               if (intf->altsetting[i].desc.bAlternateSetting == alt)
+                       return &intf->altsetting[i];
+       }
+
+       return NULL;
+}
+
 static void btusb_work(struct work_struct *work)
 {
        struct btusb_data *data = container_of(work, struct btusb_data, work);
        struct hci_dev *hdev = data->hdev;
-       int new_alts;
+       int new_alts = 0;
        int err;
 
        if (data->sco_num > 0) {
@@ -1550,44 +1652,27 @@ static void btusb_work(struct work_struct *work)
                        set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
                }
 
-               if (hdev->voice_setting & 0x0020) {
-                       static const int alts[3] = { 2, 4, 5 };
+               if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_CVSD) {
+                       if (hdev->voice_setting & 0x0020) {
+                               static const int alts[3] = { 2, 4, 5 };
 
-                       new_alts = alts[data->sco_num - 1];
-               } else {
-                       new_alts = data->sco_num;
-               }
-
-               if (data->isoc_altsetting != new_alts) {
-                       unsigned long flags;
-
-                       clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
-                       usb_kill_anchored_urbs(&data->isoc_anchor);
-
-                       /* When isochronous alternate setting needs to be
-                        * changed, because SCO connection has been added
-                        * or removed, a packet fragment may be left in the
-                        * reassembling state. This could lead to wrongly
-                        * assembled fragments.
-                        *
-                        * Clear outstanding fragment when selecting a new
-                        * alternate setting.
-                        */
-                       spin_lock_irqsave(&data->rxlock, flags);
-                       kfree_skb(data->sco_skb);
-                       data->sco_skb = NULL;
-                       spin_unlock_irqrestore(&data->rxlock, flags);
+                               new_alts = alts[data->sco_num - 1];
+                       } else {
+                               new_alts = data->sco_num;
+                       }
+               } else if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_TRANSP) {
 
-                       if (__set_isoc_interface(hdev, new_alts) < 0)
-                               return;
-               }
+                       data->usb_alt6_packet_flow = true;
 
-               if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
-                       if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
-                               clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+                       /* Check if Alt 6 is supported for Transparent audio */
+                       if (btusb_find_altsetting(data, 6))
+                               new_alts = 6;
                        else
-                               btusb_submit_isoc_urb(hdev, GFP_KERNEL);
+                               bt_dev_err(hdev, "Device does not support ALT setting 6");
                }
+
+               if (btusb_switch_alt_setting(hdev, new_alts) < 0)
+                       bt_dev_err(hdev, "set USB alt:(%d) failed!", new_alts);
        } else {
                clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
                usb_kill_anchored_urbs(&data->isoc_anchor);
@@ -2252,7 +2337,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
        if (ver.fw_variant == 0x23) {
                clear_bit(BTUSB_BOOTLOADER, &data->flags);
                btintel_check_bdaddr(hdev);
-               return 0;
+               goto finish;
        }
 
        /* If the device is not in bootloader mode, then the only possible
@@ -2452,6 +2537,23 @@ done:
         */
        btintel_load_ddc_config(hdev, fwname);
 
+       /* Read the Intel version information after loading the FW  */
+       err = btintel_read_version(hdev, &ver);
+       if (err)
+               return err;
+
+       btintel_version_info(hdev, &ver);
+
+finish:
+       /* All Intel controllers that support the Microsoft vendor
+        * extension are using 0xFC1E for VsMsftOpCode.
+        */
+       switch (ver.hw_variant) {
+       case 0x12:      /* ThP */
+               hci_set_msft_opcode(hdev, 0xFC1E);
+               break;
+       }
+
        /* Set the event mask for Intel specific vendor events. This enables
         * a few extra events that are useful during general operation. It
         * does not enable any debugging related events.
@@ -2461,13 +2563,6 @@ done:
         */
        btintel_set_event_mask(hdev, false);
 
-       /* Read the Intel version information after loading the FW  */
-       err = btintel_read_version(hdev, &ver);
-       if (err)
-               return err;
-
-       btintel_version_info(hdev, &ver);
-
        return 0;
 }
 
index b236cb1..19e4587 100644 (file)
@@ -118,6 +118,7 @@ struct bcm_device {
        u32                     oper_speed;
        int                     irq;
        bool                    irq_active_low;
+       bool                    irq_acquired;
 
 #ifdef CONFIG_PM
        struct hci_uart         *hu;
@@ -333,6 +334,8 @@ static int bcm_request_irq(struct bcm_data *bcm)
                goto unlock;
        }
 
+       bdev->irq_acquired = true;
+
        device_init_wakeup(bdev->dev, true);
 
        pm_runtime_set_autosuspend_delay(bdev->dev,
@@ -514,7 +517,7 @@ static int bcm_close(struct hci_uart *hu)
        }
 
        if (bdev) {
-               if (IS_ENABLED(CONFIG_PM) && bdev->irq > 0) {
+               if (IS_ENABLED(CONFIG_PM) && bdev->irq_acquired) {
                        devm_free_irq(bdev->dev, bdev->irq, bdev);
                        device_init_wakeup(bdev->dev, false);
                        pm_runtime_disable(bdev->dev);
@@ -1153,7 +1156,8 @@ static int bcm_of_probe(struct bcm_device *bdev)
        device_property_read_u8_array(bdev->dev, "brcm,bt-pcm-int-params",
                                      bdev->pcm_int_params, 5);
        bdev->irq = of_irq_get_byname(bdev->dev->of_node, "host-wakeup");
-
+       bdev->irq_active_low = irq_get_trigger_type(bdev->irq)
+                            & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW);
        return 0;
 }
 
index 439392b..d0ac554 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/acpi.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/serdev.h>
@@ -1596,7 +1597,7 @@ static int qca_setup(struct hci_uart *hu)
        set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
 
        bt_dev_info(hdev, "setting up %s",
-               qca_is_wcn399x(soc_type) ? "wcn399x" : "ROME");
+               qca_is_wcn399x(soc_type) ? "wcn399x" : "ROME/QCA6390");
 
 retry:
        ret = qca_power_on(hdev);
@@ -1665,10 +1666,10 @@ retry:
        }
 
        /* Setup bdaddr */
-       if (qca_is_wcn399x(soc_type))
-               hu->hdev->set_bdaddr = qca_set_bdaddr;
-       else
+       if (soc_type == QCA_ROME)
                hu->hdev->set_bdaddr = qca_set_bdaddr_rome;
+       else
+               hu->hdev->set_bdaddr = qca_set_bdaddr;
 
        return ret;
 }
@@ -1721,6 +1722,11 @@ static const struct qca_vreg_data qca_soc_data_wcn3998 = {
        .num_vregs = 4,
 };
 
+static const struct qca_vreg_data qca_soc_data_qca6390 = {
+       .soc_type = QCA_QCA6390,
+       .num_vregs = 0,
+};
+
 static void qca_power_shutdown(struct hci_uart *hu)
 {
        struct qca_serdev *qcadev;
@@ -1764,7 +1770,7 @@ static int qca_power_off(struct hci_dev *hdev)
        enum qca_btsoc_type soc_type = qca_soc_type(hu);
 
        /* Stop sending shutdown command if soc crashes. */
-       if (qca_is_wcn399x(soc_type)
+       if (soc_type != QCA_ROME
                && qca->memdump_state == QCA_MEMDUMP_IDLE) {
                qca_send_pre_shutdown_cmd(hdev);
                usleep_range(8000, 10000);
@@ -1900,7 +1906,11 @@ static int qca_serdev_probe(struct serdev_device *serdev)
                        return err;
                }
        } else {
-               qcadev->btsoc_type = QCA_ROME;
+               if (data)
+                       qcadev->btsoc_type = data->soc_type;
+               else
+                       qcadev->btsoc_type = QCA_ROME;
+
                qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable",
                                               GPIOD_OUT_LOW);
                if (!qcadev->bt_en) {
@@ -2044,21 +2054,37 @@ static int __maybe_unused qca_resume(struct device *dev)
 
 static SIMPLE_DEV_PM_OPS(qca_pm_ops, qca_suspend, qca_resume);
 
+#ifdef CONFIG_OF
 static const struct of_device_id qca_bluetooth_of_match[] = {
        { .compatible = "qcom,qca6174-bt" },
+       { .compatible = "qcom,qca6390-bt", .data = &qca_soc_data_qca6390},
        { .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990},
        { .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991},
        { .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998},
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id qca_bluetooth_acpi_match[] = {
+       { "QCOM6390", (kernel_ulong_t)&qca_soc_data_qca6390 },
+       { "DLA16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
+       { "DLB16390", (kernel_ulong_t)&qca_soc_data_qca6390 },
+       { "DLB26390", (kernel_ulong_t)&qca_soc_data_qca6390 },
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match);
+#endif
+
 
 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,
+               .of_match_table = of_match_ptr(qca_bluetooth_of_match),
+               .acpi_match_table = ACPI_PTR(qca_bluetooth_acpi_match),
                .pm = &qca_pm_ops,
        },
 };
index faca0f3..e3bbe10 100644 (file)
@@ -3631,7 +3631,7 @@ static void cdrom_update_settings(void)
 }
 
 static int cdrom_sysctl_handler(struct ctl_table *ctl, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
        
index 718d8c0..79a6e47 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/virtio.h>
 #include <linux/virtio_rng.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 
 static DEFINE_IDA(rng_index_ida);
 
index 0d10e31..1e0db78 100644 (file)
@@ -2057,7 +2057,7 @@ static char sysctl_bootid[16];
  * sysctl system call, as 16 bytes of binary data.
  */
 static int proc_do_uuid(struct ctl_table *table, int write,
-                       void __user *buffer, size_t *lenp, loff_t *ppos)
+                       void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table fake_table;
        unsigned char buf[64], tmp_uuid[16], *uuid;
index a438b12..1621ce8 100644 (file)
@@ -323,7 +323,7 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 
        for (i = 0; i < chip->nr_allocated_banks; i++) {
                if (digests[i].alg_id != chip->allocated_banks[i].alg_id) {
-                       rc = EINVAL;
+                       rc = -EINVAL;
                        goto out;
                }
        }
index 76f67b1..eff1f12 100644 (file)
@@ -681,6 +681,7 @@ out:
                rc = -ENODEV;
        return rc;
 }
+EXPORT_SYMBOL_GPL(tpm2_get_cc_attrs_tbl);
 
 /**
  * tpm2_startup - turn on the TPM
index 1a49db9..09fe452 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (C) 2012 IBM Corporation
+ * Copyright (C) 2012-2020 IBM Corporation
  *
  * Author: Ashley Lai <ashleydlai@gmail.com>
  *
@@ -134,6 +134,64 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
        return len;
 }
 
+/**
+ * ibmvtpm_crq_send_init - Send a CRQ initialize message
+ * @ibmvtpm:   vtpm device struct
+ *
+ * Return:
+ *     0 on success.
+ *     Non-zero on failure.
+ */
+static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
+{
+       int rc;
+
+       rc = ibmvtpm_send_crq_word(ibmvtpm->vdev, INIT_CRQ_CMD);
+       if (rc != H_SUCCESS)
+               dev_err(ibmvtpm->dev,
+                       "%s failed rc=%d\n", __func__, rc);
+
+       return rc;
+}
+
+/**
+ * tpm_ibmvtpm_resume - Resume from suspend
+ *
+ * @dev:       device struct
+ *
+ * Return: Always 0.
+ */
+static int tpm_ibmvtpm_resume(struct device *dev)
+{
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+       struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
+       int rc = 0;
+
+       do {
+               if (rc)
+                       msleep(100);
+               rc = plpar_hcall_norets(H_ENABLE_CRQ,
+                                       ibmvtpm->vdev->unit_address);
+       } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+       if (rc) {
+               dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc);
+               return rc;
+       }
+
+       rc = vio_enable_interrupts(ibmvtpm->vdev);
+       if (rc) {
+               dev_err(dev, "Error vio_enable_interrupts rc=%d\n", rc);
+               return rc;
+       }
+
+       rc = ibmvtpm_crq_send_init(ibmvtpm);
+       if (rc)
+               dev_err(dev, "Error send_init rc=%d\n", rc);
+
+       return rc;
+}
+
 /**
  * tpm_ibmvtpm_send() - Send a TPM command
  * @chip:      tpm chip struct
@@ -147,6 +205,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
 static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
 {
        struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
+       bool retry = true;
        int rc, sig;
 
        if (!ibmvtpm->rtce_buf) {
@@ -180,18 +239,27 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
         */
        ibmvtpm->tpm_processing_cmd = true;
 
+again:
        rc = ibmvtpm_send_crq(ibmvtpm->vdev,
                        IBMVTPM_VALID_CMD, VTPM_TPM_COMMAND,
                        count, ibmvtpm->rtce_dma_handle);
        if (rc != H_SUCCESS) {
+               /*
+                * H_CLOSED can be returned after LPM resume.  Call
+                * tpm_ibmvtpm_resume() to re-enable the CRQ then retry
+                * ibmvtpm_send_crq() once before failing.
+                */
+               if (rc == H_CLOSED && retry) {
+                       tpm_ibmvtpm_resume(ibmvtpm->dev);
+                       retry = false;
+                       goto again;
+               }
                dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc);
-               rc = 0;
                ibmvtpm->tpm_processing_cmd = false;
-       } else
-               rc = 0;
+       }
 
        spin_unlock(&ibmvtpm->rtce_lock);
-       return rc;
+       return 0;
 }
 
 static void tpm_ibmvtpm_cancel(struct tpm_chip *chip)
@@ -269,26 +337,6 @@ static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm)
        return rc;
 }
 
-/**
- * ibmvtpm_crq_send_init - Send a CRQ initialize message
- * @ibmvtpm:   vtpm device struct
- *
- * Return:
- *     0 on success.
- *     Non-zero on failure.
- */
-static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
-{
-       int rc;
-
-       rc = ibmvtpm_send_crq_word(ibmvtpm->vdev, INIT_CRQ_CMD);
-       if (rc != H_SUCCESS)
-               dev_err(ibmvtpm->dev,
-                       "ibmvtpm_crq_send_init failed rc=%d\n", rc);
-
-       return rc;
-}
-
 /**
  * tpm_ibmvtpm_remove - ibm vtpm remove entry point
  * @vdev:      vio device struct
@@ -401,44 +449,6 @@ static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm)
                                  ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE);
 }
 
-/**
- * tpm_ibmvtpm_resume - Resume from suspend
- *
- * @dev:       device struct
- *
- * Return: Always 0.
- */
-static int tpm_ibmvtpm_resume(struct device *dev)
-{
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
-       int rc = 0;
-
-       do {
-               if (rc)
-                       msleep(100);
-               rc = plpar_hcall_norets(H_ENABLE_CRQ,
-                                       ibmvtpm->vdev->unit_address);
-       } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
-
-       if (rc) {
-               dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc);
-               return rc;
-       }
-
-       rc = vio_enable_interrupts(ibmvtpm->vdev);
-       if (rc) {
-               dev_err(dev, "Error vio_enable_interrupts rc=%d\n", rc);
-               return rc;
-       }
-
-       rc = ibmvtpm_crq_send_init(ibmvtpm);
-       if (rc)
-               dev_err(dev, "Error send_init rc=%d\n", rc);
-
-       return rc;
-}
-
 static bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status)
 {
        return (status == 0);
index 27c6ca0..2435216 100644 (file)
@@ -433,6 +433,9 @@ static void disable_interrupts(struct tpm_chip *chip)
        u32 intmask;
        int rc;
 
+       if (priv->irq == 0)
+               return;
+
        rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
        if (rc < 0)
                intmask = 0;
@@ -1062,9 +1065,12 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
                if (irq) {
                        tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED,
                                                 irq);
-                       if (!(chip->flags & TPM_CHIP_FLAG_IRQ))
+                       if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) {
                                dev_err(&chip->dev, FW_BUG
                                        "TPM interrupt not working, polling instead\n");
+
+                               disable_interrupts(chip);
+                       }
                } else {
                        tpm_tis_probe_irq(chip, intmask);
                }
index 536b59a..bacebd4 100644 (file)
@@ -276,7 +276,7 @@ static void __init asm9260_acc_init(struct device_node *np)
 
        /* TODO: Convert to DT parent scheme */
        ref_clk = of_clk_get_parent_name(np, 0);
-       hw = __clk_hw_register_fixed_rate_with_accuracy(NULL, NULL, pll_clk,
+       hw = __clk_hw_register_fixed_rate(NULL, NULL, pll_clk,
                        ref_clk, NULL, NULL, 0, rate, 0,
                        CLK_FIXED_RATE_PARENT_ACCURACY);
 
index 7077be2..962014c 100644 (file)
@@ -97,7 +97,7 @@ static const struct clk_ops mmp_clk_pll_ops = {
        .recalc_rate = mmp_clk_pll_recalc_rate,
 };
 
-struct clk *mmp_clk_register_pll(char *name,
+static struct clk *mmp_clk_register_pll(char *name,
                        unsigned long default_rate,
                        void __iomem *enable_reg, u32 enable,
                        void __iomem *reg, u8 shift,
@@ -137,3 +137,34 @@ struct clk *mmp_clk_register_pll(char *name,
 
        return clk;
 }
+
+void mmp_register_pll_clks(struct mmp_clk_unit *unit,
+                       struct mmp_param_pll_clk *clks,
+                       void __iomem *base, int size)
+{
+       struct clk *clk;
+       int i;
+
+       for (i = 0; i < size; i++) {
+               void __iomem *reg = NULL;
+
+               if (clks[i].offset)
+                       reg = base + clks[i].offset;
+
+               clk = mmp_clk_register_pll(clks[i].name,
+                                       clks[i].default_rate,
+                                       base + clks[i].enable_offset,
+                                       clks[i].enable,
+                                       reg, clks[i].shift,
+                                       clks[i].input_rate,
+                                       base + clks[i].postdiv_offset,
+                                       clks[i].postdiv_shift);
+               if (IS_ERR(clk)) {
+                       pr_err("%s: failed to register clock %s\n",
+                              __func__, clks[i].name);
+                       continue;
+               }
+               if (clks[i].id)
+                       unit->clk_table[clks[i].id] = clk;
+       }
+}
index 3171236..ca7d37e 100644 (file)
@@ -176,37 +176,6 @@ void mmp_register_div_clks(struct mmp_clk_unit *unit,
        }
 }
 
-void mmp_register_pll_clks(struct mmp_clk_unit *unit,
-                       struct mmp_param_pll_clk *clks,
-                       void __iomem *base, int size)
-{
-       struct clk *clk;
-       int i;
-
-       for (i = 0; i < size; i++) {
-               void __iomem *reg = NULL;
-
-               if (clks[i].offset)
-                       reg = base + clks[i].offset;
-
-               clk = mmp_clk_register_pll(clks[i].name,
-                                       clks[i].default_rate,
-                                       base + clks[i].enable_offset,
-                                       clks[i].enable,
-                                       reg, clks[i].shift,
-                                       clks[i].input_rate,
-                                       base + clks[i].postdiv_offset,
-                                       clks[i].postdiv_shift);
-               if (IS_ERR(clk)) {
-                       pr_err("%s: failed to register clock %s\n",
-                              __func__, clks[i].name);
-                       continue;
-               }
-               if (clks[i].id)
-                       unit->clk_table[clks[i].id] = clk;
-       }
-}
-
 void mmp_clk_add(struct mmp_clk_unit *unit, unsigned int id,
                        struct clk *clk)
 {
index 971b4d6..20dc1e5 100644 (file)
@@ -238,13 +238,6 @@ void mmp_register_pll_clks(struct mmp_clk_unit *unit,
                        struct mmp_param_pll_clk *clks,
                        void __iomem *base, int size);
 
-extern struct clk *mmp_clk_register_pll(char *name,
-                       unsigned long default_rate,
-                       void __iomem *enable_reg, u32 enable,
-                       void __iomem *reg, u8 shift,
-                       unsigned long input_rate,
-                       void __iomem *postdiv_reg, u8 postdiv_shift);
-
 #define DEFINE_MIX_REG_INFO(w_d, s_d, w_m, s_m, fc)    \
 {                                                      \
        .width_div = (w_d),                             \
index a0631f7..2e2dfb2 100644 (file)
@@ -1641,8 +1641,9 @@ static SPRD_SC_GATE_CLK_FW_NAME(i2c4_eb,  "i2c4-eb",      "ext-26m", 0x0,
                                0x1000, BIT(12), 0, 0);
 static SPRD_SC_GATE_CLK_FW_NAME(uart0_eb,      "uart0-eb",     "ext-26m", 0x0,
                                0x1000, BIT(13), 0, 0);
+/* uart1_eb is for console, don't gate even if unused */
 static SPRD_SC_GATE_CLK_FW_NAME(uart1_eb,      "uart1-eb",     "ext-26m", 0x0,
-                               0x1000, BIT(14), 0, 0);
+                               0x1000, BIT(14), CLK_IGNORE_UNUSED, 0);
 static SPRD_SC_GATE_CLK_FW_NAME(uart2_eb,      "uart2-eb",     "ext-26m", 0x0,
                                0x1000, BIT(15), 0, 0);
 static SPRD_SC_GATE_CLK_FW_NAME(uart3_eb,      "uart3-eb",     "ext-26m", 0x0,
index 9dab190..aa13708 100644 (file)
@@ -44,6 +44,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
  * @base:              base port address of the IIO device
  */
 struct quad8_iio {
+       struct mutex lock;
        struct counter_device counter;
        unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
        unsigned int preset[QUAD8_NUM_COUNTERS];
@@ -123,6 +124,8 @@ static int quad8_read_raw(struct iio_dev *indio_dev,
                /* Borrow XOR Carry effectively doubles count range */
                *val = (borrow ^ carry) << 24;
 
+               mutex_lock(&priv->lock);
+
                /* Reset Byte Pointer; transfer Counter to Output Latch */
                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
                     base_offset + 1);
@@ -130,6 +133,8 @@ static int quad8_read_raw(struct iio_dev *indio_dev,
                for (i = 0; i < 3; i++)
                        *val |= (unsigned int)inb(base_offset) << (8 * i);
 
+               mutex_unlock(&priv->lock);
+
                return IIO_VAL_INT;
        case IIO_CHAN_INFO_ENABLE:
                *val = priv->ab_enable[chan->channel];
@@ -160,6 +165,8 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
                if ((unsigned int)val > 0xFFFFFF)
                        return -EINVAL;
 
+               mutex_lock(&priv->lock);
+
                /* Reset Byte Pointer */
                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
 
@@ -183,12 +190,16 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
                /* Reset Error flag */
                outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
 
+               mutex_unlock(&priv->lock);
+
                return 0;
        case IIO_CHAN_INFO_ENABLE:
                /* only boolean values accepted */
                if (val < 0 || val > 1)
                        return -EINVAL;
 
+               mutex_lock(&priv->lock);
+
                priv->ab_enable[chan->channel] = val;
 
                ior_cfg = val | priv->preset_enable[chan->channel] << 1;
@@ -196,11 +207,18 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
                /* Load I/O control configuration */
                outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
 
+               mutex_unlock(&priv->lock);
+
                return 0;
        case IIO_CHAN_INFO_SCALE:
+               mutex_lock(&priv->lock);
+
                /* Quadrature scaling only available in quadrature mode */
-               if (!priv->quadrature_mode[chan->channel] && (val2 || val != 1))
+               if (!priv->quadrature_mode[chan->channel] &&
+                               (val2 || val != 1)) {
+                       mutex_unlock(&priv->lock);
                        return -EINVAL;
+               }
 
                /* Only three gain states (1, 0.5, 0.25) */
                if (val == 1 && !val2)
@@ -214,11 +232,15 @@ static int quad8_write_raw(struct iio_dev *indio_dev,
                                priv->quadrature_scale[chan->channel] = 2;
                                break;
                        default:
+                               mutex_unlock(&priv->lock);
                                return -EINVAL;
                        }
-               else
+               else {
+                       mutex_unlock(&priv->lock);
                        return -EINVAL;
+               }
 
+               mutex_unlock(&priv->lock);
                return 0;
        }
 
@@ -255,6 +277,8 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
        if (preset > 0xFFFFFF)
                return -EINVAL;
 
+       mutex_lock(&priv->lock);
+
        priv->preset[chan->channel] = preset;
 
        /* Reset Byte Pointer */
@@ -264,6 +288,8 @@ static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
        for (i = 0; i < 3; i++)
                outb(preset >> (8 * i), base_offset);
 
+       mutex_unlock(&priv->lock);
+
        return len;
 }
 
@@ -293,6 +319,8 @@ static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
        /* Preset enable is active low in Input/Output Control register */
        preset_enable = !preset_enable;
 
+       mutex_lock(&priv->lock);
+
        priv->preset_enable[chan->channel] = preset_enable;
 
        ior_cfg = priv->ab_enable[chan->channel] |
@@ -301,6 +329,8 @@ static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
        /* Load I/O control configuration to Input / Output Control Register */
        outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
 
+       mutex_unlock(&priv->lock);
+
        return len;
 }
 
@@ -358,6 +388,8 @@ static int quad8_set_count_mode(struct iio_dev *indio_dev,
        unsigned int mode_cfg = cnt_mode << 1;
        const int base_offset = priv->base + 2 * chan->channel + 1;
 
+       mutex_lock(&priv->lock);
+
        priv->count_mode[chan->channel] = cnt_mode;
 
        /* Add quadrature mode configuration */
@@ -367,6 +399,8 @@ static int quad8_set_count_mode(struct iio_dev *indio_dev,
        /* Load mode configuration to Counter Mode Register */
        outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
+       mutex_unlock(&priv->lock);
+
        return 0;
 }
 
@@ -394,19 +428,26 @@ static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
        const struct iio_chan_spec *chan, unsigned int synchronous_mode)
 {
        struct quad8_iio *const priv = iio_priv(indio_dev);
-       const unsigned int idr_cfg = synchronous_mode |
-               priv->index_polarity[chan->channel] << 1;
        const int base_offset = priv->base + 2 * chan->channel + 1;
+       unsigned int idr_cfg = synchronous_mode;
+
+       mutex_lock(&priv->lock);
+
+       idr_cfg |= priv->index_polarity[chan->channel] << 1;
 
        /* Index function must be non-synchronous in non-quadrature mode */
-       if (synchronous_mode && !priv->quadrature_mode[chan->channel])
+       if (synchronous_mode && !priv->quadrature_mode[chan->channel]) {
+               mutex_unlock(&priv->lock);
                return -EINVAL;
+       }
 
        priv->synchronous_mode[chan->channel] = synchronous_mode;
 
        /* Load Index Control configuration to Index Control Register */
        outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
+       mutex_unlock(&priv->lock);
+
        return 0;
 }
 
@@ -434,8 +475,12 @@ static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
        const struct iio_chan_spec *chan, unsigned int quadrature_mode)
 {
        struct quad8_iio *const priv = iio_priv(indio_dev);
-       unsigned int mode_cfg = priv->count_mode[chan->channel] << 1;
        const int base_offset = priv->base + 2 * chan->channel + 1;
+       unsigned int mode_cfg;
+
+       mutex_lock(&priv->lock);
+
+       mode_cfg = priv->count_mode[chan->channel] << 1;
 
        if (quadrature_mode)
                mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
@@ -453,6 +498,8 @@ static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
        /* Load mode configuration to Counter Mode Register */
        outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
+       mutex_unlock(&priv->lock);
+
        return 0;
 }
 
@@ -480,15 +527,20 @@ static int quad8_set_index_polarity(struct iio_dev *indio_dev,
        const struct iio_chan_spec *chan, unsigned int index_polarity)
 {
        struct quad8_iio *const priv = iio_priv(indio_dev);
-       const unsigned int idr_cfg = priv->synchronous_mode[chan->channel] |
-               index_polarity << 1;
        const int base_offset = priv->base + 2 * chan->channel + 1;
+       unsigned int idr_cfg = index_polarity << 1;
+
+       mutex_lock(&priv->lock);
+
+       idr_cfg |= priv->synchronous_mode[chan->channel];
 
        priv->index_polarity[chan->channel] = index_polarity;
 
        /* Load Index Control configuration to Index Control Register */
        outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
+       mutex_unlock(&priv->lock);
+
        return 0;
 }
 
@@ -589,7 +641,7 @@ static int quad8_signal_read(struct counter_device *counter,
 static int quad8_count_read(struct counter_device *counter,
        struct counter_count *count, unsigned long *val)
 {
-       const struct quad8_iio *const priv = counter->priv;
+       struct quad8_iio *const priv = counter->priv;
        const int base_offset = priv->base + 2 * count->id;
        unsigned int flags;
        unsigned int borrow;
@@ -603,6 +655,8 @@ static int quad8_count_read(struct counter_device *counter,
        /* Borrow XOR Carry effectively doubles count range */
        *val = (unsigned long)(borrow ^ carry) << 24;
 
+       mutex_lock(&priv->lock);
+
        /* Reset Byte Pointer; transfer Counter to Output Latch */
        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
             base_offset + 1);
@@ -610,13 +664,15 @@ static int quad8_count_read(struct counter_device *counter,
        for (i = 0; i < 3; i++)
                *val |= (unsigned long)inb(base_offset) << (8 * i);
 
+       mutex_unlock(&priv->lock);
+
        return 0;
 }
 
 static int quad8_count_write(struct counter_device *counter,
        struct counter_count *count, unsigned long val)
 {
-       const struct quad8_iio *const priv = counter->priv;
+       struct quad8_iio *const priv = counter->priv;
        const int base_offset = priv->base + 2 * count->id;
        int i;
 
@@ -624,6 +680,8 @@ static int quad8_count_write(struct counter_device *counter,
        if (val > 0xFFFFFF)
                return -EINVAL;
 
+       mutex_lock(&priv->lock);
+
        /* Reset Byte Pointer */
        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
 
@@ -647,6 +705,8 @@ static int quad8_count_write(struct counter_device *counter,
        /* Reset Error flag */
        outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
 
+       mutex_unlock(&priv->lock);
+
        return 0;
 }
 
@@ -667,13 +727,13 @@ static enum counter_count_function quad8_count_functions_list[] = {
 static int quad8_function_get(struct counter_device *counter,
        struct counter_count *count, size_t *function)
 {
-       const struct quad8_iio *const priv = counter->priv;
+       struct quad8_iio *const priv = counter->priv;
        const int id = count->id;
-       const unsigned int quadrature_mode = priv->quadrature_mode[id];
-       const unsigned int scale = priv->quadrature_scale[id];
 
-       if (quadrature_mode)
-               switch (scale) {
+       mutex_lock(&priv->lock);
+
+       if (priv->quadrature_mode[id])
+               switch (priv->quadrature_scale[id]) {
                case 0:
                        *function = QUAD8_COUNT_FUNCTION_QUADRATURE_X1;
                        break;
@@ -687,6 +747,8 @@ static int quad8_function_get(struct counter_device *counter,
        else
                *function = QUAD8_COUNT_FUNCTION_PULSE_DIRECTION;
 
+       mutex_unlock(&priv->lock);
+
        return 0;
 }
 
@@ -697,10 +759,15 @@ static int quad8_function_set(struct counter_device *counter,
        const int id = count->id;
        unsigned int *const quadrature_mode = priv->quadrature_mode + id;
        unsigned int *const scale = priv->quadrature_scale + id;
-       unsigned int mode_cfg = priv->count_mode[id] << 1;
        unsigned int *const synchronous_mode = priv->synchronous_mode + id;
-       const unsigned int idr_cfg = priv->index_polarity[id] << 1;
        const int base_offset = priv->base + 2 * id + 1;
+       unsigned int mode_cfg;
+       unsigned int idr_cfg;
+
+       mutex_lock(&priv->lock);
+
+       mode_cfg = priv->count_mode[id] << 1;
+       idr_cfg = priv->index_polarity[id] << 1;
 
        if (function == QUAD8_COUNT_FUNCTION_PULSE_DIRECTION) {
                *quadrature_mode = 0;
@@ -736,6 +803,8 @@ static int quad8_function_set(struct counter_device *counter,
        /* Load mode configuration to Counter Mode Register */
        outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
+       mutex_unlock(&priv->lock);
+
        return 0;
 }
 
@@ -852,15 +921,20 @@ static int quad8_index_polarity_set(struct counter_device *counter,
 {
        struct quad8_iio *const priv = counter->priv;
        const size_t channel_id = signal->id - 16;
-       const unsigned int idr_cfg = priv->synchronous_mode[channel_id] |
-               index_polarity << 1;
        const int base_offset = priv->base + 2 * channel_id + 1;
+       unsigned int idr_cfg = index_polarity << 1;
+
+       mutex_lock(&priv->lock);
+
+       idr_cfg |= priv->synchronous_mode[channel_id];
 
        priv->index_polarity[channel_id] = index_polarity;
 
        /* Load Index Control configuration to Index Control Register */
        outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
+       mutex_unlock(&priv->lock);
+
        return 0;
 }
 
@@ -887,19 +961,26 @@ static int quad8_synchronous_mode_set(struct counter_device *counter,
 {
        struct quad8_iio *const priv = counter->priv;
        const size_t channel_id = signal->id - 16;
-       const unsigned int idr_cfg = synchronous_mode |
-               priv->index_polarity[channel_id] << 1;
        const int base_offset = priv->base + 2 * channel_id + 1;
+       unsigned int idr_cfg = synchronous_mode;
+
+       mutex_lock(&priv->lock);
+
+       idr_cfg |= priv->index_polarity[channel_id] << 1;
 
        /* Index function must be non-synchronous in non-quadrature mode */
-       if (synchronous_mode && !priv->quadrature_mode[channel_id])
+       if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
+               mutex_unlock(&priv->lock);
                return -EINVAL;
+       }
 
        priv->synchronous_mode[channel_id] = synchronous_mode;
 
        /* Load Index Control configuration to Index Control Register */
        outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
 
+       mutex_unlock(&priv->lock);
+
        return 0;
 }
 
@@ -964,6 +1045,8 @@ static int quad8_count_mode_set(struct counter_device *counter,
                break;
        }
 
+       mutex_lock(&priv->lock);
+
        priv->count_mode[count->id] = cnt_mode;
 
        /* Set count mode configuration value */
@@ -976,6 +1059,8 @@ static int quad8_count_mode_set(struct counter_device *counter,
        /* Load mode configuration to Counter Mode Register */
        outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
 
+       mutex_unlock(&priv->lock);
+
        return 0;
 }
 
@@ -1017,6 +1102,8 @@ static ssize_t quad8_count_enable_write(struct counter_device *counter,
        if (err)
                return err;
 
+       mutex_lock(&priv->lock);
+
        priv->ab_enable[count->id] = ab_enable;
 
        ior_cfg = ab_enable | priv->preset_enable[count->id] << 1;
@@ -1024,6 +1111,8 @@ static ssize_t quad8_count_enable_write(struct counter_device *counter,
        /* Load I/O control configuration */
        outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
 
+       mutex_unlock(&priv->lock);
+
        return len;
 }
 
@@ -1052,14 +1141,28 @@ static ssize_t quad8_count_preset_read(struct counter_device *counter,
        return sprintf(buf, "%u\n", priv->preset[count->id]);
 }
 
+static void quad8_preset_register_set(struct quad8_iio *quad8iio, int id,
+               unsigned int preset)
+{
+       const unsigned int base_offset = quad8iio->base + 2 * id;
+       int i;
+
+       quad8iio->preset[id] = preset;
+
+       /* Reset Byte Pointer */
+       outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
+
+       /* Set Preset Register */
+       for (i = 0; i < 3; i++)
+               outb(preset >> (8 * i), base_offset);
+}
+
 static ssize_t quad8_count_preset_write(struct counter_device *counter,
        struct counter_count *count, void *private, const char *buf, size_t len)
 {
        struct quad8_iio *const priv = counter->priv;
-       const int base_offset = priv->base + 2 * count->id;
        unsigned int preset;
        int ret;
-       int i;
 
        ret = kstrtouint(buf, 0, &preset);
        if (ret)
@@ -1069,14 +1172,11 @@ static ssize_t quad8_count_preset_write(struct counter_device *counter,
        if (preset > 0xFFFFFF)
                return -EINVAL;
 
-       priv->preset[count->id] = preset;
+       mutex_lock(&priv->lock);
 
-       /* Reset Byte Pointer */
-       outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
+       quad8_preset_register_set(priv, count->id, preset);
 
-       /* Set Preset Register */
-       for (i = 0; i < 3; i++)
-               outb(preset >> (8 * i), base_offset);
+       mutex_unlock(&priv->lock);
 
        return len;
 }
@@ -1084,15 +1184,20 @@ static ssize_t quad8_count_preset_write(struct counter_device *counter,
 static ssize_t quad8_count_ceiling_read(struct counter_device *counter,
        struct counter_count *count, void *private, char *buf)
 {
-       const struct quad8_iio *const priv = counter->priv;
+       struct quad8_iio *const priv = counter->priv;
+
+       mutex_lock(&priv->lock);
 
        /* Range Limit and Modulo-N count modes use preset value as ceiling */
        switch (priv->count_mode[count->id]) {
        case 1:
        case 3:
-               return quad8_count_preset_read(counter, count, private, buf);
+               mutex_unlock(&priv->lock);
+               return sprintf(buf, "%u\n", priv->preset[count->id]);
        }
 
+       mutex_unlock(&priv->lock);
+
        /* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
        return sprintf(buf, "33554431\n");
 }
@@ -1101,15 +1206,29 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
        struct counter_count *count, void *private, const char *buf, size_t len)
 {
        struct quad8_iio *const priv = counter->priv;
+       unsigned int ceiling;
+       int ret;
+
+       ret = kstrtouint(buf, 0, &ceiling);
+       if (ret)
+               return ret;
+
+       /* Only 24-bit values are supported */
+       if (ceiling > 0xFFFFFF)
+               return -EINVAL;
+
+       mutex_lock(&priv->lock);
 
        /* Range Limit and Modulo-N count modes use preset value as ceiling */
        switch (priv->count_mode[count->id]) {
        case 1:
        case 3:
-               return quad8_count_preset_write(counter, count, private, buf,
-                                               len);
+               quad8_preset_register_set(priv, count->id, ceiling);
+               break;
        }
 
+       mutex_unlock(&priv->lock);
+
        return len;
 }
 
@@ -1137,6 +1256,8 @@ static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
        /* Preset enable is active low in Input/Output Control register */
        preset_enable = !preset_enable;
 
+       mutex_lock(&priv->lock);
+
        priv->preset_enable[count->id] = preset_enable;
 
        ior_cfg = priv->ab_enable[count->id] | (unsigned int)preset_enable << 1;
@@ -1144,6 +1265,8 @@ static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
        /* Load I/O control configuration to Input / Output Control Register */
        outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
 
+       mutex_unlock(&priv->lock);
+
        return len;
 }
 
@@ -1429,6 +1552,9 @@ static int quad8_probe(struct device *dev, unsigned int id)
        quad8iio->counter.priv = quad8iio;
        quad8iio->base = base[id];
 
+       /* Initialize mutex */
+       mutex_init(&quad8iio->lock);
+
        /* Reset all counters and disable interrupt function */
        outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
        /* Set initial configuration for all counters */
index 4d1e25d..4d3429b 100644 (file)
@@ -1059,7 +1059,7 @@ static ssize_t store_no_turbo(struct kobject *a, struct kobj_attribute *b,
 
        update_turbo_state();
        if (global.turbo_disabled) {
-               pr_warn("Turbo disabled by BIOS or unavailable on processor\n");
+               pr_notice_once("Turbo disabled by BIOS or unavailable on processor\n");
                mutex_unlock(&intel_pstate_limits_lock);
                mutex_unlock(&intel_pstate_driver_lock);
                return -EPERM;
index b7bb7c3..b2f9882 100644 (file)
@@ -963,10 +963,12 @@ static void aead_crypt_done(struct device *jrdev, u32 *desc, u32 err,
        struct caam_drv_private_jr *jrp = dev_get_drvdata(jrdev);
        struct aead_edesc *edesc;
        int ecode = 0;
+       bool has_bklog;
 
        dev_dbg(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
 
        edesc = rctx->edesc;
+       has_bklog = edesc->bklog;
 
        if (err)
                ecode = caam_jr_strstatus(jrdev, err);
@@ -979,7 +981,7 @@ static void aead_crypt_done(struct device *jrdev, u32 *desc, u32 err,
         * If no backlog flag, the completion of the request is done
         * by CAAM, not crypto engine.
         */
-       if (!edesc->bklog)
+       if (!has_bklog)
                aead_request_complete(req, ecode);
        else
                crypto_finalize_aead_request(jrp->engine, req, ecode);
@@ -995,10 +997,12 @@ static void skcipher_crypt_done(struct device *jrdev, u32 *desc, u32 err,
        struct caam_drv_private_jr *jrp = dev_get_drvdata(jrdev);
        int ivsize = crypto_skcipher_ivsize(skcipher);
        int ecode = 0;
+       bool has_bklog;
 
        dev_dbg(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
 
        edesc = rctx->edesc;
+       has_bklog = edesc->bklog;
        if (err)
                ecode = caam_jr_strstatus(jrdev, err);
 
@@ -1028,7 +1032,7 @@ static void skcipher_crypt_done(struct device *jrdev, u32 *desc, u32 err,
         * If no backlog flag, the completion of the request is done
         * by CAAM, not crypto engine.
         */
-       if (!edesc->bklog)
+       if (!has_bklog)
                skcipher_request_complete(req, ecode);
        else
                crypto_finalize_skcipher_request(jrp->engine, req, ecode);
@@ -1711,7 +1715,7 @@ static struct skcipher_edesc *skcipher_edesc_alloc(struct skcipher_request *req,
 
        if (ivsize || mapped_dst_nents > 1)
                sg_to_sec4_set_last(edesc->sec4_sg + dst_sg_idx +
-                                   mapped_dst_nents);
+                                   mapped_dst_nents - 1 + !!ivsize);
 
        if (sec4_sg_bytes) {
                edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg,
index 943bc02..27ff4a3 100644 (file)
@@ -583,10 +583,12 @@ static inline void ahash_done_cpy(struct device *jrdev, u32 *desc, u32 err,
        struct caam_hash_state *state = ahash_request_ctx(req);
        struct caam_hash_ctx *ctx = crypto_ahash_ctx(ahash);
        int ecode = 0;
+       bool has_bklog;
 
        dev_dbg(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
 
        edesc = state->edesc;
+       has_bklog = edesc->bklog;
 
        if (err)
                ecode = caam_jr_strstatus(jrdev, err);
@@ -603,7 +605,7 @@ static inline void ahash_done_cpy(struct device *jrdev, u32 *desc, u32 err,
         * If no backlog flag, the completion of the request is done
         * by CAAM, not crypto engine.
         */
-       if (!edesc->bklog)
+       if (!has_bklog)
                req->base.complete(&req->base, ecode);
        else
                crypto_finalize_hash_request(jrp->engine, req, ecode);
@@ -632,10 +634,12 @@ static inline void ahash_done_switch(struct device *jrdev, u32 *desc, u32 err,
        struct caam_hash_state *state = ahash_request_ctx(req);
        int digestsize = crypto_ahash_digestsize(ahash);
        int ecode = 0;
+       bool has_bklog;
 
        dev_dbg(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
 
        edesc = state->edesc;
+       has_bklog = edesc->bklog;
        if (err)
                ecode = caam_jr_strstatus(jrdev, err);
 
@@ -663,7 +667,7 @@ static inline void ahash_done_switch(struct device *jrdev, u32 *desc, u32 err,
         * If no backlog flag, the completion of the request is done
         * by CAAM, not crypto engine.
         */
-       if (!edesc->bklog)
+       if (!has_bklog)
                req->base.complete(&req->base, ecode);
        else
                crypto_finalize_hash_request(jrp->engine, req, ecode);
index 4fcae37..2e44d68 100644 (file)
@@ -121,11 +121,13 @@ static void rsa_pub_done(struct device *dev, u32 *desc, u32 err, void *context)
        struct caam_drv_private_jr *jrp = dev_get_drvdata(dev);
        struct rsa_edesc *edesc;
        int ecode = 0;
+       bool has_bklog;
 
        if (err)
                ecode = caam_jr_strstatus(dev, err);
 
        edesc = req_ctx->edesc;
+       has_bklog = edesc->bklog;
 
        rsa_pub_unmap(dev, edesc, req);
        rsa_io_unmap(dev, edesc, req);
@@ -135,7 +137,7 @@ static void rsa_pub_done(struct device *dev, u32 *desc, u32 err, void *context)
         * If no backlog flag, the completion of the request is done
         * by CAAM, not crypto engine.
         */
-       if (!edesc->bklog)
+       if (!has_bklog)
                akcipher_request_complete(req, ecode);
        else
                crypto_finalize_akcipher_request(jrp->engine, req, ecode);
@@ -152,11 +154,13 @@ static void rsa_priv_f_done(struct device *dev, u32 *desc, u32 err,
        struct caam_rsa_req_ctx *req_ctx = akcipher_request_ctx(req);
        struct rsa_edesc *edesc;
        int ecode = 0;
+       bool has_bklog;
 
        if (err)
                ecode = caam_jr_strstatus(dev, err);
 
        edesc = req_ctx->edesc;
+       has_bklog = edesc->bklog;
 
        switch (key->priv_form) {
        case FORM1:
@@ -176,7 +180,7 @@ static void rsa_priv_f_done(struct device *dev, u32 *desc, u32 err,
         * If no backlog flag, the completion of the request is done
         * by CAAM, not crypto engine.
         */
-       if (!edesc->bklog)
+       if (!has_bklog)
                akcipher_request_complete(req, ecode);
        else
                crypto_finalize_akcipher_request(jrp->engine, req, ecode);
index c29b80d..b8c1c4d 100644 (file)
@@ -1054,8 +1054,8 @@ static unsigned int adjust_ctr_overflow(u8 *iv, u32 bytes)
        u32 temp = be32_to_cpu(*--b);
 
        temp = ~temp;
-       c = (u64)temp +  1; // No of block can processed withou overflow
-       if ((bytes / AES_BLOCK_SIZE) > c)
+       c = (u64)temp +  1; // No of block can processed without overflow
+       if ((bytes / AES_BLOCK_SIZE) >= c)
                bytes = c * AES_BLOCK_SIZE;
        return bytes;
 }
@@ -1077,7 +1077,14 @@ static int chcr_update_tweak(struct skcipher_request *req, u8 *iv,
 
        keylen = ablkctx->enckey_len / 2;
        key = ablkctx->key + keylen;
-       ret = aes_expandkey(&aes, key, keylen);
+       /* For a 192 bit key remove the padded zeroes which was
+        * added in chcr_xts_setkey
+        */
+       if (KEY_CONTEXT_CK_SIZE_G(ntohl(ablkctx->key_ctx_hdr))
+                       == CHCR_KEYCTX_CIPHER_KEY_SIZE_192)
+               ret = aes_expandkey(&aes, key, keylen - 8);
+       else
+               ret = aes_expandkey(&aes, key, keylen);
        if (ret)
                return ret;
        aes_encrypt(&aes, iv, iv);
@@ -1158,15 +1165,16 @@ static int chcr_final_cipher_iv(struct skcipher_request *req,
 static int chcr_handle_cipher_resp(struct skcipher_request *req,
                                   unsigned char *input, int err)
 {
+       struct chcr_skcipher_req_ctx *reqctx = skcipher_request_ctx(req);
        struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
-       struct chcr_context *ctx = c_ctx(tfm);
-       struct uld_ctx *u_ctx = ULD_CTX(c_ctx(tfm));
-       struct ablk_ctx *ablkctx = ABLK_CTX(c_ctx(tfm));
-       struct sk_buff *skb;
        struct cpl_fw6_pld *fw6_pld = (struct cpl_fw6_pld *)input;
-       struct chcr_skcipher_req_ctx *reqctx = skcipher_request_ctx(req);
-       struct cipher_wr_param wrparam;
+       struct ablk_ctx *ablkctx = ABLK_CTX(c_ctx(tfm));
+       struct uld_ctx *u_ctx = ULD_CTX(c_ctx(tfm));
        struct chcr_dev *dev = c_ctx(tfm)->dev;
+       struct chcr_context *ctx = c_ctx(tfm);
+       struct adapter *adap = padap(ctx->dev);
+       struct cipher_wr_param wrparam;
+       struct sk_buff *skb;
        int bytes;
 
        if (err)
@@ -1197,6 +1205,8 @@ static int chcr_handle_cipher_resp(struct skcipher_request *req,
        if (unlikely(bytes == 0)) {
                chcr_cipher_dma_unmap(&ULD_CTX(c_ctx(tfm))->lldi.pdev->dev,
                                      req);
+               memcpy(req->iv, reqctx->init_iv, IV);
+               atomic_inc(&adap->chcr_stats.fallback);
                err = chcr_cipher_fallback(ablkctx->sw_cipher,
                                     req->base.flags,
                                     req->src,
@@ -1248,20 +1258,28 @@ static int process_cipher(struct skcipher_request *req,
                                  struct sk_buff **skb,
                                  unsigned short op_type)
 {
+       struct chcr_skcipher_req_ctx *reqctx = skcipher_request_ctx(req);
        struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
        unsigned int ivsize = crypto_skcipher_ivsize(tfm);
-       struct chcr_skcipher_req_ctx *reqctx = skcipher_request_ctx(req);
        struct ablk_ctx *ablkctx = ABLK_CTX(c_ctx(tfm));
+       struct adapter *adap = padap(c_ctx(tfm)->dev);
        struct  cipher_wr_param wrparam;
        int bytes, err = -EINVAL;
+       int subtype;
 
        reqctx->processed = 0;
        reqctx->partial_req = 0;
        if (!req->iv)
                goto error;
+       subtype = get_cryptoalg_subtype(tfm);
        if ((ablkctx->enckey_len == 0) || (ivsize > AES_BLOCK_SIZE) ||
            (req->cryptlen == 0) ||
            (req->cryptlen % crypto_skcipher_blocksize(tfm))) {
+               if (req->cryptlen == 0 && subtype != CRYPTO_ALG_SUB_TYPE_XTS)
+                       goto fallback;
+               else if (req->cryptlen % crypto_skcipher_blocksize(tfm) &&
+                        subtype == CRYPTO_ALG_SUB_TYPE_XTS)
+                       goto fallback;
                pr_err("AES: Invalid value of Key Len %d nbytes %d IV Len %d\n",
                       ablkctx->enckey_len, req->cryptlen, ivsize);
                goto error;
@@ -1302,12 +1320,10 @@ static int process_cipher(struct skcipher_request *req,
        } else {
                bytes = req->cryptlen;
        }
-       if (get_cryptoalg_subtype(tfm) ==
-           CRYPTO_ALG_SUB_TYPE_CTR) {
+       if (subtype == CRYPTO_ALG_SUB_TYPE_CTR) {
                bytes = adjust_ctr_overflow(req->iv, bytes);
        }
-       if (get_cryptoalg_subtype(tfm) ==
-           CRYPTO_ALG_SUB_TYPE_CTR_RFC3686) {
+       if (subtype == CRYPTO_ALG_SUB_TYPE_CTR_RFC3686) {
                memcpy(reqctx->iv, ablkctx->nonce, CTR_RFC3686_NONCE_SIZE);
                memcpy(reqctx->iv + CTR_RFC3686_NONCE_SIZE, req->iv,
                                CTR_RFC3686_IV_SIZE);
@@ -1315,20 +1331,25 @@ static int process_cipher(struct skcipher_request *req,
                /* initialize counter portion of counter block */
                *(__be32 *)(reqctx->iv + CTR_RFC3686_NONCE_SIZE +
                        CTR_RFC3686_IV_SIZE) = cpu_to_be32(1);
+               memcpy(reqctx->init_iv, reqctx->iv, IV);
 
        } else {
 
                memcpy(reqctx->iv, req->iv, IV);
+               memcpy(reqctx->init_iv, req->iv, IV);
        }
        if (unlikely(bytes == 0)) {
                chcr_cipher_dma_unmap(&ULD_CTX(c_ctx(tfm))->lldi.pdev->dev,
                                      req);
+fallback:       atomic_inc(&adap->chcr_stats.fallback);
                err = chcr_cipher_fallback(ablkctx->sw_cipher,
                                           req->base.flags,
                                           req->src,
                                           req->dst,
                                           req->cryptlen,
-                                          reqctx->iv,
+                                          subtype ==
+                                          CRYPTO_ALG_SUB_TYPE_CTR_RFC3686 ?
+                                          reqctx->iv : req->iv,
                                           op_type);
                goto error;
        }
@@ -1984,7 +2005,7 @@ static int chcr_ahash_digest(struct ahash_request *req)
        req_ctx->data_len += params.bfr_len + params.sg_len;
 
        if (req->nbytes == 0) {
-               create_last_hash_block(req_ctx->reqbfr, bs, 0);
+               create_last_hash_block(req_ctx->reqbfr, bs, req_ctx->data_len);
                params.more = 1;
                params.bfr_len = bs;
        }
@@ -2250,12 +2271,28 @@ static int chcr_aes_xts_setkey(struct crypto_skcipher *cipher, const u8 *key,
        ablkctx->enckey_len = key_len;
        get_aes_decrypt_key(ablkctx->rrkey, ablkctx->key, key_len << 2);
        context_size = (KEY_CONTEXT_HDR_SALT_AND_PAD + key_len) >> 4;
-       ablkctx->key_ctx_hdr =
+       /* Both keys for xts must be aligned to 16 byte boundary
+        * by padding with zeros. So for 24 byte keys padding 8 zeroes.
+        */
+       if (key_len == 48) {
+               context_size = (KEY_CONTEXT_HDR_SALT_AND_PAD + key_len
+                               + 16) >> 4;
+               memmove(ablkctx->key + 32, ablkctx->key + 24, 24);
+               memset(ablkctx->key + 24, 0, 8);
+               memset(ablkctx->key + 56, 0, 8);
+               ablkctx->enckey_len = 64;
+               ablkctx->key_ctx_hdr =
+                       FILL_KEY_CTX_HDR(CHCR_KEYCTX_CIPHER_KEY_SIZE_192,
+                                        CHCR_KEYCTX_NO_KEY, 1,
+                                        0, context_size);
+       } else {
+               ablkctx->key_ctx_hdr =
                FILL_KEY_CTX_HDR((key_len == AES_KEYSIZE_256) ?
                                 CHCR_KEYCTX_CIPHER_KEY_SIZE_128 :
                                 CHCR_KEYCTX_CIPHER_KEY_SIZE_256,
                                 CHCR_KEYCTX_NO_KEY, 1,
                                 0, context_size);
+       }
        ablkctx->ciph_mode = CHCR_SCMD_CIPHER_MODE_AES_XTS;
        return 0;
 badkey_err:
@@ -2556,7 +2593,7 @@ int chcr_aead_dma_map(struct device *dev,
        int dst_size;
 
        dst_size = req->assoclen + req->cryptlen + (op_type ?
-                               -authsize : authsize);
+                               0 : authsize);
        if (!req->cryptlen || !dst_size)
                return 0;
        reqctx->iv_dma = dma_map_single(dev, reqctx->iv, (IV + reqctx->b0_len),
@@ -2603,15 +2640,16 @@ void chcr_aead_dma_unmap(struct device *dev,
        int dst_size;
 
        dst_size = req->assoclen + req->cryptlen + (op_type ?
-                                       -authsize : authsize);
+                                       0 : authsize);
        if (!req->cryptlen || !dst_size)
                return;
 
        dma_unmap_single(dev, reqctx->iv_dma, (IV + reqctx->b0_len),
                                        DMA_BIDIRECTIONAL);
        if (req->src == req->dst) {
-               dma_unmap_sg(dev, req->src, sg_nents(req->src),
-                                  DMA_BIDIRECTIONAL);
+               dma_unmap_sg(dev, req->src,
+                            sg_nents_for_len(req->src, dst_size),
+                            DMA_BIDIRECTIONAL);
        } else {
                dma_unmap_sg(dev, req->src, sg_nents(req->src),
                                   DMA_TO_DEVICE);
@@ -2910,7 +2948,7 @@ static void fill_sec_cpl_for_aead(struct cpl_tx_sec_pdu *sec_cpl,
        unsigned int mac_mode = CHCR_SCMD_AUTH_MODE_CBCMAC;
        unsigned int rx_channel_id = reqctx->rxqidx / ctx->rxq_perchan;
        unsigned int ccm_xtra;
-       unsigned char tag_offset = 0, auth_offset = 0;
+       unsigned int tag_offset = 0, auth_offset = 0;
        unsigned int assoclen;
 
        if (get_aead_subtype(tfm) == CRYPTO_ALG_SUB_TYPE_AEAD_RFC4309)
@@ -3702,6 +3740,13 @@ static int chcr_aead_op(struct aead_request *req,
                        return -ENOSPC;
        }
 
+       if (get_aead_subtype(tfm) == CRYPTO_ALG_SUB_TYPE_AEAD_RFC4106 &&
+           crypto_ipsec_check_assoclen(req->assoclen) != 0) {
+               pr_err("RFC4106: Invalid value of assoclen %d\n",
+                      req->assoclen);
+               return -EINVAL;
+       }
+
        /* Form a WR from req */
        skb = create_wr_fn(req, u_ctx->lldi.rxq_ids[reqctx->rxqidx], size);
 
index 542beba..b3fdbdc 100644 (file)
@@ -302,6 +302,7 @@ struct chcr_skcipher_req_ctx {
        unsigned int op;
        u16 imm;
        u8 iv[CHCR_MAX_CRYPTO_IV_LEN];
+       u8 init_iv[CHCR_MAX_CRYPTO_IV_LEN];
        u16 txqidx;
        u16 rxqidx;
 };
index 9fd3b9d..d256898 100644 (file)
@@ -294,9 +294,6 @@ static bool chcr_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
                if (ipv6_ext_hdr(ipv6_hdr(skb)->nexthdr))
                        return false;
        }
-       /* Inline single pdu */
-       if (skb_shinfo(skb)->gso_size)
-               return false;
        return true;
 }
 
index cd1769e..43d9e24 100644 (file)
@@ -120,12 +120,10 @@ out:
 static int chcr_ktls_update_connection_state(struct chcr_ktls_info *tx_info,
                                             int new_state)
 {
-       unsigned long flags;
-
        /* This function can be called from both rx (interrupt context) and tx
         * queue contexts.
         */
-       spin_lock_irqsave(&tx_info->lock, flags);
+       spin_lock_bh(&tx_info->lock);
        switch (tx_info->connection_state) {
        case KTLS_CONN_CLOSED:
                tx_info->connection_state = new_state;
@@ -169,7 +167,7 @@ static int chcr_ktls_update_connection_state(struct chcr_ktls_info *tx_info,
                pr_err("unknown KTLS connection state\n");
                break;
        }
-       spin_unlock_irqrestore(&tx_info->lock, flags);
+       spin_unlock_bh(&tx_info->lock);
 
        return tx_info->connection_state;
 }
@@ -675,41 +673,14 @@ int chcr_ktls_cpl_set_tcb_rpl(struct adapter *adap, unsigned char *input)
        return 0;
 }
 
-/*
- * chcr_write_cpl_set_tcb_ulp: update tcb values.
- * TCB is responsible to create tcp headers, so all the related values
- * should be correctly updated.
- * @tx_info - driver specific tls info.
- * @q - tx queue on which packet is going out.
- * @tid - TCB identifier.
- * @pos - current index where should we start writing.
- * @word - TCB word.
- * @mask - TCB word related mask.
- * @val - TCB word related value.
- * @reply - set 1 if looking for TP response.
- * return - next position to write.
- */
-static void *chcr_write_cpl_set_tcb_ulp(struct chcr_ktls_info *tx_info,
-                                       struct sge_eth_txq *q, u32 tid,
-                                       void *pos, u16 word, u64 mask,
+static void *__chcr_write_cpl_set_tcb_ulp(struct chcr_ktls_info *tx_info,
+                                       u32 tid, void *pos, u16 word, u64 mask,
                                        u64 val, u32 reply)
 {
        struct cpl_set_tcb_field_core *cpl;
        struct ulptx_idata *idata;
        struct ulp_txpkt *txpkt;
-       void *save_pos = NULL;
-       u8 buf[48] = {0};
-       int left;
 
-       left = (void *)q->q.stat - pos;
-       if (unlikely(left < CHCR_SET_TCB_FIELD_LEN)) {
-               if (!left) {
-                       pos = q->q.desc;
-               } else {
-                       save_pos = pos;
-                       pos = buf;
-               }
-       }
        /* ULP_TXPKT */
        txpkt = pos;
        txpkt->cmd_dest = htonl(ULPTX_CMD_V(ULP_TX_PKT) | ULP_TXPKT_DEST_V(0));
@@ -734,18 +705,54 @@ static void *chcr_write_cpl_set_tcb_ulp(struct chcr_ktls_info *tx_info,
        idata = (struct ulptx_idata *)(cpl + 1);
        idata->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_NOOP));
        idata->len = htonl(0);
+       pos = idata + 1;
 
-       if (save_pos) {
-               pos = chcr_copy_to_txd(buf, &q->q, save_pos,
-                                      CHCR_SET_TCB_FIELD_LEN);
-       } else {
-               /* check again if we are at the end of the queue */
-               if (left == CHCR_SET_TCB_FIELD_LEN)
+       return pos;
+}
+
+
+/*
+ * chcr_write_cpl_set_tcb_ulp: update tcb values.
+ * TCB is responsible to create tcp headers, so all the related values
+ * should be correctly updated.
+ * @tx_info - driver specific tls info.
+ * @q - tx queue on which packet is going out.
+ * @tid - TCB identifier.
+ * @pos - current index where should we start writing.
+ * @word - TCB word.
+ * @mask - TCB word related mask.
+ * @val - TCB word related value.
+ * @reply - set 1 if looking for TP response.
+ * return - next position to write.
+ */
+static void *chcr_write_cpl_set_tcb_ulp(struct chcr_ktls_info *tx_info,
+                                       struct sge_eth_txq *q, u32 tid,
+                                       void *pos, u16 word, u64 mask,
+                                       u64 val, u32 reply)
+{
+       int left = (void *)q->q.stat - pos;
+
+       if (unlikely(left < CHCR_SET_TCB_FIELD_LEN)) {
+               if (!left) {
                        pos = q->q.desc;
-               else
-                       pos = idata + 1;
+               } else {
+                       u8 buf[48] = {0};
+
+                       __chcr_write_cpl_set_tcb_ulp(tx_info, tid, buf, word,
+                                                    mask, val, reply);
+
+                       return chcr_copy_to_txd(buf, &q->q, pos,
+                                               CHCR_SET_TCB_FIELD_LEN);
+               }
        }
 
+       pos = __chcr_write_cpl_set_tcb_ulp(tx_info, tid, pos, word,
+                                          mask, val, reply);
+
+       /* check again if we are at the end of the queue */
+       if (left == CHCR_SET_TCB_FIELD_LEN)
+               pos = q->q.desc;
+
        return pos;
 }
 
index ccc9eda..07df88f 100644 (file)
@@ -388,7 +388,8 @@ static long dma_buf_ioctl(struct file *file,
 
                return ret;
 
-       case DMA_BUF_SET_NAME:
+       case DMA_BUF_SET_NAME_A:
+       case DMA_BUF_SET_NAME_B:
                return dma_buf_set_name(dmabuf, (const char __user *)arg);
 
        default:
@@ -655,8 +656,8 @@ EXPORT_SYMBOL_GPL(dma_buf_put);
  * calls attach() of dma_buf_ops to allow device-specific attach functionality
  * @dmabuf:            [in]    buffer to attach device to.
  * @dev:               [in]    device to be attached.
- * @importer_ops       [in]    importer operations for the attachment
- * @importer_priv      [in]    importer private pointer for the attachment
+ * @importer_ops:      [in]    importer operations for the attachment
+ * @importer_priv:     [in]    importer private pointer for the attachment
  *
  * Returns struct dma_buf_attachment pointer for this attachment. Attachments
  * must be cleaned up by calling dma_buf_detach().
index 0924836..023db68 100644 (file)
@@ -241,7 +241,8 @@ config FSL_RAID
 
 config HISI_DMA
        tristate "HiSilicon DMA Engine support"
-       depends on ARM64 || (COMPILE_TEST && PCI_MSI)
+       depends on ARM64 || COMPILE_TEST
+       depends on PCI_MSI
        select DMA_ENGINE
        select DMA_VIRTUAL_CHANNELS
        help
index 4830ba6..d31076d 100644 (file)
@@ -232,10 +232,6 @@ static void chan_dev_release(struct device *dev)
        struct dma_chan_dev *chan_dev;
 
        chan_dev = container_of(dev, typeof(*chan_dev), device);
-       if (atomic_dec_and_test(chan_dev->idr_ref)) {
-               ida_free(&dma_ida, chan_dev->dev_id);
-               kfree(chan_dev->idr_ref);
-       }
        kfree(chan_dev);
 }
 
@@ -1043,27 +1039,9 @@ static int get_dma_id(struct dma_device *device)
 }
 
 static int __dma_async_device_channel_register(struct dma_device *device,
-                                              struct dma_chan *chan,
-                                              int chan_id)
+                                              struct dma_chan *chan)
 {
        int rc = 0;
-       int chancnt = device->chancnt;
-       atomic_t *idr_ref;
-       struct dma_chan *tchan;
-
-       tchan = list_first_entry_or_null(&device->channels,
-                                        struct dma_chan, device_node);
-       if (!tchan)
-               return -ENODEV;
-
-       if (tchan->dev) {
-               idr_ref = tchan->dev->idr_ref;
-       } else {
-               idr_ref = kmalloc(sizeof(*idr_ref), GFP_KERNEL);
-               if (!idr_ref)
-                       return -ENOMEM;
-               atomic_set(idr_ref, 0);
-       }
 
        chan->local = alloc_percpu(typeof(*chan->local));
        if (!chan->local)
@@ -1079,29 +1057,36 @@ static int __dma_async_device_channel_register(struct dma_device *device,
         * When the chan_id is a negative value, we are dynamically adding
         * the channel. Otherwise we are static enumerating.
         */
-       chan->chan_id = chan_id < 0 ? chancnt : chan_id;
+       mutex_lock(&device->chan_mutex);
+       chan->chan_id = ida_alloc(&device->chan_ida, GFP_KERNEL);
+       mutex_unlock(&device->chan_mutex);
+       if (chan->chan_id < 0) {
+               pr_err("%s: unable to alloc ida for chan: %d\n",
+                      __func__, chan->chan_id);
+               goto err_out;
+       }
+
        chan->dev->device.class = &dma_devclass;
        chan->dev->device.parent = device->dev;
        chan->dev->chan = chan;
-       chan->dev->idr_ref = idr_ref;
        chan->dev->dev_id = device->dev_id;
-       atomic_inc(idr_ref);
        dev_set_name(&chan->dev->device, "dma%dchan%d",
                     device->dev_id, chan->chan_id);
-
        rc = device_register(&chan->dev->device);
        if (rc)
-               goto err_out;
+               goto err_out_ida;
        chan->client_count = 0;
-       device->chancnt = chan->chan_id + 1;
+       device->chancnt++;
 
        return 0;
 
+ err_out_ida:
+       mutex_lock(&device->chan_mutex);
+       ida_free(&device->chan_ida, chan->chan_id);
+       mutex_unlock(&device->chan_mutex);
  err_out:
        free_percpu(chan->local);
        kfree(chan->dev);
-       if (atomic_dec_return(idr_ref) == 0)
-               kfree(idr_ref);
        return rc;
 }
 
@@ -1110,7 +1095,7 @@ int dma_async_device_channel_register(struct dma_device *device,
 {
        int rc;
 
-       rc = __dma_async_device_channel_register(device, chan, -1);
+       rc = __dma_async_device_channel_register(device, chan);
        if (rc < 0)
                return rc;
 
@@ -1130,6 +1115,9 @@ static void __dma_async_device_channel_unregister(struct dma_device *device,
        device->chancnt--;
        chan->dev->chan = NULL;
        mutex_unlock(&dma_list_mutex);
+       mutex_lock(&device->chan_mutex);
+       ida_free(&device->chan_ida, chan->chan_id);
+       mutex_unlock(&device->chan_mutex);
        device_unregister(&chan->dev->device);
        free_percpu(chan->local);
 }
@@ -1152,7 +1140,7 @@ EXPORT_SYMBOL_GPL(dma_async_device_channel_unregister);
  */
 int dma_async_device_register(struct dma_device *device)
 {
-       int rc, i = 0;
+       int rc;
        struct dma_chan* chan;
 
        if (!device)
@@ -1257,9 +1245,12 @@ int dma_async_device_register(struct dma_device *device)
        if (rc != 0)
                return rc;
 
+       mutex_init(&device->chan_mutex);
+       ida_init(&device->chan_ida);
+
        /* represent channels in sysfs. Probably want devs too */
        list_for_each_entry(chan, &device->channels, device_node) {
-               rc = __dma_async_device_channel_register(device, chan, i++);
+               rc = __dma_async_device_channel_register(device, chan);
                if (rc < 0)
                        goto err_out;
        }
@@ -1334,6 +1325,7 @@ void dma_async_device_unregister(struct dma_device *device)
         */
        dma_cap_set(DMA_PRIVATE, device->cap_mask);
        dma_channel_rebalance();
+       ida_free(&dma_ida, device->dev_id);
        dma_device_put(device);
        mutex_unlock(&dma_list_mutex);
 }
index a2cadfa..364dd34 100644 (file)
@@ -240,7 +240,7 @@ static bool is_threaded_test_run(struct dmatest_info *info)
                struct dmatest_thread *thread;
 
                list_for_each_entry(thread, &dtc->threads, node) {
-                       if (!thread->done)
+                       if (!thread->done && !thread->pending)
                                return true;
                }
        }
@@ -662,8 +662,8 @@ static int dmatest_func(void *data)
                flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
 
        ktime = ktime_get();
-       while (!kthread_should_stop()
-              && !(params->iterations && total_tests >= params->iterations)) {
+       while (!(kthread_should_stop() ||
+              (params->iterations && total_tests >= params->iterations))) {
                struct dma_async_tx_descriptor *tx = NULL;
                struct dmaengine_unmap_data *um;
                dma_addr_t *dsts;
index 10117f2..d683232 100644 (file)
@@ -363,6 +363,8 @@ static void mmp_tdma_free_descriptor(struct mmp_tdma_chan *tdmac)
                gen_pool_free(gpool, (unsigned long)tdmac->desc_arr,
                                size);
        tdmac->desc_arr = NULL;
+       if (tdmac->status == DMA_ERROR)
+               tdmac->status = DMA_COMPLETE;
 
        return;
 }
@@ -443,7 +445,8 @@ static struct dma_async_tx_descriptor *mmp_tdma_prep_dma_cyclic(
        if (!desc)
                goto err_out;
 
-       mmp_tdma_config_write(chan, direction, &tdmac->slave_config);
+       if (mmp_tdma_config_write(chan, direction, &tdmac->slave_config))
+               goto err_out;
 
        while (buf < buf_len) {
                desc = &tdmac->desc_arr[i];
index 581e7a2..a3b0b4c 100644 (file)
@@ -865,6 +865,7 @@ static int pch_dma_probe(struct pci_dev *pdev,
        }
 
        pci_set_master(pdev);
+       pd->dma.dev = &pdev->dev;
 
        err = request_irq(pdev->irq, pd_irq, IRQF_SHARED, DRV_NAME, pd);
        if (err) {
@@ -880,7 +881,6 @@ static int pch_dma_probe(struct pci_dev *pdev,
                goto err_free_irq;
        }
 
-       pd->dma.dev = &pdev->dev;
 
        INIT_LIST_HEAD(&pd->dma.channels);
 
index f6a2f42..b9f0d96 100644 (file)
@@ -816,6 +816,13 @@ static bool tegra_dma_eoc_interrupt_deasserted(struct tegra_dma_channel *tdc)
 static void tegra_dma_synchronize(struct dma_chan *dc)
 {
        struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
+       int err;
+
+       err = pm_runtime_get_sync(tdc->tdma->dev);
+       if (err < 0) {
+               dev_err(tdc2dev(tdc), "Failed to synchronize DMA: %d\n", err);
+               return;
+       }
 
        /*
         * CPU, which handles interrupt, could be busy in
@@ -825,6 +832,8 @@ static void tegra_dma_synchronize(struct dma_chan *dc)
        wait_event(tdc->wq, tegra_dma_eoc_interrupt_deasserted(tdc));
 
        tasklet_kill(&tdc->tasklet);
+
+       pm_runtime_put(tdc->tdma->dev);
 }
 
 static unsigned int tegra_dma_sg_bytes_xferred(struct tegra_dma_channel *tdc,
index d7b9650..fb7c815 100644 (file)
@@ -27,6 +27,7 @@ struct psil_endpoint_config *psil_get_ep_config(u32 thread_id)
                        soc_ep_map = &j721e_ep_map;
                } else {
                        pr_err("PSIL: No compatible machine found for map\n");
+                       mutex_unlock(&ep_map_mutex);
                        return ERR_PTR(-ENOTSUPP);
                }
                pr_debug("%s: Using map for %s\n", __func__, soc_ep_map->name);
index aecd5a3..5429497 100644 (file)
@@ -1230,16 +1230,16 @@ static enum dma_status xilinx_dma_tx_status(struct dma_chan *dchan,
                return ret;
 
        spin_lock_irqsave(&chan->lock, flags);
-
-       desc = list_last_entry(&chan->active_list,
-                              struct xilinx_dma_tx_descriptor, node);
-       /*
-        * VDMA and simple mode do not support residue reporting, so the
-        * residue field will always be 0.
-        */
-       if (chan->has_sg && chan->xdev->dma_config->dmatype != XDMA_TYPE_VDMA)
-               residue = xilinx_dma_get_residue(chan, desc);
-
+       if (!list_empty(&chan->active_list)) {
+               desc = list_last_entry(&chan->active_list,
+                                      struct xilinx_dma_tx_descriptor, node);
+               /*
+                * VDMA and simple mode do not support residue reporting, so the
+                * residue field will always be 0.
+                */
+               if (chan->has_sg && chan->xdev->dma_config->dmatype != XDMA_TYPE_VDMA)
+                       residue = xilinx_dma_get_residue(chan, desc);
+       }
        spin_unlock_irqrestore(&chan->lock, flags);
 
        dma_set_residue(txstate, residue);
index b1af0de..9d25129 100644 (file)
@@ -101,7 +101,7 @@ void cper_print_bits(const char *pfx, unsigned int bits,
                if (!len)
                        len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);
                else
-                       len += snprintf(buf+len, sizeof(buf)-len, ", %s", str);
+                       len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str);
        }
        if (len)
                printk("%s\n", buf);
index cc90a74..67d2694 100644 (file)
@@ -25,7 +25,7 @@
 #define EFI_ALLOC_ALIGN                EFI_PAGE_SIZE
 #endif
 
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_X86)
 #define __efistub_global       __section(.data)
 #else
 #define __efistub_global
index d4c7e5f..ea66b1f 100644 (file)
  */
 #define EFI_READ_CHUNK_SIZE    SZ_1M
 
+struct finfo {
+       efi_file_info_t info;
+       efi_char16_t    filename[MAX_FILENAME_SIZE];
+};
+
 static efi_status_t efi_open_file(efi_file_protocol_t *volume,
-                                 efi_char16_t *filename_16,
+                                 struct finfo *fi,
                                  efi_file_protocol_t **handle,
                                  unsigned long *file_size)
 {
-       struct {
-               efi_file_info_t info;
-               efi_char16_t    filename[MAX_FILENAME_SIZE];
-       } finfo;
        efi_guid_t info_guid = EFI_FILE_INFO_ID;
        efi_file_protocol_t *fh;
        unsigned long info_sz;
        efi_status_t status;
 
-       status = volume->open(volume, &fh, filename_16, EFI_FILE_MODE_READ, 0);
+       status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0);
        if (status != EFI_SUCCESS) {
                pr_efi_err("Failed to open file: ");
-               efi_char16_printk(filename_16);
+               efi_char16_printk(fi->filename);
                efi_printk("\n");
                return status;
        }
 
-       info_sz = sizeof(finfo);
-       status = fh->get_info(fh, &info_guid, &info_sz, &finfo);
+       info_sz = sizeof(struct finfo);
+       status = fh->get_info(fh, &info_guid, &info_sz, fi);
        if (status != EFI_SUCCESS) {
                pr_efi_err("Failed to get file info\n");
                fh->close(fh);
@@ -60,7 +61,7 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume,
        }
 
        *handle = fh;
-       *file_size = finfo.info.file_size;
+       *file_size = fi->info.file_size;
        return EFI_SUCCESS;
 }
 
@@ -146,13 +147,13 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
 
        alloc_addr = alloc_size = 0;
        do {
-               efi_char16_t filename[MAX_FILENAME_SIZE];
+               struct finfo fi;
                unsigned long size;
                void *addr;
 
                offset = find_file_option(cmdline, cmdline_len,
                                          optstr, optstr_size,
-                                         filename, ARRAY_SIZE(filename));
+                                         fi.filename, ARRAY_SIZE(fi.filename));
 
                if (!offset)
                        break;
@@ -166,7 +167,7 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
                                return status;
                }
 
-               status = efi_open_file(volume, filename, &file, &size);
+               status = efi_open_file(volume, &fi, &file, &size);
                if (status != EFI_SUCCESS)
                        goto err_close_volume;
 
index 8d3a707..05ccb22 100644 (file)
@@ -20,7 +20,7 @@
 /* Maximum physical address for 64-bit kernel with 4-level paging */
 #define MAXMEM_X86_64_4LEVEL (1ull << 46)
 
-static efi_system_table_t *sys_table;
+static efi_system_table_t *sys_table __efistub_global;
 extern const bool efi_is64;
 extern u32 image_offset;
 
@@ -392,8 +392,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
        image_base = efi_table_attr(image, image_base);
        image_offset = (void *)startup_32 - image_base;
 
-       hdr = &((struct boot_params *)image_base)->hdr;
-
        status = efi_allocate_pages(0x4000, (unsigned long *)&boot_params, ULONG_MAX);
        if (status != EFI_SUCCESS) {
                efi_printk("Failed to allocate lowmem for boot params\n");
@@ -742,8 +740,15 @@ unsigned long efi_main(efi_handle_t handle,
         * now use KERNEL_IMAGE_SIZE, which will be 512MiB, the same as what
         * KASLR uses.
         *
-        * Also relocate it if image_offset is zero, i.e. we weren't loaded by
-        * LoadImage, but we are not aligned correctly.
+        * Also relocate it if image_offset is zero, i.e. the kernel wasn't
+        * loaded by LoadImage, but rather by a bootloader that called the
+        * handover entry. The reason we must always relocate in this case is
+        * to handle the case of systemd-boot booting a unified kernel image,
+        * which is a PE executable that contains the bzImage and an initrd as
+        * COFF sections. The initrd section is placed after the bzImage
+        * without ensuring that there are at least init_size bytes available
+        * for the bzImage, and thus the compressed kernel's startup code may
+        * overwrite the initrd unless it is moved out of the way.
         */
 
        buffer_start = ALIGN(bzimage_addr - image_offset,
@@ -753,8 +758,7 @@ unsigned long efi_main(efi_handle_t handle,
        if ((buffer_start < LOAD_PHYSICAL_ADDR)                              ||
            (IS_ENABLED(CONFIG_X86_32) && buffer_end > KERNEL_IMAGE_SIZE)    ||
            (IS_ENABLED(CONFIG_X86_64) && buffer_end > MAXMEM_X86_64_4LEVEL) ||
-           (image_offset == 0 && !IS_ALIGNED(bzimage_addr,
-                                             hdr->kernel_alignment))) {
+           (image_offset == 0)) {
                status = efi_relocate_kernel(&bzimage_addr,
                                             hdr->init_size, hdr->init_size,
                                             hdr->pref_address,
index 116707a..1d2e5b8 100644 (file)
@@ -12,7 +12,7 @@ config IMX_DSP
 
 config IMX_SCU
        bool "IMX SCU Protocol driver"
-       depends on IMX_MBOX || COMPILE_TEST
+       depends on IMX_MBOX
        help
          The System Controller Firmware (SCFW) is a low-level system function
          which runs on a dedicated Cortex-M core to provide power, clock, and
@@ -24,6 +24,6 @@ config IMX_SCU
 
 config IMX_SCU_PD
        bool "IMX SCU Power Domain driver"
-       depends on IMX_SCU || COMPILE_TEST
+       depends on IMX_SCU
        help
          The System Controller Firmware (SCFW) based power domain driver.
index c6d0724..43bc6cf 100644 (file)
@@ -35,7 +35,7 @@ static struct pm_api_info pm_api_list[] = {
        PM_API(PM_QUERY_DATA),
 };
 
-struct dentry *firmware_debugfs_root;
+static struct dentry *firmware_debugfs_root;
 
 /**
  * zynqmp_pm_argument_value() - Extract argument value from a PM-API request
index 89ca292..5387550 100644 (file)
@@ -248,11 +248,13 @@ static int cci_pci_sriov_configure(struct pci_dev *pcidev, int num_vfs)
                        return ret;
 
                ret = pci_enable_sriov(pcidev, num_vfs);
-               if (ret)
+               if (ret) {
                        dfl_fpga_cdev_config_ports_pf(cdev);
+                       return ret;
+               }
        }
 
-       return ret;
+       return num_vfs;
 }
 
 static void cci_pci_remove(struct pci_dev *pcidev)
index ee77650..07fa8d9 100644 (file)
@@ -583,7 +583,8 @@ static int zynq_fpga_probe(struct platform_device *pdev)
 
        priv->clk = devm_clk_get(dev, "ref_clk");
        if (IS_ERR(priv->clk)) {
-               dev_err(dev, "input clock not found\n");
+               if (PTR_ERR(priv->clk) != -EPROBE_DEFER)
+                       dev_err(dev, "input clock not found\n");
                return PTR_ERR(priv->clk);
        }
 
index 559dc24..f84f9e3 100644 (file)
@@ -2008,8 +2008,24 @@ static void amdgpu_device_fill_reset_magic(struct amdgpu_device *adev)
  */
 static bool amdgpu_device_check_vram_lost(struct amdgpu_device *adev)
 {
-       return !!memcmp(adev->gart.ptr, adev->reset_magic,
-                       AMDGPU_RESET_MAGIC_NUM);
+       if (memcmp(adev->gart.ptr, adev->reset_magic,
+                       AMDGPU_RESET_MAGIC_NUM))
+               return true;
+
+       if (!adev->in_gpu_reset)
+               return false;
+
+       /*
+        * For all ASICs with baco/mode1 reset, the VRAM is
+        * always assumed to be lost.
+        */
+       switch (amdgpu_asic_reset_method(adev)) {
+       case AMD_RESET_METHOD_BACO:
+       case AMD_RESET_METHOD_MODE1:
+               return true;
+       default:
+               return false;
+       }
 }
 
 /**
@@ -2340,6 +2356,8 @@ static int amdgpu_device_ip_suspend_phase1(struct amdgpu_device *adev)
 {
        int i, r;
 
+       amdgpu_device_set_pg_state(adev, AMD_PG_STATE_UNGATE);
+       amdgpu_device_set_cg_state(adev, AMD_CG_STATE_UNGATE);
 
        for (i = adev->num_ip_blocks - 1; i >= 0; i--) {
                if (!adev->ip_blocks[i].status.valid)
index 8ea86ff..466bfe5 100644 (file)
  * - 3.34.0 - Non-DC can flip correctly between buffers with different pitches
  * - 3.35.0 - Add drm_amdgpu_info_device::tcc_disabled_mask
  * - 3.36.0 - Allow reading more status registers on si/cik
+ * - 3.37.0 - L2 is invalidated before SDMA IBs, needed for correctness
  */
 #define KMS_DRIVER_MAJOR       3
-#define KMS_DRIVER_MINOR       36
+#define KMS_DRIVER_MINOR       37
 #define KMS_DRIVER_PATCHLEVEL  0
 
 int amdgpu_vram_limit = 0;
index 006f21e..62635e5 100644 (file)
@@ -1358,8 +1358,6 @@ static int cik_asic_reset(struct amdgpu_device *adev)
        int r;
 
        if (cik_asic_reset_method(adev) == AMD_RESET_METHOD_BACO) {
-               if (!adev->in_suspend)
-                       amdgpu_inc_vram_lost(adev);
                r = amdgpu_dpm_baco_reset(adev);
        } else {
                r = cik_asic_pci_config_reset(adev);
index d78059f..f92c158 100644 (file)
@@ -279,7 +279,7 @@ static const struct soc15_reg_golden golden_settings_gc_10_1_2_nv12[] =
 
 #define DEFAULT_SH_MEM_CONFIG \
        ((SH_MEM_ADDRESS_MODE_64 << SH_MEM_CONFIG__ADDRESS_MODE__SHIFT) | \
-        (SH_MEM_ALIGNMENT_MODE_DWORD << SH_MEM_CONFIG__ALIGNMENT_MODE__SHIFT) | \
+        (SH_MEM_ALIGNMENT_MODE_UNALIGNED << SH_MEM_CONFIG__ALIGNMENT_MODE__SHIFT) | \
         (SH_MEM_RETRY_MODE_ALL << SH_MEM_CONFIG__RETRY_MODE__SHIFT) | \
         (3 << SH_MEM_CONFIG__INITIAL_INST_PREFETCH__SHIFT))
 
index e6b113e..0c39048 100644 (file)
@@ -1234,6 +1234,8 @@ struct amdgpu_gfxoff_quirk {
 static const struct amdgpu_gfxoff_quirk amdgpu_gfxoff_quirk_list[] = {
        /* https://bugzilla.kernel.org/show_bug.cgi?id=204689 */
        { 0x1002, 0x15dd, 0x1002, 0x15dd, 0xc8 },
+       /* https://bugzilla.kernel.org/show_bug.cgi?id=207171 */
+       { 0x1002, 0x15dd, 0x103c, 0x83e7, 0xd3 },
        { 0, 0, 0, 0, 0 },
 };
 
index 074a9a0..a5b60c9 100644 (file)
 #define SDMA_OP_AQL_COPY  0
 #define SDMA_OP_AQL_BARRIER_OR  0
 
+#define SDMA_GCR_RANGE_IS_PA           (1 << 18)
+#define SDMA_GCR_SEQ(x)                        (((x) & 0x3) << 16)
+#define SDMA_GCR_GL2_WB                        (1 << 15)
+#define SDMA_GCR_GL2_INV               (1 << 14)
+#define SDMA_GCR_GL2_DISCARD           (1 << 13)
+#define SDMA_GCR_GL2_RANGE(x)          (((x) & 0x3) << 11)
+#define SDMA_GCR_GL2_US                        (1 << 10)
+#define SDMA_GCR_GL1_INV               (1 << 9)
+#define SDMA_GCR_GLV_INV               (1 << 8)
+#define SDMA_GCR_GLK_INV               (1 << 7)
+#define SDMA_GCR_GLK_WB                        (1 << 6)
+#define SDMA_GCR_GLM_INV               (1 << 5)
+#define SDMA_GCR_GLM_WB                        (1 << 4)
+#define SDMA_GCR_GL1_RANGE(x)          (((x) & 0x3) << 2)
+#define SDMA_GCR_GLI_INV(x)            (((x) & 0x3) << 0)
+
 /*define for op field*/
 #define SDMA_PKT_HEADER_op_offset 0
 #define SDMA_PKT_HEADER_op_mask   0x000000FF
index 033cbbc..52318b0 100644 (file)
@@ -351,8 +351,6 @@ static int nv_asic_reset(struct amdgpu_device *adev)
        struct smu_context *smu = &adev->smu;
 
        if (nv_asic_reset_method(adev) == AMD_RESET_METHOD_BACO) {
-               if (!adev->in_suspend)
-                       amdgpu_inc_vram_lost(adev);
                ret = smu_baco_enter(smu);
                if (ret)
                        return ret;
@@ -360,8 +358,6 @@ static int nv_asic_reset(struct amdgpu_device *adev)
                if (ret)
                        return ret;
        } else {
-               if (!adev->in_suspend)
-                       amdgpu_inc_vram_lost(adev);
                ret = nv_asic_mode1_reset(adev);
        }
 
index ebfd2cd..d2840c2 100644 (file)
@@ -382,6 +382,18 @@ static void sdma_v5_0_ring_emit_ib(struct amdgpu_ring *ring,
        unsigned vmid = AMDGPU_JOB_GET_VMID(job);
        uint64_t csa_mc_addr = amdgpu_sdma_get_csa_mc_addr(ring, vmid);
 
+       /* Invalidate L2, because if we don't do it, we might get stale cache
+        * lines from previous IBs.
+        */
+       amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_GCR_REQ));
+       amdgpu_ring_write(ring, 0);
+       amdgpu_ring_write(ring, (SDMA_GCR_GL2_INV |
+                                SDMA_GCR_GL2_WB |
+                                SDMA_GCR_GLM_INV |
+                                SDMA_GCR_GLM_WB) << 16);
+       amdgpu_ring_write(ring, 0xffffff80);
+       amdgpu_ring_write(ring, 0xffff);
+
        /* An IB packet must end on a 8 DW boundary--the next dword
         * must be on a 8-dword boundary. Our IB packet below is 6
         * dwords long, thus add x number of NOPs, such that, in
@@ -1595,7 +1607,7 @@ static const struct amdgpu_ring_funcs sdma_v5_0_ring_funcs = {
                SOC15_FLUSH_GPU_TLB_NUM_WREG * 3 +
                SOC15_FLUSH_GPU_TLB_NUM_REG_WAIT * 6 * 2 +
                10 + 10 + 10, /* sdma_v5_0_ring_emit_fence x3 for user fence, vm fence */
-       .emit_ib_size = 7 + 6, /* sdma_v5_0_ring_emit_ib */
+       .emit_ib_size = 5 + 7 + 6, /* sdma_v5_0_ring_emit_ib */
        .emit_ib = sdma_v5_0_ring_emit_ib,
        .emit_fence = sdma_v5_0_ring_emit_fence,
        .emit_pipeline_sync = sdma_v5_0_ring_emit_pipeline_sync,
index a40499d..d42a8d8 100644 (file)
@@ -569,14 +569,10 @@ static int soc15_asic_reset(struct amdgpu_device *adev)
 
        switch (soc15_asic_reset_method(adev)) {
                case AMD_RESET_METHOD_BACO:
-                       if (!adev->in_suspend)
-                               amdgpu_inc_vram_lost(adev);
                        return soc15_asic_baco_reset(adev);
                case AMD_RESET_METHOD_MODE2:
                        return amdgpu_dpm_mode2_reset(adev);
                default:
-                       if (!adev->in_suspend)
-                               amdgpu_inc_vram_lost(adev);
                        return soc15_asic_mode1_reset(adev);
        }
 }
index 78b3590..3ce10e0 100644 (file)
@@ -765,8 +765,6 @@ static int vi_asic_reset(struct amdgpu_device *adev)
        int r;
 
        if (vi_asic_reset_method(adev) == AMD_RESET_METHOD_BACO) {
-               if (!adev->in_suspend)
-                       amdgpu_inc_vram_lost(adev);
                r = amdgpu_dpm_baco_reset(adev);
        } else {
                r = vi_asic_pci_config_reset(adev);
index f7c5cdc..94c29b7 100644 (file)
@@ -3340,7 +3340,8 @@ fill_plane_dcc_attributes(struct amdgpu_device *adev,
                          const union dc_tiling_info *tiling_info,
                          const uint64_t info,
                          struct dc_plane_dcc_param *dcc,
-                         struct dc_plane_address *address)
+                         struct dc_plane_address *address,
+                         bool force_disable_dcc)
 {
        struct dc *dc = adev->dm.dc;
        struct dc_dcc_surface_param input;
@@ -3352,6 +3353,9 @@ fill_plane_dcc_attributes(struct amdgpu_device *adev,
        memset(&input, 0, sizeof(input));
        memset(&output, 0, sizeof(output));
 
+       if (force_disable_dcc)
+               return 0;
+
        if (!offset)
                return 0;
 
@@ -3401,7 +3405,8 @@ fill_plane_buffer_attributes(struct amdgpu_device *adev,
                             union dc_tiling_info *tiling_info,
                             struct plane_size *plane_size,
                             struct dc_plane_dcc_param *dcc,
-                            struct dc_plane_address *address)
+                            struct dc_plane_address *address,
+                            bool force_disable_dcc)
 {
        const struct drm_framebuffer *fb = &afb->base;
        int ret;
@@ -3507,7 +3512,8 @@ fill_plane_buffer_attributes(struct amdgpu_device *adev,
 
                ret = fill_plane_dcc_attributes(adev, afb, format, rotation,
                                                plane_size, tiling_info,
-                                               tiling_flags, dcc, address);
+                                               tiling_flags, dcc, address,
+                                               force_disable_dcc);
                if (ret)
                        return ret;
        }
@@ -3599,7 +3605,8 @@ fill_dc_plane_info_and_addr(struct amdgpu_device *adev,
                            const struct drm_plane_state *plane_state,
                            const uint64_t tiling_flags,
                            struct dc_plane_info *plane_info,
-                           struct dc_plane_address *address)
+                           struct dc_plane_address *address,
+                           bool force_disable_dcc)
 {
        const struct drm_framebuffer *fb = plane_state->fb;
        const struct amdgpu_framebuffer *afb =
@@ -3681,7 +3688,8 @@ fill_dc_plane_info_and_addr(struct amdgpu_device *adev,
                                           plane_info->rotation, tiling_flags,
                                           &plane_info->tiling_info,
                                           &plane_info->plane_size,
-                                          &plane_info->dcc, address);
+                                          &plane_info->dcc, address,
+                                          force_disable_dcc);
        if (ret)
                return ret;
 
@@ -3704,6 +3712,7 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev,
        struct dc_plane_info plane_info;
        uint64_t tiling_flags;
        int ret;
+       bool force_disable_dcc = false;
 
        ret = fill_dc_scaling_info(plane_state, &scaling_info);
        if (ret)
@@ -3718,9 +3727,11 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev,
        if (ret)
                return ret;
 
+       force_disable_dcc = adev->asic_type == CHIP_RAVEN && adev->in_suspend;
        ret = fill_dc_plane_info_and_addr(adev, plane_state, tiling_flags,
                                          &plane_info,
-                                         &dc_plane_state->address);
+                                         &dc_plane_state->address,
+                                         force_disable_dcc);
        if (ret)
                return ret;
 
@@ -4664,6 +4675,7 @@ static void amdgpu_dm_connector_destroy(struct drm_connector *connector)
                i2c_del_adapter(&aconnector->i2c->base);
                kfree(aconnector->i2c);
        }
+       kfree(aconnector->dm_dp_aux.aux.name);
 
        kfree(connector);
 }
@@ -4723,10 +4735,19 @@ amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector)
 static int
 amdgpu_dm_connector_late_register(struct drm_connector *connector)
 {
-#if defined(CONFIG_DEBUG_FS)
        struct amdgpu_dm_connector *amdgpu_dm_connector =
                to_amdgpu_dm_connector(connector);
+       int r;
+
+       if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
+           (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
+               amdgpu_dm_connector->dm_dp_aux.aux.dev = connector->kdev;
+               r = drm_dp_aux_register(&amdgpu_dm_connector->dm_dp_aux.aux);
+               if (r)
+                       return r;
+       }
 
+#if defined(CONFIG_DEBUG_FS)
        connector_debugfs_init(amdgpu_dm_connector);
 #endif
 
@@ -5332,6 +5353,7 @@ static int dm_plane_helper_prepare_fb(struct drm_plane *plane,
        uint64_t tiling_flags;
        uint32_t domain;
        int r;
+       bool force_disable_dcc = false;
 
        dm_plane_state_old = to_dm_plane_state(plane->state);
        dm_plane_state_new = to_dm_plane_state(new_state);
@@ -5390,11 +5412,13 @@ static int dm_plane_helper_prepare_fb(struct drm_plane *plane,
                        dm_plane_state_old->dc_state != dm_plane_state_new->dc_state) {
                struct dc_plane_state *plane_state = dm_plane_state_new->dc_state;
 
+               force_disable_dcc = adev->asic_type == CHIP_RAVEN && adev->in_suspend;
                fill_plane_buffer_attributes(
                        adev, afb, plane_state->format, plane_state->rotation,
                        tiling_flags, &plane_state->tiling_info,
                        &plane_state->plane_size, &plane_state->dcc,
-                       &plane_state->address);
+                       &plane_state->address,
+                       force_disable_dcc);
        }
 
        return 0;
@@ -6092,7 +6116,7 @@ static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
 
        if (connector_type == DRM_MODE_CONNECTOR_DisplayPort
                || connector_type == DRM_MODE_CONNECTOR_eDP)
-               amdgpu_dm_initialize_dp_connector(dm, aconnector);
+               amdgpu_dm_initialize_dp_connector(dm, aconnector, link->link_index);
 
 out_free:
        if (res) {
@@ -6666,7 +6690,12 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
                fill_dc_plane_info_and_addr(
                        dm->adev, new_plane_state, tiling_flags,
                        &bundle->plane_infos[planes_count],
-                       &bundle->flip_addrs[planes_count].address);
+                       &bundle->flip_addrs[planes_count].address,
+                       false);
+
+               DRM_DEBUG_DRIVER("plane: id=%d dcc_en=%d\n",
+                                new_plane_state->plane->index,
+                                bundle->plane_infos[planes_count].dcc.enable);
 
                bundle->surface_updates[planes_count].plane_info =
                        &bundle->plane_infos[planes_count];
@@ -8086,7 +8115,8 @@ dm_determine_update_type_for_commit(struct amdgpu_display_manager *dm,
                                ret = fill_dc_plane_info_and_addr(
                                        dm->adev, new_plane_state, tiling_flags,
                                        plane_info,
-                                       &flip_addr->address);
+                                       &flip_addr->address,
+                                       false);
                                if (ret)
                                        goto cleanup;
 
index fabbe78..d291775 100644 (file)
@@ -156,16 +156,16 @@ amdgpu_dm_mst_connector_late_register(struct drm_connector *connector)
                to_amdgpu_dm_connector(connector);
        int r;
 
-       amdgpu_dm_connector->dm_dp_aux.aux.dev = connector->kdev;
-       r = drm_dp_aux_register(&amdgpu_dm_connector->dm_dp_aux.aux);
-       if (r)
+       r = drm_dp_mst_connector_late_register(connector,
+                                              amdgpu_dm_connector->port);
+       if (r < 0)
                return r;
 
 #if defined(CONFIG_DEBUG_FS)
        connector_debugfs_init(amdgpu_dm_connector);
 #endif
 
-       return r;
+       return 0;
 }
 
 static void
@@ -472,9 +472,12 @@ static const struct drm_dp_mst_topology_cbs dm_mst_cbs = {
 };
 
 void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm,
-                                      struct amdgpu_dm_connector *aconnector)
+                                      struct amdgpu_dm_connector *aconnector,
+                                      int link_index)
 {
-       aconnector->dm_dp_aux.aux.name = "dmdc";
+       aconnector->dm_dp_aux.aux.name =
+               kasprintf(GFP_KERNEL, "AMDGPU DM aux hw bus %d",
+                         link_index);
        aconnector->dm_dp_aux.aux.transfer = dm_dp_aux_transfer;
        aconnector->dm_dp_aux.ddc_service = aconnector->dc_link->ddc;
 
index d6813ce..d2c5657 100644 (file)
@@ -32,7 +32,8 @@ struct amdgpu_dm_connector;
 int dm_mst_get_pbn_divider(struct dc_link *link);
 
 void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm,
-                                      struct amdgpu_dm_connector *aconnector);
+                                      struct amdgpu_dm_connector *aconnector,
+                                      int link_index);
 
 #if defined(CONFIG_DRM_AMD_DC_DCN)
 bool compute_mst_dsc_configs_for_state(struct drm_atomic_state *state,
index 7cbb1ef..27a7d2a 100644 (file)
@@ -2908,6 +2908,12 @@ bool dc_link_handle_hpd_rx_irq(struct dc_link *link, union hpd_irq_data *out_hpd
                                        sizeof(hpd_irq_dpcd_data),
                                        "Status: ");
 
+               for (i = 0; i < MAX_PIPES; i++) {
+                       pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
+                       if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link == link)
+                               link->dc->hwss.blank_stream(pipe_ctx);
+               }
+
                for (i = 0; i < MAX_PIPES; i++) {
                        pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
                        if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link == link)
@@ -2927,6 +2933,12 @@ bool dc_link_handle_hpd_rx_irq(struct dc_link *link, union hpd_irq_data *out_hpd
                if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
                        dc_link_reallocate_mst_payload(link);
 
+               for (i = 0; i < MAX_PIPES; i++) {
+                       pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
+                       if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->link == link)
+                               link->dc->hwss.unblank_stream(pipe_ctx, &previous_link_settings);
+               }
+
                status = false;
                if (out_link_loss)
                        *out_link_loss = true;
@@ -4227,6 +4239,21 @@ void dp_set_fec_enable(struct dc_link *link, bool enable)
 void dpcd_set_source_specific_data(struct dc_link *link)
 {
        const uint32_t post_oui_delay = 30; // 30ms
+       uint8_t dspc = 0;
+       enum dc_status ret = DC_ERROR_UNEXPECTED;
+
+       ret = core_link_read_dpcd(link, DP_DOWN_STREAM_PORT_COUNT, &dspc,
+                                 sizeof(dspc));
+
+       if (ret != DC_OK) {
+               DC_LOG_ERROR("Error in DP aux read transaction,"
+                            " not writing source specific data\n");
+               return;
+       }
+
+       /* Return if OUI unsupported */
+       if (!(dspc & DP_OUI_SUPPORT))
+               return;
 
        if (!link->dc->vendor_signature.is_valid) {
                struct dpcd_amd_signature amd_signature;
index 6ddbb00..4f0e720 100644 (file)
@@ -231,34 +231,6 @@ struct dc_stream_status *dc_stream_get_status(
        return dc_stream_get_status_from_state(dc->current_state, stream);
 }
 
-static void delay_cursor_until_vupdate(struct pipe_ctx *pipe_ctx, struct dc *dc)
-{
-#if defined(CONFIG_DRM_AMD_DC_DCN)
-       unsigned int vupdate_line;
-       unsigned int lines_to_vupdate, us_to_vupdate, vpos, nvpos;
-       struct dc_stream_state *stream = pipe_ctx->stream;
-       unsigned int us_per_line;
-
-       if (stream->ctx->asic_id.chip_family == FAMILY_RV &&
-                       ASICREV_IS_RAVEN(stream->ctx->asic_id.hw_internal_rev)) {
-
-               vupdate_line = dc->hwss.get_vupdate_offset_from_vsync(pipe_ctx);
-               if (!dc_stream_get_crtc_position(dc, &stream, 1, &vpos, &nvpos))
-                       return;
-
-               if (vpos >= vupdate_line)
-                       return;
-
-               us_per_line = stream->timing.h_total * 10000 / stream->timing.pix_clk_100hz;
-               lines_to_vupdate = vupdate_line - vpos;
-               us_to_vupdate = lines_to_vupdate * us_per_line;
-
-               /* 70 us is a conservative estimate of cursor update time*/
-               if (us_to_vupdate < 70)
-                       udelay(us_to_vupdate);
-       }
-#endif
-}
 
 /**
  * dc_stream_set_cursor_attributes() - Update cursor attributes and set cursor surface address
@@ -298,9 +270,7 @@ bool dc_stream_set_cursor_attributes(
 
                if (!pipe_to_program) {
                        pipe_to_program = pipe_ctx;
-
-                       delay_cursor_until_vupdate(pipe_ctx, dc);
-                       dc->hwss.pipe_control_lock(dc, pipe_to_program, true);
+                       dc->hwss.cursor_lock(dc, pipe_to_program, true);
                }
 
                dc->hwss.set_cursor_attribute(pipe_ctx);
@@ -309,7 +279,7 @@ bool dc_stream_set_cursor_attributes(
        }
 
        if (pipe_to_program)
-               dc->hwss.pipe_control_lock(dc, pipe_to_program, false);
+               dc->hwss.cursor_lock(dc, pipe_to_program, false);
 
        return true;
 }
@@ -349,16 +319,14 @@ bool dc_stream_set_cursor_position(
 
                if (!pipe_to_program) {
                        pipe_to_program = pipe_ctx;
-
-                       delay_cursor_until_vupdate(pipe_ctx, dc);
-                       dc->hwss.pipe_control_lock(dc, pipe_to_program, true);
+                       dc->hwss.cursor_lock(dc, pipe_to_program, true);
                }
 
                dc->hwss.set_cursor_position(pipe_ctx);
        }
 
        if (pipe_to_program)
-               dc->hwss.pipe_control_lock(dc, pipe_to_program, false);
+               dc->hwss.cursor_lock(dc, pipe_to_program, false);
 
        return true;
 }
index c279982..1052759 100644 (file)
@@ -2757,6 +2757,7 @@ static const struct hw_sequencer_funcs dce110_funcs = {
        .disable_plane = dce110_power_down_fe,
        .pipe_control_lock = dce_pipe_control_lock,
        .interdependent_update_lock = NULL,
+       .cursor_lock = dce_pipe_control_lock,
        .prepare_bandwidth = dce110_prepare_bandwidth,
        .optimize_bandwidth = dce110_optimize_bandwidth,
        .set_drr = set_drr,
index b035754..085c1a3 100644 (file)
@@ -1625,6 +1625,16 @@ void dcn10_pipe_control_lock(
                hws->funcs.verify_allow_pstate_change_high(dc);
 }
 
+void dcn10_cursor_lock(struct dc *dc, struct pipe_ctx *pipe, bool lock)
+{
+       /* cursor lock is per MPCC tree, so only need to lock one pipe per stream */
+       if (!pipe || pipe->top_pipe)
+               return;
+
+       dc->res_pool->mpc->funcs->cursor_lock(dc->res_pool->mpc,
+                       pipe->stream_res.opp->inst, lock);
+}
+
 static bool wait_for_reset_trigger_to_occur(
        struct dc_context *dc_ctx,
        struct timing_generator *tg)
index 16a50e0..af51424 100644 (file)
@@ -49,6 +49,7 @@ void dcn10_pipe_control_lock(
        struct dc *dc,
        struct pipe_ctx *pipe,
        bool lock);
+void dcn10_cursor_lock(struct dc *dc, struct pipe_ctx *pipe, bool lock);
 void dcn10_blank_pixel_data(
                struct dc *dc,
                struct pipe_ctx *pipe_ctx,
index dd02d39..700509b 100644 (file)
@@ -50,6 +50,7 @@ static const struct hw_sequencer_funcs dcn10_funcs = {
        .disable_audio_stream = dce110_disable_audio_stream,
        .disable_plane = dcn10_disable_plane,
        .pipe_control_lock = dcn10_pipe_control_lock,
+       .cursor_lock = dcn10_cursor_lock,
        .interdependent_update_lock = dcn10_lock_all_pipes,
        .prepare_bandwidth = dcn10_prepare_bandwidth,
        .optimize_bandwidth = dcn10_optimize_bandwidth,
index 04f8634..3fcd408 100644 (file)
@@ -223,6 +223,9 @@ struct mpcc *mpc1_insert_plane(
        REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, dpp_id);
        REG_SET(MPCC_OPP_ID[mpcc_id], 0, MPCC_OPP_ID, tree->opp_id);
 
+       /* Configure VUPDATE lock set for this MPCC to map to the OPP */
+       REG_SET(MPCC_UPDATE_LOCK_SEL[mpcc_id], 0, MPCC_UPDATE_LOCK_SEL, tree->opp_id);
+
        /* update mpc tree mux setting */
        if (tree->opp_list == insert_above_mpcc) {
                /* insert the toppest mpcc */
@@ -318,6 +321,7 @@ void mpc1_remove_mpcc(
                REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, 0xf);
                REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
                REG_SET(MPCC_OPP_ID[mpcc_id],  0, MPCC_OPP_ID,  0xf);
+               REG_SET(MPCC_UPDATE_LOCK_SEL[mpcc_id], 0, MPCC_UPDATE_LOCK_SEL, 0xf);
 
                /* mark this mpcc as not in use */
                mpc10->mpcc_in_use_mask &= ~(1 << mpcc_id);
@@ -328,6 +332,7 @@ void mpc1_remove_mpcc(
                REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, 0xf);
                REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
                REG_SET(MPCC_OPP_ID[mpcc_id],  0, MPCC_OPP_ID,  0xf);
+               REG_SET(MPCC_UPDATE_LOCK_SEL[mpcc_id], 0, MPCC_UPDATE_LOCK_SEL, 0xf);
        }
 }
 
@@ -361,6 +366,7 @@ void mpc1_mpc_init(struct mpc *mpc)
                REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, 0xf);
                REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
                REG_SET(MPCC_OPP_ID[mpcc_id],  0, MPCC_OPP_ID,  0xf);
+               REG_SET(MPCC_UPDATE_LOCK_SEL[mpcc_id], 0, MPCC_UPDATE_LOCK_SEL, 0xf);
 
                mpc1_init_mpcc(&(mpc->mpcc_array[mpcc_id]), mpcc_id);
        }
@@ -381,6 +387,7 @@ void mpc1_mpc_init_single_inst(struct mpc *mpc, unsigned int mpcc_id)
        REG_SET(MPCC_TOP_SEL[mpcc_id], 0, MPCC_TOP_SEL, 0xf);
        REG_SET(MPCC_BOT_SEL[mpcc_id], 0, MPCC_BOT_SEL, 0xf);
        REG_SET(MPCC_OPP_ID[mpcc_id],  0, MPCC_OPP_ID,  0xf);
+       REG_SET(MPCC_UPDATE_LOCK_SEL[mpcc_id], 0, MPCC_UPDATE_LOCK_SEL, 0xf);
 
        mpc1_init_mpcc(&(mpc->mpcc_array[mpcc_id]), mpcc_id);
 
@@ -453,6 +460,13 @@ void mpc1_read_mpcc_state(
                        MPCC_BUSY, &s->busy);
 }
 
+void mpc1_cursor_lock(struct mpc *mpc, int opp_id, bool lock)
+{
+       struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
+
+       REG_SET(CUR[opp_id], 0, CUR_VUPDATE_LOCK_SET, lock ? 1 : 0);
+}
+
 static const struct mpc_funcs dcn10_mpc_funcs = {
        .read_mpcc_state = mpc1_read_mpcc_state,
        .insert_plane = mpc1_insert_plane,
@@ -464,6 +478,7 @@ static const struct mpc_funcs dcn10_mpc_funcs = {
        .assert_mpcc_idle_before_connect = mpc1_assert_mpcc_idle_before_connect,
        .init_mpcc_list_from_hw = mpc1_init_mpcc_list_from_hw,
        .update_blending = mpc1_update_blending,
+       .cursor_lock = mpc1_cursor_lock,
        .set_denorm = NULL,
        .set_denorm_clamp = NULL,
        .set_output_csc = NULL,
index 962a68e..66a4719 100644 (file)
        SRII(MPCC_BG_G_Y, MPCC, inst),\
        SRII(MPCC_BG_R_CR, MPCC, inst),\
        SRII(MPCC_BG_B_CB, MPCC, inst),\
-       SRII(MPCC_BG_B_CB, MPCC, inst),\
-       SRII(MPCC_SM_CONTROL, MPCC, inst)
+       SRII(MPCC_SM_CONTROL, MPCC, inst),\
+       SRII(MPCC_UPDATE_LOCK_SEL, MPCC, inst)
 
 #define MPC_OUT_MUX_COMMON_REG_LIST_DCN1_0(inst) \
-       SRII(MUX, MPC_OUT, inst)
+       SRII(MUX, MPC_OUT, inst),\
+       VUPDATE_SRII(CUR, VUPDATE_LOCK_SET, inst)
 
 #define MPC_COMMON_REG_VARIABLE_LIST \
        uint32_t MPCC_TOP_SEL[MAX_MPCC]; \
@@ -55,7 +56,9 @@
        uint32_t MPCC_BG_R_CR[MAX_MPCC]; \
        uint32_t MPCC_BG_B_CB[MAX_MPCC]; \
        uint32_t MPCC_SM_CONTROL[MAX_MPCC]; \
-       uint32_t MUX[MAX_OPP];
+       uint32_t MUX[MAX_OPP]; \
+       uint32_t MPCC_UPDATE_LOCK_SEL[MAX_MPCC]; \
+       uint32_t CUR[MAX_OPP];
 
 #define MPC_COMMON_MASK_SH_LIST_DCN1_0(mask_sh)\
        SF(MPCC0_MPCC_TOP_SEL, MPCC_TOP_SEL, mask_sh),\
@@ -78,7 +81,8 @@
        SF(MPCC0_MPCC_SM_CONTROL, MPCC_SM_FIELD_ALT, mask_sh),\
        SF(MPCC0_MPCC_SM_CONTROL, MPCC_SM_FORCE_NEXT_FRAME_POL, mask_sh),\
        SF(MPCC0_MPCC_SM_CONTROL, MPCC_SM_FORCE_NEXT_TOP_POL, mask_sh),\
-       SF(MPC_OUT0_MUX, MPC_OUT_MUX, mask_sh)
+       SF(MPC_OUT0_MUX, MPC_OUT_MUX, mask_sh),\
+       SF(MPCC0_MPCC_UPDATE_LOCK_SEL, MPCC_UPDATE_LOCK_SEL, mask_sh)
 
 #define MPC_REG_FIELD_LIST(type) \
        type MPCC_TOP_SEL;\
        type MPCC_SM_FIELD_ALT;\
        type MPCC_SM_FORCE_NEXT_FRAME_POL;\
        type MPCC_SM_FORCE_NEXT_TOP_POL;\
-       type MPC_OUT_MUX;
+       type MPC_OUT_MUX;\
+       type MPCC_UPDATE_LOCK_SEL;\
+       type CUR_VUPDATE_LOCK_SET;
 
 struct dcn_mpc_registers {
        MPC_COMMON_REG_VARIABLE_LIST
@@ -192,4 +198,6 @@ void mpc1_read_mpcc_state(
                int mpcc_inst,
                struct mpcc_state *s);
 
+void mpc1_cursor_lock(struct mpc *mpc, int opp_id, bool lock);
+
 #endif
index 07265ca..ba849aa 100644 (file)
@@ -181,6 +181,14 @@ enum dcn10_clk_src_array_id {
        .reg_name[id] = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
                                        mm ## block ## id ## _ ## reg_name
 
+#define VUPDATE_SRII(reg_name, block, id)\
+       .reg_name[id] = BASE(mm ## reg_name ## 0 ## _ ## block ## id ## _BASE_IDX) + \
+                                       mm ## reg_name ## 0 ## _ ## block ## id
+
+/* set field/register/bitfield name */
+#define SFRB(field_name, reg_name, bitfield, post_fix)\
+       .field_name = reg_name ## __ ## bitfield ## post_fix
+
 /* NBIO */
 #define NBIO_BASE_INNER(seg) \
        NBIF_BASE__INST0_SEG ## seg
@@ -419,11 +427,13 @@ static const struct dcn_mpc_registers mpc_regs = {
 };
 
 static const struct dcn_mpc_shift mpc_shift = {
-       MPC_COMMON_MASK_SH_LIST_DCN1_0(__SHIFT)
+       MPC_COMMON_MASK_SH_LIST_DCN1_0(__SHIFT),\
+       SFRB(CUR_VUPDATE_LOCK_SET, CUR0_VUPDATE_LOCK_SET0, CUR0_VUPDATE_LOCK_SET, __SHIFT)
 };
 
 static const struct dcn_mpc_mask mpc_mask = {
-       MPC_COMMON_MASK_SH_LIST_DCN1_0(_MASK),
+       MPC_COMMON_MASK_SH_LIST_DCN1_0(_MASK),\
+       SFRB(CUR_VUPDATE_LOCK_SET, CUR0_VUPDATE_LOCK_SET0, CUR0_VUPDATE_LOCK_SET, _MASK)
 };
 
 #define tg_regs(id)\
index 22f421e..a023a4d 100644 (file)
@@ -2294,7 +2294,8 @@ void dcn20_fpga_init_hw(struct dc *dc)
 
        REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_REFDIV, 2);
        REG_UPDATE(DCHUBBUB_GLOBAL_TIMER_CNTL, DCHUBBUB_GLOBAL_TIMER_ENABLE, 1);
-       REG_WRITE(REFCLK_CNTL, 0);
+       if (REG(REFCLK_CNTL))
+               REG_WRITE(REFCLK_CNTL, 0);
        //
 
 
index 1e73357..6a21228 100644 (file)
@@ -52,6 +52,7 @@ static const struct hw_sequencer_funcs dcn20_funcs = {
        .disable_plane = dcn20_disable_plane,
        .pipe_control_lock = dcn20_pipe_control_lock,
        .interdependent_update_lock = dcn10_lock_all_pipes,
+       .cursor_lock = dcn10_cursor_lock,
        .prepare_bandwidth = dcn20_prepare_bandwidth,
        .optimize_bandwidth = dcn20_optimize_bandwidth,
        .update_bandwidth = dcn20_update_bandwidth,
index de9c857..570dfd9 100644 (file)
@@ -545,6 +545,7 @@ const struct mpc_funcs dcn20_mpc_funcs = {
        .mpc_init = mpc1_mpc_init,
        .mpc_init_single_inst = mpc1_mpc_init_single_inst,
        .update_blending = mpc2_update_blending,
+       .cursor_lock = mpc1_cursor_lock,
        .get_mpcc_for_dpp = mpc2_get_mpcc_for_dpp,
        .wait_for_idle = mpc2_assert_idle_mpcc,
        .assert_mpcc_idle_before_connect = mpc2_assert_mpcc_idle_before_connect,
index c78fd51..496658f 100644 (file)
        SF(MPC_OUT0_DENORM_CLAMP_G_Y, MPC_OUT_DENORM_CLAMP_MAX_G_Y, mask_sh),\
        SF(MPC_OUT0_DENORM_CLAMP_G_Y, MPC_OUT_DENORM_CLAMP_MIN_G_Y, mask_sh),\
        SF(MPC_OUT0_DENORM_CLAMP_B_CB, MPC_OUT_DENORM_CLAMP_MAX_B_CB, mask_sh),\
-       SF(MPC_OUT0_DENORM_CLAMP_B_CB, MPC_OUT_DENORM_CLAMP_MIN_B_CB, mask_sh)
+       SF(MPC_OUT0_DENORM_CLAMP_B_CB, MPC_OUT_DENORM_CLAMP_MIN_B_CB, mask_sh),\
+       SF(CUR_VUPDATE_LOCK_SET0, CUR_VUPDATE_LOCK_SET, mask_sh)
 
 /*
  *     DCN2 MPC_OCSC debug status register:
index 5cdbba0..c3535bd 100644 (file)
@@ -508,6 +508,10 @@ enum dcn20_clk_src_array_id {
        .block ## _ ## reg_name[id] = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
                                        mm ## block ## id ## _ ## reg_name
 
+#define VUPDATE_SRII(reg_name, block, id)\
+       .reg_name[id] = BASE(mm ## reg_name ## _ ## block ## id ## _BASE_IDX) + \
+                                       mm ## reg_name ## _ ## block ## id
+
 /* NBIO */
 #define NBIO_BASE_INNER(seg) \
        NBIO_BASE__INST0_SEG ## seg
index b9ff976..707ce0f 100644 (file)
@@ -53,6 +53,7 @@ static const struct hw_sequencer_funcs dcn21_funcs = {
        .disable_plane = dcn20_disable_plane,
        .pipe_control_lock = dcn20_pipe_control_lock,
        .interdependent_update_lock = dcn10_lock_all_pipes,
+       .cursor_lock = dcn10_cursor_lock,
        .prepare_bandwidth = dcn20_prepare_bandwidth,
        .optimize_bandwidth = dcn20_optimize_bandwidth,
        .update_bandwidth = dcn20_update_bandwidth,
index b25484a..a721bb4 100644 (file)
@@ -284,7 +284,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn2_1_soc = {
        .dram_channel_width_bytes = 4,
        .fabric_datapath_to_dcn_data_return_bytes = 32,
        .dcn_downspread_percent = 0.5,
-       .downspread_percent = 0.5,
+       .downspread_percent = 0.38,
        .dram_page_open_time_ns = 50.0,
        .dram_rw_turnaround_time_ns = 17.5,
        .dram_return_buffer_per_channel_bytes = 8192,
@@ -340,6 +340,10 @@ struct _vcs_dpi_soc_bounding_box_st dcn2_1_soc = {
        .block ## _ ## reg_name[id] = BASE(mm ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
                                        mm ## block ## id ## _ ## reg_name
 
+#define VUPDATE_SRII(reg_name, block, id)\
+       .reg_name[id] = BASE(mm ## reg_name ## _ ## block ## id ## _BASE_IDX) + \
+                                       mm ## reg_name ## _ ## block ## id
+
 /* NBIO */
 #define NBIO_BASE_INNER(seg) \
        NBIF0_BASE__INST0_SEG ## seg
@@ -1374,64 +1378,49 @@ static void update_bw_bounding_box(struct dc *dc, struct clk_bw_params *bw_param
 {
        struct dcn21_resource_pool *pool = TO_DCN21_RES_POOL(dc->res_pool);
        struct clk_limit_table *clk_table = &bw_params->clk_table;
-       unsigned int i, j, k;
-       int closest_clk_lvl;
+       struct _vcs_dpi_voltage_scaling_st clock_limits[DC__VOLTAGE_STATES];
+       unsigned int i, j, closest_clk_lvl;
 
        // Default clock levels are used for diags, which may lead to overclocking.
-       if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment) && !IS_DIAG_DC(dc->ctx->dce_environment)) {
+       if (!IS_DIAG_DC(dc->ctx->dce_environment)) {
                dcn2_1_ip.max_num_otg = pool->base.res_cap->num_timing_generator;
                dcn2_1_ip.max_num_dpp = pool->base.pipe_count;
                dcn2_1_soc.num_chans = bw_params->num_channels;
 
-               /* Vmin: leave lowest DCN clocks, override with dcfclk, fclk, memclk from fuse */
-               dcn2_1_soc.clock_limits[0].state = 0;
-               dcn2_1_soc.clock_limits[0].dcfclk_mhz = clk_table->entries[0].dcfclk_mhz;
-               dcn2_1_soc.clock_limits[0].fabricclk_mhz = clk_table->entries[0].fclk_mhz;
-               dcn2_1_soc.clock_limits[0].socclk_mhz = clk_table->entries[0].socclk_mhz;
-               dcn2_1_soc.clock_limits[0].dram_speed_mts = clk_table->entries[0].memclk_mhz * 2;
-
-               /*
-                * Other levels: find closest DCN clocks that fit the given clock limit using dcfclk
-                * as indicator
-                */
-
-               closest_clk_lvl = -1;
-               /* index currently being filled */
-               k = 1;
-               for (i = 1; i < clk_table->num_entries; i++) {
-                       /* loop backwards, skip duplicate state*/
-                       for (j = dcn2_1_soc.num_states - 1; j >= k; j--) {
+               ASSERT(clk_table->num_entries);
+               for (i = 0; i < clk_table->num_entries; i++) {
+                       /* loop backwards*/
+                       for (closest_clk_lvl = 0, j = dcn2_1_soc.num_states - 1; j >= 0; j--) {
                                if ((unsigned int) dcn2_1_soc.clock_limits[j].dcfclk_mhz <= clk_table->entries[i].dcfclk_mhz) {
                                        closest_clk_lvl = j;
                                        break;
                                }
                        }
 
-                       /* if found a lvl that fits, use the DCN clks from it, if not, go to next clk limit*/
-                       if (closest_clk_lvl != -1) {
-                               dcn2_1_soc.clock_limits[k].state = i;
-                               dcn2_1_soc.clock_limits[k].dcfclk_mhz = clk_table->entries[i].dcfclk_mhz;
-                               dcn2_1_soc.clock_limits[k].fabricclk_mhz = clk_table->entries[i].fclk_mhz;
-                               dcn2_1_soc.clock_limits[k].socclk_mhz = clk_table->entries[i].socclk_mhz;
-                               dcn2_1_soc.clock_limits[k].dram_speed_mts = clk_table->entries[i].memclk_mhz * 2;
-
-                               dcn2_1_soc.clock_limits[k].dispclk_mhz = dcn2_1_soc.clock_limits[closest_clk_lvl].dispclk_mhz;
-                               dcn2_1_soc.clock_limits[k].dppclk_mhz = dcn2_1_soc.clock_limits[closest_clk_lvl].dppclk_mhz;
-                               dcn2_1_soc.clock_limits[k].dram_bw_per_chan_gbps = dcn2_1_soc.clock_limits[closest_clk_lvl].dram_bw_per_chan_gbps;
-                               dcn2_1_soc.clock_limits[k].dscclk_mhz = dcn2_1_soc.clock_limits[closest_clk_lvl].dscclk_mhz;
-                               dcn2_1_soc.clock_limits[k].dtbclk_mhz = dcn2_1_soc.clock_limits[closest_clk_lvl].dtbclk_mhz;
-                               dcn2_1_soc.clock_limits[k].phyclk_d18_mhz = dcn2_1_soc.clock_limits[closest_clk_lvl].phyclk_d18_mhz;
-                               dcn2_1_soc.clock_limits[k].phyclk_mhz = dcn2_1_soc.clock_limits[closest_clk_lvl].phyclk_mhz;
-                               k++;
-                       }
+                       clock_limits[i].state = i;
+                       clock_limits[i].dcfclk_mhz = clk_table->entries[i].dcfclk_mhz;
+                       clock_limits[i].fabricclk_mhz = clk_table->entries[i].fclk_mhz;
+                       clock_limits[i].socclk_mhz = clk_table->entries[i].socclk_mhz;
+                       clock_limits[i].dram_speed_mts = clk_table->entries[i].memclk_mhz * 2;
+
+                       clock_limits[i].dispclk_mhz = dcn2_1_soc.clock_limits[closest_clk_lvl].dispclk_mhz;
+                       clock_limits[i].dppclk_mhz = dcn2_1_soc.clock_limits[closest_clk_lvl].dppclk_mhz;
+                       clock_limits[i].dram_bw_per_chan_gbps = dcn2_1_soc.clock_limits[closest_clk_lvl].dram_bw_per_chan_gbps;
+                       clock_limits[i].dscclk_mhz = dcn2_1_soc.clock_limits[closest_clk_lvl].dscclk_mhz;
+                       clock_limits[i].dtbclk_mhz = dcn2_1_soc.clock_limits[closest_clk_lvl].dtbclk_mhz;
+                       clock_limits[i].phyclk_d18_mhz = dcn2_1_soc.clock_limits[closest_clk_lvl].phyclk_d18_mhz;
+                       clock_limits[i].phyclk_mhz = dcn2_1_soc.clock_limits[closest_clk_lvl].phyclk_mhz;
+               }
+               for (i = 0; i < clk_table->num_entries; i++)
+                       dcn2_1_soc.clock_limits[i] = clock_limits[i];
+               if (clk_table->num_entries) {
+                       dcn2_1_soc.num_states = clk_table->num_entries;
+                       /* duplicate last level */
+                       dcn2_1_soc.clock_limits[dcn2_1_soc.num_states] = dcn2_1_soc.clock_limits[dcn2_1_soc.num_states - 1];
+                       dcn2_1_soc.clock_limits[dcn2_1_soc.num_states].state = dcn2_1_soc.num_states;
                }
-               dcn2_1_soc.num_states = k;
        }
 
-       /* duplicate last level */
-       dcn2_1_soc.clock_limits[dcn2_1_soc.num_states] = dcn2_1_soc.clock_limits[dcn2_1_soc.num_states - 1];
-       dcn2_1_soc.clock_limits[dcn2_1_soc.num_states].state = dcn2_1_soc.num_states;
-
        dml_init_instance(&dc->dml, &dcn2_1_soc, &dcn2_1_ip, DML_PROJECT_DCN21);
 }
 
index 094afc4..50ee8aa 100644 (file)
@@ -210,6 +210,22 @@ struct mpc_funcs {
                struct mpcc_blnd_cfg *blnd_cfg,
                int mpcc_id);
 
+       /*
+        * Lock cursor updates for the specified OPP.
+        * OPP defines the set of MPCC that are locked together for cursor.
+        *
+        * Parameters:
+        * [in]         mpc             - MPC context.
+        * [in]     opp_id      - The OPP to lock cursor updates on
+        * [in]         lock    - lock/unlock the OPP
+        *
+        * Return:  void
+        */
+       void (*cursor_lock)(
+                       struct mpc *mpc,
+                       int opp_id,
+                       bool lock);
+
        struct mpcc* (*get_mpcc_for_dpp)(
                        struct mpc_tree *tree,
                        int dpp_id);
index d4c1fb2..e57467d 100644 (file)
@@ -86,6 +86,7 @@ struct hw_sequencer_funcs {
                        struct dc_state *context, bool lock);
        void (*set_flip_control_gsl)(struct pipe_ctx *pipe_ctx,
                        bool flip_immediate);
+       void (*cursor_lock)(struct dc *dc, struct pipe_ctx *pipe, bool lock);
 
        /* Timing Related */
        void (*get_position)(struct pipe_ctx **pipe_ctx, int num_pipes,
index 2a12614..e4e5a53 100644 (file)
@@ -1435,7 +1435,8 @@ static int pp_get_asic_baco_capability(void *handle, bool *cap)
        if (!hwmgr)
                return -EINVAL;
 
-       if (!hwmgr->pm_en || !hwmgr->hwmgr_func->get_asic_baco_capability)
+       if (!(hwmgr->not_vf && amdgpu_dpm) ||
+               !hwmgr->hwmgr_func->get_asic_baco_capability)
                return 0;
 
        mutex_lock(&hwmgr->smu_lock);
@@ -1452,8 +1453,7 @@ static int pp_get_asic_baco_state(void *handle, int *state)
        if (!hwmgr)
                return -EINVAL;
 
-       if (!(hwmgr->not_vf && amdgpu_dpm) ||
-               !hwmgr->hwmgr_func->get_asic_baco_state)
+       if (!hwmgr->pm_en || !hwmgr->hwmgr_func->get_asic_baco_state)
                return 0;
 
        mutex_lock(&hwmgr->smu_lock);
@@ -1470,7 +1470,8 @@ static int pp_set_asic_baco_state(void *handle, int state)
        if (!hwmgr)
                return -EINVAL;
 
-       if (!hwmgr->pm_en || !hwmgr->hwmgr_func->set_asic_baco_state)
+       if (!(hwmgr->not_vf && amdgpu_dpm) ||
+               !hwmgr->hwmgr_func->set_asic_baco_state)
                return 0;
 
        mutex_lock(&hwmgr->smu_lock);
index 77c1467..719597c 100644 (file)
@@ -984,6 +984,32 @@ static int init_thermal_controller(
                        struct pp_hwmgr *hwmgr,
                        const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
 {
+       hwmgr->thermal_controller.ucType =
+                       powerplay_table->sThermalController.ucType;
+       hwmgr->thermal_controller.ucI2cLine =
+                       powerplay_table->sThermalController.ucI2cLine;
+       hwmgr->thermal_controller.ucI2cAddress =
+                       powerplay_table->sThermalController.ucI2cAddress;
+
+       hwmgr->thermal_controller.fanInfo.bNoFan =
+               (0 != (powerplay_table->sThermalController.ucFanParameters &
+                       ATOM_PP_FANPARAMETERS_NOFAN));
+
+       hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution =
+               powerplay_table->sThermalController.ucFanParameters &
+               ATOM_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK;
+
+       hwmgr->thermal_controller.fanInfo.ulMinRPM
+               = powerplay_table->sThermalController.ucFanMinRPM * 100UL;
+       hwmgr->thermal_controller.fanInfo.ulMaxRPM
+               = powerplay_table->sThermalController.ucFanMaxRPM * 100UL;
+
+       set_hw_cap(hwmgr,
+                  ATOM_PP_THERMALCONTROLLER_NONE != hwmgr->thermal_controller.ucType,
+                  PHM_PlatformCaps_ThermalController);
+
+       hwmgr->thermal_controller.use_hw_fan_control = 1;
+
        return 0;
 }
 
index 7740488..4795eb6 100644 (file)
@@ -3804,9 +3804,12 @@ static int smu7_trim_single_dpm_states(struct pp_hwmgr *hwmgr,
 {
        uint32_t i;
 
+       /* force the trim if mclk_switching is disabled to prevent flicker */
+       bool force_trim = (low_limit == high_limit);
        for (i = 0; i < dpm_table->count; i++) {
        /*skip the trim if od is enabled*/
-               if (!hwmgr->od_enabled && (dpm_table->dpm_levels[i].value < low_limit
+               if ((!hwmgr->od_enabled || force_trim)
+                       && (dpm_table->dpm_levels[i].value < low_limit
                        || dpm_table->dpm_levels[i].value > high_limit))
                        dpm_table->dpm_levels[i].enabled = false;
                else
index ff73a73..b0ed1b3 100644 (file)
@@ -895,12 +895,17 @@ static int renoir_read_sensor(struct smu_context *smu,
 
 static bool renoir_is_dpm_running(struct smu_context *smu)
 {
+       struct amdgpu_device *adev = smu->adev;
+
        /*
         * Util now, the pmfw hasn't exported the interface of SMU
         * feature mask to APU SKU so just force on all the feature
         * at early initial stage.
         */
-       return true;
+       if (adev->in_suspend)
+               return false;
+       else
+               return true;
 
 }
 
index 541c932..655ba4f 100644 (file)
@@ -1718,6 +1718,12 @@ int smu_v11_0_baco_set_state(struct smu_context *smu, enum smu_baco_state state)
                if (ret)
                        goto out;
 
+               if (ras && ras->supported) {
+                       ret = smu_send_smc_msg(smu, SMU_MSG_PrepareMp1ForUnload, NULL);
+                       if (ret)
+                               goto out;
+               }
+
                /* clear vbios scratch 6 and 7 for coming asic reinit */
                WREG32(adev->bios_scratch_reg_offset + 6, 0);
                WREG32(adev->bios_scratch_reg_offset + 7, 0);
index 2bc6e4f..9af39ec 100644 (file)
@@ -485,6 +485,9 @@ static int anx6345_get_modes(struct drm_connector *connector)
 
        num_modes += drm_add_edid_modes(connector, anx6345->edid);
 
+       /* Driver currently supports only 6bpc */
+       connector->display_info.bpc = 6;
+
 unlock:
        if (power_off)
                anx6345_poweroff(anx6345);
index 70c4b7a..9d89ebf 100644 (file)
@@ -3442,8 +3442,12 @@ static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
        drm_dp_queue_down_tx(mgr, txmsg);
 
        ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
-       if (ret > 0 && txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
-               ret = -EIO;
+       if (ret > 0) {
+               if (txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
+                       ret = -EIO;
+               else
+                       ret = size;
+       }
 
        kfree(txmsg);
 fail_put:
@@ -4295,6 +4299,7 @@ int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
        if (pos->vcpi) {
                drm_dp_mst_put_port_malloc(port);
                pos->vcpi = 0;
+               pos->pbn = 0;
        }
 
        return 0;
index 1164511..4ede08a 100644 (file)
@@ -5111,7 +5111,7 @@ static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *d
        struct drm_display_mode *mode;
        unsigned pixel_clock = (timings->pixel_clock[0] |
                                (timings->pixel_clock[1] << 8) |
-                               (timings->pixel_clock[2] << 16));
+                               (timings->pixel_clock[2] << 16)) + 1;
        unsigned hactive = (timings->hactive[0] | timings->hactive[1] << 8) + 1;
        unsigned hblank = (timings->hblank[0] | timings->hblank[1] << 8) + 1;
        unsigned hsync = (timings->hsync[0] | (timings->hsync[1] & 0x7f) << 8) + 1;
index 2c617c9..52db785 100644 (file)
@@ -3141,9 +3141,6 @@ static void hsw_ddi_pre_enable_dp(struct intel_encoder *encoder,
        intel_dp_set_link_params(intel_dp, crtc_state->port_clock,
                                 crtc_state->lane_count, is_mst);
 
-       intel_dp->regs.dp_tp_ctl = DP_TP_CTL(port);
-       intel_dp->regs.dp_tp_status = DP_TP_STATUS(port);
-
        intel_edp_panel_on(intel_dp);
 
        intel_ddi_clk_select(encoder, crtc_state);
@@ -3848,12 +3845,18 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->uapi.crtc);
        enum transcoder cpu_transcoder = pipe_config->cpu_transcoder;
+       struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
        u32 temp, flags = 0;
 
        /* XXX: DSI transcoder paranoia */
        if (drm_WARN_ON(&dev_priv->drm, transcoder_is_dsi(cpu_transcoder)))
                return;
 
+       if (INTEL_GEN(dev_priv) >= 12) {
+               intel_dp->regs.dp_tp_ctl = TGL_DP_TP_CTL(cpu_transcoder);
+               intel_dp->regs.dp_tp_status = TGL_DP_TP_STATUS(cpu_transcoder);
+       }
+
        intel_dsc_get_config(encoder, pipe_config);
 
        temp = intel_de_read(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder));
@@ -4173,6 +4176,7 @@ static const struct drm_encoder_funcs intel_ddi_funcs = {
 static struct intel_connector *
 intel_ddi_init_dp_connector(struct intel_digital_port *intel_dig_port)
 {
+       struct drm_i915_private *dev_priv = to_i915(intel_dig_port->base.base.dev);
        struct intel_connector *connector;
        enum port port = intel_dig_port->base.port;
 
@@ -4183,6 +4187,10 @@ intel_ddi_init_dp_connector(struct intel_digital_port *intel_dig_port)
        intel_dig_port->dp.output_reg = DDI_BUF_CTL(port);
        intel_dig_port->dp.prepare_link_retrain =
                intel_ddi_prepare_link_retrain;
+       if (INTEL_GEN(dev_priv) < 12) {
+               intel_dig_port->dp.regs.dp_tp_ctl = DP_TP_CTL(port);
+               intel_dig_port->dp.regs.dp_tp_status = DP_TP_STATUS(port);
+       }
 
        if (!intel_dp_init_connector(intel_dig_port, connector)) {
                kfree(connector);
index 246e406..84ecf8e 100644 (file)
@@ -4140,7 +4140,7 @@ static const struct i915_power_well_desc tgl_power_wells[] = {
        {
                .name = "AUX D TBT1",
                .domains = TGL_AUX_D_TBT1_IO_POWER_DOMAINS,
-               .ops = &hsw_power_well_ops,
+               .ops = &icl_tc_phy_aux_power_well_ops,
                .id = DISP_PW_ID_NONE,
                {
                        .hsw.regs = &icl_aux_power_well_regs,
@@ -4151,7 +4151,7 @@ static const struct i915_power_well_desc tgl_power_wells[] = {
        {
                .name = "AUX E TBT2",
                .domains = TGL_AUX_E_TBT2_IO_POWER_DOMAINS,
-               .ops = &hsw_power_well_ops,
+               .ops = &icl_tc_phy_aux_power_well_ops,
                .id = DISP_PW_ID_NONE,
                {
                        .hsw.regs = &icl_aux_power_well_regs,
@@ -4162,7 +4162,7 @@ static const struct i915_power_well_desc tgl_power_wells[] = {
        {
                .name = "AUX F TBT3",
                .domains = TGL_AUX_F_TBT3_IO_POWER_DOMAINS,
-               .ops = &hsw_power_well_ops,
+               .ops = &icl_tc_phy_aux_power_well_ops,
                .id = DISP_PW_ID_NONE,
                {
                        .hsw.regs = &icl_aux_power_well_regs,
@@ -4173,7 +4173,7 @@ static const struct i915_power_well_desc tgl_power_wells[] = {
        {
                .name = "AUX G TBT4",
                .domains = TGL_AUX_G_TBT4_IO_POWER_DOMAINS,
-               .ops = &hsw_power_well_ops,
+               .ops = &icl_tc_phy_aux_power_well_ops,
                .id = DISP_PW_ID_NONE,
                {
                        .hsw.regs = &icl_aux_power_well_regs,
@@ -4184,7 +4184,7 @@ static const struct i915_power_well_desc tgl_power_wells[] = {
        {
                .name = "AUX H TBT5",
                .domains = TGL_AUX_H_TBT5_IO_POWER_DOMAINS,
-               .ops = &hsw_power_well_ops,
+               .ops = &icl_tc_phy_aux_power_well_ops,
                .id = DISP_PW_ID_NONE,
                {
                        .hsw.regs = &icl_aux_power_well_regs,
@@ -4195,7 +4195,7 @@ static const struct i915_power_well_desc tgl_power_wells[] = {
        {
                .name = "AUX I TBT6",
                .domains = TGL_AUX_I_TBT6_IO_POWER_DOMAINS,
-               .ops = &hsw_power_well_ops,
+               .ops = &icl_tc_phy_aux_power_well_ops,
                .id = DISP_PW_ID_NONE,
                {
                        .hsw.regs = &icl_aux_power_well_regs,
index 804b1d9..a2fafd4 100644 (file)
@@ -2517,9 +2517,6 @@ static void intel_dp_prepare(struct intel_encoder *encoder,
                                 intel_crtc_has_type(pipe_config,
                                                     INTEL_OUTPUT_DP_MST));
 
-       intel_dp->regs.dp_tp_ctl = DP_TP_CTL(port);
-       intel_dp->regs.dp_tp_status = DP_TP_STATUS(port);
-
        /*
         * There are four kinds of DP registers:
         *
@@ -7836,6 +7833,8 @@ bool intel_dp_init(struct drm_i915_private *dev_priv,
 
        intel_dig_port->dp.output_reg = output_reg;
        intel_dig_port->max_lanes = 4;
+       intel_dig_port->dp.regs.dp_tp_ctl = DP_TP_CTL(port);
+       intel_dig_port->dp.regs.dp_tp_status = DP_TP_STATUS(port);
 
        intel_encoder->type = INTEL_OUTPUT_DP;
        intel_encoder->power_domain = intel_port_to_power_domain(port);
index 3e706bb..dbfa689 100644 (file)
@@ -342,6 +342,7 @@ int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector)
         */
        if (dev_priv->vbt.backlight.type !=
            INTEL_BACKLIGHT_VESA_EDP_AUX_INTERFACE &&
+           i915_modparams.enable_dpcd_backlight != 1 &&
            !drm_dp_has_quirk(&intel_dp->desc, intel_dp->edid_quirks,
                              DP_QUIRK_FORCE_DPCD_BACKLIGHT)) {
                DRM_DEV_INFO(dev->dev,
index 3993023..821411b 100644 (file)
@@ -1536,7 +1536,8 @@ bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port)
        intel_de_write(i915, HDCP_RPRIME(i915, cpu_transcoder, port), ri.reg);
 
        /* Wait for Ri prime match */
-       if (wait_for(intel_de_read(i915, HDCP_STATUS(i915, cpu_transcoder, port)) &
+       if (wait_for((intel_de_read(i915, HDCP_STATUS(i915, cpu_transcoder, port)) &
+                     (HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC)) ==
                     (HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC), 1)) {
                DRM_ERROR("Ri' mismatch detected, link check failed (%x)\n",
                          intel_de_read(i915, HDCP_STATUS(i915, cpu_transcoder, port)));
index deda351..33d8861 100644 (file)
@@ -2817,19 +2817,25 @@ static bool skl_plane_format_mod_supported(struct drm_plane *_plane,
        }
 }
 
-static bool gen12_plane_supports_mc_ccs(enum plane_id plane_id)
+static bool gen12_plane_supports_mc_ccs(struct drm_i915_private *dev_priv,
+                                       enum plane_id plane_id)
 {
+       /* Wa_14010477008:tgl[a0..c0] */
+       if (IS_TGL_REVID(dev_priv, TGL_REVID_A0, TGL_REVID_C0))
+               return false;
+
        return plane_id < PLANE_SPRITE4;
 }
 
 static bool gen12_plane_format_mod_supported(struct drm_plane *_plane,
                                             u32 format, u64 modifier)
 {
+       struct drm_i915_private *dev_priv = to_i915(_plane->dev);
        struct intel_plane *plane = to_intel_plane(_plane);
 
        switch (modifier) {
        case I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS:
-               if (!gen12_plane_supports_mc_ccs(plane->id))
+               if (!gen12_plane_supports_mc_ccs(dev_priv, plane->id))
                        return false;
                /* fall through */
        case DRM_FORMAT_MOD_LINEAR:
@@ -2998,9 +3004,10 @@ static const u32 *icl_get_plane_formats(struct drm_i915_private *dev_priv,
        }
 }
 
-static const u64 *gen12_get_plane_modifiers(enum plane_id plane_id)
+static const u64 *gen12_get_plane_modifiers(struct drm_i915_private *dev_priv,
+                                           enum plane_id plane_id)
 {
-       if (gen12_plane_supports_mc_ccs(plane_id))
+       if (gen12_plane_supports_mc_ccs(dev_priv, plane_id))
                return gen12_plane_format_modifiers_mc_ccs;
        else
                return gen12_plane_format_modifiers_rc_ccs;
@@ -3070,7 +3077,7 @@ skl_universal_plane_create(struct drm_i915_private *dev_priv,
 
        plane->has_ccs = skl_plane_has_ccs(dev_priv, pipe, plane_id);
        if (INTEL_GEN(dev_priv) >= 12) {
-               modifiers = gen12_get_plane_modifiers(plane_id);
+               modifiers = gen12_get_plane_modifiers(dev_priv, plane_id);
                plane_funcs = &gen12_plane_funcs;
        } else {
                if (plane->has_ccs)
index 37f77ae..0158e49 100644 (file)
@@ -182,21 +182,35 @@ i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj,
                              int tiling_mode, unsigned int stride)
 {
        struct i915_ggtt *ggtt = &to_i915(obj->base.dev)->ggtt;
-       struct i915_vma *vma;
+       struct i915_vma *vma, *vn;
+       LIST_HEAD(unbind);
        int ret = 0;
 
        if (tiling_mode == I915_TILING_NONE)
                return 0;
 
        mutex_lock(&ggtt->vm.mutex);
+
+       spin_lock(&obj->vma.lock);
        for_each_ggtt_vma(vma, obj) {
+               GEM_BUG_ON(vma->vm != &ggtt->vm);
+
                if (i915_vma_fence_prepare(vma, tiling_mode, stride))
                        continue;
 
+               list_move(&vma->vm_link, &unbind);
+       }
+       spin_unlock(&obj->vma.lock);
+
+       list_for_each_entry_safe(vma, vn, &unbind, vm_link) {
                ret = __i915_vma_unbind(vma);
-               if (ret)
+               if (ret) {
+                       /* Restore the remaining vma on an error */
+                       list_splice(&unbind, &ggtt->vm.bound_list);
                        break;
+               }
        }
+
        mutex_unlock(&ggtt->vm.mutex);
 
        return ret;
@@ -268,6 +282,7 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
        }
        mutex_unlock(&obj->mm.lock);
 
+       spin_lock(&obj->vma.lock);
        for_each_ggtt_vma(vma, obj) {
                vma->fence_size =
                        i915_gem_fence_size(i915, vma->size, tiling, stride);
@@ -278,6 +293,7 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
                if (vma->fence)
                        vma->fence->dirty = true;
        }
+       spin_unlock(&obj->vma.lock);
 
        obj->tiling_and_stride = tiling | stride;
        i915_gem_object_unlock(obj);
index 2d0fd50..d4f94ca 100644 (file)
@@ -1477,8 +1477,10 @@ static int igt_ppgtt_pin_update(void *arg)
                unsigned int page_size = BIT(first);
 
                obj = i915_gem_object_create_internal(dev_priv, page_size);
-               if (IS_ERR(obj))
-                       return PTR_ERR(obj);
+               if (IS_ERR(obj)) {
+                       err = PTR_ERR(obj);
+                       goto out_vm;
+               }
 
                vma = i915_vma_instance(obj, vm, NULL);
                if (IS_ERR(vma)) {
@@ -1531,8 +1533,10 @@ static int igt_ppgtt_pin_update(void *arg)
        }
 
        obj = i915_gem_object_create_internal(dev_priv, PAGE_SIZE);
-       if (IS_ERR(obj))
-               return PTR_ERR(obj);
+       if (IS_ERR(obj)) {
+               err = PTR_ERR(obj);
+               goto out_vm;
+       }
 
        vma = i915_vma_instance(obj, vm, NULL);
        if (IS_ERR(vma)) {
index cfaf141..19542fd 100644 (file)
@@ -81,13 +81,14 @@ static void rps_enable_interrupts(struct intel_rps *rps)
                events = (GEN6_PM_RP_UP_THRESHOLD |
                          GEN6_PM_RP_DOWN_THRESHOLD |
                          GEN6_PM_RP_DOWN_TIMEOUT);
-
        WRITE_ONCE(rps->pm_events, events);
+
        spin_lock_irq(&gt->irq_lock);
        gen6_gt_pm_enable_irq(gt, rps->pm_events);
        spin_unlock_irq(&gt->irq_lock);
 
-       set(gt->uncore, GEN6_PMINTRMSK, rps_pm_mask(rps, rps->cur_freq));
+       intel_uncore_write(gt->uncore,
+                          GEN6_PMINTRMSK, rps_pm_mask(rps, rps->last_freq));
 }
 
 static void gen6_rps_reset_interrupts(struct intel_rps *rps)
@@ -120,7 +121,9 @@ static void rps_disable_interrupts(struct intel_rps *rps)
        struct intel_gt *gt = rps_to_gt(rps);
 
        WRITE_ONCE(rps->pm_events, 0);
-       set(gt->uncore, GEN6_PMINTRMSK, rps_pm_sanitize_mask(rps, ~0u));
+
+       intel_uncore_write(gt->uncore,
+                          GEN6_PMINTRMSK, rps_pm_sanitize_mask(rps, ~0u));
 
        spin_lock_irq(&gt->irq_lock);
        gen6_gt_pm_disable_irq(gt, GEN6_PM_RPS_EVENTS);
index 91debbc..08b56d7 100644 (file)
@@ -521,6 +521,8 @@ int intel_timeline_read_hwsp(struct i915_request *from,
 
        rcu_read_lock();
        cl = rcu_dereference(from->hwsp_cacheline);
+       if (i915_request_completed(from)) /* confirm cacheline is valid */
+               goto unlock;
        if (unlikely(!i915_active_acquire_if_busy(&cl->active)))
                goto unlock; /* seqno wrapped and completed! */
        if (unlikely(i915_request_completed(from)))
index 074c4ef..eee5304 100644 (file)
@@ -131,6 +131,7 @@ struct kvmgt_vdev {
        struct work_struct release_work;
        atomic_t released;
        struct vfio_device *vfio_device;
+       struct vfio_group *vfio_group;
 };
 
 static inline struct kvmgt_vdev *kvmgt_vdev(struct intel_vgpu *vgpu)
@@ -151,6 +152,7 @@ static void gvt_unpin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
                unsigned long size)
 {
        struct drm_i915_private *i915 = vgpu->gvt->gt->i915;
+       struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
        int total_pages;
        int npage;
        int ret;
@@ -160,7 +162,7 @@ static void gvt_unpin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
        for (npage = 0; npage < total_pages; npage++) {
                unsigned long cur_gfn = gfn + npage;
 
-               ret = vfio_unpin_pages(mdev_dev(kvmgt_vdev(vgpu)->mdev), &cur_gfn, 1);
+               ret = vfio_group_unpin_pages(vdev->vfio_group, &cur_gfn, 1);
                drm_WARN_ON(&i915->drm, ret != 1);
        }
 }
@@ -169,6 +171,7 @@ static void gvt_unpin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
 static int gvt_pin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
                unsigned long size, struct page **page)
 {
+       struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
        unsigned long base_pfn = 0;
        int total_pages;
        int npage;
@@ -183,8 +186,8 @@ static int gvt_pin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
                unsigned long cur_gfn = gfn + npage;
                unsigned long pfn;
 
-               ret = vfio_pin_pages(mdev_dev(kvmgt_vdev(vgpu)->mdev), &cur_gfn, 1,
-                                    IOMMU_READ | IOMMU_WRITE, &pfn);
+               ret = vfio_group_pin_pages(vdev->vfio_group, &cur_gfn, 1,
+                                          IOMMU_READ | IOMMU_WRITE, &pfn);
                if (ret != 1) {
                        gvt_vgpu_err("vfio_pin_pages failed for gfn 0x%lx, ret %d\n",
                                     cur_gfn, ret);
@@ -792,6 +795,7 @@ static int intel_vgpu_open(struct mdev_device *mdev)
        struct kvmgt_vdev *vdev = kvmgt_vdev(vgpu);
        unsigned long events;
        int ret;
+       struct vfio_group *vfio_group;
 
        vdev->iommu_notifier.notifier_call = intel_vgpu_iommu_notifier;
        vdev->group_notifier.notifier_call = intel_vgpu_group_notifier;
@@ -814,6 +818,14 @@ static int intel_vgpu_open(struct mdev_device *mdev)
                goto undo_iommu;
        }
 
+       vfio_group = vfio_group_get_external_user_from_dev(mdev_dev(mdev));
+       if (IS_ERR_OR_NULL(vfio_group)) {
+               ret = !vfio_group ? -EFAULT : PTR_ERR(vfio_group);
+               gvt_vgpu_err("vfio_group_get_external_user_from_dev failed\n");
+               goto undo_register;
+       }
+       vdev->vfio_group = vfio_group;
+
        /* Take a module reference as mdev core doesn't take
         * a reference for vendor driver.
         */
@@ -830,6 +842,10 @@ static int intel_vgpu_open(struct mdev_device *mdev)
        return ret;
 
 undo_group:
+       vfio_group_put_external_user(vdev->vfio_group);
+       vdev->vfio_group = NULL;
+
+undo_register:
        vfio_unregister_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
                                        &vdev->group_notifier);
 
@@ -884,6 +900,7 @@ static void __intel_vgpu_release(struct intel_vgpu *vgpu)
        kvmgt_guest_exit(info);
 
        intel_vgpu_release_msi_eventfd_ctx(vgpu);
+       vfio_group_put_external_user(vdev->vfio_group);
 
        vdev->kvm = NULL;
        vgpu->handle = 0;
@@ -2035,33 +2052,14 @@ static int kvmgt_rw_gpa(unsigned long handle, unsigned long gpa,
                        void *buf, unsigned long len, bool write)
 {
        struct kvmgt_guest_info *info;
-       struct kvm *kvm;
-       int idx, ret;
-       bool kthread = current->mm == NULL;
 
        if (!handle_valid(handle))
                return -ESRCH;
 
        info = (struct kvmgt_guest_info *)handle;
-       kvm = info->kvm;
-
-       if (kthread) {
-               if (!mmget_not_zero(kvm->mm))
-                       return -EFAULT;
-               use_mm(kvm->mm);
-       }
-
-       idx = srcu_read_lock(&kvm->srcu);
-       ret = write ? kvm_write_guest(kvm, gpa, buf, len) :
-                     kvm_read_guest(kvm, gpa, buf, len);
-       srcu_read_unlock(&kvm->srcu, idx);
-
-       if (kthread) {
-               unuse_mm(kvm->mm);
-               mmput(kvm->mm);
-       }
 
-       return ret;
+       return vfio_dma_rw(kvmgt_vdev(info->vgpu)->vfio_group,
+                          gpa, buf, len, write);
 }
 
 static int kvmgt_read_gpa(unsigned long handle, unsigned long gpa,
index 1f5b9a5..62b901f 100644 (file)
@@ -1507,6 +1507,8 @@ IS_SUBPLATFORM(const struct drm_i915_private *i915,
        (IS_ICELAKE(p) && IS_REVID(p, since, until))
 
 #define TGL_REVID_A0           0x0
+#define TGL_REVID_B0           0x1
+#define TGL_REVID_C0           0x2
 
 #define IS_TGL_REVID(p, since, until) \
        (IS_TIGERLAKE(p) && IS_REVID(p, since, until))
index 9f0653c..d91557d 100644 (file)
@@ -3358,7 +3358,8 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
 {
        struct intel_uncore *uncore = &dev_priv->uncore;
 
-       u32 de_pipe_masked = GEN8_PIPE_CDCLK_CRC_DONE;
+       u32 de_pipe_masked = gen8_de_pipe_fault_mask(dev_priv) |
+               GEN8_PIPE_CDCLK_CRC_DONE;
        u32 de_pipe_enables;
        u32 de_port_masked = GEN8_AUX_CHANNEL_A;
        u32 de_port_enables;
@@ -3369,13 +3370,10 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
                de_misc_masked |= GEN8_DE_MISC_GSE;
 
        if (INTEL_GEN(dev_priv) >= 9) {
-               de_pipe_masked |= GEN9_DE_PIPE_IRQ_FAULT_ERRORS;
                de_port_masked |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C |
                                  GEN9_AUX_CHANNEL_D;
                if (IS_GEN9_LP(dev_priv))
                        de_port_masked |= BXT_DE_PORT_GMBUS;
-       } else {
-               de_pipe_masked |= GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
        }
 
        if (INTEL_GEN(dev_priv) >= 11)
index 551be58..66a46e4 100644 (file)
@@ -2940,49 +2940,6 @@ void i915_oa_init_reg_state(const struct intel_context *ce,
                gen8_update_reg_state_unlocked(ce, stream);
 }
 
-/**
- * i915_perf_read_locked - &i915_perf_stream_ops->read with error normalisation
- * @stream: An i915 perf stream
- * @file: An i915 perf stream file
- * @buf: destination buffer given by userspace
- * @count: the number of bytes userspace wants to read
- * @ppos: (inout) file seek position (unused)
- *
- * Besides wrapping &i915_perf_stream_ops->read this provides a common place to
- * ensure that if we've successfully copied any data then reporting that takes
- * precedence over any internal error status, so the data isn't lost.
- *
- * For example ret will be -ENOSPC whenever there is more buffered data than
- * can be copied to userspace, but that's only interesting if we weren't able
- * to copy some data because it implies the userspace buffer is too small to
- * receive a single record (and we never split records).
- *
- * Another case with ret == -EFAULT is more of a grey area since it would seem
- * like bad form for userspace to ask us to overrun its buffer, but the user
- * knows best:
- *
- *   http://yarchive.net/comp/linux/partial_reads_writes.html
- *
- * Returns: The number of bytes copied or a negative error code on failure.
- */
-static ssize_t i915_perf_read_locked(struct i915_perf_stream *stream,
-                                    struct file *file,
-                                    char __user *buf,
-                                    size_t count,
-                                    loff_t *ppos)
-{
-       /* Note we keep the offset (aka bytes read) separate from any
-        * error status so that the final check for whether we return
-        * the bytes read with a higher precedence than any error (see
-        * comment below) doesn't need to be handled/duplicated in
-        * stream->ops->read() implementations.
-        */
-       size_t offset = 0;
-       int ret = stream->ops->read(stream, buf, count, &offset);
-
-       return offset ?: (ret ?: -EAGAIN);
-}
-
 /**
  * i915_perf_read - handles read() FOP for i915 perf stream FDs
  * @file: An i915 perf stream file
@@ -3008,7 +2965,8 @@ static ssize_t i915_perf_read(struct file *file,
 {
        struct i915_perf_stream *stream = file->private_data;
        struct i915_perf *perf = stream->perf;
-       ssize_t ret;
+       size_t offset = 0;
+       int ret;
 
        /* To ensure it's handled consistently we simply treat all reads of a
         * disabled stream as an error. In particular it might otherwise lead
@@ -3031,13 +2989,12 @@ static ssize_t i915_perf_read(struct file *file,
                                return ret;
 
                        mutex_lock(&perf->lock);
-                       ret = i915_perf_read_locked(stream, file,
-                                                   buf, count, ppos);
+                       ret = stream->ops->read(stream, buf, count, &offset);
                        mutex_unlock(&perf->lock);
-               } while (ret == -EAGAIN);
+               } while (!offset && !ret);
        } else {
                mutex_lock(&perf->lock);
-               ret = i915_perf_read_locked(stream, file, buf, count, ppos);
+               ret = stream->ops->read(stream, buf, count, &offset);
                mutex_unlock(&perf->lock);
        }
 
@@ -3048,15 +3005,15 @@ static ssize_t i915_perf_read(struct file *file,
         * and read() returning -EAGAIN. Clearing the oa.pollin state here
         * effectively ensures we back off until the next hrtimer callback
         * before reporting another EPOLLIN event.
+        * The exception to this is if ops->read() returned -ENOSPC which means
+        * that more OA data is available than could fit in the user provided
+        * buffer. In this case we want the next poll() call to not block.
         */
-       if (ret >= 0 || ret == -EAGAIN) {
-               /* Maybe make ->pollin per-stream state if we support multiple
-                * concurrent streams in the future.
-                */
+       if (ret != -ENOSPC)
                stream->pollin = false;
-       }
 
-       return ret;
+       /* Possible values for ret are 0, -EFAULT, -ENOSPC, -EIO, ... */
+       return offset ?: (ret ?: -EAGAIN);
 }
 
 static enum hrtimer_restart oa_poll_check_timer_cb(struct hrtimer *hrtimer)
index 59e64ac..e0c6021 100644 (file)
@@ -34,8 +34,8 @@
  * Follow the style described here for new macros, and while changing existing
  * macros. Do **not** mass change existing definitions just to update the style.
  *
- * Layout
- * ~~~~~~
+ * File Layout
+ * ~~~~~~~~~~~
  *
  * Keep helper macros near the top. For example, _PIPE() and friends.
  *
index 08699fa..82e3bc2 100644 (file)
@@ -158,16 +158,18 @@ vma_create(struct drm_i915_gem_object *obj,
 
        GEM_BUG_ON(!IS_ALIGNED(vma->size, I915_GTT_PAGE_SIZE));
 
+       spin_lock(&obj->vma.lock);
+
        if (i915_is_ggtt(vm)) {
                if (unlikely(overflows_type(vma->size, u32)))
-                       goto err_vma;
+                       goto err_unlock;
 
                vma->fence_size = i915_gem_fence_size(vm->i915, vma->size,
                                                      i915_gem_object_get_tiling(obj),
                                                      i915_gem_object_get_stride(obj));
                if (unlikely(vma->fence_size < vma->size || /* overflow */
                             vma->fence_size > vm->total))
-                       goto err_vma;
+                       goto err_unlock;
 
                GEM_BUG_ON(!IS_ALIGNED(vma->fence_size, I915_GTT_MIN_ALIGNMENT));
 
@@ -179,8 +181,6 @@ vma_create(struct drm_i915_gem_object *obj,
                __set_bit(I915_VMA_GGTT_BIT, __i915_vma_flags(vma));
        }
 
-       spin_lock(&obj->vma.lock);
-
        rb = NULL;
        p = &obj->vma.tree.rb_node;
        while (*p) {
@@ -225,6 +225,8 @@ vma_create(struct drm_i915_gem_object *obj,
 
        return vma;
 
+err_unlock:
+       spin_unlock(&obj->vma.lock);
 err_vma:
        i915_vma_free(vma);
        return ERR_PTR(-E2BIG);
index e8c9491..64cb6ba 100644 (file)
@@ -1034,10 +1034,8 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
                return PTR_ERR(dw_plat_data->regm);
 
        irq = platform_get_irq(pdev, 0);
-       if (irq < 0) {
-               dev_err(dev, "Failed to get hdmi top irq\n");
+       if (irq < 0)
                return irq;
-       }
 
        ret = devm_request_threaded_irq(dev, irq, dw_hdmi_top_irq,
                                        dw_hdmi_top_thread_irq, IRQF_SHARED,
index 232a9d7..e770c94 100644 (file)
@@ -25,6 +25,9 @@
 MODULE_FIRMWARE("nvidia/gp108/sec2/desc.bin");
 MODULE_FIRMWARE("nvidia/gp108/sec2/image.bin");
 MODULE_FIRMWARE("nvidia/gp108/sec2/sig.bin");
+MODULE_FIRMWARE("nvidia/gv100/sec2/desc.bin");
+MODULE_FIRMWARE("nvidia/gv100/sec2/image.bin");
+MODULE_FIRMWARE("nvidia/gv100/sec2/sig.bin");
 
 static const struct nvkm_sec2_fwif
 gp108_sec2_fwif[] = {
index b6ebd95..a829565 100644 (file)
@@ -56,6 +56,22 @@ tu102_sec2_nofw(struct nvkm_sec2 *sec2, int ver,
        return 0;
 }
 
+MODULE_FIRMWARE("nvidia/tu102/sec2/desc.bin");
+MODULE_FIRMWARE("nvidia/tu102/sec2/image.bin");
+MODULE_FIRMWARE("nvidia/tu102/sec2/sig.bin");
+MODULE_FIRMWARE("nvidia/tu104/sec2/desc.bin");
+MODULE_FIRMWARE("nvidia/tu104/sec2/image.bin");
+MODULE_FIRMWARE("nvidia/tu104/sec2/sig.bin");
+MODULE_FIRMWARE("nvidia/tu106/sec2/desc.bin");
+MODULE_FIRMWARE("nvidia/tu106/sec2/image.bin");
+MODULE_FIRMWARE("nvidia/tu106/sec2/sig.bin");
+MODULE_FIRMWARE("nvidia/tu116/sec2/desc.bin");
+MODULE_FIRMWARE("nvidia/tu116/sec2/image.bin");
+MODULE_FIRMWARE("nvidia/tu116/sec2/sig.bin");
+MODULE_FIRMWARE("nvidia/tu117/sec2/desc.bin");
+MODULE_FIRMWARE("nvidia/tu117/sec2/image.bin");
+MODULE_FIRMWARE("nvidia/tu117/sec2/sig.bin");
+
 static const struct nvkm_sec2_fwif
 tu102_sec2_fwif[] = {
        {  0, gp102_sec2_load, &tu102_sec2, &gp102_sec2_acr_1 },
index d1086b2..05863b2 100644 (file)
@@ -480,9 +480,10 @@ int qxl_hw_surface_alloc(struct qxl_device *qdev,
                return ret;
 
        ret = qxl_release_reserve_list(release, true);
-       if (ret)
+       if (ret) {
+               qxl_release_free(qdev, release);
                return ret;
-
+       }
        cmd = (struct qxl_surface_cmd *)qxl_release_map(qdev, release);
        cmd->type = QXL_SURFACE_CMD_CREATE;
        cmd->flags = QXL_SURF_FLAG_KEEP_DATA;
@@ -499,8 +500,8 @@ int qxl_hw_surface_alloc(struct qxl_device *qdev,
        /* no need to add a release to the fence for this surface bo,
           since it is only released when we ask to destroy the surface
           and it would never signal otherwise */
-       qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);
        qxl_release_fence_buffer_objects(release);
+       qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);
 
        surf->hw_surf_alloc = true;
        spin_lock(&qdev->surf_id_idr_lock);
@@ -542,9 +543,8 @@ int qxl_hw_surface_dealloc(struct qxl_device *qdev,
        cmd->surface_id = id;
        qxl_release_unmap(qdev, release, &cmd->release_info);
 
-       qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);
-
        qxl_release_fence_buffer_objects(release);
+       qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);
 
        return 0;
 }
index 09583a0..91f398d 100644 (file)
@@ -510,8 +510,8 @@ static int qxl_primary_apply_cursor(struct drm_plane *plane)
        cmd->u.set.visible = 1;
        qxl_release_unmap(qdev, release, &cmd->release_info);
 
-       qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
        qxl_release_fence_buffer_objects(release);
+       qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
 
        return ret;
 
@@ -652,8 +652,8 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane,
        cmd->u.position.y = plane->state->crtc_y + fb->hot_y;
 
        qxl_release_unmap(qdev, release, &cmd->release_info);
-       qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
        qxl_release_fence_buffer_objects(release);
+       qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
 
        if (old_cursor_bo != NULL)
                qxl_bo_unpin(old_cursor_bo);
@@ -700,8 +700,8 @@ static void qxl_cursor_atomic_disable(struct drm_plane *plane,
        cmd->type = QXL_CURSOR_HIDE;
        qxl_release_unmap(qdev, release, &cmd->release_info);
 
-       qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
        qxl_release_fence_buffer_objects(release);
+       qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
 }
 
 static void qxl_update_dumb_head(struct qxl_device *qdev,
index 5bebf1e..3599db0 100644 (file)
@@ -209,9 +209,10 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
                goto out_release_backoff;
 
        rects = drawable_set_clipping(qdev, num_clips, clips_bo);
-       if (!rects)
+       if (!rects) {
+               ret = -EINVAL;
                goto out_release_backoff;
-
+       }
        drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
 
        drawable->clip.type = SPICE_CLIP_TYPE_RECTS;
@@ -242,8 +243,8 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
        }
        qxl_bo_kunmap(clips_bo);
 
-       qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
        qxl_release_fence_buffer_objects(release);
+       qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
 
 out_release_backoff:
        if (ret)
index 43688ec..60ab715 100644 (file)
@@ -212,7 +212,8 @@ qxl_image_init_helper(struct qxl_device *qdev,
                break;
        default:
                DRM_ERROR("unsupported image bit depth\n");
-               return -EINVAL; /* TODO: cleanup */
+               qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr);
+               return -EINVAL;
        }
        image->u.bitmap.flags = QXL_BITMAP_TOP_DOWN;
        image->u.bitmap.x = width;
index 8117a45..72f3f1b 100644 (file)
@@ -261,11 +261,8 @@ static int qxl_process_single_command(struct qxl_device *qdev,
                        apply_surf_reloc(qdev, &reloc_info[i]);
        }
 
+       qxl_release_fence_buffer_objects(release);
        ret = qxl_push_command_ring_release(qdev, release, cmd->type, true);
-       if (ret)
-               qxl_release_backoff_reserve_list(release);
-       else
-               qxl_release_fence_buffer_objects(release);
 
 out_free_bos:
 out_free_release:
index 8e731ed..2f31910 100644 (file)
@@ -676,7 +676,7 @@ drm_sched_get_cleanup_job(struct drm_gpu_scheduler *sched)
         */
        if ((sched->timeout != MAX_SCHEDULE_TIMEOUT &&
            !cancel_delayed_work(&sched->work_tdr)) ||
-           __kthread_should_park(sched->thread))
+           kthread_should_park())
                return NULL;
 
        spin_lock(&sched->job_list_lock);
index d4ce9ba..3221a70 100644 (file)
@@ -379,9 +379,17 @@ static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc)
        return &state->base;
 }
 
+static void tidss_crtc_destroy(struct drm_crtc *crtc)
+{
+       struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
+
+       drm_crtc_cleanup(crtc);
+       kfree(tcrtc);
+}
+
 static const struct drm_crtc_funcs tidss_crtc_funcs = {
        .reset = tidss_crtc_reset,
-       .destroy = drm_crtc_cleanup,
+       .destroy = tidss_crtc_destroy,
        .set_config = drm_atomic_helper_set_config,
        .page_flip = drm_atomic_helper_page_flip,
        .atomic_duplicate_state = tidss_crtc_duplicate_state,
@@ -400,7 +408,7 @@ struct tidss_crtc *tidss_crtc_create(struct tidss_device *tidss,
        bool has_ctm = tidss->feat->vp_feat.color.has_ctm;
        int ret;
 
-       tcrtc = devm_kzalloc(tidss->dev, sizeof(*tcrtc), GFP_KERNEL);
+       tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL);
        if (!tcrtc)
                return ERR_PTR(-ENOMEM);
 
@@ -411,8 +419,10 @@ struct tidss_crtc *tidss_crtc_create(struct tidss_device *tidss,
 
        ret = drm_crtc_init_with_planes(&tidss->ddev, crtc, primary,
                                        NULL, &tidss_crtc_funcs, NULL);
-       if (ret < 0)
+       if (ret < 0) {
+               kfree(tcrtc);
                return ERR_PTR(ret);
+       }
 
        drm_crtc_helper_add(crtc, &tidss_crtc_helper_funcs);
 
index 83785b0..30bf2a6 100644 (file)
@@ -55,12 +55,18 @@ static int tidss_encoder_atomic_check(struct drm_encoder *encoder,
        return 0;
 }
 
+static void tidss_encoder_destroy(struct drm_encoder *encoder)
+{
+       drm_encoder_cleanup(encoder);
+       kfree(encoder);
+}
+
 static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
        .atomic_check = tidss_encoder_atomic_check,
 };
 
 static const struct drm_encoder_funcs encoder_funcs = {
-       .destroy = drm_encoder_cleanup,
+       .destroy = tidss_encoder_destroy,
 };
 
 struct drm_encoder *tidss_encoder_create(struct tidss_device *tidss,
@@ -69,7 +75,7 @@ struct drm_encoder *tidss_encoder_create(struct tidss_device *tidss,
        struct drm_encoder *enc;
        int ret;
 
-       enc = devm_kzalloc(tidss->dev, sizeof(*enc), GFP_KERNEL);
+       enc = kzalloc(sizeof(*enc), GFP_KERNEL);
        if (!enc)
                return ERR_PTR(-ENOMEM);
 
@@ -77,8 +83,10 @@ struct drm_encoder *tidss_encoder_create(struct tidss_device *tidss,
 
        ret = drm_encoder_init(&tidss->ddev, enc, &encoder_funcs,
                               encoder_type, NULL);
-       if (ret < 0)
+       if (ret < 0) {
+               kfree(enc);
                return ERR_PTR(ret);
+       }
 
        drm_encoder_helper_add(enc, &encoder_helper_funcs);
 
index ff99b2d..7984889 100644 (file)
@@ -141,6 +141,14 @@ static void tidss_plane_atomic_disable(struct drm_plane *plane,
        dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, false);
 }
 
+static void drm_plane_destroy(struct drm_plane *plane)
+{
+       struct tidss_plane *tplane = to_tidss_plane(plane);
+
+       drm_plane_cleanup(plane);
+       kfree(tplane);
+}
+
 static const struct drm_plane_helper_funcs tidss_plane_helper_funcs = {
        .atomic_check = tidss_plane_atomic_check,
        .atomic_update = tidss_plane_atomic_update,
@@ -151,7 +159,7 @@ static const struct drm_plane_funcs tidss_plane_funcs = {
        .update_plane = drm_atomic_helper_update_plane,
        .disable_plane = drm_atomic_helper_disable_plane,
        .reset = drm_atomic_helper_plane_reset,
-       .destroy = drm_plane_cleanup,
+       .destroy = drm_plane_destroy,
        .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
        .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 };
@@ -175,7 +183,7 @@ struct tidss_plane *tidss_plane_create(struct tidss_device *tidss,
                           BIT(DRM_MODE_BLEND_COVERAGE));
        int ret;
 
-       tplane = devm_kzalloc(tidss->dev, sizeof(*tplane), GFP_KERNEL);
+       tplane = kzalloc(sizeof(*tplane), GFP_KERNEL);
        if (!tplane)
                return ERR_PTR(-ENOMEM);
 
@@ -190,7 +198,7 @@ struct tidss_plane *tidss_plane_create(struct tidss_device *tidss,
                                       formats, num_formats,
                                       NULL, type, NULL);
        if (ret < 0)
-               return ERR_PTR(ret);
+               goto err;
 
        drm_plane_helper_add(&tplane->plane, &tidss_plane_helper_funcs);
 
@@ -203,15 +211,19 @@ struct tidss_plane *tidss_plane_create(struct tidss_device *tidss,
                                                default_encoding,
                                                default_range);
        if (ret)
-               return ERR_PTR(ret);
+               goto err;
 
        ret = drm_plane_create_alpha_property(&tplane->plane);
        if (ret)
-               return ERR_PTR(ret);
+               goto err;
 
        ret = drm_plane_create_blend_mode_property(&tplane->plane, blend_modes);
        if (ret)
-               return ERR_PTR(ret);
+               goto err;
 
        return tplane;
+
+err:
+       kfree(tplane);
+       return ERR_PTR(ret);
 }
index 336cc91..3f60bf2 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <linux/file.h>
 #include <linux/sync_file.h>
+#include <linux/uaccess.h>
 
 #include <drm/drm_file.h>
 #include <drm/virtgpu_drm.h>
index 023a030..0a5c8cf 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <linux/virtio.h>
 #include <linux/virtio_config.h>
+#include <linux/virtio_ring.h>
 
 #include <drm/drm_file.h>
 
@@ -52,14 +53,6 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
                      events_clear, &events_clear);
 }
 
-static void virtio_gpu_context_destroy(struct virtio_gpu_device *vgdev,
-                                     uint32_t ctx_id)
-{
-       virtio_gpu_cmd_context_destroy(vgdev, ctx_id);
-       virtio_gpu_notify(vgdev);
-       ida_free(&vgdev->ctx_id_ida, ctx_id - 1);
-}
-
 static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq,
                               void (*work_func)(struct work_struct *work))
 {
@@ -274,14 +267,17 @@ int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file)
 void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file)
 {
        struct virtio_gpu_device *vgdev = dev->dev_private;
-       struct virtio_gpu_fpriv *vfpriv;
+       struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
 
        if (!vgdev->has_virgl_3d)
                return;
 
-       vfpriv = file->driver_priv;
+       if (vfpriv->context_created) {
+               virtio_gpu_cmd_context_destroy(vgdev, vfpriv->ctx_id);
+               virtio_gpu_notify(vgdev);
+       }
 
-       virtio_gpu_context_destroy(vgdev, vfpriv->ctx_id);
+       ida_free(&vgdev->ctx_id_ida, vfpriv->ctx_id - 1);
        mutex_destroy(&vfpriv->context_lock);
        kfree(vfpriv);
        file->driver_priv = NULL;
index 7c89edb..34f0737 100644 (file)
@@ -1155,6 +1155,7 @@ config HID_ALPS
 config HID_MCP2221
        tristate "Microchip MCP2221 HID USB-to-I2C/SMbus host support"
        depends on USB_HID && I2C
+       depends on GPIOLIB
        ---help---
        Provides I2C and SMBUS host adapter functionality over USB-HID
        through MCP2221 device.
index fa70415..b2ad319 100644 (file)
@@ -802,6 +802,7 @@ static int alps_probe(struct hid_device *hdev, const struct hid_device_id *id)
                break;
        case HID_DEVICE_ID_ALPS_U1_DUAL:
        case HID_DEVICE_ID_ALPS_U1:
+       case HID_DEVICE_ID_ALPS_U1_UNICORN_LEGACY:
                data->dev_type = U1;
                break;
        default:
index b18b131..1c71a1a 100644 (file)
 #define HID_DEVICE_ID_ALPS_U1_DUAL_PTP 0x121F
 #define HID_DEVICE_ID_ALPS_U1_DUAL_3BTN_PTP    0x1220
 #define HID_DEVICE_ID_ALPS_U1          0x1215
+#define HID_DEVICE_ID_ALPS_U1_UNICORN_LEGACY         0x121E
 #define HID_DEVICE_ID_ALPS_T4_BTNLESS  0x120C
 #define HID_DEVICE_ID_ALPS_1222                0x1222
 
-
 #define USB_VENDOR_ID_AMI              0x046b
 #define USB_DEVICE_ID_AMI_VIRT_KEYBOARD_AND_MOUSE      0xff10
 
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349      0x7349
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7      0x73f7
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001      0xa001
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_C002      0xc002
 
 #define USB_VENDOR_ID_ELAN             0x04f3
 #define USB_DEVICE_ID_TOSHIBA_CLICK_L9W        0x0401
 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2      0xc218
 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2    0xc219
 #define USB_DEVICE_ID_LOGITECH_G15_LCD         0xc222
+#define USB_DEVICE_ID_LOGITECH_G11             0xc225
 #define USB_DEVICE_ID_LOGITECH_G15_V2_LCD      0xc227
 #define USB_DEVICE_ID_LOGITECH_G510            0xc22d
 #define USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO  0xc22e
 #define USB_DEVICE_ID_SYMBOL_SCANNER_2 0x1300
 #define USB_DEVICE_ID_SYMBOL_SCANNER_3 0x1200
 
+#define I2C_VENDOR_ID_SYNAPTICS     0x06cb
+#define I2C_PRODUCT_ID_SYNAPTICS_SYNA2393   0x7a13
+
 #define USB_VENDOR_ID_SYNAPTICS                0x06cb
 #define USB_DEVICE_ID_SYNAPTICS_TP     0x0001
 #define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002
 #define USB_DEVICE_ID_SYNAPTICS_LTS2   0x1d10
 #define USB_DEVICE_ID_SYNAPTICS_HD     0x0ac3
 #define USB_DEVICE_ID_SYNAPTICS_QUAD_HD        0x1ac3
+#define USB_DEVICE_ID_SYNAPTICS_DELL_K12A      0x2819
 #define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5_012       0x2968
 #define USB_DEVICE_ID_SYNAPTICS_TP_V103        0x5710
 #define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5   0x81a7
index ad4b541..ef0cbcd 100644 (file)
@@ -872,6 +872,10 @@ error_hw_stop:
 }
 
 static const struct hid_device_id lg_g15_devices[] = {
+       /* The G11 is a G15 without the LCD, treat it as a G15 */
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+               USB_DEVICE_ID_LOGITECH_G11),
+               .driver_data = LG_G15 },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
                         USB_DEVICE_ID_LOGITECH_G15_LCD),
                .driver_data = LG_G15 },
index 362805d..03c720b 100644 (file)
@@ -1922,6 +1922,9 @@ static const struct hid_device_id mt_devices[] = {
        { .driver_data = MT_CLS_EGALAX_SERIAL,
                MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
                        USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) },
+       { .driver_data = MT_CLS_EGALAX,
+               MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_C002) },
 
        /* Elitegroup panel */
        { .driver_data = MT_CLS_SERIAL,
index ebec818..e4cb543 100644 (file)
@@ -163,6 +163,7 @@ static const struct hid_device_id hid_quirks[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2), HID_QUIRK_NO_INIT_REPORTS },
        { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD), HID_QUIRK_NO_INIT_REPORTS },
        { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103), HID_QUIRK_NO_INIT_REPORTS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DELL_K12A), HID_QUIRK_NO_INIT_REPORTS },
        { HID_USB_DEVICE(USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD), HID_QUIRK_BADPAD },
        { HID_USB_DEVICE(USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS), HID_QUIRK_MULTI_INPUT },
        { HID_USB_DEVICE(USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN_8882), HID_QUIRK_NOGET },
index 009000c..294c84e 100644 (file)
@@ -177,6 +177,8 @@ static const struct i2c_hid_quirks {
                 I2C_HID_QUIRK_BOGUS_IRQ },
        { USB_VENDOR_ID_ALPS_JP, HID_ANY_ID,
                 I2C_HID_QUIRK_RESET_ON_RESUME },
+       { I2C_VENDOR_ID_SYNAPTICS, I2C_PRODUCT_ID_SYNAPTICS_SYNA2393,
+                I2C_HID_QUIRK_RESET_ON_RESUME },
        { USB_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720,
                I2C_HID_QUIRK_BAD_INPUT_SIZE },
        { 0, 0 }
index c7bc9db..17a638f 100644 (file)
@@ -682,16 +682,21 @@ static int usbhid_open(struct hid_device *hid)
        struct usbhid_device *usbhid = hid->driver_data;
        int res;
 
+       mutex_lock(&usbhid->mutex);
+
        set_bit(HID_OPENED, &usbhid->iofl);
 
-       if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
-               return 0;
+       if (hid->quirks & HID_QUIRK_ALWAYS_POLL) {
+               res = 0;
+               goto Done;
+       }
 
        res = usb_autopm_get_interface(usbhid->intf);
        /* the device must be awake to reliably request remote wakeup */
        if (res < 0) {
                clear_bit(HID_OPENED, &usbhid->iofl);
-               return -EIO;
+               res = -EIO;
+               goto Done;
        }
 
        usbhid->intf->needs_remote_wakeup = 1;
@@ -725,6 +730,9 @@ static int usbhid_open(struct hid_device *hid)
                msleep(50);
 
        clear_bit(HID_RESUME_RUNNING, &usbhid->iofl);
+
+ Done:
+       mutex_unlock(&usbhid->mutex);
        return res;
 }
 
@@ -732,6 +740,8 @@ static void usbhid_close(struct hid_device *hid)
 {
        struct usbhid_device *usbhid = hid->driver_data;
 
+       mutex_lock(&usbhid->mutex);
+
        /*
         * Make sure we don't restart data acquisition due to
         * a resumption we no longer care about by avoiding racing
@@ -743,12 +753,13 @@ static void usbhid_close(struct hid_device *hid)
                clear_bit(HID_IN_POLLING, &usbhid->iofl);
        spin_unlock_irq(&usbhid->lock);
 
-       if (hid->quirks & HID_QUIRK_ALWAYS_POLL)
-               return;
+       if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) {
+               hid_cancel_delayed_stuff(usbhid);
+               usb_kill_urb(usbhid->urbin);
+               usbhid->intf->needs_remote_wakeup = 0;
+       }
 
-       hid_cancel_delayed_stuff(usbhid);
-       usb_kill_urb(usbhid->urbin);
-       usbhid->intf->needs_remote_wakeup = 0;
+       mutex_unlock(&usbhid->mutex);
 }
 
 /*
@@ -1057,6 +1068,8 @@ static int usbhid_start(struct hid_device *hid)
        unsigned int n, insize = 0;
        int ret;
 
+       mutex_lock(&usbhid->mutex);
+
        clear_bit(HID_DISCONNECTED, &usbhid->iofl);
 
        usbhid->bufsize = HID_MIN_BUFFER_SIZE;
@@ -1177,6 +1190,8 @@ static int usbhid_start(struct hid_device *hid)
                usbhid_set_leds(hid);
                device_set_wakeup_enable(&dev->dev, 1);
        }
+
+       mutex_unlock(&usbhid->mutex);
        return 0;
 
 fail:
@@ -1187,6 +1202,7 @@ fail:
        usbhid->urbout = NULL;
        usbhid->urbctrl = NULL;
        hid_free_buffers(dev, hid);
+       mutex_unlock(&usbhid->mutex);
        return ret;
 }
 
@@ -1202,6 +1218,8 @@ static void usbhid_stop(struct hid_device *hid)
                usbhid->intf->needs_remote_wakeup = 0;
        }
 
+       mutex_lock(&usbhid->mutex);
+
        clear_bit(HID_STARTED, &usbhid->iofl);
        spin_lock_irq(&usbhid->lock);   /* Sync with error and led handlers */
        set_bit(HID_DISCONNECTED, &usbhid->iofl);
@@ -1222,6 +1240,8 @@ static void usbhid_stop(struct hid_device *hid)
        usbhid->urbout = NULL;
 
        hid_free_buffers(hid_to_usb_dev(hid), hid);
+
+       mutex_unlock(&usbhid->mutex);
 }
 
 static int usbhid_power(struct hid_device *hid, int lvl)
@@ -1382,6 +1402,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
        INIT_WORK(&usbhid->reset_work, hid_reset);
        timer_setup(&usbhid->io_retry, hid_retry_timeout, 0);
        spin_lock_init(&usbhid->lock);
+       mutex_init(&usbhid->mutex);
 
        ret = hid_add_device(hid);
        if (ret) {
index 8620408..75fe85d 100644 (file)
@@ -80,6 +80,7 @@ struct usbhid_device {
        dma_addr_t outbuf_dma;                                          /* Output buffer dma */
        unsigned long last_out;                                                 /* record of last output for timeouts */
 
+       struct mutex mutex;                                             /* start/stop/open/close */
        spinlock_t lock;                                                /* fifo spinlock */
        unsigned long iofl;                                             /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
        struct timer_list io_retry;                                     /* Retry timer */
index 5ded94b..cd71e71 100644 (file)
@@ -319,9 +319,11 @@ static void wacom_feature_mapping(struct hid_device *hdev,
                        data[0] = field->report->id;
                        ret = wacom_get_report(hdev, HID_FEATURE_REPORT,
                                               data, n, WAC_CMD_RETRIES);
-                       if (ret == n) {
+                       if (ret == n && features->type == HID_GENERIC) {
                                ret = hid_report_raw_event(hdev,
                                        HID_FEATURE_REPORT, data, n, 0);
+                       } else if (ret == 2 && features->type != HID_GENERIC) {
+                               features->touch_max = data[1];
                        } else {
                                features->touch_max = 16;
                                hid_warn(hdev, "wacom_feature_mapping: "
index d99a9d4..1c96809 100644 (file)
@@ -1427,11 +1427,13 @@ static void wacom_intuos_pro2_bt_pad(struct wacom_wac *wacom)
 {
        struct input_dev *pad_input = wacom->pad_input;
        unsigned char *data = wacom->data;
+       int nbuttons = wacom->features.numbered_buttons;
 
-       int buttons = data[282] | ((data[281] & 0x40) << 2);
+       int expresskeys = data[282];
+       int center = (data[281] & 0x40) >> 6;
        int ring = data[285] & 0x7F;
        bool ringstatus = data[285] & 0x80;
-       bool prox = buttons || ringstatus;
+       bool prox = expresskeys || center || ringstatus;
 
        /* Fix touchring data: userspace expects 0 at left and increasing clockwise */
        ring = 71 - ring;
@@ -1439,7 +1441,8 @@ static void wacom_intuos_pro2_bt_pad(struct wacom_wac *wacom)
        if (ring > 71)
                ring -= 72;
 
-       wacom_report_numbered_buttons(pad_input, 9, buttons);
+       wacom_report_numbered_buttons(pad_input, nbuttons,
+                                      expresskeys | (center << (nbuttons - 1)));
 
        input_report_abs(pad_input, ABS_WHEEL, ringstatus ? ring : 0);
 
@@ -2637,9 +2640,25 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev,
                        case HID_DG_TIPSWITCH:
                                hid_data->last_slot_field = equivalent_usage;
                                break;
+                       case HID_DG_CONTACTCOUNT:
+                               hid_data->cc_report = report->id;
+                               hid_data->cc_index = i;
+                               hid_data->cc_value_index = j;
+                               break;
                        }
                }
        }
+
+       if (hid_data->cc_report != 0 &&
+           hid_data->cc_index >= 0) {
+               struct hid_field *field = report->field[hid_data->cc_index];
+               int value = field->value[hid_data->cc_value_index];
+               if (value)
+                       hid_data->num_expected = value;
+       }
+       else {
+               hid_data->num_expected = wacom_wac->features.touch_max;
+       }
 }
 
 static void wacom_wac_finger_report(struct hid_device *hdev,
@@ -2649,7 +2668,6 @@ static void wacom_wac_finger_report(struct hid_device *hdev,
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        struct input_dev *input = wacom_wac->touch_input;
        unsigned touch_max = wacom_wac->features.touch_max;
-       struct hid_data *hid_data = &wacom_wac->hid_data;
 
        /* If more packets of data are expected, give us a chance to
         * process them rather than immediately syncing a partial
@@ -2663,7 +2681,6 @@ static void wacom_wac_finger_report(struct hid_device *hdev,
 
        input_sync(input);
        wacom_wac->hid_data.num_received = 0;
-       hid_data->num_expected = 0;
 
        /* keep touch state for pen event */
        wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(wacom_wac);
@@ -2738,73 +2755,12 @@ static void wacom_report_events(struct hid_device *hdev,
        }
 }
 
-static void wacom_set_num_expected(struct hid_device *hdev,
-                                  struct hid_report *report,
-                                  int collection_index,
-                                  struct hid_field *field,
-                                  int field_index)
-{
-       struct wacom *wacom = hid_get_drvdata(hdev);
-       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-       struct hid_data *hid_data = &wacom_wac->hid_data;
-       unsigned int original_collection_level =
-               hdev->collection[collection_index].level;
-       bool end_collection = false;
-       int i;
-
-       if (hid_data->num_expected)
-               return;
-
-       // find the contact count value for this segment
-       for (i = field_index; i < report->maxfield && !end_collection; i++) {
-               struct hid_field *field = report->field[i];
-               unsigned int field_level =
-                       hdev->collection[field->usage[0].collection_index].level;
-               unsigned int j;
-
-               if (field_level != original_collection_level)
-                       continue;
-
-               for (j = 0; j < field->maxusage; j++) {
-                       struct hid_usage *usage = &field->usage[j];
-
-                       if (usage->collection_index != collection_index) {
-                               end_collection = true;
-                               break;
-                       }
-                       if (wacom_equivalent_usage(usage->hid) == HID_DG_CONTACTCOUNT) {
-                               hid_data->cc_report = report->id;
-                               hid_data->cc_index = i;
-                               hid_data->cc_value_index = j;
-
-                               if (hid_data->cc_report != 0 &&
-                                   hid_data->cc_index >= 0) {
-
-                                       struct hid_field *field =
-                                               report->field[hid_data->cc_index];
-                                       int value =
-                                               field->value[hid_data->cc_value_index];
-
-                                       if (value)
-                                               hid_data->num_expected = value;
-                               }
-                       }
-               }
-       }
-
-       if (hid_data->cc_report == 0 || hid_data->cc_index < 0)
-               hid_data->num_expected = wacom_wac->features.touch_max;
-}
-
 static int wacom_wac_collection(struct hid_device *hdev, struct hid_report *report,
                         int collection_index, struct hid_field *field,
                         int field_index)
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
 
-       if (WACOM_FINGER_FIELD(field))
-               wacom_set_num_expected(hdev, report, collection_index, field,
-                                      field_index);
        wacom_report_events(hdev, report, collection_index, field_index);
 
        /*
index 0370364..501c43c 100644 (file)
@@ -839,6 +839,9 @@ void vmbus_initiate_unload(bool crash)
 {
        struct vmbus_channel_message_header hdr;
 
+       if (xchg(&vmbus_connection.conn_state, DISCONNECTED) == DISCONNECTED)
+               return;
+
        /* Pre-Win2012R2 hosts don't support reconnect */
        if (vmbus_proto_version < VERSION_WIN8_1)
                return;
index 6098e0c..533c8b8 100644 (file)
@@ -184,11 +184,7 @@ void hv_synic_enable_regs(unsigned int cpu)
 
        shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR;
        shared_sint.masked = false;
-       if (ms_hyperv.hints & HV_DEPRECATING_AEOI_RECOMMENDED)
-               shared_sint.auto_eoi = false;
-       else
-               shared_sint.auto_eoi = true;
-
+       shared_sint.auto_eoi = hv_recommend_using_aeoi();
        hv_set_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
 
        /* Enable the global synic bit */
index 8a28785..ccf752b 100644 (file)
@@ -11,7 +11,7 @@
 
 #include "hyperv_vmbus.h"
 
-struct dentry *hv_debug_root;
+static struct dentry *hv_debug_root;
 
 static int hv_debugfs_delay_get(void *data, u64 *val)
 {
index e70783e..f9d14db 100644 (file)
@@ -286,8 +286,8 @@ TRACE_EVENT(vmbus_send_tl_connect_request,
                    __field(int, ret)
                    ),
            TP_fast_assign(
-                   memcpy(__entry->guest_id, &msg->guest_endpoint_id.b, 16);
-                   memcpy(__entry->host_id, &msg->host_service_id.b, 16);
+                   export_guid(__entry->guest_id, &msg->guest_endpoint_id);
+                   export_guid(__entry->host_id, &msg->host_service_id);
                    __entry->ret = ret;
                    ),
            TP_printk("sending guest_endpoint_id %pUl, host_service_id %pUl, "
index f5fa3b3..70b30e2 100644 (file)
@@ -292,7 +292,7 @@ struct vmbus_msginfo {
        struct list_head msglist_entry;
 
        /* The message itself */
-       unsigned char msg[0];
+       unsigned char msg[];
 };
 
 
index 029378c..e06c6b9 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/kdebug.h>
 #include <linux/efi.h>
 #include <linux/random.h>
+#include <linux/kernel.h>
 #include <linux/syscore_ops.h>
 #include <clocksource/hyperv_timer.h>
 #include "hyperv_vmbus.h"
@@ -48,14 +49,35 @@ static int hyperv_cpuhp_online;
 
 static void *hv_panic_page;
 
+/*
+ * Boolean to control whether to report panic messages over Hyper-V.
+ *
+ * It can be set via /proc/sys/kernel/hyperv/record_panic_msg
+ */
+static int sysctl_record_panic_msg = 1;
+
+static int hyperv_report_reg(void)
+{
+       return !sysctl_record_panic_msg || !hv_panic_page;
+}
+
 static int hyperv_panic_event(struct notifier_block *nb, unsigned long val,
                              void *args)
 {
        struct pt_regs *regs;
 
-       regs = current_pt_regs();
+       vmbus_initiate_unload(true);
 
-       hyperv_report_panic(regs, val);
+       /*
+        * Hyper-V should be notified only once about a panic.  If we will be
+        * doing hyperv_report_panic_msg() later with kmsg data, don't do
+        * the notification here.
+        */
+       if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE
+           && hyperv_report_reg()) {
+               regs = current_pt_regs();
+               hyperv_report_panic(regs, val, false);
+       }
        return NOTIFY_DONE;
 }
 
@@ -65,7 +87,13 @@ static int hyperv_die_event(struct notifier_block *nb, unsigned long val,
        struct die_args *die = (struct die_args *)args;
        struct pt_regs *regs = die->regs;
 
-       hyperv_report_panic(regs, val);
+       /*
+        * Hyper-V should be notified only once about a panic.  If we will be
+        * doing hyperv_report_panic_msg() later with kmsg data, don't do
+        * the notification here.
+        */
+       if (hyperv_report_reg())
+               hyperv_report_panic(regs, val, true);
        return NOTIFY_DONE;
 }
 
@@ -950,6 +978,9 @@ static int vmbus_resume(struct device *child_device)
 
        return drv->resume(dev);
 }
+#else
+#define vmbus_suspend NULL
+#define vmbus_resume NULL
 #endif /* CONFIG_PM_SLEEP */
 
 /*
@@ -969,11 +1000,22 @@ static void vmbus_device_release(struct device *device)
 }
 
 /*
- * Note: we must use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS rather than
- * SET_SYSTEM_SLEEP_PM_OPS: see the comment before vmbus_bus_pm.
+ * Note: we must use the "noirq" ops: see the comment before vmbus_bus_pm.
+ *
+ * suspend_noirq/resume_noirq are set to NULL to support Suspend-to-Idle: we
+ * shouldn't suspend the vmbus devices upon Suspend-to-Idle, otherwise there
+ * is no way to wake up a Generation-2 VM.
+ *
+ * The other 4 ops are for hibernation.
  */
+
 static const struct dev_pm_ops vmbus_pm = {
-       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(vmbus_suspend, vmbus_resume)
+       .suspend_noirq  = NULL,
+       .resume_noirq   = NULL,
+       .freeze_noirq   = vmbus_suspend,
+       .thaw_noirq     = vmbus_resume,
+       .poweroff_noirq = vmbus_suspend,
+       .restore_noirq  = vmbus_resume,
 };
 
 /* The one and only one */
@@ -1252,13 +1294,6 @@ static void vmbus_isr(void)
        add_interrupt_randomness(HYPERVISOR_CALLBACK_VECTOR, 0);
 }
 
-/*
- * Boolean to control whether to report panic messages over Hyper-V.
- *
- * It can be set via /proc/sys/kernel/hyperv/record_panic_msg
- */
-static int sysctl_record_panic_msg = 1;
-
 /*
  * Callback from kmsg_dump. Grab as much as possible from the end of the kmsg
  * buffer and call into Hyper-V to transfer the data.
@@ -1382,19 +1417,29 @@ static int vmbus_bus_init(void)
                        hv_panic_page = (void *)hv_alloc_hyperv_zeroed_page();
                        if (hv_panic_page) {
                                ret = kmsg_dump_register(&hv_kmsg_dumper);
-                               if (ret)
+                               if (ret) {
                                        pr_err("Hyper-V: kmsg dump register "
                                                "error 0x%x\n", ret);
+                                       hv_free_hyperv_page(
+                                           (unsigned long)hv_panic_page);
+                                       hv_panic_page = NULL;
+                               }
                        } else
                                pr_err("Hyper-V: panic message page memory "
                                        "allocation failed");
                }
 
                register_die_notifier(&hyperv_die_block);
-               atomic_notifier_chain_register(&panic_notifier_list,
-                                              &hyperv_panic_block);
        }
 
+       /*
+        * Always register the panic notifier because we need to unload
+        * the VMbus channel connection to prevent any VMbus
+        * activity after the VM panics.
+        */
+       atomic_notifier_chain_register(&panic_notifier_list,
+                              &hyperv_panic_block);
+
        vmbus_request_offers();
 
        return 0;
@@ -1407,7 +1452,6 @@ err_alloc:
        hv_remove_vmbus_irq();
 
        bus_unregister(&hv_bus);
-       hv_free_hyperv_page((unsigned long)hv_panic_page);
        unregister_sysctl_table(hv_ctl_table_hdr);
        hv_ctl_table_hdr = NULL;
        return ret;
@@ -2204,8 +2248,6 @@ static int vmbus_bus_suspend(struct device *dev)
 
        vmbus_initiate_unload(false);
 
-       vmbus_connection.conn_state = DISCONNECTED;
-
        /* Reset the event for the next resume. */
        reinit_completion(&vmbus_connection.ready_for_resume_event);
 
@@ -2253,6 +2295,9 @@ static int vmbus_bus_resume(struct device *dev)
 
        return 0;
 }
+#else
+#define vmbus_bus_suspend NULL
+#define vmbus_bus_resume NULL
 #endif /* CONFIG_PM_SLEEP */
 
 static const struct acpi_device_id vmbus_acpi_device_ids[] = {
@@ -2263,16 +2308,24 @@ static const struct acpi_device_id vmbus_acpi_device_ids[] = {
 MODULE_DEVICE_TABLE(acpi, vmbus_acpi_device_ids);
 
 /*
- * Note: we must use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS rather than
- * SET_SYSTEM_SLEEP_PM_OPS, otherwise NIC SR-IOV can not work, because the
- * "pci_dev_pm_ops" uses the "noirq" callbacks: in the resume path, the
- * pci "noirq" restore callback runs before "non-noirq" callbacks (see
+ * Note: we must use the "no_irq" ops, otherwise hibernation can not work with
+ * PCI device assignment, because "pci_dev_pm_ops" uses the "noirq" ops: in
+ * the resume path, the pci "noirq" restore op runs before "non-noirq" op (see
  * resume_target_kernel() -> dpm_resume_start(), and hibernation_restore() ->
  * dpm_resume_end()). This means vmbus_bus_resume() and the pci-hyperv's
- * resume callback must also run via the "noirq" callbacks.
+ * resume callback must also run via the "noirq" ops.
+ *
+ * Set suspend_noirq/resume_noirq to NULL for Suspend-to-Idle: see the comment
+ * earlier in this file before vmbus_pm.
  */
+
 static const struct dev_pm_ops vmbus_bus_pm = {
-       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(vmbus_bus_suspend, vmbus_bus_resume)
+       .suspend_noirq  = NULL,
+       .resume_noirq   = NULL,
+       .freeze_noirq   = vmbus_bus_suspend,
+       .thaw_noirq     = vmbus_bus_resume,
+       .poweroff_noirq = vmbus_bus_suspend,
+       .restore_noirq  = vmbus_bus_resume
 };
 
 static struct acpi_driver vmbus_acpi_driver = {
@@ -2289,7 +2342,6 @@ static void hv_kexec_handler(void)
 {
        hv_stimer_global_cleanup();
        vmbus_initiate_unload(false);
-       vmbus_connection.conn_state = DISCONNECTED;
        /* Make sure conn_state is set as hv_synic_cleanup checks for it */
        mb();
        cpuhp_remove_state(hyperv_cpuhp_online);
@@ -2306,7 +2358,6 @@ static void hv_crash_handler(struct pt_regs *regs)
         * doing the cleanup for current CPU only. This should be sufficient
         * for kdump.
         */
-       vmbus_connection.conn_state = DISCONNECTED;
        cpu = smp_processor_id();
        hv_stimer_cleanup(cpu);
        hv_synic_disable_regs(cpu);
index 05a3083..4c62f90 100644 (file)
@@ -412,7 +412,7 @@ config SENSORS_DRIVETEMP
          hard disk drives.
 
          This driver can also be built as a module. If so, the module
-         will be called satatemp.
+         will be called drivetemp.
 
 config SENSORS_DS620
        tristate "Dallas Semiconductor DS620"
index 370d0c7..9179460 100644 (file)
@@ -264,12 +264,18 @@ static int drivetemp_get_scttemp(struct drivetemp_data *st, u32 attr, long *val)
                return err;
        switch (attr) {
        case hwmon_temp_input:
+               if (!temp_is_valid(buf[SCT_STATUS_TEMP]))
+                       return -ENODATA;
                *val = temp_from_sct(buf[SCT_STATUS_TEMP]);
                break;
        case hwmon_temp_lowest:
+               if (!temp_is_valid(buf[SCT_STATUS_TEMP_LOWEST]))
+                       return -ENODATA;
                *val = temp_from_sct(buf[SCT_STATUS_TEMP_LOWEST]);
                break;
        case hwmon_temp_highest:
+               if (!temp_is_valid(buf[SCT_STATUS_TEMP_HIGHEST]))
+                       return -ENODATA;
                *val = temp_from_sct(buf[SCT_STATUS_TEMP_HIGHEST]);
                break;
        default:
index f2d81b0..e3f1ebe 100644 (file)
@@ -506,7 +506,7 @@ static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id)
        }
        data->config = config;
 
-       hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+       hwmon_dev = devm_hwmon_device_register_with_info(dev, "jc42",
                                                         data, &jc42_chip_info,
                                                         NULL);
        return PTR_ERR_OR_ZERO(hwmon_dev);
index 3f37d5d..9915578 100644 (file)
@@ -186,7 +186,7 @@ static long get_raw_temp(struct k10temp_data *data)
        return temp;
 }
 
-const char *k10temp_temp_label[] = {
+static const char *k10temp_temp_label[] = {
        "Tctl",
        "Tdie",
        "Tccd1",
@@ -199,12 +199,12 @@ const char *k10temp_temp_label[] = {
        "Tccd8",
 };
 
-const char *k10temp_in_label[] = {
+static const char *k10temp_in_label[] = {
        "Vcore",
        "Vsoc",
 };
 
-const char *k10temp_curr_label[] = {
+static const char *k10temp_curr_label[] = {
        "Icore",
        "Isoc",
 };
index 4d23152..0c62271 100644 (file)
 #define ISL68137_VOUT_AVS      0x30
 #define RAA_DMPVR2_READ_VMON   0xc8
 
-enum versions {
+enum chips {
        isl68137,
+       isl68220,
+       isl68221,
+       isl68222,
+       isl68223,
+       isl68224,
+       isl68225,
+       isl68226,
+       isl68227,
+       isl68229,
+       isl68233,
+       isl68239,
+       isl69222,
+       isl69223,
+       isl69224,
+       isl69225,
+       isl69227,
+       isl69228,
+       isl69234,
+       isl69236,
+       isl69239,
+       isl69242,
+       isl69243,
+       isl69247,
+       isl69248,
+       isl69254,
+       isl69255,
+       isl69256,
+       isl69259,
+       isl69260,
+       isl69268,
+       isl69269,
+       isl69298,
+       raa228000,
+       raa228004,
+       raa228006,
+       raa228228,
+       raa229001,
+       raa229004,
+};
+
+enum variants {
+       raa_dmpvr1_2rail,
        raa_dmpvr2_1rail,
        raa_dmpvr2_2rail,
        raa_dmpvr2_3rail,
@@ -186,7 +228,7 @@ static int isl68137_probe(struct i2c_client *client,
        memcpy(info, &raa_dmpvr_info, sizeof(*info));
 
        switch (id->driver_data) {
-       case isl68137:
+       case raa_dmpvr1_2rail:
                info->pages = 2;
                info->R[PSC_VOLTAGE_IN] = 3;
                info->func[0] &= ~PMBUS_HAVE_VMON;
@@ -224,11 +266,47 @@ static int isl68137_probe(struct i2c_client *client,
 }
 
 static const struct i2c_device_id raa_dmpvr_id[] = {
-       {"isl68137", isl68137},
-       {"raa_dmpvr2_1rail", raa_dmpvr2_1rail},
-       {"raa_dmpvr2_2rail", raa_dmpvr2_2rail},
-       {"raa_dmpvr2_3rail", raa_dmpvr2_3rail},
-       {"raa_dmpvr2_hv", raa_dmpvr2_hv},
+       {"isl68137", raa_dmpvr1_2rail},
+       {"isl68220", raa_dmpvr2_2rail},
+       {"isl68221", raa_dmpvr2_3rail},
+       {"isl68222", raa_dmpvr2_2rail},
+       {"isl68223", raa_dmpvr2_2rail},
+       {"isl68224", raa_dmpvr2_3rail},
+       {"isl68225", raa_dmpvr2_2rail},
+       {"isl68226", raa_dmpvr2_3rail},
+       {"isl68227", raa_dmpvr2_1rail},
+       {"isl68229", raa_dmpvr2_3rail},
+       {"isl68233", raa_dmpvr2_2rail},
+       {"isl68239", raa_dmpvr2_3rail},
+
+       {"isl69222", raa_dmpvr2_2rail},
+       {"isl69223", raa_dmpvr2_3rail},
+       {"isl69224", raa_dmpvr2_2rail},
+       {"isl69225", raa_dmpvr2_2rail},
+       {"isl69227", raa_dmpvr2_3rail},
+       {"isl69228", raa_dmpvr2_3rail},
+       {"isl69234", raa_dmpvr2_2rail},
+       {"isl69236", raa_dmpvr2_2rail},
+       {"isl69239", raa_dmpvr2_3rail},
+       {"isl69242", raa_dmpvr2_2rail},
+       {"isl69243", raa_dmpvr2_1rail},
+       {"isl69247", raa_dmpvr2_2rail},
+       {"isl69248", raa_dmpvr2_2rail},
+       {"isl69254", raa_dmpvr2_2rail},
+       {"isl69255", raa_dmpvr2_2rail},
+       {"isl69256", raa_dmpvr2_2rail},
+       {"isl69259", raa_dmpvr2_2rail},
+       {"isl69260", raa_dmpvr2_2rail},
+       {"isl69268", raa_dmpvr2_2rail},
+       {"isl69269", raa_dmpvr2_3rail},
+       {"isl69298", raa_dmpvr2_2rail},
+
+       {"raa228000", raa_dmpvr2_hv},
+       {"raa228004", raa_dmpvr2_hv},
+       {"raa228006", raa_dmpvr2_hv},
+       {"raa228228", raa_dmpvr2_2rail},
+       {"raa229001", raa_dmpvr2_2rail},
+       {"raa229004", raa_dmpvr2_2rail},
        {}
 };
 
index 20ef638..f5c00f9 100644 (file)
@@ -384,7 +384,6 @@ static int altr_i2c_probe(struct platform_device *pdev)
        struct altr_i2c_dev *idev = NULL;
        struct resource *res;
        int irq, ret;
-       u32 val;
 
        idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL);
        if (!idev)
@@ -411,17 +410,17 @@ static int altr_i2c_probe(struct platform_device *pdev)
        init_completion(&idev->msg_complete);
        spin_lock_init(&idev->lock);
 
-       val = device_property_read_u32(idev->dev, "fifo-size",
+       ret = device_property_read_u32(idev->dev, "fifo-size",
                                       &idev->fifo_size);
-       if (val) {
+       if (ret) {
                dev_err(&pdev->dev, "FIFO size set to default of %d\n",
                        ALTR_I2C_DFLT_FIFO_SZ);
                idev->fifo_size = ALTR_I2C_DFLT_FIFO_SZ;
        }
 
-       val = device_property_read_u32(idev->dev, "clock-frequency",
+       ret = device_property_read_u32(idev->dev, "clock-frequency",
                                       &idev->bus_clk_rate);
-       if (val) {
+       if (ret) {
                dev_err(&pdev->dev, "Default to 100kHz\n");
                idev->bus_clk_rate = I2C_MAX_STANDARD_MODE_FREQ;        /* default clock rate */
        }
index 5e4800d..cd3fd5e 100644 (file)
@@ -349,12 +349,12 @@ static int amd_mp2_pci_probe(struct pci_dev *pci_dev,
        if (!privdata)
                return -ENOMEM;
 
+       privdata->pci_dev = pci_dev;
        rc = amd_mp2_pci_init(privdata, pci_dev);
        if (rc)
                return rc;
 
        mutex_init(&privdata->c2p_lock);
-       privdata->pci_dev = pci_dev;
 
        pm_runtime_set_autosuspend_delay(&pci_dev->dev, 1000);
        pm_runtime_use_autosuspend(&pci_dev->dev);
index 07c1993..f51702d 100644 (file)
@@ -603,6 +603,7 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
        /* Ack all interrupts except for Rx done */
        writel(irq_received & ~ASPEED_I2CD_INTR_RX_DONE,
               bus->base + ASPEED_I2C_INTR_STS_REG);
+       readl(bus->base + ASPEED_I2C_INTR_STS_REG);
        irq_remaining = irq_received;
 
 #if IS_ENABLED(CONFIG_I2C_SLAVE)
@@ -645,9 +646,11 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
                        irq_received, irq_handled);
 
        /* Ack Rx done */
-       if (irq_received & ASPEED_I2CD_INTR_RX_DONE)
+       if (irq_received & ASPEED_I2CD_INTR_RX_DONE) {
                writel(ASPEED_I2CD_INTR_RX_DONE,
                       bus->base + ASPEED_I2C_INTR_STS_REG);
+               readl(bus->base + ASPEED_I2C_INTR_STS_REG);
+       }
        spin_unlock(&bus->lock);
        return irq_remaining ? IRQ_NONE : IRQ_HANDLED;
 }
index 44be092..d091a12 100644 (file)
@@ -360,6 +360,9 @@ static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
                        value = (u8)((val >> S_RX_DATA_SHIFT) & S_RX_DATA_MASK);
                        i2c_slave_event(iproc_i2c->slave,
                                        I2C_SLAVE_WRITE_RECEIVED, &value);
+                       if (rx_status == I2C_SLAVE_RX_END)
+                               i2c_slave_event(iproc_i2c->slave,
+                                               I2C_SLAVE_STOP, &value);
                }
        } else if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) {
                /* Master read other than start */
index c98befe..5536673 100644 (file)
@@ -354,10 +354,16 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
        adap->dev.of_node = pdev->dev.of_node;
        adap->nr = -1;
 
-       dev_pm_set_driver_flags(&pdev->dev,
-                               DPM_FLAG_SMART_PREPARE |
-                               DPM_FLAG_SMART_SUSPEND |
-                               DPM_FLAG_LEAVE_SUSPENDED);
+       if (dev->flags & ACCESS_NO_IRQ_SUSPEND) {
+               dev_pm_set_driver_flags(&pdev->dev,
+                                       DPM_FLAG_SMART_PREPARE |
+                                       DPM_FLAG_LEAVE_SUSPENDED);
+       } else {
+               dev_pm_set_driver_flags(&pdev->dev,
+                                       DPM_FLAG_SMART_PREPARE |
+                                       DPM_FLAG_SMART_SUSPEND |
+                                       DPM_FLAG_LEAVE_SUSPENDED);
+       }
 
        /* The code below assumes runtime PM to be disabled. */
        WARN_ON(pm_runtime_enabled(&pdev->dev));
index 5cc0b0e..a669127 100644 (file)
@@ -2273,19 +2273,6 @@ i2c_new_scanned_device(struct i2c_adapter *adap,
 }
 EXPORT_SYMBOL_GPL(i2c_new_scanned_device);
 
-struct i2c_client *
-i2c_new_probed_device(struct i2c_adapter *adap,
-                     struct i2c_board_info *info,
-                     unsigned short const *addr_list,
-                     int (*probe)(struct i2c_adapter *adap, unsigned short addr))
-{
-       struct i2c_client *client;
-
-       client = i2c_new_scanned_device(adap, info, addr_list, probe);
-       return IS_ERR(client) ? NULL : client;
-}
-EXPORT_SYMBOL_GPL(i2c_new_probed_device);
-
 struct i2c_adapter *i2c_get_adapter(int nr)
 {
        struct i2c_adapter *adapter;
index 02981f3..08ba1a8 100644 (file)
 #define AD7193_CH_AINCOM       0x600 /* AINCOM - AINCOM */
 
 /* ID Register Bit Designations (AD7192_REG_ID) */
-#define ID_AD7190              0x4
-#define ID_AD7192              0x0
-#define ID_AD7193              0x2
-#define ID_AD7195              0x6
+#define CHIPID_AD7190          0x4
+#define CHIPID_AD7192          0x0
+#define CHIPID_AD7193          0x2
+#define CHIPID_AD7195          0x6
 #define AD7192_ID_MASK         0x0F
 
 /* GPOCON Register Bit Designations (AD7192_REG_GPOCON) */
@@ -161,7 +161,20 @@ enum {
        AD7192_SYSCALIB_FULL_SCALE,
 };
 
+enum {
+       ID_AD7190,
+       ID_AD7192,
+       ID_AD7193,
+       ID_AD7195,
+};
+
+struct ad7192_chip_info {
+       unsigned int                    chip_id;
+       const char                      *name;
+};
+
 struct ad7192_state {
+       const struct ad7192_chip_info   *chip_info;
        struct regulator                *avdd;
        struct regulator                *dvdd;
        struct clk                      *mclk;
@@ -172,7 +185,6 @@ struct ad7192_state {
        u32                             conf;
        u32                             scale_avail[8][2];
        u8                              gpocon;
-       u8                              devid;
        u8                              clock_sel;
        struct mutex                    lock;   /* protect sensor state */
        u8                              syscalib_mode[8];
@@ -348,7 +360,7 @@ static int ad7192_setup(struct ad7192_state *st, struct device_node *np)
 
        id &= AD7192_ID_MASK;
 
-       if (id != st->devid)
+       if (id != st->chip_info->chip_id)
                dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n",
                         id);
 
@@ -363,7 +375,7 @@ static int ad7192_setup(struct ad7192_state *st, struct device_node *np)
                st->mode |= AD7192_MODE_REJ60;
 
        refin2_en = of_property_read_bool(np, "adi,refin2-pins-enable");
-       if (refin2_en && st->devid != ID_AD7195)
+       if (refin2_en && st->chip_info->chip_id != CHIPID_AD7195)
                st->conf |= AD7192_CONF_REFSEL;
 
        st->conf &= ~AD7192_CONF_CHOP;
@@ -859,12 +871,31 @@ static const struct iio_chan_spec ad7193_channels[] = {
        IIO_CHAN_SOFT_TIMESTAMP(14),
 };
 
+static const struct ad7192_chip_info ad7192_chip_info_tbl[] = {
+       [ID_AD7190] = {
+               .chip_id = CHIPID_AD7190,
+               .name = "ad7190",
+       },
+       [ID_AD7192] = {
+               .chip_id = CHIPID_AD7192,
+               .name = "ad7192",
+       },
+       [ID_AD7193] = {
+               .chip_id = CHIPID_AD7193,
+               .name = "ad7193",
+       },
+       [ID_AD7195] = {
+               .chip_id = CHIPID_AD7195,
+               .name = "ad7195",
+       },
+};
+
 static int ad7192_channels_config(struct iio_dev *indio_dev)
 {
        struct ad7192_state *st = iio_priv(indio_dev);
 
-       switch (st->devid) {
-       case ID_AD7193:
+       switch (st->chip_info->chip_id) {
+       case CHIPID_AD7193:
                indio_dev->channels = ad7193_channels;
                indio_dev->num_channels = ARRAY_SIZE(ad7193_channels);
                break;
@@ -878,10 +909,10 @@ static int ad7192_channels_config(struct iio_dev *indio_dev)
 }
 
 static const struct of_device_id ad7192_of_match[] = {
-       { .compatible = "adi,ad7190", .data = (void *)ID_AD7190 },
-       { .compatible = "adi,ad7192", .data = (void *)ID_AD7192 },
-       { .compatible = "adi,ad7193", .data = (void *)ID_AD7193 },
-       { .compatible = "adi,ad7195", .data = (void *)ID_AD7195 },
+       { .compatible = "adi,ad7190", .data = &ad7192_chip_info_tbl[ID_AD7190] },
+       { .compatible = "adi,ad7192", .data = &ad7192_chip_info_tbl[ID_AD7192] },
+       { .compatible = "adi,ad7193", .data = &ad7192_chip_info_tbl[ID_AD7193] },
+       { .compatible = "adi,ad7195", .data = &ad7192_chip_info_tbl[ID_AD7195] },
        {}
 };
 MODULE_DEVICE_TABLE(of, ad7192_of_match);
@@ -938,16 +969,16 @@ static int ad7192_probe(struct spi_device *spi)
        }
 
        spi_set_drvdata(spi, indio_dev);
-       st->devid = (unsigned long)of_device_get_match_data(&spi->dev);
+       st->chip_info = of_device_get_match_data(&spi->dev);
        indio_dev->dev.parent = &spi->dev;
-       indio_dev->name = spi_get_device_id(spi)->name;
+       indio_dev->name = st->chip_info->name;
        indio_dev->modes = INDIO_DIRECT_MODE;
 
        ret = ad7192_channels_config(indio_dev);
        if (ret < 0)
                goto error_disable_dvdd;
 
-       if (st->devid == ID_AD7195)
+       if (st->chip_info->chip_id == CHIPID_AD7195)
                indio_dev->info = &ad7195_info;
        else
                indio_dev->info = &ad7192_info;
index b747db9..e5691e3 100644 (file)
@@ -542,7 +542,7 @@ static const struct iio_info ad7797_info = {
        .read_raw = &ad7793_read_raw,
        .write_raw = &ad7793_write_raw,
        .write_raw_get_fmt = &ad7793_write_raw_get_fmt,
-       .attrs = &ad7793_attribute_group,
+       .attrs = &ad7797_attribute_group,
        .validate_trigger = ad_sd_validate_trigger,
 };
 
index 80c3f96..ae622ee 100644 (file)
@@ -1418,8 +1418,30 @@ static unsigned int stm32_adc_dma_residue(struct stm32_adc *adc)
 static void stm32_adc_dma_buffer_done(void *data)
 {
        struct iio_dev *indio_dev = data;
+       struct stm32_adc *adc = iio_priv(indio_dev);
+       int residue = stm32_adc_dma_residue(adc);
+
+       /*
+        * In DMA mode the trigger services of IIO are not used
+        * (e.g. no call to iio_trigger_poll).
+        * Calling irq handler associated to the hardware trigger is not
+        * relevant as the conversions have already been done. Data
+        * transfers are performed directly in DMA callback instead.
+        * This implementation avoids to call trigger irq handler that
+        * may sleep, in an atomic context (DMA irq handler context).
+        */
+       dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
 
-       iio_trigger_poll_chained(indio_dev->trig);
+       while (residue >= indio_dev->scan_bytes) {
+               u16 *buffer = (u16 *)&adc->rx_buf[adc->bufi];
+
+               iio_push_to_buffers(indio_dev, buffer);
+
+               residue -= indio_dev->scan_bytes;
+               adc->bufi += indio_dev->scan_bytes;
+               if (adc->bufi >= adc->rx_buf_sz)
+                       adc->bufi = 0;
+       }
 }
 
 static int stm32_adc_dma_start(struct iio_dev *indio_dev)
@@ -1845,6 +1867,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
 {
        struct iio_dev *indio_dev;
        struct device *dev = &pdev->dev;
+       irqreturn_t (*handler)(int irq, void *p) = NULL;
        struct stm32_adc *adc;
        int ret;
 
@@ -1911,9 +1934,11 @@ static int stm32_adc_probe(struct platform_device *pdev)
        if (ret < 0)
                return ret;
 
+       if (!adc->dma_chan)
+               handler = &stm32_adc_trigger_handler;
+
        ret = iio_triggered_buffer_setup(indio_dev,
-                                        &iio_pollfunc_store_time,
-                                        &stm32_adc_trigger_handler,
+                                        &iio_pollfunc_store_time, handler,
                                         &stm32_adc_buffer_setup_ops);
        if (ret) {
                dev_err(&pdev->dev, "buffer setup failed\n");
index 9a46080..abe4b56 100644 (file)
@@ -29,7 +29,7 @@ struct ads8344 {
        struct mutex lock;
 
        u8 tx_buf ____cacheline_aligned;
-       u16 rx_buf;
+       u8 rx_buf[3];
 };
 
 #define ADS8344_VOLTAGE_CHANNEL(chan, si)                              \
@@ -89,11 +89,11 @@ static int ads8344_adc_conversion(struct ads8344 *adc, int channel,
 
        udelay(9);
 
-       ret = spi_read(spi, &adc->rx_buf, 2);
+       ret = spi_read(spi, adc->rx_buf, sizeof(adc->rx_buf));
        if (ret)
                return ret;
 
-       return adc->rx_buf;
+       return adc->rx_buf[0] << 9 | adc->rx_buf[1] << 1 | adc->rx_buf[2] >> 7;
 }
 
 static int ads8344_read_raw(struct iio_dev *iio,
index ec227b3..6fd06e4 100644 (file)
@@ -102,6 +102,16 @@ static const unsigned int XADC_ZYNQ_UNMASK_TIMEOUT = 500;
 
 #define XADC_FLAGS_BUFFERED BIT(0)
 
+/*
+ * The XADC hardware supports a samplerate of up to 1MSPS. Unfortunately it does
+ * not have a hardware FIFO. Which means an interrupt is generated for each
+ * conversion sequence. At 1MSPS sample rate the CPU in ZYNQ7000 is completely
+ * overloaded by the interrupts that it soft-lockups. For this reason the driver
+ * limits the maximum samplerate 150kSPS. At this rate the CPU is fairly busy,
+ * but still responsive.
+ */
+#define XADC_MAX_SAMPLERATE 150000
+
 static void xadc_write_reg(struct xadc *xadc, unsigned int reg,
        uint32_t val)
 {
@@ -674,7 +684,7 @@ static int xadc_trigger_set_state(struct iio_trigger *trigger, bool state)
 
        spin_lock_irqsave(&xadc->lock, flags);
        xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &val);
-       xadc_write_reg(xadc, XADC_AXI_REG_IPISR, val & XADC_AXI_INT_EOS);
+       xadc_write_reg(xadc, XADC_AXI_REG_IPISR, XADC_AXI_INT_EOS);
        if (state)
                val |= XADC_AXI_INT_EOS;
        else
@@ -722,13 +732,14 @@ static int xadc_power_adc_b(struct xadc *xadc, unsigned int seq_mode)
 {
        uint16_t val;
 
+       /* Powerdown the ADC-B when it is not needed. */
        switch (seq_mode) {
        case XADC_CONF1_SEQ_SIMULTANEOUS:
        case XADC_CONF1_SEQ_INDEPENDENT:
-               val = XADC_CONF2_PD_ADC_B;
+               val = 0;
                break;
        default:
-               val = 0;
+               val = XADC_CONF2_PD_ADC_B;
                break;
        }
 
@@ -797,6 +808,16 @@ static int xadc_preenable(struct iio_dev *indio_dev)
        if (ret)
                goto err;
 
+       /*
+        * In simultaneous mode the upper and lower aux channels are samples at
+        * the same time. In this mode the upper 8 bits in the sequencer
+        * register are don't care and the lower 8 bits control two channels
+        * each. As such we must set the bit if either the channel in the lower
+        * group or the upper group is enabled.
+        */
+       if (seq_mode == XADC_CONF1_SEQ_SIMULTANEOUS)
+               scan_mask = ((scan_mask >> 8) | scan_mask) & 0xff0000;
+
        ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(1), scan_mask >> 16);
        if (ret)
                goto err;
@@ -823,11 +844,27 @@ static const struct iio_buffer_setup_ops xadc_buffer_ops = {
        .postdisable = &xadc_postdisable,
 };
 
+static int xadc_read_samplerate(struct xadc *xadc)
+{
+       unsigned int div;
+       uint16_t val16;
+       int ret;
+
+       ret = xadc_read_adc_reg(xadc, XADC_REG_CONF2, &val16);
+       if (ret)
+               return ret;
+
+       div = (val16 & XADC_CONF2_DIV_MASK) >> XADC_CONF2_DIV_OFFSET;
+       if (div < 2)
+               div = 2;
+
+       return xadc_get_dclk_rate(xadc) / div / 26;
+}
+
 static int xadc_read_raw(struct iio_dev *indio_dev,
        struct iio_chan_spec const *chan, int *val, int *val2, long info)
 {
        struct xadc *xadc = iio_priv(indio_dev);
-       unsigned int div;
        uint16_t val16;
        int ret;
 
@@ -880,41 +917,31 @@ static int xadc_read_raw(struct iio_dev *indio_dev,
                *val = -((273150 << 12) / 503975);
                return IIO_VAL_INT;
        case IIO_CHAN_INFO_SAMP_FREQ:
-               ret = xadc_read_adc_reg(xadc, XADC_REG_CONF2, &val16);
-               if (ret)
+               ret = xadc_read_samplerate(xadc);
+               if (ret < 0)
                        return ret;
 
-               div = (val16 & XADC_CONF2_DIV_MASK) >> XADC_CONF2_DIV_OFFSET;
-               if (div < 2)
-                       div = 2;
-
-               *val = xadc_get_dclk_rate(xadc) / div / 26;
-
+               *val = ret;
                return IIO_VAL_INT;
        default:
                return -EINVAL;
        }
 }
 
-static int xadc_write_raw(struct iio_dev *indio_dev,
-       struct iio_chan_spec const *chan, int val, int val2, long info)
+static int xadc_write_samplerate(struct xadc *xadc, int val)
 {
-       struct xadc *xadc = iio_priv(indio_dev);
        unsigned long clk_rate = xadc_get_dclk_rate(xadc);
        unsigned int div;
 
        if (!clk_rate)
                return -EINVAL;
 
-       if (info != IIO_CHAN_INFO_SAMP_FREQ)
-               return -EINVAL;
-
        if (val <= 0)
                return -EINVAL;
 
        /* Max. 150 kSPS */
-       if (val > 150000)
-               val = 150000;
+       if (val > XADC_MAX_SAMPLERATE)
+               val = XADC_MAX_SAMPLERATE;
 
        val *= 26;
 
@@ -927,7 +954,7 @@ static int xadc_write_raw(struct iio_dev *indio_dev,
         * limit.
         */
        div = clk_rate / val;
-       if (clk_rate / div / 26 > 150000)
+       if (clk_rate / div / 26 > XADC_MAX_SAMPLERATE)
                div++;
        if (div < 2)
                div = 2;
@@ -938,6 +965,17 @@ static int xadc_write_raw(struct iio_dev *indio_dev,
                div << XADC_CONF2_DIV_OFFSET);
 }
 
+static int xadc_write_raw(struct iio_dev *indio_dev,
+       struct iio_chan_spec const *chan, int val, int val2, long info)
+{
+       struct xadc *xadc = iio_priv(indio_dev);
+
+       if (info != IIO_CHAN_INFO_SAMP_FREQ)
+               return -EINVAL;
+
+       return xadc_write_samplerate(xadc, val);
+}
+
 static const struct iio_event_spec xadc_temp_events[] = {
        {
                .type = IIO_EV_TYPE_THRESH,
@@ -1223,6 +1261,21 @@ static int xadc_probe(struct platform_device *pdev)
        if (ret)
                goto err_free_samplerate_trigger;
 
+       /*
+        * Make sure not to exceed the maximum samplerate since otherwise the
+        * resulting interrupt storm will soft-lock the system.
+        */
+       if (xadc->ops->flags & XADC_FLAGS_BUFFERED) {
+               ret = xadc_read_samplerate(xadc);
+               if (ret < 0)
+                       goto err_free_samplerate_trigger;
+               if (ret > XADC_MAX_SAMPLERATE) {
+                       ret = xadc_write_samplerate(xadc, XADC_MAX_SAMPLERATE);
+                       if (ret < 0)
+                               goto err_free_samplerate_trigger;
+               }
+       }
+
        ret = request_irq(xadc->irq, xadc->ops->interrupt_handler, 0,
                        dev_name(&pdev->dev), indio_dev);
        if (ret)
index 0e35ff0..13bdfbb 100644 (file)
@@ -79,7 +79,7 @@ int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr)
        struct st_sensor_odr_avl odr_out = {0, 0};
        struct st_sensor_data *sdata = iio_priv(indio_dev);
 
-       if (!sdata->sensor_settings->odr.addr)
+       if (!sdata->sensor_settings->odr.mask)
                return 0;
 
        err = st_sensors_match_odr(sdata->sensor_settings, odr, &odr_out);
index a98ea76..2d7623b 100644 (file)
@@ -525,7 +525,7 @@ static int ad5770r_channel_config(struct ad5770r_state *st)
                ret = fwnode_property_read_u32(child, "num", &num);
                if (ret)
                        return ret;
-               if (num > AD5770R_MAX_CHANNELS)
+               if (num >= AD5770R_MAX_CHANNELS)
                        return -EINVAL;
 
                ret = fwnode_property_read_u32_array(child,
index 7cb9ff3..0b8d2f7 100644 (file)
@@ -1617,6 +1617,10 @@ static int __maybe_unused inv_mpu_resume(struct device *dev)
        if (result)
                goto out_unlock;
 
+       pm_runtime_disable(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
        result = inv_mpu6050_switch_engine(st, true, st->suspended_sensors);
        if (result)
                goto out_unlock;
@@ -1638,13 +1642,18 @@ static int __maybe_unused inv_mpu_suspend(struct device *dev)
 
        mutex_lock(&st->lock);
 
+       st->suspended_sensors = 0;
+       if (pm_runtime_suspended(dev)) {
+               result = 0;
+               goto out_unlock;
+       }
+
        if (iio_buffer_enabled(indio_dev)) {
                result = inv_mpu6050_prepare_fifo(st, false);
                if (result)
                        goto out_unlock;
        }
 
-       st->suspended_sensors = 0;
        if (st->chip_config.accl_en)
                st->suspended_sensors |= INV_MPU6050_SENSOR_ACCL;
        if (st->chip_config.gyro_en)
index f2113a6..41cb20c 100644 (file)
@@ -337,6 +337,7 @@ enum st_lsm6dsx_fifo_mode {
  * @gain: Configured sensor sensitivity.
  * @odr: Output data rate of the sensor [Hz].
  * @watermark: Sensor watermark level.
+ * @decimator: Sensor decimation factor.
  * @sip: Number of samples in a given pattern.
  * @ts_ref: Sensor timestamp reference for hw one.
  * @ext_info: Sensor settings if it is connected to i2c controller
@@ -350,11 +351,13 @@ struct st_lsm6dsx_sensor {
        u32 odr;
 
        u16 watermark;
+       u8 decimator;
        u8 sip;
        s64 ts_ref;
 
        struct {
                const struct st_lsm6dsx_ext_dev_settings *settings;
+               u32 slv_odr;
                u8 addr;
        } ext_info;
 };
index bb89934..afd00da 100644 (file)
@@ -93,6 +93,7 @@ st_lsm6dsx_get_decimator_val(struct st_lsm6dsx_sensor *sensor, u32 max_odr)
                        break;
        }
 
+       sensor->decimator = decimator;
        return i == max_size ? 0 : st_lsm6dsx_decimator_table[i].val;
 }
 
@@ -337,7 +338,7 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr,
 int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
 {
        struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor, *ext_sensor = NULL;
-       int err, acc_sip, gyro_sip, ts_sip, ext_sip, read_len, offset;
+       int err, sip, acc_sip, gyro_sip, ts_sip, ext_sip, read_len, offset;
        u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE;
        u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask;
        u8 gyro_buff[ST_LSM6DSX_IIO_BUFF_SIZE];
@@ -399,19 +400,20 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
                acc_sip = acc_sensor->sip;
                ts_sip = hw->ts_sip;
                offset = 0;
+               sip = 0;
 
                while (acc_sip > 0 || gyro_sip > 0 || ext_sip > 0) {
-                       if (gyro_sip > 0) {
+                       if (gyro_sip > 0 && !(sip % gyro_sensor->decimator)) {
                                memcpy(gyro_buff, &hw->buff[offset],
                                       ST_LSM6DSX_SAMPLE_SIZE);
                                offset += ST_LSM6DSX_SAMPLE_SIZE;
                        }
-                       if (acc_sip > 0) {
+                       if (acc_sip > 0 && !(sip % acc_sensor->decimator)) {
                                memcpy(acc_buff, &hw->buff[offset],
                                       ST_LSM6DSX_SAMPLE_SIZE);
                                offset += ST_LSM6DSX_SAMPLE_SIZE;
                        }
-                       if (ext_sip > 0) {
+                       if (ext_sip > 0 && !(sip % ext_sensor->decimator)) {
                                memcpy(ext_buff, &hw->buff[offset],
                                       ST_LSM6DSX_SAMPLE_SIZE);
                                offset += ST_LSM6DSX_SAMPLE_SIZE;
@@ -441,18 +443,25 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
                                offset += ST_LSM6DSX_SAMPLE_SIZE;
                        }
 
-                       if (gyro_sip-- > 0)
+                       if (gyro_sip > 0 && !(sip % gyro_sensor->decimator)) {
                                iio_push_to_buffers_with_timestamp(
                                        hw->iio_devs[ST_LSM6DSX_ID_GYRO],
                                        gyro_buff, gyro_sensor->ts_ref + ts);
-                       if (acc_sip-- > 0)
+                               gyro_sip--;
+                       }
+                       if (acc_sip > 0 && !(sip % acc_sensor->decimator)) {
                                iio_push_to_buffers_with_timestamp(
                                        hw->iio_devs[ST_LSM6DSX_ID_ACC],
                                        acc_buff, acc_sensor->ts_ref + ts);
-                       if (ext_sip-- > 0)
+                               acc_sip--;
+                       }
+                       if (ext_sip > 0 && !(sip % ext_sensor->decimator)) {
                                iio_push_to_buffers_with_timestamp(
                                        hw->iio_devs[ST_LSM6DSX_ID_EXT0],
                                        ext_buff, ext_sensor->ts_ref + ts);
+                               ext_sip--;
+                       }
+                       sip++;
                }
        }
 
index 84d219a..4426524 100644 (file)
@@ -2036,11 +2036,21 @@ static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw)
        return 0;
 }
 
-static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
+static int st_lsm6dsx_reset_device(struct st_lsm6dsx_hw *hw)
 {
        const struct st_lsm6dsx_reg *reg;
        int err;
 
+       /*
+        * flush hw FIFO before device reset in order to avoid
+        * possible races on interrupt line 1. If the first interrupt
+        * line is asserted during hw reset the device will work in
+        * I3C-only mode (if it is supported)
+        */
+       err = st_lsm6dsx_flush_fifo(hw);
+       if (err < 0 && err != -ENOTSUPP)
+               return err;
+
        /* device sw reset */
        reg = &hw->settings->reset;
        err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
@@ -2059,6 +2069,18 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
 
        msleep(50);
 
+       return 0;
+}
+
+static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
+{
+       const struct st_lsm6dsx_reg *reg;
+       int err;
+
+       err = st_lsm6dsx_reset_device(hw);
+       if (err < 0)
+               return err;
+
        /* enable Block Data Update */
        reg = &hw->settings->bdu;
        err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
index 95ddd19..64ef07a 100644 (file)
@@ -421,7 +421,8 @@ int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable)
 
        settings = sensor->ext_info.settings;
        if (enable) {
-               err = st_lsm6dsx_shub_set_odr(sensor, sensor->odr);
+               err = st_lsm6dsx_shub_set_odr(sensor,
+                                             sensor->ext_info.slv_odr);
                if (err < 0)
                        return err;
        } else {
@@ -459,7 +460,7 @@ st_lsm6dsx_shub_read_oneshot(struct st_lsm6dsx_sensor *sensor,
        if (err < 0)
                return err;
 
-       delay = 1000000000 / sensor->odr;
+       delay = 1000000000 / sensor->ext_info.slv_odr;
        usleep_range(delay, 2 * delay);
 
        len = min_t(int, sizeof(data), ch->scan_type.realbits >> 3);
@@ -500,8 +501,8 @@ st_lsm6dsx_shub_read_raw(struct iio_dev *iio_dev,
                iio_device_release_direct_mode(iio_dev);
                break;
        case IIO_CHAN_INFO_SAMP_FREQ:
-               *val = sensor->odr / 1000;
-               *val2 = (sensor->odr % 1000) * 1000;
+               *val = sensor->ext_info.slv_odr / 1000;
+               *val2 = (sensor->ext_info.slv_odr % 1000) * 1000;
                ret = IIO_VAL_INT_PLUS_MICRO;
                break;
        case IIO_CHAN_INFO_SCALE:
@@ -535,8 +536,20 @@ st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev,
 
                val = val * 1000 + val2 / 1000;
                err = st_lsm6dsx_shub_get_odr_val(sensor, val, &data);
-               if (!err)
-                       sensor->odr = val;
+               if (!err) {
+                       struct st_lsm6dsx_hw *hw = sensor->hw;
+                       struct st_lsm6dsx_sensor *ref_sensor;
+                       u8 odr_val;
+                       int odr;
+
+                       ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+                       odr = st_lsm6dsx_check_odr(ref_sensor, val, &odr_val);
+                       if (odr < 0)
+                               return odr;
+
+                       sensor->ext_info.slv_odr = val;
+                       sensor->odr = odr;
+               }
                break;
        }
        default:
@@ -613,6 +626,7 @@ st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw,
                             const struct st_lsm6dsx_ext_dev_settings *info,
                             u8 i2c_addr, const char *name)
 {
+       enum st_lsm6dsx_sensor_id ref_id = ST_LSM6DSX_ID_ACC;
        struct iio_chan_spec *ext_channels;
        struct st_lsm6dsx_sensor *sensor;
        struct iio_dev *iio_dev;
@@ -628,7 +642,8 @@ st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw,
        sensor = iio_priv(iio_dev);
        sensor->id = id;
        sensor->hw = hw;
-       sensor->odr = info->odr_table.odr_avl[0].milli_hz;
+       sensor->odr = hw->settings->odr_table[ref_id].odr_avl[0].milli_hz;
+       sensor->ext_info.slv_odr = info->odr_table.odr_avl[0].milli_hz;
        sensor->gain = info->fs_table.fs_avl[0].gain;
        sensor->ext_info.settings = info;
        sensor->ext_info.addr = i2c_addr;
index 2352c42..24f7bbf 100644 (file)
@@ -915,14 +915,11 @@ static ssize_t iio_write_channel_info(struct device *dev,
                        return -EINVAL;
                integer = ch;
        } else {
-               ret = iio_str_to_fixpoint(buf, fract_mult, &integer, &fract);
+               ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract,
+                                           scale_db);
                if (ret)
                        return ret;
        }
-       ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract,
-                                   scale_db);
-       if (ret)
-               return ret;
 
        ret = indio_dev->info->write_raw(indio_dev, this_attr->c,
                                         integer, fract, this_attr->address);
index 4794113..17f14e0 100644 (file)
@@ -862,7 +862,7 @@ static struct cm_id_private *cm_alloc_id_priv(struct ib_device *device,
 
        ret = xa_alloc_cyclic_irq(&cm.local_id_table, &id, NULL, xa_limit_32b,
                                  &cm.local_id_next, GFP_KERNEL);
-       if (ret)
+       if (ret < 0)
                goto error;
        cm_id_priv->id.local_id = (__force __be32)id ^ cm.random_id_operand;
 
@@ -1828,11 +1828,9 @@ static void cm_format_mra(struct cm_mra_msg *mra_msg,
 
 static void cm_format_rej(struct cm_rej_msg *rej_msg,
                          struct cm_id_private *cm_id_priv,
-                         enum ib_cm_rej_reason reason,
-                         void *ari,
-                         u8 ari_length,
-                         const void *private_data,
-                         u8 private_data_len)
+                         enum ib_cm_rej_reason reason, void *ari,
+                         u8 ari_length, const void *private_data,
+                         u8 private_data_len, enum ib_cm_state state)
 {
        lockdep_assert_held(&cm_id_priv->lock);
 
@@ -1840,7 +1838,7 @@ static void cm_format_rej(struct cm_rej_msg *rej_msg,
        IBA_SET(CM_REJ_REMOTE_COMM_ID, rej_msg,
                be32_to_cpu(cm_id_priv->id.remote_id));
 
-       switch(cm_id_priv->id.state) {
+       switch (state) {
        case IB_CM_REQ_RCVD:
                IBA_SET(CM_REJ_LOCAL_COMM_ID, rej_msg, be32_to_cpu(0));
                IBA_SET(CM_REJ_MESSAGE_REJECTED, rej_msg, CM_MSG_RESPONSE_REQ);
@@ -1905,8 +1903,9 @@ static void cm_dup_req_handler(struct cm_work *work,
                              cm_id_priv->private_data_len);
                break;
        case IB_CM_TIMEWAIT:
-               cm_format_rej((struct cm_rej_msg *) msg->mad, cm_id_priv,
-                             IB_CM_REJ_STALE_CONN, NULL, 0, NULL, 0);
+               cm_format_rej((struct cm_rej_msg *)msg->mad, cm_id_priv,
+                             IB_CM_REJ_STALE_CONN, NULL, 0, NULL, 0,
+                             IB_CM_TIMEWAIT);
                break;
        default:
                goto unlock;
@@ -2904,6 +2903,7 @@ static int cm_send_rej_locked(struct cm_id_private *cm_id_priv,
                              u8 ari_length, const void *private_data,
                              u8 private_data_len)
 {
+       enum ib_cm_state state = cm_id_priv->id.state;
        struct ib_mad_send_buf *msg;
        int ret;
 
@@ -2913,7 +2913,7 @@ static int cm_send_rej_locked(struct cm_id_private *cm_id_priv,
            (ari && ari_length > IB_CM_REJ_ARI_LENGTH))
                return -EINVAL;
 
-       switch (cm_id_priv->id.state) {
+       switch (state) {
        case IB_CM_REQ_SENT:
        case IB_CM_MRA_REQ_RCVD:
        case IB_CM_REQ_RCVD:
@@ -2925,7 +2925,8 @@ static int cm_send_rej_locked(struct cm_id_private *cm_id_priv,
                if (ret)
                        return ret;
                cm_format_rej((struct cm_rej_msg *)msg->mad, cm_id_priv, reason,
-                             ari, ari_length, private_data, private_data_len);
+                             ari, ari_length, private_data, private_data_len,
+                             state);
                break;
        case IB_CM_REP_SENT:
        case IB_CM_MRA_REP_RCVD:
@@ -2934,7 +2935,8 @@ static int cm_send_rej_locked(struct cm_id_private *cm_id_priv,
                if (ret)
                        return ret;
                cm_format_rej((struct cm_rej_msg *)msg->mad, cm_id_priv, reason,
-                             ari, ari_length, private_data, private_data_len);
+                             ari, ari_length, private_data, private_data_len,
+                             state);
                break;
        default:
                pr_debug("%s: local_id %d, cm_id->state: %d\n", __func__,
index 5128cb1..177333d 100644 (file)
@@ -360,7 +360,7 @@ lookup_get_fd_uobject(const struct uverbs_api_object *obj,
         * uverbs_uobject_fd_release(), and the caller is expected to ensure
         * that release is never done while a call to lookup is possible.
         */
-       if (f->f_op != fd_type->fops) {
+       if (f->f_op != fd_type->fops || uobject->ufile != ufile) {
                fput(f);
                return ERR_PTR(-EBADF);
        }
@@ -474,16 +474,15 @@ alloc_begin_fd_uobject(const struct uverbs_api_object *obj,
        filp = anon_inode_getfile(fd_type->name, fd_type->fops, NULL,
                                  fd_type->flags);
        if (IS_ERR(filp)) {
+               uverbs_uobject_put(uobj);
                uobj = ERR_CAST(filp);
-               goto err_uobj;
+               goto err_fd;
        }
        uobj->object = filp;
 
        uobj->id = new_fd;
        return uobj;
 
-err_uobj:
-       uverbs_uobject_put(uobj);
 err_fd:
        put_unused_fd(new_fd);
        return uobj;
@@ -679,7 +678,6 @@ void rdma_lookup_put_uobject(struct ib_uobject *uobj,
                             enum rdma_lookup_mode mode)
 {
        assert_uverbs_usecnt(uobj, mode);
-       uobj->uapi_object->type_class->lookup_put(uobj, mode);
        /*
         * In order to unlock an object, either decrease its usecnt for
         * read access or zero it in case of exclusive access. See
@@ -696,6 +694,7 @@ void rdma_lookup_put_uobject(struct ib_uobject *uobj,
                break;
        }
 
+       uobj->uapi_object->type_class->lookup_put(uobj, mode);
        /* Pairs with the kref obtained by type->lookup_get */
        uverbs_uobject_put(uobj);
 }
index 2d4083b..17fc25d 100644 (file)
@@ -820,6 +820,10 @@ void uverbs_user_mmap_disassociate(struct ib_uverbs_file *ufile)
                        ret = mmget_not_zero(mm);
                        if (!ret) {
                                list_del_init(&priv->list);
+                               if (priv->entry) {
+                                       rdma_user_mmap_entry_put(priv->entry);
+                                       priv->entry = NULL;
+                               }
                                mm = NULL;
                                continue;
                        }
index e8b4b37..688f196 100644 (file)
@@ -1046,7 +1046,7 @@ i40iw_sc_query_rdma_features(struct i40iw_sc_cqp *cqp,
        u64 header;
 
        wqe = i40iw_sc_cqp_get_next_send_wqe(cqp, scratch);
-       if (wqe)
+       if (!wqe)
                return I40IW_ERR_RING_FULL;
 
        set_64bit_val(wqe, 32, feat_mem->pa);
index a66518a..275722c 100644 (file)
@@ -1499,8 +1499,9 @@ static int __mlx4_ib_create_default_rules(
        int i;
 
        for (i = 0; i < ARRAY_SIZE(pdefault_rules->rules_create_list); i++) {
+               union ib_flow_spec ib_spec = {};
                int ret;
-               union ib_flow_spec ib_spec;
+
                switch (pdefault_rules->rules_create_list[i]) {
                case 0:
                        /* no rule */
index 2a33480..228be05 100644 (file)
@@ -1,11 +1,25 @@
 # SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_MLX5_INFINIBAND)  += mlx5_ib.o
+obj-$(CONFIG_MLX5_INFINIBAND) += mlx5_ib.o
+
+mlx5_ib-y := ah.o \
+            cmd.o \
+            cong.o \
+            cq.o \
+            doorbell.o \
+            gsi.o \
+            ib_virt.o \
+            mad.o \
+            main.o \
+            mem.o \
+            mr.o \
+            qp.o \
+            qpc.o \
+            restrack.o \
+            srq.o \
+            srq_cmd.o
 
-mlx5_ib-y :=   main.o cq.o doorbell.o qp.o mem.o srq_cmd.o \
-               srq.o mr.o ah.o mad.o gsi.o ib_virt.o cmd.o \
-               cong.o restrack.o
 mlx5_ib-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += odp.o
 mlx5_ib-$(CONFIG_MLX5_ESWITCH) += ib_rep.o
-mlx5_ib-$(CONFIG_INFINIBAND_USER_ACCESS) += devx.o
-mlx5_ib-$(CONFIG_INFINIBAND_USER_ACCESS) += flow.o
-mlx5_ib-$(CONFIG_INFINIBAND_USER_ACCESS) += qos.o
+mlx5_ib-$(CONFIG_INFINIBAND_USER_ACCESS) += devx.o \
+                                           flow.o \
+                                           qos.o
index 4c26492..a2fcbc4 100644 (file)
@@ -327,23 +327,6 @@ int mlx5_cmd_xrcd_dealloc(struct mlx5_core_dev *dev, u32 xrcdn, u16 uid)
        return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
 }
 
-int mlx5_cmd_alloc_q_counter(struct mlx5_core_dev *dev, u16 *counter_id,
-                            u16 uid)
-{
-       u32 in[MLX5_ST_SZ_DW(alloc_q_counter_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(alloc_q_counter_out)] = {0};
-       int err;
-
-       MLX5_SET(alloc_q_counter_in, in, opcode, MLX5_CMD_OP_ALLOC_Q_COUNTER);
-       MLX5_SET(alloc_q_counter_in, in, uid, uid);
-
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-       if (!err)
-               *counter_id = MLX5_GET(alloc_q_counter_out, out,
-                                      counter_set_id);
-       return err;
-}
-
 int mlx5_cmd_mad_ifc(struct mlx5_core_dev *dev, const void *inb, void *outb,
                     u16 opmod, u8 port)
 {
index 945ebce..43079b1 100644 (file)
@@ -61,8 +61,6 @@ int mlx5_cmd_detach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid,
                        u32 qpn, u16 uid);
 int mlx5_cmd_xrcd_alloc(struct mlx5_core_dev *dev, u32 *xrcdn, u16 uid);
 int mlx5_cmd_xrcd_dealloc(struct mlx5_core_dev *dev, u32 xrcdn, u16 uid);
-int mlx5_cmd_alloc_q_counter(struct mlx5_core_dev *dev, u16 *counter_id,
-                            u16 uid);
 int mlx5_cmd_mad_ifc(struct mlx5_core_dev *dev, const void *inb, void *outb,
                     u16 opmod, u8 port);
 #endif /* MLX5_IB_CMD_H */
index 146ba29..0c18cb6 100644 (file)
@@ -36,6 +36,7 @@
 #include <rdma/ib_cache.h>
 #include "mlx5_ib.h"
 #include "srq.h"
+#include "qp.h"
 
 static void mlx5_ib_cq_comp(struct mlx5_core_cq *cq, struct mlx5_eqe *eqe)
 {
@@ -201,7 +202,7 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe,
        case MLX5_CQE_RESP_WR_IMM:
                wc->opcode      = IB_WC_RECV_RDMA_WITH_IMM;
                wc->wc_flags    = IB_WC_WITH_IMM;
-               wc->ex.imm_data = cqe->imm_inval_pkey;
+               wc->ex.imm_data = cqe->immediate;
                break;
        case MLX5_CQE_RESP_SEND:
                wc->opcode   = IB_WC_RECV;
@@ -213,12 +214,12 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe,
        case MLX5_CQE_RESP_SEND_IMM:
                wc->opcode      = IB_WC_RECV;
                wc->wc_flags    = IB_WC_WITH_IMM;
-               wc->ex.imm_data = cqe->imm_inval_pkey;
+               wc->ex.imm_data = cqe->immediate;
                break;
        case MLX5_CQE_RESP_SEND_INV:
                wc->opcode      = IB_WC_RECV;
                wc->wc_flags    = IB_WC_WITH_INVALIDATE;
-               wc->ex.invalidate_rkey = be32_to_cpu(cqe->imm_inval_pkey);
+               wc->ex.invalidate_rkey = be32_to_cpu(cqe->inval_rkey);
                break;
        }
        wc->src_qp         = be32_to_cpu(cqe->flags_rqpn) & 0xffffff;
@@ -226,7 +227,7 @@ static void handle_responder(struct ib_wc *wc, struct mlx5_cqe64 *cqe,
        g = (be32_to_cpu(cqe->flags_rqpn) >> 28) & 3;
        wc->wc_flags |= g ? IB_WC_GRH : 0;
        if (unlikely(is_qp1(qp->ibqp.qp_type))) {
-               u16 pkey = be32_to_cpu(cqe->imm_inval_pkey) & 0xffff;
+               u16 pkey = be32_to_cpu(cqe->pkey) & 0xffff;
 
                ib_find_cached_pkey(&dev->ib_dev, qp->port, pkey,
                                    &wc->pkey_index);
@@ -484,7 +485,7 @@ repoll:
                 * because CQs will be locked while QPs are removed
                 * from the table.
                 */
-               mqp = __mlx5_qp_lookup(dev->mdev, qpn);
+               mqp = radix_tree_lookup(&dev->qp_table.tree, qpn);
                *cur_qp = to_mibqp(mqp);
        }
 
index 46e1ab7..35b98c2 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/mlx5/driver.h>
 #include <linux/mlx5/fs.h>
 #include "mlx5_ib.h"
+#include "qp.h"
 #include <linux/xarray.h>
 
 #define UVERBS_MODULE_NAME mlx5_ib
@@ -1356,7 +1357,7 @@ static int devx_obj_cleanup(struct ib_uobject *uobject,
        }
 
        if (obj->flags & DEVX_OBJ_FLAGS_DCT)
-               ret = mlx5_core_destroy_dct(obj->ib_dev->mdev, &obj->core_dct);
+               ret = mlx5_core_destroy_dct(obj->ib_dev, &obj->core_dct);
        else if (obj->flags & DEVX_OBJ_FLAGS_CQ)
                ret = mlx5_core_destroy_cq(obj->ib_dev->mdev, &obj->core_cq);
        else
@@ -1450,9 +1451,8 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)(
 
        if (opcode == MLX5_CMD_OP_CREATE_DCT) {
                obj->flags |= DEVX_OBJ_FLAGS_DCT;
-               err = mlx5_core_create_dct(dev->mdev, &obj->core_dct,
-                                          cmd_in, cmd_in_len,
-                                          cmd_out, cmd_out_len);
+               err = mlx5_core_create_dct(dev, &obj->core_dct, cmd_in,
+                                          cmd_in_len, cmd_out, cmd_out_len);
        } else if (opcode == MLX5_CMD_OP_CREATE_CQ) {
                obj->flags |= DEVX_OBJ_FLAGS_CQ;
                obj->core_cq.comp = devx_cq_comp;
@@ -1499,7 +1499,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_DEVX_OBJ_CREATE)(
 
 obj_destroy:
        if (obj->flags & DEVX_OBJ_FLAGS_DCT)
-               mlx5_core_destroy_dct(obj->ib_dev->mdev, &obj->core_dct);
+               mlx5_core_destroy_dct(obj->ib_dev, &obj->core_dct);
        else if (obj->flags & DEVX_OBJ_FLAGS_CQ)
                mlx5_core_destroy_cq(obj->ib_dev->mdev, &obj->core_cq);
        else
index 862b7bf..69cb7e6 100644 (file)
@@ -427,7 +427,7 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_FLOW_ACTION_CREATE_MODIFY_HEADER)(
 
        num_actions = uverbs_attr_ptr_get_array_size(
                attrs, MLX5_IB_ATTR_CREATE_MODIFY_HEADER_ACTIONS_PRM,
-               MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto));
+               MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto));
        if (num_actions < 0)
                return num_actions;
 
@@ -648,7 +648,7 @@ DECLARE_UVERBS_NAMED_METHOD(
                        UA_MANDATORY),
        UVERBS_ATTR_PTR_IN(MLX5_IB_ATTR_CREATE_MODIFY_HEADER_ACTIONS_PRM,
                           UVERBS_ATTR_MIN_SIZE(MLX5_UN_SZ_BYTES(
-                                  set_action_in_add_action_in_auto)),
+                                  set_add_copy_action_in_auto)),
                           UA_MANDATORY,
                           UA_ALLOC_AND_COPY),
        UVERBS_ATTR_CONST_IN(MLX5_IB_ATTR_CREATE_MODIFY_HEADER_FT_TYPE,
index b611653..46b2d37 100644 (file)
@@ -134,7 +134,7 @@ int mlx5_ib_get_vf_stats(struct ib_device *device, int vf,
        if (!out)
                return -ENOMEM;
 
-       err = mlx5_core_query_vport_counter(mdev, true, vf, port, out, out_sz);
+       err = mlx5_core_query_vport_counter(mdev, true, vf, port, out);
        if (err)
                goto ex;
 
index 14e0c17..454ce5d 100644 (file)
@@ -30,7 +30,6 @@
  * SOFTWARE.
  */
 
-#include <linux/mlx5/cmd.h>
 #include <linux/mlx5/vport.h>
 #include <rdma/ib_mad.h>
 #include <rdma/ib_smi.h>
@@ -188,8 +187,8 @@ static int process_pma_cmd(struct mlx5_ib_dev *dev, u8 port_num,
                        goto done;
                }
 
-               err = mlx5_core_query_vport_counter(mdev, 0, 0,
-                                                   mdev_port_num, out_cnt, sz);
+               err = mlx5_core_query_vport_counter(mdev, 0, 0, mdev_port_num,
+                                                   out_cnt);
                if (!err)
                        pma_cnt_ext_assign(pma_cnt_ext, out_cnt);
        } else {
index 6679756..65e0e24 100644 (file)
@@ -59,6 +59,7 @@
 #include "ib_rep.h"
 #include "cmd.h"
 #include "srq.h"
+#include "qp.h"
 #include <linux/mlx5/fs_helpers.h>
 #include <linux/mlx5/accel.h>
 #include <rdma/uverbs_std_types.h>
@@ -2443,7 +2444,7 @@ static int handle_alloc_dm_sw_icm(struct ib_ucontext *ctx,
        act_size = roundup_pow_of_two(act_size);
 
        dm->size = act_size;
-       err = mlx5_dm_sw_icm_alloc(dev, type, act_size,
+       err = mlx5_dm_sw_icm_alloc(dev, type, act_size, attr->alignment,
                                   to_mucontext(ctx)->devx_uid, &dm->dev_addr,
                                   &dm->icm_dm.obj_id);
        if (err)
@@ -4632,8 +4633,7 @@ static void delay_drop_handler(struct work_struct *work)
        atomic_inc(&delay_drop->events_cnt);
 
        mutex_lock(&delay_drop->lock);
-       err = mlx5_core_set_delay_drop(delay_drop->dev->mdev,
-                                      delay_drop->timeout);
+       err = mlx5_core_set_delay_drop(delay_drop->dev, delay_drop->timeout);
        if (err) {
                mlx5_ib_warn(delay_drop->dev, "Failed to set delay drop, timeout=%u\n",
                             delay_drop->timeout);
@@ -5439,15 +5439,21 @@ static bool is_mdev_switchdev_mode(const struct mlx5_core_dev *mdev)
 
 static void mlx5_ib_dealloc_counters(struct mlx5_ib_dev *dev)
 {
+       u32 in[MLX5_ST_SZ_DW(dealloc_q_counter_in)] = {};
        int num_cnt_ports;
        int i;
 
        num_cnt_ports = is_mdev_switchdev_mode(dev->mdev) ? 1 : dev->num_ports;
 
+       MLX5_SET(dealloc_q_counter_in, in, opcode,
+                MLX5_CMD_OP_DEALLOC_Q_COUNTER);
+
        for (i = 0; i < num_cnt_ports; i++) {
-               if (dev->port[i].cnts.set_id_valid)
-                       mlx5_core_dealloc_q_counter(dev->mdev,
-                                                   dev->port[i].cnts.set_id);
+               if (dev->port[i].cnts.set_id) {
+                       MLX5_SET(dealloc_q_counter_in, in, counter_set_id,
+                                dev->port[i].cnts.set_id);
+                       mlx5_cmd_exec_in(dev->mdev, dealloc_q_counter, in);
+               }
                kfree(dev->port[i].cnts.names);
                kfree(dev->port[i].cnts.offsets);
        }
@@ -5556,11 +5562,14 @@ static void mlx5_ib_fill_counters(struct mlx5_ib_dev *dev,
 
 static int mlx5_ib_alloc_counters(struct mlx5_ib_dev *dev)
 {
+       u32 out[MLX5_ST_SZ_DW(alloc_q_counter_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(alloc_q_counter_in)] = {};
        int num_cnt_ports;
        int err = 0;
        int i;
        bool is_shared;
 
+       MLX5_SET(alloc_q_counter_in, in, opcode, MLX5_CMD_OP_ALLOC_Q_COUNTER);
        is_shared = MLX5_CAP_GEN(dev->mdev, log_max_uctx) != 0;
        num_cnt_ports = is_mdev_switchdev_mode(dev->mdev) ? 1 : dev->num_ports;
 
@@ -5572,17 +5581,19 @@ static int mlx5_ib_alloc_counters(struct mlx5_ib_dev *dev)
                mlx5_ib_fill_counters(dev, dev->port[i].cnts.names,
                                      dev->port[i].cnts.offsets);
 
-               err = mlx5_cmd_alloc_q_counter(dev->mdev,
-                                              &dev->port[i].cnts.set_id,
-                                              is_shared ?
-                                              MLX5_SHARED_RESOURCE_UID : 0);
+               MLX5_SET(alloc_q_counter_in, in, uid,
+                        is_shared ? MLX5_SHARED_RESOURCE_UID : 0);
+
+               err = mlx5_cmd_exec_inout(dev->mdev, alloc_q_counter, in, out);
                if (err) {
                        mlx5_ib_warn(dev,
                                     "couldn't allocate queue counter for port %d, err %d\n",
                                     i + 1, err);
                        goto err_alloc;
                }
-               dev->port[i].cnts.set_id_valid = true;
+
+               dev->port[i].cnts.set_id =
+                       MLX5_GET(alloc_q_counter_out, out, counter_set_id);
        }
        return 0;
 
@@ -5638,27 +5649,23 @@ static int mlx5_ib_query_q_counters(struct mlx5_core_dev *mdev,
                                    struct rdma_hw_stats *stats,
                                    u16 set_id)
 {
-       int outlen = MLX5_ST_SZ_BYTES(query_q_counter_out);
-       void *out;
+       u32 out[MLX5_ST_SZ_DW(query_q_counter_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(query_q_counter_in)] = {};
        __be32 val;
        int ret, i;
 
-       out = kvzalloc(outlen, GFP_KERNEL);
-       if (!out)
-               return -ENOMEM;
-
-       ret = mlx5_core_query_q_counter(mdev, set_id, 0, out, outlen);
+       MLX5_SET(query_q_counter_in, in, opcode, MLX5_CMD_OP_QUERY_Q_COUNTER);
+       MLX5_SET(query_q_counter_in, in, counter_set_id, set_id);
+       ret = mlx5_cmd_exec_inout(mdev, query_q_counter, in, out);
        if (ret)
-               goto free;
+               return ret;
 
        for (i = 0; i < cnts->num_q_counters; i++) {
-               val = *(__be32 *)(out + cnts->offsets[i]);
+               val = *(__be32 *)((void *)out + cnts->offsets[i]);
                stats->value[i] = (u64)be32_to_cpu(val);
        }
 
-free:
-       kvfree(out);
-       return ret;
+       return 0;
 }
 
 static int mlx5_ib_query_ext_ppcnt_counters(struct mlx5_ib_dev *dev,
@@ -5765,20 +5772,38 @@ static int mlx5_ib_counter_update_stats(struct rdma_counter *counter)
                                        counter->stats, counter->id);
 }
 
+static int mlx5_ib_counter_dealloc(struct rdma_counter *counter)
+{
+       struct mlx5_ib_dev *dev = to_mdev(counter->device);
+       u32 in[MLX5_ST_SZ_DW(dealloc_q_counter_in)] = {};
+
+       if (!counter->id)
+               return 0;
+
+       MLX5_SET(dealloc_q_counter_in, in, opcode,
+                MLX5_CMD_OP_DEALLOC_Q_COUNTER);
+       MLX5_SET(dealloc_q_counter_in, in, counter_set_id, counter->id);
+       return mlx5_cmd_exec_in(dev->mdev, dealloc_q_counter, in);
+}
+
 static int mlx5_ib_counter_bind_qp(struct rdma_counter *counter,
                                   struct ib_qp *qp)
 {
        struct mlx5_ib_dev *dev = to_mdev(qp->device);
-       u16 cnt_set_id = 0;
        int err;
 
        if (!counter->id) {
-               err = mlx5_cmd_alloc_q_counter(dev->mdev,
-                                              &cnt_set_id,
-                                              MLX5_SHARED_RESOURCE_UID);
+               u32 out[MLX5_ST_SZ_DW(alloc_q_counter_out)] = {};
+               u32 in[MLX5_ST_SZ_DW(alloc_q_counter_in)] = {};
+
+               MLX5_SET(alloc_q_counter_in, in, opcode,
+                        MLX5_CMD_OP_ALLOC_Q_COUNTER);
+               MLX5_SET(alloc_q_counter_in, in, uid, MLX5_SHARED_RESOURCE_UID);
+               err = mlx5_cmd_exec_inout(dev->mdev, alloc_q_counter, in, out);
                if (err)
                        return err;
-               counter->id = cnt_set_id;
+               counter->id =
+                       MLX5_GET(alloc_q_counter_out, out, counter_set_id);
        }
 
        err = mlx5_ib_qp_set_counter(qp, counter);
@@ -5788,7 +5813,7 @@ static int mlx5_ib_counter_bind_qp(struct rdma_counter *counter,
        return 0;
 
 fail_set_counter:
-       mlx5_core_dealloc_q_counter(dev->mdev, cnt_set_id);
+       mlx5_ib_counter_dealloc(counter);
        counter->id = 0;
 
        return err;
@@ -5799,13 +5824,6 @@ static int mlx5_ib_counter_unbind_qp(struct ib_qp *qp)
        return mlx5_ib_qp_set_counter(qp, NULL);
 }
 
-static int mlx5_ib_counter_dealloc(struct rdma_counter *counter)
-{
-       struct mlx5_ib_dev *dev = to_mdev(counter->device);
-
-       return mlx5_core_dealloc_q_counter(dev->mdev, counter->id);
-}
-
 static int mlx5_ib_rn_get_params(struct ib_device *device, u8 port_num,
                                 enum rdma_netdev_t type,
                                 struct rdma_netdev_alloc_params *params)
@@ -7175,6 +7193,9 @@ static const struct mlx5_ib_profile pf_profile = {
        STAGE_CREATE(MLX5_IB_STAGE_ROCE,
                     mlx5_ib_stage_roce_init,
                     mlx5_ib_stage_roce_cleanup),
+       STAGE_CREATE(MLX5_IB_STAGE_QP,
+                    mlx5_init_qp_table,
+                    mlx5_cleanup_qp_table),
        STAGE_CREATE(MLX5_IB_STAGE_SRQ,
                     mlx5_init_srq_table,
                     mlx5_cleanup_srq_table),
@@ -7232,6 +7253,9 @@ const struct mlx5_ib_profile raw_eth_profile = {
        STAGE_CREATE(MLX5_IB_STAGE_ROCE,
                     mlx5_ib_stage_raw_eth_roce_init,
                     mlx5_ib_stage_raw_eth_roce_cleanup),
+       STAGE_CREATE(MLX5_IB_STAGE_QP,
+                    mlx5_init_qp_table,
+                    mlx5_cleanup_qp_table),
        STAGE_CREATE(MLX5_IB_STAGE_SRQ,
                     mlx5_init_srq_table,
                     mlx5_cleanup_srq_table),
index a4e5223..aaabb8a 100644 (file)
@@ -780,7 +780,6 @@ struct mlx5_ib_counters {
        u32 num_cong_counters;
        u32 num_ext_ppcnt_counters;
        u16 set_id;
-       bool set_id_valid;
 };
 
 struct mlx5_ib_multiport_info;
@@ -870,6 +869,7 @@ enum mlx5_ib_stages {
        MLX5_IB_STAGE_CAPS,
        MLX5_IB_STAGE_NON_DEFAULT_CB,
        MLX5_IB_STAGE_ROCE,
+       MLX5_IB_STAGE_QP,
        MLX5_IB_STAGE_SRQ,
        MLX5_IB_STAGE_DEVICE_RESOURCES,
        MLX5_IB_STAGE_DEVICE_NOTIFIER,
@@ -1065,6 +1065,7 @@ struct mlx5_ib_dev {
        struct mlx5_dm          dm;
        u16                     devx_whitelist_uid;
        struct mlx5_srq_table   srq_table;
+       struct mlx5_qp_table    qp_table;
        struct mlx5_async_ctx   async_ctx;
        struct mlx5_devx_event_table devx_event_table;
        struct mlx5_var_table var_table;
index 3de7606..16af110 100644 (file)
@@ -36,6 +36,7 @@
 
 #include "mlx5_ib.h"
 #include "cmd.h"
+#include "qp.h"
 
 #include <linux/mlx5/eq.h>
 
@@ -1219,7 +1220,7 @@ static inline struct mlx5_core_rsc_common *odp_get_rsc(struct mlx5_ib_dev *dev,
        case MLX5_WQE_PF_TYPE_REQ_SEND_OR_WRITE:
        case MLX5_WQE_PF_TYPE_RESP:
        case MLX5_WQE_PF_TYPE_REQ_READ_OR_ATOMIC:
-               common = mlx5_core_res_hold(dev->mdev, wq_num, MLX5_RES_QP);
+               common = mlx5_core_res_hold(dev, wq_num, MLX5_RES_QP);
                break;
        default:
                break;
index 1456db4..d93eec5 100644 (file)
@@ -39,6 +39,7 @@
 #include "mlx5_ib.h"
 #include "ib_rep.h"
 #include "cmd.h"
+#include "qp.h"
 
 /* not supported currently */
 static int wq_signature;
@@ -1254,7 +1255,7 @@ static int create_raw_packet_qp_tis(struct mlx5_ib_dev *dev,
                                    struct mlx5_ib_sq *sq, u32 tdn,
                                    struct ib_pd *pd)
 {
-       u32 in[MLX5_ST_SZ_DW(create_tis_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(create_tis_in)] = {};
        void *tisc = MLX5_ADDR_OF(create_tis_in, in, ctx);
 
        MLX5_SET(create_tis_in, in, uid, to_mpd(pd)->uid);
@@ -1262,7 +1263,7 @@ static int create_raw_packet_qp_tis(struct mlx5_ib_dev *dev,
        if (qp->flags & MLX5_IB_QP_UNDERLAY)
                MLX5_SET(tisc, tisc, underlay_qpn, qp->underlay_qpn);
 
-       return mlx5_core_create_tis(dev->mdev, in, sizeof(in), &sq->tisn);
+       return mlx5_core_create_tis(dev->mdev, in, &sq->tisn);
 }
 
 static void destroy_raw_packet_qp_tis(struct mlx5_ib_dev *dev,
@@ -1336,7 +1337,7 @@ static int create_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
        pas = (__be64 *)MLX5_ADDR_OF(wq, wq, pas);
        mlx5_ib_populate_pas(dev, sq->ubuffer.umem, page_shift, pas, 0);
 
-       err = mlx5_core_create_sq_tracked(dev->mdev, in, inlen, &sq->base.mqp);
+       err = mlx5_core_create_sq_tracked(dev, in, inlen, &sq->base.mqp);
 
        kvfree(in);
 
@@ -1356,7 +1357,7 @@ static void destroy_raw_packet_qp_sq(struct mlx5_ib_dev *dev,
                                     struct mlx5_ib_sq *sq)
 {
        destroy_flow_rule_vport_sq(sq);
-       mlx5_core_destroy_sq_tracked(dev->mdev, &sq->base.mqp);
+       mlx5_core_destroy_sq_tracked(dev, &sq->base.mqp);
        ib_umem_release(sq->ubuffer.umem);
 }
 
@@ -1426,7 +1427,7 @@ static int create_raw_packet_qp_rq(struct mlx5_ib_dev *dev,
        qp_pas = (__be64 *)MLX5_ADDR_OF(create_qp_in, qpin, pas);
        memcpy(pas, qp_pas, rq_pas_size);
 
-       err = mlx5_core_create_rq_tracked(dev->mdev, in, inlen, &rq->base.mqp);
+       err = mlx5_core_create_rq_tracked(dev, in, inlen, &rq->base.mqp);
 
        kvfree(in);
 
@@ -1436,7 +1437,7 @@ static int create_raw_packet_qp_rq(struct mlx5_ib_dev *dev,
 static void destroy_raw_packet_qp_rq(struct mlx5_ib_dev *dev,
                                     struct mlx5_ib_rq *rq)
 {
-       mlx5_core_destroy_rq_tracked(dev->mdev, &rq->base.mqp);
+       mlx5_core_destroy_rq_tracked(dev, &rq->base.mqp);
 }
 
 static bool tunnel_offload_supported(struct mlx5_core_dev *dev)
@@ -1459,9 +1460,8 @@ static void destroy_raw_packet_qp_tir(struct mlx5_ib_dev *dev,
 
 static int create_raw_packet_qp_tir(struct mlx5_ib_dev *dev,
                                    struct mlx5_ib_rq *rq, u32 tdn,
-                                   u32 *qp_flags_en,
-                                   struct ib_pd *pd,
-                                   u32 *out, int outlen)
+                                   u32 *qp_flags_en, struct ib_pd *pd,
+                                   u32 *out)
 {
        u8 lb_flag = 0;
        u32 *in;
@@ -1494,9 +1494,8 @@ static int create_raw_packet_qp_tir(struct mlx5_ib_dev *dev,
        }
 
        MLX5_SET(tirc, tirc, self_lb_block, lb_flag);
-
-       err = mlx5_core_create_tir_out(dev->mdev, in, inlen, out, outlen);
-
+       MLX5_SET(create_tir_in, in, opcode, MLX5_CMD_OP_CREATE_TIR);
+       err = mlx5_cmd_exec_inout(dev->mdev, create_tir, in, out);
        rq->tirn = MLX5_GET(create_tir_out, out, tirn);
        if (!err && MLX5_GET(tirc, tirc, self_lb_block)) {
                err = mlx5_ib_enable_lb(dev, false, true);
@@ -1556,9 +1555,8 @@ static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
                if (err)
                        goto err_destroy_sq;
 
-               err = create_raw_packet_qp_tir(
-                       dev, rq, tdn, &qp->flags_en, pd, out,
-                       MLX5_ST_SZ_BYTES(create_tir_out));
+               err = create_raw_packet_qp_tir(dev, rq, tdn, &qp->flags_en, pd,
+                                              out);
                if (err)
                        goto err_destroy_rq;
 
@@ -1853,7 +1851,8 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
        MLX5_SET(rx_hash_field_select, hfso, selected_fields, selected_fields);
 
 create_tir:
-       err = mlx5_core_create_tir_out(dev->mdev, in, inlen, out, outlen);
+       MLX5_SET(create_tir_in, in, opcode, MLX5_CMD_OP_CREATE_TIR);
+       err = mlx5_cmd_exec_inout(dev->mdev, create_tir, in, out);
 
        qp->rss_qp.tirn = MLX5_GET(create_tir_out, out, tirn);
        if (!err && MLX5_GET(tirc, tirc, self_lb_block)) {
@@ -2347,7 +2346,7 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd,
                err = create_raw_packet_qp(dev, qp, in, inlen, pd, udata,
                                           &resp);
        } else {
-               err = mlx5_core_create_qp(dev->mdev, &base->mqp, in, inlen);
+               err = mlx5_core_create_qp(dev, &base->mqp, in, inlen);
        }
 
        if (err) {
@@ -2513,8 +2512,7 @@ static void destroy_qp_common(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
        if (qp->state != IB_QPS_RESET) {
                if (qp->ibqp.qp_type != IB_QPT_RAW_PACKET &&
                    !(qp->flags & MLX5_IB_QP_UNDERLAY)) {
-                       err = mlx5_core_qp_modify(dev->mdev,
-                                                 MLX5_CMD_OP_2RST_QP, 0,
+                       err = mlx5_core_qp_modify(dev, MLX5_CMD_OP_2RST_QP, 0,
                                                  NULL, &base->mqp);
                } else {
                        struct mlx5_modify_raw_qp_param raw_qp_param = {
@@ -2555,7 +2553,7 @@ static void destroy_qp_common(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
            qp->flags & MLX5_IB_QP_UNDERLAY) {
                destroy_raw_packet_qp(dev, qp);
        } else {
-               err = mlx5_core_destroy_qp(dev->mdev, &base->mqp);
+               err = mlx5_core_destroy_qp(dev, &base->mqp);
                if (err)
                        mlx5_ib_warn(dev, "failed to destroy QP 0x%x\n",
                                     base->mqp.qpn);
@@ -2818,7 +2816,7 @@ static int mlx5_ib_destroy_dct(struct mlx5_ib_qp *mqp)
        if (mqp->state == IB_QPS_RTR) {
                int err;
 
-               err = mlx5_core_destroy_dct(dev->mdev, &mqp->dct.mdct);
+               err = mlx5_core_destroy_dct(dev, &mqp->dct.mdct);
                if (err) {
                        mlx5_ib_warn(dev, "failed to destroy DCT %d\n", err);
                        return err;
@@ -2933,7 +2931,7 @@ static int modify_raw_packet_eth_prio(struct mlx5_core_dev *dev,
        tisc = MLX5_ADDR_OF(modify_tis_in, in, ctx);
        MLX5_SET(tisc, tisc, prio, ((sl & 0x7) << 1));
 
-       err = mlx5_core_modify_tis(dev, sq->tisn, in, inlen);
+       err = mlx5_core_modify_tis(dev, sq->tisn, in);
 
        kvfree(in);
 
@@ -2960,7 +2958,7 @@ static int modify_raw_packet_tx_affinity(struct mlx5_core_dev *dev,
        tisc = MLX5_ADDR_OF(modify_tis_in, in, ctx);
        MLX5_SET(tisc, tisc, lag_tx_port_affinity, tx_affinity);
 
-       err = mlx5_core_modify_tis(dev, sq->tisn, in, inlen);
+       err = mlx5_core_modify_tis(dev, sq->tisn, in);
 
        kvfree(in);
 
@@ -3240,7 +3238,7 @@ static int modify_raw_packet_qp_rq(
                                "RAW PACKET QP counters are not supported on current FW\n");
        }
 
-       err = mlx5_core_modify_rq(dev->mdev, rq->base.mqp.qpn, in, inlen);
+       err = mlx5_core_modify_rq(dev->mdev, rq->base.mqp.qpn, in);
        if (err)
                goto out;
 
@@ -3303,7 +3301,7 @@ static int modify_raw_packet_qp_sq(
                MLX5_SET(sqc, sqc, packet_pacing_rate_limit_index, rl_index);
        }
 
-       err = mlx5_core_modify_sq(dev, sq->base.mqp.qpn, in, inlen);
+       err = mlx5_core_modify_sq(dev, sq->base.mqp.qpn, in);
        if (err) {
                /* Remove new rate from table if failed */
                if (new_rate_added)
@@ -3462,10 +3460,9 @@ static int __mlx5_ib_qp_set_counter(struct ib_qp *qp,
        base = &mqp->trans_qp.base;
        context.qp_counter_set_usr_page &= cpu_to_be32(0xffffff);
        context.qp_counter_set_usr_page |= cpu_to_be32(set_id << 24);
-       return mlx5_core_qp_modify(dev->mdev,
-                                  MLX5_CMD_OP_RTS2RTS_QP,
-                                  MLX5_QP_OPTPAR_COUNTER_SET_ID,
-                                  &context, &base->mqp);
+       return mlx5_core_qp_modify(dev, MLX5_CMD_OP_RTS2RTS_QP,
+                                  MLX5_QP_OPTPAR_COUNTER_SET_ID, &context,
+                                  &base->mqp);
 }
 
 static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
@@ -3752,8 +3749,7 @@ static int __mlx5_ib_modify_qp(struct ib_qp *ibqp,
 
                err = modify_raw_packet_qp(dev, qp, &raw_qp_param, tx_affinity);
        } else {
-               err = mlx5_core_qp_modify(dev->mdev, op, optpar, context,
-                                         &base->mqp);
+               err = mlx5_core_qp_modify(dev, op, optpar, context, &base->mqp);
        }
 
        if (err)
@@ -3927,7 +3923,7 @@ static int mlx5_ib_modify_dct(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                MLX5_SET(dctc, dctc, my_addr_index, attr->ah_attr.grh.sgid_index);
                MLX5_SET(dctc, dctc, hop_limit, attr->ah_attr.grh.hop_limit);
 
-               err = mlx5_core_create_dct(dev->mdev, &qp->dct.mdct, qp->dct.in,
+               err = mlx5_core_create_dct(dev, &qp->dct.mdct, qp->dct.in,
                                           MLX5_ST_SZ_BYTES(create_dct_in), out,
                                           sizeof(out));
                if (err)
@@ -3935,7 +3931,7 @@ static int mlx5_ib_modify_dct(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                resp.dctn = qp->dct.mdct.mqp.qpn;
                err = ib_copy_to_udata(udata, &resp, resp.response_length);
                if (err) {
-                       mlx5_core_destroy_dct(dev->mdev, &qp->dct.mdct);
+                       mlx5_core_destroy_dct(dev, &qp->dct.mdct);
                        return err;
                }
        } else {
@@ -5558,7 +5554,9 @@ static void to_rdma_ah_attr(struct mlx5_ib_dev *ibdev,
        rdma_ah_set_path_bits(ah_attr, path->grh_mlid & 0x7f);
        rdma_ah_set_static_rate(ah_attr,
                                path->static_rate ? path->static_rate - 5 : 0);
-       if (path->grh_mlid & (1 << 7)) {
+
+       if (path->grh_mlid & (1 << 7) ||
+           ah_attr->type == RDMA_AH_ATTR_TYPE_ROCE) {
                u32 tc_fl = be32_to_cpu(path->tclass_flowlabel);
 
                rdma_ah_set_grh(ah_attr, NULL,
@@ -5697,8 +5695,7 @@ static int query_qp_attr(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp,
        if (!outb)
                return -ENOMEM;
 
-       err = mlx5_core_qp_query(dev->mdev, &qp->trans_qp.base.mqp, outb,
-                                outlen);
+       err = mlx5_core_qp_query(dev, &qp->trans_qp.base.mqp, outb, outlen);
        if (err)
                goto out;
 
@@ -5776,7 +5773,7 @@ static int mlx5_ib_dct_query_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *mqp,
        if (!out)
                return -ENOMEM;
 
-       err = mlx5_core_dct_query(dev->mdev, dct, out, outlen);
+       err = mlx5_core_dct_query(dev, dct, out, outlen);
        if (err)
                goto out;
 
@@ -5962,7 +5959,7 @@ static int set_delay_drop(struct mlx5_ib_dev *dev)
        if (dev->delay_drop.activate)
                goto out;
 
-       err = mlx5_core_set_delay_drop(dev->mdev, dev->delay_drop.timeout);
+       err = mlx5_core_set_delay_drop(dev, dev->delay_drop.timeout);
        if (err)
                goto out;
 
@@ -6068,13 +6065,13 @@ static int  create_rq(struct mlx5_ib_rwq *rwq, struct ib_pd *pd,
        }
        rq_pas0 = (__be64 *)MLX5_ADDR_OF(wq, wq, pas);
        mlx5_ib_populate_pas(dev, rwq->umem, rwq->page_shift, rq_pas0, 0);
-       err = mlx5_core_create_rq_tracked(dev->mdev, in, inlen, &rwq->core_qp);
+       err = mlx5_core_create_rq_tracked(dev, in, inlen, &rwq->core_qp);
        if (!err && init_attr->create_flags & IB_WQ_FLAGS_DELAY_DROP) {
                err = set_delay_drop(dev);
                if (err) {
                        mlx5_ib_warn(dev, "Failed to enable delay drop err=%d\n",
                                     err);
-                       mlx5_core_destroy_rq_tracked(dev->mdev, &rwq->core_qp);
+                       mlx5_core_destroy_rq_tracked(dev, &rwq->core_qp);
                } else {
                        rwq->create_flags |= MLX5_IB_WQ_FLAGS_DELAY_DROP;
                }
@@ -6256,7 +6253,7 @@ struct ib_wq *mlx5_ib_create_wq(struct ib_pd *pd,
        return &rwq->ibwq;
 
 err_copy:
-       mlx5_core_destroy_rq_tracked(dev->mdev, &rwq->core_qp);
+       mlx5_core_destroy_rq_tracked(dev, &rwq->core_qp);
 err_user_rq:
        destroy_user_rq(dev, pd, rwq, udata);
 err:
@@ -6269,7 +6266,7 @@ void mlx5_ib_destroy_wq(struct ib_wq *wq, struct ib_udata *udata)
        struct mlx5_ib_dev *dev = to_mdev(wq->device);
        struct mlx5_ib_rwq *rwq = to_mrwq(wq);
 
-       mlx5_core_destroy_rq_tracked(dev->mdev, &rwq->core_qp);
+       mlx5_core_destroy_rq_tracked(dev, &rwq->core_qp);
        destroy_user_rq(dev, wq->pd, rwq, udata);
        kfree(rwq);
 }
@@ -6447,7 +6444,7 @@ int mlx5_ib_modify_wq(struct ib_wq *wq, struct ib_wq_attr *wq_attr,
                                "Receive WQ counters are not supported on current FW\n");
        }
 
-       err = mlx5_core_modify_rq(dev->mdev, rwq->core_qp.qpn, in, inlen);
+       err = mlx5_core_modify_rq(dev->mdev, rwq->core_qp.qpn, in);
        if (!err)
                rwq->ibwq.state = (wq_state == MLX5_RQC_STATE_ERR) ? IB_WQS_ERR : wq_state;
 
diff --git a/drivers/infiniband/hw/mlx5/qp.h b/drivers/infiniband/hw/mlx5/qp.h
new file mode 100644 (file)
index 0000000..ad9d76e
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/*
+ * Copyright (c) 2013-2020, Mellanox Technologies inc. All rights reserved.
+ */
+
+#ifndef _MLX5_IB_QP_H
+#define _MLX5_IB_QP_H
+
+#include "mlx5_ib.h"
+
+int mlx5_init_qp_table(struct mlx5_ib_dev *dev);
+void mlx5_cleanup_qp_table(struct mlx5_ib_dev *dev);
+
+int mlx5_core_create_dct(struct mlx5_ib_dev *dev, struct mlx5_core_dct *qp,
+                        u32 *in, int inlen, u32 *out, int outlen);
+int mlx5_core_create_qp(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp,
+                       u32 *in, int inlen);
+int mlx5_core_qp_modify(struct mlx5_ib_dev *dev, u16 opcode, u32 opt_param_mask,
+                       void *qpc, struct mlx5_core_qp *qp);
+int mlx5_core_destroy_qp(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp);
+int mlx5_core_destroy_dct(struct mlx5_ib_dev *dev, struct mlx5_core_dct *dct);
+int mlx5_core_qp_query(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp,
+                      u32 *out, int outlen);
+int mlx5_core_dct_query(struct mlx5_ib_dev *dev, struct mlx5_core_dct *dct,
+                       u32 *out, int outlen);
+
+int mlx5_core_set_delay_drop(struct mlx5_ib_dev *dev, u32 timeout_usec);
+
+void mlx5_core_destroy_rq_tracked(struct mlx5_ib_dev *dev,
+                                 struct mlx5_core_qp *rq);
+int mlx5_core_create_sq_tracked(struct mlx5_ib_dev *dev, u32 *in, int inlen,
+                               struct mlx5_core_qp *sq);
+void mlx5_core_destroy_sq_tracked(struct mlx5_ib_dev *dev,
+                                 struct mlx5_core_qp *sq);
+
+int mlx5_core_create_rq_tracked(struct mlx5_ib_dev *dev, u32 *in, int inlen,
+                               struct mlx5_core_qp *rq);
+
+struct mlx5_core_rsc_common *mlx5_core_res_hold(struct mlx5_ib_dev *dev,
+                                               int res_num,
+                                               enum mlx5_res_type res_type);
+void mlx5_core_res_put(struct mlx5_core_rsc_common *res);
+
+int mlx5_core_xrcd_alloc(struct mlx5_ib_dev *dev, u32 *xrcdn);
+int mlx5_core_xrcd_dealloc(struct mlx5_ib_dev *dev, u32 xrcdn);
+#endif /* _MLX5_IB_QP_H */
diff --git a/drivers/infiniband/hw/mlx5/qpc.c b/drivers/infiniband/hw/mlx5/qpc.c
new file mode 100644 (file)
index 0000000..ea62735
--- /dev/null
@@ -0,0 +1,605 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/*
+ * Copyright (c) 2013-2020, Mellanox Technologies inc. All rights reserved.
+ */
+
+#include <linux/gfp.h>
+#include <linux/mlx5/qp.h>
+#include <linux/mlx5/driver.h>
+#include "mlx5_ib.h"
+#include "qp.h"
+
+static int mlx5_core_drain_dct(struct mlx5_ib_dev *dev,
+                              struct mlx5_core_dct *dct);
+
+static struct mlx5_core_rsc_common *
+mlx5_get_rsc(struct mlx5_qp_table *table, u32 rsn)
+{
+       struct mlx5_core_rsc_common *common;
+       unsigned long flags;
+
+       spin_lock_irqsave(&table->lock, flags);
+
+       common = radix_tree_lookup(&table->tree, rsn);
+       if (common)
+               refcount_inc(&common->refcount);
+
+       spin_unlock_irqrestore(&table->lock, flags);
+
+       return common;
+}
+
+void mlx5_core_put_rsc(struct mlx5_core_rsc_common *common)
+{
+       if (refcount_dec_and_test(&common->refcount))
+               complete(&common->free);
+}
+
+static u64 qp_allowed_event_types(void)
+{
+       u64 mask;
+
+       mask = BIT(MLX5_EVENT_TYPE_PATH_MIG) |
+              BIT(MLX5_EVENT_TYPE_COMM_EST) |
+              BIT(MLX5_EVENT_TYPE_SQ_DRAINED) |
+              BIT(MLX5_EVENT_TYPE_SRQ_LAST_WQE) |
+              BIT(MLX5_EVENT_TYPE_WQ_CATAS_ERROR) |
+              BIT(MLX5_EVENT_TYPE_PATH_MIG_FAILED) |
+              BIT(MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR) |
+              BIT(MLX5_EVENT_TYPE_WQ_ACCESS_ERROR);
+
+       return mask;
+}
+
+static u64 rq_allowed_event_types(void)
+{
+       u64 mask;
+
+       mask = BIT(MLX5_EVENT_TYPE_SRQ_LAST_WQE) |
+              BIT(MLX5_EVENT_TYPE_WQ_CATAS_ERROR);
+
+       return mask;
+}
+
+static u64 sq_allowed_event_types(void)
+{
+       return BIT(MLX5_EVENT_TYPE_WQ_CATAS_ERROR);
+}
+
+static u64 dct_allowed_event_types(void)
+{
+       return BIT(MLX5_EVENT_TYPE_DCT_DRAINED);
+}
+
+static bool is_event_type_allowed(int rsc_type, int event_type)
+{
+       switch (rsc_type) {
+       case MLX5_EVENT_QUEUE_TYPE_QP:
+               return BIT(event_type) & qp_allowed_event_types();
+       case MLX5_EVENT_QUEUE_TYPE_RQ:
+               return BIT(event_type) & rq_allowed_event_types();
+       case MLX5_EVENT_QUEUE_TYPE_SQ:
+               return BIT(event_type) & sq_allowed_event_types();
+       case MLX5_EVENT_QUEUE_TYPE_DCT:
+               return BIT(event_type) & dct_allowed_event_types();
+       default:
+               WARN(1, "Event arrived for unknown resource type");
+               return false;
+       }
+}
+
+static int rsc_event_notifier(struct notifier_block *nb,
+                             unsigned long type, void *data)
+{
+       struct mlx5_core_rsc_common *common;
+       struct mlx5_qp_table *table;
+       struct mlx5_core_dct *dct;
+       u8 event_type = (u8)type;
+       struct mlx5_core_qp *qp;
+       struct mlx5_eqe *eqe;
+       u32 rsn;
+
+       switch (event_type) {
+       case MLX5_EVENT_TYPE_DCT_DRAINED:
+               eqe = data;
+               rsn = be32_to_cpu(eqe->data.dct.dctn) & 0xffffff;
+               rsn |= (MLX5_RES_DCT << MLX5_USER_INDEX_LEN);
+               break;
+       case MLX5_EVENT_TYPE_PATH_MIG:
+       case MLX5_EVENT_TYPE_COMM_EST:
+       case MLX5_EVENT_TYPE_SQ_DRAINED:
+       case MLX5_EVENT_TYPE_SRQ_LAST_WQE:
+       case MLX5_EVENT_TYPE_WQ_CATAS_ERROR:
+       case MLX5_EVENT_TYPE_PATH_MIG_FAILED:
+       case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
+       case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR:
+               eqe = data;
+               rsn = be32_to_cpu(eqe->data.qp_srq.qp_srq_n) & 0xffffff;
+               rsn |= (eqe->data.qp_srq.type << MLX5_USER_INDEX_LEN);
+               break;
+       default:
+               return NOTIFY_DONE;
+       }
+
+       table = container_of(nb, struct mlx5_qp_table, nb);
+       common = mlx5_get_rsc(table, rsn);
+       if (!common)
+               return NOTIFY_OK;
+
+       if (!is_event_type_allowed((rsn >> MLX5_USER_INDEX_LEN), event_type))
+               goto out;
+
+       switch (common->res) {
+       case MLX5_RES_QP:
+       case MLX5_RES_RQ:
+       case MLX5_RES_SQ:
+               qp = (struct mlx5_core_qp *)common;
+               qp->event(qp, event_type);
+               break;
+       case MLX5_RES_DCT:
+               dct = (struct mlx5_core_dct *)common;
+               if (event_type == MLX5_EVENT_TYPE_DCT_DRAINED)
+                       complete(&dct->drained);
+               break;
+       default:
+               break;
+       }
+out:
+       mlx5_core_put_rsc(common);
+
+       return NOTIFY_OK;
+}
+
+static int create_resource_common(struct mlx5_ib_dev *dev,
+                                 struct mlx5_core_qp *qp, int rsc_type)
+{
+       struct mlx5_qp_table *table = &dev->qp_table;
+       int err;
+
+       qp->common.res = rsc_type;
+       spin_lock_irq(&table->lock);
+       err = radix_tree_insert(&table->tree,
+                               qp->qpn | (rsc_type << MLX5_USER_INDEX_LEN),
+                               qp);
+       spin_unlock_irq(&table->lock);
+       if (err)
+               return err;
+
+       refcount_set(&qp->common.refcount, 1);
+       init_completion(&qp->common.free);
+       qp->pid = current->pid;
+
+       return 0;
+}
+
+static void destroy_resource_common(struct mlx5_ib_dev *dev,
+                                   struct mlx5_core_qp *qp)
+{
+       struct mlx5_qp_table *table = &dev->qp_table;
+       unsigned long flags;
+
+       spin_lock_irqsave(&table->lock, flags);
+       radix_tree_delete(&table->tree,
+                         qp->qpn | (qp->common.res << MLX5_USER_INDEX_LEN));
+       spin_unlock_irqrestore(&table->lock, flags);
+       mlx5_core_put_rsc((struct mlx5_core_rsc_common *)qp);
+       wait_for_completion(&qp->common.free);
+}
+
+static int _mlx5_core_destroy_dct(struct mlx5_ib_dev *dev,
+                                 struct mlx5_core_dct *dct, bool need_cleanup)
+{
+       u32 in[MLX5_ST_SZ_DW(destroy_dct_in)] = {};
+       struct mlx5_core_qp *qp = &dct->mqp;
+       int err;
+
+       err = mlx5_core_drain_dct(dev, dct);
+       if (err) {
+               if (dev->mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR)
+                       goto destroy;
+
+               return err;
+       }
+       wait_for_completion(&dct->drained);
+destroy:
+       if (need_cleanup)
+               destroy_resource_common(dev, &dct->mqp);
+       MLX5_SET(destroy_dct_in, in, opcode, MLX5_CMD_OP_DESTROY_DCT);
+       MLX5_SET(destroy_dct_in, in, dctn, qp->qpn);
+       MLX5_SET(destroy_dct_in, in, uid, qp->uid);
+       err = mlx5_cmd_exec_in(dev->mdev, destroy_dct, in);
+       return err;
+}
+
+int mlx5_core_create_dct(struct mlx5_ib_dev *dev, struct mlx5_core_dct *dct,
+                        u32 *in, int inlen, u32 *out, int outlen)
+{
+       struct mlx5_core_qp *qp = &dct->mqp;
+       int err;
+
+       init_completion(&dct->drained);
+       MLX5_SET(create_dct_in, in, opcode, MLX5_CMD_OP_CREATE_DCT);
+
+       err = mlx5_cmd_exec(dev->mdev, in, inlen, out, outlen);
+       if (err)
+               return err;
+
+       qp->qpn = MLX5_GET(create_dct_out, out, dctn);
+       qp->uid = MLX5_GET(create_dct_in, in, uid);
+       err = create_resource_common(dev, qp, MLX5_RES_DCT);
+       if (err)
+               goto err_cmd;
+
+       return 0;
+err_cmd:
+       _mlx5_core_destroy_dct(dev, dct, false);
+       return err;
+}
+
+int mlx5_core_create_qp(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp,
+                       u32 *in, int inlen)
+{
+       u32 out[MLX5_ST_SZ_DW(create_qp_out)] = {};
+       u32 din[MLX5_ST_SZ_DW(destroy_qp_in)] = {};
+       int err;
+
+       MLX5_SET(create_qp_in, in, opcode, MLX5_CMD_OP_CREATE_QP);
+
+       err = mlx5_cmd_exec(dev->mdev, in, inlen, out, sizeof(out));
+       if (err)
+               return err;
+
+       qp->uid = MLX5_GET(create_qp_in, in, uid);
+       qp->qpn = MLX5_GET(create_qp_out, out, qpn);
+
+       err = create_resource_common(dev, qp, MLX5_RES_QP);
+       if (err)
+               goto err_cmd;
+
+       mlx5_debug_qp_add(dev->mdev, qp);
+
+       return 0;
+
+err_cmd:
+       MLX5_SET(destroy_qp_in, din, opcode, MLX5_CMD_OP_DESTROY_QP);
+       MLX5_SET(destroy_qp_in, din, qpn, qp->qpn);
+       MLX5_SET(destroy_qp_in, din, uid, qp->uid);
+       mlx5_cmd_exec_in(dev->mdev, destroy_qp, din);
+       return err;
+}
+
+static int mlx5_core_drain_dct(struct mlx5_ib_dev *dev,
+                              struct mlx5_core_dct *dct)
+{
+       u32 in[MLX5_ST_SZ_DW(drain_dct_in)] = {};
+       struct mlx5_core_qp *qp = &dct->mqp;
+
+       MLX5_SET(drain_dct_in, in, opcode, MLX5_CMD_OP_DRAIN_DCT);
+       MLX5_SET(drain_dct_in, in, dctn, qp->qpn);
+       MLX5_SET(drain_dct_in, in, uid, qp->uid);
+       return mlx5_cmd_exec_in(dev->mdev, drain_dct, in);
+}
+
+int mlx5_core_destroy_dct(struct mlx5_ib_dev *dev,
+                         struct mlx5_core_dct *dct)
+{
+       return _mlx5_core_destroy_dct(dev, dct, true);
+}
+
+int mlx5_core_destroy_qp(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp)
+{
+       u32 in[MLX5_ST_SZ_DW(destroy_qp_in)] = {};
+
+       mlx5_debug_qp_remove(dev->mdev, qp);
+
+       destroy_resource_common(dev, qp);
+
+       MLX5_SET(destroy_qp_in, in, opcode, MLX5_CMD_OP_DESTROY_QP);
+       MLX5_SET(destroy_qp_in, in, qpn, qp->qpn);
+       MLX5_SET(destroy_qp_in, in, uid, qp->uid);
+       mlx5_cmd_exec_in(dev->mdev, destroy_qp, in);
+       return 0;
+}
+
+int mlx5_core_set_delay_drop(struct mlx5_ib_dev *dev,
+                            u32 timeout_usec)
+{
+       u32 in[MLX5_ST_SZ_DW(set_delay_drop_params_in)] = {};
+
+       MLX5_SET(set_delay_drop_params_in, in, opcode,
+                MLX5_CMD_OP_SET_DELAY_DROP_PARAMS);
+       MLX5_SET(set_delay_drop_params_in, in, delay_drop_timeout,
+                timeout_usec / 100);
+       return mlx5_cmd_exec_in(dev->mdev, set_delay_drop_params, in);
+}
+
+struct mbox_info {
+       u32 *in;
+       u32 *out;
+       int inlen;
+       int outlen;
+};
+
+static int mbox_alloc(struct mbox_info *mbox, int inlen, int outlen)
+{
+       mbox->inlen  = inlen;
+       mbox->outlen = outlen;
+       mbox->in = kzalloc(mbox->inlen, GFP_KERNEL);
+       mbox->out = kzalloc(mbox->outlen, GFP_KERNEL);
+       if (!mbox->in || !mbox->out) {
+               kfree(mbox->in);
+               kfree(mbox->out);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void mbox_free(struct mbox_info *mbox)
+{
+       kfree(mbox->in);
+       kfree(mbox->out);
+}
+
+static int modify_qp_mbox_alloc(struct mlx5_core_dev *dev, u16 opcode, int qpn,
+                               u32 opt_param_mask, void *qpc,
+                               struct mbox_info *mbox, u16 uid)
+{
+       mbox->out = NULL;
+       mbox->in = NULL;
+
+#define MBOX_ALLOC(mbox, typ)  \
+       mbox_alloc(mbox, MLX5_ST_SZ_BYTES(typ##_in), MLX5_ST_SZ_BYTES(typ##_out))
+
+#define MOD_QP_IN_SET(typ, in, _opcode, _qpn, _uid)                            \
+       do {                                                                   \
+               MLX5_SET(typ##_in, in, opcode, _opcode);                       \
+               MLX5_SET(typ##_in, in, qpn, _qpn);                             \
+               MLX5_SET(typ##_in, in, uid, _uid);                             \
+       } while (0)
+
+#define MOD_QP_IN_SET_QPC(typ, in, _opcode, _qpn, _opt_p, _qpc, _uid)          \
+       do {                                                                   \
+               MOD_QP_IN_SET(typ, in, _opcode, _qpn, _uid);                   \
+               MLX5_SET(typ##_in, in, opt_param_mask, _opt_p);                \
+               memcpy(MLX5_ADDR_OF(typ##_in, in, qpc), _qpc,                  \
+                      MLX5_ST_SZ_BYTES(qpc));                                 \
+       } while (0)
+
+       switch (opcode) {
+       /* 2RST & 2ERR */
+       case MLX5_CMD_OP_2RST_QP:
+               if (MBOX_ALLOC(mbox, qp_2rst))
+                       return -ENOMEM;
+               MOD_QP_IN_SET(qp_2rst, mbox->in, opcode, qpn, uid);
+               break;
+       case MLX5_CMD_OP_2ERR_QP:
+               if (MBOX_ALLOC(mbox, qp_2err))
+                       return -ENOMEM;
+               MOD_QP_IN_SET(qp_2err, mbox->in, opcode, qpn, uid);
+               break;
+
+       /* MODIFY with QPC */
+       case MLX5_CMD_OP_RST2INIT_QP:
+               if (MBOX_ALLOC(mbox, rst2init_qp))
+                       return -ENOMEM;
+               MOD_QP_IN_SET_QPC(rst2init_qp, mbox->in, opcode, qpn,
+                                 opt_param_mask, qpc, uid);
+               break;
+       case MLX5_CMD_OP_INIT2RTR_QP:
+               if (MBOX_ALLOC(mbox, init2rtr_qp))
+                       return -ENOMEM;
+               MOD_QP_IN_SET_QPC(init2rtr_qp, mbox->in, opcode, qpn,
+                                 opt_param_mask, qpc, uid);
+               break;
+       case MLX5_CMD_OP_RTR2RTS_QP:
+               if (MBOX_ALLOC(mbox, rtr2rts_qp))
+                       return -ENOMEM;
+               MOD_QP_IN_SET_QPC(rtr2rts_qp, mbox->in, opcode, qpn,
+                                 opt_param_mask, qpc, uid);
+               break;
+       case MLX5_CMD_OP_RTS2RTS_QP:
+               if (MBOX_ALLOC(mbox, rts2rts_qp))
+                       return -ENOMEM;
+               MOD_QP_IN_SET_QPC(rts2rts_qp, mbox->in, opcode, qpn,
+                                 opt_param_mask, qpc, uid);
+               break;
+       case MLX5_CMD_OP_SQERR2RTS_QP:
+               if (MBOX_ALLOC(mbox, sqerr2rts_qp))
+                       return -ENOMEM;
+               MOD_QP_IN_SET_QPC(sqerr2rts_qp, mbox->in, opcode, qpn,
+                                 opt_param_mask, qpc, uid);
+               break;
+       case MLX5_CMD_OP_INIT2INIT_QP:
+               if (MBOX_ALLOC(mbox, init2init_qp))
+                       return -ENOMEM;
+               MOD_QP_IN_SET_QPC(init2init_qp, mbox->in, opcode, qpn,
+                                 opt_param_mask, qpc, uid);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int mlx5_core_qp_modify(struct mlx5_ib_dev *dev, u16 opcode, u32 opt_param_mask,
+                       void *qpc, struct mlx5_core_qp *qp)
+{
+       struct mbox_info mbox;
+       int err;
+
+       err = modify_qp_mbox_alloc(dev->mdev, opcode, qp->qpn,
+                                  opt_param_mask, qpc, &mbox, qp->uid);
+       if (err)
+               return err;
+
+       err = mlx5_cmd_exec(dev->mdev, mbox.in, mbox.inlen, mbox.out,
+                           mbox.outlen);
+       mbox_free(&mbox);
+       return err;
+}
+
+int mlx5_init_qp_table(struct mlx5_ib_dev *dev)
+{
+       struct mlx5_qp_table *table = &dev->qp_table;
+
+       spin_lock_init(&table->lock);
+       INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
+       mlx5_qp_debugfs_init(dev->mdev);
+
+       table->nb.notifier_call = rsc_event_notifier;
+       mlx5_notifier_register(dev->mdev, &table->nb);
+
+       return 0;
+}
+
+void mlx5_cleanup_qp_table(struct mlx5_ib_dev *dev)
+{
+       struct mlx5_qp_table *table = &dev->qp_table;
+
+       mlx5_notifier_unregister(dev->mdev, &table->nb);
+       mlx5_qp_debugfs_cleanup(dev->mdev);
+}
+
+int mlx5_core_qp_query(struct mlx5_ib_dev *dev, struct mlx5_core_qp *qp,
+                      u32 *out, int outlen)
+{
+       u32 in[MLX5_ST_SZ_DW(query_qp_in)] = {};
+
+       MLX5_SET(query_qp_in, in, opcode, MLX5_CMD_OP_QUERY_QP);
+       MLX5_SET(query_qp_in, in, qpn, qp->qpn);
+       return mlx5_cmd_exec(dev->mdev, in, sizeof(in), out, outlen);
+}
+
+int mlx5_core_dct_query(struct mlx5_ib_dev *dev, struct mlx5_core_dct *dct,
+                       u32 *out, int outlen)
+{
+       u32 in[MLX5_ST_SZ_DW(query_dct_in)] = {};
+       struct mlx5_core_qp *qp = &dct->mqp;
+
+       MLX5_SET(query_dct_in, in, opcode, MLX5_CMD_OP_QUERY_DCT);
+       MLX5_SET(query_dct_in, in, dctn, qp->qpn);
+
+       return mlx5_cmd_exec(dev->mdev, (void *)&in, sizeof(in), (void *)out,
+                            outlen);
+}
+
+int mlx5_core_xrcd_alloc(struct mlx5_ib_dev *dev, u32 *xrcdn)
+{
+       u32 out[MLX5_ST_SZ_DW(alloc_xrcd_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(alloc_xrcd_in)] = {};
+       int err;
+
+       MLX5_SET(alloc_xrcd_in, in, opcode, MLX5_CMD_OP_ALLOC_XRCD);
+       err = mlx5_cmd_exec_inout(dev->mdev, alloc_xrcd, in, out);
+       if (!err)
+               *xrcdn = MLX5_GET(alloc_xrcd_out, out, xrcd);
+       return err;
+}
+
+int mlx5_core_xrcd_dealloc(struct mlx5_ib_dev *dev, u32 xrcdn)
+{
+       u32 in[MLX5_ST_SZ_DW(dealloc_xrcd_in)] = {};
+
+       MLX5_SET(dealloc_xrcd_in, in, opcode, MLX5_CMD_OP_DEALLOC_XRCD);
+       MLX5_SET(dealloc_xrcd_in, in, xrcd, xrcdn);
+       return mlx5_cmd_exec_in(dev->mdev, dealloc_xrcd, in);
+}
+
+static void destroy_rq_tracked(struct mlx5_ib_dev *dev, u32 rqn, u16 uid)
+{
+       u32 in[MLX5_ST_SZ_DW(destroy_rq_in)] = {};
+
+       MLX5_SET(destroy_rq_in, in, opcode, MLX5_CMD_OP_DESTROY_RQ);
+       MLX5_SET(destroy_rq_in, in, rqn, rqn);
+       MLX5_SET(destroy_rq_in, in, uid, uid);
+       mlx5_cmd_exec_in(dev->mdev, destroy_rq, in);
+}
+
+int mlx5_core_create_rq_tracked(struct mlx5_ib_dev *dev, u32 *in, int inlen,
+                               struct mlx5_core_qp *rq)
+{
+       int err;
+       u32 rqn;
+
+       err = mlx5_core_create_rq(dev->mdev, in, inlen, &rqn);
+       if (err)
+               return err;
+
+       rq->uid = MLX5_GET(create_rq_in, in, uid);
+       rq->qpn = rqn;
+       err = create_resource_common(dev, rq, MLX5_RES_RQ);
+       if (err)
+               goto err_destroy_rq;
+
+       return 0;
+
+err_destroy_rq:
+       destroy_rq_tracked(dev, rq->qpn, rq->uid);
+
+       return err;
+}
+
+void mlx5_core_destroy_rq_tracked(struct mlx5_ib_dev *dev,
+                                 struct mlx5_core_qp *rq)
+{
+       destroy_resource_common(dev, rq);
+       destroy_rq_tracked(dev, rq->qpn, rq->uid);
+}
+
+static void destroy_sq_tracked(struct mlx5_ib_dev *dev, u32 sqn, u16 uid)
+{
+       u32 in[MLX5_ST_SZ_DW(destroy_sq_in)] = {};
+
+       MLX5_SET(destroy_sq_in, in, opcode, MLX5_CMD_OP_DESTROY_SQ);
+       MLX5_SET(destroy_sq_in, in, sqn, sqn);
+       MLX5_SET(destroy_sq_in, in, uid, uid);
+       mlx5_cmd_exec_in(dev->mdev, destroy_sq, in);
+}
+
+int mlx5_core_create_sq_tracked(struct mlx5_ib_dev *dev, u32 *in, int inlen,
+                               struct mlx5_core_qp *sq)
+{
+       u32 out[MLX5_ST_SZ_DW(create_sq_out)] = {};
+       int err;
+
+       MLX5_SET(create_sq_in, in, opcode, MLX5_CMD_OP_CREATE_SQ);
+       err = mlx5_cmd_exec(dev->mdev, in, inlen, out, sizeof(out));
+       if (err)
+               return err;
+
+       sq->qpn = MLX5_GET(create_sq_out, out, sqn);
+       sq->uid = MLX5_GET(create_sq_in, in, uid);
+       err = create_resource_common(dev, sq, MLX5_RES_SQ);
+       if (err)
+               goto err_destroy_sq;
+
+       return 0;
+
+err_destroy_sq:
+       destroy_sq_tracked(dev, sq->qpn, sq->uid);
+
+       return err;
+}
+
+void mlx5_core_destroy_sq_tracked(struct mlx5_ib_dev *dev,
+                                 struct mlx5_core_qp *sq)
+{
+       destroy_resource_common(dev, sq);
+       destroy_sq_tracked(dev, sq->qpn, sq->uid);
+}
+
+struct mlx5_core_rsc_common *mlx5_core_res_hold(struct mlx5_ib_dev *dev,
+                                               int res_num,
+                                               enum mlx5_res_type res_type)
+{
+       u32 rsn = res_num | (res_type << MLX5_USER_INDEX_LEN);
+       struct mlx5_qp_table *table = &dev->qp_table;
+
+       return mlx5_get_rsc(table, rsn);
+}
+
+void mlx5_core_res_put(struct mlx5_core_rsc_common *res)
+{
+       mlx5_core_put_rsc(res);
+}
index 8fc3630..c851570 100644 (file)
@@ -5,9 +5,9 @@
 
 #include <linux/kernel.h>
 #include <linux/mlx5/driver.h>
-#include <linux/mlx5/cmd.h>
 #include "mlx5_ib.h"
 #include "srq.h"
+#include "qp.h"
 
 static int get_pas_size(struct mlx5_srq_attr *in)
 {
index 5724cbb..04d2e72 100644 (file)
@@ -248,8 +248,8 @@ int rvt_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
         */
        if (udata && udata->outlen >= sizeof(__u64)) {
                cq->ip = rvt_create_mmap_info(rdi, sz, udata, u_wc);
-               if (!cq->ip) {
-                       err = -ENOMEM;
+               if (IS_ERR(cq->ip)) {
+                       err = PTR_ERR(cq->ip);
                        goto bail_wc;
                }
 
index 652f4a7..37853aa 100644 (file)
@@ -154,7 +154,7 @@ done:
  * @udata: user data (must be valid!)
  * @obj: opaque pointer to a cq, wq etc
  *
- * Return: rvt_mmap struct on success
+ * Return: rvt_mmap struct on success, ERR_PTR on failure
  */
 struct rvt_mmap_info *rvt_create_mmap_info(struct rvt_dev_info *rdi, u32 size,
                                           struct ib_udata *udata, void *obj)
@@ -166,7 +166,7 @@ struct rvt_mmap_info *rvt_create_mmap_info(struct rvt_dev_info *rdi, u32 size,
 
        ip = kmalloc_node(sizeof(*ip), GFP_KERNEL, rdi->dparms.node);
        if (!ip)
-               return ip;
+               return ERR_PTR(-ENOMEM);
 
        size = PAGE_ALIGN(size);
 
index 0e1b291..500a7ee 100644 (file)
@@ -1244,8 +1244,8 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd,
 
                        qp->ip = rvt_create_mmap_info(rdi, s, udata,
                                                      qp->r_rq.wq);
-                       if (!qp->ip) {
-                               ret = ERR_PTR(-ENOMEM);
+                       if (IS_ERR(qp->ip)) {
+                               ret = ERR_CAST(qp->ip);
                                goto bail_qpn;
                        }
 
index 24fef02..f547c11 100644 (file)
@@ -111,8 +111,8 @@ int rvt_create_srq(struct ib_srq *ibsrq, struct ib_srq_init_attr *srq_init_attr,
                u32 s = sizeof(struct rvt_rwq) + srq->rq.size * sz;
 
                srq->ip = rvt_create_mmap_info(dev, s, udata, srq->rq.wq);
-               if (!srq->ip) {
-                       ret = -ENOMEM;
+               if (IS_ERR(srq->ip)) {
+                       ret = PTR_ERR(srq->ip);
                        goto bail_wq;
                }
 
index ae92c80..9f53aa4 100644 (file)
@@ -920,20 +920,27 @@ static int siw_fastreg_mr(struct ib_pd *pd, struct siw_sqe *sqe)
 {
        struct ib_mr *base_mr = (struct ib_mr *)(uintptr_t)sqe->base_mr;
        struct siw_device *sdev = to_siw_dev(pd->device);
-       struct siw_mem *mem = siw_mem_id2obj(sdev, sqe->rkey  >> 8);
+       struct siw_mem *mem;
        int rv = 0;
 
        siw_dbg_pd(pd, "STag 0x%08x\n", sqe->rkey);
 
-       if (unlikely(!mem || !base_mr)) {
+       if (unlikely(!base_mr)) {
                pr_warn("siw: fastreg: STag 0x%08x unknown\n", sqe->rkey);
                return -EINVAL;
        }
+
        if (unlikely(base_mr->rkey >> 8 != sqe->rkey  >> 8)) {
                pr_warn("siw: fastreg: STag 0x%08x: bad MR\n", sqe->rkey);
-               rv = -EINVAL;
-               goto out;
+               return -EINVAL;
        }
+
+       mem = siw_mem_id2obj(sdev, sqe->rkey  >> 8);
+       if (unlikely(!mem)) {
+               pr_warn("siw: fastreg: STag 0x%08x unknown\n", sqe->rkey);
+               return -EINVAL;
+       }
+
        if (unlikely(mem->pd != pd)) {
                pr_warn("siw: fastreg: PD mismatch\n");
                rv = -EINVAL;
index 2adfde8..2a11a63 100644 (file)
@@ -96,6 +96,8 @@ static inline void tcs_cmd_gen(struct tcs_cmd *cmd, u64 vote_x, u64 vote_y,
        if (!cmd)
                return;
 
+       memset(cmd, 0, sizeof(*cmd));
+
        if (vote_x == 0 && vote_y == 0)
                valid = false;
 
@@ -112,8 +114,7 @@ static inline void tcs_cmd_gen(struct tcs_cmd *cmd, u64 vote_x, u64 vote_y,
         * Set the wait for completion flag on command that need to be completed
         * before the next command.
         */
-       if (commit)
-               cmd->wait = true;
+       cmd->wait = commit;
 }
 
 static void tcs_list_gen(struct list_head *bcm_list, int bucket,
index 58b4a4d..2ab07ce 100644 (file)
@@ -362,7 +362,7 @@ config IPMMU_VMSA
 
 config SPAPR_TCE_IOMMU
        bool "sPAPR TCE IOMMU Support"
-       depends on PPC_POWERNV || PPC_PSERIES || (PPC && COMPILE_TEST)
+       depends on PPC_POWERNV || PPC_PSERIES
        select IOMMU_API
        help
          Enables bits of IOMMU API required by VFIO. The iommu_ops
@@ -457,7 +457,7 @@ config S390_AP_IOMMU
 
 config MTK_IOMMU
        bool "MTK IOMMU Support"
-       depends on ARM || ARM64 || COMPILE_TEST
+       depends on HAS_DMA
        depends on ARCH_MEDIATEK || COMPILE_TEST
        select ARM_DMA_USE_IOMMU
        select IOMMU_API
index 6be3853..2b9a67e 100644 (file)
@@ -2936,7 +2936,7 @@ static int __init parse_amd_iommu_intr(char *str)
 {
        for (; *str; ++str) {
                if (strncmp(str, "legacy", 6) == 0) {
-                       amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY;
+                       amd_iommu_guest_ir = AMD_IOMMU_GUEST_IR_LEGACY_GA;
                        break;
                }
                if (strncmp(str, "vapic", 5) == 0) {
index ef0a524..0182cff 100644 (file)
@@ -371,11 +371,11 @@ int dmar_disabled = 0;
 int dmar_disabled = 1;
 #endif /* CONFIG_INTEL_IOMMU_DEFAULT_ON */
 
-#ifdef INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON
+#ifdef CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON
 int intel_iommu_sm = 1;
 #else
 int intel_iommu_sm;
-#endif /* INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON */
+#endif /* CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON */
 
 int intel_iommu_enabled = 0;
 EXPORT_SYMBOL_GPL(intel_iommu_enabled);
index 2b47141..7b37542 100644 (file)
@@ -170,6 +170,7 @@ static struct dev_iommu *dev_iommu_get(struct device *dev)
 
 static void dev_iommu_free(struct device *dev)
 {
+       iommu_fwspec_free(dev);
        kfree(dev->iommu);
        dev->iommu = NULL;
 }
@@ -1428,7 +1429,7 @@ struct iommu_group *iommu_group_get_for_dev(struct device *dev)
 
        return group;
 }
-EXPORT_SYMBOL(iommu_group_get_for_dev);
+EXPORT_SYMBOL_GPL(iommu_group_get_for_dev);
 
 struct iommu_domain *iommu_group_default_domain(struct iommu_group *group)
 {
index 0e2a964..5b3b270 100644 (file)
@@ -824,8 +824,11 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
        qcom_iommu->dev = dev;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res)
+       if (res) {
                qcom_iommu->local_base = devm_ioremap_resource(dev, res);
+               if (IS_ERR(qcom_iommu->local_base))
+                       return PTR_ERR(qcom_iommu->local_base);
+       }
 
        qcom_iommu->iface_clk = devm_clk_get(dev, "iface");
        if (IS_ERR(qcom_iommu->iface_clk)) {
index eb9bce9..fd7c537 100644 (file)
@@ -416,7 +416,7 @@ static const struct irq_domain_ops bcm7038_l1_domain_ops = {
        .map                    = bcm7038_l1_map,
 };
 
-int __init bcm7038_l1_of_init(struct device_node *dn,
+static int __init bcm7038_l1_of_init(struct device_node *dn,
                              struct device_node *parent)
 {
        struct bcm7038_l1_chip *intc;
index 54d142c..124251b 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/dma-iommu.h>
 #include <linux/efi.h>
 #include <linux/interrupt.h>
+#include <linux/iopoll.h>
 #include <linux/irqdomain.h>
 #include <linux/list.h>
 #include <linux/log2.h>
@@ -3672,6 +3673,20 @@ out:
        return IRQ_SET_MASK_OK_DONE;
 }
 
+static void its_wait_vpt_parse_complete(void)
+{
+       void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
+       u64 val;
+
+       if (!gic_rdists->has_vpend_valid_dirty)
+               return;
+
+       WARN_ON_ONCE(readq_relaxed_poll_timeout(vlpi_base + GICR_VPENDBASER,
+                                               val,
+                                               !(val & GICR_VPENDBASER_Dirty),
+                                               10, 500));
+}
+
 static void its_vpe_schedule(struct its_vpe *vpe)
 {
        void __iomem *vlpi_base = gic_data_rdist_vlpi_base();
@@ -3702,6 +3717,8 @@ static void its_vpe_schedule(struct its_vpe *vpe)
        val |= vpe->idai ? GICR_VPENDBASER_IDAI : 0;
        val |= GICR_VPENDBASER_Valid;
        gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
+
+       its_wait_vpt_parse_complete();
 }
 
 static void its_vpe_deschedule(struct its_vpe *vpe)
@@ -3910,6 +3927,8 @@ static void its_vpe_4_1_schedule(struct its_vpe *vpe,
        val |= FIELD_PREP(GICR_VPENDBASER_4_1_VPEID, vpe->vpe_id);
 
        gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
+
+       its_wait_vpt_parse_complete();
 }
 
 static void its_vpe_4_1_deschedule(struct its_vpe *vpe,
@@ -4035,6 +4054,7 @@ static int its_sgi_set_affinity(struct irq_data *d,
         * not on the host (since they can only be targetting a vPE).
         * Tell the kernel we've done whatever it asked for.
         */
+       irq_data_update_effective_affinity(d, mask_val);
        return IRQ_SET_MASK_OK;
 }
 
index 9dbc81b..d7006ef 100644 (file)
@@ -873,6 +873,7 @@ static int __gic_update_rdist_properties(struct redist_region *region,
        gic_data.rdists.has_rvpeid &= !!(typer & GICR_TYPER_RVPEID);
        gic_data.rdists.has_direct_lpi &= (!!(typer & GICR_TYPER_DirectLPIS) |
                                           gic_data.rdists.has_rvpeid);
+       gic_data.rdists.has_vpend_valid_dirty &= !!(typer & GICR_TYPER_DIRTY);
 
        /* Detect non-sensical configurations */
        if (WARN_ON_ONCE(gic_data.rdists.has_rvpeid && !gic_data.rdists.has_vlpis)) {
@@ -893,10 +894,11 @@ static void gic_update_rdist_properties(void)
        if (WARN_ON(gic_data.ppi_nr == UINT_MAX))
                gic_data.ppi_nr = 0;
        pr_info("%d PPIs implemented\n", gic_data.ppi_nr);
-       pr_info("%sVLPI support, %sdirect LPI support, %sRVPEID support\n",
-               !gic_data.rdists.has_vlpis ? "no " : "",
-               !gic_data.rdists.has_direct_lpi ? "no " : "",
-               !gic_data.rdists.has_rvpeid ? "no " : "");
+       if (gic_data.rdists.has_vlpis)
+               pr_info("GICv4 features: %s%s%s\n",
+                       gic_data.rdists.has_direct_lpi ? "DirectLPI " : "",
+                       gic_data.rdists.has_rvpeid ? "RVPEID " : "",
+                       gic_data.rdists.has_vpend_valid_dirty ? "Valid+Dirty " : "");
 }
 
 /* Check whether it's single security state view */
@@ -1620,6 +1622,7 @@ static int __init gic_init_bases(void __iomem *dist_base,
        gic_data.rdists.has_rvpeid = true;
        gic_data.rdists.has_vlpis = true;
        gic_data.rdists.has_direct_lpi = true;
+       gic_data.rdists.has_vpend_valid_dirty = true;
 
        if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) {
                err = -ENOMEM;
index 6b566bb..ff7627b 100644 (file)
@@ -220,10 +220,16 @@ static int mbigen_irq_domain_alloc(struct irq_domain *domain,
        return 0;
 }
 
+static void mbigen_irq_domain_free(struct irq_domain *domain, unsigned int virq,
+                                  unsigned int nr_irqs)
+{
+       platform_msi_domain_free(domain, virq, nr_irqs);
+}
+
 static const struct irq_domain_ops mbigen_domain_ops = {
        .translate      = mbigen_domain_translate,
        .alloc          = mbigen_irq_domain_alloc,
-       .free           = irq_domain_free_irqs_common,
+       .free           = mbigen_irq_domain_free,
 };
 
 static int mbigen_of_create_domain(struct platform_device *pdev,
index ccc7f82..bc7aebc 100644 (file)
@@ -144,12 +144,17 @@ struct meson_gpio_irq_controller {
 static void meson_gpio_irq_update_bits(struct meson_gpio_irq_controller *ctl,
                                       unsigned int reg, u32 mask, u32 val)
 {
+       unsigned long flags;
        u32 tmp;
 
+       spin_lock_irqsave(&ctl->lock, flags);
+
        tmp = readl_relaxed(ctl->base + reg);
        tmp &= ~mask;
        tmp |= val;
        writel_relaxed(tmp, ctl->base + reg);
+
+       spin_unlock_irqrestore(&ctl->lock, flags);
 }
 
 static void meson_gpio_irq_init_dummy(struct meson_gpio_irq_controller *ctl)
@@ -196,14 +201,15 @@ meson_gpio_irq_request_channel(struct meson_gpio_irq_controller *ctl,
                               unsigned long  hwirq,
                               u32 **channel_hwirq)
 {
+       unsigned long flags;
        unsigned int idx;
 
-       spin_lock(&ctl->lock);
+       spin_lock_irqsave(&ctl->lock, flags);
 
        /* Find a free channel */
        idx = find_first_zero_bit(ctl->channel_map, NUM_CHANNEL);
        if (idx >= NUM_CHANNEL) {
-               spin_unlock(&ctl->lock);
+               spin_unlock_irqrestore(&ctl->lock, flags);
                pr_err("No channel available\n");
                return -ENOSPC;
        }
@@ -211,6 +217,8 @@ meson_gpio_irq_request_channel(struct meson_gpio_irq_controller *ctl,
        /* Mark the channel as used */
        set_bit(idx, ctl->channel_map);
 
+       spin_unlock_irqrestore(&ctl->lock, flags);
+
        /*
         * Setup the mux of the channel to route the signal of the pad
         * to the appropriate input of the GIC
@@ -225,8 +233,6 @@ meson_gpio_irq_request_channel(struct meson_gpio_irq_controller *ctl,
         */
        *channel_hwirq = &(ctl->channel_irqs[idx]);
 
-       spin_unlock(&ctl->lock);
-
        pr_debug("hwirq %lu assigned to channel %d - irq %u\n",
                 hwirq, idx, **channel_hwirq);
 
@@ -287,13 +293,9 @@ static int meson_gpio_irq_type_setup(struct meson_gpio_irq_controller *ctl,
                        val |= REG_EDGE_POL_LOW(params, idx);
        }
 
-       spin_lock(&ctl->lock);
-
        meson_gpio_irq_update_bits(ctl, REG_EDGE_POL,
                                   REG_EDGE_POL_MASK(params, idx), val);
 
-       spin_unlock(&ctl->lock);
-
        return 0;
 }
 
index 547045d..91adf77 100644 (file)
@@ -66,7 +66,7 @@ struct mvebu_icu_irq_data {
        unsigned int type;
 };
 
-DEFINE_STATIC_KEY_FALSE(legacy_bindings);
+static DEFINE_STATIC_KEY_FALSE(legacy_bindings);
 
 static void mvebu_icu_init(struct mvebu_icu *icu,
                           struct mvebu_icu_msi_data *msi_data,
index c34fb3a..d0a71fe 100644 (file)
@@ -56,7 +56,7 @@
 #define     CONTEXT_THRESHOLD          0x00
 #define     CONTEXT_CLAIM              0x04
 
-#define        PLIC_DISABLE_THRESHOLD          0xf
+#define        PLIC_DISABLE_THRESHOLD          0x7
 #define        PLIC_ENABLE_THRESHOLD           0
 
 struct plic_priv {
index 8f6e6b0..7e3ebf6 100644 (file)
@@ -37,6 +37,7 @@
 #define VINT_ENABLE_SET_OFFSET 0x0
 #define VINT_ENABLE_CLR_OFFSET 0x8
 #define VINT_STATUS_OFFSET     0x18
+#define VINT_STATUS_MASKED_OFFSET      0x20
 
 /**
  * struct ti_sci_inta_event_desc - Description of an event coming to
@@ -116,7 +117,7 @@ static void ti_sci_inta_irq_handler(struct irq_desc *desc)
        chained_irq_enter(irq_desc_get_chip(desc), desc);
 
        val = readq_relaxed(inta->base + vint_desc->vint_id * 0x1000 +
-                           VINT_STATUS_OFFSET);
+                           VINT_STATUS_MASKED_OFFSET);
 
        for_each_set_bit(bit, &val, MAX_EVENTS_PER_VINT) {
                virq = irq_find_mapping(domain, vint_desc->events[bit].hwirq);
index e325e87..11e8c7d 100644 (file)
@@ -743,10 +743,10 @@ check_send(struct isar_hw *isar, u8 rdm)
        }
 }
 
-const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4",
+static const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4",
                       "300", "600", "1200", "2400", "4800", "7200",
                       "9600nt", "9600t", "12000", "14400", "WRONG"};
-const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21",
+static const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21",
                       "Bell103", "V23", "Bell202", "V17", "V29", "V27ter"};
 
 static void
index 7af0c53..28b8581 100644 (file)
@@ -183,8 +183,7 @@ static void mac_hid_stop_emulation(void)
 }
 
 static int mac_hid_toggle_emumouse(struct ctl_table *table, int write,
-                                  void __user *buffer, size_t *lenp,
-                                  loff_t *ppos)
+                                  void *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = table->data;
        int old_val = *valp;
index 58fd137..3e50009 100644 (file)
@@ -585,10 +585,12 @@ static struct pgpath *__map_bio(struct multipath *m, struct bio *bio)
 
        /* Do we need to select a new pgpath? */
        pgpath = READ_ONCE(m->current_pgpath);
-       queue_io = test_bit(MPATHF_QUEUE_IO, &m->flags);
-       if (!pgpath || !queue_io)
+       if (!pgpath || !test_bit(MPATHF_QUEUE_IO, &m->flags))
                pgpath = choose_pgpath(m, bio->bi_iter.bi_size);
 
+       /* MPATHF_QUEUE_IO might have been cleared by choose_pgpath. */
+       queue_io = test_bit(MPATHF_QUEUE_IO, &m->flags);
+
        if ((pgpath && queue_io) ||
            (!pgpath && test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags))) {
                /* Queue for the daemon to resubmit */
index 49147e6..fb41b4f 100644 (file)
@@ -435,7 +435,7 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io,
        fio->level++;
 
        if (type == DM_VERITY_BLOCK_TYPE_METADATA)
-               block += v->data_blocks;
+               block = block - v->hash_start + v->data_blocks;
 
        /*
         * For RS(M, N), the continuous FEC data is divided into blocks of N
index 114927d..613c171 100644 (file)
@@ -931,6 +931,24 @@ static int writecache_alloc_entries(struct dm_writecache *wc)
        return 0;
 }
 
+static int writecache_read_metadata(struct dm_writecache *wc, sector_t n_sectors)
+{
+       struct dm_io_region region;
+       struct dm_io_request req;
+
+       region.bdev = wc->ssd_dev->bdev;
+       region.sector = wc->start_sector;
+       region.count = n_sectors;
+       req.bi_op = REQ_OP_READ;
+       req.bi_op_flags = REQ_SYNC;
+       req.mem.type = DM_IO_VMA;
+       req.mem.ptr.vma = (char *)wc->memory_map;
+       req.client = wc->dm_io;
+       req.notify.fn = NULL;
+
+       return dm_io(&req, 1, &region, NULL);
+}
+
 static void writecache_resume(struct dm_target *ti)
 {
        struct dm_writecache *wc = ti->private;
@@ -941,8 +959,18 @@ static void writecache_resume(struct dm_target *ti)
 
        wc_lock(wc);
 
-       if (WC_MODE_PMEM(wc))
+       if (WC_MODE_PMEM(wc)) {
                persistent_memory_invalidate_cache(wc->memory_map, wc->memory_map_size);
+       } else {
+               r = writecache_read_metadata(wc, wc->metadata_sectors);
+               if (r) {
+                       size_t sb_entries_offset;
+                       writecache_error(wc, r, "unable to read metadata: %d", r);
+                       sb_entries_offset = offsetof(struct wc_memory_superblock, entries);
+                       memset((char *)wc->memory_map + sb_entries_offset, -1,
+                              (wc->metadata_sectors << SECTOR_SHIFT) - sb_entries_offset);
+               }
+       }
 
        wc->tree = RB_ROOT;
        INIT_LIST_HEAD(&wc->lru);
@@ -2102,6 +2130,12 @@ static int writecache_ctr(struct dm_target *ti, unsigned argc, char **argv)
                ti->error = "Invalid block size";
                goto bad;
        }
+       if (wc->block_size < bdev_logical_block_size(wc->dev->bdev) ||
+           wc->block_size < bdev_logical_block_size(wc->ssd_dev->bdev)) {
+               r = -EINVAL;
+               ti->error = "Block size is smaller than device logical block size";
+               goto bad;
+       }
        wc->block_size_bits = __ffs(wc->block_size);
 
        wc->max_writeback_jobs = MAX_WRITEBACK_JOBS;
@@ -2200,8 +2234,6 @@ invalid_optional:
                        goto bad;
                }
        } else {
-               struct dm_io_region region;
-               struct dm_io_request req;
                size_t n_blocks, n_metadata_blocks;
                uint64_t n_bitmap_bits;
 
@@ -2258,19 +2290,9 @@ invalid_optional:
                        goto bad;
                }
 
-               region.bdev = wc->ssd_dev->bdev;
-               region.sector = wc->start_sector;
-               region.count = wc->metadata_sectors;
-               req.bi_op = REQ_OP_READ;
-               req.bi_op_flags = REQ_SYNC;
-               req.mem.type = DM_IO_VMA;
-               req.mem.ptr.vma = (char *)wc->memory_map;
-               req.client = wc->dm_io;
-               req.notify.fn = NULL;
-
-               r = dm_io(&req, 1, &region, NULL);
+               r = writecache_read_metadata(wc, wc->block_size >> SECTOR_SHIFT);
                if (r) {
-                       ti->error = "Unable to read metadata";
+                       ti->error = "Unable to read first block of metadata";
                        goto bad;
                }
        }
index 0f3417d..069c42f 100644 (file)
@@ -103,6 +103,8 @@ lirc_mode2_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_map_peek_elem_proto;
        case BPF_FUNC_ktime_get_ns:
                return &bpf_ktime_get_ns_proto;
+       case BPF_FUNC_ktime_get_boot_ns:
+               return &bpf_ktime_get_boot_ns_proto;
        case BPF_FUNC_tail_call:
                return &bpf_tail_call_proto;
        case BPF_FUNC_get_prandom_u32:
index 3d21c38..0c390fe 100644 (file)
@@ -203,11 +203,12 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        }
        hw = to_me_hw(dev);
        hw->mem_addr = pcim_iomap_table(pdev)[0];
-       hw->irq = pdev->irq;
        hw->read_fws = mei_me_read_fws;
 
        pci_enable_msi(pdev);
 
+       hw->irq = pdev->irq;
+
         /* request and enable interrupt */
        irqflags = pci_dev_msi_enabled(pdev) ? IRQF_ONESHOT : IRQF_SHARED;
 
index 8f201d0..3bfe72c 100644 (file)
@@ -116,7 +116,7 @@ config MIC_COSM
 
 config VOP
        tristate "VOP Driver"
-       depends on VOP_BUS
+       depends on VOP_BUS && VHOST_DPN
        select VHOST_RING
        select VIRTIO
        help
index 5bd0ab8..baa6314 100644 (file)
@@ -878,7 +878,7 @@ static int mmc_send_hpi_cmd(struct mmc_card *card)
  *     Issued High Priority Interrupt, and check for card status
  *     until out-of prg-state.
  */
-int mmc_interrupt_hpi(struct mmc_card *card)
+static int mmc_interrupt_hpi(struct mmc_card *card)
 {
        int err;
        u32 status;
index c2239ee..75934f3 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/delay.h>
 #include <linux/highmem.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
@@ -349,12 +350,16 @@ static int cqhci_enable(struct mmc_host *mmc, struct mmc_card *card)
 /* CQHCI is idle and should halt immediately, so set a small timeout */
 #define CQHCI_OFF_TIMEOUT 100
 
+static u32 cqhci_read_ctl(struct cqhci_host *cq_host)
+{
+       return cqhci_readl(cq_host, CQHCI_CTL);
+}
+
 static void cqhci_off(struct mmc_host *mmc)
 {
        struct cqhci_host *cq_host = mmc->cqe_private;
-       ktime_t timeout;
-       bool timed_out;
        u32 reg;
+       int err;
 
        if (!cq_host->enabled || !mmc->cqe_on || cq_host->recovery_halt)
                return;
@@ -364,15 +369,9 @@ static void cqhci_off(struct mmc_host *mmc)
 
        cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL);
 
-       timeout = ktime_add_us(ktime_get(), CQHCI_OFF_TIMEOUT);
-       while (1) {
-               timed_out = ktime_compare(ktime_get(), timeout) > 0;
-               reg = cqhci_readl(cq_host, CQHCI_CTL);
-               if ((reg & CQHCI_HALT) || timed_out)
-                       break;
-       }
-
-       if (timed_out)
+       err = readx_poll_timeout(cqhci_read_ctl, cq_host, reg,
+                                reg & CQHCI_HALT, 0, CQHCI_OFF_TIMEOUT);
+       if (err < 0)
                pr_err("%s: cqhci: CQE stuck on\n", mmc_hostname(mmc));
        else
                pr_debug("%s: cqhci: CQE off\n", mmc_hostname(mmc));
index 8b038e7..2e58743 100644 (file)
@@ -357,14 +357,6 @@ static void meson_mx_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
                meson_mx_mmc_start_cmd(mmc, mrq->cmd);
 }
 
-static int meson_mx_mmc_card_busy(struct mmc_host *mmc)
-{
-       struct meson_mx_mmc_host *host = mmc_priv(mmc);
-       u32 irqc = readl(host->base + MESON_MX_SDIO_IRQC);
-
-       return !!(irqc & MESON_MX_SDIO_IRQC_FORCE_DATA_DAT_MASK);
-}
-
 static void meson_mx_mmc_read_response(struct mmc_host *mmc,
                                       struct mmc_command *cmd)
 {
@@ -506,7 +498,6 @@ static void meson_mx_mmc_timeout(struct timer_list *t)
 static struct mmc_host_ops meson_mx_mmc_ops = {
        .request                = meson_mx_mmc_request,
        .set_ios                = meson_mx_mmc_set_ios,
-       .card_busy              = meson_mx_mmc_card_busy,
        .get_cd                 = mmc_gpio_get_cd,
        .get_ro                 = mmc_gpio_get_ro,
 };
@@ -570,7 +561,7 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
        mmc->f_max = clk_round_rate(host->cfg_div_clk,
                                    clk_get_rate(host->parent_clk));
 
-       mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23;
+       mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
        mmc->ops = &meson_mx_mmc_ops;
 
        ret = mmc_of_parse(mmc);
index 09ff731..a8bcb3f 100644 (file)
@@ -2087,6 +2087,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)
                goto clk_disable;
        }
 
+       msm_host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY;
+
        pm_runtime_get_noresume(&pdev->dev);
        pm_runtime_set_active(&pdev->dev);
        pm_runtime_enable(&pdev->dev);
index 525de24..2527244 100644 (file)
@@ -601,6 +601,9 @@ static int intel_select_drive_strength(struct mmc_card *card,
        struct sdhci_pci_slot *slot = sdhci_priv(host);
        struct intel_host *intel_host = sdhci_pci_priv(slot);
 
+       if (!(mmc_driver_type_mask(intel_host->drv_strength) & card_drv))
+               return 0;
+
        return intel_host->drv_strength;
 }
 
index 1dea1ba..4703cd5 100644 (file)
@@ -235,6 +235,16 @@ static void xenon_voltage_switch(struct sdhci_host *host)
 {
        /* Wait for 5ms after set 1.8V signal enable bit */
        usleep_range(5000, 5500);
+
+       /*
+        * For some reason the controller's Host Control2 register reports
+        * the bit representing 1.8V signaling as 0 when read after it was
+        * written as 1. Subsequent read reports 1.
+        *
+        * Since this may cause some issues, do an empty read of the Host
+        * Control2 register here to circumvent this.
+        */
+       sdhci_readw(host, SDHCI_HOST_CONTROL2);
 }
 
 static const struct sdhci_ops sdhci_xenon_ops = {
index 7ddb742..6539238 100644 (file)
@@ -18,3 +18,5 @@ spi-nor-objs                  += winbond.o
 spi-nor-objs                   += xilinx.o
 spi-nor-objs                   += xmc.o
 obj-$(CONFIG_MTD_SPI_NOR)      += spi-nor.o
+
+obj-$(CONFIG_MTD_SPI_NOR)      += controllers/
index b103fbd..c7d310e 100644 (file)
@@ -50,7 +50,7 @@ config BONDING
          The driver supports multiple bonding modes to allow for both high
          performance and high availability operation.
 
-         Refer to <file:Documentation/networking/bonding.txt> for more
+         Refer to <file:Documentation/networking/bonding.rst> for more
          information.
 
          To compile this driver as a module, choose M here: the module
@@ -126,7 +126,7 @@ config EQUALIZER
          Linux driver or with a Livingston Portmaster 2e.
 
          Say Y if you want this and read
-         <file:Documentation/networking/eql.txt>.  You may also want to read
+         <file:Documentation/networking/eql.rst>.  You may also want to read
          section 6.2 of the NET-3-HOWTO, available from
          <http://www.tldp.org/docs.html#howto>.
 
@@ -302,7 +302,7 @@ config NETCONSOLE
        tristate "Network console logging support"
        ---help---
          If you want to log kernel messages over the network, enable this.
-         See <file:Documentation/networking/netconsole.txt> for details.
+         See <file:Documentation/networking/netconsole.rst> for details.
 
 config NETCONSOLE_DYNAMIC
        bool "Dynamic reconfiguration of logging targets"
@@ -312,7 +312,7 @@ config NETCONSOLE_DYNAMIC
          This option enables the ability to dynamically reconfigure target
          parameters (interface, IP addresses, port numbers, MAC addresses)
          at runtime through a userspace interface exported using configfs.
-         See <file:Documentation/networking/netconsole.txt> for details.
+         See <file:Documentation/networking/netconsole.rst> for details.
 
 config NETPOLL
        def_bool NETCONSOLE
@@ -355,7 +355,7 @@ config TUN
          devices, driver will automatically delete tunXX or tapXX device and
          all routes corresponding to it.
 
-         Please read <file:Documentation/networking/tuntap.txt> for more
+         Please read <file:Documentation/networking/tuntap.rst> for more
          information.
 
          To compile this driver as a module, choose M here: the module
@@ -460,7 +460,7 @@ config NET_SB1000
 
          At present this driver only compiles as a module, so say M here if
          you have this card. The module will be called sb1000. Then read
-         <file:Documentation/networking/device_drivers/sb1000.txt> for
+         <file:Documentation/networking/device_drivers/sb1000.rst> for
          information on how to use this module, as it needs special ppp
          scripts for establishing a connection. Further documentation
          and the necessary scripts can be found at:
index af509b0..10589a8 100644 (file)
@@ -48,7 +48,7 @@ config LTPC
          If you are in doubt, this card is the one with the 65C02 chip on it.
          You also need version 1.3.3 or later of the netatalk package.
          This driver is experimental, which means that it may not work.
-         See the file <file:Documentation/networking/ltpc.txt>.
+         See the file <file:Documentation/networking/ltpc.rst>.
 
 config COPS
        tristate "COPS LocalTalk PC support"
@@ -59,7 +59,7 @@ config COPS
          package. This driver is experimental, which means that it may not
          work. This driver will only work if you choose "AppleTalk DDP"
          networking support, above.
-         Please read the file <file:Documentation/networking/cops.txt>.
+         Please read the file <file:Documentation/networking/cops.rst>.
 
 config COPS_DAYNA
        bool "Dayna firmware support"
@@ -86,7 +86,7 @@ config IPDDP
          box is stuck on an AppleTalk only network) or decapsulate (e.g. if
          you want your Linux box to act as an Internet gateway for a zoo of
          AppleTalk connected Macs). Please see the file
-         <file:Documentation/networking/ipddp.txt> for more information.
+         <file:Documentation/networking/ipddp.rst> for more information.
 
          If you say Y here, the AppleTalk-IP support will be compiled into
          the kernel. In this case, you can either use encapsulation or
@@ -107,4 +107,4 @@ config IPDDP_ENCAP
          IP packets inside AppleTalk frames; this is useful if your Linux box
          is stuck on an AppleTalk network (which hopefully contains a
          decapsulator somewhere). Please see
-         <file:Documentation/networking/ipddp.txt> for more information.
+         <file:Documentation/networking/ipddp.rst> for more information.
index 27551bf..43eef60 100644 (file)
@@ -9,7 +9,7 @@ menuconfig ARCNET
        ---help---
          If you have a network card of this type, say Y and check out the
          (arguably) beautiful poetry in
-         <file:Documentation/networking/arcnet.txt>.
+         <file:Documentation/networking/arcnet.rst>.
 
          You need both this driver, and the driver for the particular ARCnet
          chipset of your card. If you don't know, then it's probably a
@@ -28,7 +28,7 @@ config ARCNET_1201
          arc0 device.  You need to say Y here to communicate with
          industry-standard RFC1201 implementations, like the arcether.com
          packet driver or most DOS/Windows ODI drivers.  Please read the
-         ARCnet documentation in <file:Documentation/networking/arcnet.txt>
+         ARCnet documentation in <file:Documentation/networking/arcnet.rst>
          for more information about using arc0.
 
 config ARCNET_1051
@@ -42,7 +42,7 @@ config ARCNET_1051
          industry-standard RFC1201 implementations, like the arcether.com
          packet driver or most DOS/Windows ODI drivers. RFC1201 is included
          automatically as the arc0 device. Please read the ARCnet
-         documentation in <file:Documentation/networking/arcnet.txt> for more
+         documentation in <file:Documentation/networking/arcnet.rst> for more
          information about using arc0e and arc0s.
 
 config ARCNET_RAW
index 2e70e43..baa9319 100644 (file)
@@ -4491,7 +4491,6 @@ static void bond_uninit(struct net_device *bond_dev)
 
        list_del(&bond->bond_list);
 
-       lockdep_unregister_key(&bond->stats_lock_key);
        bond_debug_unregister(bond);
 }
 
@@ -4896,8 +4895,7 @@ static int bond_init(struct net_device *bond_dev)
                return -ENOMEM;
 
        spin_lock_init(&bond->stats_lock);
-       lockdep_register_key(&bond->stats_lock_key);
-       lockdep_set_class(&bond->stats_lock, &bond->stats_lock_key);
+       netdev_lockdep_set_classes(bond_dev);
 
        list_add_tail(&bond->bond_list, &bn->dev_list);
 
index 45b77bc..48cdf3a 100644 (file)
@@ -14,7 +14,7 @@
 
 #ifndef _BONDING_PRIV_H
 #define _BONDING_PRIV_H
-#include <linux/vermagic.h>
+#include <generated/utsrelease.h>
 
 #define DRV_NAME       "bonding"
 #define DRV_DESCRIPTION        "Ethernet Channel Bonding Driver"
index 9db0570..1538ad1 100644 (file)
@@ -28,7 +28,7 @@ config CAIF_SPI_SLAVE
          The CAIF Link layer SPI Protocol driver for Slave SPI interface.
          This driver implements a platform driver to accommodate for a
          platform specific SPI device. A sample CAIF SPI Platform device is
-         provided in <file:Documentation/networking/caif/spi_porting.txt>.
+         provided in <file:Documentation/networking/caif/spi_porting.rst>.
 
 config CAIF_SPI_SYNC
        bool "Next command and length in start of frame"
@@ -50,7 +50,7 @@ config CAIF_HSI
 
 config CAIF_VIRTIO
        tristate "CAIF virtio transport driver"
-       depends on CAIF && HAS_DMA
+       depends on CAIF && HAS_DMA && VHOST_DPN
        select VHOST_RING
        select VIRTIO
        select GENERIC_ALLOCATOR
index 68e2381..ceb8be6 100644 (file)
@@ -1474,15 +1474,19 @@ static int b53_arl_rw_op(struct b53_device *dev, unsigned int op)
                reg |= ARLTBL_RW;
        else
                reg &= ~ARLTBL_RW;
+       if (dev->vlan_enabled)
+               reg &= ~ARLTBL_IVL_SVL_SELECT;
+       else
+               reg |= ARLTBL_IVL_SVL_SELECT;
        b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, reg);
 
        return b53_arl_op_wait(dev);
 }
 
 static int b53_arl_read(struct b53_device *dev, u64 mac,
-                       u16 vid, struct b53_arl_entry *ent, u8 *idx,
-                       bool is_valid)
+                       u16 vid, struct b53_arl_entry *ent, u8 *idx)
 {
+       DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES);
        unsigned int i;
        int ret;
 
@@ -1490,8 +1494,10 @@ static int b53_arl_read(struct b53_device *dev, u64 mac,
        if (ret)
                return ret;
 
+       bitmap_zero(free_bins, dev->num_arl_bins);
+
        /* Read the bins */
-       for (i = 0; i < dev->num_arl_entries; i++) {
+       for (i = 0; i < dev->num_arl_bins; i++) {
                u64 mac_vid;
                u32 fwd_entry;
 
@@ -1501,13 +1507,24 @@ static int b53_arl_read(struct b53_device *dev, u64 mac,
                           B53_ARLTBL_DATA_ENTRY(i), &fwd_entry);
                b53_arl_to_entry(ent, mac_vid, fwd_entry);
 
-               if (!(fwd_entry & ARLTBL_VALID))
+               if (!(fwd_entry & ARLTBL_VALID)) {
+                       set_bit(i, free_bins);
                        continue;
+               }
                if ((mac_vid & ARLTBL_MAC_MASK) != mac)
                        continue;
+               if (dev->vlan_enabled &&
+                   ((mac_vid >> ARLTBL_VID_S) & ARLTBL_VID_MASK) != vid)
+                       continue;
                *idx = i;
+               return 0;
        }
 
+       if (bitmap_weight(free_bins, dev->num_arl_bins) == 0)
+               return -ENOSPC;
+
+       *idx = find_first_bit(free_bins, dev->num_arl_bins);
+
        return -ENOENT;
 }
 
@@ -1532,15 +1549,27 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,
        if (ret)
                return ret;
 
-       ret = b53_arl_read(dev, mac, vid, &ent, &idx, is_valid);
+       ret = b53_arl_read(dev, mac, vid, &ent, &idx);
+
        /* If this is a read, just finish now */
        if (op)
                return ret;
 
-       /* We could not find a matching MAC, so reset to a new entry */
-       if (ret) {
+       switch (ret) {
+       case -ENOSPC:
+               dev_dbg(dev->dev, "{%pM,%.4d} no space left in ARL\n",
+                       addr, vid);
+               return is_valid ? ret : 0;
+       case -ENOENT:
+               /* We could not find a matching MAC, so reset to a new entry */
+               dev_dbg(dev->dev, "{%pM,%.4d} not found, using idx: %d\n",
+                       addr, vid, idx);
                fwd_entry = 0;
-               idx = 1;
+               break;
+       default:
+               dev_dbg(dev->dev, "{%pM,%.4d} found, using idx: %d\n",
+                       addr, vid, idx);
+               break;
        }
 
        /* For multicast address, the port is a bitmask and the validity
@@ -1558,7 +1587,6 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,
                ent.is_valid = !!(ent.port);
        }
 
-       ent.is_valid = is_valid;
        ent.vid = vid;
        ent.is_static = true;
        ent.is_age = false;
@@ -1664,7 +1692,7 @@ int b53_fdb_dump(struct dsa_switch *ds, int port,
                if (ret)
                        return ret;
 
-               if (priv->num_arl_entries > 2) {
+               if (priv->num_arl_bins > 2) {
                        b53_arl_search_rd(priv, 1, &results[1]);
                        ret = b53_fdb_copy(port, &results[1], cb, data);
                        if (ret)
@@ -1674,7 +1702,7 @@ int b53_fdb_dump(struct dsa_switch *ds, int port,
                                break;
                }
 
-       } while (count++ < 1024);
+       } while (count++ < b53_max_arl_entries(priv) / 2);
 
        return 0;
 }
@@ -2157,7 +2185,8 @@ struct b53_chip_data {
        u16 enabled_ports;
        u8 cpu_port;
        u8 vta_regs[3];
-       u8 arl_entries;
+       u8 arl_bins;
+       u16 arl_buckets;
        u8 duplex_reg;
        u8 jumbo_pm_reg;
        u8 jumbo_size_reg;
@@ -2176,7 +2205,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM5325",
                .vlans = 16,
                .enabled_ports = 0x1f,
-               .arl_entries = 2,
+               .arl_bins = 2,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT_25,
                .duplex_reg = B53_DUPLEX_STAT_FE,
        },
@@ -2185,7 +2215,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM5365",
                .vlans = 256,
                .enabled_ports = 0x1f,
-               .arl_entries = 2,
+               .arl_bins = 2,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT_25,
                .duplex_reg = B53_DUPLEX_STAT_FE,
        },
@@ -2194,7 +2225,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM5389",
                .vlans = 4096,
                .enabled_ports = 0x1f,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT,
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2206,7 +2238,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM5395",
                .vlans = 4096,
                .enabled_ports = 0x1f,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT,
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2218,7 +2251,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM5397",
                .vlans = 4096,
                .enabled_ports = 0x1f,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT,
                .vta_regs = B53_VTA_REGS_9798,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2230,7 +2264,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM5398",
                .vlans = 4096,
                .enabled_ports = 0x7f,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT,
                .vta_regs = B53_VTA_REGS_9798,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2242,7 +2277,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM53115",
                .vlans = 4096,
                .enabled_ports = 0x1f,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .vta_regs = B53_VTA_REGS,
                .cpu_port = B53_CPU_PORT,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2254,7 +2290,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM53125",
                .vlans = 4096,
                .enabled_ports = 0xff,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT,
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2266,7 +2303,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM53128",
                .vlans = 4096,
                .enabled_ports = 0x1ff,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT,
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2278,7 +2316,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM63xx",
                .vlans = 4096,
                .enabled_ports = 0, /* pdata must provide them */
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT,
                .vta_regs = B53_VTA_REGS_63XX,
                .duplex_reg = B53_DUPLEX_STAT_63XX,
@@ -2290,7 +2329,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM53010",
                .vlans = 4096,
                .enabled_ports = 0x1f,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2302,7 +2342,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM53011",
                .vlans = 4096,
                .enabled_ports = 0x1bf,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2314,7 +2355,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM53012",
                .vlans = 4096,
                .enabled_ports = 0x1bf,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2326,7 +2368,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM53018",
                .vlans = 4096,
                .enabled_ports = 0x1f,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2338,7 +2381,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM53019",
                .vlans = 4096,
                .enabled_ports = 0x1f,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2350,7 +2394,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM585xx/586xx/88312",
                .vlans  = 4096,
                .enabled_ports = 0x1ff,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT,
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2362,7 +2407,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM583xx/11360",
                .vlans = 4096,
                .enabled_ports = 0x103,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT,
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2374,7 +2420,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM7445",
                .vlans  = 4096,
                .enabled_ports = 0x1ff,
-               .arl_entries = 4,
+               .arl_bins = 4,
+               .arl_buckets = 1024,
                .cpu_port = B53_CPU_PORT,
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2386,7 +2433,8 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .dev_name = "BCM7278",
                .vlans = 4096,
                .enabled_ports = 0x1ff,
-               .arl_entries= 4,
+               .arl_bins = 4,
+               .arl_buckets = 256,
                .cpu_port = B53_CPU_PORT,
                .vta_regs = B53_VTA_REGS,
                .duplex_reg = B53_DUPLEX_STAT_GE,
@@ -2414,7 +2462,8 @@ static int b53_switch_init(struct b53_device *dev)
                        dev->jumbo_pm_reg = chip->jumbo_pm_reg;
                        dev->cpu_port = chip->cpu_port;
                        dev->num_vlans = chip->vlans;
-                       dev->num_arl_entries = chip->arl_entries;
+                       dev->num_arl_bins = chip->arl_bins;
+                       dev->num_arl_buckets = chip->arl_buckets;
                        break;
                }
        }
index 3d42318..e942c60 100644 (file)
@@ -117,7 +117,8 @@ struct b53_device {
        u8 jumbo_pm_reg;
        u8 jumbo_size_reg;
        int reset_gpio;
-       u8 num_arl_entries;
+       u8 num_arl_bins;
+       u16 num_arl_buckets;
        enum dsa_tag_protocol tag_protocol;
 
        /* used ports mask */
@@ -212,6 +213,11 @@ static inline int is58xx(struct b53_device *dev)
 #define B53_CPU_PORT_25        5
 #define B53_CPU_PORT   8
 
+static inline unsigned int b53_max_arl_entries(struct b53_device *dev)
+{
+       return dev->num_arl_buckets * dev->num_arl_bins;
+}
+
 struct b53_device *b53_switch_alloc(struct device *base,
                                    const struct b53_io_ops *ops,
                                    void *priv);
index 2a9f421..c90985c 100644 (file)
 /* ARL Table Read/Write Register (8 bit) */
 #define B53_ARLTBL_RW_CTRL             0x00
 #define    ARLTBL_RW                   BIT(0)
+#define    ARLTBL_IVL_SVL_SELECT       BIT(6)
 #define    ARLTBL_START_DONE           BIT(7)
 
 /* MAC Address Index Register (48 bit) */
  *
  * BCM5325 and BCM5365 share most definitions below
  */
-#define B53_ARLTBL_MAC_VID_ENTRY(n)    (0x10 * (n))
+#define B53_ARLTBL_MAC_VID_ENTRY(n)    ((0x10 * (n)) + 0x10)
 #define   ARLTBL_MAC_MASK              0xffffffffffffULL
 #define   ARLTBL_VID_S                 48
 #define   ARLTBL_VID_MASK_25           0xff
 #define   ARLTBL_VALID_25              BIT(63)
 
 /* ARL Table Data Entry N Registers (32 bit) */
-#define B53_ARLTBL_DATA_ENTRY(n)       ((0x10 * (n)) + 0x08)
+#define B53_ARLTBL_DATA_ENTRY(n)       ((0x10 * (n)) + 0x18)
 #define   ARLTBL_DATA_PORT_ID_MASK     0x1ff
 #define   ARLTBL_TC(tc)                        ((3 & tc) << 11)
 #define   ARLTBL_AGE                   BIT(14)
 #define   ARLTBL_STATIC                        BIT(15)
 #define   ARLTBL_VALID                 BIT(16)
 
+/* Maximum number of bin entries in the ARL for all switches */
+#define B53_ARLTBL_MAX_BIN_ENTRIES     4
+
 /* ARL Search Control Register (8 bit) */
 #define B53_ARL_SRCH_CTL               0x50
 #define B53_ARL_SRCH_CTL_25            0x20
index 0a1be52..1207c30 100644 (file)
@@ -524,7 +524,7 @@ static void b53_srab_prepare_irq(struct platform_device *pdev)
 
                port->num = i;
                port->dev = dev;
-               port->irq = platform_get_irq_byname(pdev, name);
+               port->irq = platform_get_irq_byname_optional(pdev, name);
                kfree(name);
        }
 
index 2d0d91d..5c444cd 100644 (file)
@@ -66,58 +66,6 @@ static const struct mt7530_mib_desc mt7530_mib[] = {
        MIB_DESC(1, 0xb8, "RxArlDrop"),
 };
 
-static int
-mt7623_trgmii_write(struct mt7530_priv *priv,  u32 reg, u32 val)
-{
-       int ret;
-
-       ret =  regmap_write(priv->ethernet, TRGMII_BASE(reg), val);
-       if (ret < 0)
-               dev_err(priv->dev,
-                       "failed to priv write register\n");
-       return ret;
-}
-
-static u32
-mt7623_trgmii_read(struct mt7530_priv *priv, u32 reg)
-{
-       int ret;
-       u32 val;
-
-       ret = regmap_read(priv->ethernet, TRGMII_BASE(reg), &val);
-       if (ret < 0) {
-               dev_err(priv->dev,
-                       "failed to priv read register\n");
-               return ret;
-       }
-
-       return val;
-}
-
-static void
-mt7623_trgmii_rmw(struct mt7530_priv *priv, u32 reg,
-                 u32 mask, u32 set)
-{
-       u32 val;
-
-       val = mt7623_trgmii_read(priv, reg);
-       val &= ~mask;
-       val |= set;
-       mt7623_trgmii_write(priv, reg, val);
-}
-
-static void
-mt7623_trgmii_set(struct mt7530_priv *priv, u32 reg, u32 val)
-{
-       mt7623_trgmii_rmw(priv, reg, 0, val);
-}
-
-static void
-mt7623_trgmii_clear(struct mt7530_priv *priv, u32 reg, u32 val)
-{
-       mt7623_trgmii_rmw(priv, reg, val, 0);
-}
-
 static int
 core_read_mmd_indirect(struct mt7530_priv *priv, int prtad, int devad)
 {
@@ -530,27 +478,6 @@ mt7530_pad_clk_setup(struct dsa_switch *ds, int mode)
                for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
                        mt7530_rmw(priv, MT7530_TRGMII_RD(i),
                                   RD_TAP_MASK, RD_TAP(16));
-       else
-               if (priv->id != ID_MT7621)
-                       mt7623_trgmii_set(priv, GSW_INTF_MODE,
-                                         INTF_MODE_TRGMII);
-
-       return 0;
-}
-
-static int
-mt7623_pad_clk_setup(struct dsa_switch *ds)
-{
-       struct mt7530_priv *priv = ds->priv;
-       int i;
-
-       for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
-               mt7623_trgmii_write(priv, GSW_TRGMII_TD_ODT(i),
-                                   TD_DM_DRVP(8) | TD_DM_DRVN(8));
-
-       mt7623_trgmii_set(priv, GSW_TRGMII_RCK_CTRL, RX_RST | RXC_DQSISEL);
-       mt7623_trgmii_clear(priv, GSW_TRGMII_RCK_CTRL, RX_RST);
-
        return 0;
 }
 
@@ -846,8 +773,9 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
         */
        mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,
                   MT7530_PORT_MATRIX_MODE);
-       mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK,
-                  VLAN_ATTR(MT7530_VLAN_TRANSPARENT));
+       mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK | PVC_EG_TAG_MASK,
+                  VLAN_ATTR(MT7530_VLAN_TRANSPARENT) |
+                  PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
 
        for (i = 0; i < MT7530_NUM_PORTS; i++) {
                if (dsa_is_user_port(ds, i) &&
@@ -863,8 +791,8 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
        if (all_user_ports_removed) {
                mt7530_write(priv, MT7530_PCR_P(MT7530_CPU_PORT),
                             PCR_MATRIX(dsa_user_ports(priv->ds)));
-               mt7530_write(priv, MT7530_PVC_P(MT7530_CPU_PORT),
-                            PORT_SPEC_TAG);
+               mt7530_write(priv, MT7530_PVC_P(MT7530_CPU_PORT), PORT_SPEC_TAG
+                            | PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
        }
 }
 
@@ -890,8 +818,9 @@ mt7530_port_set_vlan_aware(struct dsa_switch *ds, int port)
        /* Set the port as a user port which is to be able to recognize VID
         * from incoming packets before fetching entry within the VLAN table.
         */
-       mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK,
-                  VLAN_ATTR(MT7530_VLAN_USER));
+       mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK | PVC_EG_TAG_MASK,
+                  VLAN_ATTR(MT7530_VLAN_USER) |
+                  PVC_EG_TAG(MT7530_VLAN_EG_DISABLED));
 }
 
 static void
@@ -1303,10 +1232,6 @@ mt7530_setup(struct dsa_switch *ds)
        dn = dsa_to_port(ds, MT7530_CPU_PORT)->master->dev.of_node->parent;
 
        if (priv->id == ID_MT7530) {
-               priv->ethernet = syscon_node_to_regmap(dn);
-               if (IS_ERR(priv->ethernet))
-                       return PTR_ERR(priv->ethernet);
-
                regulator_set_voltage(priv->core_pwr, 1000000, 1000000);
                ret = regulator_enable(priv->core_pwr);
                if (ret < 0) {
@@ -1380,6 +1305,10 @@ mt7530_setup(struct dsa_switch *ds)
                        mt7530_cpu_port_enable(priv, i);
                else
                        mt7530_port_disable(ds, i);
+
+               /* Enable consistent egress tag */
+               mt7530_rmw(priv, MT7530_PVC_P(i), PVC_EG_TAG_MASK,
+                          PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
        }
 
        /* Setup port 5 */
@@ -1468,14 +1397,6 @@ static void mt7530_phylink_mac_config(struct dsa_switch *ds, int port,
                /* Setup TX circuit incluing relevant PAD and driving */
                mt7530_pad_clk_setup(ds, state->interface);
 
-               if (priv->id == ID_MT7530) {
-                       /* Setup RX circuit, relevant PAD and driving on the
-                        * host which must be placed after the setup on the
-                        * device side is all finished.
-                        */
-                       mt7623_pad_clk_setup(ds);
-               }
-
                priv->p6_interface = state->interface;
                break;
        default:
index ef9b52f..979bb63 100644 (file)
@@ -172,9 +172,16 @@ enum mt7530_port_mode {
 /* Register for port vlan control */
 #define MT7530_PVC_P(x)                        (0x2010 + ((x) * 0x100))
 #define  PORT_SPEC_TAG                 BIT(5)
+#define  PVC_EG_TAG(x)                 (((x) & 0x7) << 8)
+#define  PVC_EG_TAG_MASK               PVC_EG_TAG(7)
 #define  VLAN_ATTR(x)                  (((x) & 0x3) << 6)
 #define  VLAN_ATTR_MASK                        VLAN_ATTR(3)
 
+enum mt7530_vlan_port_eg_tag {
+       MT7530_VLAN_EG_DISABLED = 0,
+       MT7530_VLAN_EG_CONSISTENT = 1,
+};
+
 enum mt7530_vlan_port_attr {
        MT7530_VLAN_USER = 0,
        MT7530_VLAN_TRANSPARENT = 3,
@@ -277,7 +284,6 @@ enum mt7530_vlan_port_attr {
 
 /* Registers for TRGMII on the both side */
 #define MT7530_TRGMII_RCK_CTRL         0x7a00
-#define GSW_TRGMII_RCK_CTRL            0x300
 #define  RX_RST                                BIT(31)
 #define  RXC_DQSISEL                   BIT(30)
 #define  DQSI1_TAP_MASK                        (0x7f << 8)
@@ -286,31 +292,24 @@ enum mt7530_vlan_port_attr {
 #define  DQSI0_TAP(x)                  ((x) & 0x7f)
 
 #define MT7530_TRGMII_RCK_RTT          0x7a04
-#define GSW_TRGMII_RCK_RTT             0x304
 #define  DQS1_GATE                     BIT(31)
 #define  DQS0_GATE                     BIT(30)
 
 #define MT7530_TRGMII_RD(x)            (0x7a10 + (x) * 8)
-#define GSW_TRGMII_RD(x)               (0x310 + (x) * 8)
 #define  BSLIP_EN                      BIT(31)
 #define  EDGE_CHK                      BIT(30)
 #define  RD_TAP_MASK                   0x7f
 #define  RD_TAP(x)                     ((x) & 0x7f)
 
-#define GSW_TRGMII_TXCTRL              0x340
 #define MT7530_TRGMII_TXCTRL           0x7a40
 #define  TRAIN_TXEN                    BIT(31)
 #define  TXC_INV                       BIT(30)
 #define  TX_RST                                BIT(28)
 
 #define MT7530_TRGMII_TD_ODT(i)                (0x7a54 + 8 * (i))
-#define GSW_TRGMII_TD_ODT(i)           (0x354 + 8 * (i))
 #define  TD_DM_DRVP(x)                 ((x) & 0xf)
 #define  TD_DM_DRVN(x)                 (((x) & 0xf) << 4)
 
-#define GSW_INTF_MODE                  0x390
-#define  INTF_MODE_TRGMII              BIT(1)
-
 #define MT7530_TRGMII_TCK_CTRL         0x7a78
 #define  TCK_TAP(x)                    (((x) & 0xf) << 8)
 
@@ -443,7 +442,6 @@ static const char *p5_intf_modes(unsigned int p5_interface)
  * @ds:                        The pointer to the dsa core structure
  * @bus:               The bus used for the device and built-in PHY
  * @rstc:              The pointer to reset control used by MCM
- * @ethernet:          The regmap used for access TRGMII-based registers
  * @core_pwr:          The power supplied into the core
  * @io_pwr:            The power supplied into the I/O
  * @reset:             The descriptor for GPIO line tied to its reset pin
@@ -460,7 +458,6 @@ struct mt7530_priv {
        struct dsa_switch       *ds;
        struct mii_bus          *bus;
        struct reset_control    *rstc;
-       struct regmap           *ethernet;
        struct regulator        *core_pwr;
        struct regulator        *io_pwr;
        struct gpio_desc        *reset;
index 6435020..51185e4 100644 (file)
@@ -24,8 +24,8 @@ config NET_DSA_MV88E6XXX_PTP
        bool "PTP support for Marvell 88E6xxx"
        default n
        depends on NET_DSA_MV88E6XXX_GLOBAL2
+       depends on PTP_1588_CLOCK
        imply NETWORK_PHY_TIMESTAMPING
-       imply PTP_1588_CLOCK
        help
          Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch
          chips that support it.
index 2215932..2b4a723 100644 (file)
@@ -709,7 +709,8 @@ static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
        ops = chip->info->ops;
 
        mv88e6xxx_reg_lock(chip);
-       if (!mv88e6xxx_port_ppu_updates(chip, port) && ops->port_set_link)
+       if ((!mv88e6xxx_port_ppu_updates(chip, port) ||
+            mode == MLO_AN_FIXED) && ops->port_set_link)
                err = ops->port_set_link(chip, port, LINK_FORCED_DOWN);
        mv88e6xxx_reg_unlock(chip);
 
@@ -731,7 +732,7 @@ static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
        ops = chip->info->ops;
 
        mv88e6xxx_reg_lock(chip);
-       if (!mv88e6xxx_port_ppu_updates(chip, port)) {
+       if (!mv88e6xxx_port_ppu_updates(chip, port) || mode == MLO_AN_FIXED) {
                /* FIXME: for an automedia port, should we force the link
                 * down here - what if the link comes up due to "other" media
                 * while we're bringing the port up, how is the exclusivity
@@ -3961,7 +3962,6 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
        .serdes_get_stats = mv88e6390_serdes_get_stats,
        .serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
        .serdes_get_regs = mv88e6390_serdes_get_regs,
-       .phylink_validate = mv88e6390_phylink_validate,
        .gpio_ops = &mv88e6352_gpio_ops,
        .phylink_validate = mv88e6390_phylink_validate,
 };
@@ -4020,7 +4020,6 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
        .serdes_get_stats = mv88e6390_serdes_get_stats,
        .serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
        .serdes_get_regs = mv88e6390_serdes_get_regs,
-       .phylink_validate = mv88e6390_phylink_validate,
        .gpio_ops = &mv88e6352_gpio_ops,
        .phylink_validate = mv88e6390x_phylink_validate,
 };
@@ -4078,7 +4077,6 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
        .serdes_get_stats = mv88e6390_serdes_get_stats,
        .serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
        .serdes_get_regs = mv88e6390_serdes_get_regs,
-       .phylink_validate = mv88e6390_phylink_validate,
        .avb_ops = &mv88e6390_avb_ops,
        .ptp_ops = &mv88e6352_ptp_ops,
        .phylink_validate = mv88e6390_phylink_validate,
@@ -4234,7 +4232,6 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
        .serdes_get_stats = mv88e6390_serdes_get_stats,
        .serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
        .serdes_get_regs = mv88e6390_serdes_get_regs,
-       .phylink_validate = mv88e6390_phylink_validate,
        .gpio_ops = &mv88e6352_gpio_ops,
        .avb_ops = &mv88e6390_avb_ops,
        .ptp_ops = &mv88e6352_ptp_ops,
index 2098f19..9c07b4f 100644 (file)
@@ -534,21 +534,21 @@ static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane,
        int err;
 
        err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
-                                   MV88E6390_PCS_CONTROL_1, &val);
+                                   MV88E6390_10G_CTRL1, &val);
 
        if (err)
                return err;
 
        if (up)
-               new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET |
-                                 MV88E6390_PCS_CONTROL_1_LOOPBACK |
-                                 MV88E6390_PCS_CONTROL_1_PDOWN);
+               new_val = val & ~(MDIO_CTRL1_RESET |
+                                 MDIO_PCS_CTRL1_LOOPBACK |
+                                 MDIO_CTRL1_LPOWER);
        else
-               new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN;
+               new_val = val | MDIO_CTRL1_LPOWER;
 
        if (val != new_val)
                err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
-                                            MV88E6390_PCS_CONTROL_1, new_val);
+                                            MV88E6390_10G_CTRL1, new_val);
 
        return err;
 }
@@ -748,8 +748,8 @@ int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
                                      MV88E6390_SGMII_BMCR, bmcr);
 }
 
-int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
-                                  u8 lane, struct phylink_link_state *state)
+static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip,
+       int port, u8 lane, struct phylink_link_state *state)
 {
        u16 lpa, status;
        int err;
@@ -771,6 +771,45 @@ int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
        return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
 }
 
+static int mv88e6390_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip,
+       int port, u8 lane, struct phylink_link_state *state)
+{
+       u16 status;
+       int err;
+
+       err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+                                   MV88E6390_10G_STAT1, &status);
+       if (err)
+               return err;
+
+       state->link = !!(status & MDIO_STAT1_LSTATUS);
+       if (state->link) {
+               state->speed = SPEED_10000;
+               state->duplex = DUPLEX_FULL;
+       }
+
+       return 0;
+}
+
+int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
+                                  u8 lane, struct phylink_link_state *state)
+{
+       switch (state->interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_1000BASEX:
+       case PHY_INTERFACE_MODE_2500BASEX:
+               return mv88e6390_serdes_pcs_get_state_sgmii(chip, port, lane,
+                                                           state);
+       case PHY_INTERFACE_MODE_XAUI:
+       case PHY_INTERFACE_MODE_RXAUI:
+               return mv88e6390_serdes_pcs_get_state_10g(chip, port, lane,
+                                                         state);
+
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
                                    u8 lane)
 {
index 7990cad..14315f2 100644 (file)
 #define MV88E6390_PORT10_LANE3         0x17
 
 /* 10GBASE-R and 10GBASE-X4/X2 */
-#define MV88E6390_PCS_CONTROL_1                0x1000
-#define MV88E6390_PCS_CONTROL_1_RESET          BIT(15)
-#define MV88E6390_PCS_CONTROL_1_LOOPBACK       BIT(14)
-#define MV88E6390_PCS_CONTROL_1_SPEED          BIT(13)
-#define MV88E6390_PCS_CONTROL_1_PDOWN          BIT(11)
+#define MV88E6390_10G_CTRL1            (0x1000 + MDIO_CTRL1)
+#define MV88E6390_10G_STAT1            (0x1000 + MDIO_STAT1)
 
 /* 1000BASE-X and SGMII */
 #define MV88E6390_SGMII_BMCR           (0x2000 + MII_BMCR)
index 79ca3aa..a2dfd73 100644 (file)
@@ -7,6 +7,7 @@
 #include <soc/mscc/ocelot_sys.h>
 #include <soc/mscc/ocelot_dev.h>
 #include <soc/mscc/ocelot_ana.h>
+#include <soc/mscc/ocelot_ptp.h>
 #include <soc/mscc/ocelot.h>
 #include <linux/packing.h>
 #include <linux/module.h>
@@ -46,11 +47,8 @@ static int felix_fdb_add(struct dsa_switch *ds, int port,
                         const unsigned char *addr, u16 vid)
 {
        struct ocelot *ocelot = ds->priv;
-       bool vlan_aware;
 
-       vlan_aware = dsa_port_is_vlan_filtering(dsa_to_port(ds, port));
-
-       return ocelot_fdb_add(ocelot, port, addr, vid, vlan_aware);
+       return ocelot_fdb_add(ocelot, port, addr, vid);
 }
 
 static int felix_fdb_del(struct dsa_switch *ds, int port,
@@ -403,6 +401,7 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
        ocelot->stats_layout    = felix->info->stats_layout;
        ocelot->num_stats       = felix->info->num_stats;
        ocelot->shared_queue_sz = felix->info->shared_queue_sz;
+       ocelot->num_mact_rows   = felix->info->num_mact_rows;
        ocelot->vcap_is2_keys   = felix->info->vcap_is2_keys;
        ocelot->vcap_is2_actions= felix->info->vcap_is2_actions;
        ocelot->vcap            = felix->info->vcap;
@@ -497,6 +496,23 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
        return 0;
 }
 
+static struct ptp_clock_info ocelot_ptp_clock_info = {
+       .owner          = THIS_MODULE,
+       .name           = "felix ptp",
+       .max_adj        = 0x7fffffff,
+       .n_alarm        = 0,
+       .n_ext_ts       = 0,
+       .n_per_out      = OCELOT_PTP_PINS_NUM,
+       .n_pins         = OCELOT_PTP_PINS_NUM,
+       .pps            = 0,
+       .gettime64      = ocelot_ptp_gettime64,
+       .settime64      = ocelot_ptp_settime64,
+       .adjtime        = ocelot_ptp_adjtime,
+       .adjfine        = ocelot_ptp_adjfine,
+       .verify         = ocelot_ptp_verify,
+       .enable         = ocelot_ptp_enable,
+};
+
 /* Hardware initialization done here so that we can allocate structures with
  * devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing
  * us to allocate structures twice (leak memory) and map PCI memory twice
@@ -507,12 +523,21 @@ static int felix_setup(struct dsa_switch *ds)
        struct ocelot *ocelot = ds->priv;
        struct felix *felix = ocelot_to_felix(ocelot);
        int port, err;
+       int tc;
 
        err = felix_init_structs(felix, ds->num_ports);
        if (err)
                return err;
 
        ocelot_init(ocelot);
+       if (ocelot->ptp) {
+               err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
+               if (err) {
+                       dev_err(ocelot->dev,
+                               "Timestamp initialization failed\n");
+                       ocelot->ptp = 0;
+               }
+       }
 
        for (port = 0; port < ds->num_ports; port++) {
                ocelot_init_port(ocelot, port);
@@ -532,6 +557,12 @@ static int felix_setup(struct dsa_switch *ds)
        ocelot_write_rix(ocelot,
                         ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports, 0)),
                         ANA_PGID_PGID, PGID_UC);
+       /* Setup the per-traffic class flooding PGIDs */
+       for (tc = 0; tc < FELIX_NUM_TC; tc++)
+               ocelot_write_rix(ocelot, ANA_FLOODING_FLD_MULTICAST(PGID_MC) |
+                                ANA_FLOODING_FLD_BROADCAST(PGID_MC) |
+                                ANA_FLOODING_FLD_UNICAST(PGID_UC),
+                                ANA_FLOODING, tc);
 
        ds->mtu_enforcement_ingress = true;
        /* It looks like the MAC/PCS interrupt register - PM0_IEVENT (0x8040)
@@ -551,6 +582,7 @@ static void felix_teardown(struct dsa_switch *ds)
        if (felix->info->mdio_bus_free)
                felix->info->mdio_bus_free(ocelot);
 
+       ocelot_deinit_timestamp(ocelot);
        /* stop workqueue thread */
        ocelot_deinit(ocelot);
 }
@@ -742,6 +774,11 @@ static int felix_pci_probe(struct pci_dev *pdev,
        struct felix *felix;
        int err;
 
+       if (pdev->dev.of_node && !of_device_is_available(pdev->dev.of_node)) {
+               dev_info(&pdev->dev, "device is disabled, skipping\n");
+               return -ENODEV;
+       }
+
        err = pci_enable_device(pdev);
        if (err) {
                dev_err(&pdev->dev, "device enable failed\n");
index 82d46f2..b94386f 100644 (file)
@@ -5,6 +5,7 @@
 #define _MSCC_FELIX_H
 
 #define ocelot_to_felix(o)             container_of((o), struct felix, ocelot)
+#define FELIX_NUM_TC                   8
 
 /* Platform-specific information */
 struct felix_info {
@@ -15,6 +16,7 @@ struct felix_info {
        const u32 *const                *map;
        const struct ocelot_ops         *ops;
        int                             shared_queue_sz;
+       int                             num_mact_rows;
        const struct ocelot_stat_layout *stats_layout;
        unsigned int                    num_stats;
        int                             num_ports;
index b4078f3..1c56568 100644 (file)
@@ -313,6 +313,8 @@ static const u32 vsc9959_ptp_regmap[] = {
        REG(PTP_PIN_TOD_SEC_MSB,           0x000004),
        REG(PTP_PIN_TOD_SEC_LSB,           0x000008),
        REG(PTP_PIN_TOD_NSEC,              0x00000c),
+       REG(PTP_PIN_WF_HIGH_PERIOD,        0x000014),
+       REG(PTP_PIN_WF_LOW_PERIOD,         0x000018),
        REG(PTP_CFG_MISC,                  0x0000a0),
        REG(PTP_CLK_CFG_ADJ_CFG,           0x0000a4),
        REG(PTP_CLK_CFG_ADJ_FREQ,          0x0000a8),
@@ -1220,6 +1222,7 @@ struct felix_info felix_info_vsc9959 = {
        .vcap_is2_actions       = vsc9959_vcap_is2_actions,
        .vcap                   = vsc9959_vcap_props,
        .shared_queue_sz        = 128 * 1024,
+       .num_mact_rows          = 2048,
        .num_ports              = 6,
        .switch_pci_bar         = 4,
        .imdio_pci_bar          = 0,
index 0fe1ae1..68c3086 100644 (file)
@@ -20,6 +20,7 @@ tristate "NXP SJA1105 Ethernet switch family support"
 config NET_DSA_SJA1105_PTP
        bool "Support for the PTP clock on the NXP SJA1105 Ethernet switch"
        depends on NET_DSA_SJA1105
+       depends on PTP_1588_CLOCK
        help
          This enables support for timestamping and PTP clock manipulations in
          the SJA1105 DSA driver.
index 8b60dbd..2f62942 100644 (file)
@@ -49,6 +49,7 @@ struct sja1105_regs {
        u64 ptpschtm;
        u64 ptpegr_ts[SJA1105_NUM_PORTS];
        u64 pad_mii_tx[SJA1105_NUM_PORTS];
+       u64 pad_mii_rx[SJA1105_NUM_PORTS];
        u64 pad_mii_id[SJA1105_NUM_PORTS];
        u64 cgu_idiv[SJA1105_NUM_PORTS];
        u64 mii_tx_clk[SJA1105_NUM_PORTS];
index 0fdc2d5..2a9b8a6 100644 (file)
@@ -7,12 +7,16 @@
 
 #define SJA1105_SIZE_CGU_CMD   4
 
-struct sja1105_cfg_pad_mii_tx {
+/* Common structure for CFG_PAD_MIIx_RX and CFG_PAD_MIIx_TX */
+struct sja1105_cfg_pad_mii {
        u64 d32_os;
+       u64 d32_ih;
        u64 d32_ipud;
+       u64 d10_ih;
        u64 d10_os;
        u64 d10_ipud;
        u64 ctrl_os;
+       u64 ctrl_ih;
        u64 ctrl_ipud;
        u64 clk_os;
        u64 clk_ih;
@@ -338,16 +342,19 @@ static int sja1105_cgu_rgmii_tx_clk_config(struct sja1105_private *priv,
 
 /* AGU */
 static void
-sja1105_cfg_pad_mii_tx_packing(void *buf, struct sja1105_cfg_pad_mii_tx *cmd,
-                              enum packing_op op)
+sja1105_cfg_pad_mii_packing(void *buf, struct sja1105_cfg_pad_mii *cmd,
+                           enum packing_op op)
 {
        const int size = 4;
 
        sja1105_packing(buf, &cmd->d32_os,   28, 27, size, op);
+       sja1105_packing(buf, &cmd->d32_ih,   26, 26, size, op);
        sja1105_packing(buf, &cmd->d32_ipud, 25, 24, size, op);
        sja1105_packing(buf, &cmd->d10_os,   20, 19, size, op);
+       sja1105_packing(buf, &cmd->d10_ih,   18, 18, size, op);
        sja1105_packing(buf, &cmd->d10_ipud, 17, 16, size, op);
        sja1105_packing(buf, &cmd->ctrl_os,  12, 11, size, op);
+       sja1105_packing(buf, &cmd->ctrl_ih,  10, 10, size, op);
        sja1105_packing(buf, &cmd->ctrl_ipud, 9,  8, size, op);
        sja1105_packing(buf, &cmd->clk_os,    4,  3, size, op);
        sja1105_packing(buf, &cmd->clk_ih,    2,  2, size, op);
@@ -358,7 +365,7 @@ static int sja1105_rgmii_cfg_pad_tx_config(struct sja1105_private *priv,
                                           int port)
 {
        const struct sja1105_regs *regs = priv->info->regs;
-       struct sja1105_cfg_pad_mii_tx pad_mii_tx;
+       struct sja1105_cfg_pad_mii pad_mii_tx = {0};
        u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
 
        /* Payload */
@@ -375,12 +382,45 @@ static int sja1105_rgmii_cfg_pad_tx_config(struct sja1105_private *priv,
        pad_mii_tx.clk_os    = 3; /* TX_CLK output stage */
        pad_mii_tx.clk_ih    = 0; /* TX_CLK input hysteresis (default) */
        pad_mii_tx.clk_ipud  = 2; /* TX_CLK input stage (default) */
-       sja1105_cfg_pad_mii_tx_packing(packed_buf, &pad_mii_tx, PACK);
+       sja1105_cfg_pad_mii_packing(packed_buf, &pad_mii_tx, PACK);
 
        return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_tx[port],
                                packed_buf, SJA1105_SIZE_CGU_CMD);
 }
 
+static int sja1105_cfg_pad_rx_config(struct sja1105_private *priv, int port)
+{
+       const struct sja1105_regs *regs = priv->info->regs;
+       struct sja1105_cfg_pad_mii pad_mii_rx = {0};
+       u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0};
+
+       /* Payload */
+       pad_mii_rx.d32_ih    = 0; /* RXD[3:2] input stage hysteresis: */
+                                 /*          non-Schmitt (default) */
+       pad_mii_rx.d32_ipud  = 2; /* RXD[3:2] input weak pull-up/down */
+                                 /*          plain input (default) */
+       pad_mii_rx.d10_ih    = 0; /* RXD[1:0] input stage hysteresis: */
+                                 /*          non-Schmitt (default) */
+       pad_mii_rx.d10_ipud  = 2; /* RXD[1:0] input weak pull-up/down */
+                                 /*          plain input (default) */
+       pad_mii_rx.ctrl_ih   = 0; /* RX_DV/CRS_DV/RX_CTL and RX_ER */
+                                 /* input stage hysteresis: */
+                                 /* non-Schmitt (default) */
+       pad_mii_rx.ctrl_ipud = 3; /* RX_DV/CRS_DV/RX_CTL and RX_ER */
+                                 /* input stage weak pull-up/down: */
+                                 /* pull-down */
+       pad_mii_rx.clk_os    = 2; /* RX_CLK/RXC output stage: */
+                                 /* medium noise/fast speed (default) */
+       pad_mii_rx.clk_ih    = 0; /* RX_CLK/RXC input hysteresis: */
+                                 /* non-Schmitt (default) */
+       pad_mii_rx.clk_ipud  = 2; /* RX_CLK/RXC input pull-up/down: */
+                                 /* plain input (default) */
+       sja1105_cfg_pad_mii_packing(packed_buf, &pad_mii_rx, PACK);
+
+       return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_rx[port],
+                               packed_buf, SJA1105_SIZE_CGU_CMD);
+}
+
 static void
 sja1105_cfg_pad_mii_id_packing(void *buf, struct sja1105_cfg_pad_mii_id *cmd,
                               enum packing_op op)
@@ -669,10 +709,14 @@ int sja1105_clocking_setup_port(struct sja1105_private *priv, int port)
                        phy_mode);
                return -EINVAL;
        }
-       if (rc)
+       if (rc) {
                dev_err(dev, "Clocking setup for port %d failed: %d\n",
                        port, rc);
-       return rc;
+               return rc;
+       }
+
+       /* Internally pull down the RX_DV/CRS_DV/RX_CTL and RX_ER inputs */
+       return sja1105_cfg_pad_rx_config(priv, port);
 }
 
 int sja1105_clocking_setup(struct sja1105_private *priv)
index d742ffc..709f035 100644 (file)
@@ -421,92 +421,96 @@ static char sja1105pqrs_extra_port_stats[][ETH_GSTRING_LEN] = {
 void sja1105_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data)
 {
        struct sja1105_private *priv = ds->priv;
-       struct sja1105_port_status status;
+       struct sja1105_port_status *status;
        int rc, i, k = 0;
 
-       memset(&status, 0, sizeof(status));
+       status = kzalloc(sizeof(*status), GFP_KERNEL);
+       if (!status)
+               goto out;
 
-       rc = sja1105_port_status_get(priv, &status, port);
+       rc = sja1105_port_status_get(priv, status, port);
        if (rc < 0) {
                dev_err(ds->dev, "Failed to read port %d counters: %d\n",
                        port, rc);
-               return;
+               goto out;
        }
        memset(data, 0, ARRAY_SIZE(sja1105_port_stats) * sizeof(u64));
-       data[k++] = status.mac.n_runt;
-       data[k++] = status.mac.n_soferr;
-       data[k++] = status.mac.n_alignerr;
-       data[k++] = status.mac.n_miierr;
-       data[k++] = status.mac.typeerr;
-       data[k++] = status.mac.sizeerr;
-       data[k++] = status.mac.tctimeout;
-       data[k++] = status.mac.priorerr;
-       data[k++] = status.mac.nomaster;
-       data[k++] = status.mac.memov;
-       data[k++] = status.mac.memerr;
-       data[k++] = status.mac.invtyp;
-       data[k++] = status.mac.intcyov;
-       data[k++] = status.mac.domerr;
-       data[k++] = status.mac.pcfbagdrop;
-       data[k++] = status.mac.spcprior;
-       data[k++] = status.mac.ageprior;
-       data[k++] = status.mac.portdrop;
-       data[k++] = status.mac.lendrop;
-       data[k++] = status.mac.bagdrop;
-       data[k++] = status.mac.policeerr;
-       data[k++] = status.mac.drpnona664err;
-       data[k++] = status.mac.spcerr;
-       data[k++] = status.mac.agedrp;
-       data[k++] = status.hl1.n_n664err;
-       data[k++] = status.hl1.n_vlanerr;
-       data[k++] = status.hl1.n_unreleased;
-       data[k++] = status.hl1.n_sizeerr;
-       data[k++] = status.hl1.n_crcerr;
-       data[k++] = status.hl1.n_vlnotfound;
-       data[k++] = status.hl1.n_ctpolerr;
-       data[k++] = status.hl1.n_polerr;
-       data[k++] = status.hl1.n_rxfrm;
-       data[k++] = status.hl1.n_rxbyte;
-       data[k++] = status.hl1.n_txfrm;
-       data[k++] = status.hl1.n_txbyte;
-       data[k++] = status.hl2.n_qfull;
-       data[k++] = status.hl2.n_part_drop;
-       data[k++] = status.hl2.n_egr_disabled;
-       data[k++] = status.hl2.n_not_reach;
+       data[k++] = status->mac.n_runt;
+       data[k++] = status->mac.n_soferr;
+       data[k++] = status->mac.n_alignerr;
+       data[k++] = status->mac.n_miierr;
+       data[k++] = status->mac.typeerr;
+       data[k++] = status->mac.sizeerr;
+       data[k++] = status->mac.tctimeout;
+       data[k++] = status->mac.priorerr;
+       data[k++] = status->mac.nomaster;
+       data[k++] = status->mac.memov;
+       data[k++] = status->mac.memerr;
+       data[k++] = status->mac.invtyp;
+       data[k++] = status->mac.intcyov;
+       data[k++] = status->mac.domerr;
+       data[k++] = status->mac.pcfbagdrop;
+       data[k++] = status->mac.spcprior;
+       data[k++] = status->mac.ageprior;
+       data[k++] = status->mac.portdrop;
+       data[k++] = status->mac.lendrop;
+       data[k++] = status->mac.bagdrop;
+       data[k++] = status->mac.policeerr;
+       data[k++] = status->mac.drpnona664err;
+       data[k++] = status->mac.spcerr;
+       data[k++] = status->mac.agedrp;
+       data[k++] = status->hl1.n_n664err;
+       data[k++] = status->hl1.n_vlanerr;
+       data[k++] = status->hl1.n_unreleased;
+       data[k++] = status->hl1.n_sizeerr;
+       data[k++] = status->hl1.n_crcerr;
+       data[k++] = status->hl1.n_vlnotfound;
+       data[k++] = status->hl1.n_ctpolerr;
+       data[k++] = status->hl1.n_polerr;
+       data[k++] = status->hl1.n_rxfrm;
+       data[k++] = status->hl1.n_rxbyte;
+       data[k++] = status->hl1.n_txfrm;
+       data[k++] = status->hl1.n_txbyte;
+       data[k++] = status->hl2.n_qfull;
+       data[k++] = status->hl2.n_part_drop;
+       data[k++] = status->hl2.n_egr_disabled;
+       data[k++] = status->hl2.n_not_reach;
 
        if (priv->info->device_id == SJA1105E_DEVICE_ID ||
            priv->info->device_id == SJA1105T_DEVICE_ID)
-               return;
+               goto out;;
 
        memset(data + k, 0, ARRAY_SIZE(sja1105pqrs_extra_port_stats) *
                        sizeof(u64));
        for (i = 0; i < 8; i++) {
-               data[k++] = status.hl2.qlevel_hwm[i];
-               data[k++] = status.hl2.qlevel[i];
+               data[k++] = status->hl2.qlevel_hwm[i];
+               data[k++] = status->hl2.qlevel[i];
        }
-       data[k++] = status.ether.n_drops_nolearn;
-       data[k++] = status.ether.n_drops_noroute;
-       data[k++] = status.ether.n_drops_ill_dtag;
-       data[k++] = status.ether.n_drops_dtag;
-       data[k++] = status.ether.n_drops_sotag;
-       data[k++] = status.ether.n_drops_sitag;
-       data[k++] = status.ether.n_drops_utag;
-       data[k++] = status.ether.n_tx_bytes_1024_2047;
-       data[k++] = status.ether.n_tx_bytes_512_1023;
-       data[k++] = status.ether.n_tx_bytes_256_511;
-       data[k++] = status.ether.n_tx_bytes_128_255;
-       data[k++] = status.ether.n_tx_bytes_65_127;
-       data[k++] = status.ether.n_tx_bytes_64;
-       data[k++] = status.ether.n_tx_mcast;
-       data[k++] = status.ether.n_tx_bcast;
-       data[k++] = status.ether.n_rx_bytes_1024_2047;
-       data[k++] = status.ether.n_rx_bytes_512_1023;
-       data[k++] = status.ether.n_rx_bytes_256_511;
-       data[k++] = status.ether.n_rx_bytes_128_255;
-       data[k++] = status.ether.n_rx_bytes_65_127;
-       data[k++] = status.ether.n_rx_bytes_64;
-       data[k++] = status.ether.n_rx_mcast;
-       data[k++] = status.ether.n_rx_bcast;
+       data[k++] = status->ether.n_drops_nolearn;
+       data[k++] = status->ether.n_drops_noroute;
+       data[k++] = status->ether.n_drops_ill_dtag;
+       data[k++] = status->ether.n_drops_dtag;
+       data[k++] = status->ether.n_drops_sotag;
+       data[k++] = status->ether.n_drops_sitag;
+       data[k++] = status->ether.n_drops_utag;
+       data[k++] = status->ether.n_tx_bytes_1024_2047;
+       data[k++] = status->ether.n_tx_bytes_512_1023;
+       data[k++] = status->ether.n_tx_bytes_256_511;
+       data[k++] = status->ether.n_tx_bytes_128_255;
+       data[k++] = status->ether.n_tx_bytes_65_127;
+       data[k++] = status->ether.n_tx_bytes_64;
+       data[k++] = status->ether.n_tx_mcast;
+       data[k++] = status->ether.n_tx_bcast;
+       data[k++] = status->ether.n_rx_bytes_1024_2047;
+       data[k++] = status->ether.n_rx_bytes_512_1023;
+       data[k++] = status->ether.n_rx_bytes_256_511;
+       data[k++] = status->ether.n_rx_bytes_128_255;
+       data[k++] = status->ether.n_rx_bytes_65_127;
+       data[k++] = status->ether.n_rx_bytes_64;
+       data[k++] = status->ether.n_rx_mcast;
+       data[k++] = status->ether.n_rx_bcast;
+out:
+       kfree(status);
 }
 
 void sja1105_get_strings(struct dsa_switch *ds, int port,
index a22f8e3..bc0e47c 100644 (file)
 
 /* PTPSYNCTS has no interrupt or update mechanism, because the intended
  * hardware use case is for the timestamp to be collected synchronously,
- * immediately after the CAS_MASTER SJA1105 switch has triggered a CASSYNC
- * pulse on the PTP_CLK pin. When used as a generic extts source, it needs
- * polling and a comparison with the old value. The polling interval is just
- * the Nyquist rate of a canonical PPS input (e.g. from a GPS module).
- * Anything of higher frequency than 1 Hz will be lost, since there is no
- * timestamp FIFO.
+ * immediately after the CAS_MASTER SJA1105 switch has performed a CASSYNC
+ * one-shot toggle (no return to level) on the PTP_CLK pin. When used as a
+ * generic extts source, the PTPSYNCTS register needs polling and a comparison
+ * with the old value. The polling interval is configured as the Nyquist rate
+ * of a signal with 50% duty cycle and 1Hz frequency, which is sadly all that
+ * this hardware can do (but may be enough for some setups). Anything of higher
+ * frequency than 1 Hz will be lost, since there is no timestamp FIFO.
  */
-#define SJA1105_EXTTS_INTERVAL         (HZ / 2)
+#define SJA1105_EXTTS_INTERVAL         (HZ / 4)
 
 /*            This range is actually +/- SJA1105_MAX_ADJ_PPB
  *            divided by 1000 (ppb -> ppm) and with a 16-bit
@@ -754,7 +755,16 @@ static int sja1105_extts_enable(struct sja1105_private *priv,
                return -EOPNOTSUPP;
 
        /* Reject requests with unsupported flags */
-       if (extts->flags)
+       if (extts->flags & ~(PTP_ENABLE_FEATURE |
+                            PTP_RISING_EDGE |
+                            PTP_FALLING_EDGE |
+                            PTP_STRICT_FLAGS))
+               return -EOPNOTSUPP;
+
+       /* We can only enable time stamping on both edges, sadly. */
+       if ((extts->flags & PTP_STRICT_FLAGS) &&
+           (extts->flags & PTP_ENABLE_FEATURE) &&
+           (extts->flags & PTP_EXTTS_EDGES) != PTP_EXTTS_EDGES)
                return -EOPNOTSUPP;
 
        rc = sja1105_change_ptp_clk_pin_func(priv, PTP_PF_EXTTS);
index 04bdb72..43f14a5 100644 (file)
@@ -443,6 +443,7 @@ static struct sja1105_regs sja1105et_regs = {
        .rgu = 0x100440,
        /* UM10944.pdf, Table 86, ACU Register overview */
        .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808},
+       .pad_mii_rx = {0x100801, 0x100803, 0x100805, 0x100807, 0x100809},
        .rmii_pll1 = 0x10000A,
        .cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F},
        .mac = {0x200, 0x202, 0x204, 0x206, 0x208},
@@ -475,6 +476,7 @@ static struct sja1105_regs sja1105pqrs_regs = {
        .rgu = 0x100440,
        /* UM10944.pdf, Table 86, ACU Register overview */
        .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808},
+       .pad_mii_rx = {0x100801, 0x100803, 0x100805, 0x100807, 0x100809},
        .pad_mii_id = {0x100810, 0x100811, 0x100812, 0x100813, 0x100814},
        .sgmii = 0x1F0000,
        .rmii_pll1 = 0x10000A,
index b762176..139d012 100644 (file)
@@ -85,7 +85,6 @@
 #include <linux/device.h>
 #include <linux/eisa.h>
 #include <linux/bitops.h>
-#include <linux/vermagic.h>
 
 #include <linux/uaccess.h>
 #include <asm/io.h>
index 90312fc..47b4215 100644 (file)
@@ -22,7 +22,6 @@
 
 */
 
-#include <linux/vermagic.h>
 #define DRV_NAME               "3c515"
 
 #define CORKSCREW 1
index a2b7f7a..5984b70 100644 (file)
@@ -1149,7 +1149,7 @@ static int vortex_probe1(struct device *gendev, void __iomem *ioaddr, int irq,
 
        print_info = (vortex_debug > 1);
        if (print_info)
-               pr_info("See Documentation/networking/device_drivers/3com/vortex.txt\n");
+               pr_info("See Documentation/networking/device_drivers/3com/vortex.rst\n");
 
        pr_info("%s: 3Com %s %s at %p.\n",
               print_name,
@@ -1954,7 +1954,7 @@ vortex_error(struct net_device *dev, int status)
                                   dev->name, tx_status);
                        if (tx_status == 0x82) {
                                pr_err("Probably a duplex mismatch.  See "
-                                               "Documentation/networking/device_drivers/3com/vortex.txt\n");
+                                               "Documentation/networking/device_drivers/3com/vortex.rst\n");
                        }
                        dump_tx_ring(dev);
                }
index 3a6fc99..7cc2598 100644 (file)
@@ -76,7 +76,7 @@ config VORTEX
          "Hurricane" (3c555/3cSOHO)                           PCI
 
          If you have such a card, say Y here.  More specific information is in
-         <file:Documentation/networking/device_drivers/3com/vortex.txt> and
+         <file:Documentation/networking/device_drivers/3com/vortex.rst> and
          in the comments at the beginning of
          <file:drivers/net/ethernet/3com/3c59x.c>.
 
index 2db4221..a64191f 100644 (file)
@@ -45,7 +45,6 @@
 #include <asm/processor.h>             /* Processor type for cache alignment. */
 #include <linux/uaccess.h>
 #include <asm/io.h>
-#include <linux/vermagic.h>
 
 /*
  * The current frame processor firmware fails to checksum a fragment
index 1b19385..865892c 100644 (file)
@@ -714,11 +714,11 @@ static int et131x_init_eeprom(struct et131x_adapter *adapter)
                         * gather additional information that normally would
                         * come from the eeprom, like MAC Address
                         */
-                       adapter->has_eeprom = 0;
+                       adapter->has_eeprom = false;
                        return -EIO;
                }
        }
-       adapter->has_eeprom = 1;
+       adapter->has_eeprom = true;
 
        /* Read the EEPROM for information regarding LED behavior. Refer to
         * et131x_xcvr_init() for its use.
index 18d3b43..b3b8a80 100644 (file)
@@ -417,7 +417,7 @@ static void emac_timeout(struct net_device *dev, unsigned int txqueue)
 /* Hardware start transmission.
  * Send a packet to media from the upper layer.
  */
-static int emac_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t emac_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct emac_board_info *db = netdev_priv(dev);
        unsigned long channel;
@@ -425,7 +425,7 @@ static int emac_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        channel = db->tx_fifo_stat & 3;
        if (channel == 3)
-               return 1;
+               return NETDEV_TX_BUSY;
 
        channel = (channel == 1 ? 1 : 0);
 
index 1671c1f..907125a 100644 (file)
@@ -554,7 +554,7 @@ static irqreturn_t altera_isr(int irq, void *dev_id)
  * physically contiguous fragment starting at
  * skb->data, for length of skb_headlen(skb).
  */
-static int tse_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t tse_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct altera_tse_private *priv = netdev_priv(dev);
        unsigned int txsize = priv->tx_ring_size;
@@ -562,7 +562,7 @@ static int tse_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct tse_buffer *buffer = NULL;
        int nfrags = skb_shinfo(skb)->nr_frags;
        unsigned int nopaged_len = skb_headlen(skb);
-       enum netdev_tx ret = NETDEV_TX_OK;
+       netdev_tx_t ret = NETDEV_TX_OK;
        dma_addr_t dma_addr;
 
        spin_lock_bh(&priv->tx_lock);
index 8baf847..7be3dcb 100644 (file)
@@ -404,6 +404,10 @@ struct ena_admin_basic_stats {
        u32 rx_drops_low;
 
        u32 rx_drops_high;
+
+       u32 tx_drops_low;
+
+       u32 tx_drops_high;
 };
 
 struct ena_admin_acq_get_stats_resp {
@@ -1017,6 +1021,10 @@ struct ena_admin_aenq_keep_alive_desc {
        u32 rx_drops_low;
 
        u32 rx_drops_high;
+
+       u32 tx_drops_low;
+
+       u32 tx_drops_high;
 };
 
 struct ena_admin_ena_mmio_req_read_less_resp {
index a250046..b51bf62 100644 (file)
@@ -1067,16 +1067,10 @@ static void ena_com_hash_key_fill_default_key(struct ena_com_dev *ena_dev)
 static int ena_com_hash_key_allocate(struct ena_com_dev *ena_dev)
 {
        struct ena_rss *rss = &ena_dev->rss;
-       struct ena_admin_get_feat_resp get_resp;
-       int rc;
 
-       rc = ena_com_get_feature_ex(ena_dev, &get_resp,
-                                   ENA_ADMIN_RSS_HASH_FUNCTION,
-                                   ena_dev->rss.hash_key_dma_addr,
-                                   sizeof(ena_dev->rss.hash_key), 0);
-       if (unlikely(rc)) {
+       if (!ena_com_check_supported_feature_id(ena_dev,
+                                               ENA_ADMIN_RSS_HASH_FUNCTION))
                return -EOPNOTSUPP;
-       }
 
        rss->hash_key =
                dma_alloc_coherent(ena_dev->dmadev, sizeof(*rss->hash_key),
@@ -2286,6 +2280,7 @@ int ena_com_fill_hash_function(struct ena_com_dev *ena_dev,
        struct ena_admin_get_feat_resp get_resp;
        struct ena_admin_feature_rss_flow_hash_control *hash_key =
                rss->hash_key;
+       enum ena_admin_hash_functions old_func;
        int rc;
 
        /* Make sure size is a mult of DWs */
@@ -2325,26 +2320,27 @@ int ena_com_fill_hash_function(struct ena_com_dev *ena_dev,
                return -EINVAL;
        }
 
+       old_func = rss->hash_func;
        rss->hash_func = func;
        rc = ena_com_set_hash_function(ena_dev);
 
        /* Restore the old function */
        if (unlikely(rc))
-               ena_com_get_hash_function(ena_dev, NULL, NULL);
+               rss->hash_func = old_func;
 
        return rc;
 }
 
 int ena_com_get_hash_function(struct ena_com_dev *ena_dev,
-                             enum ena_admin_hash_functions *func,
-                             u8 *key)
+                             enum ena_admin_hash_functions *func)
 {
        struct ena_rss *rss = &ena_dev->rss;
        struct ena_admin_get_feat_resp get_resp;
-       struct ena_admin_feature_rss_flow_hash_control *hash_key =
-               rss->hash_key;
        int rc;
 
+       if (unlikely(!func))
+               return -EINVAL;
+
        rc = ena_com_get_feature_ex(ena_dev, &get_resp,
                                    ENA_ADMIN_RSS_HASH_FUNCTION,
                                    rss->hash_key_dma_addr,
@@ -2357,8 +2353,15 @@ int ena_com_get_hash_function(struct ena_com_dev *ena_dev,
        if (rss->hash_func)
                rss->hash_func--;
 
-       if (func)
-               *func = rss->hash_func;
+       *func = rss->hash_func;
+
+       return 0;
+}
+
+int ena_com_get_hash_key(struct ena_com_dev *ena_dev, u8 *key)
+{
+       struct ena_admin_feature_rss_flow_hash_control *hash_key =
+               ena_dev->rss.hash_key;
 
        if (key)
                memcpy(key, hash_key->key, (size_t)(hash_key->keys_num) << 2);
@@ -2641,10 +2644,10 @@ int ena_com_rss_init(struct ena_com_dev *ena_dev, u16 indr_tbl_log_size)
         * ignore this error and have indirection table support only.
         */
        rc = ena_com_hash_key_allocate(ena_dev);
-       if (unlikely(rc) && rc != -EOPNOTSUPP)
-               goto err_hash_key;
-       else if (rc != -EOPNOTSUPP)
+       if (likely(!rc))
                ena_com_hash_key_fill_default_key(ena_dev);
+       else if (rc != -EOPNOTSUPP)
+               goto err_hash_key;
 
        rc = ena_com_hash_ctrl_init(ena_dev);
        if (unlikely(rc))
index 469f298..13a1b78 100644 (file)
@@ -54,9 +54,9 @@
 #undef pr_fmt
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#define ENA_MAX_NUM_IO_QUEUES          128U
+#define ENA_MAX_NUM_IO_QUEUES 128U
 /* We need to queues for each IO (on for Tx and one for Rx) */
-#define ENA_TOTAL_NUM_QUEUES           (2 * (ENA_MAX_NUM_IO_QUEUES))
+#define ENA_TOTAL_NUM_QUEUES (2 * (ENA_MAX_NUM_IO_QUEUES))
 
 #define ENA_MAX_HANDLERS 256
 
 /*****************************************************************************/
 /* ENA adaptive interrupt moderation settings */
 
-#define ENA_INTR_INITIAL_TX_INTERVAL_USECS             64
-#define ENA_INTR_INITIAL_RX_INTERVAL_USECS             0
-#define ENA_DEFAULT_INTR_DELAY_RESOLUTION              1
+#define ENA_INTR_INITIAL_TX_INTERVAL_USECS 64
+#define ENA_INTR_INITIAL_RX_INTERVAL_USECS 0
+#define ENA_DEFAULT_INTR_DELAY_RESOLUTION 1
 
-#define ENA_HW_HINTS_NO_TIMEOUT                                0xFFFF
+#define ENA_HW_HINTS_NO_TIMEOUT        0xFFFF
 
-#define ENA_FEATURE_MAX_QUEUE_EXT_VER  1
+#define ENA_FEATURE_MAX_QUEUE_EXT_VER 1
 
 struct ena_llq_configurations {
        enum ena_admin_llq_header_location llq_header_location;
@@ -501,18 +501,6 @@ bool ena_com_get_admin_running_state(struct ena_com_dev *ena_dev);
  */
 void ena_com_set_admin_polling_mode(struct ena_com_dev *ena_dev, bool polling);
 
-/* ena_com_set_admin_polling_mode - Get the admin completion queue polling mode
- * @ena_dev: ENA communication layer struct
- *
- * Get the admin completion mode.
- * If polling mode is on, ena_com_execute_admin_command will perform a
- * polling on the admin completion queue for the commands completion,
- * otherwise it will wait on wait event.
- *
- * @return state
- */
-bool ena_com_get_ena_admin_polling_mode(struct ena_com_dev *ena_dev);
-
 /* ena_com_set_admin_auto_polling_mode - Enable autoswitch to polling mode
  * @ena_dev: ENA communication layer struct
  * @polling: Enable/Disable polling mode
@@ -695,13 +683,11 @@ int ena_com_fill_hash_function(struct ena_com_dev *ena_dev,
  */
 int ena_com_set_hash_function(struct ena_com_dev *ena_dev);
 
-/* ena_com_get_hash_function - Retrieve the hash function and the hash key
- * from the device.
+/* ena_com_get_hash_function - Retrieve the hash function from the device.
  * @ena_dev: ENA communication layer struct
  * @func: hash function
- * @key: hash key
  *
- * Retrieve the hash function and the hash key from the device.
+ * Retrieve the hash function from the device.
  *
  * @note: If the caller called ena_com_fill_hash_function but didn't flash
  * it to the device, the new configuration will be lost.
@@ -709,9 +695,20 @@ int ena_com_set_hash_function(struct ena_com_dev *ena_dev);
  * @return: 0 on Success and negative value otherwise.
  */
 int ena_com_get_hash_function(struct ena_com_dev *ena_dev,
-                             enum ena_admin_hash_functions *func,
-                             u8 *key);
+                             enum ena_admin_hash_functions *func);
 
+/* ena_com_get_hash_key - Retrieve the hash key
+ * @ena_dev: ENA communication layer struct
+ * @key: hash key
+ *
+ * Retrieve the hash key.
+ *
+ * @note: If the caller called ena_com_fill_hash_key but didn't flash
+ * it to the device, the new configuration will be lost.
+ *
+ * @return: 0 on Success and negative value otherwise.
+ */
+int ena_com_get_hash_key(struct ena_com_dev *ena_dev, u8 *key);
 /* ena_com_fill_hash_ctrl - Fill RSS hash control
  * @ena_dev: ENA communication layer struct.
  * @proto: The protocol to configure.
index 9cc28b4..830d371 100644 (file)
@@ -83,6 +83,7 @@ static const struct ena_stats ena_stats_tx_strings[] = {
        ENA_STAT_TX_ENTRY(bad_req_id),
        ENA_STAT_TX_ENTRY(llq_buffer_copy),
        ENA_STAT_TX_ENTRY(missed_tx),
+       ENA_STAT_TX_ENTRY(unmask_interrupt),
 };
 
 static const struct ena_stats ena_stats_rx_strings[] = {
@@ -635,6 +636,32 @@ static u32 ena_get_rxfh_key_size(struct net_device *netdev)
        return ENA_HASH_KEY_SIZE;
 }
 
+static int ena_indirection_table_set(struct ena_adapter *adapter,
+                                    const u32 *indir)
+{
+       struct ena_com_dev *ena_dev = adapter->ena_dev;
+       int i, rc;
+
+       for (i = 0; i < ENA_RX_RSS_TABLE_SIZE; i++) {
+               rc = ena_com_indirect_table_fill_entry(ena_dev,
+                                                      i,
+                                                      ENA_IO_RXQ_IDX(indir[i]));
+               if (unlikely(rc)) {
+                       netif_err(adapter, drv, adapter->netdev,
+                                 "Cannot fill indirect table (index is too large)\n");
+                       return rc;
+               }
+       }
+
+       rc = ena_com_indirect_table_set(ena_dev);
+       if (rc) {
+               netif_err(adapter, drv, adapter->netdev,
+                         "Cannot set indirect table\n");
+               return rc == -EPERM ? -EOPNOTSUPP : rc;
+       }
+       return rc;
+}
+
 static int ena_indirection_table_get(struct ena_adapter *adapter, u32 *indir)
 {
        struct ena_com_dev *ena_dev = adapter->ena_dev;
@@ -672,17 +699,18 @@ static int ena_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
        /* We call this function in order to check if the device
         * supports getting/setting the hash function.
         */
-       rc = ena_com_get_hash_function(adapter->ena_dev, &ena_func, key);
+       rc = ena_com_get_hash_function(adapter->ena_dev, &ena_func);
        if (rc) {
-               if (rc == -EOPNOTSUPP) {
-                       key = NULL;
-                       hfunc = NULL;
+               if (rc == -EOPNOTSUPP)
                        rc = 0;
-               }
 
                return rc;
        }
 
+       rc = ena_com_get_hash_key(adapter->ena_dev, key);
+       if (rc)
+               return rc;
+
        switch (ena_func) {
        case ENA_ADMIN_TOEPLITZ:
                func = ETH_RSS_HASH_TOP;
@@ -699,7 +727,7 @@ static int ena_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
        if (hfunc)
                *hfunc = func;
 
-       return rc;
+       return 0;
 }
 
 static int ena_set_rxfh(struct net_device *netdev, const u32 *indir,
@@ -707,27 +735,13 @@ static int ena_set_rxfh(struct net_device *netdev, const u32 *indir,
 {
        struct ena_adapter *adapter = netdev_priv(netdev);
        struct ena_com_dev *ena_dev = adapter->ena_dev;
-       enum ena_admin_hash_functions func;
-       int rc, i;
+       enum ena_admin_hash_functions func = 0;
+       int rc;
 
        if (indir) {
-               for (i = 0; i < ENA_RX_RSS_TABLE_SIZE; i++) {
-                       rc = ena_com_indirect_table_fill_entry(ena_dev,
-                                                              i,
-                                                              ENA_IO_RXQ_IDX(indir[i]));
-                       if (unlikely(rc)) {
-                               netif_err(adapter, drv, netdev,
-                                         "Cannot fill indirect table (index is too large)\n");
-                               return rc;
-                       }
-               }
-
-               rc = ena_com_indirect_table_set(ena_dev);
-               if (rc) {
-                       netif_err(adapter, drv, netdev,
-                                 "Cannot set indirect table\n");
-                       return rc == -EPERM ? -EOPNOTSUPP : rc;
-               }
+               rc = ena_indirection_table_set(adapter, indir);
+               if (rc)
+                       return rc;
        }
 
        switch (hfunc) {
@@ -746,7 +760,7 @@ static int ena_set_rxfh(struct net_device *netdev, const u32 *indir,
                return -EOPNOTSUPP;
        }
 
-       if (key) {
+       if (key || func) {
                rc = ena_com_fill_hash_function(ena_dev, func, key,
                                                ENA_HASH_KEY_SIZE,
                                                0xFFFFFFFF);
index 2cc765d..2818965 100644 (file)
@@ -1762,6 +1762,9 @@ static void ena_unmask_interrupt(struct ena_ring *tx_ring,
                                tx_ring->smoothed_interval,
                                true);
 
+       u64_stats_update_begin(&tx_ring->syncp);
+       tx_ring->tx_stats.unmask_interrupt++;
+       u64_stats_update_end(&tx_ring->syncp);
        /* It is a shared MSI-X.
         * Tx and Rx CQ have pointer to it.
         * So we use one of them to reach the intr reg
@@ -3169,6 +3172,7 @@ static void ena_get_stats64(struct net_device *netdev,
        struct ena_ring *rx_ring, *tx_ring;
        unsigned int start;
        u64 rx_drops;
+       u64 tx_drops;
        int i;
 
        if (!test_bit(ENA_FLAG_DEV_UP, &adapter->flags))
@@ -3203,9 +3207,11 @@ static void ena_get_stats64(struct net_device *netdev,
        do {
                start = u64_stats_fetch_begin_irq(&adapter->syncp);
                rx_drops = adapter->dev_stats.rx_drops;
+               tx_drops = adapter->dev_stats.tx_drops;
        } while (u64_stats_fetch_retry_irq(&adapter->syncp, start));
 
        stats->rx_dropped = rx_drops;
+       stats->tx_dropped = tx_drops;
 
        stats->multicast = 0;
        stats->collisions = 0;
@@ -3433,6 +3439,7 @@ static void ena_destroy_device(struct ena_adapter *adapter, bool graceful)
 
        ena_com_mmio_reg_read_request_destroy(ena_dev);
 
+       /* return reset reason to default value */
        adapter->reset_reason = ENA_REGS_RESET_NORMAL;
 
        clear_bit(ENA_FLAG_TRIGGER_RESET, &adapter->flags);
@@ -3991,7 +3998,7 @@ static int ena_rss_init_default(struct ena_adapter *adapter)
                }
        }
 
-       rc = ena_com_fill_hash_function(ena_dev, ENA_ADMIN_CRC32, NULL,
+       rc = ena_com_fill_hash_function(ena_dev, ENA_ADMIN_TOEPLITZ, NULL,
                                        ENA_HASH_KEY_SIZE, 0xFFFFFFFF);
        if (unlikely(rc && (rc != -EOPNOTSUPP))) {
                dev_err(dev, "Cannot fill hash function\n");
@@ -4356,6 +4363,7 @@ static void __ena_shutoff(struct pci_dev *pdev, bool shutdown)
        cancel_work_sync(&adapter->reset_task);
 
        rtnl_lock(); /* lock released inside the below if-else block */
+       adapter->reset_reason = ENA_REGS_RESET_SHUTDOWN;
        ena_destroy_device(adapter, true);
        if (shutdown) {
                netif_device_detach(netdev);
@@ -4514,14 +4522,17 @@ static void ena_keep_alive_wd(void *adapter_data,
        struct ena_adapter *adapter = (struct ena_adapter *)adapter_data;
        struct ena_admin_aenq_keep_alive_desc *desc;
        u64 rx_drops;
+       u64 tx_drops;
 
        desc = (struct ena_admin_aenq_keep_alive_desc *)aenq_e;
        adapter->last_keep_alive_jiffies = jiffies;
 
        rx_drops = ((u64)desc->rx_drops_high << 32) | desc->rx_drops_low;
+       tx_drops = ((u64)desc->tx_drops_high << 32) | desc->tx_drops_low;
 
        u64_stats_update_begin(&adapter->syncp);
        adapter->dev_stats.rx_drops = rx_drops;
+       adapter->dev_stats.tx_drops = tx_drops;
        u64_stats_update_end(&adapter->syncp);
 }
 
index 97dfd0c..7df67bf 100644 (file)
@@ -69,7 +69,7 @@
  * 16kB.
  */
 #if PAGE_SIZE > SZ_16K
-#define ENA_PAGE_SIZE SZ_16K
+#define ENA_PAGE_SIZE (_AC(SZ_16K, UL))
 #else
 #define ENA_PAGE_SIZE PAGE_SIZE
 #endif
@@ -248,6 +248,7 @@ struct ena_stats_tx {
        u64 bad_req_id;
        u64 llq_buffer_copy;
        u64 missed_tx;
+       u64 unmask_interrupt;
 };
 
 struct ena_stats_rx {
@@ -333,6 +334,7 @@ struct ena_stats_dev {
        u64 interface_down;
        u64 admin_q_pause;
        u64 rx_drops;
+       u64 tx_drops;
 };
 
 enum ena_flags_t {
index cf3562e..50fb663 100644 (file)
@@ -536,7 +536,7 @@ void lance_tx_timeout(struct net_device *dev, unsigned int txqueue)
 }
 EXPORT_SYMBOL_GPL(lance_tx_timeout);
 
-int lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
+netdev_tx_t lance_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct lance_private *lp = netdev_priv(dev);
        volatile struct lance_init_block *ib = lp->init_block;
index 8266b3c..e53551d 100644 (file)
@@ -241,7 +241,7 @@ struct lance_private {
 /* Now the prototypes we export */
 int lance_open(struct net_device *dev);
 int lance_close(struct net_device *dev);
-int lance_start_xmit(struct sk_buff *skb, struct net_device *dev);
+netdev_tx_t lance_start_xmit(struct sk_buff *skb, struct net_device *dev);
 void lance_set_multicast(struct net_device *dev);
 void lance_tx_timeout(struct net_device *dev, unsigned int txqueue);
 #ifdef CONFIG_NET_POLL_CONTROLLER
index b71f9b0..a87264f 100644 (file)
@@ -514,7 +514,7 @@ static void xgbe_isr_task(unsigned long data)
                                xgbe_disable_rx_tx_ints(pdata);
 
                                /* Turn on polling */
-                               __napi_schedule_irqoff(&pdata->napi);
+                               __napi_schedule(&pdata->napi);
                        }
                } else {
                        /* Don't clear Rx/Tx status if doing per channel DMA
index 8b55566..130a105 100644 (file)
@@ -25,6 +25,10 @@ atlantic-objs := aq_main.o \
        hw_atl/hw_atl_utils.o \
        hw_atl/hw_atl_utils_fw2x.o \
        hw_atl/hw_atl_llh.o \
+       hw_atl2/hw_atl2.o \
+       hw_atl2/hw_atl2_utils.o \
+       hw_atl2/hw_atl2_utils_fw.o \
+       hw_atl2/hw_atl2_llh.o \
        macsec/macsec_api.o
 
 atlantic-$(CONFIG_MACSEC) += aq_macsec.o
index 7560f55..52b9833 100644 (file)
@@ -80,8 +80,8 @@
 
 #define AQ_CFG_LOCK_TRYS   100U
 
-#define AQ_CFG_DRV_AUTHOR      "aQuantia"
-#define AQ_CFG_DRV_DESC        "aQuantia Corporation(R) Network Driver"
+#define AQ_CFG_DRV_AUTHOR      "Marvell"
+#define AQ_CFG_DRV_DESC        "Marvell (Aquantia) Corporation(R) Network Driver"
 #define AQ_CFG_DRV_NAME        "atlantic"
 
 #endif /* AQ_CFG_H */
index c8c402b..53620ba 100644 (file)
 #define AQ_DEVICE_ID_AQC111S   0x91B1
 #define AQ_DEVICE_ID_AQC112S   0x92B1
 
-#define HW_ATL_NIC_NAME "aQuantia AQtion 10Gbit Network Adapter"
+#define AQ_DEVICE_ID_AQC113DEV 0x00C0
+#define AQ_DEVICE_ID_AQC113CS  0x94C0
+#define AQ_DEVICE_ID_AQC114CS  0x93C0
+#define AQ_DEVICE_ID_AQC113    0x04C0
+#define AQ_DEVICE_ID_AQC113C   0x14C0
+#define AQ_DEVICE_ID_AQC115C   0x12C0
+
+#define HW_ATL_NIC_NAME "Marvell (aQuantia) AQtion 10Gbit Network Adapter"
 
 #define AQ_HWREV_ANY   0
 #define AQ_HWREV_1     1
 #define AQ_HWREV_2     2
 
-#define AQ_NIC_RATE_10G        BIT(0)
-#define AQ_NIC_RATE_5G         BIT(1)
-#define AQ_NIC_RATE_5GSR       BIT(2)
-#define AQ_NIC_RATE_2GS        BIT(3)
-#define AQ_NIC_RATE_1G         BIT(4)
-#define AQ_NIC_RATE_100M       BIT(5)
-
-#define AQ_NIC_RATE_EEE_10G    BIT(6)
-#define AQ_NIC_RATE_EEE_5G     BIT(7)
-#define AQ_NIC_RATE_EEE_2GS    BIT(8)
-#define AQ_NIC_RATE_EEE_1G     BIT(9)
+#define AQ_NIC_RATE_10G                BIT(0)
+#define AQ_NIC_RATE_5G         BIT(1)
+#define AQ_NIC_RATE_5GSR       BIT(2)
+#define AQ_NIC_RATE_2GS                BIT(3)
+#define AQ_NIC_RATE_1G         BIT(4)
+#define AQ_NIC_RATE_100M       BIT(5)
+#define AQ_NIC_RATE_10M                BIT(6)
+
+#define AQ_NIC_RATE_EEE_10G    BIT(7)
+#define AQ_NIC_RATE_EEE_5G     BIT(8)
+#define AQ_NIC_RATE_EEE_2GS    BIT(9)
+#define AQ_NIC_RATE_EEE_1G     BIT(10)
+#define AQ_NIC_RATE_EEE_100M   BIT(11)
 
 #endif /* AQ_COMMON_H */
index 7241cf9..0c9dd8e 100644 (file)
@@ -611,6 +611,9 @@ static enum hw_atl_fw2x_rate eee_mask_to_ethtool_mask(u32 speed)
        if (speed & AQ_NIC_RATE_EEE_1G)
                rate |= SUPPORTED_1000baseT_Full;
 
+       if (speed & AQ_NIC_RATE_EEE_100M)
+               rate |= SUPPORTED_100baseT_Full;
+
        return rate;
 }
 
index 7d71bc7..03fea94 100644 (file)
@@ -55,6 +55,7 @@ struct aq_hw_caps_s {
        u8 rx_rings;
        bool flow_control;
        bool is_64_dma;
+       u32 priv_data_len;
 };
 
 struct aq_hw_link_status_s {
@@ -136,6 +137,19 @@ enum aq_priv_flags {
                                 BIT(AQ_HW_LOOPBACK_PHYINT_SYS) |\
                                 BIT(AQ_HW_LOOPBACK_PHYEXT_SYS))
 
+#define ATL_HW_CHIP_MIPS         0x00000001U
+#define ATL_HW_CHIP_TPO2         0x00000002U
+#define ATL_HW_CHIP_RPF2         0x00000004U
+#define ATL_HW_CHIP_MPI_AQ       0x00000010U
+#define ATL_HW_CHIP_ATLANTIC     0x00800000U
+#define ATL_HW_CHIP_REVISION_A0  0x01000000U
+#define ATL_HW_CHIP_REVISION_B0  0x02000000U
+#define ATL_HW_CHIP_REVISION_B1  0x04000000U
+#define ATL_HW_CHIP_ANTIGUA      0x08000000U
+
+#define ATL_HW_IS_CHIP_FEATURE(_HW_, _F_) (!!(ATL_HW_CHIP_##_F_ & \
+       (_HW_)->chip_features))
+
 struct aq_hw_s {
        atomic_t flags;
        u8 rbl_enabled:1;
@@ -159,6 +173,7 @@ struct aq_hw_s {
        struct hw_atl_utils_fw_rpc rpc;
        s64 ptp_clk_offset;
        u16 phy_id;
+       void *priv;
 };
 
 struct aq_ring_s;
@@ -182,6 +197,11 @@ struct aq_hw_ops {
 
        int (*hw_set_mac_address)(struct aq_hw_s *self, u8 *mac_addr);
 
+       int (*hw_soft_reset)(struct aq_hw_s *self);
+
+       int (*hw_prepare)(struct aq_hw_s *self,
+                         const struct aq_fw_ops **fw_ops);
+
        int (*hw_reset)(struct aq_hw_s *self);
 
        int (*hw_init)(struct aq_hw_s *self, u8 *mac_addr);
@@ -254,7 +274,7 @@ struct aq_hw_ops {
 
        struct aq_stats_s *(*hw_get_hw_stats)(struct aq_hw_s *self);
 
-       int (*hw_get_fw_version)(struct aq_hw_s *self, u32 *fw_version);
+       u32 (*hw_get_fw_version)(struct aq_hw_s *self);
 
        int (*hw_set_offload)(struct aq_hw_s *self,
                              struct aq_nic_cfg_s *aq_nic_cfg);
index 0b3e234..91870ce 100644 (file)
@@ -401,7 +401,7 @@ static u32 aq_sc_idx_max(const enum aq_macsec_sc_sa sc_sa)
                break;
        default:
                break;
-       };
+       }
 
        return result;
 }
@@ -417,7 +417,7 @@ static u32 aq_to_hw_sc_idx(const u32 sc_idx, const enum aq_macsec_sc_sa sc_sa)
                return sc_idx;
        default:
                WARN_ONCE(true, "Invalid sc_sa");
-       };
+       }
 
        return sc_idx;
 }
index a369705..f97b073 100644 (file)
@@ -257,6 +257,20 @@ static void aq_nic_polling_timer_cb(struct timer_list *t)
                  AQ_CFG_POLLING_TIMER_INTERVAL);
 }
 
+static int aq_nic_hw_prepare(struct aq_nic_s *self)
+{
+       int err = 0;
+
+       err = self->aq_hw_ops->hw_soft_reset(self->aq_hw);
+       if (err)
+               goto exit;
+
+       err = self->aq_hw_ops->hw_prepare(self->aq_hw, &self->aq_fw_ops);
+
+exit:
+       return err;
+}
+
 int aq_nic_ndev_register(struct aq_nic_s *self)
 {
        int err = 0;
@@ -266,7 +280,7 @@ int aq_nic_ndev_register(struct aq_nic_s *self)
                goto err_exit;
        }
 
-       err = hw_atl_utils_initfw(self->aq_hw, &self->aq_fw_ops);
+       err = aq_nic_hw_prepare(self);
        if (err)
                goto err_exit;
 
@@ -364,7 +378,8 @@ int aq_nic_init(struct aq_nic_s *self)
        if (err < 0)
                goto err_exit;
 
-       if (self->aq_nic_cfg.aq_hw_caps->media_type == AQ_HW_MEDIA_TYPE_TP) {
+       if (ATL_HW_IS_CHIP_FEATURE(self->aq_hw, ATLANTIC) &&
+           self->aq_nic_cfg.aq_hw_caps->media_type == AQ_HW_MEDIA_TYPE_TP) {
                self->aq_hw->phy_id = HW_ATL_PHY_ID_MAX;
                err = aq_phy_init(self->aq_hw);
        }
@@ -764,6 +779,9 @@ int aq_nic_get_regs(struct aq_nic_s *self, struct ethtool_regs *regs, void *p)
        u32 *regs_buff = p;
        int err = 0;
 
+       if (unlikely(!self->aq_hw_ops->hw_get_regs))
+               return -EOPNOTSUPP;
+
        regs->version = 1;
 
        err = self->aq_hw_ops->hw_get_regs(self->aq_hw,
@@ -778,6 +796,9 @@ err_exit:
 
 int aq_nic_get_regs_count(struct aq_nic_s *self)
 {
+       if (unlikely(!self->aq_hw_ops->hw_get_regs))
+               return 0;
+
        return self->aq_nic_cfg.aq_hw_caps->mac_regs_count;
 }
 
@@ -885,6 +906,10 @@ void aq_nic_get_link_ksettings(struct aq_nic_s *self,
                ethtool_link_ksettings_add_link_mode(cmd, supported,
                                                     100baseT_Full);
 
+       if (self->aq_nic_cfg.aq_hw_caps->link_speed_msk & AQ_NIC_RATE_10M)
+               ethtool_link_ksettings_add_link_mode(cmd, supported,
+                                                    10baseT_Full);
+
        if (self->aq_nic_cfg.aq_hw_caps->flow_control) {
                ethtool_link_ksettings_add_link_mode(cmd, supported,
                                                     Pause);
@@ -924,6 +949,10 @@ void aq_nic_get_link_ksettings(struct aq_nic_s *self,
                ethtool_link_ksettings_add_link_mode(cmd, advertising,
                                                     100baseT_Full);
 
+       if (self->aq_nic_cfg.link_speed_msk  & AQ_NIC_RATE_10M)
+               ethtool_link_ksettings_add_link_mode(cmd, advertising,
+                                                    10baseT_Full);
+
        if (self->aq_nic_cfg.fc.cur & AQ_NIC_FC_RX)
                ethtool_link_ksettings_add_link_mode(cmd, advertising,
                                                     Pause);
@@ -954,6 +983,10 @@ int aq_nic_set_link_ksettings(struct aq_nic_s *self,
                speed = cmd->base.speed;
 
                switch (speed) {
+               case SPEED_10:
+                       rate = AQ_NIC_RATE_10M;
+                       break;
+
                case SPEED_100:
                        rate = AQ_NIC_RATE_100M;
                        break;
@@ -1006,11 +1039,7 @@ struct aq_nic_cfg_s *aq_nic_get_cfg(struct aq_nic_s *self)
 
 u32 aq_nic_get_fw_version(struct aq_nic_s *self)
 {
-       u32 fw_version = 0U;
-
-       self->aq_hw_ops->hw_get_fw_version(self->aq_hw, &fw_version);
-
-       return fw_version;
+       return self->aq_hw_ops->hw_get_fw_version(self->aq_hw);
 }
 
 int aq_nic_set_loopback(struct aq_nic_s *self)
index 2edf137..d10fff8 100644 (file)
@@ -16,6 +16,7 @@
 #include "aq_pci_func.h"
 #include "hw_atl/hw_atl_a0.h"
 #include "hw_atl/hw_atl_b0.h"
+#include "hw_atl2/hw_atl2.h"
 #include "aq_filters.h"
 #include "aq_drvinfo.h"
 #include "aq_macsec.h"
@@ -41,6 +42,13 @@ static const struct pci_device_id aq_pci_tbl[] = {
        { PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC111S), },
        { PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC112S), },
 
+       { PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC113DEV), },
+       { PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC113CS), },
+       { PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC114CS), },
+       { PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC113), },
+       { PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC113C), },
+       { PCI_VDEVICE(AQUANTIA, AQ_DEVICE_ID_AQC115C), },
+
        {}
 };
 
@@ -57,7 +65,7 @@ static const struct aq_board_revision_s hw_atl_boards[] = {
        { AQ_DEVICE_ID_D108,    AQ_HWREV_2,     &hw_atl_ops_b0, &hw_atl_b0_caps_aqc108, },
        { AQ_DEVICE_ID_D109,    AQ_HWREV_2,     &hw_atl_ops_b0, &hw_atl_b0_caps_aqc109, },
 
-       { AQ_DEVICE_ID_AQC100,  AQ_HWREV_ANY,   &hw_atl_ops_b1, &hw_atl_b0_caps_aqc107, },
+       { AQ_DEVICE_ID_AQC100,  AQ_HWREV_ANY,   &hw_atl_ops_b1, &hw_atl_b0_caps_aqc100, },
        { AQ_DEVICE_ID_AQC107,  AQ_HWREV_ANY,   &hw_atl_ops_b1, &hw_atl_b0_caps_aqc107, },
        { AQ_DEVICE_ID_AQC108,  AQ_HWREV_ANY,   &hw_atl_ops_b1, &hw_atl_b0_caps_aqc108, },
        { AQ_DEVICE_ID_AQC109,  AQ_HWREV_ANY,   &hw_atl_ops_b1, &hw_atl_b0_caps_aqc109, },
@@ -70,6 +78,13 @@ static const struct aq_board_revision_s hw_atl_boards[] = {
        { AQ_DEVICE_ID_AQC109S, AQ_HWREV_ANY,   &hw_atl_ops_b1, &hw_atl_b0_caps_aqc109s, },
        { AQ_DEVICE_ID_AQC111S, AQ_HWREV_ANY,   &hw_atl_ops_b1, &hw_atl_b0_caps_aqc111s, },
        { AQ_DEVICE_ID_AQC112S, AQ_HWREV_ANY,   &hw_atl_ops_b1, &hw_atl_b0_caps_aqc112s, },
+
+       { AQ_DEVICE_ID_AQC113DEV,       AQ_HWREV_ANY,   &hw_atl2_ops, &hw_atl2_caps_aqc113, },
+       { AQ_DEVICE_ID_AQC113,          AQ_HWREV_ANY,   &hw_atl2_ops, &hw_atl2_caps_aqc113, },
+       { AQ_DEVICE_ID_AQC113CS,        AQ_HWREV_ANY,   &hw_atl2_ops, &hw_atl2_caps_aqc113, },
+       { AQ_DEVICE_ID_AQC114CS,        AQ_HWREV_ANY,   &hw_atl2_ops, &hw_atl2_caps_aqc113, },
+       { AQ_DEVICE_ID_AQC113C,         AQ_HWREV_ANY,   &hw_atl2_ops, &hw_atl2_caps_aqc113, },
+       { AQ_DEVICE_ID_AQC115C,         AQ_HWREV_ANY,   &hw_atl2_ops, &hw_atl2_caps_aqc113, },
 };
 
 MODULE_DEVICE_TABLE(pci, aq_pci_tbl);
@@ -104,10 +119,8 @@ int aq_pci_func_init(struct pci_dev *pdev)
        int err;
 
        err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
-       if (!err) {
+       if (!err)
                err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
-
-       }
        if (err) {
                err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
                if (!err)
@@ -237,6 +250,15 @@ static int aq_pci_probe(struct pci_dev *pdev,
                goto err_ioremap;
        }
        self->aq_hw->aq_nic_cfg = aq_nic_get_cfg(self);
+       if (self->aq_hw->aq_nic_cfg->aq_hw_caps->priv_data_len) {
+               int len = self->aq_hw->aq_nic_cfg->aq_hw_caps->priv_data_len;
+
+               self->aq_hw->priv = kzalloc(len, GFP_KERNEL);
+               if (!self->aq_hw->priv) {
+                       err = -ENOMEM;
+                       goto err_free_aq_hw;
+               }
+       }
 
        for (bar = 0; bar < 4; ++bar) {
                if (IORESOURCE_MEM & pci_resource_flags(pdev, bar)) {
@@ -245,19 +267,19 @@ static int aq_pci_probe(struct pci_dev *pdev,
                        mmio_pa = pci_resource_start(pdev, bar);
                        if (mmio_pa == 0U) {
                                err = -EIO;
-                               goto err_free_aq_hw;
+                               goto err_free_aq_hw_priv;
                        }
 
                        reg_sz = pci_resource_len(pdev, bar);
                        if ((reg_sz <= 24 /*ATL_REGS_SIZE*/)) {
                                err = -EIO;
-                               goto err_free_aq_hw;
+                               goto err_free_aq_hw_priv;
                        }
 
                        self->aq_hw->mmio = ioremap(mmio_pa, reg_sz);
                        if (!self->aq_hw->mmio) {
                                err = -EIO;
-                               goto err_free_aq_hw;
+                               goto err_free_aq_hw_priv;
                        }
                        break;
                }
@@ -265,7 +287,7 @@ static int aq_pci_probe(struct pci_dev *pdev,
 
        if (bar == 4) {
                err = -EIO;
-               goto err_free_aq_hw;
+               goto err_free_aq_hw_priv;
        }
 
        numvecs = min((u8)AQ_CFG_VECS_DEF,
@@ -305,6 +327,8 @@ err_register:
        aq_pci_free_irq_vectors(self);
 err_hwinit:
        iounmap(self->aq_hw->mmio);
+err_free_aq_hw_priv:
+       kfree(self->aq_hw->priv);
 err_free_aq_hw:
        kfree(self->aq_hw);
 err_ioremap:
@@ -332,6 +356,7 @@ static void aq_pci_remove(struct pci_dev *pdev)
                aq_nic_free_vectors(self);
                aq_pci_free_irq_vectors(self);
                iounmap(self->aq_hw->mmio);
+               kfree(self->aq_hw->priv);
                kfree(self->aq_hw);
                pci_release_regions(pdev);
                free_netdev(self->ndev);
index 9b1062b..eee265b 100644 (file)
@@ -267,7 +267,7 @@ static int hw_atl_a0_hw_init_tx_path(struct aq_hw_s *self)
        hw_atl_tdm_tx_desc_wr_wb_irq_en_set(self, 1U);
 
        /* misc */
-       aq_hw_write_reg(self, 0x00007040U, IS_CHIP_FEATURE(TPO2) ?
+       aq_hw_write_reg(self, 0x00007040U, ATL_HW_IS_CHIP_FEATURE(self, TPO2) ?
                        0x00010000U : 0x00000000U);
        hw_atl_tdm_tx_dca_en_set(self, 0U);
        hw_atl_tdm_tx_dca_mode_set(self, 0U);
@@ -886,6 +886,8 @@ static int hw_atl_a0_hw_ring_rx_stop(struct aq_hw_s *self,
 }
 
 const struct aq_hw_ops hw_atl_ops_a0 = {
+       .hw_soft_reset        = hw_atl_utils_soft_reset,
+       .hw_prepare           = hw_atl_utils_initfw,
        .hw_set_mac_address   = hw_atl_a0_hw_mac_addr_set,
        .hw_init              = hw_atl_a0_hw_init,
        .hw_reset             = hw_atl_a0_hw_reset,
index d20d91c..cbb7a00 100644 (file)
@@ -187,8 +187,8 @@ static int hw_atl_b0_hw_qos_set(struct aq_hw_s *self)
        return aq_hw_err_from_flags(self);
 }
 
-static int hw_atl_b0_hw_rss_hash_set(struct aq_hw_s *self,
-                                    struct aq_rss_parameters *rss_params)
+int hw_atl_b0_hw_rss_hash_set(struct aq_hw_s *self,
+                             struct aq_rss_parameters *rss_params)
 {
        struct aq_nic_cfg_s *cfg = self->aq_nic_cfg;
        unsigned int addr = 0U;
@@ -215,8 +215,8 @@ err_exit:
        return err;
 }
 
-static int hw_atl_b0_hw_rss_set(struct aq_hw_s *self,
-                               struct aq_rss_parameters *rss_params)
+int hw_atl_b0_hw_rss_set(struct aq_hw_s *self,
+                        struct aq_rss_parameters *rss_params)
 {
        u32 num_rss_queues = max(1U, self->aq_nic_cfg->num_rss_queues);
        u8 *indirection_table = rss_params->indirection_table;
@@ -251,9 +251,10 @@ err_exit:
        return err;
 }
 
-static int hw_atl_b0_hw_offload_set(struct aq_hw_s *self,
-                                   struct aq_nic_cfg_s *aq_nic_cfg)
+int hw_atl_b0_hw_offload_set(struct aq_hw_s *self,
+                            struct aq_nic_cfg_s *aq_nic_cfg)
 {
+       u64 rxcsum = !!(aq_nic_cfg->features & NETIF_F_RXCSUM);
        unsigned int i;
 
        /* TX checksums offloads*/
@@ -261,10 +262,8 @@ static int hw_atl_b0_hw_offload_set(struct aq_hw_s *self,
        hw_atl_tpo_tcp_udp_crc_offload_en_set(self, 1);
 
        /* RX checksums offloads*/
-       hw_atl_rpo_ipv4header_crc_offload_en_set(self, !!(aq_nic_cfg->features &
-                                                NETIF_F_RXCSUM));
-       hw_atl_rpo_tcp_udp_crc_offload_en_set(self, !!(aq_nic_cfg->features &
-                                             NETIF_F_RXCSUM));
+       hw_atl_rpo_ipv4header_crc_offload_en_set(self, rxcsum);
+       hw_atl_rpo_tcp_udp_crc_offload_en_set(self, rxcsum);
 
        /* LSO offloads*/
        hw_atl_tdm_large_send_offload_en_set(self, 0xFFFFFFFFU);
@@ -272,7 +271,7 @@ static int hw_atl_b0_hw_offload_set(struct aq_hw_s *self,
        /* Outer VLAN tag offload */
        hw_atl_rpo_outer_vlan_tag_mode_set(self, 1U);
 
-/* LRO offloads */
+       /* LRO offloads */
        {
                unsigned int val = (8U < HW_ATL_B0_LRO_RXD_MAX) ? 0x3U :
                        ((4U < HW_ATL_B0_LRO_RXD_MAX) ? 0x2U :
@@ -314,7 +313,7 @@ static int hw_atl_b0_hw_offload_set(struct aq_hw_s *self,
 static int hw_atl_b0_hw_init_tx_path(struct aq_hw_s *self)
 {
        /* Tx TC/Queue number config */
-       hw_atl_rpb_tps_tx_tc_mode_set(self, 1U);
+       hw_atl_tpb_tps_tx_tc_mode_set(self, 1U);
 
        hw_atl_thm_lso_tcp_flag_of_first_pkt_set(self, 0x0FF6U);
        hw_atl_thm_lso_tcp_flag_of_middle_pkt_set(self, 0x0FF6U);
@@ -324,7 +323,7 @@ static int hw_atl_b0_hw_init_tx_path(struct aq_hw_s *self)
        hw_atl_tdm_tx_desc_wr_wb_irq_en_set(self, 1U);
 
        /* misc */
-       aq_hw_write_reg(self, 0x00007040U, IS_CHIP_FEATURE(TPO2) ?
+       aq_hw_write_reg(self, 0x00007040U, ATL_HW_IS_CHIP_FEATURE(self, TPO2) ?
                        0x00010000U : 0x00000000U);
        hw_atl_tdm_tx_dca_en_set(self, 0U);
        hw_atl_tdm_tx_dca_mode_set(self, 0U);
@@ -372,8 +371,8 @@ static int hw_atl_b0_hw_init_rx_path(struct aq_hw_s *self)
        hw_atl_rdm_rx_desc_wr_wb_irq_en_set(self, 1U);
 
        /* misc */
-       aq_hw_write_reg(self, 0x00005040U,
-                       IS_CHIP_FEATURE(RPF2) ? 0x000F0000U : 0x00000000U);
+       aq_hw_write_reg(self, 0x00005040U, ATL_HW_IS_CHIP_FEATURE(self, RPF2) ?
+                       0x000F0000U : 0x00000000U);
 
        hw_atl_rpfl2broadcast_flr_act_set(self, 1U);
        hw_atl_rpfl2broadcast_count_threshold_set(self, 0xFFFFU & (~0U / 256U));
@@ -384,7 +383,7 @@ static int hw_atl_b0_hw_init_rx_path(struct aq_hw_s *self)
        return aq_hw_err_from_flags(self);
 }
 
-static int hw_atl_b0_hw_mac_addr_set(struct aq_hw_s *self, u8 *mac_addr)
+int hw_atl_b0_hw_mac_addr_set(struct aq_hw_s *self, u8 *mac_addr)
 {
        unsigned int h = 0U;
        unsigned int l = 0U;
@@ -479,23 +478,21 @@ err_exit:
        return err;
 }
 
-static int hw_atl_b0_hw_ring_tx_start(struct aq_hw_s *self,
-                                     struct aq_ring_s *ring)
+int hw_atl_b0_hw_ring_tx_start(struct aq_hw_s *self, struct aq_ring_s *ring)
 {
        hw_atl_tdm_tx_desc_en_set(self, 1, ring->idx);
 
        return aq_hw_err_from_flags(self);
 }
 
-static int hw_atl_b0_hw_ring_rx_start(struct aq_hw_s *self,
-                                     struct aq_ring_s *ring)
+int hw_atl_b0_hw_ring_rx_start(struct aq_hw_s *self, struct aq_ring_s *ring)
 {
        hw_atl_rdm_rx_desc_en_set(self, 1, ring->idx);
 
        return aq_hw_err_from_flags(self);
 }
 
-static int hw_atl_b0_hw_start(struct aq_hw_s *self)
+int hw_atl_b0_hw_start(struct aq_hw_s *self)
 {
        hw_atl_tpb_tx_buff_en_set(self, 1);
        hw_atl_rpb_rx_buff_en_set(self, 1);
@@ -511,9 +508,8 @@ static int hw_atl_b0_hw_tx_ring_tail_update(struct aq_hw_s *self,
        return 0;
 }
 
-static int hw_atl_b0_hw_ring_tx_xmit(struct aq_hw_s *self,
-                                    struct aq_ring_s *ring,
-                                    unsigned int frags)
+int hw_atl_b0_hw_ring_tx_xmit(struct aq_hw_s *self, struct aq_ring_s *ring,
+                             unsigned int frags)
 {
        struct aq_ring_buff_s *buff = NULL;
        struct hw_atl_txd_s *txd = NULL;
@@ -600,9 +596,8 @@ static int hw_atl_b0_hw_ring_tx_xmit(struct aq_hw_s *self,
        return aq_hw_err_from_flags(self);
 }
 
-static int hw_atl_b0_hw_ring_rx_init(struct aq_hw_s *self,
-                                    struct aq_ring_s *aq_ring,
-                                    struct aq_ring_param_s *aq_ring_param)
+int hw_atl_b0_hw_ring_rx_init(struct aq_hw_s *self, struct aq_ring_s *aq_ring,
+                             struct aq_ring_param_s *aq_ring_param)
 {
        u32 dma_desc_addr_msw = (u32)(((u64)aq_ring->dx_ring_pa) >> 32);
        u32 vlan_rx_stripping = self->aq_nic_cfg->is_vlan_rx_strip;
@@ -643,9 +638,8 @@ static int hw_atl_b0_hw_ring_rx_init(struct aq_hw_s *self,
        return aq_hw_err_from_flags(self);
 }
 
-static int hw_atl_b0_hw_ring_tx_init(struct aq_hw_s *self,
-                                    struct aq_ring_s *aq_ring,
-                                    struct aq_ring_param_s *aq_ring_param)
+int hw_atl_b0_hw_ring_tx_init(struct aq_hw_s *self, struct aq_ring_s *aq_ring,
+                             struct aq_ring_param_s *aq_ring_param)
 {
        u32 dma_desc_msw_addr = (u32)(((u64)aq_ring->dx_ring_pa) >> 32);
        u32 dma_desc_lsw_addr = (u32)aq_ring->dx_ring_pa;
@@ -673,9 +667,8 @@ static int hw_atl_b0_hw_ring_tx_init(struct aq_hw_s *self,
        return aq_hw_err_from_flags(self);
 }
 
-static int hw_atl_b0_hw_ring_rx_fill(struct aq_hw_s *self,
-                                    struct aq_ring_s *ring,
-                                    unsigned int sw_tail_old)
+int hw_atl_b0_hw_ring_rx_fill(struct aq_hw_s *self, struct aq_ring_s *ring,
+                             unsigned int sw_tail_old)
 {
        for (; sw_tail_old != ring->sw_tail;
                sw_tail_old = aq_ring_next_dx(ring, sw_tail_old)) {
@@ -734,8 +727,8 @@ static int hw_atl_b0_hw_ring_hwts_rx_receive(struct aq_hw_s *self,
        return aq_hw_err_from_flags(self);
 }
 
-static int hw_atl_b0_hw_ring_tx_head_update(struct aq_hw_s *self,
-                                           struct aq_ring_s *ring)
+int hw_atl_b0_hw_ring_tx_head_update(struct aq_hw_s *self,
+                                    struct aq_ring_s *ring)
 {
        unsigned int hw_head_;
        int err = 0;
@@ -753,8 +746,7 @@ err_exit:
        return err;
 }
 
-static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self,
-                                       struct aq_ring_s *ring)
+int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self, struct aq_ring_s *ring)
 {
        for (; ring->hw_head != ring->sw_tail;
                ring->hw_head = aq_ring_next_dx(ring, ring->hw_head)) {
@@ -854,14 +846,14 @@ static int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self,
        return aq_hw_err_from_flags(self);
 }
 
-static int hw_atl_b0_hw_irq_enable(struct aq_hw_s *self, u64 mask)
+int hw_atl_b0_hw_irq_enable(struct aq_hw_s *self, u64 mask)
 {
        hw_atl_itr_irq_msk_setlsw_set(self, LODWORD(mask));
 
        return aq_hw_err_from_flags(self);
 }
 
-static int hw_atl_b0_hw_irq_disable(struct aq_hw_s *self, u64 mask)
+int hw_atl_b0_hw_irq_disable(struct aq_hw_s *self, u64 mask)
 {
        hw_atl_itr_irq_msk_clearlsw_set(self, LODWORD(mask));
        hw_atl_itr_irq_status_clearlsw_set(self, LODWORD(mask));
@@ -871,7 +863,7 @@ static int hw_atl_b0_hw_irq_disable(struct aq_hw_s *self, u64 mask)
        return aq_hw_err_from_flags(self);
 }
 
-static int hw_atl_b0_hw_irq_read(struct aq_hw_s *self, u64 *mask)
+int hw_atl_b0_hw_irq_read(struct aq_hw_s *self, u64 *mask)
 {
        *mask = hw_atl_itr_irq_statuslsw_get(self);
 
@@ -880,8 +872,8 @@ static int hw_atl_b0_hw_irq_read(struct aq_hw_s *self, u64 *mask)
 
 #define IS_FILTER_ENABLED(_F_) ((packet_filter & (_F_)) ? 1U : 0U)
 
-static int hw_atl_b0_hw_packet_filter_set(struct aq_hw_s *self,
-                                         unsigned int packet_filter)
+int hw_atl_b0_hw_packet_filter_set(struct aq_hw_s *self,
+                                  unsigned int packet_filter)
 {
        struct aq_nic_cfg_s *cfg = self->aq_nic_cfg;
        unsigned int i = 0U;
@@ -1071,16 +1063,14 @@ err_exit:
        return err;
 }
 
-static int hw_atl_b0_hw_ring_tx_stop(struct aq_hw_s *self,
-                                    struct aq_ring_s *ring)
+int hw_atl_b0_hw_ring_tx_stop(struct aq_hw_s *self, struct aq_ring_s *ring)
 {
        hw_atl_tdm_tx_desc_en_set(self, 0U, ring->idx);
 
        return aq_hw_err_from_flags(self);
 }
 
-static int hw_atl_b0_hw_ring_rx_stop(struct aq_hw_s *self,
-                                    struct aq_ring_s *ring)
+int hw_atl_b0_hw_ring_rx_stop(struct aq_hw_s *self, struct aq_ring_s *ring)
 {
        hw_atl_rdm_rx_desc_en_set(self, 0U, ring->idx);
 
@@ -1089,7 +1079,7 @@ static int hw_atl_b0_hw_ring_rx_stop(struct aq_hw_s *self,
 
 static int hw_atl_b0_tx_tc_mode_get(struct aq_hw_s *self, u32 *tc_mode)
 {
-       *tc_mode = hw_atl_rpb_tps_tx_tc_mode_get(self);
+       *tc_mode = hw_atl_tpb_tps_tx_tc_mode_get(self);
        return aq_hw_err_from_flags(self);
 }
 
@@ -1478,6 +1468,8 @@ static int hw_atl_b0_set_loopback(struct aq_hw_s *self, u32 mode, bool enable)
 }
 
 const struct aq_hw_ops hw_atl_ops_b0 = {
+       .hw_soft_reset        = hw_atl_utils_soft_reset,
+       .hw_prepare           = hw_atl_utils_initfw,
        .hw_set_mac_address   = hw_atl_b0_hw_mac_addr_set,
        .hw_init              = hw_atl_b0_hw_init,
        .hw_reset             = hw_atl_b0_hw_reset,
index 09af168..f5091d7 100644 (file)
@@ -33,4 +33,41 @@ extern const struct aq_hw_ops hw_atl_ops_b0;
 
 #define hw_atl_ops_b1 hw_atl_ops_b0
 
+int hw_atl_b0_hw_rss_hash_set(struct aq_hw_s *self,
+                             struct aq_rss_parameters *rss_params);
+int hw_atl_b0_hw_rss_set(struct aq_hw_s *self,
+                        struct aq_rss_parameters *rss_params);
+int hw_atl_b0_hw_offload_set(struct aq_hw_s *self,
+                            struct aq_nic_cfg_s *aq_nic_cfg);
+
+int hw_atl_b0_hw_ring_tx_start(struct aq_hw_s *self, struct aq_ring_s *ring);
+int hw_atl_b0_hw_ring_rx_start(struct aq_hw_s *self, struct aq_ring_s *ring);
+
+int hw_atl_b0_hw_ring_rx_init(struct aq_hw_s *self, struct aq_ring_s *aq_ring,
+                             struct aq_ring_param_s *aq_ring_param);
+int hw_atl_b0_hw_ring_rx_fill(struct aq_hw_s *self, struct aq_ring_s *ring,
+                             unsigned int sw_tail_old);
+int hw_atl_b0_hw_ring_rx_receive(struct aq_hw_s *self, struct aq_ring_s *ring);
+
+int hw_atl_b0_hw_ring_tx_init(struct aq_hw_s *self, struct aq_ring_s *aq_ring,
+                             struct aq_ring_param_s *aq_ring_param);
+int hw_atl_b0_hw_ring_tx_xmit(struct aq_hw_s *self, struct aq_ring_s *ring,
+                             unsigned int frags);
+int hw_atl_b0_hw_ring_tx_head_update(struct aq_hw_s *self,
+                                    struct aq_ring_s *ring);
+
+int hw_atl_b0_hw_ring_tx_stop(struct aq_hw_s *self, struct aq_ring_s *ring);
+int hw_atl_b0_hw_ring_rx_stop(struct aq_hw_s *self, struct aq_ring_s *ring);
+
+int hw_atl_b0_hw_mac_addr_set(struct aq_hw_s *self, u8 *mac_addr);
+
+int hw_atl_b0_hw_start(struct aq_hw_s *self);
+
+int hw_atl_b0_hw_irq_enable(struct aq_hw_s *self, u64 mask);
+int hw_atl_b0_hw_irq_disable(struct aq_hw_s *self, u64 mask);
+int hw_atl_b0_hw_irq_read(struct aq_hw_s *self, u64 *mask);
+
+int hw_atl_b0_hw_packet_filter_set(struct aq_hw_s *self,
+                                  unsigned int packet_filter);
+
 #endif /* HW_ATL_B0_H */
index d1f68fc..9e2d01a 100644 (file)
@@ -693,6 +693,13 @@ void hw_atl_rpfl2multicast_flr_en_set(struct aq_hw_s *aq_hw,
                            HW_ATL_RPFL2MC_ENF_SHIFT, l2multicast_flr_en);
 }
 
+u32 hw_atl_rpfl2promiscuous_mode_en_get(struct aq_hw_s *aq_hw)
+{
+       return aq_hw_read_reg_bit(aq_hw, HW_ATL_RPFL2PROMIS_MODE_ADR,
+                                 HW_ATL_RPFL2PROMIS_MODE_MSK,
+                                 HW_ATL_RPFL2PROMIS_MODE_SHIFT);
+}
+
 void hw_atl_rpfl2promiscuous_mode_en_set(struct aq_hw_s *aq_hw,
                                         u32 l2promiscuous_mode_en)
 {
@@ -867,6 +874,13 @@ void hw_atl_rpf_vlan_prom_mode_en_set(struct aq_hw_s *aq_hw,
                            vlan_prom_mode_en);
 }
 
+u32 hw_atl_rpf_vlan_prom_mode_en_get(struct aq_hw_s *aq_hw)
+{
+       return aq_hw_read_reg_bit(aq_hw, HW_ATL_RPF_VL_PROMIS_MODE_ADR,
+                                 HW_ATL_RPF_VL_PROMIS_MODE_MSK,
+                                 HW_ATL_RPF_VL_PROMIS_MODE_SHIFT);
+}
+
 void hw_atl_rpf_vlan_accept_untagged_packets_set(struct aq_hw_s *aq_hw,
                                                 u32 vlan_acc_untagged_packets)
 {
@@ -1304,14 +1318,14 @@ void hw_atl_tpb_tx_buff_en_set(struct aq_hw_s *aq_hw, u32 tx_buff_en)
                            HW_ATL_TPB_TX_BUF_EN_SHIFT, tx_buff_en);
 }
 
-u32 hw_atl_rpb_tps_tx_tc_mode_get(struct aq_hw_s *aq_hw)
+u32 hw_atl_tpb_tps_tx_tc_mode_get(struct aq_hw_s *aq_hw)
 {
        return aq_hw_read_reg_bit(aq_hw, HW_ATL_TPB_TX_TC_MODE_ADDR,
                        HW_ATL_TPB_TX_TC_MODE_MSK,
                        HW_ATL_TPB_TX_TC_MODE_SHIFT);
 }
 
-void hw_atl_rpb_tps_tx_tc_mode_set(struct aq_hw_s *aq_hw,
+void hw_atl_tpb_tps_tx_tc_mode_set(struct aq_hw_s *aq_hw,
                                   u32 tx_traf_class_mode)
 {
        aq_hw_write_reg_bit(aq_hw, HW_ATL_TPB_TX_TC_MODE_ADDR,
index 62992b2..b88cb84 100644 (file)
@@ -349,6 +349,9 @@ void hw_atl_rpfl2multicast_flr_en_set(struct aq_hw_s *aq_hw,
                                      u32 l2multicast_flr_en,
                                      u32 filter);
 
+/* get l2 promiscuous mode enable */
+u32 hw_atl_rpfl2promiscuous_mode_en_get(struct aq_hw_s *aq_hw);
+
 /* set l2 promiscuous mode enable */
 void hw_atl_rpfl2promiscuous_mode_en_set(struct aq_hw_s *aq_hw,
                                         u32 l2promiscuous_mode_en);
@@ -420,6 +423,9 @@ void hw_atl_rpf_vlan_outer_etht_set(struct aq_hw_s *aq_hw, u32 vlan_outer_etht);
 void hw_atl_rpf_vlan_prom_mode_en_set(struct aq_hw_s *aq_hw,
                                      u32 vlan_prom_mode_en);
 
+/* Get VLAN promiscuous mode enable */
+u32 hw_atl_rpf_vlan_prom_mode_en_get(struct aq_hw_s *aq_hw);
+
 /* Set VLAN untagged action */
 void hw_atl_rpf_vlan_untagged_act_set(struct aq_hw_s *aq_hw,
                                      u32 vlan_untagged_act);
@@ -610,11 +616,11 @@ void hw_atl_thm_lso_tcp_flag_of_middle_pkt_set(struct aq_hw_s *aq_hw,
 /* tpb */
 
 /* set TX Traffic Class Mode */
-void hw_atl_rpb_tps_tx_tc_mode_set(struct aq_hw_s *aq_hw,
+void hw_atl_tpb_tps_tx_tc_mode_set(struct aq_hw_s *aq_hw,
                                   u32 tx_traf_class_mode);
 
 /* get TX Traffic Class Mode */
-u32 hw_atl_rpb_tps_tx_tc_mode_get(struct aq_hw_s *aq_hw);
+u32 hw_atl_tpb_tps_tx_tc_mode_get(struct aq_hw_s *aq_hw);
 
 /* set tx buffer enable */
 void hw_atl_tpb_tx_buff_en_set(struct aq_hw_s *aq_hw, u32 tx_buff_en);
index 354705f..1100d40 100644 (file)
@@ -53,7 +53,6 @@ enum mcp_area {
        MCP_AREA_SETTINGS = 0x20000000,
 };
 
-static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual);
 static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self,
                                      enum hal_atl_utils_fw_state_e state);
 static u32 hw_atl_utils_get_mpi_mbox_tid(struct aq_hw_s *self);
@@ -67,14 +66,10 @@ int hw_atl_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops)
 {
        int err = 0;
 
-       err = hw_atl_utils_soft_reset(self);
-       if (err)
-               return err;
-
        hw_atl_utils_hw_chip_features_init(self,
                                           &self->chip_features);
 
-       hw_atl_utils_get_fw_version(self, &self->fw_ver_actual);
+       self->fw_ver_actual = hw_atl_utils_get_fw_version(self);
 
        if (hw_atl_utils_ver_match(HW_ATL_FW_VER_1X,
                                   self->fw_ver_actual) == 0) {
@@ -313,7 +308,7 @@ int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a,
        for (++cnt; --cnt && !err;) {
                aq_hw_write_reg(self, HW_ATL_MIF_CMD, 0x00008000U);
 
-               if (IS_CHIP_FEATURE(REVISION_B1))
+               if (ATL_HW_IS_CHIP_FEATURE(self, REVISION_B1))
                        err = readx_poll_timeout_atomic(hw_atl_utils_mif_addr_get,
                                                        self, val, val != a,
                                                        1U, 1000U);
@@ -409,7 +404,7 @@ static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 addr, u32 *p,
        if (err < 0)
                goto err_exit;
 
-       if (IS_CHIP_FEATURE(REVISION_B1))
+       if (ATL_HW_IS_CHIP_FEATURE(self, REVISION_B1))
                err = hw_atl_utils_write_b1_mbox(self, addr, p, cnt, area);
        else
                err = hw_atl_utils_write_b0_mbox(self, addr, p, cnt);
@@ -438,7 +433,7 @@ int hw_atl_write_fwsettings_dwords(struct aq_hw_s *self, u32 offset, u32 *p,
                                             p, cnt, MCP_AREA_SETTINGS);
 }
 
-static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual)
+int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual)
 {
        const u32 dw_major_mask = 0xff000000U;
        const u32 dw_minor_mask = 0x00ffffffU;
@@ -501,7 +496,7 @@ int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size)
        struct aq_hw_atl_utils_fw_rpc_tid_s sw;
        int err = 0;
 
-       if (!IS_CHIP_FEATURE(MIPS)) {
+       if (!ATL_HW_IS_CHIP_FEATURE(self, MIPS)) {
                err = -1;
                goto err_exit;
        }
@@ -607,7 +602,7 @@ void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self,
        if (err < 0)
                goto err_exit;
 
-       if (IS_CHIP_FEATURE(REVISION_A0)) {
+       if (ATL_HW_IS_CHIP_FEATURE(self, REVISION_A0)) {
                unsigned int mtu = self->aq_nic_cfg ?
                                        self->aq_nic_cfg->mtu : 1514U;
                pmbox->stats.ubrc = pmbox->stats.uprc * mtu;
@@ -806,22 +801,24 @@ void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p)
        u32 mif_rev = val & 0xFFU;
        u32 chip_features = 0U;
 
+       chip_features |= ATL_HW_CHIP_ATLANTIC;
+
        if ((0xFU & mif_rev) == 1U) {
-               chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 |
-                       HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
-                       HAL_ATLANTIC_UTILS_CHIP_MIPS;
+               chip_features |= ATL_HW_CHIP_REVISION_A0 |
+                       ATL_HW_CHIP_MPI_AQ |
+                       ATL_HW_CHIP_MIPS;
        } else if ((0xFU & mif_rev) == 2U) {
-               chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_B0 |
-                       HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
-                       HAL_ATLANTIC_UTILS_CHIP_MIPS |
-                       HAL_ATLANTIC_UTILS_CHIP_TPO2 |
-                       HAL_ATLANTIC_UTILS_CHIP_RPF2;
+               chip_features |= ATL_HW_CHIP_REVISION_B0 |
+                       ATL_HW_CHIP_MPI_AQ |
+                       ATL_HW_CHIP_MIPS |
+                       ATL_HW_CHIP_TPO2 |
+                       ATL_HW_CHIP_RPF2;
        } else if ((0xFU & mif_rev) == 0xAU) {
-               chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_B1 |
-                       HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
-                       HAL_ATLANTIC_UTILS_CHIP_MIPS |
-                       HAL_ATLANTIC_UTILS_CHIP_TPO2 |
-                       HAL_ATLANTIC_UTILS_CHIP_RPF2;
+               chip_features |= ATL_HW_CHIP_REVISION_B1 |
+                       ATL_HW_CHIP_MPI_AQ |
+                       ATL_HW_CHIP_MIPS |
+                       ATL_HW_CHIP_TPO2 |
+                       ATL_HW_CHIP_RPF2;
        }
 
        *p = chip_features;
@@ -919,11 +916,9 @@ int hw_atl_utils_hw_get_regs(struct aq_hw_s *self,
        return 0;
 }
 
-int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version)
+u32 hw_atl_utils_get_fw_version(struct aq_hw_s *self)
 {
-       *fw_version = aq_hw_read_reg(self, 0x18U);
-
-       return 0;
+       return aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION);
 }
 
 static int aq_fw1x_set_wake_magic(struct aq_hw_s *self, bool wol_enabled,
index b155139..99c1b66 100644 (file)
@@ -360,6 +360,8 @@ struct aq_rx_filter_vlan {
        u8 queue;
 };
 
+#define HW_ATL_VLAN_MAX_FILTERS         16U
+
 struct aq_rx_filter_l2 {
        s8 queue;
        u8 location;
@@ -406,17 +408,6 @@ enum hw_atl_rx_ctrl_registers_l3l4 {
 #define HW_ATL_GET_REG_LOCATION_FL3L4(location) \
        ((location) - AQ_RX_FIRST_LOC_FL3L4)
 
-#define HAL_ATLANTIC_UTILS_CHIP_MIPS         0x00000001U
-#define HAL_ATLANTIC_UTILS_CHIP_TPO2         0x00000002U
-#define HAL_ATLANTIC_UTILS_CHIP_RPF2         0x00000004U
-#define HAL_ATLANTIC_UTILS_CHIP_MPI_AQ       0x00000010U
-#define HAL_ATLANTIC_UTILS_CHIP_REVISION_A0  0x01000000U
-#define HAL_ATLANTIC_UTILS_CHIP_REVISION_B0  0x02000000U
-#define HAL_ATLANTIC_UTILS_CHIP_REVISION_B1  0x04000000U
-
-#define IS_CHIP_FEATURE(_F_) (HAL_ATLANTIC_UTILS_CHIP_##_F_ & \
-       self->chip_features)
-
 enum hal_atl_utils_fw_state_e {
        MPI_DEINIT = 0,
        MPI_RESET = 1,
@@ -622,7 +613,7 @@ int hw_atl_utils_hw_set_power(struct aq_hw_s *self,
 
 int hw_atl_utils_hw_deinit(struct aq_hw_s *self);
 
-int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version);
+u32 hw_atl_utils_get_fw_version(struct aq_hw_s *self);
 
 int hw_atl_utils_update_stats(struct aq_hw_s *self);
 
@@ -643,6 +634,8 @@ int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size);
 int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
                             struct hw_atl_utils_fw_rpc **rpc);
 
+int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual);
+
 extern const struct aq_fw_ops aq_fw_1x_ops;
 extern const struct aq_fw_ops aq_fw_2x_ops;
 
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c
new file mode 100644 (file)
index 0000000..04d194f
--- /dev/null
@@ -0,0 +1,684 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Atlantic Network Driver
+ * Copyright (C) 2020 Marvell International Ltd.
+ */
+
+#include "aq_hw.h"
+#include "aq_hw_utils.h"
+#include "aq_ring.h"
+#include "aq_nic.h"
+#include "hw_atl/hw_atl_b0.h"
+#include "hw_atl/hw_atl_utils.h"
+#include "hw_atl/hw_atl_llh.h"
+#include "hw_atl2_utils.h"
+#include "hw_atl2_llh.h"
+#include "hw_atl2_internal.h"
+#include "hw_atl2_llh_internal.h"
+
+static int hw_atl2_act_rslvr_table_set(struct aq_hw_s *self, u8 location,
+                                      u32 tag, u32 mask, u32 action);
+
+#define DEFAULT_BOARD_BASIC_CAPABILITIES \
+       .is_64_dma = true,                \
+       .msix_irqs = 8U,                  \
+       .irq_mask = ~0U,                  \
+       .vecs = HW_ATL2_RSS_MAX,          \
+       .tcs = HW_ATL2_TC_MAX,    \
+       .rxd_alignment = 1U,              \
+       .rxd_size = HW_ATL2_RXD_SIZE,   \
+       .rxds_max = HW_ATL2_MAX_RXD,    \
+       .rxds_min = HW_ATL2_MIN_RXD,    \
+       .txd_alignment = 1U,              \
+       .txd_size = HW_ATL2_TXD_SIZE,   \
+       .txds_max = HW_ATL2_MAX_TXD,    \
+       .txds_min = HW_ATL2_MIN_TXD,    \
+       .txhwb_alignment = 4096U,         \
+       .tx_rings = HW_ATL2_TX_RINGS,   \
+       .rx_rings = HW_ATL2_RX_RINGS,   \
+       .hw_features = NETIF_F_HW_CSUM |  \
+                       NETIF_F_RXCSUM |  \
+                       NETIF_F_RXHASH |  \
+                       NETIF_F_SG |      \
+                       NETIF_F_TSO |     \
+                       NETIF_F_TSO6 |    \
+                       NETIF_F_LRO |     \
+                       NETIF_F_NTUPLE |  \
+                       NETIF_F_HW_VLAN_CTAG_FILTER | \
+                       NETIF_F_HW_VLAN_CTAG_RX |     \
+                       NETIF_F_HW_VLAN_CTAG_TX |     \
+                       NETIF_F_GSO_UDP_L4      |     \
+                       NETIF_F_GSO_PARTIAL,          \
+       .hw_priv_flags = IFF_UNICAST_FLT, \
+       .flow_control = true,             \
+       .mtu = HW_ATL2_MTU_JUMBO,         \
+       .mac_regs_count = 72,             \
+       .hw_alive_check_addr = 0x10U,     \
+       .priv_data_len = sizeof(struct hw_atl2_priv)
+
+const struct aq_hw_caps_s hw_atl2_caps_aqc113 = {
+       DEFAULT_BOARD_BASIC_CAPABILITIES,
+       .media_type = AQ_HW_MEDIA_TYPE_TP,
+       .link_speed_msk = AQ_NIC_RATE_10G |
+                         AQ_NIC_RATE_5G  |
+                         AQ_NIC_RATE_2GS |
+                         AQ_NIC_RATE_1G  |
+                         AQ_NIC_RATE_100M      |
+                         AQ_NIC_RATE_10M,
+};
+
+static u32 hw_atl2_sem_act_rslvr_get(struct aq_hw_s *self)
+{
+       return hw_atl_reg_glb_cpu_sem_get(self, HW_ATL2_FW_SM_ACT_RSLVR);
+}
+
+static int hw_atl2_hw_reset(struct aq_hw_s *self)
+{
+       struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
+       int err;
+
+       err = hw_atl2_utils_soft_reset(self);
+       if (err)
+               return err;
+
+       memset(priv, 0, sizeof(*priv));
+
+       self->aq_fw_ops->set_state(self, MPI_RESET);
+
+       err = aq_hw_err_from_flags(self);
+
+       return err;
+}
+
+static int hw_atl2_hw_queue_to_tc_map_set(struct aq_hw_s *self)
+{
+       if (!hw_atl_rpb_rpf_rx_traf_class_mode_get(self)) {
+               aq_hw_write_reg(self, HW_ATL2_RX_Q_TC_MAP_ADR(0), 0x11110000);
+               aq_hw_write_reg(self, HW_ATL2_RX_Q_TC_MAP_ADR(8), 0x33332222);
+               aq_hw_write_reg(self, HW_ATL2_RX_Q_TC_MAP_ADR(16), 0x55554444);
+               aq_hw_write_reg(self, HW_ATL2_RX_Q_TC_MAP_ADR(24), 0x77776666);
+       } else {
+               aq_hw_write_reg(self, HW_ATL2_RX_Q_TC_MAP_ADR(0), 0x00000000);
+               aq_hw_write_reg(self, HW_ATL2_RX_Q_TC_MAP_ADR(8), 0x11111111);
+               aq_hw_write_reg(self, HW_ATL2_RX_Q_TC_MAP_ADR(16), 0x22222222);
+               aq_hw_write_reg(self, HW_ATL2_RX_Q_TC_MAP_ADR(24), 0x33333333);
+       }
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl2_hw_qos_set(struct aq_hw_s *self)
+{
+       struct aq_nic_cfg_s *cfg = self->aq_nic_cfg;
+       u32 tx_buff_size = HW_ATL2_TXBUF_MAX;
+       u32 rx_buff_size = HW_ATL2_RXBUF_MAX;
+       unsigned int prio = 0U;
+       u32 threshold = 0U;
+       u32 tc = 0U;
+
+       /* TPS Descriptor rate init */
+       hw_atl_tps_tx_pkt_shed_desc_rate_curr_time_res_set(self, 0x0U);
+       hw_atl_tps_tx_pkt_shed_desc_rate_lim_set(self, 0xA);
+
+       /* TPS VM init */
+       hw_atl_tps_tx_pkt_shed_desc_vm_arb_mode_set(self, 0U);
+
+       /* TPS TC credits init */
+       hw_atl_tps_tx_pkt_shed_desc_tc_arb_mode_set(self, 0U);
+       hw_atl_tps_tx_pkt_shed_data_arb_mode_set(self, 0U);
+
+       tc = 0;
+
+       /* TX Packet Scheduler Data TC0 */
+       hw_atl2_tps_tx_pkt_shed_tc_data_max_credit_set(self, 0xFFF0, tc);
+       hw_atl2_tps_tx_pkt_shed_tc_data_weight_set(self, 0x640, tc);
+       hw_atl_tps_tx_pkt_shed_desc_tc_max_credit_set(self, 0x50, tc);
+       hw_atl_tps_tx_pkt_shed_desc_tc_weight_set(self, 0x1E, tc);
+
+       /* Tx buf size TC0 */
+       hw_atl_tpb_tx_pkt_buff_size_per_tc_set(self, tx_buff_size, tc);
+
+       threshold = (tx_buff_size * (1024 / 32U) * 66U) / 100U;
+       hw_atl_tpb_tx_buff_hi_threshold_per_tc_set(self, threshold, tc);
+
+       threshold = (tx_buff_size * (1024 / 32U) * 50U) / 100U;
+       hw_atl_tpb_tx_buff_lo_threshold_per_tc_set(self, threshold, tc);
+
+       /* QoS Rx buf size per TC */
+       hw_atl_rpb_rx_pkt_buff_size_per_tc_set(self, rx_buff_size, tc);
+
+       threshold = (rx_buff_size * (1024U / 32U) * 66U) / 100U;
+       hw_atl_rpb_rx_buff_hi_threshold_per_tc_set(self, threshold, tc);
+
+       threshold = (rx_buff_size * (1024U / 32U) * 50U) / 100U;
+       hw_atl_rpb_rx_buff_lo_threshold_per_tc_set(self, threshold, tc);
+
+       /* QoS 802.1p priority -> TC mapping */
+       for (prio = 0; prio < 8; ++prio)
+               hw_atl_rpf_rpb_user_priority_tc_map_set(self, prio,
+                                                       cfg->tcs * prio / 8);
+
+       /* ATL2 Apply legacy ring to TC mapping */
+       hw_atl2_hw_queue_to_tc_map_set(self);
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl2_hw_rss_set(struct aq_hw_s *self,
+                             struct aq_rss_parameters *rss_params)
+{
+       u8 *indirection_table = rss_params->indirection_table;
+       int i;
+
+       for (i = HW_ATL2_RSS_REDIRECTION_MAX; i--;)
+               hw_atl2_new_rpf_rss_redir_set(self, 0, i, indirection_table[i]);
+
+       return hw_atl_b0_hw_rss_set(self, rss_params);
+}
+
+static int hw_atl2_hw_init_tx_path(struct aq_hw_s *self)
+{
+       /* Tx TC/RSS number config */
+       hw_atl_tpb_tps_tx_tc_mode_set(self, 1U);
+
+       hw_atl_thm_lso_tcp_flag_of_first_pkt_set(self, 0x0FF6U);
+       hw_atl_thm_lso_tcp_flag_of_middle_pkt_set(self, 0x0FF6U);
+       hw_atl_thm_lso_tcp_flag_of_last_pkt_set(self, 0x0F7FU);
+
+       /* Tx interrupts */
+       hw_atl_tdm_tx_desc_wr_wb_irq_en_set(self, 1U);
+
+       /* misc */
+       hw_atl_tdm_tx_dca_en_set(self, 0U);
+       hw_atl_tdm_tx_dca_mode_set(self, 0U);
+
+       hw_atl_tpb_tx_path_scp_ins_en_set(self, 1U);
+
+       hw_atl2_tpb_tx_buf_clk_gate_en_set(self, 0U);
+
+       return aq_hw_err_from_flags(self);
+}
+
+static void hw_atl2_hw_init_new_rx_filters(struct aq_hw_s *self)
+{
+       struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
+       u8 index;
+
+       hw_atl2_rpf_act_rslvr_section_en_set(self, 0xFFFF);
+       hw_atl2_rpfl2_uc_flr_tag_set(self, HW_ATL2_RPF_TAG_BASE_UC,
+                                    HW_ATL2_MAC_UC);
+       hw_atl2_rpfl2_bc_flr_tag_set(self, HW_ATL2_RPF_TAG_BASE_UC);
+
+       index = priv->art_base_index + HW_ATL2_RPF_L2_PROMISC_OFF_INDEX;
+       hw_atl2_act_rslvr_table_set(self, index, 0,
+                                   HW_ATL2_RPF_TAG_UC_MASK |
+                                       HW_ATL2_RPF_TAG_ALLMC_MASK,
+                                   HW_ATL2_ACTION_DROP);
+
+       index = priv->art_base_index + HW_ATL2_RPF_VLAN_PROMISC_OFF_INDEX;
+       hw_atl2_act_rslvr_table_set(self, index, 0,
+                                   HW_ATL2_RPF_TAG_VLAN_MASK |
+                                       HW_ATL2_RPF_TAG_UNTAG_MASK,
+                                   HW_ATL2_ACTION_DROP);
+
+       index = priv->art_base_index + HW_ATL2_RPF_VLAN_INDEX;
+       hw_atl2_act_rslvr_table_set(self, index, HW_ATL2_RPF_TAG_BASE_VLAN,
+                                   HW_ATL2_RPF_TAG_VLAN_MASK,
+                                   HW_ATL2_ACTION_ASSIGN_TC(0));
+
+       index = priv->art_base_index + HW_ATL2_RPF_MAC_INDEX;
+       hw_atl2_act_rslvr_table_set(self, index, HW_ATL2_RPF_TAG_BASE_UC,
+                                   HW_ATL2_RPF_TAG_UC_MASK,
+                                   HW_ATL2_ACTION_ASSIGN_TC(0));
+
+       index = priv->art_base_index + HW_ATL2_RPF_ALLMC_INDEX;
+       hw_atl2_act_rslvr_table_set(self, index, HW_ATL2_RPF_TAG_BASE_ALLMC,
+                                   HW_ATL2_RPF_TAG_ALLMC_MASK,
+                                   HW_ATL2_ACTION_ASSIGN_TC(0));
+
+       index = priv->art_base_index + HW_ATL2_RPF_UNTAG_INDEX;
+       hw_atl2_act_rslvr_table_set(self, index, HW_ATL2_RPF_TAG_UNTAG_MASK,
+                                   HW_ATL2_RPF_TAG_UNTAG_MASK,
+                                   HW_ATL2_ACTION_ASSIGN_TC(0));
+
+       index = priv->art_base_index + HW_ATL2_RPF_VLAN_PROMISC_ON_INDEX;
+       hw_atl2_act_rslvr_table_set(self, index, 0, HW_ATL2_RPF_TAG_VLAN_MASK,
+                                   HW_ATL2_ACTION_DISABLE);
+
+       index = priv->art_base_index + HW_ATL2_RPF_L2_PROMISC_ON_INDEX;
+       hw_atl2_act_rslvr_table_set(self, index, 0, HW_ATL2_RPF_TAG_UC_MASK,
+                                   HW_ATL2_ACTION_DISABLE);
+}
+
+static void hw_atl2_hw_new_rx_filter_vlan_promisc(struct aq_hw_s *self,
+                                                 bool promisc)
+{
+       u16 off_action = (!promisc &&
+                         !hw_atl_rpfl2promiscuous_mode_en_get(self)) ?
+                               HW_ATL2_ACTION_DROP : HW_ATL2_ACTION_DISABLE;
+       struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
+       u8 index;
+
+       index = priv->art_base_index + HW_ATL2_RPF_VLAN_PROMISC_OFF_INDEX;
+       hw_atl2_act_rslvr_table_set(self, index, 0,
+                                   HW_ATL2_RPF_TAG_VLAN_MASK |
+                                   HW_ATL2_RPF_TAG_UNTAG_MASK, off_action);
+}
+
+static void hw_atl2_hw_new_rx_filter_promisc(struct aq_hw_s *self, bool promisc)
+{
+       u16 off_action = promisc ? HW_ATL2_ACTION_DISABLE : HW_ATL2_ACTION_DROP;
+       struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
+       bool vlan_promisc_enable;
+       u8 index;
+
+       index = priv->art_base_index + HW_ATL2_RPF_L2_PROMISC_OFF_INDEX;
+       hw_atl2_act_rslvr_table_set(self, index, 0,
+                                   HW_ATL2_RPF_TAG_UC_MASK |
+                                   HW_ATL2_RPF_TAG_ALLMC_MASK,
+                                   off_action);
+
+       /* turn VLAN promisc mode too */
+       vlan_promisc_enable = hw_atl_rpf_vlan_prom_mode_en_get(self);
+       hw_atl2_hw_new_rx_filter_vlan_promisc(self, promisc |
+                                             vlan_promisc_enable);
+}
+
+static int hw_atl2_act_rslvr_table_set(struct aq_hw_s *self, u8 location,
+                                      u32 tag, u32 mask, u32 action)
+{
+       u32 val;
+       int err;
+
+       err = readx_poll_timeout_atomic(hw_atl2_sem_act_rslvr_get,
+                                       self, val, val == 1,
+                                       1, 10000U);
+       if (err)
+               return err;
+
+       hw_atl2_rpf_act_rslvr_record_set(self, location, tag, mask,
+                                        action);
+
+       hw_atl_reg_glb_cpu_sem_set(self, 1, HW_ATL2_FW_SM_ACT_RSLVR);
+
+       return err;
+}
+
+static int hw_atl2_hw_init_rx_path(struct aq_hw_s *self)
+{
+       struct aq_nic_cfg_s *cfg = self->aq_nic_cfg;
+       int i;
+
+       /* Rx TC/RSS number config */
+       hw_atl_rpb_rpf_rx_traf_class_mode_set(self, 1U);
+
+       /* Rx flow control */
+       hw_atl_rpb_rx_flow_ctl_mode_set(self, 1U);
+
+       hw_atl2_rpf_rss_hash_type_set(self, HW_ATL2_RPF_RSS_HASH_TYPE_ALL);
+
+       /* RSS Ring selection */
+       hw_atl_reg_rx_flr_rss_control1set(self, cfg->is_rss ?
+                                               HW_ATL_RSS_ENABLED_3INDEX_BITS :
+                                               HW_ATL_RSS_DISABLED);
+
+       /* Multicast filters */
+       for (i = HW_ATL2_MAC_MAX; i--;) {
+               hw_atl_rpfl2_uc_flr_en_set(self, (i == 0U) ? 1U : 0U, i);
+               hw_atl_rpfl2unicast_flr_act_set(self, 1U, i);
+       }
+
+       hw_atl_reg_rx_flr_mcst_flr_msk_set(self, 0x00000000U);
+       hw_atl_reg_rx_flr_mcst_flr_set(self, HW_ATL_MCAST_FLT_ANY_TO_HOST, 0U);
+
+       /* Vlan filters */
+       hw_atl_rpf_vlan_outer_etht_set(self, ETH_P_8021AD);
+       hw_atl_rpf_vlan_inner_etht_set(self, ETH_P_8021Q);
+
+       hw_atl_rpf_vlan_prom_mode_en_set(self, 1);
+
+       /* Always accept untagged packets */
+       hw_atl_rpf_vlan_accept_untagged_packets_set(self, 1U);
+       hw_atl_rpf_vlan_untagged_act_set(self, 1U);
+
+       hw_atl2_hw_init_new_rx_filters(self);
+
+       /* Rx Interrupts */
+       hw_atl_rdm_rx_desc_wr_wb_irq_en_set(self, 1U);
+
+       hw_atl_rpfl2broadcast_flr_act_set(self, 1U);
+       hw_atl_rpfl2broadcast_count_threshold_set(self, 0xFFFFU & (~0U / 256U));
+
+       hw_atl_rdm_rx_dca_en_set(self, 0U);
+       hw_atl_rdm_rx_dca_mode_set(self, 0U);
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl2_hw_init(struct aq_hw_s *self, u8 *mac_addr)
+{
+       static u32 aq_hw_atl2_igcr_table_[4][2] = {
+               [AQ_HW_IRQ_INVALID] = { 0x20000000U, 0x20000000U },
+               [AQ_HW_IRQ_LEGACY]  = { 0x20000080U, 0x20000080U },
+               [AQ_HW_IRQ_MSI]     = { 0x20000021U, 0x20000025U },
+               [AQ_HW_IRQ_MSIX]    = { 0x20000022U, 0x20000026U },
+       };
+
+       struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
+       struct aq_nic_cfg_s *aq_nic_cfg = self->aq_nic_cfg;
+       u8 base_index, count;
+       int err;
+
+       err = hw_atl2_utils_get_action_resolve_table_caps(self, &base_index,
+                                                         &count);
+       if (err)
+               return err;
+
+       priv->art_base_index = 8 * base_index;
+
+       hw_atl2_init_launchtime(self);
+
+       hw_atl2_hw_init_tx_path(self);
+       hw_atl2_hw_init_rx_path(self);
+
+       hw_atl_b0_hw_mac_addr_set(self, mac_addr);
+
+       self->aq_fw_ops->set_link_speed(self, aq_nic_cfg->link_speed_msk);
+       self->aq_fw_ops->set_state(self, MPI_INIT);
+
+       hw_atl2_hw_qos_set(self);
+       hw_atl2_hw_rss_set(self, &aq_nic_cfg->aq_rss);
+       hw_atl_b0_hw_rss_hash_set(self, &aq_nic_cfg->aq_rss);
+
+       hw_atl2_rpf_new_enable_set(self, 1);
+
+       /* Reset link status and read out initial hardware counters */
+       self->aq_link_status.mbps = 0;
+       self->aq_fw_ops->update_stats(self);
+
+       err = aq_hw_err_from_flags(self);
+       if (err < 0)
+               goto err_exit;
+
+       /* Interrupts */
+       hw_atl_reg_irq_glb_ctl_set(self,
+                                  aq_hw_atl2_igcr_table_[aq_nic_cfg->irq_type]
+                                                [(aq_nic_cfg->vecs > 1U) ?
+                                                 1 : 0]);
+
+       hw_atl_itr_irq_auto_masklsw_set(self, aq_nic_cfg->aq_hw_caps->irq_mask);
+
+       /* Interrupts */
+       hw_atl_reg_gen_irq_map_set(self,
+                                  ((HW_ATL2_ERR_INT << 0x18) |
+                                   (1U << 0x1F)) |
+                                  ((HW_ATL2_ERR_INT << 0x10) |
+                                   (1U << 0x17)), 0U);
+
+       hw_atl_b0_hw_offload_set(self, aq_nic_cfg);
+
+err_exit:
+       return err;
+}
+
+static int hw_atl2_hw_ring_rx_init(struct aq_hw_s *self,
+                                  struct aq_ring_s *aq_ring,
+                                  struct aq_ring_param_s *aq_ring_param)
+{
+       return hw_atl_b0_hw_ring_rx_init(self, aq_ring, aq_ring_param);
+}
+
+static int hw_atl2_hw_ring_tx_init(struct aq_hw_s *self,
+                                  struct aq_ring_s *aq_ring,
+                                  struct aq_ring_param_s *aq_ring_param)
+{
+       return hw_atl_b0_hw_ring_tx_init(self, aq_ring, aq_ring_param);
+}
+
+#define IS_FILTER_ENABLED(_F_) ((packet_filter & (_F_)) ? 1U : 0U)
+
+static int hw_atl2_hw_packet_filter_set(struct aq_hw_s *self,
+                                       unsigned int packet_filter)
+{
+       hw_atl2_hw_new_rx_filter_promisc(self, IS_FILTER_ENABLED(IFF_PROMISC));
+
+       return hw_atl_b0_hw_packet_filter_set(self, packet_filter);
+}
+
+#undef IS_FILTER_ENABLED
+
+static int hw_atl2_hw_multicast_list_set(struct aq_hw_s *self,
+                                        u8 ar_mac
+                                        [AQ_HW_MULTICAST_ADDRESS_MAX]
+                                        [ETH_ALEN],
+                                        u32 count)
+{
+       struct aq_nic_cfg_s *cfg = self->aq_nic_cfg;
+       int err = 0;
+
+       if (count > (HW_ATL2_MAC_MAX - HW_ATL2_MAC_MIN)) {
+               err = -EBADRQC;
+               goto err_exit;
+       }
+       for (cfg->mc_list_count = 0U;
+                       cfg->mc_list_count < count;
+                       ++cfg->mc_list_count) {
+               u32 i = cfg->mc_list_count;
+               u32 h = (ar_mac[i][0] << 8) | (ar_mac[i][1]);
+               u32 l = (ar_mac[i][2] << 24) | (ar_mac[i][3] << 16) |
+                                       (ar_mac[i][4] << 8) | ar_mac[i][5];
+
+               hw_atl_rpfl2_uc_flr_en_set(self, 0U, HW_ATL2_MAC_MIN + i);
+
+               hw_atl_rpfl2unicast_dest_addresslsw_set(self, l,
+                                                       HW_ATL2_MAC_MIN + i);
+
+               hw_atl_rpfl2unicast_dest_addressmsw_set(self, h,
+                                                       HW_ATL2_MAC_MIN + i);
+
+               hw_atl2_rpfl2_uc_flr_tag_set(self, 1, HW_ATL2_MAC_MIN + i);
+
+               hw_atl_rpfl2_uc_flr_en_set(self, (cfg->is_mc_list_enabled),
+                                          HW_ATL2_MAC_MIN + i);
+       }
+
+       err = aq_hw_err_from_flags(self);
+
+err_exit:
+       return err;
+}
+
+static int hw_atl2_hw_interrupt_moderation_set(struct aq_hw_s *self)
+{
+       unsigned int i = 0U;
+       u32 itr_tx = 2U;
+       u32 itr_rx = 2U;
+
+       switch (self->aq_nic_cfg->itr) {
+       case  AQ_CFG_INTERRUPT_MODERATION_ON:
+       case  AQ_CFG_INTERRUPT_MODERATION_AUTO:
+               hw_atl_tdm_tx_desc_wr_wb_irq_en_set(self, 0U);
+               hw_atl_tdm_tdm_intr_moder_en_set(self, 1U);
+               hw_atl_rdm_rx_desc_wr_wb_irq_en_set(self, 0U);
+               hw_atl_rdm_rdm_intr_moder_en_set(self, 1U);
+
+               if (self->aq_nic_cfg->itr == AQ_CFG_INTERRUPT_MODERATION_ON) {
+                       /* HW timers are in 2us units */
+                       int tx_max_timer = self->aq_nic_cfg->tx_itr / 2;
+                       int tx_min_timer = tx_max_timer / 2;
+
+                       int rx_max_timer = self->aq_nic_cfg->rx_itr / 2;
+                       int rx_min_timer = rx_max_timer / 2;
+
+                       tx_max_timer = min(HW_ATL2_INTR_MODER_MAX,
+                                          tx_max_timer);
+                       tx_min_timer = min(HW_ATL2_INTR_MODER_MIN,
+                                          tx_min_timer);
+                       rx_max_timer = min(HW_ATL2_INTR_MODER_MAX,
+                                          rx_max_timer);
+                       rx_min_timer = min(HW_ATL2_INTR_MODER_MIN,
+                                          rx_min_timer);
+
+                       itr_tx |= tx_min_timer << 0x8U;
+                       itr_tx |= tx_max_timer << 0x10U;
+                       itr_rx |= rx_min_timer << 0x8U;
+                       itr_rx |= rx_max_timer << 0x10U;
+               } else {
+                       static unsigned int hw_atl2_timers_table_tx_[][2] = {
+                               {0xfU, 0xffU}, /* 10Gbit */
+                               {0xfU, 0x1ffU}, /* 5Gbit */
+                               {0xfU, 0x1ffU}, /* 5Gbit 5GS */
+                               {0xfU, 0x1ffU}, /* 2.5Gbit */
+                               {0xfU, 0x1ffU}, /* 1Gbit */
+                               {0xfU, 0x1ffU}, /* 100Mbit */
+                       };
+                       static unsigned int hw_atl2_timers_table_rx_[][2] = {
+                               {0x6U, 0x38U},/* 10Gbit */
+                               {0xCU, 0x70U},/* 5Gbit */
+                               {0xCU, 0x70U},/* 5Gbit 5GS */
+                               {0x18U, 0xE0U},/* 2.5Gbit */
+                               {0x30U, 0x80U},/* 1Gbit */
+                               {0x4U, 0x50U},/* 100Mbit */
+                       };
+                       unsigned int mbps = self->aq_link_status.mbps;
+                       unsigned int speed_index;
+
+                       speed_index = hw_atl_utils_mbps_2_speed_index(mbps);
+
+                       /* Update user visible ITR settings */
+                       self->aq_nic_cfg->tx_itr = hw_atl2_timers_table_tx_
+                                                       [speed_index][1] * 2;
+                       self->aq_nic_cfg->rx_itr = hw_atl2_timers_table_rx_
+                                                       [speed_index][1] * 2;
+
+                       itr_tx |= hw_atl2_timers_table_tx_
+                                               [speed_index][0] << 0x8U;
+                       itr_tx |= hw_atl2_timers_table_tx_
+                                               [speed_index][1] << 0x10U;
+
+                       itr_rx |= hw_atl2_timers_table_rx_
+                                               [speed_index][0] << 0x8U;
+                       itr_rx |= hw_atl2_timers_table_rx_
+                                               [speed_index][1] << 0x10U;
+               }
+               break;
+       case AQ_CFG_INTERRUPT_MODERATION_OFF:
+               hw_atl_tdm_tx_desc_wr_wb_irq_en_set(self, 1U);
+               hw_atl_tdm_tdm_intr_moder_en_set(self, 0U);
+               hw_atl_rdm_rx_desc_wr_wb_irq_en_set(self, 1U);
+               hw_atl_rdm_rdm_intr_moder_en_set(self, 0U);
+               itr_tx = 0U;
+               itr_rx = 0U;
+               break;
+       }
+
+       for (i = HW_ATL2_RINGS_MAX; i--;) {
+               hw_atl2_reg_tx_intr_moder_ctrl_set(self, itr_tx, i);
+               hw_atl_reg_rx_intr_moder_ctrl_set(self, itr_rx, i);
+       }
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl2_hw_stop(struct aq_hw_s *self)
+{
+       hw_atl_b0_hw_irq_disable(self, HW_ATL2_INT_MASK);
+
+       return 0;
+}
+
+static struct aq_stats_s *hw_atl2_utils_get_hw_stats(struct aq_hw_s *self)
+{
+       return &self->curr_stats;
+}
+
+static int hw_atl2_hw_vlan_set(struct aq_hw_s *self,
+                              struct aq_rx_filter_vlan *aq_vlans)
+{
+       struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
+       u32 queue;
+       u8 index;
+       int i;
+
+       hw_atl_rpf_vlan_prom_mode_en_set(self, 1U);
+
+       for (i = 0; i < HW_ATL_VLAN_MAX_FILTERS; i++) {
+               queue = HW_ATL2_ACTION_ASSIGN_QUEUE(aq_vlans[i].queue);
+
+               hw_atl_rpf_vlan_flr_en_set(self, 0U, i);
+               hw_atl_rpf_vlan_rxq_en_flr_set(self, 0U, i);
+               index = priv->art_base_index + HW_ATL2_RPF_VLAN_USER_INDEX + i;
+               hw_atl2_act_rslvr_table_set(self, index, 0, 0,
+                                           HW_ATL2_ACTION_DISABLE);
+               if (aq_vlans[i].enable) {
+                       hw_atl_rpf_vlan_id_flr_set(self,
+                                                  aq_vlans[i].vlan_id, i);
+                       hw_atl_rpf_vlan_flr_act_set(self, 1U, i);
+                       hw_atl_rpf_vlan_flr_en_set(self, 1U, i);
+
+                       if (aq_vlans[i].queue != 0xFF) {
+                               hw_atl_rpf_vlan_rxq_flr_set(self,
+                                                           aq_vlans[i].queue,
+                                                           i);
+                               hw_atl_rpf_vlan_rxq_en_flr_set(self, 1U, i);
+
+                               hw_atl2_rpf_vlan_flr_tag_set(self, i + 2, i);
+
+                               index = priv->art_base_index +
+                                       HW_ATL2_RPF_VLAN_USER_INDEX + i;
+                               hw_atl2_act_rslvr_table_set(self, index,
+                                       (i + 2) << HW_ATL2_RPF_TAG_VLAN_OFFSET,
+                                       HW_ATL2_RPF_TAG_VLAN_MASK, queue);
+                       } else {
+                               hw_atl2_rpf_vlan_flr_tag_set(self, 1, i);
+                       }
+               }
+       }
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl2_hw_vlan_ctrl(struct aq_hw_s *self, bool enable)
+{
+       /* set promisc in case of disabing the vlan filter */
+       hw_atl_rpf_vlan_prom_mode_en_set(self, !enable);
+       hw_atl2_hw_new_rx_filter_vlan_promisc(self, !enable);
+
+       return aq_hw_err_from_flags(self);
+}
+
+const struct aq_hw_ops hw_atl2_ops = {
+       .hw_soft_reset        = hw_atl2_utils_soft_reset,
+       .hw_prepare           = hw_atl2_utils_initfw,
+       .hw_set_mac_address   = hw_atl_b0_hw_mac_addr_set,
+       .hw_init              = hw_atl2_hw_init,
+       .hw_reset             = hw_atl2_hw_reset,
+       .hw_start             = hw_atl_b0_hw_start,
+       .hw_ring_tx_start     = hw_atl_b0_hw_ring_tx_start,
+       .hw_ring_tx_stop      = hw_atl_b0_hw_ring_tx_stop,
+       .hw_ring_rx_start     = hw_atl_b0_hw_ring_rx_start,
+       .hw_ring_rx_stop      = hw_atl_b0_hw_ring_rx_stop,
+       .hw_stop              = hw_atl2_hw_stop,
+
+       .hw_ring_tx_xmit         = hw_atl_b0_hw_ring_tx_xmit,
+       .hw_ring_tx_head_update  = hw_atl_b0_hw_ring_tx_head_update,
+
+       .hw_ring_rx_receive      = hw_atl_b0_hw_ring_rx_receive,
+       .hw_ring_rx_fill         = hw_atl_b0_hw_ring_rx_fill,
+
+       .hw_irq_enable           = hw_atl_b0_hw_irq_enable,
+       .hw_irq_disable          = hw_atl_b0_hw_irq_disable,
+       .hw_irq_read             = hw_atl_b0_hw_irq_read,
+
+       .hw_ring_rx_init             = hw_atl2_hw_ring_rx_init,
+       .hw_ring_tx_init             = hw_atl2_hw_ring_tx_init,
+       .hw_packet_filter_set        = hw_atl2_hw_packet_filter_set,
+       .hw_filter_vlan_set          = hw_atl2_hw_vlan_set,
+       .hw_filter_vlan_ctrl         = hw_atl2_hw_vlan_ctrl,
+       .hw_multicast_list_set       = hw_atl2_hw_multicast_list_set,
+       .hw_interrupt_moderation_set = hw_atl2_hw_interrupt_moderation_set,
+       .hw_rss_set                  = hw_atl2_hw_rss_set,
+       .hw_rss_hash_set             = hw_atl_b0_hw_rss_hash_set,
+       .hw_get_hw_stats             = hw_atl2_utils_get_hw_stats,
+       .hw_get_fw_version           = hw_atl2_utils_get_fw_version,
+       .hw_set_offload              = hw_atl_b0_hw_offload_set,
+};
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.h
new file mode 100644 (file)
index 0000000..de8723f
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Atlantic Network Driver
+ * Copyright (C) 2020 Marvell International Ltd.
+ */
+
+#ifndef HW_ATL2_H
+#define HW_ATL2_H
+
+#include "aq_common.h"
+
+extern const struct aq_hw_caps_s hw_atl2_caps_aqc113;
+extern const struct aq_hw_ops hw_atl2_ops;
+
+#endif /* HW_ATL2_H */
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_internal.h
new file mode 100644 (file)
index 0000000..e66b358
--- /dev/null
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Atlantic Network Driver
+ * Copyright (C) 2020 Marvell International Ltd.
+ */
+
+#ifndef HW_ATL2_INTERNAL_H
+#define HW_ATL2_INTERNAL_H
+
+#include "hw_atl2_utils.h"
+
+#define HW_ATL2_MTU_JUMBO  16352U
+#define HW_ATL2_MTU        1514U
+
+#define HW_ATL2_TX_RINGS 4U
+#define HW_ATL2_RX_RINGS 4U
+
+#define HW_ATL2_RINGS_MAX 32U
+#define HW_ATL2_TXD_SIZE       (16U)
+#define HW_ATL2_RXD_SIZE       (16U)
+
+#define HW_ATL2_MAC_UC   0U
+#define HW_ATL2_MAC_MIN  1U
+#define HW_ATL2_MAC_MAX  38U
+
+/* interrupts */
+#define HW_ATL2_ERR_INT 8U
+#define HW_ATL2_INT_MASK  (0xFFFFFFFFU)
+
+#define HW_ATL2_TXBUF_MAX              128U
+#define HW_ATL2_RXBUF_MAX              192U
+
+#define HW_ATL2_RSS_REDIRECTION_MAX 64U
+
+#define HW_ATL2_TC_MAX 1U
+#define HW_ATL2_RSS_MAX 8U
+
+#define HW_ATL2_INTR_MODER_MAX  0x1FF
+#define HW_ATL2_INTR_MODER_MIN  0xFF
+
+#define HW_ATL2_MIN_RXD \
+       (ALIGN(AQ_CFG_SKB_FRAGS_MAX + 1U, AQ_HW_RXD_MULTIPLE))
+#define HW_ATL2_MIN_TXD \
+       (ALIGN(AQ_CFG_SKB_FRAGS_MAX + 1U, AQ_HW_TXD_MULTIPLE))
+
+#define HW_ATL2_MAX_RXD 8184U
+#define HW_ATL2_MAX_TXD 8184U
+
+#define HW_ATL2_FW_SM_ACT_RSLVR  0x3U
+
+#define HW_ATL2_RPF_TAG_UC_OFFSET      0x0
+#define HW_ATL2_RPF_TAG_ALLMC_OFFSET   0x6
+#define HW_ATL2_RPF_TAG_ET_OFFSET      0x7
+#define HW_ATL2_RPF_TAG_VLAN_OFFSET    0xA
+#define HW_ATL2_RPF_TAG_UNTAG_OFFSET   0xE
+#define HW_ATL2_RPF_TAG_L3_V4_OFFSET   0xF
+#define HW_ATL2_RPF_TAG_L3_V6_OFFSET   0x12
+#define HW_ATL2_RPF_TAG_L4_OFFSET      0x15
+#define HW_ATL2_RPF_TAG_L4_FLEX_OFFSET 0x18
+#define HW_ATL2_RPF_TAG_FLEX_OFFSET    0x1B
+#define HW_ATL2_RPF_TAG_PCP_OFFSET     0x1D
+
+#define HW_ATL2_RPF_TAG_UC_MASK    (0x0000003F << HW_ATL2_RPF_TAG_UC_OFFSET)
+#define HW_ATL2_RPF_TAG_ALLMC_MASK (0x00000001 << HW_ATL2_RPF_TAG_ALLMC_OFFSET)
+#define HW_ATL2_RPF_TAG_UNTAG_MASK (0x00000001 << HW_ATL2_RPF_TAG_UNTAG_OFFSET)
+#define HW_ATL2_RPF_TAG_VLAN_MASK  (0x0000000F << HW_ATL2_RPF_TAG_VLAN_OFFSET)
+#define HW_ATL2_RPF_TAG_ET_MASK    (0x00000007 << HW_ATL2_RPF_TAG_ET_OFFSET)
+#define HW_ATL2_RPF_TAG_L3_V4_MASK (0x00000007 << HW_ATL2_RPF_TAG_L3_V4_OFFSET)
+#define HW_ATL2_RPF_TAG_L3_V6_MASK (0x00000007 << HW_ATL2_RPF_TAG_L3_V6_OFFSET)
+#define HW_ATL2_RPF_TAG_L4_MASK    (0x00000007 << HW_ATL2_RPF_TAG_L4_OFFSET)
+#define HW_ATL2_RPF_TAG_PCP_MASK   (0x00000007 << HW_ATL2_RPF_TAG_PCP_OFFSET)
+
+#define HW_ATL2_RPF_TAG_BASE_UC    BIT(HW_ATL2_RPF_TAG_UC_OFFSET)
+#define HW_ATL2_RPF_TAG_BASE_ALLMC BIT(HW_ATL2_RPF_TAG_ALLMC_OFFSET)
+#define HW_ATL2_RPF_TAG_BASE_UNTAG BIT(HW_ATL2_RPF_TAG_UNTAG_OFFSET)
+#define HW_ATL2_RPF_TAG_BASE_VLAN  BIT(HW_ATL2_RPF_TAG_VLAN_OFFSET)
+
+enum HW_ATL2_RPF_ART_INDEX {
+       HW_ATL2_RPF_L2_PROMISC_OFF_INDEX,
+       HW_ATL2_RPF_VLAN_PROMISC_OFF_INDEX,
+       HW_ATL2_RPF_L3L4_USER_INDEX     = 8,
+       HW_ATL2_RPF_ET_PCP_USER_INDEX   = HW_ATL2_RPF_L3L4_USER_INDEX + 16,
+       HW_ATL2_RPF_VLAN_USER_INDEX     = HW_ATL2_RPF_ET_PCP_USER_INDEX + 16,
+       HW_ATL2_RPF_PCP_TO_TC_INDEX     = HW_ATL2_RPF_VLAN_USER_INDEX +
+                                         HW_ATL_VLAN_MAX_FILTERS,
+       HW_ATL2_RPF_VLAN_INDEX          = HW_ATL2_RPF_PCP_TO_TC_INDEX +
+                                         AQ_CFG_TCS_MAX,
+       HW_ATL2_RPF_MAC_INDEX,
+       HW_ATL2_RPF_ALLMC_INDEX,
+       HW_ATL2_RPF_UNTAG_INDEX,
+       HW_ATL2_RPF_VLAN_PROMISC_ON_INDEX,
+       HW_ATL2_RPF_L2_PROMISC_ON_INDEX,
+};
+
+#define HW_ATL2_ACTION(ACTION, RSS, INDEX, VALID) \
+       ((((ACTION) & 0x3U) << 8) | \
+       (((RSS) & 0x1U) << 7) | \
+       (((INDEX) & 0x3FU) << 2) | \
+       (((VALID) & 0x1U) << 0))
+
+#define HW_ATL2_ACTION_DROP HW_ATL2_ACTION(0, 0, 0, 1)
+#define HW_ATL2_ACTION_DISABLE HW_ATL2_ACTION(0, 0, 0, 0)
+#define HW_ATL2_ACTION_ASSIGN_QUEUE(QUEUE) HW_ATL2_ACTION(1, 0, (QUEUE), 1)
+#define HW_ATL2_ACTION_ASSIGN_TC(TC) HW_ATL2_ACTION(1, 1, (TC), 1)
+
+enum HW_ATL2_RPF_RSS_HASH_TYPE {
+       HW_ATL2_RPF_RSS_HASH_TYPE_NONE = 0,
+       HW_ATL2_RPF_RSS_HASH_TYPE_IPV4 = BIT(0),
+       HW_ATL2_RPF_RSS_HASH_TYPE_IPV4_TCP = BIT(1),
+       HW_ATL2_RPF_RSS_HASH_TYPE_IPV4_UDP = BIT(2),
+       HW_ATL2_RPF_RSS_HASH_TYPE_IPV6 = BIT(3),
+       HW_ATL2_RPF_RSS_HASH_TYPE_IPV6_TCP = BIT(4),
+       HW_ATL2_RPF_RSS_HASH_TYPE_IPV6_UDP = BIT(5),
+       HW_ATL2_RPF_RSS_HASH_TYPE_IPV6_EX = BIT(6),
+       HW_ATL2_RPF_RSS_HASH_TYPE_IPV6_EX_TCP = BIT(7),
+       HW_ATL2_RPF_RSS_HASH_TYPE_IPV6_EX_UDP = BIT(8),
+       HW_ATL2_RPF_RSS_HASH_TYPE_ALL = HW_ATL2_RPF_RSS_HASH_TYPE_IPV4 |
+                                       HW_ATL2_RPF_RSS_HASH_TYPE_IPV4_TCP |
+                                       HW_ATL2_RPF_RSS_HASH_TYPE_IPV4_UDP |
+                                       HW_ATL2_RPF_RSS_HASH_TYPE_IPV6 |
+                                       HW_ATL2_RPF_RSS_HASH_TYPE_IPV6_TCP |
+                                       HW_ATL2_RPF_RSS_HASH_TYPE_IPV6_UDP |
+                                       HW_ATL2_RPF_RSS_HASH_TYPE_IPV6_EX |
+                                       HW_ATL2_RPF_RSS_HASH_TYPE_IPV6_EX_TCP |
+                                       HW_ATL2_RPF_RSS_HASH_TYPE_IPV6_EX_UDP,
+};
+
+#define HW_ATL_RSS_DISABLED 0x00000000U
+#define HW_ATL_RSS_ENABLED_3INDEX_BITS 0xB3333333U
+
+#define HW_ATL_MCAST_FLT_ANY_TO_HOST 0x00010FFFU
+
+struct hw_atl2_priv {
+       struct statistics_s last_stats;
+       unsigned int art_base_index;
+};
+
+#endif /* HW_ATL2_INTERNAL_H */
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.c
new file mode 100644 (file)
index 0000000..e779d70
--- /dev/null
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Atlantic Network Driver
+ * Copyright (C) 2020 Marvell International Ltd.
+ */
+
+#include "hw_atl2_llh.h"
+#include "hw_atl2_llh_internal.h"
+#include "aq_hw_utils.h"
+
+void hw_atl2_rpf_rss_hash_type_set(struct aq_hw_s *aq_hw, u32 rss_hash_type)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_PIF_RPF_RSS_HASH_TYPEI_ADR,
+                           HW_ATL2_RPF_PIF_RPF_RSS_HASH_TYPEI_MSK,
+                           HW_ATL2_RPF_PIF_RPF_RSS_HASH_TYPEI_SHIFT,
+                           rss_hash_type);
+}
+
+/* rpf */
+
+void hw_atl2_rpf_new_enable_set(struct aq_hw_s *aq_hw, u32 enable)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_NEW_EN_ADR,
+                           HW_ATL2_RPF_NEW_EN_MSK,
+                           HW_ATL2_RPF_NEW_EN_SHIFT,
+                           enable);
+}
+
+void hw_atl2_rpfl2_uc_flr_tag_set(struct aq_hw_s *aq_hw, u32 tag, u32 filter)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPFL2UC_TAG_ADR(filter),
+                           HW_ATL2_RPFL2UC_TAG_MSK,
+                           HW_ATL2_RPFL2UC_TAG_SHIFT,
+                           tag);
+}
+
+void hw_atl2_rpfl2_bc_flr_tag_set(struct aq_hw_s *aq_hw, u32 tag)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_L2_BC_TAG_ADR,
+                           HW_ATL2_RPF_L2_BC_TAG_MSK,
+                           HW_ATL2_RPF_L2_BC_TAG_SHIFT,
+                           tag);
+}
+
+void hw_atl2_new_rpf_rss_redir_set(struct aq_hw_s *aq_hw, u32 tc, u32 index,
+                                  u32 queue)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_RSS_REDIR_ADR(tc, index),
+                           HW_ATL2_RPF_RSS_REDIR_MSK(tc),
+                           HW_ATL2_RPF_RSS_REDIR_SHIFT(tc),
+                           queue);
+}
+
+void hw_atl2_rpf_vlan_flr_tag_set(struct aq_hw_s *aq_hw, u32 tag, u32 filter)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_VL_TAG_ADR(filter),
+                           HW_ATL2_RPF_VL_TAG_MSK,
+                           HW_ATL2_RPF_VL_TAG_SHIFT,
+                           tag);
+}
+
+/* TX */
+
+void hw_atl2_tpb_tx_buf_clk_gate_en_set(struct aq_hw_s *aq_hw, u32 clk_gate_en)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL2_TPB_TX_BUF_CLK_GATE_EN_ADR,
+                           HW_ATL2_TPB_TX_BUF_CLK_GATE_EN_MSK,
+                           HW_ATL2_TPB_TX_BUF_CLK_GATE_EN_SHIFT,
+                           clk_gate_en);
+}
+
+void hw_atl2_reg_tx_intr_moder_ctrl_set(struct aq_hw_s *aq_hw,
+                                       u32 tx_intr_moderation_ctl,
+                                       u32 queue)
+{
+       aq_hw_write_reg(aq_hw, HW_ATL2_TX_INTR_MODERATION_CTL_ADR(queue),
+                       tx_intr_moderation_ctl);
+}
+
+void hw_atl2_tps_tx_pkt_shed_tc_data_max_credit_set(struct aq_hw_s *aq_hw,
+                                                   u32 max_credit,
+                                                   u32 tc)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL2_TPS_DATA_TCTCREDIT_MAX_ADR(tc),
+                           HW_ATL2_TPS_DATA_TCTCREDIT_MAX_MSK,
+                           HW_ATL2_TPS_DATA_TCTCREDIT_MAX_SHIFT,
+                           max_credit);
+}
+
+void hw_atl2_tps_tx_pkt_shed_tc_data_weight_set(struct aq_hw_s *aq_hw,
+                                               u32 tx_pkt_shed_tc_data_weight,
+                                               u32 tc)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL2_TPS_DATA_TCTWEIGHT_ADR(tc),
+                           HW_ATL2_TPS_DATA_TCTWEIGHT_MSK,
+                           HW_ATL2_TPS_DATA_TCTWEIGHT_SHIFT,
+                           tx_pkt_shed_tc_data_weight);
+}
+
+u32 hw_atl2_get_hw_version(struct aq_hw_s *aq_hw)
+{
+       return aq_hw_read_reg(aq_hw, HW_ATL2_FPGA_VER_ADR);
+}
+
+void hw_atl2_init_launchtime(struct aq_hw_s *aq_hw)
+{
+       u32 hw_ver = hw_atl2_get_hw_version(aq_hw);
+
+       aq_hw_write_reg_bit(aq_hw, HW_ATL2_LT_CTRL_ADR,
+                           HW_ATL2_LT_CTRL_CLK_RATIO_MSK,
+                           HW_ATL2_LT_CTRL_CLK_RATIO_SHIFT,
+                           hw_ver  < HW_ATL2_FPGA_VER_U32(1, 0, 0, 0) ?
+                           HW_ATL2_LT_CTRL_CLK_RATIO_FULL_SPEED :
+                           hw_ver >= HW_ATL2_FPGA_VER_U32(1, 0, 85, 2) ?
+                           HW_ATL2_LT_CTRL_CLK_RATIO_HALF_SPEED :
+                           HW_ATL2_LT_CTRL_CLK_RATIO_QUATER_SPEED);
+}
+
+/* set action resolver record */
+void hw_atl2_rpf_act_rslvr_record_set(struct aq_hw_s *aq_hw, u8 location,
+                                     u32 tag, u32 mask, u32 action)
+{
+       aq_hw_write_reg(aq_hw,
+                       HW_ATL2_RPF_ACT_RSLVR_REQ_TAG_ADR(location),
+                       tag);
+       aq_hw_write_reg(aq_hw,
+                       HW_ATL2_RPF_ACT_RSLVR_TAG_MASK_ADR(location),
+                       mask);
+       aq_hw_write_reg(aq_hw,
+                       HW_ATL2_RPF_ACT_RSLVR_ACTN_ADR(location),
+                       action);
+}
+
+void hw_atl2_rpf_act_rslvr_section_en_set(struct aq_hw_s *aq_hw, u32 sections)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL2_RPF_REC_TAB_EN_ADR,
+                           HW_ATL2_RPF_REC_TAB_EN_MSK,
+                           HW_ATL2_RPF_REC_TAB_EN_SHIFT,
+                           sections);
+}
+
+void hw_atl2_mif_shared_buf_get(struct aq_hw_s *aq_hw, int offset, u32 *data,
+                               int len)
+{
+       int j = 0;
+       int i;
+
+       for (i = offset; i < offset + len; i++, j++)
+               data[j] = aq_hw_read_reg(aq_hw,
+                                        HW_ATL2_MIF_SHARED_BUFFER_IN_ADR(i));
+}
+
+void hw_atl2_mif_shared_buf_write(struct aq_hw_s *aq_hw, int offset, u32 *data,
+                                 int len)
+{
+       int j = 0;
+       int i;
+
+       for (i = offset; i < offset + len; i++, j++)
+               aq_hw_write_reg(aq_hw, HW_ATL2_MIF_SHARED_BUFFER_IN_ADR(i),
+                               data[j]);
+}
+
+void hw_atl2_mif_shared_buf_read(struct aq_hw_s *aq_hw, int offset, u32 *data,
+                                int len)
+{
+       int j = 0;
+       int i;
+
+       for (i = offset; i < offset + len; i++, j++)
+               data[j] = aq_hw_read_reg(aq_hw,
+                                        HW_ATL2_MIF_SHARED_BUFFER_OUT_ADR(i));
+}
+
+void hw_atl2_mif_host_finished_write_set(struct aq_hw_s *aq_hw, u32 finish)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL2_MIF_HOST_FINISHED_WRITE_ADR,
+                           HW_ATL2_MIF_HOST_FINISHED_WRITE_MSK,
+                           HW_ATL2_MIF_HOST_FINISHED_WRITE_SHIFT,
+                           finish);
+}
+
+u32 hw_atl2_mif_mcp_finished_read_get(struct aq_hw_s *aq_hw)
+{
+       return aq_hw_read_reg_bit(aq_hw, HW_ATL2_MIF_MCP_FINISHED_READ_ADR,
+                                 HW_ATL2_MIF_MCP_FINISHED_READ_MSK,
+                                 HW_ATL2_MIF_MCP_FINISHED_READ_SHIFT);
+}
+
+u32 hw_atl2_mif_mcp_boot_reg_get(struct aq_hw_s *aq_hw)
+{
+       return aq_hw_read_reg(aq_hw, HW_ATL2_MIF_BOOT_REG_ADR);
+}
+
+void hw_atl2_mif_mcp_boot_reg_set(struct aq_hw_s *aq_hw, u32 val)
+{
+       return aq_hw_write_reg(aq_hw, HW_ATL2_MIF_BOOT_REG_ADR, val);
+}
+
+u32 hw_atl2_mif_host_req_int_get(struct aq_hw_s *aq_hw)
+{
+       return aq_hw_read_reg(aq_hw, HW_ATL2_MCP_HOST_REQ_INT_ADR);
+}
+
+void hw_atl2_mif_host_req_int_clr(struct aq_hw_s *aq_hw, u32 val)
+{
+       return aq_hw_write_reg(aq_hw, HW_ATL2_MCP_HOST_REQ_INT_CLR_ADR,
+                              val);
+}
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh.h
new file mode 100644 (file)
index 0000000..8c6d78a
--- /dev/null
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Atlantic Network Driver
+ * Copyright (C) 2020 Marvell International Ltd.
+ */
+
+#ifndef HW_ATL2_LLH_H
+#define HW_ATL2_LLH_H
+
+#include <linux/types.h>
+
+struct aq_hw_s;
+
+/* Set TX Interrupt Moderation Control Register */
+void hw_atl2_reg_tx_intr_moder_ctrl_set(struct aq_hw_s *aq_hw,
+                                       u32 tx_intr_moderation_ctl,
+                                       u32 queue);
+
+/** Set RSS HASH type */
+void hw_atl2_rpf_rss_hash_type_set(struct aq_hw_s *aq_hw, u32 rss_hash_type);
+
+/* set new RPF enable */
+void hw_atl2_rpf_new_enable_set(struct aq_hw_s *aq_hw, u32 enable);
+
+/* set l2 unicast filter tag */
+void hw_atl2_rpfl2_uc_flr_tag_set(struct aq_hw_s *aq_hw, u32 tag, u32 filter);
+
+/* set l2 broadcast filter tag */
+void hw_atl2_rpfl2_bc_flr_tag_set(struct aq_hw_s *aq_hw, u32 tag);
+
+/* set new rss redirection table */
+void hw_atl2_new_rpf_rss_redir_set(struct aq_hw_s *aq_hw, u32 tc, u32 index,
+                                  u32 queue);
+
+/* Set VLAN filter tag */
+void hw_atl2_rpf_vlan_flr_tag_set(struct aq_hw_s *aq_hw, u32 tag, u32 filter);
+
+/* set tx buffer clock gate enable */
+void hw_atl2_tpb_tx_buf_clk_gate_en_set(struct aq_hw_s *aq_hw, u32 clk_gate_en);
+
+/* set tx packet scheduler tc data max credit */
+void hw_atl2_tps_tx_pkt_shed_tc_data_max_credit_set(struct aq_hw_s *aq_hw,
+                                                   u32 max_credit,
+                                                   u32 tc);
+
+/* set tx packet scheduler tc data weight */
+void hw_atl2_tps_tx_pkt_shed_tc_data_weight_set(struct aq_hw_s *aq_hw,
+                                               u32 tx_pkt_shed_tc_data_weight,
+                                               u32 tc);
+
+u32 hw_atl2_get_hw_version(struct aq_hw_s *aq_hw);
+
+void hw_atl2_init_launchtime(struct aq_hw_s *aq_hw);
+
+/* set action resolver record */
+void hw_atl2_rpf_act_rslvr_record_set(struct aq_hw_s *aq_hw, u8 location,
+                                     u32 tag, u32 mask, u32 action);
+
+/* set enable action resolver section */
+void hw_atl2_rpf_act_rslvr_section_en_set(struct aq_hw_s *aq_hw, u32 sections);
+
+/* get data from firmware shared input buffer */
+void hw_atl2_mif_shared_buf_get(struct aq_hw_s *aq_hw, int offset, u32 *data,
+                               int len);
+
+/* set data into firmware shared input buffer */
+void hw_atl2_mif_shared_buf_write(struct aq_hw_s *aq_hw, int offset, u32 *data,
+                                 int len);
+
+/* get data from firmware shared output buffer */
+void hw_atl2_mif_shared_buf_read(struct aq_hw_s *aq_hw, int offset, u32 *data,
+                                int len);
+
+/* set host finished write shared buffer indication */
+void hw_atl2_mif_host_finished_write_set(struct aq_hw_s *aq_hw, u32 finish);
+
+/* get mcp finished read shared buffer indication */
+u32 hw_atl2_mif_mcp_finished_read_get(struct aq_hw_s *aq_hw);
+
+/* get mcp boot register */
+u32 hw_atl2_mif_mcp_boot_reg_get(struct aq_hw_s *aq_hw);
+
+/* set mcp boot register */
+void hw_atl2_mif_mcp_boot_reg_set(struct aq_hw_s *aq_hw, u32 val);
+
+/* get host interrupt request */
+u32 hw_atl2_mif_host_req_int_get(struct aq_hw_s *aq_hw);
+
+/* clear host interrupt request */
+void hw_atl2_mif_host_req_int_clr(struct aq_hw_s *aq_hw, u32 val);
+
+#endif /* HW_ATL2_LLH_H */
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh_internal.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_llh_internal.h
new file mode 100644 (file)
index 0000000..cde9e9d
--- /dev/null
@@ -0,0 +1,328 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Atlantic Network Driver
+ * Copyright (C) 2020 Marvell International Ltd.
+ */
+
+#ifndef HW_ATL2_LLH_INTERNAL_H
+#define HW_ATL2_LLH_INTERNAL_H
+
+/* RX pif_rpf_rss_hash_type_i Bitfield Definitions
+ */
+#define HW_ATL2_RPF_PIF_RPF_RSS_HASH_TYPEI_ADR 0x000054C8
+#define HW_ATL2_RPF_PIF_RPF_RSS_HASH_TYPEI_MSK 0x000001FF
+#define HW_ATL2_RPF_PIF_RPF_RSS_HASH_TYPEI_MSKN 0xFFFFFE00
+#define HW_ATL2_RPF_PIF_RPF_RSS_HASH_TYPEI_SHIFT 0
+#define HW_ATL2_RPF_PIF_RPF_RSS_HASH_TYPEI_WIDTH 9
+
+/* rx rpf_new_rpf_en bitfield definitions
+ * preprocessor definitions for the bitfield "rpf_new_rpf_en_i".
+ * port="pif_rpf_new_rpf_en_i
+ */
+
+/* register address for bitfield rpf_new_rpf_en */
+#define HW_ATL2_RPF_NEW_EN_ADR 0x00005104
+/* bitmask for bitfield rpf_new_rpf_en */
+#define HW_ATL2_RPF_NEW_EN_MSK 0x00000800
+/* inverted bitmask for bitfield rpf_new_rpf_en */
+#define HW_ATL2_RPF_NEW_EN_MSKN 0xfffff7ff
+/* lower bit position of bitfield rpf_new_rpf_en */
+#define HW_ATL2_RPF_NEW_EN_SHIFT 11
+/* width of bitfield rpf_new_rpf_en */
+#define HW_ATL2_RPF_NEW_EN_WIDTH 1
+/* default value of bitfield rpf_new_rpf_en */
+#define HW_ATL2_RPF_NEW_EN_DEFAULT 0x0
+
+/* rx l2_uc_req_tag0{f}[5:0] bitfield definitions
+ * preprocessor definitions for the bitfield "l2_uc_req_tag0{f}[7:0]".
+ * parameter: filter {f} | stride size 0x8 | range [0, 37]
+ * port="pif_rpf_l2_uc_req_tag0[5:0]"
+ */
+
+/* register address for bitfield l2_uc_req_tag0{f}[2:0] */
+#define HW_ATL2_RPFL2UC_TAG_ADR(filter) (0x00005114 + (filter) * 0x8)
+/* bitmask for bitfield l2_uc_req_tag0{f}[2:0] */
+#define HW_ATL2_RPFL2UC_TAG_MSK 0x0FC00000
+/* inverted bitmask for bitfield l2_uc_req_tag0{f}[2:0] */
+#define HW_ATL2_RPFL2UC_TAG_MSKN 0xF03FFFFF
+/* lower bit position of bitfield l2_uc_req_tag0{f}[2:0] */
+#define HW_ATL2_RPFL2UC_TAG_SHIFT 22
+/* width of bitfield l2_uc_req_tag0{f}[2:0] */
+#define HW_ATL2_RPFL2UC_TAG_WIDTH 6
+/* default value of bitfield l2_uc_req_tag0{f}[2:0] */
+#define HW_ATL2_RPFL2UC_TAG_DEFAULT 0x0
+
+/* rpf_l2_bc_req_tag[5:0] bitfield definitions
+ * preprocessor definitions for the bitfield "rpf_l2_bc_req_tag[5:0]".
+ * port="pifrpf_l2_bc_req_tag_i[5:0]"
+ */
+
+/* register address for bitfield rpf_l2_bc_req_tag */
+#define HW_ATL2_RPF_L2_BC_TAG_ADR 0x000050F0
+/* bitmask for bitfield rpf_l2_bc_req_tag */
+#define HW_ATL2_RPF_L2_BC_TAG_MSK 0x0000003F
+/* inverted bitmask for bitfield rpf_l2_bc_req_tag */
+#define HW_ATL2_RPF_L2_BC_TAG_MSKN 0xffffffc0
+/* lower bit position of bitfield rpf_l2_bc_req_tag */
+#define HW_ATL2_RPF_L2_BC_TAG_SHIFT 0
+/* width of bitfield rpf_l2_bc_req_tag */
+#define HW_ATL2_RPF_L2_BC_TAG_WIDTH 6
+/* default value of bitfield rpf_l2_bc_req_tag */
+#define HW_ATL2_RPF_L2_BC_TAG_DEFAULT 0x0
+
+/* rx rpf_rss_red1_data_[4:0] bitfield definitions
+ * preprocessor definitions for the bitfield "rpf_rss_red1_data[4:0]".
+ * port="pif_rpf_rss_red1_data_i[4:0]"
+ */
+
+/* register address for bitfield rpf_rss_red1_data[4:0] */
+#define HW_ATL2_RPF_RSS_REDIR_ADR(TC, INDEX) (0x00006200 + \
+                                       (0x100 * !!((TC) > 3)) + (INDEX) * 4)
+/* bitmask for bitfield rpf_rss_red1_data[4:0] */
+#define HW_ATL2_RPF_RSS_REDIR_MSK(TC)  (0x00000001F << (5 * ((TC) % 4)))
+/* lower bit position of bitfield rpf_rss_red1_data[4:0] */
+#define HW_ATL2_RPF_RSS_REDIR_SHIFT(TC) (5 * ((TC) % 4))
+/* width of bitfield rpf_rss_red1_data[4:0] */
+#define HW_ATL2_RPF_RSS_REDIR_WIDTH 5
+/* default value of bitfield rpf_rss_red1_data[4:0] */
+#define HW_ATL2_RPF_RSS_REDIR_DEFAULT 0x0
+
+/* rx vlan_req_tag0{f}[3:0] bitfield definitions
+ * preprocessor definitions for the bitfield "vlan_req_tag0{f}[3:0]".
+ * parameter: filter {f} | stride size 0x4 | range [0, 15]
+ * port="pif_rpf_vlan_req_tag0[3:0]"
+ */
+
+/* register address for bitfield vlan_req_tag0{f}[3:0] */
+#define HW_ATL2_RPF_VL_TAG_ADR(filter) (0x00005290 + (filter) * 0x4)
+/* bitmask for bitfield vlan_req_tag0{f}[3:0] */
+#define HW_ATL2_RPF_VL_TAG_MSK 0x0000F000
+/* inverted bitmask for bitfield vlan_req_tag0{f}[3:0] */
+#define HW_ATL2_RPF_VL_TAG_MSKN 0xFFFF0FFF
+/* lower bit position of bitfield vlan_req_tag0{f}[3:0] */
+#define HW_ATL2_RPF_VL_TAG_SHIFT 12
+/* width of bitfield vlan_req_tag0{f}[3:0] */
+#define HW_ATL2_RPF_VL_TAG_WIDTH 4
+/* default value of bitfield vlan_req_tag0{f}[3:0] */
+#define HW_ATL2_RPF_VL_TAG_DEFAULT 0x0
+
+/* RX rx_q{Q}_tc_map[2:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "rx_q{Q}_tc_map[2:0]".
+ * Parameter: Queue {Q} | bit-level stride | range [0, 31]
+ * PORT="pif_rx_q0_tc_map_i[2:0]"
+ */
+
+/* Register address for bitfield rx_q{Q}_tc_map[2:0] */
+#define HW_ATL2_RX_Q_TC_MAP_ADR(queue) \
+       (((queue) < 32) ? 0x00005900 + ((queue) / 8) * 4 : 0)
+/* Lower bit position of bitfield rx_q{Q}_tc_map[2:0] */
+#define HW_ATL2_RX_Q_TC_MAP_SHIFT(queue) \
+       (((queue) < 32) ? ((queue) * 4) % 32 : 0)
+/* Width of bitfield rx_q{Q}_tc_map[2:0] */
+#define HW_ATL2_RX_Q_TC_MAP_WIDTH 3
+/* Default value of bitfield rx_q{Q}_tc_map[2:0] */
+#define HW_ATL2_RX_Q_TC_MAP_DEFAULT 0x0
+
+/* tx tx_buffer_clk_gate_en bitfield definitions
+ * preprocessor definitions for the bitfield "tx_buffer_clk_gate_en".
+ * port="pif_tpb_tx_buffer_clk_gate_en_i"
+ */
+
+/* register address for bitfield tx_buffer_clk_gate_en */
+#define HW_ATL2_TPB_TX_BUF_CLK_GATE_EN_ADR 0x00007900
+/* bitmask for bitfield tx_buffer_clk_gate_en */
+#define HW_ATL2_TPB_TX_BUF_CLK_GATE_EN_MSK 0x00000020
+/* inverted bitmask for bitfield tx_buffer_clk_gate_en */
+#define HW_ATL2_TPB_TX_BUF_CLK_GATE_EN_MSKN 0xffffffdf
+/* lower bit position of bitfield tx_buffer_clk_gate_en */
+#define HW_ATL2_TPB_TX_BUF_CLK_GATE_EN_SHIFT 5
+/* width of bitfield tx_buffer_clk_gate_en */
+#define HW_ATL2_TPB_TX_BUF_CLK_GATE_EN_WIDTH 1
+/* default value of bitfield tx_buffer_clk_gate_en */
+#define HW_ATL2_TPB_TX_BUF_CLK_GATE_EN_DEFAULT 0x0
+
+/* tx data_tc{t}_credit_max[b:0] bitfield definitions
+ * preprocessor definitions for the bitfield "data_tc{t}_credit_max[b:0]".
+ * parameter: tc {t} | stride size 0x4 | range [0, 7]
+ * port="pif_tps_data_tc0_credit_max_i[11:0]"
+ */
+
+/* register address for bitfield data_tc{t}_credit_max[b:0] */
+#define HW_ATL2_TPS_DATA_TCTCREDIT_MAX_ADR(tc) (0x00007110 + (tc) * 0x4)
+/* bitmask for bitfield data_tc{t}_credit_max[b:0] */
+#define HW_ATL2_TPS_DATA_TCTCREDIT_MAX_MSK 0x0fff0000
+/* inverted bitmask for bitfield data_tc{t}_credit_max[b:0] */
+#define HW_ATL2_TPS_DATA_TCTCREDIT_MAX_MSKN 0xf000ffff
+/* lower bit position of bitfield data_tc{t}_credit_max[b:0] */
+#define HW_ATL2_TPS_DATA_TCTCREDIT_MAX_SHIFT 16
+/* width of bitfield data_tc{t}_credit_max[b:0] */
+#define HW_ATL2_TPS_DATA_TCTCREDIT_MAX_WIDTH 12
+/* default value of bitfield data_tc{t}_credit_max[b:0] */
+#define HW_ATL2_TPS_DATA_TCTCREDIT_MAX_DEFAULT 0x0
+
+/* tx data_tc{t}_weight[8:0] bitfield definitions
+ * preprocessor definitions for the bitfield "data_tc{t}_weight[8:0]".
+ * parameter: tc {t} | stride size 0x4 | range [0, 7]
+ * port="pif_tps_data_tc0_weight_i[8:0]"
+ */
+
+/* register address for bitfield data_tc{t}_weight[8:0] */
+#define HW_ATL2_TPS_DATA_TCTWEIGHT_ADR(tc) (0x00007110 + (tc) * 0x4)
+/* bitmask for bitfield data_tc{t}_weight[8:0] */
+#define HW_ATL2_TPS_DATA_TCTWEIGHT_MSK 0x000001ff
+/* inverted bitmask for bitfield data_tc{t}_weight[8:0] */
+#define HW_ATL2_TPS_DATA_TCTWEIGHT_MSKN 0xfffffe00
+/* lower bit position of bitfield data_tc{t}_weight[8:0] */
+#define HW_ATL2_TPS_DATA_TCTWEIGHT_SHIFT 0
+/* width of bitfield data_tc{t}_weight[8:0] */
+#define HW_ATL2_TPS_DATA_TCTWEIGHT_WIDTH 9
+/* default value of bitfield data_tc{t}_weight[8:0] */
+#define HW_ATL2_TPS_DATA_TCTWEIGHT_DEFAULT 0x0
+
+/* tx interrupt moderation control register definitions
+ * Preprocessor definitions for TX Interrupt Moderation Control Register
+ * Base Address: 0x00007c28
+ * Parameter: queue {Q} | stride size 0x4 | range [0, 31]
+ */
+
+#define HW_ATL2_TX_INTR_MODERATION_CTL_ADR(queue) (0x00007c28u + (queue) * 0x40)
+
+/* Launch time control register */
+#define HW_ATL2_LT_CTRL_ADR 0x00007a1c
+
+#define HW_ATL2_LT_CTRL_AVB_LEN_CMP_TRSHLD_MSK 0xFFFF0000
+#define HW_ATL2_LT_CTRL_AVB_LEN_CMP_TRSHLD_SHIFT 16
+
+#define HW_ATL2_LT_CTRL_CLK_RATIO_MSK 0x0000FF00
+#define HW_ATL2_LT_CTRL_CLK_RATIO_SHIFT 8
+#define HW_ATL2_LT_CTRL_CLK_RATIO_QUATER_SPEED 4
+#define HW_ATL2_LT_CTRL_CLK_RATIO_HALF_SPEED 2
+#define HW_ATL2_LT_CTRL_CLK_RATIO_FULL_SPEED 1
+
+#define HW_ATL2_LT_CTRL_25G_MODE_SUPPORT_MSK 0x00000008
+#define HW_ATL2_LT_CTRL_25G_MODE_SUPPORT_SHIFT 3
+
+#define HW_ATL2_LT_CTRL_LINK_SPEED_MSK 0x00000007
+#define HW_ATL2_LT_CTRL_LINK_SPEED_SHIFT 0
+
+/* FPGA VER register */
+#define HW_ATL2_FPGA_VER_ADR 0x000000f4
+#define HW_ATL2_FPGA_VER_U32(mj, mi, bl, rv) \
+       ((((mj) & 0xff) << 24) | \
+        (((mi) & 0xff) << 16) | \
+        (((bl) & 0xff) << 8) | \
+        (((rv) & 0xff) << 0))
+
+/* ahb_mem_addr{f}[31:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "ahb_mem_addr{f}[31:0]".
+ * Parameter: filter {f} | stride size 0x10 | range [0, 127]
+ * PORT="ahb_mem_addr{f}[31:0]"
+ */
+
+/* Register address for bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_REQ_TAG_ADR(filter) \
+       (0x00014000u + (filter) * 0x10)
+/* Bitmask for bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_REQ_TAG_MSK 0xFFFFFFFFu
+/* Inverted bitmask for bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_REQ_TAG_MSKN 0x00000000u
+/* Lower bit position of bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_REQ_TAG_SHIFT 0
+/* Width of bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_REQ_TAG_WIDTH 31
+/* Default value of bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_REQ_TAG_DEFAULT 0x0
+
+/* Register address for bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_TAG_MASK_ADR(filter) \
+       (0x00014004u + (filter) * 0x10)
+/* Bitmask for bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_TAG_MASK_MSK 0xFFFFFFFFu
+/* Inverted bitmask for bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_TAG_MASK_MSKN 0x00000000u
+/* Lower bit position of bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_TAG_MASK_SHIFT 0
+/* Width of bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_TAG_MASK_WIDTH 31
+/* Default value of bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_TAG_MASK_DEFAULT 0x0
+
+/* Register address for bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_ACTN_ADR(filter) \
+       (0x00014008u + (filter) * 0x10)
+/* Bitmask for bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_ACTN_MSK 0x000007FFu
+/* Inverted bitmask for bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_ACTN_MSKN 0xFFFFF800u
+/* Lower bit position of bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_ACTN_SHIFT 0
+/* Width of bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_ACTN_WIDTH 10
+/* Default value of bitfield ahb_mem_addr{f}[31:0] */
+#define HW_ATL2_RPF_ACT_RSLVR_ACTN_DEFAULT 0x0
+
+/* rpf_rec_tab_en[15:0] Bitfield Definitions
+ * Preprocessor definitions for the bitfield "rpf_rec_tab_en[15:0]".
+ * PORT="pif_rpf_rec_tab_en[15:0]"
+ */
+/* Register address for bitfield rpf_rec_tab_en[15:0] */
+#define HW_ATL2_RPF_REC_TAB_EN_ADR 0x00006ff0u
+/* Bitmask for bitfield rpf_rec_tab_en[15:0] */
+#define HW_ATL2_RPF_REC_TAB_EN_MSK 0x0000FFFFu
+/* Inverted bitmask for bitfield rpf_rec_tab_en[15:0] */
+#define HW_ATL2_RPF_REC_TAB_EN_MSKN 0xFFFF0000u
+/* Lower bit position of bitfield rpf_rec_tab_en[15:0] */
+#define HW_ATL2_RPF_REC_TAB_EN_SHIFT 0
+/* Width of bitfield rpf_rec_tab_en[15:0] */
+#define HW_ATL2_RPF_REC_TAB_EN_WIDTH 16
+/* Default value of bitfield rpf_rec_tab_en[15:0] */
+#define HW_ATL2_RPF_REC_TAB_EN_DEFAULT 0x0
+
+/* Register address for firmware shared input buffer */
+#define HW_ATL2_MIF_SHARED_BUFFER_IN_ADR(dword) (0x00012000U + (dword) * 0x4U)
+/* Register address for firmware shared output buffer */
+#define HW_ATL2_MIF_SHARED_BUFFER_OUT_ADR(dword) (0x00013000U + (dword) * 0x4U)
+
+/* pif_host_finished_buf_wr_i Bitfield Definitions
+ * Preprocessor definitions for the bitfield "pif_host_finished_buf_wr_i".
+ * PORT="pif_host_finished_buf_wr_i"
+ */
+/* Register address for bitfield rpif_host_finished_buf_wr_i */
+#define HW_ATL2_MIF_HOST_FINISHED_WRITE_ADR 0x00000e00u
+/* Bitmask for bitfield pif_host_finished_buf_wr_i */
+#define HW_ATL2_MIF_HOST_FINISHED_WRITE_MSK 0x00000001u
+/* Inverted bitmask for bitfield pif_host_finished_buf_wr_i */
+#define HW_ATL2_MIF_HOST_FINISHED_WRITE_MSKN 0xFFFFFFFEu
+/* Lower bit position of bitfield pif_host_finished_buf_wr_i */
+#define HW_ATL2_MIF_HOST_FINISHED_WRITE_SHIFT 0
+/* Width of bitfield pif_host_finished_buf_wr_i */
+#define HW_ATL2_MIF_HOST_FINISHED_WRITE_WIDTH 1
+/* Default value of bitfield pif_host_finished_buf_wr_i */
+#define HW_ATL2_MIF_HOST_FINISHED_WRITE_DEFAULT 0x0
+
+/* pif_mcp_finished_buf_rd_i Bitfield Definitions
+ * Preprocessor definitions for the bitfield "pif_mcp_finished_buf_rd_i".
+ * PORT="pif_mcp_finished_buf_rd_i"
+ */
+/* Register address for bitfield pif_mcp_finished_buf_rd_i */
+#define HW_ATL2_MIF_MCP_FINISHED_READ_ADR 0x00000e04u
+/* Bitmask for bitfield pif_mcp_finished_buf_rd_i */
+#define HW_ATL2_MIF_MCP_FINISHED_READ_MSK 0x00000001u
+/* Inverted bitmask for bitfield pif_mcp_finished_buf_rd_i */
+#define HW_ATL2_MIF_MCP_FINISHED_READ_MSKN 0xFFFFFFFEu
+/* Lower bit position of bitfield pif_mcp_finished_buf_rd_i */
+#define HW_ATL2_MIF_MCP_FINISHED_READ_SHIFT 0
+/* Width of bitfield pif_mcp_finished_buf_rd_i */
+#define HW_ATL2_MIF_MCP_FINISHED_READ_WIDTH 1
+/* Default value of bitfield pif_mcp_finished_buf_rd_i */
+#define HW_ATL2_MIF_MCP_FINISHED_READ_DEFAULT 0x0
+
+/* Register address for bitfield pif_mcp_boot_reg */
+#define HW_ATL2_MIF_BOOT_REG_ADR 0x00003040u
+
+#define HW_ATL2_MCP_HOST_REQ_INT_READY BIT(0)
+
+#define HW_ATL2_MCP_HOST_REQ_INT_ADR 0x00000F00u
+#define HW_ATL2_MCP_HOST_REQ_INT_SET_ADR 0x00000F04u
+#define HW_ATL2_MCP_HOST_REQ_INT_CLR_ADR 0x00000F08u
+
+#endif /* HW_ATL2_LLH_INTERNAL_H */
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.c
new file mode 100644 (file)
index 0000000..85ccc9a
--- /dev/null
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Atlantic Network Driver
+ * Copyright (C) 2020 Marvell International Ltd.
+ */
+
+#include <linux/iopoll.h>
+
+#include "aq_hw_utils.h"
+#include "hw_atl/hw_atl_utils.h"
+#include "hw_atl2_utils.h"
+#include "hw_atl2_llh.h"
+#include "hw_atl2_llh_internal.h"
+
+#define HW_ATL2_FW_VER_1X          0x01000000U
+
+#define AQ_A2_BOOT_STARTED         BIT(0x18)
+#define AQ_A2_CRASH_INIT           BIT(0x1B)
+#define AQ_A2_BOOT_CODE_FAILED     BIT(0x1C)
+#define AQ_A2_FW_INIT_FAILED       BIT(0x1D)
+#define AQ_A2_FW_INIT_COMP_SUCCESS BIT(0x1F)
+
+#define AQ_A2_FW_BOOT_FAILED_MASK (AQ_A2_CRASH_INIT | \
+                                  AQ_A2_BOOT_CODE_FAILED | \
+                                  AQ_A2_FW_INIT_FAILED)
+#define AQ_A2_FW_BOOT_COMPLETE_MASK (AQ_A2_FW_BOOT_FAILED_MASK | \
+                                    AQ_A2_FW_INIT_COMP_SUCCESS)
+
+#define AQ_A2_FW_BOOT_REQ_REBOOT        BIT(0x0)
+#define AQ_A2_FW_BOOT_REQ_HOST_BOOT     BIT(0x8)
+#define AQ_A2_FW_BOOT_REQ_MAC_FAST_BOOT BIT(0xA)
+#define AQ_A2_FW_BOOT_REQ_PHY_FAST_BOOT BIT(0xB)
+
+int hw_atl2_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops)
+{
+       int err;
+
+       self->fw_ver_actual = hw_atl2_utils_get_fw_version(self);
+
+       if (hw_atl_utils_ver_match(HW_ATL2_FW_VER_1X,
+                                  self->fw_ver_actual) == 0) {
+               *fw_ops = &aq_a2_fw_ops;
+       } else {
+               aq_pr_err("Bad FW version detected: %x, but continue\n",
+                         self->fw_ver_actual);
+               *fw_ops = &aq_a2_fw_ops;
+       }
+       aq_pr_trace("Detect ATL2FW %x\n", self->fw_ver_actual);
+       self->aq_fw_ops = *fw_ops;
+       err = self->aq_fw_ops->init(self);
+
+       self->chip_features |= ATL_HW_CHIP_ANTIGUA;
+
+       return err;
+}
+
+static bool hw_atl2_mcp_boot_complete(struct aq_hw_s *self)
+{
+       u32 rbl_status;
+
+       rbl_status = hw_atl2_mif_mcp_boot_reg_get(self);
+       if (rbl_status & AQ_A2_FW_BOOT_COMPLETE_MASK)
+               return true;
+
+       /* Host boot requested */
+       if (hw_atl2_mif_host_req_int_get(self) & HW_ATL2_MCP_HOST_REQ_INT_READY)
+               return true;
+
+       return false;
+}
+
+int hw_atl2_utils_soft_reset(struct aq_hw_s *self)
+{
+       bool rbl_complete = false;
+       u32 rbl_status = 0;
+       u32 rbl_request;
+       int err;
+
+       err = readx_poll_timeout_atomic(hw_atl2_mif_mcp_boot_reg_get, self,
+                               rbl_status,
+                               ((rbl_status & AQ_A2_BOOT_STARTED) &&
+                                (rbl_status != 0xFFFFFFFFu)),
+                               10, 500000);
+       if (err)
+               aq_pr_trace("Boot code probably hanged, reboot anyway");
+
+       hw_atl2_mif_host_req_int_clr(self, 0x01);
+       rbl_request = AQ_A2_FW_BOOT_REQ_REBOOT;
+#ifdef AQ_CFG_FAST_START
+       rbl_request |= AQ_A2_FW_BOOT_REQ_MAC_FAST_BOOT;
+#endif
+       hw_atl2_mif_mcp_boot_reg_set(self, rbl_request);
+
+       /* Wait for RBL boot */
+       err = readx_poll_timeout_atomic(hw_atl2_mif_mcp_boot_reg_get, self,
+                               rbl_status,
+                               ((rbl_status & AQ_A2_BOOT_STARTED) &&
+                                (rbl_status != 0xFFFFFFFFu)),
+                               10, 200000);
+       if (err) {
+               aq_pr_err("Boot code hanged");
+               goto err_exit;
+       }
+
+       err = readx_poll_timeout_atomic(hw_atl2_mcp_boot_complete, self,
+                                       rbl_complete,
+                                       rbl_complete,
+                                       10, 2000000);
+
+       if (err) {
+               aq_pr_err("FW Restart timed out");
+               goto err_exit;
+       }
+
+       rbl_status = hw_atl2_mif_mcp_boot_reg_get(self);
+
+       if (rbl_status & AQ_A2_FW_BOOT_FAILED_MASK) {
+               err = -EIO;
+               aq_pr_err("FW Restart failed");
+               goto err_exit;
+       }
+
+       if (hw_atl2_mif_host_req_int_get(self) &
+           HW_ATL2_MCP_HOST_REQ_INT_READY) {
+               err = -EIO;
+               aq_pr_err("No FW detected. Dynamic FW load not implemented");
+               goto err_exit;
+       }
+
+       if (self->aq_fw_ops) {
+               err = self->aq_fw_ops->init(self);
+               if (err) {
+                       aq_pr_err("FW Init failed");
+                       goto err_exit;
+               }
+       }
+
+err_exit:
+       return err;
+}
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.h
new file mode 100644 (file)
index 0000000..2317dd8
--- /dev/null
@@ -0,0 +1,606 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Atlantic Network Driver
+ * Copyright (C) 2020 Marvell International Ltd.
+ */
+
+#ifndef HW_ATL2_UTILS_H
+#define HW_ATL2_UTILS_H
+
+#include "aq_hw.h"
+
+/* F W    A P I */
+
+struct link_options_s {
+       u8 link_up:1;
+       u8 link_renegotiate:1;
+       u8 minimal_link_speed:1;
+       u8 internal_loopback:1;
+       u8 external_loopback:1;
+       u8 rate_10M_hd:1;
+       u8 rate_100M_hd:1;
+       u8 rate_1G_hd:1;
+
+       u8 rate_10M:1;
+       u8 rate_100M:1;
+       u8 rate_1G:1;
+       u8 rate_2P5G:1;
+       u8 rate_N2P5G:1;
+       u8 rate_5G:1;
+       u8 rate_N5G:1;
+       u8 rate_10G:1;
+
+       u8 eee_100M:1;
+       u8 eee_1G:1;
+       u8 eee_2P5G:1;
+       u8 eee_5G:1;
+       u8 eee_10G:1;
+       u8 rsvd3:3;
+
+       u8 pause_rx:1;
+       u8 pause_tx:1;
+       u8 rsvd4:1;
+       u8 downshift:1;
+       u8 downshift_retry:4;
+};
+
+struct link_control_s {
+       u8 mode:4;
+       u8 disable_crc_corruption:1;
+       u8 discard_short_frames:1;
+       u8 flow_control_mode:1;
+       u8 disable_length_check:1;
+
+       u8 discard_errored_frames:1;
+       u8 control_frame_enable:1;
+       u8 enable_tx_padding:1;
+       u8 enable_crc_forwarding:1;
+       u8 enable_frame_padding_removal_rx: 1;
+       u8 promiscuous_mode: 1;
+       u8 rsvd:2;
+
+       u16 rsvd2;
+};
+
+struct thermal_shutdown_s {
+       u8 enable:1;
+       u8 warning_enable:1;
+       u8 rsvd:6;
+
+       u8 shutdown_temperature;
+       u8 cold_temperature;
+       u8 warning_temperature;
+};
+
+struct mac_address_s {
+       u8 mac_address[6];
+};
+
+struct mac_address_aligned_s {
+       struct mac_address_s aligned;
+       u16 rsvd;
+};
+
+struct sleep_proxy_s {
+       struct wake_on_lan_s {
+               u8 wake_on_magic_packet:1;
+               u8 wake_on_pattern:1;
+               u8 wake_on_link_up:1;
+               u8 wake_on_link_down:1;
+               u8 wake_on_ping:1;
+               u8 wake_on_timer:1;
+               u8 rsvd:2;
+
+               u8 rsvd2;
+               u16 rsvd3;
+
+               u32 link_up_timeout;
+               u32 link_down_timeout;
+               u32 timer;
+       } wake_on_lan;
+
+       struct {
+               u32 mask[4];
+               u32 crc32;
+       } wake_up_pattern[8];
+
+       struct __attribute__ ((__packed__)) {
+               u8 arp_responder:1;
+               u8 echo_responder:1;
+               u8 igmp_client:1;
+               u8 echo_truncate:1;
+               u8 address_guard:1;
+               u8 ignore_fragmented:1;
+               u8 rsvd:2;
+
+               u16 echo_max_len;
+               u8 rsvd2;
+       } ipv4_offload;
+
+       u32 ipv4_offload_addr[8];
+       u32 reserved[8];
+
+       struct __attribute__ ((__packed__)) {
+               u8 ns_responder:1;
+               u8 echo_responder:1;
+               u8 mld_client:1;
+               u8 echo_truncate:1;
+               u8 address_guard:1;
+               u8 rsvd:3;
+
+               u16 echo_max_len;
+               u8 rsvd2;
+       } ipv6_offload;
+
+       u32 ipv6_offload_addr[16][4];
+
+       struct {
+               u16 port[16];
+       } tcp_port_offload;
+
+       struct {
+               u16 port[16];
+       } udp_port_offload;
+
+       struct {
+               u32 retry_count;
+               u32 retry_interval;
+       } ka4_offload;
+
+       struct {
+               u32 timeout;
+               u16 local_port;
+               u16 remote_port;
+               u8 remote_mac_addr[6];
+               u16 rsvd;
+               u32 rsvd2;
+               u32 rsvd3;
+               u16 rsvd4;
+               u16 win_size;
+               u32 seq_num;
+               u32 ack_num;
+               u32 local_ip;
+               u32 remote_ip;
+       } ka4_connection[16];
+
+       struct {
+               u32 retry_count;
+               u32 retry_interval;
+       } ka6_offload;
+
+       struct {
+               u32 timeout;
+               u16 local_port;
+               u16 remote_port;
+               u8 remote_mac_addr[6];
+               u16 rsvd;
+               u32 rsvd2;
+               u32 rsvd3;
+               u16 rsvd4;
+               u16 win_size;
+               u32 seq_num;
+               u32 ack_num;
+               u32 local_ip[4];
+               u32 remote_ip[4];
+       } ka6_connection[16];
+
+       struct {
+               u32 rr_count;
+               u32 rr_buf_len;
+               u32 idx_offset;
+               u32 rr__offset;
+       } mdns_offload;
+};
+
+struct pause_quanta_s {
+       u16 quanta_10M;
+       u16 threshold_10M;
+       u16 quanta_100M;
+       u16 threshold_100M;
+       u16 quanta_1G;
+       u16 threshold_1G;
+       u16 quanta_2P5G;
+       u16 threshold_2P5G;
+       u16 quanta_5G;
+       u16 threshold_5G;
+       u16 quanta_10G;
+       u16 threshold_10G;
+};
+
+struct data_buffer_status_s {
+       u32 data_offset;
+       u32 data_length;
+};
+
+struct device_caps_s {
+       u8 finite_flashless:1;
+       u8 cable_diag:1;
+       u8 ncsi:1;
+       u8 avb:1;
+       u8 rsvd:4;
+
+       u8 rsvd2;
+       u16 rsvd3;
+       u32 rsvd4;
+};
+
+struct version_s {
+       struct bundle_version_t {
+               u8 major;
+               u8 minor;
+               u16 build;
+       } bundle;
+       struct mac_version_t {
+               u8 major;
+               u8 minor;
+               u16 build;
+       } mac;
+       struct phy_version_t {
+               u8 major;
+               u8 minor;
+               u16 build;
+       } phy;
+       u32 rsvd;
+};
+
+struct link_status_s {
+       u8 link_state:4;
+       u8 link_rate:4;
+
+       u8 pause_tx:1;
+       u8 pause_rx:1;
+       u8 eee:1;
+       u8 duplex:1;
+       u8 rsvd:4;
+
+       u16 rsvd2;
+};
+
+struct wol_status_s {
+       u8 wake_count;
+       u8 wake_reason;
+
+       u16 wake_up_packet_length :12;
+       u16 wake_up_pattern_number :3;
+       u16 rsvd:1;
+
+       u32 wake_up_packet[379];
+};
+
+struct mac_health_monitor_s {
+       u8 mac_ready:1;
+       u8 mac_fault:1;
+       u8 mac_flashless_finished:1;
+       u8 rsvd:5;
+
+       u8 mac_temperature;
+       u16 mac_heart_beat;
+       u16 mac_fault_code;
+       u16 rsvd2;
+};
+
+struct phy_health_monitor_s {
+       u8 phy_ready:1;
+       u8 phy_fault:1;
+       u8 phy_hot_warning:1;
+       u8 rsvd:5;
+
+       u8 phy_temperature;
+       u16 phy_heart_beat;
+       u16 phy_fault_code;
+       u16 rsvd2;
+};
+
+struct device_link_caps_s {
+       u8 rsvd:3;
+       u8 internal_loopback:1;
+       u8 external_loopback:1;
+       u8 rate_10M_hd:1;
+       u8 rate_100M_hd:1;
+       u8 rate_1G_hd:1;
+
+       u8 rate_10M:1;
+       u8 rate_100M:1;
+       u8 rate_1G:1;
+       u8 rate_2P5G:1;
+       u8 rate_N2P5G:1;
+       u8 rate_5G:1;
+       u8 rate_N5G:1;
+       u8 rate_10G:1;
+
+       u8 rsvd3:1;
+       u8 eee_100M:1;
+       u8 eee_1G:1;
+       u8 eee_2P5G:1;
+       u8 rsvd4:1;
+       u8 eee_5G:1;
+       u8 rsvd5:1;
+       u8 eee_10G:1;
+
+       u8 pause_rx:1;
+       u8 pause_tx:1;
+       u8 pfc:1;
+       u8 downshift:1;
+       u8 downshift_retry:4;
+};
+
+struct sleep_proxy_caps_s {
+       u8 ipv4_offload:1;
+       u8 ipv6_offload:1;
+       u8 tcp_port_offload:1;
+       u8 udp_port_offload:1;
+       u8 ka4_offload:1;
+       u8 ka6_offload:1;
+       u8 mdns_offload:1;
+       u8 wake_on_ping:1;
+
+       u8 wake_on_magic_packet:1;
+       u8 wake_on_pattern:1;
+       u8 wake_on_timer:1;
+       u8 wake_on_link:1;
+       u8 wake_patterns_count:4;
+
+       u8 ipv4_count;
+       u8 ipv6_count;
+
+       u8 tcp_port_offload_count;
+       u8 udp_port_offload_count;
+
+       u8 tcp4_ka_count;
+       u8 tcp6_ka_count;
+
+       u8 igmp_offload:1;
+       u8 mld_offload:1;
+       u8 rsvd:6;
+
+       u8 rsvd2;
+       u16 rsvd3;
+};
+
+struct lkp_link_caps_s {
+       u8 rsvd:5;
+       u8 rate_10M_hd:1;
+       u8 rate_100M_hd:1;
+       u8 rate_1G_hd:1;
+
+       u8 rate_10M:1;
+       u8 rate_100M:1;
+       u8 rate_1G:1;
+       u8 rate_2P5G:1;
+       u8 rate_N2P5G:1;
+       u8 rate_5G:1;
+       u8 rate_N5G:1;
+       u8 rate_10G:1;
+
+       u8 rsvd2:1;
+       u8 eee_100M:1;
+       u8 eee_1G:1;
+       u8 eee_2P5G:1;
+       u8 rsvd3:1;
+       u8 eee_5G:1;
+       u8 rsvd4:1;
+       u8 eee_10G:1;
+
+       u8 pause_rx:1;
+       u8 pause_tx:1;
+       u8 rsvd5:6;
+};
+
+struct core_dump_s {
+       u32 reg0;
+       u32 reg1;
+       u32 reg2;
+
+       u32 hi;
+       u32 lo;
+
+       u32 regs[32];
+};
+
+struct trace_s {
+       u32 sync_counter;
+       u32 mem_buffer[0x1ff];
+};
+
+struct cable_diag_control_s {
+       u8 toggle :1;
+       u8 rsvd:7;
+
+       u8 wait_timeout_sec;
+       u16 rsvd2;
+};
+
+struct cable_diag_lane_data_s {
+       u8 result_code;
+       u8 dist;
+       u8 far_dist;
+       u8 rsvd;
+};
+
+struct cable_diag_status_s {
+       struct cable_diag_lane_data_s lane_data[4];
+       u8 transact_id;
+       u8 status:4;
+       u8 rsvd:4;
+       u16 rsvd2;
+};
+
+struct statistics_s {
+       struct {
+               u32 link_up;
+               u32 link_down;
+       } link;
+
+       struct {
+               u64 tx_unicast_octets;
+               u64 tx_multicast_octets;
+               u64 tx_broadcast_octets;
+               u64 rx_unicast_octets;
+               u64 rx_multicast_octets;
+               u64 rx_broadcast_octets;
+
+               u32 tx_unicast_frames;
+               u32 tx_multicast_frames;
+               u32 tx_broadcast_frames;
+               u32 tx_errors;
+
+               u32 rx_unicast_frames;
+               u32 rx_multicast_frames;
+               u32 rx_broadcast_frames;
+               u32 rx_dropped_frames;
+               u32 rx_error_frames;
+
+               u32 tx_good_frames;
+               u32 rx_good_frames;
+               u32 reserve_fw_gap;
+       } msm;
+       u32 main_loop_cycles;
+       u32 reserve_fw_gap;
+};
+
+struct filter_caps_s {
+       u8 l2_filters_base_index:6;
+       u8 flexible_filter_mask:2;
+       u8 l2_filter_count;
+       u8 ethertype_filter_base_index;
+       u8 ethertype_filter_count;
+
+       u8 vlan_filter_base_index;
+       u8 vlan_filter_count;
+       u8 l3_ip4_filter_base_index:4;
+       u8 l3_ip4_filter_count:4;
+       u8 l3_ip6_filter_base_index:4;
+       u8 l3_ip6_filter_count:4;
+
+       u8 l4_filter_base_index:4;
+       u8 l4_filter_count:4;
+       u8 l4_flex_filter_base_index:4;
+       u8 l4_flex_filter_count:4;
+       u8 rslv_tbl_base_index;
+       u8 rslv_tbl_count;
+};
+
+struct request_policy_s {
+       struct {
+               u8 all:1;
+               u8 mcast:1;
+               u8 rx_queue_tc_index:5;
+               u8 queue_or_tc:1;
+       } promisc;
+
+       struct {
+               u8 accept:1;
+               u8 rsvd:1;
+               u8 rx_queue_tc_index:5;
+               u8 queue_or_tc:1;
+       } bcast;
+
+       struct {
+               u8 accept:1;
+               u8 rsvd:1;
+               u8 rx_queue_tc_index:5;
+               u8 queue_or_tc:1;
+       } mcast;
+
+       u8 rsvd:8;
+};
+
+struct fw_interface_in {
+       u32 mtu;
+       u32 rsvd1;
+       struct mac_address_aligned_s mac_address;
+       struct link_control_s link_control;
+       u32 rsvd2;
+       struct link_options_s link_options;
+       u32 rsvd3;
+       struct thermal_shutdown_s thermal_shutdown;
+       u32 rsvd4;
+       struct sleep_proxy_s sleep_proxy;
+       u32 rsvd5;
+       struct pause_quanta_s pause_quanta[8];
+       struct cable_diag_control_s cable_diag_control;
+       u32 rsvd6;
+       struct data_buffer_status_s data_buffer_status;
+       u32 rsvd7;
+       struct request_policy_s request_policy;
+};
+
+struct transaction_counter_s {
+       u16 transaction_cnt_a;
+       u16 transaction_cnt_b;
+};
+
+struct management_status_s {
+       struct mac_address_s mac_address;
+       u16 vlan;
+
+       struct{
+               u32 enable : 1;
+               u32 rsvd:31;
+       } flags;
+
+       u32 rsvd1;
+       u32 rsvd2;
+       u32 rsvd3;
+       u32 rsvd4;
+       u32 rsvd5;
+};
+
+struct fw_interface_out {
+       struct transaction_counter_s transaction_id;
+       struct version_s version;
+       struct link_status_s link_status;
+       struct wol_status_s wol_status;
+       u32 rsvd;
+       u32 rsvd2;
+       struct mac_health_monitor_s mac_health_monitor;
+       u32 rsvd3;
+       u32 rsvd4;
+       struct phy_health_monitor_s phy_health_monitor;
+       u32 rsvd5;
+       u32 rsvd6;
+       struct cable_diag_status_s cable_diag_status;
+       u32 rsvd7;
+       struct device_link_caps_s device_link_caps;
+       u32 rsvd8;
+       struct sleep_proxy_caps_s sleep_proxy_caps;
+       u32 rsvd9;
+       struct lkp_link_caps_s lkp_link_caps;
+       u32 rsvd10;
+       struct core_dump_s core_dump;
+       u32 rsvd11;
+       struct statistics_s stats;
+       u32 rsvd12;
+       struct filter_caps_s filter_caps;
+       struct device_caps_s device_caps;
+       u32 rsvd13;
+       struct management_status_s management_status;
+       u32 reserve[21];
+       struct trace_s trace;
+};
+
+#define  AQ_A2_FW_LINK_RATE_INVALID 0
+#define  AQ_A2_FW_LINK_RATE_10M     1
+#define  AQ_A2_FW_LINK_RATE_100M    2
+#define  AQ_A2_FW_LINK_RATE_1G      3
+#define  AQ_A2_FW_LINK_RATE_2G5     4
+#define  AQ_A2_FW_LINK_RATE_5G      5
+#define  AQ_A2_FW_LINK_RATE_10G     6
+
+#define  AQ_HOST_MODE_INVALID      0U
+#define  AQ_HOST_MODE_ACTIVE       1U
+#define  AQ_HOST_MODE_SLEEP_PROXY  2U
+#define  AQ_HOST_MODE_LOW_POWER    3U
+#define  AQ_HOST_MODE_SHUTDOWN     4U
+
+int hw_atl2_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops);
+
+int hw_atl2_utils_soft_reset(struct aq_hw_s *self);
+
+u32 hw_atl2_utils_get_fw_version(struct aq_hw_s *self);
+
+int hw_atl2_utils_get_action_resolve_table_caps(struct aq_hw_s *self,
+                                               u8 *base_index, u8 *count);
+
+extern const struct aq_fw_ops aq_a2_fw_ops;
+
+#endif /* HW_ATL2_UTILS_H */
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c
new file mode 100644 (file)
index 0000000..f5fb4b1
--- /dev/null
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Atlantic Network Driver
+ * Copyright (C) 2020 Marvell International Ltd.
+ */
+
+#include <linux/iopoll.h>
+
+#include "aq_hw.h"
+#include "hw_atl/hw_atl_llh.h"
+#include "hw_atl2_utils.h"
+#include "hw_atl2_llh.h"
+#include "hw_atl2_internal.h"
+
+#define AQ_A2_FW_READ_TRY_MAX 1000
+
+#define hw_atl2_shared_buffer_write(HW, ITEM, VARIABLE) \
+       hw_atl2_mif_shared_buf_write(HW,\
+               (offsetof(struct fw_interface_in, ITEM) / sizeof(u32)),\
+               (u32 *)&(VARIABLE), sizeof(VARIABLE) / sizeof(u32))
+
+#define hw_atl2_shared_buffer_get(HW, ITEM, VARIABLE) \
+       hw_atl2_mif_shared_buf_get(HW, \
+               (offsetof(struct fw_interface_in, ITEM) / sizeof(u32)),\
+               (u32 *)&(VARIABLE), \
+               sizeof(VARIABLE) / sizeof(u32))
+
+/* This should never be used on non atomic fields,
+ * treat any > u32 read as non atomic.
+ */
+#define hw_atl2_shared_buffer_read(HW, ITEM, VARIABLE) \
+{\
+       BUILD_BUG_ON_MSG((offsetof(struct fw_interface_out, ITEM) % \
+                        sizeof(u32)) != 0,\
+                        "Non aligned read " # ITEM);\
+       BUILD_BUG_ON_MSG(sizeof(VARIABLE) > sizeof(u32),\
+                        "Non atomic read " # ITEM);\
+       hw_atl2_mif_shared_buf_read(HW, \
+               (offsetof(struct fw_interface_out, ITEM) / sizeof(u32)),\
+               (u32 *)&(VARIABLE), sizeof(VARIABLE) / sizeof(u32));\
+}
+
+#define hw_atl2_shared_buffer_read_safe(HW, ITEM, DATA) \
+       hw_atl2_shared_buffer_read_block((HW), \
+               (offsetof(struct fw_interface_out, ITEM) / sizeof(u32)),\
+               sizeof(((struct fw_interface_out *)0)->ITEM) / sizeof(u32),\
+               (DATA))
+
+static int hw_atl2_shared_buffer_read_block(struct aq_hw_s *self,
+                                           u32 offset, u32 dwords, void *data)
+{
+       struct transaction_counter_s tid1, tid2;
+       int cnt = 0;
+
+       do {
+               do {
+                       hw_atl2_shared_buffer_read(self, transaction_id, tid1);
+                       cnt++;
+                       if (cnt > AQ_A2_FW_READ_TRY_MAX)
+                               return -ETIME;
+                       if (tid1.transaction_cnt_a != tid1.transaction_cnt_b)
+                               udelay(1);
+               } while (tid1.transaction_cnt_a != tid1.transaction_cnt_b);
+
+               hw_atl2_mif_shared_buf_read(self, offset, (u32 *)data, dwords);
+
+               hw_atl2_shared_buffer_read(self, transaction_id, tid2);
+
+               cnt++;
+               if (cnt > AQ_A2_FW_READ_TRY_MAX)
+                       return -ETIME;
+       } while (tid2.transaction_cnt_a != tid2.transaction_cnt_b ||
+                tid1.transaction_cnt_a != tid2.transaction_cnt_a);
+
+       return 0;
+}
+
+static inline int hw_atl2_shared_buffer_finish_ack(struct aq_hw_s *self)
+{
+       u32 val;
+       int err;
+
+       hw_atl2_mif_host_finished_write_set(self, 1U);
+       err = readx_poll_timeout_atomic(hw_atl2_mif_mcp_finished_read_get,
+                                       self, val, val == 0U,
+                                       100, 100000U);
+       WARN(err, "hw_atl2_shared_buffer_finish_ack");
+
+       return err;
+}
+
+static int aq_a2_fw_init(struct aq_hw_s *self)
+{
+       struct link_control_s link_control;
+       u32 mtu;
+       u32 val;
+       int err;
+
+       hw_atl2_shared_buffer_get(self, link_control, link_control);
+       link_control.mode = AQ_HOST_MODE_ACTIVE;
+       hw_atl2_shared_buffer_write(self, link_control, link_control);
+
+       hw_atl2_shared_buffer_get(self, mtu, mtu);
+       mtu = HW_ATL2_MTU_JUMBO;
+       hw_atl2_shared_buffer_write(self, mtu, mtu);
+
+       hw_atl2_mif_host_finished_write_set(self, 1U);
+       err = readx_poll_timeout_atomic(hw_atl2_mif_mcp_finished_read_get,
+                                       self, val, val == 0U,
+                                       100, 5000000U);
+       WARN(err, "hw_atl2_shared_buffer_finish_ack");
+
+       return err;
+}
+
+static int aq_a2_fw_deinit(struct aq_hw_s *self)
+{
+       struct link_control_s link_control;
+
+       hw_atl2_shared_buffer_get(self, link_control, link_control);
+       link_control.mode = AQ_HOST_MODE_SHUTDOWN;
+       hw_atl2_shared_buffer_write(self, link_control, link_control);
+
+       return hw_atl2_shared_buffer_finish_ack(self);
+}
+
+static void a2_link_speed_mask2fw(u32 speed,
+                                 struct link_options_s *link_options)
+{
+       link_options->rate_10G = !!(speed & AQ_NIC_RATE_10G);
+       link_options->rate_5G = !!(speed & AQ_NIC_RATE_5G);
+       link_options->rate_N5G = !!(speed & AQ_NIC_RATE_5GSR);
+       link_options->rate_2P5G = !!(speed & AQ_NIC_RATE_2GS);
+       link_options->rate_N2P5G = link_options->rate_2P5G;
+       link_options->rate_1G = !!(speed & AQ_NIC_RATE_1G);
+       link_options->rate_100M = !!(speed & AQ_NIC_RATE_100M);
+       link_options->rate_10M = !!(speed & AQ_NIC_RATE_10M);
+}
+
+static int aq_a2_fw_set_link_speed(struct aq_hw_s *self, u32 speed)
+{
+       struct link_options_s link_options;
+
+       hw_atl2_shared_buffer_get(self, link_options, link_options);
+       link_options.link_up = 1U;
+       a2_link_speed_mask2fw(speed, &link_options);
+       hw_atl2_shared_buffer_write(self, link_options, link_options);
+
+       return hw_atl2_shared_buffer_finish_ack(self);
+}
+
+static int aq_a2_fw_set_state(struct aq_hw_s *self,
+                             enum hal_atl_utils_fw_state_e state)
+{
+       struct link_options_s link_options;
+
+       hw_atl2_shared_buffer_get(self, link_options, link_options);
+
+       switch (state) {
+       case MPI_INIT:
+               link_options.link_up = 1U;
+               break;
+       case MPI_DEINIT:
+               link_options.link_up = 0U;
+               break;
+       case MPI_RESET:
+       case MPI_POWER:
+               /* No actions */
+               break;
+       }
+
+       hw_atl2_shared_buffer_write(self, link_options, link_options);
+
+       return hw_atl2_shared_buffer_finish_ack(self);
+}
+
+static int aq_a2_fw_update_link_status(struct aq_hw_s *self)
+{
+       struct link_status_s link_status;
+
+       hw_atl2_shared_buffer_read(self, link_status, link_status);
+
+       switch (link_status.link_rate) {
+       case AQ_A2_FW_LINK_RATE_10G:
+               self->aq_link_status.mbps = 10000;
+               break;
+       case AQ_A2_FW_LINK_RATE_5G:
+               self->aq_link_status.mbps = 5000;
+               break;
+       case AQ_A2_FW_LINK_RATE_2G5:
+               self->aq_link_status.mbps = 2500;
+               break;
+       case AQ_A2_FW_LINK_RATE_1G:
+               self->aq_link_status.mbps = 1000;
+               break;
+       case AQ_A2_FW_LINK_RATE_100M:
+               self->aq_link_status.mbps = 100;
+               break;
+       case AQ_A2_FW_LINK_RATE_10M:
+               self->aq_link_status.mbps = 10;
+               break;
+       default:
+               self->aq_link_status.mbps = 0;
+       }
+
+       return 0;
+}
+
+static int aq_a2_fw_get_mac_permanent(struct aq_hw_s *self, u8 *mac)
+{
+       struct mac_address_aligned_s mac_address;
+
+       hw_atl2_shared_buffer_get(self, mac_address, mac_address);
+       ether_addr_copy(mac, (u8 *)mac_address.aligned.mac_address);
+
+       if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) {
+               unsigned int rnd = 0;
+               u32 h;
+               u32 l;
+
+               get_random_bytes(&rnd, sizeof(unsigned int));
+
+               l = 0xE3000000U | (0xFFFFU & rnd) | (0x00 << 16);
+               h = 0x8001300EU;
+
+               mac[5] = (u8)(0xFFU & l);
+               l >>= 8;
+               mac[4] = (u8)(0xFFU & l);
+               l >>= 8;
+               mac[3] = (u8)(0xFFU & l);
+               l >>= 8;
+               mac[2] = (u8)(0xFFU & l);
+               mac[1] = (u8)(0xFFU & h);
+               h >>= 8;
+               mac[0] = (u8)(0xFFU & h);
+       }
+
+       return 0;
+}
+
+static int aq_a2_fw_update_stats(struct aq_hw_s *self)
+{
+       struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv;
+       struct statistics_s stats;
+
+       hw_atl2_shared_buffer_read_safe(self, stats, &stats);
+
+#define AQ_SDELTA(_N_, _F_) (self->curr_stats._N_ += \
+                       stats.msm._F_ - priv->last_stats.msm._F_)
+
+       if (self->aq_link_status.mbps) {
+               AQ_SDELTA(uprc, rx_unicast_frames);
+               AQ_SDELTA(mprc, rx_multicast_frames);
+               AQ_SDELTA(bprc, rx_broadcast_frames);
+               AQ_SDELTA(erpr, rx_error_frames);
+
+               AQ_SDELTA(uptc, tx_unicast_frames);
+               AQ_SDELTA(mptc, tx_multicast_frames);
+               AQ_SDELTA(bptc, tx_broadcast_frames);
+               AQ_SDELTA(erpt, tx_errors);
+
+               AQ_SDELTA(ubrc, rx_unicast_octets);
+               AQ_SDELTA(ubtc, tx_unicast_octets);
+               AQ_SDELTA(mbrc, rx_multicast_octets);
+               AQ_SDELTA(mbtc, tx_multicast_octets);
+               AQ_SDELTA(bbrc, rx_broadcast_octets);
+               AQ_SDELTA(bbtc, tx_broadcast_octets);
+       }
+#undef AQ_SDELTA
+       self->curr_stats.dma_pkt_rc =
+               hw_atl_stats_rx_dma_good_pkt_counter_get(self);
+       self->curr_stats.dma_pkt_tc =
+               hw_atl_stats_tx_dma_good_pkt_counter_get(self);
+       self->curr_stats.dma_oct_rc =
+               hw_atl_stats_rx_dma_good_octet_counter_get(self);
+       self->curr_stats.dma_oct_tc =
+               hw_atl_stats_tx_dma_good_octet_counter_get(self);
+       self->curr_stats.dpc = hw_atl_rpb_rx_dma_drop_pkt_cnt_get(self);
+
+       memcpy(&priv->last_stats, &stats, sizeof(stats));
+
+       return 0;
+}
+
+static int aq_a2_fw_renegotiate(struct aq_hw_s *self)
+{
+       struct link_options_s link_options;
+       int err;
+
+       hw_atl2_shared_buffer_get(self, link_options, link_options);
+       link_options.link_renegotiate = 1U;
+       hw_atl2_shared_buffer_write(self, link_options, link_options);
+
+       err = hw_atl2_shared_buffer_finish_ack(self);
+
+       /* We should put renegotiate status back to zero
+        * after command completes
+        */
+       link_options.link_renegotiate = 0U;
+       hw_atl2_shared_buffer_write(self, link_options, link_options);
+
+       return err;
+}
+
+u32 hw_atl2_utils_get_fw_version(struct aq_hw_s *self)
+{
+       struct version_s version;
+
+       hw_atl2_shared_buffer_read_safe(self, version, &version);
+
+       /* A2 FW version is stored in reverse order */
+       return version.mac.major << 24 |
+              version.mac.minor << 16 |
+              version.mac.build;
+}
+
+int hw_atl2_utils_get_action_resolve_table_caps(struct aq_hw_s *self,
+                                               u8 *base_index, u8 *count)
+{
+       struct filter_caps_s filter_caps;
+       int err;
+
+       err = hw_atl2_shared_buffer_read_safe(self, filter_caps, &filter_caps);
+       if (err)
+               return err;
+
+       *base_index = filter_caps.rslv_tbl_base_index;
+       *count = filter_caps.rslv_tbl_count;
+       return 0;
+}
+
+const struct aq_fw_ops aq_a2_fw_ops = {
+       .init               = aq_a2_fw_init,
+       .deinit             = aq_a2_fw_deinit,
+       .reset              = NULL,
+       .renegotiate        = aq_a2_fw_renegotiate,
+       .get_mac_permanent  = aq_a2_fw_get_mac_permanent,
+       .set_link_speed     = aq_a2_fw_set_link_speed,
+       .set_state          = aq_a2_fw_set_state,
+       .update_link_status = aq_a2_fw_update_link_status,
+       .update_stats       = aq_a2_fw_update_stats,
+};
index 02b7705..112edbd 100644 (file)
@@ -871,13 +871,40 @@ static void ag71xx_mac_validate(struct phylink_config *config,
                            unsigned long *supported,
                            struct phylink_link_state *state)
 {
+       struct ag71xx *ag = netdev_priv(to_net_dev(config->dev));
        __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
 
-       if (state->interface != PHY_INTERFACE_MODE_NA &&
-           state->interface != PHY_INTERFACE_MODE_GMII &&
-           state->interface != PHY_INTERFACE_MODE_MII) {
-               bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
-               return;
+       switch (state->interface) {
+       case PHY_INTERFACE_MODE_NA:
+               break;
+       case PHY_INTERFACE_MODE_MII:
+               if ((ag71xx_is(ag, AR9330) && ag->mac_idx == 0) ||
+                   ag71xx_is(ag, AR9340) ||
+                   ag71xx_is(ag, QCA9530) ||
+                   (ag71xx_is(ag, QCA9550) && ag->mac_idx == 1))
+                       break;
+               goto unsupported;
+       case PHY_INTERFACE_MODE_GMII:
+               if ((ag71xx_is(ag, AR9330) && ag->mac_idx == 1) ||
+                   (ag71xx_is(ag, AR9340) && ag->mac_idx == 1) ||
+                   (ag71xx_is(ag, QCA9530) && ag->mac_idx == 1))
+                       break;
+               goto unsupported;
+       case PHY_INTERFACE_MODE_SGMII:
+               if (ag71xx_is(ag, QCA9550) && ag->mac_idx == 0)
+                       break;
+               goto unsupported;
+       case PHY_INTERFACE_MODE_RMII:
+               if (ag71xx_is(ag, AR9340) && ag->mac_idx == 0)
+                       break;
+               goto unsupported;
+       case PHY_INTERFACE_MODE_RGMII:
+               if ((ag71xx_is(ag, AR9340) && ag->mac_idx == 0) ||
+                   (ag71xx_is(ag, QCA9550) && ag->mac_idx == 1))
+                       break;
+               goto unsupported;
+       default:
+               goto unsupported;
        }
 
        phylink_set(mask, MII);
@@ -889,6 +916,8 @@ static void ag71xx_mac_validate(struct phylink_config *config,
        phylink_set(mask, 100baseT_Full);
 
        if (state->interface == PHY_INTERFACE_MODE_NA ||
+           state->interface == PHY_INTERFACE_MODE_SGMII ||
+           state->interface == PHY_INTERFACE_MODE_RGMII ||
            state->interface == PHY_INTERFACE_MODE_GMII) {
                phylink_set(mask, 1000baseT_Full);
                phylink_set(mask, 1000baseX_Full);
@@ -898,6 +927,10 @@ static void ag71xx_mac_validate(struct phylink_config *config,
                   __ETHTOOL_LINK_MODE_MASK_NBITS);
        bitmap_and(state->advertising, state->advertising, mask,
                   __ETHTOOL_LINK_MODE_MASK_NBITS);
+
+       return;
+unsupported:
+       bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
 }
 
 static void ag71xx_mac_pcs_get_state(struct phylink_config *config,
index 00bd7bd..decab9a 100644 (file)
@@ -1186,7 +1186,7 @@ static void atl1c_start_mac(struct atl1c_adapter *adapter)
        struct atl1c_hw *hw = &adapter->hw;
        u32 mac, txq, rxq;
 
-       hw->mac_duplex = adapter->link_duplex == FULL_DUPLEX ? true : false;
+       hw->mac_duplex = adapter->link_duplex == FULL_DUPLEX;
        hw->mac_speed = adapter->link_speed == SPEED_1000 ?
                atl1c_mac_speed_1000 : atl1c_mac_speed_10_100;
 
@@ -2449,12 +2449,6 @@ static int atl1c_resume(struct device *dev)
        atl1c_reset_mac(&adapter->hw);
        atl1c_phy_init(&adapter->hw);
 
-#if 0
-       AT_READ_REG(&adapter->hw, REG_PM_CTRLSTAT, &pm_data);
-       pm_data &= ~PM_CTRLSTAT_PME_EN;
-       AT_WRITE_REG(&adapter->hw, REG_PM_CTRLSTAT, pm_data);
-#endif
-
        netif_device_attach(netdev);
        if (netif_running(netdev))
                atl1c_up(adapter);
index af7ce5c..b25356e 100644 (file)
@@ -664,7 +664,8 @@ static struct sk_buff *bcm_sysport_rx_refill(struct bcm_sysport_priv *priv,
        dma_addr_t mapping;
 
        /* Allocate a new SKB for a new packet */
-       skb = netdev_alloc_skb(priv->netdev, RX_BUF_LENGTH);
+       skb = __netdev_alloc_skb(priv->netdev, RX_BUF_LENGTH,
+                                GFP_ATOMIC | __GFP_NOWARN);
        if (!skb) {
                priv->mib.alloc_rx_buff_failed++;
                netif_err(priv, rx_err, ndev, "SKB alloc failed\n");
@@ -2475,7 +2476,6 @@ static int bcm_sysport_probe(struct platform_device *pdev)
                priv->wol_irq = platform_get_irq(pdev, 1);
        }
        if (priv->irq0 <= 0 || (priv->irq1 <= 0 && !priv->is_lite)) {
-               dev_err(&pdev->dev, "invalid interrupts\n");
                ret = -EINVAL;
                goto err_free_netdev;
        }
index c46c1b1..6795b6d 100644 (file)
@@ -202,13 +202,8 @@ static int bgmac_probe(struct platform_device *pdev)
        if (bgmac->irq < 0)
                return bgmac->irq;
 
-       regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "amac_base");
-       if (!regs) {
-               dev_err(&pdev->dev, "Unable to obtain base resource\n");
-               return -EINVAL;
-       }
-
-       bgmac->plat.base = devm_ioremap_resource(&pdev->dev, regs);
+       bgmac->plat.base =
+               devm_platform_ioremap_resource_byname(pdev, "amac_base");
        if (IS_ERR(bgmac->plat.base))
                return PTR_ERR(bgmac->plat.base);
 
index 2c6ba04..17ae6df 100644 (file)
@@ -1145,7 +1145,7 @@ static void bnx2x_dcbx_get_num_pg_traf_type(struct bnx2x *bp,
                                        break;
                                }
                        }
-                       if (false == pg_found) {
+                       if (!pg_found) {
                                data[help_data->num_of_pg].pg = add_pg;
                                data[help_data->num_of_pg].pg_priority =
                                                (1 << ttp[add_traf_type]);
@@ -1155,7 +1155,7 @@ static void bnx2x_dcbx_get_num_pg_traf_type(struct bnx2x *bp,
                }
                DP(BNX2X_MSG_DCB,
                   "add_traf_type %d pg_found %s num_of_pg %d\n",
-                  add_traf_type, (false == pg_found) ? "NO" : "YES",
+                  add_traf_type, !pg_found ? "NO" : "YES",
                   help_data->num_of_pg);
        }
 }
@@ -1544,8 +1544,7 @@ static void bnx2x_dcbx_2cos_limit_cee_three_pg_to_cos_params(
                        if (pg_entry < DCBX_MAX_NUM_PG_BW_ENTRIES) {
                                entry = 0;
 
-                               if (i == (num_of_pri-1) &&
-                                   false == b_found_strict)
+                               if (i == (num_of_pri-1) && !b_found_strict)
                                        /* last entry will be handled separately
                                         * If no priority is strict than last
                                         * entry goes to last queue.
index 5097a44..b4476f4 100644 (file)
@@ -331,27 +331,6 @@ bnx2x_vf_set_igu_info(struct bnx2x *bp, u8 igu_sb_id, u8 abs_vfid)
        BP_VFDB(bp)->vf_sbs_pool++;
 }
 
-static inline void bnx2x_vf_vlan_credit(struct bnx2x *bp,
-                                       struct bnx2x_vlan_mac_obj *obj,
-                                       atomic_t *counter)
-{
-       struct list_head *pos;
-       int read_lock;
-       int cnt = 0;
-
-       read_lock = bnx2x_vlan_mac_h_read_lock(bp, obj);
-       if (read_lock)
-               DP(BNX2X_MSG_SP, "Failed to take vlan mac read head; continuing anyway\n");
-
-       list_for_each(pos, &obj->head)
-               cnt++;
-
-       if (!read_lock)
-               bnx2x_vlan_mac_h_read_unlock(bp, obj);
-
-       atomic_set(counter, cnt);
-}
-
 static int bnx2x_vf_vlan_mac_clear(struct bnx2x *bp, struct bnx2x_virtf *vf,
                                   int qid, bool drv_only, int type)
 {
index fead64f..f86b621 100644 (file)
@@ -1766,7 +1766,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
 
                rc = -EIO;
                if (rx_err & RX_CMPL_ERRORS_BUFFER_ERROR_MASK) {
-                       bnapi->cp_ring.rx_buf_errors++;
+                       bnapi->cp_ring.sw_stats.rx.rx_buf_errors++;
                        if (!(bp->flags & BNXT_FLAG_CHIP_P5)) {
                                netdev_warn(bp->dev, "RX buffer error %x\n",
                                            rx_err);
@@ -1849,7 +1849,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
        } else {
                if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS) {
                        if (dev->features & NETIF_F_RXCSUM)
-                               bnapi->cp_ring.rx_l4_csum_errors++;
+                               bnapi->cp_ring.sw_stats.rx.rx_l4_csum_errors++;
                }
        }
 
@@ -5045,8 +5045,7 @@ int bnxt_hwrm_vnic_cfg(struct bnxt *bp, u16 vnic_id)
        req.dflt_ring_grp = cpu_to_le16(bp->grp_info[grp_idx].fw_grp_id);
        req.lb_rule = cpu_to_le16(0xffff);
 vnic_mru:
-       req.mru = cpu_to_le16(bp->dev->mtu + ETH_HLEN + ETH_FCS_LEN +
-                             VLAN_HLEN);
+       req.mru = cpu_to_le16(bp->dev->mtu + ETH_HLEN + VLAN_HLEN);
 
        req.vnic_id = cpu_to_le16(vnic->fw_vnic_id);
 #ifdef CONFIG_BNXT_SRIOV
@@ -5356,9 +5355,9 @@ static void bnxt_set_db(struct bnxt *bp, struct bnxt_db_info *db, u32 ring_type,
 {
        if (bp->flags & BNXT_FLAG_CHIP_P5) {
                if (BNXT_PF(bp))
-                       db->doorbell = bp->bar1 + 0x10000;
+                       db->doorbell = bp->bar1 + DB_PF_OFFSET_P5;
                else
-                       db->doorbell = bp->bar1 + 0x4000;
+                       db->doorbell = bp->bar1 + DB_VF_OFFSET_P5;
                switch (ring_type) {
                case HWRM_RING_ALLOC_TX:
                        db->db_key64 = DBR_PATH_L2 | DBR_TYPE_SQ;
@@ -6365,6 +6364,7 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp)
 {
        struct hwrm_func_qcfg_input req = {0};
        struct hwrm_func_qcfg_output *resp = bp->hwrm_cmd_resp_addr;
+       u32 min_db_offset = 0;
        u16 flags;
        int rc;
 
@@ -6413,6 +6413,21 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp)
        if (!bp->max_mtu)
                bp->max_mtu = BNXT_MAX_MTU;
 
+       if (bp->db_size)
+               goto func_qcfg_exit;
+
+       if (bp->flags & BNXT_FLAG_CHIP_P5) {
+               if (BNXT_PF(bp))
+                       min_db_offset = DB_PF_OFFSET_P5;
+               else
+                       min_db_offset = DB_VF_OFFSET_P5;
+       }
+       bp->db_size = PAGE_ALIGN(le16_to_cpu(resp->l2_doorbell_bar_size_kb) *
+                                1024);
+       if (!bp->db_size || bp->db_size > pci_resource_len(bp->pdev, 2) ||
+           bp->db_size <= min_db_offset)
+               bp->db_size = pci_resource_len(bp->pdev, 2);
+
 func_qcfg_exit:
        mutex_unlock(&bp->hwrm_cmd_lock);
        return rc;
@@ -6434,23 +6449,13 @@ static int bnxt_hwrm_func_backing_store_qcaps(struct bnxt *bp)
        if (!rc) {
                struct bnxt_ctx_pg_info *ctx_pg;
                struct bnxt_ctx_mem_info *ctx;
-               int i;
+               int i, tqm_rings;
 
                ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
                if (!ctx) {
                        rc = -ENOMEM;
                        goto ctx_err;
                }
-               ctx_pg = kzalloc(sizeof(*ctx_pg) * (bp->max_q + 1), GFP_KERNEL);
-               if (!ctx_pg) {
-                       kfree(ctx);
-                       rc = -ENOMEM;
-                       goto ctx_err;
-               }
-               for (i = 0; i < bp->max_q + 1; i++, ctx_pg++)
-                       ctx->tqm_mem[i] = ctx_pg;
-
-               bp->ctx = ctx;
                ctx->qp_max_entries = le32_to_cpu(resp->qp_max_entries);
                ctx->qp_min_qp1_entries = le16_to_cpu(resp->qp_min_qp1_entries);
                ctx->qp_max_l2_entries = le16_to_cpu(resp->qp_max_l2_entries);
@@ -6483,6 +6488,20 @@ static int bnxt_hwrm_func_backing_store_qcaps(struct bnxt *bp)
                ctx->tim_entry_size = le16_to_cpu(resp->tim_entry_size);
                ctx->tim_max_entries = le32_to_cpu(resp->tim_max_entries);
                ctx->ctx_kind_initializer = resp->ctx_kind_initializer;
+               ctx->tqm_fp_rings_count = resp->tqm_fp_rings_count;
+               if (!ctx->tqm_fp_rings_count)
+                       ctx->tqm_fp_rings_count = bp->max_q;
+
+               tqm_rings = ctx->tqm_fp_rings_count + 1;
+               ctx_pg = kcalloc(tqm_rings, sizeof(*ctx_pg), GFP_KERNEL);
+               if (!ctx_pg) {
+                       kfree(ctx);
+                       rc = -ENOMEM;
+                       goto ctx_err;
+               }
+               for (i = 0; i < tqm_rings; i++, ctx_pg++)
+                       ctx->tqm_mem[i] = ctx_pg;
+               bp->ctx = ctx;
        } else {
                rc = 0;
        }
@@ -6642,7 +6661,7 @@ static int bnxt_alloc_ctx_pg_tbls(struct bnxt *bp,
        int rc;
 
        if (!mem_size)
-               return 0;
+               return -EINVAL;
 
        ctx_pg->nr_pages = DIV_ROUND_UP(mem_size, BNXT_PAGE_SIZE);
        if (ctx_pg->nr_pages > MAX_CTX_TOTAL_PAGES) {
@@ -6735,7 +6754,7 @@ static void bnxt_free_ctx_mem(struct bnxt *bp)
                return;
 
        if (ctx->tqm_mem[0]) {
-               for (i = 0; i < bp->max_q + 1; i++)
+               for (i = 0; i < ctx->tqm_fp_rings_count + 1; i++)
                        bnxt_free_ctx_pg_tbls(bp, ctx->tqm_mem[i]);
                kfree(ctx->tqm_mem[0]);
                ctx->tqm_mem[0] = NULL;
@@ -6756,6 +6775,7 @@ static int bnxt_alloc_ctx_mem(struct bnxt *bp)
        struct bnxt_ctx_pg_info *ctx_pg;
        struct bnxt_ctx_mem_info *ctx;
        u32 mem_size, ena, entries;
+       u32 entries_sp, min;
        u32 num_mr, num_ah;
        u32 extra_srqs = 0;
        u32 extra_qps = 0;
@@ -6845,14 +6865,17 @@ static int bnxt_alloc_ctx_mem(struct bnxt *bp)
        ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_TIM;
 
 skip_rdma:
-       entries = ctx->qp_max_l2_entries + extra_qps;
+       min = ctx->tqm_min_entries_per_ring;
+       entries_sp = ctx->vnic_max_vnic_entries + ctx->qp_max_l2_entries +
+                    2 * (extra_qps + ctx->qp_min_qp1_entries) + min;
+       entries_sp = roundup(entries_sp, ctx->tqm_entries_multiple);
+       entries = ctx->qp_max_l2_entries + extra_qps + ctx->qp_min_qp1_entries;
        entries = roundup(entries, ctx->tqm_entries_multiple);
-       entries = clamp_t(u32, entries, ctx->tqm_min_entries_per_ring,
-                         ctx->tqm_max_entries_per_ring);
-       for (i = 0; i < bp->max_q + 1; i++) {
+       entries = clamp_t(u32, entries, min, ctx->tqm_max_entries_per_ring);
+       for (i = 0; i < ctx->tqm_fp_rings_count + 1; i++) {
                ctx_pg = ctx->tqm_mem[i];
-               ctx_pg->entries = entries;
-               mem_size = ctx->tqm_entry_size * entries;
+               ctx_pg->entries = i ? entries : entries_sp;
+               mem_size = ctx->tqm_entry_size * ctx_pg->entries;
                rc = bnxt_alloc_ctx_pg_tbls(bp, ctx_pg, mem_size, 1, false);
                if (rc)
                        return rc;
@@ -9780,6 +9803,7 @@ static netdev_features_t bnxt_fix_features(struct net_device *dev,
                                           netdev_features_t features)
 {
        struct bnxt *bp = netdev_priv(dev);
+       netdev_features_t vlan_features;
 
        if ((features & NETIF_F_NTUPLE) && !bnxt_rfs_capable(bp))
                features &= ~NETIF_F_NTUPLE;
@@ -9796,12 +9820,14 @@ static netdev_features_t bnxt_fix_features(struct net_device *dev,
        /* Both CTAG and STAG VLAN accelaration on the RX side have to be
         * turned on or off together.
         */
-       if ((features & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX)) !=
-           (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX)) {
+       vlan_features = features & (NETIF_F_HW_VLAN_CTAG_RX |
+                                   NETIF_F_HW_VLAN_STAG_RX);
+       if (vlan_features != (NETIF_F_HW_VLAN_CTAG_RX |
+                             NETIF_F_HW_VLAN_STAG_RX)) {
                if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
                        features &= ~(NETIF_F_HW_VLAN_CTAG_RX |
                                      NETIF_F_HW_VLAN_STAG_RX);
-               else
+               else if (vlan_features)
                        features |= NETIF_F_HW_VLAN_CTAG_RX |
                                    NETIF_F_HW_VLAN_STAG_RX;
        }
@@ -10262,7 +10288,7 @@ static void bnxt_chk_missed_irq(struct bnxt *bp)
                        bnxt_dbg_hwrm_ring_info_get(bp,
                                DBG_RING_INFO_GET_REQ_RING_TYPE_L2_CMPL,
                                fw_ring_id, &val[0], &val[1]);
-                       cpr->missed_irqs++;
+                       cpr->sw_stats.cmn.missed_irqs++;
                }
        }
 }
@@ -10891,6 +10917,9 @@ static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev)
        bp->dev = dev;
        bp->pdev = pdev;
 
+       /* Doorbell BAR bp->bar1 is mapped after bnxt_fw_init_one_p2()
+        * determines the BAR size.
+        */
        bp->bar0 = pci_ioremap_bar(pdev, 0);
        if (!bp->bar0) {
                dev_err(&pdev->dev, "Cannot map device registers, aborting\n");
@@ -10898,13 +10927,6 @@ static int bnxt_init_board(struct pci_dev *pdev, struct net_device *dev)
                goto init_err_release;
        }
 
-       bp->bar1 = pci_ioremap_bar(pdev, 2);
-       if (!bp->bar1) {
-               dev_err(&pdev->dev, "Cannot map doorbell registers, aborting\n");
-               rc = -ENOMEM;
-               goto init_err_release;
-       }
-
        bp->bar2 = pci_ioremap_bar(pdev, 4);
        if (!bp->bar2) {
                dev_err(&pdev->dev, "Cannot map bar4 registers, aborting\n");
@@ -11826,6 +11848,16 @@ static int bnxt_pcie_dsn_get(struct bnxt *bp, u8 dsn[])
        return 0;
 }
 
+static int bnxt_map_db_bar(struct bnxt *bp)
+{
+       if (!bp->db_size)
+               return -ENODEV;
+       bp->bar1 = pci_iomap(bp->pdev, 2, bp->db_size);
+       if (!bp->bar1)
+               return -ENOMEM;
+       return 0;
+}
+
 static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        struct net_device *dev;
@@ -11886,6 +11918,13 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (rc)
                goto init_err_pci_clean;
 
+       rc = bnxt_map_db_bar(bp);
+       if (rc) {
+               dev_err(&pdev->dev, "Cannot map doorbell BAR rc = %d, aborting\n",
+                       rc);
+               goto init_err_pci_clean;
+       }
+
        dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG |
                           NETIF_F_TSO | NETIF_F_TSO6 |
                           NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE |
@@ -12212,12 +12251,15 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev)
                bnxt_ulp_start(bp, err);
        }
 
-       if (result != PCI_ERS_RESULT_RECOVERED && netif_running(netdev))
-               dev_close(netdev);
+       if (result != PCI_ERS_RESULT_RECOVERED) {
+               if (netif_running(netdev))
+                       dev_close(netdev);
+               pci_disable_device(pdev);
+       }
 
        rtnl_unlock();
 
-       return PCI_ERS_RESULT_RECOVERED;
+       return result;
 }
 
 /**
index f2caa27..c04ac4a 100644 (file)
@@ -537,6 +537,9 @@ struct nqe_cn {
 #define DBR_TYPE_NQ_ARM                                        (0xbULL << 60)
 #define DBR_TYPE_NULL                                  (0xfULL << 60)
 
+#define DB_PF_OFFSET_P5                                        0x10000
+#define DB_VF_OFFSET_P5                                        0x4000
+
 #define INVALID_HW_RING_ID     ((u16)-1)
 
 /* The hardware supports certain page sizes.  Use the supported page sizes
@@ -907,6 +910,20 @@ struct bnxt_rx_ring_info {
        struct page_pool        *page_pool;
 };
 
+struct bnxt_rx_sw_stats {
+       u64                     rx_l4_csum_errors;
+       u64                     rx_buf_errors;
+};
+
+struct bnxt_cmn_sw_stats {
+       u64                     missed_irqs;
+};
+
+struct bnxt_sw_stats {
+       struct bnxt_rx_sw_stats rx;
+       struct bnxt_cmn_sw_stats cmn;
+};
+
 struct bnxt_cp_ring_info {
        struct bnxt_napi        *bnapi;
        u32                     cp_raw_cons;
@@ -934,9 +951,8 @@ struct bnxt_cp_ring_info {
        struct ctx_hw_stats     *hw_stats;
        dma_addr_t              hw_stats_map;
        u32                     hw_stats_ctx_id;
-       u64                     rx_l4_csum_errors;
-       u64                     rx_buf_errors;
-       u64                     missed_irqs;
+
+       struct bnxt_sw_stats    sw_stats;
 
        struct bnxt_ring_struct cp_ring_struct;
 
@@ -1066,7 +1082,6 @@ struct bnxt_vf_info {
 #define BNXT_VF_LINK_FORCED    0x4
 #define BNXT_VF_LINK_UP                0x8
 #define BNXT_VF_TRUST          0x10
-       u32     func_flags; /* func cfg flags */
        u32     min_tx_rate;
        u32     max_tx_rate;
        void    *hwrm_cmd_req_addr;
@@ -1357,6 +1372,7 @@ struct bnxt_ctx_mem_info {
        u16     mrav_num_entries_units;
        u8      tqm_entries_multiple;
        u8      ctx_kind_initializer;
+       u8      tqm_fp_rings_count;
 
        u32     flags;
        #define BNXT_CTX_FLAG_INITED    0x01
@@ -1816,6 +1832,7 @@ struct bnxt {
        /* ensure atomic 64-bit doorbell writes on 32-bit systems. */
        spinlock_t              db_lock;
 #endif
+       int                     db_size;
 
 #define BNXT_NTP_FLTR_MAX_FLTR 4096
 #define BNXT_NTP_FLTR_HASH_SIZE        512
index 95f893f..d5c8bd4 100644 (file)
@@ -43,7 +43,7 @@ static inline void bnxt_link_bp_to_dl(struct bnxt *bp, struct devlink *dl)
 #define BNXT_NVM_CFG_VER_BITS          24
 #define BNXT_NVM_CFG_VER_BYTES         4
 
-#define BNXT_MSIX_VEC_MAX      1280
+#define BNXT_MSIX_VEC_MAX      512
 #define BNXT_MSIX_VEC_MIN_MAX  128
 
 enum bnxt_nvm_dir_type {
index 34046a6..dd0c3f2 100644 (file)
@@ -137,7 +137,7 @@ reset_coalesce:
        return rc;
 }
 
-static const char * const bnxt_ring_stats_str[] = {
+static const char * const bnxt_ring_rx_stats_str[] = {
        "rx_ucast_packets",
        "rx_mcast_packets",
        "rx_bcast_packets",
@@ -146,6 +146,9 @@ static const char * const bnxt_ring_stats_str[] = {
        "rx_ucast_bytes",
        "rx_mcast_bytes",
        "rx_bcast_bytes",
+};
+
+static const char * const bnxt_ring_tx_stats_str[] = {
        "tx_ucast_packets",
        "tx_mcast_packets",
        "tx_bcast_packets",
@@ -171,9 +174,12 @@ static const char * const bnxt_ring_tpa2_stats_str[] = {
        "rx_tpa_errors",
 };
 
-static const char * const bnxt_ring_sw_stats_str[] = {
+static const char * const bnxt_rx_sw_stats_str[] = {
        "rx_l4_csum_errors",
        "rx_buf_errors",
+};
+
+static const char * const bnxt_cmn_sw_stats_str[] = {
        "missed_irqs",
 };
 
@@ -303,6 +309,11 @@ static struct {
        {0, "tx_total_discard_pkts"},
 };
 
+#define NUM_RING_RX_SW_STATS           ARRAY_SIZE(bnxt_rx_sw_stats_str)
+#define NUM_RING_CMN_SW_STATS          ARRAY_SIZE(bnxt_cmn_sw_stats_str)
+#define NUM_RING_RX_HW_STATS           ARRAY_SIZE(bnxt_ring_rx_stats_str)
+#define NUM_RING_TX_HW_STATS           ARRAY_SIZE(bnxt_ring_tx_stats_str)
+
 static const struct {
        long offset;
        char string[ETH_GSTRING_LEN];
@@ -482,12 +493,21 @@ static int bnxt_get_num_tpa_ring_stats(struct bnxt *bp)
 
 static int bnxt_get_num_ring_stats(struct bnxt *bp)
 {
-       int num_stats;
+       int rx, tx, cmn;
+       bool sh = false;
+
+       if (bp->flags & BNXT_FLAG_SHARED_RINGS)
+               sh = true;
 
-       num_stats = ARRAY_SIZE(bnxt_ring_stats_str) +
-                   ARRAY_SIZE(bnxt_ring_sw_stats_str) +
-                   bnxt_get_num_tpa_ring_stats(bp);
-       return num_stats * bp->cp_nr_rings;
+       rx = NUM_RING_RX_HW_STATS + NUM_RING_RX_SW_STATS +
+            bnxt_get_num_tpa_ring_stats(bp);
+       tx = NUM_RING_TX_HW_STATS;
+       cmn = NUM_RING_CMN_SW_STATS;
+       if (sh)
+               return (rx + tx + cmn) * bp->cp_nr_rings;
+       else
+               return rx * bp->rx_nr_rings + tx * bp->tx_nr_rings +
+                      cmn * bp->cp_nr_rings;
 }
 
 static int bnxt_get_num_stats(struct bnxt *bp)
@@ -528,13 +548,29 @@ static int bnxt_get_sset_count(struct net_device *dev, int sset)
        }
 }
 
+static bool is_rx_ring(struct bnxt *bp, int ring_num)
+{
+       return ring_num < bp->rx_nr_rings;
+}
+
+static bool is_tx_ring(struct bnxt *bp, int ring_num)
+{
+       int tx_base = 0;
+
+       if (!(bp->flags & BNXT_FLAG_SHARED_RINGS))
+               tx_base = bp->rx_nr_rings;
+
+       if (ring_num >= tx_base && ring_num < (tx_base + bp->tx_nr_rings))
+               return true;
+       return false;
+}
+
 static void bnxt_get_ethtool_stats(struct net_device *dev,
                                   struct ethtool_stats *stats, u64 *buf)
 {
        u32 i, j = 0;
        struct bnxt *bp = netdev_priv(dev);
-       u32 stat_fields = ARRAY_SIZE(bnxt_ring_stats_str) +
-                         bnxt_get_num_tpa_ring_stats(bp);
+       u32 tpa_stats;
 
        if (!bp->bnapi) {
                j += bnxt_get_num_ring_stats(bp) + BNXT_NUM_SW_FUNC_STATS;
@@ -544,17 +580,42 @@ static void bnxt_get_ethtool_stats(struct net_device *dev,
        for (i = 0; i < BNXT_NUM_SW_FUNC_STATS; i++)
                bnxt_sw_func_stats[i].counter = 0;
 
+       tpa_stats = bnxt_get_num_tpa_ring_stats(bp);
        for (i = 0; i < bp->cp_nr_rings; i++) {
                struct bnxt_napi *bnapi = bp->bnapi[i];
                struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
                __le64 *hw_stats = (__le64 *)cpr->hw_stats;
+               u64 *sw;
                int k;
 
-               for (k = 0; k < stat_fields; j++, k++)
+               if (is_rx_ring(bp, i)) {
+                       for (k = 0; k < NUM_RING_RX_HW_STATS; j++, k++)
+                               buf[j] = le64_to_cpu(hw_stats[k]);
+               }
+               if (is_tx_ring(bp, i)) {
+                       k = NUM_RING_RX_HW_STATS;
+                       for (; k < NUM_RING_RX_HW_STATS + NUM_RING_TX_HW_STATS;
+                              j++, k++)
+                               buf[j] = le64_to_cpu(hw_stats[k]);
+               }
+               if (!tpa_stats || !is_rx_ring(bp, i))
+                       goto skip_tpa_ring_stats;
+
+               k = NUM_RING_RX_HW_STATS + NUM_RING_TX_HW_STATS;
+               for (; k < NUM_RING_RX_HW_STATS + NUM_RING_TX_HW_STATS +
+                          tpa_stats; j++, k++)
                        buf[j] = le64_to_cpu(hw_stats[k]);
-               buf[j++] = cpr->rx_l4_csum_errors;
-               buf[j++] = cpr->rx_buf_errors;
-               buf[j++] = cpr->missed_irqs;
+
+skip_tpa_ring_stats:
+               sw = (u64 *)&cpr->sw_stats.rx;
+               if (is_rx_ring(bp, i)) {
+                       for (k = 0; k < NUM_RING_RX_SW_STATS; j++, k++)
+                               buf[j] = sw[k];
+               }
+
+               sw = (u64 *)&cpr->sw_stats.cmn;
+               for (k = 0; k < NUM_RING_CMN_SW_STATS; j++, k++)
+                       buf[j] = sw[k];
 
                bnxt_sw_func_stats[RX_TOTAL_DISCARDS].counter +=
                        le64_to_cpu(cpr->hw_stats->rx_discard_pkts);
@@ -632,31 +693,48 @@ static void bnxt_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
        switch (stringset) {
        case ETH_SS_STATS:
                for (i = 0; i < bp->cp_nr_rings; i++) {
-                       num_str = ARRAY_SIZE(bnxt_ring_stats_str);
-                       for (j = 0; j < num_str; j++) {
-                               sprintf(buf, "[%d]: %s", i,
-                                       bnxt_ring_stats_str[j]);
-                               buf += ETH_GSTRING_LEN;
+                       if (is_rx_ring(bp, i)) {
+                               num_str = NUM_RING_RX_HW_STATS;
+                               for (j = 0; j < num_str; j++) {
+                                       sprintf(buf, "[%d]: %s", i,
+                                               bnxt_ring_rx_stats_str[j]);
+                                       buf += ETH_GSTRING_LEN;
+                               }
                        }
-                       if (!BNXT_SUPPORTS_TPA(bp))
+                       if (is_tx_ring(bp, i)) {
+                               num_str = NUM_RING_TX_HW_STATS;
+                               for (j = 0; j < num_str; j++) {
+                                       sprintf(buf, "[%d]: %s", i,
+                                               bnxt_ring_tx_stats_str[j]);
+                                       buf += ETH_GSTRING_LEN;
+                               }
+                       }
+                       num_str = bnxt_get_num_tpa_ring_stats(bp);
+                       if (!num_str || !is_rx_ring(bp, i))
                                goto skip_tpa_stats;
 
-                       if (bp->max_tpa_v2) {
-                               num_str = ARRAY_SIZE(bnxt_ring_tpa2_stats_str);
+                       if (bp->max_tpa_v2)
                                str = bnxt_ring_tpa2_stats_str;
-                       } else {
-                               num_str = ARRAY_SIZE(bnxt_ring_tpa_stats_str);
+                       else
                                str = bnxt_ring_tpa_stats_str;
-                       }
+
                        for (j = 0; j < num_str; j++) {
                                sprintf(buf, "[%d]: %s", i, str[j]);
                                buf += ETH_GSTRING_LEN;
                        }
 skip_tpa_stats:
-                       num_str = ARRAY_SIZE(bnxt_ring_sw_stats_str);
+                       if (is_rx_ring(bp, i)) {
+                               num_str = NUM_RING_RX_SW_STATS;
+                               for (j = 0; j < num_str; j++) {
+                                       sprintf(buf, "[%d]: %s", i,
+                                               bnxt_rx_sw_stats_str[j]);
+                                       buf += ETH_GSTRING_LEN;
+                               }
+                       }
+                       num_str = NUM_RING_CMN_SW_STATS;
                        for (j = 0; j < num_str; j++) {
                                sprintf(buf, "[%d]: %s", i,
-                                       bnxt_ring_sw_stats_str[j]);
+                                       bnxt_cmn_sw_stats_str[j]);
                                buf += ETH_GSTRING_LEN;
                        }
                }
@@ -1749,8 +1827,8 @@ static int bnxt_flash_nvram(struct net_device *dev,
        return rc;
 }
 
-static int bnxt_firmware_reset(struct net_device *dev,
-                              u16 dir_type)
+static int bnxt_hwrm_firmware_reset(struct net_device *dev, u8 proc_type,
+                                   u8 self_reset, u8 flags)
 {
        struct hwrm_fw_reset_input req = {0};
        struct bnxt *bp = netdev_priv(dev);
@@ -1758,48 +1836,77 @@ static int bnxt_firmware_reset(struct net_device *dev,
 
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FW_RESET, -1, -1);
 
+       req.embedded_proc_type = proc_type;
+       req.selfrst_status = self_reset;
+       req.flags = flags;
+
+       if (proc_type == FW_RESET_REQ_EMBEDDED_PROC_TYPE_AP) {
+               rc = hwrm_send_message_silent(bp, &req, sizeof(req),
+                                             HWRM_CMD_TIMEOUT);
+       } else {
+               rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+               if (rc == -EACCES)
+                       bnxt_print_admin_err(bp);
+       }
+       return rc;
+}
+
+static int bnxt_firmware_reset(struct net_device *dev,
+                              enum bnxt_nvm_directory_type dir_type)
+{
+       u8 self_reset = FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE;
+       u8 proc_type, flags = 0;
+
        /* TODO: Address self-reset of APE/KONG/BONO/TANG or ungraceful reset */
        /*       (e.g. when firmware isn't already running) */
        switch (dir_type) {
        case BNX_DIR_TYPE_CHIMP_PATCH:
        case BNX_DIR_TYPE_BOOTCODE:
        case BNX_DIR_TYPE_BOOTCODE_2:
-               req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_BOOT;
+               proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_BOOT;
                /* Self-reset ChiMP upon next PCIe reset: */
-               req.selfrst_status = FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST;
+               self_reset = FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST;
                break;
        case BNX_DIR_TYPE_APE_FW:
        case BNX_DIR_TYPE_APE_PATCH:
-               req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT;
+               proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT;
                /* Self-reset APE upon next PCIe reset: */
-               req.selfrst_status = FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST;
+               self_reset = FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST;
                break;
        case BNX_DIR_TYPE_KONG_FW:
        case BNX_DIR_TYPE_KONG_PATCH:
-               req.embedded_proc_type =
-                       FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL;
+               proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL;
                break;
        case BNX_DIR_TYPE_BONO_FW:
        case BNX_DIR_TYPE_BONO_PATCH:
-               req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE;
-               break;
-       case BNXT_FW_RESET_CHIP:
-               req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIP;
-               req.selfrst_status = FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP;
-               if (bp->fw_cap & BNXT_FW_CAP_HOT_RESET)
-                       req.flags = FW_RESET_REQ_FLAGS_RESET_GRACEFUL;
-               break;
-       case BNXT_FW_RESET_AP:
-               req.embedded_proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_AP;
+               proc_type = FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE;
                break;
        default:
                return -EINVAL;
        }
 
-       rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
-       if (rc == -EACCES)
-               bnxt_print_admin_err(bp);
-       return rc;
+       return bnxt_hwrm_firmware_reset(dev, proc_type, self_reset, flags);
+}
+
+static int bnxt_firmware_reset_chip(struct net_device *dev)
+{
+       struct bnxt *bp = netdev_priv(dev);
+       u8 flags = 0;
+
+       if (bp->fw_cap & BNXT_FW_CAP_HOT_RESET)
+               flags = FW_RESET_REQ_FLAGS_RESET_GRACEFUL;
+
+       return bnxt_hwrm_firmware_reset(dev,
+                                       FW_RESET_REQ_EMBEDDED_PROC_TYPE_CHIP,
+                                       FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP,
+                                       flags);
+}
+
+static int bnxt_firmware_reset_ap(struct net_device *dev)
+{
+       return bnxt_hwrm_firmware_reset(dev, FW_RESET_REQ_EMBEDDED_PROC_TYPE_AP,
+                                       FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE,
+                                       0);
 }
 
 static int bnxt_flash_firmware(struct net_device *dev,
@@ -1988,9 +2095,9 @@ static int bnxt_flash_firmware_from_file(struct net_device *dev,
                           rc, filename);
                return rc;
        }
-       if (bnxt_dir_type_is_ape_bin_format(dir_type) == true)
+       if (bnxt_dir_type_is_ape_bin_format(dir_type))
                rc = bnxt_flash_firmware(dev, dir_type, fw->data, fw->size);
-       else if (bnxt_dir_type_is_other_exec_format(dir_type) == true)
+       else if (bnxt_dir_type_is_other_exec_format(dir_type))
                rc = bnxt_flash_microcode(dev, dir_type, fw->data, fw->size);
        else
                rc = bnxt_flash_nvram(dev, dir_type, BNX_DIR_ORDINAL_FIRST,
@@ -2377,7 +2484,7 @@ static int bnxt_set_eeprom(struct net_device *dev,
        }
 
        /* Create or re-write an NVM item: */
-       if (bnxt_dir_type_is_executable(type) == true)
+       if (bnxt_dir_type_is_executable(type))
                return -EOPNOTSUPP;
        ext = eeprom->magic & 0xffff;
        ordinal = eeprom->offset >> 16;
@@ -2975,7 +3082,11 @@ static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest,
 static int bnxt_reset(struct net_device *dev, u32 *flags)
 {
        struct bnxt *bp = netdev_priv(dev);
-       int rc = 0;
+       bool reload = false;
+       u32 req = *flags;
+
+       if (!req)
+               return -EINVAL;
 
        if (!BNXT_PF(bp)) {
                netdev_err(dev, "Reset is not supported from a VF\n");
@@ -2989,33 +3100,37 @@ static int bnxt_reset(struct net_device *dev, u32 *flags)
                return -EBUSY;
        }
 
-       if (*flags == ETH_RESET_ALL) {
+       if ((req & BNXT_FW_RESET_CHIP) == BNXT_FW_RESET_CHIP) {
                /* This feature is not supported in older firmware versions */
-               if (bp->hwrm_spec_code < 0x10803)
-                       return -EOPNOTSUPP;
-
-               rc = bnxt_firmware_reset(dev, BNXT_FW_RESET_CHIP);
-               if (!rc) {
-                       netdev_info(dev, "Reset request successful.\n");
-                       if (!(bp->fw_cap & BNXT_FW_CAP_HOT_RESET))
-                               netdev_info(dev, "Reload driver to complete reset\n");
-                       *flags = 0;
+               if (bp->hwrm_spec_code >= 0x10803) {
+                       if (!bnxt_firmware_reset_chip(dev)) {
+                               netdev_info(dev, "Firmware reset request successful.\n");
+                               if (!(bp->fw_cap & BNXT_FW_CAP_HOT_RESET))
+                                       reload = true;
+                               *flags &= ~BNXT_FW_RESET_CHIP;
+                       }
+               } else if (req == BNXT_FW_RESET_CHIP) {
+                       return -EOPNOTSUPP; /* only request, fail hard */
                }
-       } else if (*flags == ETH_RESET_AP) {
-               /* This feature is not supported in older firmware versions */
-               if (bp->hwrm_spec_code < 0x10803)
-                       return -EOPNOTSUPP;
+       }
 
-               rc = bnxt_firmware_reset(dev, BNXT_FW_RESET_AP);
-               if (!rc) {
-                       netdev_info(dev, "Reset Application Processor request successful.\n");
-                       *flags = 0;
+       if (req & BNXT_FW_RESET_AP) {
+               /* This feature is not supported in older firmware versions */
+               if (bp->hwrm_spec_code >= 0x10803) {
+                       if (!bnxt_firmware_reset_ap(dev)) {
+                               netdev_info(dev, "Reset application processor successful.\n");
+                               reload = true;
+                               *flags &= ~BNXT_FW_RESET_AP;
+                       }
+               } else if (req == BNXT_FW_RESET_AP) {
+                       return -EOPNOTSUPP; /* only request, fail hard */
                }
-       } else {
-               rc = -EINVAL;
        }
 
-       return rc;
+       if (reload)
+               netdev_info(dev, "Reload driver to complete reset\n");
+
+       return 0;
 }
 
 static int bnxt_hwrm_dbg_dma_data(struct bnxt *bp, void *msg, int msg_len,
index 3576d95..ce7585f 100644 (file)
@@ -77,8 +77,12 @@ struct hwrm_dbg_cmn_output {
 #define BNXT_LED_DFLT_ENABLES(x)                       \
        cpu_to_le32(BNXT_LED_DFLT_ENA << (BNXT_LED_DFLT_ENA_SHIFT * (x)))
 
-#define BNXT_FW_RESET_AP       0xfffe
-#define BNXT_FW_RESET_CHIP     0xffff
+#define BNXT_FW_RESET_AP       (ETH_RESET_AP << ETH_RESET_SHARED_SHIFT)
+#define BNXT_FW_RESET_CHIP     ((ETH_RESET_MGMT | ETH_RESET_IRQ |      \
+                                 ETH_RESET_DMA | ETH_RESET_FILTER |    \
+                                 ETH_RESET_OFFLOAD | ETH_RESET_MAC |   \
+                                 ETH_RESET_PHY | ETH_RESET_RAM)        \
+                                << ETH_RESET_SHARED_SHIFT)
 
 extern const struct ethtool_ops bnxt_ethtool_ops;
 
index 7cf27df..7e9235c 100644 (file)
@@ -2,7 +2,7 @@
  *
  * Copyright (c) 2014-2016 Broadcom Corporation
  * Copyright (c) 2014-2018 Broadcom Limited
- * Copyright (c) 2018-2019 Broadcom Inc.
+ * Copyright (c) 2018-2020 Broadcom Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -207,6 +207,8 @@ struct cmd_nums {
        #define HWRM_PORT_PHY_MDIO_READ                   0xb6UL
        #define HWRM_PORT_PHY_MDIO_BUS_ACQUIRE            0xb7UL
        #define HWRM_PORT_PHY_MDIO_BUS_RELEASE            0xb8UL
+       #define HWRM_PORT_QSTATS_EXT_PFC_WD               0xb9UL
+       #define HWRM_PORT_ECN_QSTATS                      0xbaUL
        #define HWRM_FW_RESET                             0xc0UL
        #define HWRM_FW_QSTATUS                           0xc1UL
        #define HWRM_FW_HEALTH_CHECK                      0xc2UL
@@ -220,6 +222,8 @@ struct cmd_nums {
        #define HWRM_FW_SET_STRUCTURED_DATA               0xcaUL
        #define HWRM_FW_GET_STRUCTURED_DATA               0xcbUL
        #define HWRM_FW_IPC_MAILBOX                       0xccUL
+       #define HWRM_FW_ECN_CFG                           0xcdUL
+       #define HWRM_FW_ECN_QCFG                          0xceUL
        #define HWRM_EXEC_FWD_RESP                        0xd0UL
        #define HWRM_REJECT_FWD_RESP                      0xd1UL
        #define HWRM_FWD_RESP                             0xd2UL
@@ -233,6 +237,7 @@ struct cmd_nums {
        #define HWRM_TEMP_MONITOR_QUERY                   0xe0UL
        #define HWRM_REG_POWER_QUERY                      0xe1UL
        #define HWRM_CORE_FREQUENCY_QUERY                 0xe2UL
+       #define HWRM_REG_POWER_HISTOGRAM                  0xe3UL
        #define HWRM_WOL_FILTER_ALLOC                     0xf0UL
        #define HWRM_WOL_FILTER_FREE                      0xf1UL
        #define HWRM_WOL_FILTER_QCFG                      0xf2UL
@@ -331,6 +336,7 @@ struct cmd_nums {
        #define HWRM_FUNC_VF_BW_CFG                       0x195UL
        #define HWRM_FUNC_VF_BW_QCFG                      0x196UL
        #define HWRM_FUNC_HOST_PF_IDS_QUERY               0x197UL
+       #define HWRM_FUNC_QSTATS_EXT                      0x198UL
        #define HWRM_SELFTEST_QLIST                       0x200UL
        #define HWRM_SELFTEST_EXEC                        0x201UL
        #define HWRM_SELFTEST_IRQ                         0x202UL
@@ -341,6 +347,31 @@ struct cmd_nums {
        #define HWRM_MFG_OTP_CFG                          0x207UL
        #define HWRM_MFG_OTP_QCFG                         0x208UL
        #define HWRM_MFG_HDMA_TEST                        0x209UL
+       #define HWRM_MFG_FRU_EEPROM_WRITE                 0x20aUL
+       #define HWRM_MFG_FRU_EEPROM_READ                  0x20bUL
+       #define HWRM_TF                                   0x2bcUL
+       #define HWRM_TF_VERSION_GET                       0x2bdUL
+       #define HWRM_TF_SESSION_OPEN                      0x2c6UL
+       #define HWRM_TF_SESSION_ATTACH                    0x2c7UL
+       #define HWRM_TF_SESSION_CLOSE                     0x2c8UL
+       #define HWRM_TF_SESSION_QCFG                      0x2c9UL
+       #define HWRM_TF_SESSION_RESC_QCAPS                0x2caUL
+       #define HWRM_TF_SESSION_RESC_ALLOC                0x2cbUL
+       #define HWRM_TF_SESSION_RESC_FREE                 0x2ccUL
+       #define HWRM_TF_SESSION_RESC_FLUSH                0x2cdUL
+       #define HWRM_TF_TBL_TYPE_GET                      0x2d0UL
+       #define HWRM_TF_TBL_TYPE_SET                      0x2d1UL
+       #define HWRM_TF_CTXT_MEM_RGTR                     0x2daUL
+       #define HWRM_TF_CTXT_MEM_UNRGTR                   0x2dbUL
+       #define HWRM_TF_EXT_EM_QCAPS                      0x2dcUL
+       #define HWRM_TF_EXT_EM_OP                         0x2ddUL
+       #define HWRM_TF_EXT_EM_CFG                        0x2deUL
+       #define HWRM_TF_EXT_EM_QCFG                       0x2dfUL
+       #define HWRM_TF_TCAM_SET                          0x2eeUL
+       #define HWRM_TF_TCAM_GET                          0x2efUL
+       #define HWRM_TF_TCAM_MOVE                         0x2f0UL
+       #define HWRM_TF_TCAM_FREE                         0x2f1UL
+       #define HWRM_SV                                   0x400UL
        #define HWRM_DBG_READ_DIRECT                      0xff10UL
        #define HWRM_DBG_READ_INDIRECT                    0xff11UL
        #define HWRM_DBG_WRITE_DIRECT                     0xff12UL
@@ -356,6 +387,10 @@ struct cmd_nums {
        #define HWRM_DBG_RING_INFO_GET                    0xff1cUL
        #define HWRM_DBG_CRASHDUMP_HEADER                 0xff1dUL
        #define HWRM_DBG_CRASHDUMP_ERASE                  0xff1eUL
+       #define HWRM_DBG_DRV_TRACE                        0xff1fUL
+       #define HWRM_DBG_QCAPS                            0xff20UL
+       #define HWRM_DBG_QCFG                             0xff21UL
+       #define HWRM_DBG_CRASHDUMP_MEDIUM_CFG             0xff22UL
        #define HWRM_NVM_FACTORY_DEFAULTS                 0xffeeUL
        #define HWRM_NVM_VALIDATE_OPTION                  0xffefUL
        #define HWRM_NVM_FLUSH                            0xfff0UL
@@ -429,8 +464,8 @@ struct hwrm_err_output {
 #define HWRM_VERSION_MAJOR 1
 #define HWRM_VERSION_MINOR 10
 #define HWRM_VERSION_UPDATE 1
-#define HWRM_VERSION_RSVD 12
-#define HWRM_VERSION_STR "1.10.1.12"
+#define HWRM_VERSION_RSVD 33
+#define HWRM_VERSION_STR "1.10.1.33"
 
 /* hwrm_ver_get_input (size:192b/24B) */
 struct hwrm_ver_get_input {
@@ -482,6 +517,7 @@ struct hwrm_ver_get_output {
        #define VER_GET_RESP_DEV_CAPS_CFG_CFA_EEM_SUPPORTED                        0x800UL
        #define VER_GET_RESP_DEV_CAPS_CFG_CFA_ADV_FLOW_MGNT_SUPPORTED              0x1000UL
        #define VER_GET_RESP_DEV_CAPS_CFG_CFA_TFLIB_SUPPORTED                      0x2000UL
+       #define VER_GET_RESP_DEV_CAPS_CFG_CFA_TRUFLOW_SUPPORTED                    0x4000UL
        u8      roce_fw_maj_8b;
        u8      roce_fw_min_8b;
        u8      roce_fw_bld_8b;
@@ -647,6 +683,7 @@ struct hwrm_async_event_cmpl {
        #define ASYNC_EVENT_CMPL_EVENT_ID_TFLIB_LINK_STATUS_CHANGE   0x3eUL
        #define ASYNC_EVENT_CMPL_EVENT_ID_QUIESCE_DONE               0x3fUL
        #define ASYNC_EVENT_CMPL_EVENT_ID_DEFERRED_RESPONSE          0x40UL
+       #define ASYNC_EVENT_CMPL_EVENT_ID_PFC_WATCHDOG_CFG_CHANGE    0x41UL
        #define ASYNC_EVENT_CMPL_EVENT_ID_FW_TRACE_MSG               0xfeUL
        #define ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR                 0xffUL
        #define ASYNC_EVENT_CMPL_EVENT_ID_LAST                      ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR
@@ -1089,7 +1126,7 @@ struct hwrm_func_qcaps_input {
        u8      unused_0[6];
 };
 
-/* hwrm_func_qcaps_output (size:640b/80B) */
+/* hwrm_func_qcaps_output (size:704b/88B) */
 struct hwrm_func_qcaps_output {
        __le16  error_code;
        __le16  req_type;
@@ -1126,6 +1163,10 @@ struct hwrm_func_qcaps_output {
        #define FUNC_QCAPS_RESP_FLAGS_ERR_RECOVER_RELOAD                    0x2000000UL
        #define FUNC_QCAPS_RESP_FLAGS_NOTIFY_VF_DEF_VNIC_CHNG_SUPPORTED     0x4000000UL
        #define FUNC_QCAPS_RESP_FLAGS_VLAN_ACCELERATION_TX_DISABLED         0x8000000UL
+       #define FUNC_QCAPS_RESP_FLAGS_COREDUMP_CMD_SUPPORTED                0x10000000UL
+       #define FUNC_QCAPS_RESP_FLAGS_CRASHDUMP_CMD_SUPPORTED               0x20000000UL
+       #define FUNC_QCAPS_RESP_FLAGS_PFC_WD_STATS_SUPPORTED                0x40000000UL
+       #define FUNC_QCAPS_RESP_FLAGS_DBG_QCAPS_CMD_SUPPORTED               0x80000000UL
        u8      mac_address[6];
        __le16  max_rsscos_ctx;
        __le16  max_cmpl_rings;
@@ -1146,7 +1187,12 @@ struct hwrm_func_qcaps_output {
        __le32  max_flow_id;
        __le32  max_hw_ring_grps;
        __le16  max_sp_tx_rings;
-       u8      unused_0;
+       u8      unused_0[2];
+       __le32  flags_ext;
+       #define FUNC_QCAPS_RESP_FLAGS_EXT_ECN_MARK_SUPPORTED         0x1UL
+       #define FUNC_QCAPS_RESP_FLAGS_EXT_ECN_STATS_SUPPORTED        0x2UL
+       #define FUNC_QCAPS_RESP_FLAGS_EXT_EXT_HW_STATS_SUPPORTED     0x4UL
+       u8      unused_1[3];
        u8      valid;
 };
 
@@ -1161,7 +1207,7 @@ struct hwrm_func_qcfg_input {
        u8      unused_0[6];
 };
 
-/* hwrm_func_qcfg_output (size:704b/88B) */
+/* hwrm_func_qcfg_output (size:768b/96B) */
 struct hwrm_func_qcfg_output {
        __le16  error_code;
        __le16  req_type;
@@ -1267,7 +1313,11 @@ struct hwrm_func_qcfg_output {
        u8      always_1;
        __le32  reset_addr_poll;
        __le16  legacy_l2_db_size_kb;
-       u8      unused_2[1];
+       __le16  svif_info;
+       #define FUNC_QCFG_RESP_SVIF_INFO_SVIF_MASK      0x7fffUL
+       #define FUNC_QCFG_RESP_SVIF_INFO_SVIF_SFT       0
+       #define FUNC_QCFG_RESP_SVIF_INFO_SVIF_VALID     0x8000UL
+       u8      unused_2[7];
        u8      valid;
 };
 
@@ -1420,9 +1470,10 @@ struct hwrm_func_qstats_input {
        __le64  resp_addr;
        __le16  fid;
        u8      flags;
-       #define FUNC_QSTATS_REQ_FLAGS_UNUSED    0x0UL
-       #define FUNC_QSTATS_REQ_FLAGS_ROCE_ONLY 0x1UL
-       #define FUNC_QSTATS_REQ_FLAGS_LAST     FUNC_QSTATS_REQ_FLAGS_ROCE_ONLY
+       #define FUNC_QSTATS_REQ_FLAGS_UNUSED       0x0UL
+       #define FUNC_QSTATS_REQ_FLAGS_ROCE_ONLY    0x1UL
+       #define FUNC_QSTATS_REQ_FLAGS_COUNTER_MASK 0x2UL
+       #define FUNC_QSTATS_REQ_FLAGS_LAST        FUNC_QSTATS_REQ_FLAGS_COUNTER_MASK
        u8      unused_0[5];
 };
 
@@ -1456,6 +1507,53 @@ struct hwrm_func_qstats_output {
        u8      valid;
 };
 
+/* hwrm_func_qstats_ext_input (size:192b/24B) */
+struct hwrm_func_qstats_ext_input {
+       __le16  req_type;
+       __le16  cmpl_ring;
+       __le16  seq_id;
+       __le16  target_id;
+       __le64  resp_addr;
+       __le16  fid;
+       u8      flags;
+       #define FUNC_QSTATS_EXT_REQ_FLAGS_UNUSED       0x0UL
+       #define FUNC_QSTATS_EXT_REQ_FLAGS_ROCE_ONLY    0x1UL
+       #define FUNC_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK 0x2UL
+       #define FUNC_QSTATS_EXT_REQ_FLAGS_LAST        FUNC_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK
+       u8      unused_0[5];
+};
+
+/* hwrm_func_qstats_ext_output (size:1472b/184B) */
+struct hwrm_func_qstats_ext_output {
+       __le16  error_code;
+       __le16  req_type;
+       __le16  seq_id;
+       __le16  resp_len;
+       __le64  rx_ucast_pkts;
+       __le64  rx_mcast_pkts;
+       __le64  rx_bcast_pkts;
+       __le64  rx_discard_pkts;
+       __le64  rx_drop_pkts;
+       __le64  rx_ucast_bytes;
+       __le64  rx_mcast_bytes;
+       __le64  rx_bcast_bytes;
+       __le64  tx_ucast_pkts;
+       __le64  tx_mcast_pkts;
+       __le64  tx_bcast_pkts;
+       __le64  tx_discard_pkts;
+       __le64  tx_drop_pkts;
+       __le64  tx_ucast_bytes;
+       __le64  tx_mcast_bytes;
+       __le64  tx_bcast_bytes;
+       __le64  rx_tpa_eligible_pkt;
+       __le64  rx_tpa_eligible_bytes;
+       __le64  rx_tpa_pkt;
+       __le64  rx_tpa_bytes;
+       __le64  rx_tpa_errors;
+       u8      unused_0[7];
+       u8      valid;
+};
+
 /* hwrm_func_clr_stats_input (size:192b/24B) */
 struct hwrm_func_clr_stats_input {
        __le16  req_type;
@@ -1808,7 +1906,7 @@ struct hwrm_func_backing_store_qcaps_output {
        u8      ctx_kind_initializer;
        __le32  rsvd;
        __le16  rsvd1;
-       u8      rsvd2;
+       u8      tqm_fp_rings_count;
        u8      valid;
 };
 
@@ -2231,7 +2329,17 @@ struct hwrm_error_recovery_qcfg_output {
        #define ERROR_RECOVERY_QCFG_RESP_RESET_REG_ADDR_SFT           2
        __le32  reset_reg_val[16];
        u8      delay_after_reset[16];
-       u8      unused_1[7];
+       __le32  err_recovery_cnt_reg;
+       #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_MASK    0x3UL
+       #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_SFT     0
+       #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_PCIE_CFG  0x0UL
+       #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_GRC       0x1UL
+       #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_BAR0      0x2UL
+       #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_BAR1      0x3UL
+       #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_LAST     ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SPACE_BAR1
+       #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_MASK          0xfffffffcUL
+       #define ERROR_RECOVERY_QCFG_RESP_ERR_RECOVERY_CNT_REG_ADDR_SFT           2
+       u8      unused_1[3];
        u8      valid;
 };
 
@@ -2934,7 +3042,11 @@ struct hwrm_port_qstats_input {
        __le16  target_id;
        __le64  resp_addr;
        __le16  port_id;
-       u8      unused_0[6];
+       u8      flags;
+       #define PORT_QSTATS_REQ_FLAGS_UNUSED       0x0UL
+       #define PORT_QSTATS_REQ_FLAGS_COUNTER_MASK 0x1UL
+       #define PORT_QSTATS_REQ_FLAGS_LAST        PORT_QSTATS_REQ_FLAGS_COUNTER_MASK
+       u8      unused_0[5];
        __le64  tx_stat_host_addr;
        __le64  rx_stat_host_addr;
 };
@@ -3058,7 +3170,11 @@ struct hwrm_port_qstats_ext_input {
        __le16  port_id;
        __le16  tx_stat_size;
        __le16  rx_stat_size;
-       u8      unused_0[2];
+       u8      flags;
+       #define PORT_QSTATS_EXT_REQ_FLAGS_UNUSED       0x0UL
+       #define PORT_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK 0x1UL
+       #define PORT_QSTATS_EXT_REQ_FLAGS_LAST        PORT_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK
+       u8      unused_0;
        __le64  tx_stat_host_addr;
        __le64  rx_stat_host_addr;
 };
@@ -3840,14 +3956,22 @@ struct hwrm_queue_pfcenable_qcfg_output {
        __le16  seq_id;
        __le16  resp_len;
        __le32  flags;
-       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI0_PFC_ENABLED     0x1UL
-       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI1_PFC_ENABLED     0x2UL
-       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI2_PFC_ENABLED     0x4UL
-       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI3_PFC_ENABLED     0x8UL
-       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI4_PFC_ENABLED     0x10UL
-       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI5_PFC_ENABLED     0x20UL
-       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI6_PFC_ENABLED     0x40UL
-       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI7_PFC_ENABLED     0x80UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI0_PFC_ENABLED              0x1UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI1_PFC_ENABLED              0x2UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI2_PFC_ENABLED              0x4UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI3_PFC_ENABLED              0x8UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI4_PFC_ENABLED              0x10UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI5_PFC_ENABLED              0x20UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI6_PFC_ENABLED              0x40UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI7_PFC_ENABLED              0x80UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI0_PFC_WATCHDOG_ENABLED     0x100UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI1_PFC_WATCHDOG_ENABLED     0x200UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI2_PFC_WATCHDOG_ENABLED     0x400UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI3_PFC_WATCHDOG_ENABLED     0x800UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI4_PFC_WATCHDOG_ENABLED     0x1000UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI5_PFC_WATCHDOG_ENABLED     0x2000UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI6_PFC_WATCHDOG_ENABLED     0x4000UL
+       #define QUEUE_PFCENABLE_QCFG_RESP_FLAGS_PRI7_PFC_WATCHDOG_ENABLED     0x8000UL
        u8      unused_0[3];
        u8      valid;
 };
@@ -3860,14 +3984,22 @@ struct hwrm_queue_pfcenable_cfg_input {
        __le16  target_id;
        __le64  resp_addr;
        __le32  flags;
-       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI0_PFC_ENABLED     0x1UL
-       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI1_PFC_ENABLED     0x2UL
-       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI2_PFC_ENABLED     0x4UL
-       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI3_PFC_ENABLED     0x8UL
-       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI4_PFC_ENABLED     0x10UL
-       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI5_PFC_ENABLED     0x20UL
-       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI6_PFC_ENABLED     0x40UL
-       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI7_PFC_ENABLED     0x80UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI0_PFC_ENABLED              0x1UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI1_PFC_ENABLED              0x2UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI2_PFC_ENABLED              0x4UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI3_PFC_ENABLED              0x8UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI4_PFC_ENABLED              0x10UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI5_PFC_ENABLED              0x20UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI6_PFC_ENABLED              0x40UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI7_PFC_ENABLED              0x80UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI0_PFC_WATCHDOG_ENABLED     0x100UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI1_PFC_WATCHDOG_ENABLED     0x200UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI2_PFC_WATCHDOG_ENABLED     0x400UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI3_PFC_WATCHDOG_ENABLED     0x800UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI4_PFC_WATCHDOG_ENABLED     0x1000UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI5_PFC_WATCHDOG_ENABLED     0x2000UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI6_PFC_WATCHDOG_ENABLED     0x4000UL
+       #define QUEUE_PFCENABLE_CFG_REQ_FLAGS_PRI7_PFC_WATCHDOG_ENABLED     0x8000UL
        __le16  port_id;
        u8      unused_0[2];
 };
@@ -5287,7 +5419,11 @@ struct hwrm_ring_cmpl_ring_qaggint_params_input {
        __le16  target_id;
        __le64  resp_addr;
        __le16  ring_id;
-       u8      unused_0[6];
+       __le16  flags;
+       #define RING_CMPL_RING_QAGGINT_PARAMS_REQ_FLAGS_UNUSED_0_MASK 0x3UL
+       #define RING_CMPL_RING_QAGGINT_PARAMS_REQ_FLAGS_UNUSED_0_SFT 0
+       #define RING_CMPL_RING_QAGGINT_PARAMS_REQ_FLAGS_IS_NQ        0x4UL
+       u8      unused_0[4];
 };
 
 /* hwrm_ring_cmpl_ring_qaggint_params_output (size:256b/32B) */
@@ -7618,7 +7754,9 @@ struct hwrm_nvm_modify_input {
        __le64  resp_addr;
        __le64  host_src_addr;
        __le16  dir_idx;
-       u8      unused_0[2];
+       __le16  flags;
+       #define NVM_MODIFY_REQ_FLAGS_BATCH_MODE     0x1UL
+       #define NVM_MODIFY_REQ_FLAGS_BATCH_LAST     0x2UL
        __le32  offset;
        __le32  len;
        u8      unused_1[4];
@@ -8027,4 +8165,18 @@ struct hwrm_selftest_irq_output {
        u8      valid;
 };
 
+/* fw_status_reg (size:32b/4B) */
+struct fw_status_reg {
+       u32     fw_status;
+       #define FW_STATUS_REG_CODE_MASK              0xffffUL
+       #define FW_STATUS_REG_CODE_SFT               0
+       #define FW_STATUS_REG_CODE_READY               0x8000UL
+       #define FW_STATUS_REG_CODE_LAST               FW_STATUS_REG_CODE_READY
+       #define FW_STATUS_REG_IMAGE_DEGRADED         0x10000UL
+       #define FW_STATUS_REG_RECOVERABLE            0x20000UL
+       #define FW_STATUS_REG_CRASHDUMP_ONGOING      0x40000UL
+       #define FW_STATUS_REG_CRASHDUMP_COMPLETE     0x80000UL
+       #define FW_STATUS_REG_SHUTDOWN               0x100000UL
+};
+
 #endif /* _BNXT_HSI_H_ */
index 6ea3df6..3a9a51f 100644 (file)
@@ -85,11 +85,10 @@ int bnxt_set_vf_spoofchk(struct net_device *dev, int vf_id, bool setting)
        if (old_setting == setting)
                return 0;
 
-       func_flags = vf->func_flags;
        if (setting)
-               func_flags |= FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK_ENABLE;
+               func_flags = FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK_ENABLE;
        else
-               func_flags |= FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK_DISABLE;
+               func_flags = FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK_DISABLE;
        /*TODO: if the driver supports VLAN filter on guest VLAN,
         * the spoof check should also include vlan anti-spoofing
         */
@@ -98,7 +97,6 @@ int bnxt_set_vf_spoofchk(struct net_device *dev, int vf_id, bool setting)
        req.flags = cpu_to_le32(func_flags);
        rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
        if (!rc) {
-               vf->func_flags = func_flags;
                if (setting)
                        vf->flags |= BNXT_VF_SPOOFCHK;
                else
@@ -228,7 +226,6 @@ int bnxt_set_vf_mac(struct net_device *dev, int vf_id, u8 *mac)
        memcpy(vf->mac_addr, mac, ETH_ALEN);
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
        req.fid = cpu_to_le16(vf->fw_fid);
-       req.flags = cpu_to_le32(vf->func_flags);
        req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_DFLT_MAC_ADDR);
        memcpy(req.dflt_mac_addr, mac, ETH_ALEN);
        return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
@@ -266,7 +263,6 @@ int bnxt_set_vf_vlan(struct net_device *dev, int vf_id, u16 vlan_id, u8 qos,
 
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
        req.fid = cpu_to_le16(vf->fw_fid);
-       req.flags = cpu_to_le32(vf->func_flags);
        req.dflt_vlan = cpu_to_le16(vlan_tag);
        req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_DFLT_VLAN);
        rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
@@ -305,7 +301,6 @@ int bnxt_set_vf_bw(struct net_device *dev, int vf_id, int min_tx_rate,
                return 0;
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
        req.fid = cpu_to_le16(vf->fw_fid);
-       req.flags = cpu_to_le32(vf->func_flags);
        req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_MAX_BW);
        req.max_bw = cpu_to_le32(max_tx_rate);
        req.enables |= cpu_to_le32(FUNC_CFG_REQ_ENABLES_MIN_BW);
@@ -477,7 +472,6 @@ static void __bnxt_set_vf_params(struct bnxt *bp, int vf_id)
        vf = &bp->pf.vf[vf_id];
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1);
        req.fid = cpu_to_le16(vf->fw_fid);
-       req.flags = cpu_to_le32(vf->func_flags);
 
        if (is_valid_ether_addr(vf->mac_addr)) {
                req.enables |= cpu_to_le32(FUNC_CFG_REQ_ENABLES_DFLT_MAC_ADDR);
@@ -651,7 +645,7 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs)
                                  FUNC_CFG_REQ_ENABLES_NUM_VNICS |
                                  FUNC_CFG_REQ_ENABLES_NUM_HW_RING_GRPS);
 
-       mtu = bp->dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
+       mtu = bp->dev->mtu + ETH_HLEN + VLAN_HLEN;
        req.mru = cpu_to_le16(mtu);
        req.mtu = cpu_to_le16(mtu);
 
index 4a316c4..8c8368c 100644 (file)
@@ -104,7 +104,13 @@ static void bnxt_fill_msix_vecs(struct bnxt *bp, struct bnxt_msix_entry *ent)
        for (i = 0; i < num_msix; i++) {
                ent[i].vector = bp->irq_tbl[idx + i].vector;
                ent[i].ring_idx = idx + i;
-               ent[i].db_offset = (idx + i) * 0x80;
+               if (bp->flags & BNXT_FLAG_CHIP_P5) {
+                       ent[i].db_offset = DB_PF_OFFSET_P5;
+                       if (BNXT_VF(bp))
+                               ent[i].db_offset = DB_VF_OFFSET_P5;
+               } else {
+                       ent[i].db_offset = (idx + i) * 0x80;
+               }
        }
 }
 
@@ -475,6 +481,8 @@ struct bnxt_en_dev *bnxt_ulp_probe(struct net_device *dev)
                        edev->flags |= BNXT_EN_FLAG_ROCEV2_CAP;
                edev->net = dev;
                edev->pdev = bp->pdev;
+               edev->l2_db_size = bp->db_size;
+               edev->l2_db_size_nc = bp->db_size;
                bp->edev = edev;
        }
        return bp->edev;
index 9895406..6b4d255 100644 (file)
@@ -67,6 +67,14 @@ struct bnxt_en_dev {
        #define BNXT_EN_FLAG_ULP_STOPPED        0x8
        const struct bnxt_en_ops        *en_ops;
        struct bnxt_ulp                 ulp_tbl[BNXT_MAX_ULP];
+       int                             l2_db_size;     /* Doorbell BAR size in
+                                                        * bytes mapped by L2
+                                                        * driver.
+                                                        */
+       int                             l2_db_size_nc;  /* Doorbell BAR size in
+                                                        * bytes mapped as non-
+                                                        * cacheable.
+                                                        */
 };
 
 struct bnxt_en_ops {
index d975338..ff31da0 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Broadcom GENET (Gigabit Ethernet) controller driver
  *
- * Copyright (c) 2014-2019 Broadcom
+ * Copyright (c) 2014-2020 Broadcom
  */
 
 #define pr_fmt(fmt)                            "bcmgenet: " fmt
 #include <linux/dma-mapping.h>
 #include <linux/pm.h>
 #include <linux/clk.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_net.h>
-#include <linux/of_platform.h>
 #include <net/arp.h>
 
 #include <linux/mii.h>
@@ -70,6 +65,9 @@
 #define GENET_RDMA_REG_OFF     (priv->hw_params->rdma_offset + \
                                TOTAL_DESC * DMA_DESC_SIZE)
 
+/* Forward declarations */
+static void bcmgenet_set_rx_mode(struct net_device *dev);
+
 static inline void bcmgenet_writel(u32 value, void __iomem *offset)
 {
        /* MIPS chips strapped for BE will automagically configure the
@@ -461,6 +459,384 @@ static inline void bcmgenet_rdma_ring_writel(struct bcmgenet_priv *priv,
                        genet_dma_ring_regs[r]);
 }
 
+static bool bcmgenet_hfb_is_filter_enabled(struct bcmgenet_priv *priv,
+                                          u32 f_index)
+{
+       u32 offset;
+       u32 reg;
+
+       offset = HFB_FLT_ENABLE_V3PLUS + (f_index < 32) * sizeof(u32);
+       reg = bcmgenet_hfb_reg_readl(priv, offset);
+       return !!(reg & (1 << (f_index % 32)));
+}
+
+static void bcmgenet_hfb_enable_filter(struct bcmgenet_priv *priv, u32 f_index)
+{
+       u32 offset;
+       u32 reg;
+
+       offset = HFB_FLT_ENABLE_V3PLUS + (f_index < 32) * sizeof(u32);
+       reg = bcmgenet_hfb_reg_readl(priv, offset);
+       reg |= (1 << (f_index % 32));
+       bcmgenet_hfb_reg_writel(priv, reg, offset);
+       reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
+       reg |= RBUF_HFB_EN;
+       bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
+}
+
+static void bcmgenet_hfb_disable_filter(struct bcmgenet_priv *priv, u32 f_index)
+{
+       u32 offset, reg, reg1;
+
+       offset = HFB_FLT_ENABLE_V3PLUS;
+       reg = bcmgenet_hfb_reg_readl(priv, offset);
+       reg1 = bcmgenet_hfb_reg_readl(priv, offset + sizeof(u32));
+       if  (f_index < 32) {
+               reg1 &= ~(1 << (f_index % 32));
+               bcmgenet_hfb_reg_writel(priv, reg1, offset + sizeof(u32));
+       } else {
+               reg &= ~(1 << (f_index % 32));
+               bcmgenet_hfb_reg_writel(priv, reg, offset);
+       }
+       if (!reg && !reg1) {
+               reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
+               reg &= ~RBUF_HFB_EN;
+               bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
+       }
+}
+
+static void bcmgenet_hfb_set_filter_rx_queue_mapping(struct bcmgenet_priv *priv,
+                                                    u32 f_index, u32 rx_queue)
+{
+       u32 offset;
+       u32 reg;
+
+       offset = f_index / 8;
+       reg = bcmgenet_rdma_readl(priv, DMA_INDEX2RING_0 + offset);
+       reg &= ~(0xF << (4 * (f_index % 8)));
+       reg |= ((rx_queue & 0xF) << (4 * (f_index % 8)));
+       bcmgenet_rdma_writel(priv, reg, DMA_INDEX2RING_0 + offset);
+}
+
+static void bcmgenet_hfb_set_filter_length(struct bcmgenet_priv *priv,
+                                          u32 f_index, u32 f_length)
+{
+       u32 offset;
+       u32 reg;
+
+       offset = HFB_FLT_LEN_V3PLUS +
+                ((priv->hw_params->hfb_filter_cnt - 1 - f_index) / 4) *
+                sizeof(u32);
+       reg = bcmgenet_hfb_reg_readl(priv, offset);
+       reg &= ~(0xFF << (8 * (f_index % 4)));
+       reg |= ((f_length & 0xFF) << (8 * (f_index % 4)));
+       bcmgenet_hfb_reg_writel(priv, reg, offset);
+}
+
+static int bcmgenet_hfb_find_unused_filter(struct bcmgenet_priv *priv)
+{
+       u32 f_index;
+
+       /* First MAX_NUM_OF_FS_RULES are reserved for Rx NFC filters */
+       for (f_index = MAX_NUM_OF_FS_RULES;
+            f_index < priv->hw_params->hfb_filter_cnt; f_index++)
+               if (!bcmgenet_hfb_is_filter_enabled(priv, f_index))
+                       return f_index;
+
+       return -ENOMEM;
+}
+
+static int bcmgenet_hfb_validate_mask(void *mask, size_t size)
+{
+       while (size) {
+               switch (*(unsigned char *)mask++) {
+               case 0x00:
+               case 0x0f:
+               case 0xf0:
+               case 0xff:
+                       size--;
+                       continue;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+#define VALIDATE_MASK(x) \
+       bcmgenet_hfb_validate_mask(&(x), sizeof(x))
+
+static int bcmgenet_hfb_insert_data(u32 *f, int offset,
+                                   void *val, void *mask, size_t size)
+{
+       int index;
+       u32 tmp;
+
+       index = offset / 2;
+       tmp = f[index];
+
+       while (size--) {
+               if (offset++ & 1) {
+                       tmp &= ~0x300FF;
+                       tmp |= (*(unsigned char *)val++);
+                       switch ((*(unsigned char *)mask++)) {
+                       case 0xFF:
+                               tmp |= 0x30000;
+                               break;
+                       case 0xF0:
+                               tmp |= 0x20000;
+                               break;
+                       case 0x0F:
+                               tmp |= 0x10000;
+                               break;
+                       }
+                       f[index++] = tmp;
+                       if (size)
+                               tmp = f[index];
+               } else {
+                       tmp &= ~0xCFF00;
+                       tmp |= (*(unsigned char *)val++) << 8;
+                       switch ((*(unsigned char *)mask++)) {
+                       case 0xFF:
+                               tmp |= 0xC0000;
+                               break;
+                       case 0xF0:
+                               tmp |= 0x80000;
+                               break;
+                       case 0x0F:
+                               tmp |= 0x40000;
+                               break;
+                       }
+                       if (!size)
+                               f[index] = tmp;
+               }
+       }
+
+       return 0;
+}
+
+static void bcmgenet_hfb_set_filter(struct bcmgenet_priv *priv, u32 *f_data,
+                                   u32 f_length, u32 rx_queue, int f_index)
+{
+       u32 base = f_index * priv->hw_params->hfb_filter_size;
+       int i;
+
+       for (i = 0; i < f_length; i++)
+               bcmgenet_hfb_writel(priv, f_data[i], (base + i) * sizeof(u32));
+
+       bcmgenet_hfb_set_filter_length(priv, f_index, 2 * f_length);
+       bcmgenet_hfb_set_filter_rx_queue_mapping(priv, f_index, rx_queue);
+}
+
+static int bcmgenet_hfb_create_rxnfc_filter(struct bcmgenet_priv *priv,
+                                           struct bcmgenet_rxnfc_rule *rule)
+{
+       struct ethtool_rx_flow_spec *fs = &rule->fs;
+       int err = 0, offset = 0, f_length = 0;
+       u16 val_16, mask_16;
+       u8 val_8, mask_8;
+       size_t size;
+       u32 *f_data;
+
+       f_data = kcalloc(priv->hw_params->hfb_filter_size, sizeof(u32),
+                        GFP_KERNEL);
+       if (!f_data)
+               return -ENOMEM;
+
+       if (fs->flow_type & FLOW_MAC_EXT) {
+               bcmgenet_hfb_insert_data(f_data, 0,
+                                        &fs->h_ext.h_dest, &fs->m_ext.h_dest,
+                                        sizeof(fs->h_ext.h_dest));
+       }
+
+       if (fs->flow_type & FLOW_EXT) {
+               if (fs->m_ext.vlan_etype ||
+                   fs->m_ext.vlan_tci) {
+                       bcmgenet_hfb_insert_data(f_data, 12,
+                                                &fs->h_ext.vlan_etype,
+                                                &fs->m_ext.vlan_etype,
+                                                sizeof(fs->h_ext.vlan_etype));
+                       bcmgenet_hfb_insert_data(f_data, 14,
+                                                &fs->h_ext.vlan_tci,
+                                                &fs->m_ext.vlan_tci,
+                                                sizeof(fs->h_ext.vlan_tci));
+                       offset += VLAN_HLEN;
+                       f_length += DIV_ROUND_UP(VLAN_HLEN, 2);
+               }
+       }
+
+       switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
+       case ETHER_FLOW:
+               f_length += DIV_ROUND_UP(ETH_HLEN, 2);
+               bcmgenet_hfb_insert_data(f_data, 0,
+                                        &fs->h_u.ether_spec.h_dest,
+                                        &fs->m_u.ether_spec.h_dest,
+                                        sizeof(fs->h_u.ether_spec.h_dest));
+               bcmgenet_hfb_insert_data(f_data, ETH_ALEN,
+                                        &fs->h_u.ether_spec.h_source,
+                                        &fs->m_u.ether_spec.h_source,
+                                        sizeof(fs->h_u.ether_spec.h_source));
+               bcmgenet_hfb_insert_data(f_data, (2 * ETH_ALEN) + offset,
+                                        &fs->h_u.ether_spec.h_proto,
+                                        &fs->m_u.ether_spec.h_proto,
+                                        sizeof(fs->h_u.ether_spec.h_proto));
+               break;
+       case IP_USER_FLOW:
+               f_length += DIV_ROUND_UP(ETH_HLEN + 20, 2);
+               /* Specify IP Ether Type */
+               val_16 = htons(ETH_P_IP);
+               mask_16 = 0xFFFF;
+               bcmgenet_hfb_insert_data(f_data, (2 * ETH_ALEN) + offset,
+                                        &val_16, &mask_16, sizeof(val_16));
+               bcmgenet_hfb_insert_data(f_data, 15 + offset,
+                                        &fs->h_u.usr_ip4_spec.tos,
+                                        &fs->m_u.usr_ip4_spec.tos,
+                                        sizeof(fs->h_u.usr_ip4_spec.tos));
+               bcmgenet_hfb_insert_data(f_data, 23 + offset,
+                                        &fs->h_u.usr_ip4_spec.proto,
+                                        &fs->m_u.usr_ip4_spec.proto,
+                                        sizeof(fs->h_u.usr_ip4_spec.proto));
+               bcmgenet_hfb_insert_data(f_data, 26 + offset,
+                                        &fs->h_u.usr_ip4_spec.ip4src,
+                                        &fs->m_u.usr_ip4_spec.ip4src,
+                                        sizeof(fs->h_u.usr_ip4_spec.ip4src));
+               bcmgenet_hfb_insert_data(f_data, 30 + offset,
+                                        &fs->h_u.usr_ip4_spec.ip4dst,
+                                        &fs->m_u.usr_ip4_spec.ip4dst,
+                                        sizeof(fs->h_u.usr_ip4_spec.ip4dst));
+               if (!fs->m_u.usr_ip4_spec.l4_4_bytes)
+                       break;
+
+               /* Only supports 20 byte IPv4 header */
+               val_8 = 0x45;
+               mask_8 = 0xFF;
+               bcmgenet_hfb_insert_data(f_data, ETH_HLEN + offset,
+                                        &val_8, &mask_8,
+                                        sizeof(val_8));
+               size = sizeof(fs->h_u.usr_ip4_spec.l4_4_bytes);
+               bcmgenet_hfb_insert_data(f_data,
+                                        ETH_HLEN + 20 + offset,
+                                        &fs->h_u.usr_ip4_spec.l4_4_bytes,
+                                        &fs->m_u.usr_ip4_spec.l4_4_bytes,
+                                        size);
+               f_length += DIV_ROUND_UP(size, 2);
+               break;
+       }
+
+       if (!fs->ring_cookie || fs->ring_cookie == RX_CLS_FLOW_WAKE) {
+               /* Ring 0 flows can be handled by the default Descriptor Ring
+                * We'll map them to ring 0, but don't enable the filter
+                */
+               bcmgenet_hfb_set_filter(priv, f_data, f_length, 0,
+                                       fs->location);
+               rule->state = BCMGENET_RXNFC_STATE_DISABLED;
+       } else {
+               /* Other Rx rings are direct mapped here */
+               bcmgenet_hfb_set_filter(priv, f_data, f_length,
+                                       fs->ring_cookie, fs->location);
+               bcmgenet_hfb_enable_filter(priv, fs->location);
+               rule->state = BCMGENET_RXNFC_STATE_ENABLED;
+       }
+
+       kfree(f_data);
+
+       return err;
+}
+
+/* bcmgenet_hfb_add_filter
+ *
+ * Add new filter to Hardware Filter Block to match and direct Rx traffic to
+ * desired Rx queue.
+ *
+ * f_data is an array of unsigned 32-bit integers where each 32-bit integer
+ * provides filter data for 2 bytes (4 nibbles) of Rx frame:
+ *
+ * bits 31:20 - unused
+ * bit  19    - nibble 0 match enable
+ * bit  18    - nibble 1 match enable
+ * bit  17    - nibble 2 match enable
+ * bit  16    - nibble 3 match enable
+ * bits 15:12 - nibble 0 data
+ * bits 11:8  - nibble 1 data
+ * bits 7:4   - nibble 2 data
+ * bits 3:0   - nibble 3 data
+ *
+ * Example:
+ * In order to match:
+ * - Ethernet frame type = 0x0800 (IP)
+ * - IP version field = 4
+ * - IP protocol field = 0x11 (UDP)
+ *
+ * The following filter is needed:
+ * u32 hfb_filter_ipv4_udp[] = {
+ *   Rx frame offset 0x00: 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ *   Rx frame offset 0x08: 0x00000000, 0x00000000, 0x000F0800, 0x00084000,
+ *   Rx frame offset 0x10: 0x00000000, 0x00000000, 0x00000000, 0x00030011,
+ * };
+ *
+ * To add the filter to HFB and direct the traffic to Rx queue 0, call:
+ * bcmgenet_hfb_add_filter(priv, hfb_filter_ipv4_udp,
+ *                         ARRAY_SIZE(hfb_filter_ipv4_udp), 0);
+ */
+int bcmgenet_hfb_add_filter(struct bcmgenet_priv *priv, u32 *f_data,
+                           u32 f_length, u32 rx_queue)
+{
+       int f_index;
+
+       f_index = bcmgenet_hfb_find_unused_filter(priv);
+       if (f_index < 0)
+               return -ENOMEM;
+
+       if (f_length > priv->hw_params->hfb_filter_size)
+               return -EINVAL;
+
+       bcmgenet_hfb_set_filter(priv, f_data, f_length, rx_queue, f_index);
+       bcmgenet_hfb_enable_filter(priv, f_index);
+
+       return 0;
+}
+
+/* bcmgenet_hfb_clear
+ *
+ * Clear Hardware Filter Block and disable all filtering.
+ */
+static void bcmgenet_hfb_clear(struct bcmgenet_priv *priv)
+{
+       u32 i;
+
+       bcmgenet_hfb_reg_writel(priv, 0x0, HFB_CTRL);
+       bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS);
+       bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS + 4);
+
+       for (i = DMA_INDEX2RING_0; i <= DMA_INDEX2RING_7; i++)
+               bcmgenet_rdma_writel(priv, 0x0, i);
+
+       for (i = 0; i < (priv->hw_params->hfb_filter_cnt / 4); i++)
+               bcmgenet_hfb_reg_writel(priv, 0x0,
+                                       HFB_FLT_LEN_V3PLUS + i * sizeof(u32));
+
+       for (i = 0; i < priv->hw_params->hfb_filter_cnt *
+                       priv->hw_params->hfb_filter_size; i++)
+               bcmgenet_hfb_writel(priv, 0x0, i * sizeof(u32));
+}
+
+static void bcmgenet_hfb_init(struct bcmgenet_priv *priv)
+{
+       int i;
+
+       if (GENET_IS_V1(priv) || GENET_IS_V2(priv))
+               return;
+
+       INIT_LIST_HEAD(&priv->rxnfc_list);
+       for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) {
+               INIT_LIST_HEAD(&priv->rxnfc_rules[i].list);
+               priv->rxnfc_rules[i].state = BCMGENET_RXNFC_STATE_UNUSED;
+       }
+
+       bcmgenet_hfb_clear(priv);
+}
+
 static int bcmgenet_begin(struct net_device *dev)
 {
        struct bcmgenet_priv *priv = netdev_priv(dev);
@@ -934,6 +1310,8 @@ static void bcmgenet_get_ethtool_stats(struct net_device *dev,
        if (netif_running(dev))
                bcmgenet_update_mib_counters(priv);
 
+       dev->netdev_ops->ndo_get_stats(dev);
+
        for (i = 0; i < BCMGENET_STATS_LEN; i++) {
                const struct bcmgenet_stats *s;
                char *p;
@@ -1043,6 +1421,229 @@ static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e)
        return phy_ethtool_set_eee(dev->phydev, e);
 }
 
+static int bcmgenet_validate_flow(struct net_device *dev,
+                                 struct ethtool_rxnfc *cmd)
+{
+       struct ethtool_usrip4_spec *l4_mask;
+       struct ethhdr *eth_mask;
+
+       if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) {
+               netdev_err(dev, "rxnfc: Invalid location (%d)\n",
+                          cmd->fs.location);
+               return -EINVAL;
+       }
+
+       switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
+       case IP_USER_FLOW:
+               l4_mask = &cmd->fs.m_u.usr_ip4_spec;
+               /* don't allow mask which isn't valid */
+               if (VALIDATE_MASK(l4_mask->ip4src) ||
+                   VALIDATE_MASK(l4_mask->ip4dst) ||
+                   VALIDATE_MASK(l4_mask->l4_4_bytes) ||
+                   VALIDATE_MASK(l4_mask->proto) ||
+                   VALIDATE_MASK(l4_mask->ip_ver) ||
+                   VALIDATE_MASK(l4_mask->tos)) {
+                       netdev_err(dev, "rxnfc: Unsupported mask\n");
+                       return -EINVAL;
+               }
+               break;
+       case ETHER_FLOW:
+               eth_mask = &cmd->fs.m_u.ether_spec;
+               /* don't allow mask which isn't valid */
+               if (VALIDATE_MASK(eth_mask->h_source) ||
+                   VALIDATE_MASK(eth_mask->h_source) ||
+                   VALIDATE_MASK(eth_mask->h_proto)) {
+                       netdev_err(dev, "rxnfc: Unsupported mask\n");
+                       return -EINVAL;
+               }
+               break;
+       default:
+               netdev_err(dev, "rxnfc: Unsupported flow type (0x%x)\n",
+                          cmd->fs.flow_type);
+               return -EINVAL;
+       }
+
+       if ((cmd->fs.flow_type & FLOW_EXT)) {
+               /* don't allow mask which isn't valid */
+               if (VALIDATE_MASK(cmd->fs.m_ext.vlan_etype) ||
+                   VALIDATE_MASK(cmd->fs.m_ext.vlan_tci)) {
+                       netdev_err(dev, "rxnfc: Unsupported mask\n");
+                       return -EINVAL;
+               }
+               if (cmd->fs.m_ext.data[0] || cmd->fs.m_ext.data[1]) {
+                       netdev_err(dev, "rxnfc: user-def not supported\n");
+                       return -EINVAL;
+               }
+       }
+
+       if ((cmd->fs.flow_type & FLOW_MAC_EXT)) {
+               /* don't allow mask which isn't valid */
+               if (VALIDATE_MASK(cmd->fs.m_ext.h_dest)) {
+                       netdev_err(dev, "rxnfc: Unsupported mask\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int bcmgenet_insert_flow(struct net_device *dev,
+                               struct ethtool_rxnfc *cmd)
+{
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       struct bcmgenet_rxnfc_rule *loc_rule;
+       int err;
+
+       if (priv->hw_params->hfb_filter_size < 128) {
+               netdev_err(dev, "rxnfc: Not supported by this device\n");
+               return -EINVAL;
+       }
+
+       if (cmd->fs.ring_cookie > priv->hw_params->rx_queues &&
+           cmd->fs.ring_cookie != RX_CLS_FLOW_WAKE) {
+               netdev_err(dev, "rxnfc: Unsupported action (%llu)\n",
+                          cmd->fs.ring_cookie);
+               return -EINVAL;
+       }
+
+       err = bcmgenet_validate_flow(dev, cmd);
+       if (err)
+               return err;
+
+       loc_rule = &priv->rxnfc_rules[cmd->fs.location];
+       if (loc_rule->state == BCMGENET_RXNFC_STATE_ENABLED)
+               bcmgenet_hfb_disable_filter(priv, cmd->fs.location);
+       if (loc_rule->state != BCMGENET_RXNFC_STATE_UNUSED)
+               list_del(&loc_rule->list);
+       loc_rule->state = BCMGENET_RXNFC_STATE_UNUSED;
+       memcpy(&loc_rule->fs, &cmd->fs,
+              sizeof(struct ethtool_rx_flow_spec));
+
+       err = bcmgenet_hfb_create_rxnfc_filter(priv, loc_rule);
+       if (err) {
+               netdev_err(dev, "rxnfc: Could not install rule (%d)\n",
+                          err);
+               return err;
+       }
+
+       list_add_tail(&loc_rule->list, &priv->rxnfc_list);
+
+       return 0;
+}
+
+static int bcmgenet_delete_flow(struct net_device *dev,
+                               struct ethtool_rxnfc *cmd)
+{
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       struct bcmgenet_rxnfc_rule *rule;
+       int err = 0;
+
+       if (cmd->fs.location >= MAX_NUM_OF_FS_RULES)
+               return -EINVAL;
+
+       rule = &priv->rxnfc_rules[cmd->fs.location];
+       if (rule->state == BCMGENET_RXNFC_STATE_UNUSED) {
+               err =  -ENOENT;
+               goto out;
+       }
+
+       if (rule->state == BCMGENET_RXNFC_STATE_ENABLED)
+               bcmgenet_hfb_disable_filter(priv, cmd->fs.location);
+       if (rule->state != BCMGENET_RXNFC_STATE_UNUSED)
+               list_del(&rule->list);
+       rule->state = BCMGENET_RXNFC_STATE_UNUSED;
+       memset(&rule->fs, 0, sizeof(struct ethtool_rx_flow_spec));
+
+out:
+       return err;
+}
+
+static int bcmgenet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
+{
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       int err = 0;
+
+       switch (cmd->cmd) {
+       case ETHTOOL_SRXCLSRLINS:
+               err = bcmgenet_insert_flow(dev, cmd);
+               break;
+       case ETHTOOL_SRXCLSRLDEL:
+               err = bcmgenet_delete_flow(dev, cmd);
+               break;
+       default:
+               netdev_warn(priv->dev, "Unsupported ethtool command. (%d)\n",
+                           cmd->cmd);
+               return -EINVAL;
+       }
+
+       return err;
+}
+
+static int bcmgenet_get_flow(struct net_device *dev, struct ethtool_rxnfc *cmd,
+                            int loc)
+{
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       struct bcmgenet_rxnfc_rule *rule;
+       int err = 0;
+
+       if (loc < 0 || loc >= MAX_NUM_OF_FS_RULES)
+               return -EINVAL;
+
+       rule = &priv->rxnfc_rules[loc];
+       if (rule->state == BCMGENET_RXNFC_STATE_UNUSED)
+               err = -ENOENT;
+       else
+               memcpy(&cmd->fs, &rule->fs,
+                      sizeof(struct ethtool_rx_flow_spec));
+
+       return err;
+}
+
+static int bcmgenet_get_num_flows(struct bcmgenet_priv *priv)
+{
+       struct list_head *pos;
+       int res = 0;
+
+       list_for_each(pos, &priv->rxnfc_list)
+               res++;
+
+       return res;
+}
+
+static int bcmgenet_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
+                             u32 *rule_locs)
+{
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       struct bcmgenet_rxnfc_rule *rule;
+       int err = 0;
+       int i = 0;
+
+       switch (cmd->cmd) {
+       case ETHTOOL_GRXRINGS:
+               cmd->data = priv->hw_params->rx_queues ?: 1;
+               break;
+       case ETHTOOL_GRXCLSRLCNT:
+               cmd->rule_cnt = bcmgenet_get_num_flows(priv);
+               cmd->data = MAX_NUM_OF_FS_RULES;
+               break;
+       case ETHTOOL_GRXCLSRULE:
+               err = bcmgenet_get_flow(dev, cmd, cmd->fs.location);
+               break;
+       case ETHTOOL_GRXCLSRLALL:
+               list_for_each_entry(rule, &priv->rxnfc_list, list)
+                       if (i < cmd->rule_cnt)
+                               rule_locs[i++] = rule->fs.location;
+               cmd->rule_cnt = i;
+               cmd->data = MAX_NUM_OF_FS_RULES;
+               break;
+       default:
+               err = -EOPNOTSUPP;
+               break;
+       }
+
+       return err;
+}
+
 /* standard ethtool support functions. */
 static const struct ethtool_ops bcmgenet_ethtool_ops = {
        .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
@@ -1067,6 +1668,8 @@ static const struct ethtool_ops bcmgenet_ethtool_ops = {
        .get_link_ksettings     = bcmgenet_get_link_ksettings,
        .set_link_ksettings     = bcmgenet_set_link_ksettings,
        .get_ts_info            = ethtool_op_get_ts_info,
+       .get_rxnfc              = bcmgenet_get_rxnfc,
+       .set_rxnfc              = bcmgenet_set_rxnfc,
 };
 
 /* Power down the unimac, based on mode. */
@@ -1622,7 +2225,8 @@ static struct sk_buff *bcmgenet_rx_refill(struct bcmgenet_priv *priv,
        dma_addr_t mapping;
 
        /* Allocate a new Rx skb */
-       skb = netdev_alloc_skb(priv->dev, priv->rx_buf_len + SKB_ALIGNMENT);
+       skb = __netdev_alloc_skb(priv->dev, priv->rx_buf_len + SKB_ALIGNMENT,
+                                GFP_ATOMIC | __GFP_NOWARN);
        if (!skb) {
                priv->mib.alloc_rx_buff_failed++;
                netif_err(priv, rx_err, priv->dev,
@@ -2666,10 +3270,7 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id)
 
 static irqreturn_t bcmgenet_wol_isr(int irq, void *dev_id)
 {
-       struct bcmgenet_priv *priv = dev_id;
-
-       pm_wakeup_event(&priv->pdev->dev, 0);
-
+       /* Acknowledge the interrupt */
        return IRQ_HANDLED;
 }
 
@@ -2707,9 +3308,8 @@ static void bcmgenet_umac_reset(struct bcmgenet_priv *priv)
 static void bcmgenet_set_hw_addr(struct bcmgenet_priv *priv,
                                 unsigned char *addr)
 {
-       bcmgenet_umac_writel(priv, (addr[0] << 24) | (addr[1] << 16) |
-                       (addr[2] << 8) | addr[3], UMAC_MAC0);
-       bcmgenet_umac_writel(priv, (addr[4] << 8) | addr[5], UMAC_MAC1);
+       bcmgenet_umac_writel(priv, get_unaligned_be32(&addr[0]), UMAC_MAC0);
+       bcmgenet_umac_writel(priv, get_unaligned_be16(&addr[4]), UMAC_MAC1);
 }
 
 static void bcmgenet_get_hw_addr(struct bcmgenet_priv *priv,
@@ -2718,13 +3318,9 @@ static void bcmgenet_get_hw_addr(struct bcmgenet_priv *priv,
        u32 addr_tmp;
 
        addr_tmp = bcmgenet_umac_readl(priv, UMAC_MAC0);
-       addr[0] = addr_tmp >> 24;
-       addr[1] = (addr_tmp >> 16) & 0xff;
-       addr[2] = (addr_tmp >>  8) & 0xff;
-       addr[3] = addr_tmp & 0xff;
+       put_unaligned_be32(addr_tmp, &addr[0]);
        addr_tmp = bcmgenet_umac_readl(priv, UMAC_MAC1);
-       addr[4] = (addr_tmp >> 8) & 0xff;
-       addr[5] = addr_tmp & 0xff;
+       put_unaligned_be16(addr_tmp, &addr[4]);
 }
 
 /* Returns a reusable dma control register value */
@@ -2763,43 +3359,12 @@ static void bcmgenet_enable_dma(struct bcmgenet_priv *priv, u32 dma_ctrl)
        bcmgenet_tdma_writel(priv, reg, DMA_CTRL);
 }
 
-/* bcmgenet_hfb_clear
- *
- * Clear Hardware Filter Block and disable all filtering.
- */
-static void bcmgenet_hfb_clear(struct bcmgenet_priv *priv)
-{
-       u32 i;
-
-       bcmgenet_hfb_reg_writel(priv, 0x0, HFB_CTRL);
-       bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS);
-       bcmgenet_hfb_reg_writel(priv, 0x0, HFB_FLT_ENABLE_V3PLUS + 4);
-
-       for (i = DMA_INDEX2RING_0; i <= DMA_INDEX2RING_7; i++)
-               bcmgenet_rdma_writel(priv, 0x0, i);
-
-       for (i = 0; i < (priv->hw_params->hfb_filter_cnt / 4); i++)
-               bcmgenet_hfb_reg_writel(priv, 0x0,
-                                       HFB_FLT_LEN_V3PLUS + i * sizeof(u32));
-
-       for (i = 0; i < priv->hw_params->hfb_filter_cnt *
-                       priv->hw_params->hfb_filter_size; i++)
-               bcmgenet_hfb_writel(priv, 0x0, i * sizeof(u32));
-}
-
-static void bcmgenet_hfb_init(struct bcmgenet_priv *priv)
-{
-       if (GENET_IS_V1(priv) || GENET_IS_V2(priv))
-               return;
-
-       bcmgenet_hfb_clear(priv);
-}
-
 static void bcmgenet_netif_start(struct net_device *dev)
 {
        struct bcmgenet_priv *priv = netdev_priv(dev);
 
        /* Start the network engine */
+       bcmgenet_set_rx_mode(dev);
        bcmgenet_enable_rx_napi(priv);
 
        umac_enable_set(priv, CMD_TX_EN | CMD_RX_EN, true);
@@ -3156,6 +3721,7 @@ static struct net_device_stats *bcmgenet_get_stats(struct net_device *dev)
        dev->stats.rx_packets = rx_packets;
        dev->stats.rx_errors = rx_errors;
        dev->stats.rx_missed_errors = rx_errors;
+       dev->stats.rx_dropped = rx_dropped;
        return &dev->stats;
 }
 
@@ -3417,8 +3983,6 @@ MODULE_DEVICE_TABLE(of, bcmgenet_match);
 static int bcmgenet_probe(struct platform_device *pdev)
 {
        struct bcmgenet_platform_data *pd = pdev->dev.platform_data;
-       struct device_node *dn = pdev->dev.of_node;
-       const struct of_device_id *of_id = NULL;
        const struct bcmgenet_plat_data *pdata;
        struct bcmgenet_priv *priv;
        struct net_device *dev;
@@ -3433,12 +3997,6 @@ static int bcmgenet_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       if (dn) {
-               of_id = of_match_node(bcmgenet_match, dn);
-               if (!of_id)
-                       return -EINVAL;
-       }
-
        priv = netdev_priv(dev);
        priv->irq0 = platform_get_irq(pdev, 0);
        if (priv->irq0 < 0) {
@@ -3500,13 +4058,16 @@ static int bcmgenet_probe(struct platform_device *pdev)
                priv->dma_max_burst_length = DMA_MAX_BURST_LENGTH;
        }
 
-       priv->clk = devm_clk_get(&priv->pdev->dev, "enet");
+       priv->clk = devm_clk_get_optional(&priv->pdev->dev, "enet");
        if (IS_ERR(priv->clk)) {
                dev_dbg(&priv->pdev->dev, "failed to get enet clock\n");
-               priv->clk = NULL;
+               err = PTR_ERR(priv->clk);
+               goto err;
        }
 
-       clk_prepare_enable(priv->clk);
+       err = clk_prepare_enable(priv->clk);
+       if (err)
+               goto err;
 
        bcmgenet_set_hw_params(priv);
 
@@ -3524,16 +4085,18 @@ static int bcmgenet_probe(struct platform_device *pdev)
        priv->rx_buf_len = RX_BUF_LENGTH;
        INIT_WORK(&priv->bcmgenet_irq_work, bcmgenet_irq_task);
 
-       priv->clk_wol = devm_clk_get(&priv->pdev->dev, "enet-wol");
+       priv->clk_wol = devm_clk_get_optional(&priv->pdev->dev, "enet-wol");
        if (IS_ERR(priv->clk_wol)) {
                dev_dbg(&priv->pdev->dev, "failed to get enet-wol clock\n");
-               priv->clk_wol = NULL;
+               err = PTR_ERR(priv->clk_wol);
+               goto err;
        }
 
-       priv->clk_eee = devm_clk_get(&priv->pdev->dev, "enet-eee");
+       priv->clk_eee = devm_clk_get_optional(&priv->pdev->dev, "enet-eee");
        if (IS_ERR(priv->clk_eee)) {
                dev_dbg(&priv->pdev->dev, "failed to get enet-eee clock\n");
-               priv->clk_eee = NULL;
+               err = PTR_ERR(priv->clk_eee);
+               goto err;
        }
 
        /* If this is an internal GPHY, power it on now, before UniMAC is
@@ -3542,7 +4105,7 @@ static int bcmgenet_probe(struct platform_device *pdev)
        if (device_get_phy_mode(&pdev->dev) == PHY_INTERFACE_MODE_INTERNAL)
                bcmgenet_power_up(priv, GENET_POWER_PASSIVE);
 
-       if ((pd) && (!IS_ERR_OR_NULL(pd->mac_address)))
+       if (pd && !IS_ERR_OR_NULL(pd->mac_address))
                ether_addr_copy(dev->dev_addr, pd->mac_address);
        else
                if (!device_get_mac_address(&pdev->dev, dev->dev_addr, ETH_ALEN))
@@ -3608,11 +4171,10 @@ static void bcmgenet_shutdown(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM_SLEEP
-static int bcmgenet_resume(struct device *d)
+static int bcmgenet_resume_noirq(struct device *d)
 {
        struct net_device *dev = dev_get_drvdata(d);
        struct bcmgenet_priv *priv = netdev_priv(dev);
-       unsigned long dma_ctrl;
        int ret;
        u32 reg;
 
@@ -3624,6 +4186,38 @@ static int bcmgenet_resume(struct device *d)
        if (ret)
                return ret;
 
+       if (device_may_wakeup(d) && priv->wolopts) {
+               /* Account for Wake-on-LAN events and clear those events
+                * (Some devices need more time between enabling the clocks
+                *  and the interrupt register reflecting the wake event so
+                *  read the register twice)
+                */
+               reg = bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_STAT);
+               reg = bcmgenet_intrl2_0_readl(priv, INTRL2_CPU_STAT);
+               if (reg & UMAC_IRQ_WAKE_EVENT)
+                       pm_wakeup_event(&priv->pdev->dev, 0);
+       }
+
+       bcmgenet_intrl2_0_writel(priv, UMAC_IRQ_WAKE_EVENT, INTRL2_CPU_CLEAR);
+
+       return 0;
+}
+
+static int bcmgenet_resume(struct device *d)
+{
+       struct net_device *dev = dev_get_drvdata(d);
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       unsigned long dma_ctrl;
+       u32 offset, reg;
+       int ret;
+
+       if (!netif_running(dev))
+               return 0;
+
+       /* From WOL-enabled suspend, switch to regular clock */
+       if (device_may_wakeup(d) && priv->wolopts)
+               bcmgenet_power_up(priv, GENET_POWER_WOL_MAGIC);
+
        /* If this is an internal GPHY, power it back on now, before UniMAC is
         * brought out of reset as absolutely no UniMAC activity is allowed
         */
@@ -3634,10 +4228,6 @@ static int bcmgenet_resume(struct device *d)
 
        init_umac(priv);
 
-       /* From WOL-enabled suspend, switch to regular clock */
-       if (priv->wolopts)
-               clk_disable_unprepare(priv->clk_wol);
-
        phy_init_hw(dev->phydev);
 
        /* Speed settings must be restored */
@@ -3649,15 +4239,17 @@ static int bcmgenet_resume(struct device *d)
 
        bcmgenet_set_hw_addr(priv, dev->dev_addr);
 
+       offset = HFB_FLT_ENABLE_V3PLUS;
+       bcmgenet_hfb_reg_writel(priv, priv->hfb_en[1], offset);
+       bcmgenet_hfb_reg_writel(priv, priv->hfb_en[2], offset + sizeof(u32));
+       bcmgenet_hfb_reg_writel(priv, priv->hfb_en[0], HFB_CTRL);
+
        if (priv->internal_phy) {
                reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT);
                reg |= EXT_ENERGY_DET_MASK;
                bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
        }
 
-       if (priv->wolopts)
-               bcmgenet_power_up(priv, GENET_POWER_WOL_MAGIC);
-
        /* Disable RX/TX DMA and flush TX queues */
        dma_ctrl = bcmgenet_dma_disable(priv);
 
@@ -3694,7 +4286,7 @@ static int bcmgenet_suspend(struct device *d)
 {
        struct net_device *dev = dev_get_drvdata(d);
        struct bcmgenet_priv *priv = netdev_priv(dev);
-       int ret = 0;
+       u32 offset;
 
        if (!netif_running(dev))
                return 0;
@@ -3706,25 +4298,53 @@ static int bcmgenet_suspend(struct device *d)
        if (!device_may_wakeup(d))
                phy_suspend(dev->phydev);
 
+       /* Preserve filter state and disable filtering */
+       priv->hfb_en[0] = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
+       offset = HFB_FLT_ENABLE_V3PLUS;
+       priv->hfb_en[1] = bcmgenet_hfb_reg_readl(priv, offset);
+       priv->hfb_en[2] = bcmgenet_hfb_reg_readl(priv, offset + sizeof(u32));
+       bcmgenet_hfb_reg_writel(priv, 0, HFB_CTRL);
+
+       return 0;
+}
+
+static int bcmgenet_suspend_noirq(struct device *d)
+{
+       struct net_device *dev = dev_get_drvdata(d);
+       struct bcmgenet_priv *priv = netdev_priv(dev);
+       int ret = 0;
+
+       if (!netif_running(dev))
+               return 0;
+
        /* Prepare the device for Wake-on-LAN and switch to the slow clock */
-       if (device_may_wakeup(d) && priv->wolopts) {
+       if (device_may_wakeup(d) && priv->wolopts)
                ret = bcmgenet_power_down(priv, GENET_POWER_WOL_MAGIC);
-               clk_prepare_enable(priv->clk_wol);
-       } else if (priv->internal_phy) {
+       else if (priv->internal_phy)
                ret = bcmgenet_power_down(priv, GENET_POWER_PASSIVE);
-       }
+
+       /* Let the framework handle resumption and leave the clocks on */
+       if (ret)
+               return ret;
 
        /* Turn off the clocks */
        clk_disable_unprepare(priv->clk);
 
-       if (ret)
-               bcmgenet_resume(d);
-
-       return ret;
+       return 0;
 }
+#else
+#define bcmgenet_suspend       NULL
+#define bcmgenet_suspend_noirq NULL
+#define bcmgenet_resume                NULL
+#define bcmgenet_resume_noirq  NULL
 #endif /* CONFIG_PM_SLEEP */
 
-static SIMPLE_DEV_PM_OPS(bcmgenet_pm_ops, bcmgenet_suspend, bcmgenet_resume);
+static const struct dev_pm_ops bcmgenet_pm_ops = {
+       .suspend        = bcmgenet_suspend,
+       .suspend_noirq  = bcmgenet_suspend_noirq,
+       .resume         = bcmgenet_resume,
+       .resume_noirq   = bcmgenet_resume_noirq,
+};
 
 static const struct acpi_device_id genet_acpi_match[] = {
        { "BCM6E4E", (kernel_ulong_t)&bcm2711_plat_data },
@@ -3740,7 +4360,7 @@ static struct platform_driver bcmgenet_driver = {
                .name   = "bcmgenet",
                .of_match_table = bcmgenet_match,
                .pm     = &bcmgenet_pm_ops,
-               .acpi_match_table = ACPI_PTR(genet_acpi_match),
+               .acpi_match_table = genet_acpi_match,
        },
 };
 module_platform_driver(bcmgenet_driver);
index daf8fb2..a12cb59 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2014-2017 Broadcom
+ * Copyright (c) 2014-2020 Broadcom
  */
 
 #ifndef __BCMGENET_H__
@@ -14,6 +14,7 @@
 #include <linux/if_vlan.h>
 #include <linux/phy.h>
 #include <linux/dim.h>
+#include <linux/ethtool.h>
 
 /* total number of Buffer Descriptors, same for Rx/Tx */
 #define TOTAL_DESC                             256
@@ -31,6 +32,7 @@
 #define DMA_MAX_BURST_LENGTH    0x10
 
 /* misc. configuration */
+#define MAX_NUM_OF_FS_RULES            16
 #define CLEAR_ALL_HFB                  0xFF
 #define DMA_FC_THRESH_HI               (TOTAL_DESC >> 4)
 #define DMA_FC_THRESH_LO               5
@@ -310,6 +312,8 @@ struct bcmgenet_mib_counters {
 #define UMAC_IRQ_HFB_SM                        (1 << 10)
 #define UMAC_IRQ_HFB_MM                        (1 << 11)
 #define UMAC_IRQ_MPD_R                 (1 << 12)
+#define UMAC_IRQ_WAKE_EVENT            (UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM | \
+                                        UMAC_IRQ_MPD_R)
 #define UMAC_IRQ_RXDMA_MBDONE          (1 << 13)
 #define UMAC_IRQ_RXDMA_PDONE           (1 << 14)
 #define UMAC_IRQ_RXDMA_BDONE           (1 << 15)
@@ -608,6 +612,18 @@ struct bcmgenet_rx_ring {
        struct bcmgenet_priv *priv;
 };
 
+enum bcmgenet_rxnfc_state {
+       BCMGENET_RXNFC_STATE_UNUSED = 0,
+       BCMGENET_RXNFC_STATE_DISABLED,
+       BCMGENET_RXNFC_STATE_ENABLED
+};
+
+struct bcmgenet_rxnfc_rule {
+       struct  list_head list;
+       struct ethtool_rx_flow_spec     fs;
+       enum bcmgenet_rxnfc_state state;
+};
+
 /* device context */
 struct bcmgenet_priv {
        void __iomem *base;
@@ -626,6 +642,8 @@ struct bcmgenet_priv {
        struct enet_cb *rx_cbs;
        unsigned int num_rx_bds;
        unsigned int rx_buf_len;
+       struct bcmgenet_rxnfc_rule rxnfc_rules[MAX_NUM_OF_FS_RULES];
+       struct list_head rxnfc_list;
 
        struct bcmgenet_rx_ring rx_rings[DESC_INDEX + 1];
 
@@ -676,6 +694,9 @@ struct bcmgenet_priv {
        /* WOL */
        struct clk *clk_wol;
        u32 wolopts;
+       u8 sopass[SOPASS_MAX];
+       bool wol_active;
+       u32 hfb_en[3];
 
        struct bcmgenet_mib_counters mib;
 
index c9a4369..4ea6a26 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support
  *
- * Copyright (c) 2014-2017 Broadcom
+ * Copyright (c) 2014-2020 Broadcom
  */
 
 #define pr_fmt(fmt)                            "bcmgenet_wol: " fmt
 void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 {
        struct bcmgenet_priv *priv = netdev_priv(dev);
-       u32 reg;
 
-       wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE;
+       wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER;
        wol->wolopts = priv->wolopts;
        memset(wol->sopass, 0, sizeof(wol->sopass));
 
-       if (wol->wolopts & WAKE_MAGICSECURE) {
-               reg = bcmgenet_umac_readl(priv, UMAC_MPD_PW_MS);
-               put_unaligned_be16(reg, &wol->sopass[0]);
-               reg = bcmgenet_umac_readl(priv, UMAC_MPD_PW_LS);
-               put_unaligned_be32(reg, &wol->sopass[2]);
-       }
+       if (wol->wolopts & WAKE_MAGICSECURE)
+               memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass));
 }
 
 /* ethtool function - set WOL (Wake on LAN) settings.
@@ -62,25 +57,15 @@ int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
 {
        struct bcmgenet_priv *priv = netdev_priv(dev);
        struct device *kdev = &priv->pdev->dev;
-       u32 reg;
 
        if (!device_can_wakeup(kdev))
                return -ENOTSUPP;
 
-       if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE))
+       if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER))
                return -EINVAL;
 
-       reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
-       if (wol->wolopts & WAKE_MAGICSECURE) {
-               bcmgenet_umac_writel(priv, get_unaligned_be16(&wol->sopass[0]),
-                                    UMAC_MPD_PW_MS);
-               bcmgenet_umac_writel(priv, get_unaligned_be32(&wol->sopass[2]),
-                                    UMAC_MPD_PW_LS);
-               reg |= MPD_PW_EN;
-       } else {
-               reg &= ~MPD_PW_EN;
-       }
-       bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
+       if (wol->wolopts & WAKE_MAGICSECURE)
+               memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass));
 
        /* Flag the device and relevant IRQ as wakeup capable */
        if (wol->wolopts) {
@@ -120,12 +105,21 @@ static int bcmgenet_poll_wol_status(struct bcmgenet_priv *priv)
        return retries;
 }
 
+static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv)
+{
+       bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]),
+                            UMAC_MPD_PW_MS);
+       bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]),
+                            UMAC_MPD_PW_LS);
+}
+
 int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
                                enum bcmgenet_power_mode mode)
 {
        struct net_device *dev = priv->dev;
+       struct bcmgenet_rxnfc_rule *rule;
+       u32 reg, hfb_ctrl_reg, hfb_enable = 0;
        int retries = 0;
-       u32 reg;
 
        if (mode != GENET_POWER_WOL_MAGIC) {
                netif_err(priv, wol, dev, "unsupported mode: %d\n", mode);
@@ -142,22 +136,48 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
        bcmgenet_umac_writel(priv, reg, UMAC_CMD);
        mdelay(10);
 
-       reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
-       reg |= MPD_EN;
-       bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
+       if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
+               reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
+               reg |= MPD_EN;
+               if (priv->wolopts & WAKE_MAGICSECURE) {
+                       bcmgenet_set_mpd_password(priv);
+                       reg |= MPD_PW_EN;
+               }
+               bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
+       }
+
+       hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
+       if (priv->wolopts & WAKE_FILTER) {
+               list_for_each_entry(rule, &priv->rxnfc_list, list)
+                       if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE)
+                               hfb_enable |= (1 << rule->fs.location);
+               reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN;
+               bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
+       }
 
        /* Do not leave UniMAC in MPD mode only */
        retries = bcmgenet_poll_wol_status(priv);
        if (retries < 0) {
                reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
-               reg &= ~MPD_EN;
+               reg &= ~(MPD_EN | MPD_PW_EN);
                bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
+               bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
                return retries;
        }
 
        netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n",
                  retries);
 
+       clk_prepare_enable(priv->clk_wol);
+       priv->wol_active = 1;
+
+       if (hfb_enable) {
+               bcmgenet_hfb_reg_writel(priv, hfb_enable,
+                                       HFB_FLT_ENABLE_V3PLUS + 4);
+               hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN;
+               bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL);
+       }
+
        /* Enable CRC forward */
        reg = bcmgenet_umac_readl(priv, UMAC_CMD);
        priv->crc_fwd_en = 1;
@@ -173,6 +193,12 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv,
                bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT);
        }
 
+       reg = UMAC_IRQ_MPD_R;
+       if (hfb_enable)
+               reg |=  UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM;
+
+       bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR);
+
        return 0;
 }
 
@@ -186,12 +212,22 @@ void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv,
                return;
        }
 
+       if (!priv->wol_active)
+               return; /* failed to suspend so skip the rest */
+
+       priv->wol_active = 0;
+       clk_disable_unprepare(priv->clk_wol);
+
+       /* Disable Magic Packet Detection */
        reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
-       if (!(reg & MPD_EN))
-               return; /* already powered up so skip the rest */
-       reg &= ~MPD_EN;
+       reg &= ~(MPD_EN | MPD_PW_EN);
        bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
 
+       /* Disable WAKE_FILTER Detection */
+       reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL);
+       reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN);
+       bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL);
+
        /* Disable CRC Forward */
        reg = bcmgenet_umac_readl(priv, UMAC_CMD);
        reg &= ~CMD_CRC_FWD;
index 53b50c2..2c4c12b 100644 (file)
@@ -35,8 +35,8 @@ config MACB
 config MACB_USE_HWSTAMP
        bool "Use IEEE 1588 hwstamp"
        depends on MACB
+       depends on PTP_1588_CLOCK
        default y
-       imply PTP_1588_CLOCK
        ---help---
          Enable IEEE 1588 Precision Time Protocol (PTP) support for MACB.
 
index a0e8c5b..36290a8 100644 (file)
@@ -334,8 +334,10 @@ static int macb_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
        int status;
 
        status = pm_runtime_get_sync(&bp->pdev->dev);
-       if (status < 0)
+       if (status < 0) {
+               pm_runtime_put_noidle(&bp->pdev->dev);
                goto mdio_pm_exit;
+       }
 
        status = macb_mdio_wait_for_idle(bp);
        if (status < 0)
@@ -386,8 +388,10 @@ static int macb_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
        int status;
 
        status = pm_runtime_get_sync(&bp->pdev->dev);
-       if (status < 0)
+       if (status < 0) {
+               pm_runtime_put_noidle(&bp->pdev->dev);
                goto mdio_pm_exit;
+       }
 
        status = macb_mdio_wait_for_idle(bp);
        if (status < 0)
@@ -3816,8 +3820,10 @@ static int at91ether_open(struct net_device *dev)
        int ret;
 
        ret = pm_runtime_get_sync(&lp->pdev->dev);
-       if (ret < 0)
+       if (ret < 0) {
+               pm_runtime_put_noidle(&lp->pdev->dev);
                return ret;
+       }
 
        /* Clear internal statistics */
        ctl = macb_readl(lp, NCR);
@@ -4172,15 +4178,9 @@ static int fu540_c000_clk_init(struct platform_device *pdev, struct clk **pclk,
 
 static int fu540_c000_init(struct platform_device *pdev)
 {
-       struct resource *res;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       if (!res)
-               return -ENODEV;
-
-       mgmt->reg = ioremap(res->start, resource_size(res));
-       if (!mgmt->reg)
-               return -ENOMEM;
+       mgmt->reg = devm_platform_ioremap_resource(pdev, 1);
+       if (IS_ERR(mgmt->reg))
+               return PTR_ERR(mgmt->reg);
 
        return macb_init(pdev);
 }
index 6a700d3..4520e7e 100644 (file)
@@ -54,7 +54,7 @@ config        THUNDER_NIC_RGX
 config CAVIUM_PTP
        tristate "Cavium PTP coprocessor as PTP clock"
        depends on 64BIT && PCI
-       imply PTP_1588_CLOCK
+       depends on PTP_1588_CLOCK
        ---help---
          This driver adds support for the Precision Time Protocol Clocks and
          Timestamping coprocessor (PTP) found on Cavium processors.
index 3d01d36..fb380b4 100644 (file)
@@ -712,18 +712,6 @@ struct octeon_device *lio_get_device(u32 octeon_id);
  */
 int lio_get_device_id(void *dev);
 
-static inline u16 OCTEON_MAJOR_REV(struct octeon_device *oct)
-{
-       u16 rev = (oct->rev_id & 0xC) >> 2;
-
-       return (rev == 0) ? 1 : rev;
-}
-
-static inline u16 OCTEON_MINOR_REV(struct octeon_device *oct)
-{
-       return oct->rev_id & 0x3;
-}
-
 /** Read windowed register.
  *  @param  oct   -  pointer to the Octeon device.
  *  @param  addr  -  Address of the register to read.
index 9909bfd..82cdfa5 100644 (file)
@@ -26,7 +26,7 @@ config CHELSIO_T1
          This driver supports Chelsio gigabit and 10-gigabit
          Ethernet cards. More information about adapter features and
          performance tuning is in
-         <file:Documentation/networking/device_drivers/chelsio/cxgb.txt>.
+         <file:Documentation/networking/device_drivers/chelsio/cxgb.rst>.
 
          For general information about Chelsio and our products, visit
          our website at <http://www.chelsio.com>.
index 19c1156..7b9cd69 100644 (file)
@@ -1049,9 +1049,9 @@ static void cudbg_t4_fwcache(struct cudbg_init *pdbg_init,
        }
 }
 
-static unsigned long cudbg_mem_region_size(struct cudbg_init *pdbg_init,
-                                          struct cudbg_error *cudbg_err,
-                                          u8 mem_type)
+static int cudbg_mem_region_size(struct cudbg_init *pdbg_init,
+                                struct cudbg_error *cudbg_err,
+                                u8 mem_type, unsigned long *region_size)
 {
        struct adapter *padap = pdbg_init->adap;
        struct cudbg_meminfo mem_info;
@@ -1060,15 +1060,23 @@ static unsigned long cudbg_mem_region_size(struct cudbg_init *pdbg_init,
 
        memset(&mem_info, 0, sizeof(struct cudbg_meminfo));
        rc = cudbg_fill_meminfo(padap, &mem_info);
-       if (rc)
+       if (rc) {
+               cudbg_err->sys_err = rc;
                return rc;
+       }
 
        cudbg_t4_fwcache(pdbg_init, cudbg_err);
        rc = cudbg_meminfo_get_mem_index(padap, &mem_info, mem_type, &mc_idx);
-       if (rc)
+       if (rc) {
+               cudbg_err->sys_err = rc;
                return rc;
+       }
+
+       if (region_size)
+               *region_size = mem_info.avail[mc_idx].limit -
+                              mem_info.avail[mc_idx].base;
 
-       return mem_info.avail[mc_idx].limit - mem_info.avail[mc_idx].base;
+       return 0;
 }
 
 static int cudbg_collect_mem_region(struct cudbg_init *pdbg_init,
@@ -1076,7 +1084,12 @@ static int cudbg_collect_mem_region(struct cudbg_init *pdbg_init,
                                    struct cudbg_error *cudbg_err,
                                    u8 mem_type)
 {
-       unsigned long size = cudbg_mem_region_size(pdbg_init, cudbg_err, mem_type);
+       unsigned long size = 0;
+       int rc;
+
+       rc = cudbg_mem_region_size(pdbg_init, cudbg_err, mem_type, &size);
+       if (rc)
+               return rc;
 
        return cudbg_read_fw_mem(pdbg_init, dbg_buff, mem_type, size,
                                 cudbg_err);
index af1f40c..f5bc996 100644 (file)
@@ -311,32 +311,17 @@ static int cxgb4_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
  */
 static int cxgb4_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
 {
-       struct adapter *adapter = (struct adapter *)container_of(ptp,
-                                  struct adapter, ptp_clock_info);
-       struct fw_ptp_cmd c;
+       struct adapter *adapter = container_of(ptp, struct adapter,
+                                              ptp_clock_info);
        u64 ns;
-       int err;
-
-       memset(&c, 0, sizeof(c));
-       c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PTP_CMD) |
-                                    FW_CMD_REQUEST_F |
-                                    FW_CMD_READ_F |
-                                    FW_PTP_CMD_PORTID_V(0));
-       c.retval_len16 = cpu_to_be32(FW_CMD_LEN16_V(sizeof(c) / 16));
-       c.u.ts.sc = FW_PTP_SC_GET_TIME;
 
-       err = t4_wr_mbox(adapter, adapter->mbox, &c, sizeof(c), &c);
-       if (err < 0) {
-               dev_err(adapter->pdev_dev,
-                       "PTP: %s error %d\n", __func__, -err);
-               return err;
-       }
+       ns = t4_read_reg(adapter, T5_PORT_REG(0, MAC_PORT_PTP_SUM_LO_A));
+       ns |= (u64)t4_read_reg(adapter,
+                              T5_PORT_REG(0, MAC_PORT_PTP_SUM_HI_A)) << 32;
 
        /* convert to timespec*/
-       ns = be64_to_cpu(c.u.ts.tm);
        *ts = ns_to_timespec64(ns);
-
-       return err;
+       return 0;
 }
 
 /**
index f5dd34d..6516c45 100644 (file)
@@ -2207,6 +2207,9 @@ static void ethofld_hard_xmit(struct net_device *dev,
        if (unlikely(skip_eotx_wr)) {
                start = (u64 *)wr;
                eosw_txq->state = next_state;
+               eosw_txq->cred -= wrlen16;
+               eosw_txq->ncompl++;
+               eosw_txq->last_compl = 0;
                goto write_wr_headers;
        }
 
@@ -2365,6 +2368,34 @@ netdev_tx_t t4_start_xmit(struct sk_buff *skb, struct net_device *dev)
        return cxgb4_eth_xmit(skb, dev);
 }
 
+static void eosw_txq_flush_pending_skbs(struct sge_eosw_txq *eosw_txq)
+{
+       int pktcount = eosw_txq->pidx - eosw_txq->last_pidx;
+       int pidx = eosw_txq->pidx;
+       struct sk_buff *skb;
+
+       if (!pktcount)
+               return;
+
+       if (pktcount < 0)
+               pktcount += eosw_txq->ndesc;
+
+       while (pktcount--) {
+               pidx--;
+               if (pidx < 0)
+                       pidx += eosw_txq->ndesc;
+
+               skb = eosw_txq->desc[pidx].skb;
+               if (skb) {
+                       dev_consume_skb_any(skb);
+                       eosw_txq->desc[pidx].skb = NULL;
+                       eosw_txq->inuse--;
+               }
+       }
+
+       eosw_txq->pidx = eosw_txq->last_pidx + 1;
+}
+
 /**
  * cxgb4_ethofld_send_flowc - Send ETHOFLD flowc request to bind eotid to tc.
  * @dev - netdevice
@@ -2440,9 +2471,11 @@ int cxgb4_ethofld_send_flowc(struct net_device *dev, u32 eotid, u32 tc)
                                            FW_FLOWC_MNEM_EOSTATE_CLOSING :
                                            FW_FLOWC_MNEM_EOSTATE_ESTABLISHED);
 
-       eosw_txq->cred -= len16;
-       eosw_txq->ncompl++;
-       eosw_txq->last_compl = 0;
+       /* Free up any pending skbs to ensure there's room for
+        * termination FLOWC.
+        */
+       if (tc == FW_SCHED_CLS_NONE)
+               eosw_txq_flush_pending_skbs(eosw_txq);
 
        ret = eosw_txq_enqueue(eosw_txq, skb);
        if (ret) {
@@ -2695,6 +2728,7 @@ static void ofldtxq_stop(struct sge_uld_txq *q, struct fw_wr_hdr *wr)
  *     is ever running at a time ...
  */
 static void service_ofldq(struct sge_uld_txq *q)
+       __must_hold(&q->sendq.lock)
 {
        u64 *pos, *before, *end;
        int credits;
index 239f678..2a3480f 100644 (file)
@@ -3742,7 +3742,7 @@ int t4_phy_fw_ver(struct adapter *adap, int *phy_fw_ver)
                 FW_PARAMS_PARAM_Z_V(FW_PARAMS_PARAM_DEV_PHYFW_VERSION));
        ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1,
                              &param, &val);
-       if (ret < 0)
+       if (ret)
                return ret;
        *phy_fw_ver = val;
        return 0;
index bb20e50..4a9fcd6 100644 (file)
 
 #define MAC_PORT_CFG2_A 0x818
 
+#define MAC_PORT_PTP_SUM_LO_A 0x990
+#define MAC_PORT_PTP_SUM_HI_A 0x994
+
 #define MPS_CMN_CTL_A  0x9000
 
 #define COUNTPAUSEMCRX_S    5
index 9cc3541..cec865a 100644 (file)
@@ -2480,7 +2480,7 @@ static int setup_debugfs(struct adapter *adapter)
        for (i = 0; i < ARRAY_SIZE(debugfs_files); i++)
                debugfs_create_file(debugfs_files[i].name,
                                    debugfs_files[i].mode,
-                                   adapter->debugfs_root, (void *)adapter,
+                                   adapter->debugfs_root, adapter,
                                    debugfs_files[i].fops);
 
        return 0;
index 48f3198..8d845f5 100644 (file)
@@ -24,7 +24,7 @@ config CS89x0
        ---help---
          Support for CS89x0 chipset based Ethernet cards. If you have a
          network (Ethernet) card of this type, say Y and read the file
-         <file:Documentation/networking/device_drivers/cirrus/cs89x0.txt>.
+         <file:Documentation/networking/device_drivers/cirrus/cs89x0.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called cs89x0.
index 5bff5c2..8d13ea3 100644 (file)
@@ -1224,7 +1224,8 @@ map_error:
        return -ENOMEM;
 }
 
-static int gmac_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+static netdev_tx_t gmac_start_xmit(struct sk_buff *skb,
+                                  struct net_device *netdev)
 {
        struct gemini_ethernet_port *port = netdev_priv(netdev);
        unsigned short m = (1 << port->txq_order) - 1;
index 8ce6888..177f36f 100644 (file)
@@ -114,7 +114,7 @@ config DE4X5
          These include the DE425, DE434, DE435, DE450 and DE500 models.  If
          you have a network card of this type, say Y.  More specific
          information is contained in
-         <file:Documentation/networking/device_drivers/dec/de4x5.txt>.
+         <file:Documentation/networking/device_drivers/dec/de4x5.rst>.
 
          To compile this driver as a module, choose M here. The module will
          be called de4x5.
@@ -138,7 +138,7 @@ config DM9102
          This driver is for DM9102(A)/DM9132/DM9801 compatible PCI cards from
          Davicom (<http://www.davicom.com.tw/>).  If you have such a network
          (Ethernet) card, say Y.  Some information is contained in the file
-         <file:Documentation/networking/device_drivers/dec/dmfe.txt>.
+         <file:Documentation/networking/device_drivers/dec/dmfe.rst>.
 
          To compile this driver as a module, choose M here. The module will
          be called dmfe.
index f16853c..0ccd999 100644 (file)
@@ -951,7 +951,7 @@ static void    reset_init_sia(struct net_device *dev, s32 sicr, s32 strr, s32 si
 static int     test_ans(struct net_device *dev, s32 irqs, s32 irq_mask, s32 msec);
 static int     test_tp(struct net_device *dev, s32 msec);
 static int     EISA_signature(char *name, struct device *device);
-static int     PCI_signature(char *name, struct de4x5_private *lp);
+static void    PCI_signature(char *name, struct de4x5_private *lp);
 static void    DevicePresent(struct net_device *dev, u_long iobase);
 static void    enet_addr_rst(u_long aprom_addr);
 static int     de4x5_bad_srom(struct de4x5_private *lp);
@@ -3902,14 +3902,14 @@ EISA_signature(char *name, struct device *device)
 /*
 ** Look for a particular board name in the PCI configuration space
 */
-static int
+static void
 PCI_signature(char *name, struct de4x5_private *lp)
 {
-    int i, status = 0, siglen = ARRAY_SIZE(de4x5_signatures);
+    int i, siglen = ARRAY_SIZE(de4x5_signatures);
 
     if (lp->chipset == DC21040) {
        strcpy(name, "DE434/5");
-       return status;
+       return;
     } else {                           /* Search for a DEC name in the SROM */
        int tmp = *((char *)&lp->srom + 19) * 3;
        strncpy(name, (char *)&lp->srom + 26 + tmp, 8);
@@ -3935,8 +3935,6 @@ PCI_signature(char *name, struct de4x5_private *lp)
     } else if ((lp->chipset & ~0x00ff) == DC2114x) {
        lp->useSROM = true;
     }
-
-    return status;
 }
 
 /*
index 48ea658..15efc29 100644 (file)
@@ -1277,7 +1277,7 @@ static const struct net_device_ops tulip_netdev_ops = {
 #endif
 };
 
-const struct pci_device_id early_486_chipsets[] = {
+static const struct pci_device_id early_486_chipsets[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82424) },
        { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496) },
        { },
index 6430905..5143722 100644 (file)
@@ -1869,7 +1869,7 @@ Compile command:
 
 gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -c dl2k.c
 
-Read Documentation/networking/device_drivers/dlink/dl2k.txt for details.
+Read Documentation/networking/device_drivers/dlink/dl2k.rst for details.
 
 */
 
index 057a508..db98274 100644 (file)
@@ -776,8 +776,7 @@ static int dnet_probe(struct platform_device *pdev)
 
        spin_lock_init(&bp->lock);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       bp->regs = devm_ioremap_resource(&pdev->dev, res);
+       bp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(bp->regs)) {
                err = PTR_ERR(bp->regs);
                goto err_out_free_dev;
index 32cf54f..473b337 100644 (file)
@@ -1057,9 +1057,6 @@ static int ftmac100_probe(struct platform_device *pdev)
        struct ftmac100 *priv;
        int err;
 
-       if (!pdev)
-               return -ENODEV;
-
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res)
                return -ENXIO;
index 2cd1f8e..c4416a5 100644 (file)
@@ -2107,7 +2107,7 @@ workaround:
 
        /* Workaround for DPAA_A050385 requires data start to be aligned */
        start = PTR_ALIGN(new_skb->data, DPAA_A050385_ALIGN);
-       if (start - new_skb->data != 0)
+       if (start - new_skb->data)
                skb_reserve(new_skb, start - new_skb->data);
 
        skb_put(new_skb, skb->len);
index a9afe46..0a31e42 100644 (file)
@@ -127,16 +127,19 @@ static int dpaa2_dbg_ch_show(struct seq_file *file, void *offset)
        int i;
 
        seq_printf(file, "Channel stats for %s:\n", priv->net_dev->name);
-       seq_printf(file, "%s%16s%16s%16s%16s\n",
-                  "CHID", "CPU", "Deq busy", "CDANs", "Buf count");
+       seq_printf(file, "%s%16s%16s%16s%16s%16s%16s\n",
+                  "CHID", "CPU", "Deq busy", "Frames", "CDANs",
+                  "Avg Frm/CDAN", "Buf count");
 
        for (i = 0; i < priv->num_channels; i++) {
                ch = priv->channel[i];
-               seq_printf(file, "%4d%16d%16llu%16llu%16d\n",
+               seq_printf(file, "%4d%16d%16llu%16llu%16llu%16llu%16d\n",
                           ch->ch_id,
                           ch->nctx.desired_cpu,
                           ch->stats.dequeue_portal_busy,
+                          ch->stats.frames,
                           ch->stats.cdan,
+                          div64_u64(ch->stats.frames, ch->stats.cdan),
                           ch->buf_count);
        }
 
index b6c4663..11accab 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
 /* Copyright 2014-2016 Freescale Semiconductor Inc.
- * Copyright 2016-2019 NXP
+ * Copyright 2016-2020 NXP
  */
 #include <linux/init.h>
 #include <linux/module.h>
@@ -268,7 +268,7 @@ static int xdp_enqueue(struct dpaa2_eth_priv *priv, struct dpaa2_fd *fd,
 
        fq = &priv->fq[queue_id];
        for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
-               err = priv->enqueue(priv, fq, fd, 0);
+               err = priv->enqueue(priv, fq, fd, 0, 1, NULL);
                if (err != -EBUSY)
                        break;
        }
@@ -493,6 +493,7 @@ static int consume_frames(struct dpaa2_eth_channel *ch,
                return 0;
 
        fq->stats.frames += cleaned;
+       ch->stats.frames += cleaned;
 
        /* A dequeue operation only pulls frames from a single queue
         * into the store. Return the frame queue as an out param.
@@ -847,7 +848,7 @@ static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev)
         * the Tx confirmation callback for this frame
         */
        for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
-               err = priv->enqueue(priv, fq, &fd, prio);
+               err = priv->enqueue(priv, fq, &fd, prio, 1, NULL);
                if (err != -EBUSY)
                        break;
        }
@@ -1880,20 +1881,16 @@ static int dpaa2_eth_xdp(struct net_device *dev, struct netdev_bpf *xdp)
        return 0;
 }
 
-static int dpaa2_eth_xdp_xmit_frame(struct net_device *net_dev,
-                                   struct xdp_frame *xdpf)
+static int dpaa2_eth_xdp_create_fd(struct net_device *net_dev,
+                                  struct xdp_frame *xdpf,
+                                  struct dpaa2_fd *fd)
 {
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
        struct device *dev = net_dev->dev.parent;
-       struct rtnl_link_stats64 *percpu_stats;
-       struct dpaa2_eth_drv_stats *percpu_extras;
        unsigned int needed_headroom;
        struct dpaa2_eth_swa *swa;
-       struct dpaa2_eth_fq *fq;
-       struct dpaa2_fd fd;
        void *buffer_start, *aligned_start;
        dma_addr_t addr;
-       int err, i;
 
        /* We require a minimum headroom to be able to transmit the frame.
         * Otherwise return an error and let the original net_device handle it
@@ -1902,11 +1899,8 @@ static int dpaa2_eth_xdp_xmit_frame(struct net_device *net_dev,
        if (xdpf->headroom < needed_headroom)
                return -EINVAL;
 
-       percpu_stats = this_cpu_ptr(priv->percpu_stats);
-       percpu_extras = this_cpu_ptr(priv->percpu_extras);
-
        /* Setup the FD fields */
-       memset(&fd, 0, sizeof(fd));
+       memset(fd, 0, sizeof(*fd));
 
        /* Align FD address, if possible */
        buffer_start = xdpf->data - needed_headroom;
@@ -1924,32 +1918,14 @@ static int dpaa2_eth_xdp_xmit_frame(struct net_device *net_dev,
        addr = dma_map_single(dev, buffer_start,
                              swa->xdp.dma_size,
                              DMA_BIDIRECTIONAL);
-       if (unlikely(dma_mapping_error(dev, addr))) {
-               percpu_stats->tx_dropped++;
+       if (unlikely(dma_mapping_error(dev, addr)))
                return -ENOMEM;
-       }
-
-       dpaa2_fd_set_addr(&fd, addr);
-       dpaa2_fd_set_offset(&fd, xdpf->data - buffer_start);
-       dpaa2_fd_set_len(&fd, xdpf->len);
-       dpaa2_fd_set_format(&fd, dpaa2_fd_single);
-       dpaa2_fd_set_ctrl(&fd, FD_CTRL_PTA);
 
-       fq = &priv->fq[smp_processor_id() % dpaa2_eth_queue_count(priv)];
-       for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) {
-               err = priv->enqueue(priv, fq, &fd, 0);
-               if (err != -EBUSY)
-                       break;
-       }
-       percpu_extras->tx_portal_busy += i;
-       if (unlikely(err < 0)) {
-               percpu_stats->tx_errors++;
-               /* let the Rx device handle the cleanup */
-               return err;
-       }
-
-       percpu_stats->tx_packets++;
-       percpu_stats->tx_bytes += dpaa2_fd_get_len(&fd);
+       dpaa2_fd_set_addr(fd, addr);
+       dpaa2_fd_set_offset(fd, xdpf->data - buffer_start);
+       dpaa2_fd_set_len(fd, xdpf->len);
+       dpaa2_fd_set_format(fd, dpaa2_fd_single);
+       dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA);
 
        return 0;
 }
@@ -1957,8 +1933,13 @@ static int dpaa2_eth_xdp_xmit_frame(struct net_device *net_dev,
 static int dpaa2_eth_xdp_xmit(struct net_device *net_dev, int n,
                              struct xdp_frame **frames, u32 flags)
 {
-       int drops = 0;
-       int i, err;
+       struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+       int total_enqueued = 0, retries = 0, enqueued;
+       struct dpaa2_eth_drv_stats *percpu_extras;
+       struct rtnl_link_stats64 *percpu_stats;
+       int num_fds, i, err, max_retries;
+       struct dpaa2_eth_fq *fq;
+       struct dpaa2_fd *fds;
 
        if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
                return -EINVAL;
@@ -1966,17 +1947,40 @@ static int dpaa2_eth_xdp_xmit(struct net_device *net_dev, int n,
        if (!netif_running(net_dev))
                return -ENETDOWN;
 
+       fq = &priv->fq[smp_processor_id()];
+       fds = fq->xdp_fds;
+
+       percpu_stats = this_cpu_ptr(priv->percpu_stats);
+       percpu_extras = this_cpu_ptr(priv->percpu_extras);
+
+       /* create a FD for each xdp_frame in the list received */
        for (i = 0; i < n; i++) {
-               struct xdp_frame *xdpf = frames[i];
+               err = dpaa2_eth_xdp_create_fd(net_dev, frames[i], &fds[i]);
+               if (err)
+                       break;
+       }
+       num_fds = i;
 
-               err = dpaa2_eth_xdp_xmit_frame(net_dev, xdpf);
-               if (err) {
-                       xdp_return_frame_rx_napi(xdpf);
-                       drops++;
+       /* try to enqueue all the FDs until the max number of retries is hit */
+       max_retries = num_fds * DPAA2_ETH_ENQUEUE_RETRIES;
+       while (total_enqueued < num_fds && retries < max_retries) {
+               err = priv->enqueue(priv, fq, &fds[total_enqueued],
+                                   0, num_fds - total_enqueued, &enqueued);
+               if (err == -EBUSY) {
+                       percpu_extras->tx_portal_busy += ++retries;
+                       continue;
                }
+               total_enqueued += enqueued;
        }
 
-       return n - drops;
+       /* update statistics */
+       percpu_stats->tx_packets += total_enqueued;
+       for (i = 0; i < total_enqueued; i++)
+               percpu_stats->tx_bytes += dpaa2_fd_get_len(&fds[i]);
+       for (i = total_enqueued; i < n; i++)
+               xdp_return_frame_rx_napi(frames[i]);
+
+       return total_enqueued;
 }
 
 static int update_xps(struct dpaa2_eth_priv *priv)
@@ -2018,7 +2022,7 @@ static int dpaa2_eth_setup_tc(struct net_device *net_dev,
        int i;
 
        if (type != TC_SETUP_QDISC_MQPRIO)
-               return -EINVAL;
+               return -EOPNOTSUPP;
 
        mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
        num_queues = dpaa2_eth_queue_count(priv);
@@ -2030,7 +2034,7 @@ static int dpaa2_eth_setup_tc(struct net_device *net_dev,
        if (num_tc  > dpaa2_eth_tc_count(priv)) {
                netdev_err(net_dev, "Max %d traffic classes supported\n",
                           dpaa2_eth_tc_count(priv));
-               return -EINVAL;
+               return -EOPNOTSUPP;
        }
 
        if (!num_tc) {
@@ -2523,19 +2527,38 @@ static int set_buffer_layout(struct dpaa2_eth_priv *priv)
 
 static inline int dpaa2_eth_enqueue_qd(struct dpaa2_eth_priv *priv,
                                       struct dpaa2_eth_fq *fq,
-                                      struct dpaa2_fd *fd, u8 prio)
+                                      struct dpaa2_fd *fd, u8 prio,
+                                      u32 num_frames __always_unused,
+                                      int *frames_enqueued)
 {
-       return dpaa2_io_service_enqueue_qd(fq->channel->dpio,
-                                          priv->tx_qdid, prio,
-                                          fq->tx_qdbin, fd);
+       int err;
+
+       err = dpaa2_io_service_enqueue_qd(fq->channel->dpio,
+                                         priv->tx_qdid, prio,
+                                         fq->tx_qdbin, fd);
+       if (!err && frames_enqueued)
+               *frames_enqueued = 1;
+       return err;
 }
 
-static inline int dpaa2_eth_enqueue_fq(struct dpaa2_eth_priv *priv,
-                                      struct dpaa2_eth_fq *fq,
-                                      struct dpaa2_fd *fd, u8 prio)
+static inline int dpaa2_eth_enqueue_fq_multiple(struct dpaa2_eth_priv *priv,
+                                               struct dpaa2_eth_fq *fq,
+                                               struct dpaa2_fd *fd,
+                                               u8 prio, u32 num_frames,
+                                               int *frames_enqueued)
 {
-       return dpaa2_io_service_enqueue_fq(fq->channel->dpio,
-                                          fq->tx_fqid[prio], fd);
+       int err;
+
+       err = dpaa2_io_service_enqueue_multiple_fq(fq->channel->dpio,
+                                                  fq->tx_fqid[prio],
+                                                  fd, num_frames);
+
+       if (err == 0)
+               return -EBUSY;
+
+       if (frames_enqueued)
+               *frames_enqueued = err;
+       return 0;
 }
 
 static void set_enqueue_mode(struct dpaa2_eth_priv *priv)
@@ -2544,7 +2567,7 @@ static void set_enqueue_mode(struct dpaa2_eth_priv *priv)
                                   DPNI_ENQUEUE_FQID_VER_MINOR) < 0)
                priv->enqueue = dpaa2_eth_enqueue_qd;
        else
-               priv->enqueue = dpaa2_eth_enqueue_fq;
+               priv->enqueue = dpaa2_eth_enqueue_fq_multiple;
 }
 
 static int set_pause(struct dpaa2_eth_priv *priv)
@@ -2605,7 +2628,7 @@ static void update_tx_fqids(struct dpaa2_eth_priv *priv)
                }
        }
 
-       priv->enqueue = dpaa2_eth_enqueue_fq;
+       priv->enqueue = dpaa2_eth_enqueue_fq_multiple;
 
        return;
 
@@ -2679,8 +2702,10 @@ static int setup_dpni(struct fsl_mc_device *ls_dev)
 
        priv->cls_rules = devm_kzalloc(dev, sizeof(struct dpaa2_eth_cls_rule) *
                                       dpaa2_eth_fs_count(priv), GFP_KERNEL);
-       if (!priv->cls_rules)
+       if (!priv->cls_rules) {
+               err = -ENOMEM;
                goto close;
+       }
 
        return 0;
 
index 7635db3..43cd840 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
 /* Copyright 2014-2016 Freescale Semiconductor Inc.
- * Copyright 2016 NXP
+ * Copyright 2016-2020 NXP
  */
 
 #ifndef __DPAA2_ETH_H
@@ -288,6 +288,8 @@ struct dpaa2_eth_ch_stats {
        __u64 xdp_tx;
        __u64 xdp_tx_err;
        __u64 xdp_redirect;
+       /* Must be last, does not show up in ethtool stats */
+       __u64 frames;
 };
 
 /* Maximum number of queues associated with a DPNI */
@@ -325,6 +327,8 @@ struct dpaa2_eth_fq {
                        const struct dpaa2_fd *fd,
                        struct dpaa2_eth_fq *fq);
        struct dpaa2_eth_fq_stats stats;
+
+       struct dpaa2_fd xdp_fds[DEV_MAP_BULK_SIZE];
 };
 
 struct dpaa2_eth_ch_xdp {
@@ -371,7 +375,9 @@ struct dpaa2_eth_priv {
        struct dpaa2_eth_fq fq[DPAA2_ETH_MAX_QUEUES];
        int (*enqueue)(struct dpaa2_eth_priv *priv,
                       struct dpaa2_eth_fq *fq,
-                      struct dpaa2_fd *fd, u8 prio);
+                      struct dpaa2_fd *fd, u8 prio,
+                      u32 num_frames,
+                      int *frames_enqueued);
 
        u8 num_channels;
        struct dpaa2_eth_channel *channel[DPAA2_ETH_MAX_DPCONS];
index 94347c6..bd13ee4 100644 (file)
@@ -277,7 +277,7 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev,
        /* Per-channel stats */
        for (k = 0; k < priv->num_channels; k++) {
                ch_stats = &priv->channel[k]->stats;
-               for (j = 0; j < sizeof(*ch_stats) / sizeof(__u64); j++)
+               for (j = 0; j < sizeof(*ch_stats) / sizeof(__u64) - 1; j++)
                        *((__u64 *)data + i + j) += *((__u64 *)ch_stats + j);
        }
        i += j;
index ccf2611..298c557 100644 (file)
@@ -756,6 +756,9 @@ void enetc_get_si_caps(struct enetc_si *si)
 
        if (val & ENETC_SIPCAPR0_QBV)
                si->hw_features |= ENETC_SI_F_QBV;
+
+       if (val & ENETC_SIPCAPR0_PSFP)
+               si->hw_features |= ENETC_SI_F_PSFP;
 }
 
 static int enetc_dma_alloc_bdr(struct enetc_bdr *r, size_t bd_size)
@@ -1518,6 +1521,8 @@ int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
                return enetc_setup_tc_cbs(ndev, type_data);
        case TC_SETUP_QDISC_ETF:
                return enetc_setup_tc_txtime(ndev, type_data);
+       case TC_SETUP_BLOCK:
+               return enetc_setup_tc_psfp(ndev, type_data);
        default:
                return -EOPNOTSUPP;
        }
@@ -1567,15 +1572,42 @@ static int enetc_set_rss(struct net_device *ndev, int en)
        return 0;
 }
 
+static int enetc_set_psfp(struct net_device *ndev, int en)
+{
+       struct enetc_ndev_priv *priv = netdev_priv(ndev);
+       int err;
+
+       if (en) {
+               err = enetc_psfp_enable(priv);
+               if (err)
+                       return err;
+
+               priv->active_offloads |= ENETC_F_QCI;
+               return 0;
+       }
+
+       err = enetc_psfp_disable(priv);
+       if (err)
+               return err;
+
+       priv->active_offloads &= ~ENETC_F_QCI;
+
+       return 0;
+}
+
 int enetc_set_features(struct net_device *ndev,
                       netdev_features_t features)
 {
        netdev_features_t changed = ndev->features ^ features;
+       int err = 0;
 
        if (changed & NETIF_F_RXHASH)
                enetc_set_rss(ndev, !!(features & NETIF_F_RXHASH));
 
-       return 0;
+       if (changed & NETIF_F_HW_TC)
+               err = enetc_set_psfp(ndev, !!(features & NETIF_F_HW_TC));
+
+       return err;
 }
 
 #ifdef CONFIG_FSL_ENETC_PTP_CLOCK
index 56c43f3..b705464 100644 (file)
@@ -151,6 +151,7 @@ enum enetc_errata {
 };
 
 #define ENETC_SI_F_QBV BIT(0)
+#define ENETC_SI_F_PSFP BIT(1)
 
 /* PCI IEP device data */
 struct enetc_si {
@@ -203,12 +204,20 @@ struct enetc_cls_rule {
 };
 
 #define ENETC_MAX_BDR_INT      2 /* fixed to max # of available cpus */
+struct psfp_cap {
+       u32 max_streamid;
+       u32 max_psfp_filter;
+       u32 max_psfp_gate;
+       u32 max_psfp_gatelist;
+       u32 max_psfp_meter;
+};
 
 /* TODO: more hardware offloads */
 enum enetc_active_offloads {
        ENETC_F_RX_TSTAMP       = BIT(0),
        ENETC_F_TX_TSTAMP       = BIT(1),
        ENETC_F_QBV             = BIT(2),
+       ENETC_F_QCI             = BIT(3),
 };
 
 struct enetc_ndev_priv {
@@ -231,6 +240,8 @@ struct enetc_ndev_priv {
 
        struct enetc_cls_rule *cls_rules;
 
+       struct psfp_cap psfp_cap;
+
        struct device_node *phy_node;
        phy_interface_t if_mode;
 };
@@ -289,9 +300,84 @@ int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data);
 void enetc_sched_speed_set(struct net_device *ndev);
 int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data);
 int enetc_setup_tc_txtime(struct net_device *ndev, void *type_data);
+int enetc_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
+                           void *cb_priv);
+int enetc_setup_tc_psfp(struct net_device *ndev, void *type_data);
+int enetc_psfp_init(struct enetc_ndev_priv *priv);
+int enetc_psfp_clean(struct enetc_ndev_priv *priv);
+
+static inline void enetc_get_max_cap(struct enetc_ndev_priv *priv)
+{
+       u32 reg;
+
+       reg = enetc_port_rd(&priv->si->hw, ENETC_PSIDCAPR);
+       priv->psfp_cap.max_streamid = reg & ENETC_PSIDCAPR_MSK;
+       /* Port stream filter capability */
+       reg = enetc_port_rd(&priv->si->hw, ENETC_PSFCAPR);
+       priv->psfp_cap.max_psfp_filter = reg & ENETC_PSFCAPR_MSK;
+       /* Port stream gate capability */
+       reg = enetc_port_rd(&priv->si->hw, ENETC_PSGCAPR);
+       priv->psfp_cap.max_psfp_gate = (reg & ENETC_PSGCAPR_SGIT_MSK);
+       priv->psfp_cap.max_psfp_gatelist = (reg & ENETC_PSGCAPR_GCL_MSK) >> 16;
+       /* Port flow meter capability */
+       reg = enetc_port_rd(&priv->si->hw, ENETC_PFMCAPR);
+       priv->psfp_cap.max_psfp_meter = reg & ENETC_PFMCAPR_MSK;
+}
+
+static inline int enetc_psfp_enable(struct enetc_ndev_priv *priv)
+{
+       struct enetc_hw *hw = &priv->si->hw;
+       int err;
+
+       enetc_get_max_cap(priv);
+
+       err = enetc_psfp_init(priv);
+       if (err)
+               return err;
+
+       enetc_wr(hw, ENETC_PPSFPMR, enetc_rd(hw, ENETC_PPSFPMR) |
+                ENETC_PPSFPMR_PSFPEN | ENETC_PPSFPMR_VS |
+                ENETC_PPSFPMR_PVC | ENETC_PPSFPMR_PVZC);
+
+       return 0;
+}
+
+static inline int enetc_psfp_disable(struct enetc_ndev_priv *priv)
+{
+       struct enetc_hw *hw = &priv->si->hw;
+       int err;
+
+       err = enetc_psfp_clean(priv);
+       if (err)
+               return err;
+
+       enetc_wr(hw, ENETC_PPSFPMR, enetc_rd(hw, ENETC_PPSFPMR) &
+                ~ENETC_PPSFPMR_PSFPEN & ~ENETC_PPSFPMR_VS &
+                ~ENETC_PPSFPMR_PVC & ~ENETC_PPSFPMR_PVZC);
+
+       memset(&priv->psfp_cap, 0, sizeof(struct psfp_cap));
+
+       return 0;
+}
+
 #else
 #define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP
 #define enetc_sched_speed_set(ndev) (void)0
 #define enetc_setup_tc_cbs(ndev, type_data) -EOPNOTSUPP
 #define enetc_setup_tc_txtime(ndev, type_data) -EOPNOTSUPP
+#define enetc_setup_tc_psfp(ndev, type_data) -EOPNOTSUPP
+#define enetc_setup_tc_block_cb NULL
+
+#define enetc_get_max_cap(p)           \
+       memset(&((p)->psfp_cap), 0, sizeof(struct psfp_cap))
+
+static inline int enetc_psfp_enable(struct enetc_ndev_priv *priv)
+{
+       return 0;
+}
+
+static inline int enetc_psfp_disable(struct enetc_ndev_priv *priv)
+{
+       return 0;
+}
 #endif
index 2a65231..6314051 100644 (file)
@@ -19,6 +19,7 @@
 #define ENETC_SICTR1   0x1c
 #define ENETC_SIPCAPR0 0x20
 #define ENETC_SIPCAPR0_QBV     BIT(4)
+#define ENETC_SIPCAPR0_PSFP    BIT(9)
 #define ENETC_SIPCAPR0_RSS     BIT(8)
 #define ENETC_SIPCAPR1 0x24
 #define ENETC_SITGTGR  0x30
@@ -228,6 +229,15 @@ enum enetc_bdr_type {TX, RX};
 #define ENETC_PM0_IFM_RLP      (BIT(5) | BIT(11))
 #define ENETC_PM0_IFM_RGAUTO   (BIT(15) | ENETC_PMO_IFM_RG | BIT(1))
 #define ENETC_PM0_IFM_XGMII    BIT(12)
+#define ENETC_PSIDCAPR         0x1b08
+#define ENETC_PSIDCAPR_MSK     GENMASK(15, 0)
+#define ENETC_PSFCAPR          0x1b18
+#define ENETC_PSFCAPR_MSK      GENMASK(15, 0)
+#define ENETC_PSGCAPR          0x1b28
+#define ENETC_PSGCAPR_GCL_MSK  GENMASK(18, 16)
+#define ENETC_PSGCAPR_SGIT_MSK GENMASK(15, 0)
+#define ENETC_PFMCAPR          0x1b38
+#define ENETC_PFMCAPR_MSK      GENMASK(15, 0)
 
 /* MAC counters */
 #define ENETC_PM0_REOCT                0x8100
@@ -557,6 +567,9 @@ enum bdcr_cmd_class {
        BDCR_CMD_RFS,
        BDCR_CMD_PORT_GCL,
        BDCR_CMD_RECV_CLASSIFIER,
+       BDCR_CMD_STREAM_IDENTIFY,
+       BDCR_CMD_STREAM_FILTER,
+       BDCR_CMD_STREAM_GCL,
        __BDCR_CMD_MAX_LEN,
        BDCR_CMD_MAX_LEN = __BDCR_CMD_MAX_LEN - 1,
 };
@@ -588,13 +601,152 @@ struct tgs_gcl_data {
        struct gce      entry[];
 };
 
+/* class 7, command 0, Stream Identity Entry Configuration */
+struct streamid_conf {
+       __le32  stream_handle;  /* init gate value */
+       __le32  iports;
+               u8      id_type;
+               u8      oui[3];
+               u8      res[3];
+               u8      en;
+};
+
+#define ENETC_CBDR_SID_VID_MASK 0xfff
+#define ENETC_CBDR_SID_VIDM BIT(12)
+#define ENETC_CBDR_SID_TG_MASK 0xc000
+/* streamid_conf address point to this data space */
+struct streamid_data {
+       union {
+               u8 dmac[6];
+               u8 smac[6];
+       };
+       u16     vid_vidm_tg;
+};
+
+#define ENETC_CBDR_SFI_PRI_MASK 0x7
+#define ENETC_CBDR_SFI_PRIM            BIT(3)
+#define ENETC_CBDR_SFI_BLOV            BIT(4)
+#define ENETC_CBDR_SFI_BLEN            BIT(5)
+#define ENETC_CBDR_SFI_MSDUEN  BIT(6)
+#define ENETC_CBDR_SFI_FMITEN  BIT(7)
+#define ENETC_CBDR_SFI_ENABLE  BIT(7)
+/* class 8, command 0, Stream Filter Instance, Short Format */
+struct sfi_conf {
+       __le32  stream_handle;
+               u8      multi;
+               u8      res[2];
+               u8      sthm;
+       /* Max Service Data Unit or Flow Meter Instance Table index.
+        * Depending on the value of FLT this represents either Max
+        * Service Data Unit (max frame size) allowed by the filter
+        * entry or is an index into the Flow Meter Instance table
+        * index identifying the policer which will be used to police
+        * it.
+        */
+       __le16  fm_inst_table_index;
+       __le16  msdu;
+       __le16  sg_inst_table_index;
+               u8      res1[2];
+       __le32  input_ports;
+               u8      res2[3];
+               u8      en;
+};
+
+/* class 8, command 2 stream Filter Instance status query short format
+ * command no need structure define
+ * Stream Filter Instance Query Statistics Response data
+ */
+struct sfi_counter_data {
+       u32 matchl;
+       u32 matchh;
+       u32 msdu_dropl;
+       u32 msdu_droph;
+       u32 stream_gate_dropl;
+       u32 stream_gate_droph;
+       u32 flow_meter_dropl;
+       u32 flow_meter_droph;
+};
+
+#define ENETC_CBDR_SGI_OIPV_MASK 0x7
+#define ENETC_CBDR_SGI_OIPV_EN BIT(3)
+#define ENETC_CBDR_SGI_CGTST   BIT(6)
+#define ENETC_CBDR_SGI_OGTST   BIT(7)
+#define ENETC_CBDR_SGI_CFG_CHG  BIT(1)
+#define ENETC_CBDR_SGI_CFG_PND  BIT(2)
+#define ENETC_CBDR_SGI_OEX             BIT(4)
+#define ENETC_CBDR_SGI_OEXEN   BIT(5)
+#define ENETC_CBDR_SGI_IRX             BIT(6)
+#define ENETC_CBDR_SGI_IRXEN   BIT(7)
+#define ENETC_CBDR_SGI_ACLLEN_MASK 0x3
+#define ENETC_CBDR_SGI_OCLLEN_MASK 0xc
+#define        ENETC_CBDR_SGI_EN               BIT(7)
+/* class 9, command 0, Stream Gate Instance Table, Short Format
+ * class 9, command 2, Stream Gate Instance Table entry query write back
+ * Short Format
+ */
+struct sgi_table {
+       u8      res[8];
+       u8      oipv;
+       u8      res0[2];
+       u8      ocgtst;
+       u8      res1[7];
+       u8      gset;
+       u8      oacl_len;
+       u8      res2[2];
+       u8      en;
+};
+
+#define ENETC_CBDR_SGI_AIPV_MASK 0x7
+#define ENETC_CBDR_SGI_AIPV_EN BIT(3)
+#define ENETC_CBDR_SGI_AGTST   BIT(7)
+
+/* class 9, command 1, Stream Gate Control List, Long Format */
+struct sgcl_conf {
+       u8      aipv;
+       u8      res[2];
+       u8      agtst;
+       u8      res1[4];
+       union {
+               struct {
+                       u8 res2[4];
+                       u8 acl_len;
+                       u8 res3[3];
+               };
+               u8 cct[8]; /* Config change time */
+       };
+};
+
+#define ENETC_CBDR_SGL_IOMEN   BIT(0)
+#define ENETC_CBDR_SGL_IPVEN   BIT(3)
+#define ENETC_CBDR_SGL_GTST            BIT(4)
+#define ENETC_CBDR_SGL_IPV_MASK 0xe
+/* Stream Gate Control List Entry */
+struct sgce {
+       u32     interval;
+       u8      msdu[3];
+       u8      multi;
+};
+
+/* stream control list class 9 , cmd 1 data buffer */
+struct sgcl_data {
+       u32             btl;
+       u32             bth;
+       u32             ct;
+       u32             cte;
+       struct sgce     sgcl[0];
+};
+
 struct enetc_cbd {
        union{
+               struct sfi_conf sfi_conf;
+               struct sgi_table sgi_table;
                struct {
                        __le32  addr[2];
                        union {
                                __le32  opt[4];
                                struct tgs_gcl_conf     gcl_conf;
+                               struct streamid_conf    sid_set;
+                               struct sgcl_conf        sgcl_conf;
                        };
                };      /* Long format */
                __le32 data[6];
@@ -621,3 +773,10 @@ struct enetc_cbd {
 /* Port time specific departure */
 #define ENETC_PTCTSDR(n)       (0x1210 + 4 * (n))
 #define ENETC_TSDE             BIT(31)
+
+/* PSFP setting */
+#define ENETC_PPSFPMR 0x11b00
+#define ENETC_PPSFPMR_PSFPEN BIT(0)
+#define ENETC_PPSFPMR_VS BIT(1)
+#define ENETC_PPSFPMR_PVC BIT(2)
+#define ENETC_PPSFPMR_PVZC BIT(3)
index ebc635f..15f37c5 100644 (file)
@@ -74,8 +74,8 @@ err_pci_mem_reg:
        pci_disable_device(pdev);
 err_pci_enable:
 err_mdiobus_alloc:
-       iounmap(port_regs);
 err_hw_alloc:
+       iounmap(port_regs);
 err_ioremap:
        return err;
 }
index 85e2b74..824d211 100644 (file)
@@ -50,21 +50,6 @@ static void enetc_set_vlan_promisc(struct enetc_hw *hw, char si_map)
        enetc_port_wr(hw, ENETC_PSIPVMR, ENETC_PSIPVMR_SET_VP(si_map) | val);
 }
 
-static bool enetc_si_vlan_promisc_is_on(struct enetc_pf *pf, int si_idx)
-{
-       return pf->vlan_promisc_simap & BIT(si_idx);
-}
-
-static bool enetc_vlan_filter_is_on(struct enetc_pf *pf)
-{
-       int i;
-
-       for_each_set_bit(i, pf->active_vlans, VLAN_N_VID)
-               return true;
-
-       return false;
-}
-
 static void enetc_enable_si_vlan_promisc(struct enetc_pf *pf, int si_idx)
 {
        pf->vlan_promisc_simap |= BIT(si_idx);
@@ -204,6 +189,7 @@ static void enetc_pf_set_rx_mode(struct net_device *ndev)
 {
        struct enetc_ndev_priv *priv = netdev_priv(ndev);
        struct enetc_pf *pf = enetc_si_priv(priv->si);
+       char vlan_promisc_simap = pf->vlan_promisc_simap;
        struct enetc_hw *hw = &priv->si->hw;
        bool uprom = false, mprom = false;
        struct enetc_mac_filter *filter;
@@ -216,16 +202,16 @@ static void enetc_pf_set_rx_mode(struct net_device *ndev)
                psipmr = ENETC_PSIPMR_SET_UP(0) | ENETC_PSIPMR_SET_MP(0);
                uprom = true;
                mprom = true;
-               /* enable VLAN promisc mode for SI0 */
-               if (!enetc_si_vlan_promisc_is_on(pf, 0))
-                       enetc_enable_si_vlan_promisc(pf, 0);
-
+               /* Enable VLAN promiscuous mode for SI0 (PF) */
+               vlan_promisc_simap |= BIT(0);
        } else if (ndev->flags & IFF_ALLMULTI) {
                /* enable multi cast promisc mode for SI0 (PF) */
                psipmr = ENETC_PSIPMR_SET_MP(0);
                mprom = true;
        }
 
+       enetc_set_vlan_promisc(&pf->si->hw, vlan_promisc_simap);
+
        /* first 2 filter entries belong to PF */
        if (!uprom) {
                /* Update unicast filters */
@@ -306,9 +292,6 @@ static int enetc_vlan_rx_add_vid(struct net_device *ndev, __be16 prot, u16 vid)
        struct enetc_pf *pf = enetc_si_priv(priv->si);
        int idx;
 
-       if (enetc_si_vlan_promisc_is_on(pf, 0))
-               enetc_disable_si_vlan_promisc(pf, 0);
-
        __set_bit(vid, pf->active_vlans);
 
        idx = enetc_vid_hash_idx(vid);
@@ -326,9 +309,6 @@ static int enetc_vlan_rx_del_vid(struct net_device *ndev, __be16 prot, u16 vid)
        __clear_bit(vid, pf->active_vlans);
        enetc_sync_vlan_ht_filter(pf, true);
 
-       if (!enetc_vlan_filter_is_on(pf))
-               enetc_enable_si_vlan_promisc(pf, 0);
-
        return 0;
 }
 
@@ -677,6 +657,15 @@ static int enetc_pf_set_features(struct net_device *ndev,
                enetc_enable_txvlan(&priv->si->hw, 0,
                                    !!(features & NETIF_F_HW_VLAN_CTAG_TX));
 
+       if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) {
+               struct enetc_pf *pf = enetc_si_priv(priv->si);
+
+               if (!!(features & NETIF_F_HW_VLAN_CTAG_FILTER))
+                       enetc_disable_si_vlan_promisc(pf, 0);
+               else
+                       enetc_enable_si_vlan_promisc(pf, 0);
+       }
+
        if (changed & NETIF_F_LOOPBACK)
                enetc_set_loopback(ndev, !!(features & NETIF_F_LOOPBACK));
 
@@ -719,12 +708,11 @@ static void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
 
        ndev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
                            NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
-                           NETIF_F_LOOPBACK;
+                           NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_LOOPBACK;
        ndev->features = NETIF_F_HIGHDMA | NETIF_F_SG |
                         NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
                         NETIF_F_HW_VLAN_CTAG_TX |
-                        NETIF_F_HW_VLAN_CTAG_RX |
-                        NETIF_F_HW_VLAN_CTAG_FILTER;
+                        NETIF_F_HW_VLAN_CTAG_RX;
 
        if (si->num_rss)
                ndev->hw_features |= NETIF_F_RXHASH;
@@ -739,6 +727,12 @@ static void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
        if (si->hw_features & ENETC_SI_F_QBV)
                priv->active_offloads |= ENETC_F_QBV;
 
+       if (si->hw_features & ENETC_SI_F_PSFP && !enetc_psfp_enable(priv)) {
+               priv->active_offloads |= ENETC_F_QCI;
+               ndev->features |= NETIF_F_HW_TC;
+               ndev->hw_features |= NETIF_F_HW_TC;
+       }
+
        /* pick up primary MAC address from SI */
        enetc_get_primary_mac_addr(&si->hw, ndev->dev_addr);
 }
index 0c6bf3a..fd3df19 100644 (file)
@@ -5,6 +5,9 @@
 
 #include <net/pkt_sched.h>
 #include <linux/math64.h>
+#include <linux/refcount.h>
+#include <net/pkt_cls.h>
+#include <net/tc_act/tc_gate.h>
 
 static u16 enetc_get_max_gcl_len(struct enetc_hw *hw)
 {
@@ -331,3 +334,1103 @@ int enetc_setup_tc_txtime(struct net_device *ndev, void *type_data)
 
        return 0;
 }
+
+enum streamid_type {
+       STREAMID_TYPE_RESERVED = 0,
+       STREAMID_TYPE_NULL,
+       STREAMID_TYPE_SMAC,
+};
+
+enum streamid_vlan_tagged {
+       STREAMID_VLAN_RESERVED = 0,
+       STREAMID_VLAN_TAGGED,
+       STREAMID_VLAN_UNTAGGED,
+       STREAMID_VLAN_ALL,
+};
+
+#define ENETC_PSFP_WILDCARD -1
+#define HANDLE_OFFSET 100
+
+enum forward_type {
+       FILTER_ACTION_TYPE_PSFP = BIT(0),
+       FILTER_ACTION_TYPE_ACL = BIT(1),
+       FILTER_ACTION_TYPE_BOTH = GENMASK(1, 0),
+};
+
+/* This is for limit output type for input actions */
+struct actions_fwd {
+       u64 actions;
+       u64 keys;       /* include the must needed keys */
+       enum forward_type output;
+};
+
+struct psfp_streamfilter_counters {
+       u64 matching_frames_count;
+       u64 passing_frames_count;
+       u64 not_passing_frames_count;
+       u64 passing_sdu_count;
+       u64 not_passing_sdu_count;
+       u64 red_frames_count;
+};
+
+struct enetc_streamid {
+       u32 index;
+       union {
+               u8 src_mac[6];
+               u8 dst_mac[6];
+       };
+       u8 filtertype;
+       u16 vid;
+       u8 tagged;
+       s32 handle;
+};
+
+struct enetc_psfp_filter {
+       u32 index;
+       s32 handle;
+       s8 prio;
+       u32 gate_id;
+       s32 meter_id;
+       refcount_t refcount;
+       struct hlist_node node;
+};
+
+struct enetc_psfp_gate {
+       u32 index;
+       s8 init_ipv;
+       u64 basetime;
+       u64 cycletime;
+       u64 cycletimext;
+       u32 num_entries;
+       refcount_t refcount;
+       struct hlist_node node;
+       struct action_gate_entry entries[0];
+};
+
+struct enetc_stream_filter {
+       struct enetc_streamid sid;
+       u32 sfi_index;
+       u32 sgi_index;
+       struct flow_stats stats;
+       struct hlist_node node;
+};
+
+struct enetc_psfp {
+       unsigned long dev_bitmap;
+       unsigned long *psfp_sfi_bitmap;
+       struct hlist_head stream_list;
+       struct hlist_head psfp_filter_list;
+       struct hlist_head psfp_gate_list;
+       spinlock_t psfp_lock; /* spinlock for the struct enetc_psfp r/w */
+};
+
+static struct actions_fwd enetc_act_fwd[] = {
+       {
+               BIT(FLOW_ACTION_GATE),
+               BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS),
+               FILTER_ACTION_TYPE_PSFP
+       },
+       /* example for ACL actions */
+       {
+               BIT(FLOW_ACTION_DROP),
+               0,
+               FILTER_ACTION_TYPE_ACL
+       }
+};
+
+static struct enetc_psfp epsfp = {
+       .psfp_sfi_bitmap = NULL,
+};
+
+static LIST_HEAD(enetc_block_cb_list);
+
+static inline int enetc_get_port(struct enetc_ndev_priv *priv)
+{
+       return priv->si->pdev->devfn & 0x7;
+}
+
+/* Stream Identity Entry Set Descriptor */
+static int enetc_streamid_hw_set(struct enetc_ndev_priv *priv,
+                                struct enetc_streamid *sid,
+                                u8 enable)
+{
+       struct enetc_cbd cbd = {.cmd = 0};
+       struct streamid_data *si_data;
+       struct streamid_conf *si_conf;
+       u16 data_size;
+       dma_addr_t dma;
+       int err;
+
+       if (sid->index >= priv->psfp_cap.max_streamid)
+               return -EINVAL;
+
+       if (sid->filtertype != STREAMID_TYPE_NULL &&
+           sid->filtertype != STREAMID_TYPE_SMAC)
+               return -EOPNOTSUPP;
+
+       /* Disable operation before enable */
+       cbd.index = cpu_to_le16((u16)sid->index);
+       cbd.cls = BDCR_CMD_STREAM_IDENTIFY;
+       cbd.status_flags = 0;
+
+       data_size = sizeof(struct streamid_data);
+       si_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL);
+       cbd.length = cpu_to_le16(data_size);
+
+       dma = dma_map_single(&priv->si->pdev->dev, si_data,
+                            data_size, DMA_FROM_DEVICE);
+       if (dma_mapping_error(&priv->si->pdev->dev, dma)) {
+               netdev_err(priv->si->ndev, "DMA mapping failed!\n");
+               kfree(si_data);
+               return -ENOMEM;
+       }
+
+       cbd.addr[0] = lower_32_bits(dma);
+       cbd.addr[1] = upper_32_bits(dma);
+       memset(si_data->dmac, 0xff, ETH_ALEN);
+       si_data->vid_vidm_tg =
+               cpu_to_le16(ENETC_CBDR_SID_VID_MASK
+                           + ((0x3 << 14) | ENETC_CBDR_SID_VIDM));
+
+       si_conf = &cbd.sid_set;
+       /* Only one port supported for one entry, set itself */
+       si_conf->iports = 1 << enetc_get_port(priv);
+       si_conf->id_type = 1;
+       si_conf->oui[2] = 0x0;
+       si_conf->oui[1] = 0x80;
+       si_conf->oui[0] = 0xC2;
+
+       err = enetc_send_cmd(priv->si, &cbd);
+       if (err)
+               return -EINVAL;
+
+       if (!enable) {
+               kfree(si_data);
+               return 0;
+       }
+
+       /* Enable the entry overwrite again incase space flushed by hardware */
+       memset(&cbd, 0, sizeof(cbd));
+
+       cbd.index = cpu_to_le16((u16)sid->index);
+       cbd.cmd = 0;
+       cbd.cls = BDCR_CMD_STREAM_IDENTIFY;
+       cbd.status_flags = 0;
+
+       si_conf->en = 0x80;
+       si_conf->stream_handle = cpu_to_le32(sid->handle);
+       si_conf->iports = 1 << enetc_get_port(priv);
+       si_conf->id_type = sid->filtertype;
+       si_conf->oui[2] = 0x0;
+       si_conf->oui[1] = 0x80;
+       si_conf->oui[0] = 0xC2;
+
+       memset(si_data, 0, data_size);
+
+       cbd.length = cpu_to_le16(data_size);
+
+       cbd.addr[0] = lower_32_bits(dma);
+       cbd.addr[1] = upper_32_bits(dma);
+
+       /* VIDM default to be 1.
+        * VID Match. If set (b1) then the VID must match, otherwise
+        * any VID is considered a match. VIDM setting is only used
+        * when TG is set to b01.
+        */
+       if (si_conf->id_type == STREAMID_TYPE_NULL) {
+               ether_addr_copy(si_data->dmac, sid->dst_mac);
+               si_data->vid_vidm_tg =
+               cpu_to_le16((sid->vid & ENETC_CBDR_SID_VID_MASK) +
+                           ((((u16)(sid->tagged) & 0x3) << 14)
+                            | ENETC_CBDR_SID_VIDM));
+       } else if (si_conf->id_type == STREAMID_TYPE_SMAC) {
+               ether_addr_copy(si_data->smac, sid->src_mac);
+               si_data->vid_vidm_tg =
+               cpu_to_le16((sid->vid & ENETC_CBDR_SID_VID_MASK) +
+                           ((((u16)(sid->tagged) & 0x3) << 14)
+                            | ENETC_CBDR_SID_VIDM));
+       }
+
+       err = enetc_send_cmd(priv->si, &cbd);
+       kfree(si_data);
+
+       return err;
+}
+
+/* Stream Filter Instance Set Descriptor */
+static int enetc_streamfilter_hw_set(struct enetc_ndev_priv *priv,
+                                    struct enetc_psfp_filter *sfi,
+                                    u8 enable)
+{
+       struct enetc_cbd cbd = {.cmd = 0};
+       struct sfi_conf *sfi_config;
+
+       cbd.index = cpu_to_le16(sfi->index);
+       cbd.cls = BDCR_CMD_STREAM_FILTER;
+       cbd.status_flags = 0x80;
+       cbd.length = cpu_to_le16(1);
+
+       sfi_config = &cbd.sfi_conf;
+       if (!enable)
+               goto exit;
+
+       sfi_config->en = 0x80;
+
+       if (sfi->handle >= 0) {
+               sfi_config->stream_handle =
+                       cpu_to_le32(sfi->handle);
+               sfi_config->sthm |= 0x80;
+       }
+
+       sfi_config->sg_inst_table_index = cpu_to_le16(sfi->gate_id);
+       sfi_config->input_ports = 1 << enetc_get_port(priv);
+
+       /* The priority value which may be matched against the
+        * frame’s priority value to determine a match for this entry.
+        */
+       if (sfi->prio >= 0)
+               sfi_config->multi |= (sfi->prio & 0x7) | 0x8;
+
+       /* Filter Type. Identifies the contents of the MSDU/FM_INST_INDEX
+        * field as being either an MSDU value or an index into the Flow
+        * Meter Instance table.
+        * TODO: no limit max sdu
+        */
+
+       if (sfi->meter_id >= 0) {
+               sfi_config->fm_inst_table_index = cpu_to_le16(sfi->meter_id);
+               sfi_config->multi |= 0x80;
+       }
+
+exit:
+       return enetc_send_cmd(priv->si, &cbd);
+}
+
+static int enetc_streamcounter_hw_get(struct enetc_ndev_priv *priv,
+                                     u32 index,
+                                     struct psfp_streamfilter_counters *cnt)
+{
+       struct enetc_cbd cbd = { .cmd = 2 };
+       struct sfi_counter_data *data_buf;
+       dma_addr_t dma;
+       u16 data_size;
+       int err;
+
+       cbd.index = cpu_to_le16((u16)index);
+       cbd.cmd = 2;
+       cbd.cls = BDCR_CMD_STREAM_FILTER;
+       cbd.status_flags = 0;
+
+       data_size = sizeof(struct sfi_counter_data);
+       data_buf = kzalloc(data_size, __GFP_DMA | GFP_KERNEL);
+       if (!data_buf)
+               return -ENOMEM;
+
+       dma = dma_map_single(&priv->si->pdev->dev, data_buf,
+                            data_size, DMA_FROM_DEVICE);
+       if (dma_mapping_error(&priv->si->pdev->dev, dma)) {
+               netdev_err(priv->si->ndev, "DMA mapping failed!\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+       cbd.addr[0] = lower_32_bits(dma);
+       cbd.addr[1] = upper_32_bits(dma);
+
+       cbd.length = cpu_to_le16(data_size);
+
+       err = enetc_send_cmd(priv->si, &cbd);
+       if (err)
+               goto exit;
+
+       cnt->matching_frames_count =
+                       ((u64)le32_to_cpu(data_buf->matchh) << 32)
+                       + data_buf->matchl;
+
+       cnt->not_passing_sdu_count =
+                       ((u64)le32_to_cpu(data_buf->msdu_droph) << 32)
+                       + data_buf->msdu_dropl;
+
+       cnt->passing_sdu_count = cnt->matching_frames_count
+                               - cnt->not_passing_sdu_count;
+
+       cnt->not_passing_frames_count =
+               ((u64)le32_to_cpu(data_buf->stream_gate_droph) << 32)
+               + le32_to_cpu(data_buf->stream_gate_dropl);
+
+       cnt->passing_frames_count = cnt->matching_frames_count
+                               - cnt->not_passing_sdu_count
+                               - cnt->not_passing_frames_count;
+
+       cnt->red_frames_count =
+               ((u64)le32_to_cpu(data_buf->flow_meter_droph) << 32)
+               + le32_to_cpu(data_buf->flow_meter_dropl);
+
+exit:
+       kfree(data_buf);
+       return err;
+}
+
+static u64 get_ptp_now(struct enetc_hw *hw)
+{
+       u64 now_lo, now_hi, now;
+
+       now_lo = enetc_rd(hw, ENETC_SICTR0);
+       now_hi = enetc_rd(hw, ENETC_SICTR1);
+       now = now_lo | now_hi << 32;
+
+       return now;
+}
+
+static int get_start_ns(u64 now, u64 cycle, u64 *start)
+{
+       u64 n;
+
+       if (!cycle)
+               return -EFAULT;
+
+       n = div64_u64(now, cycle);
+
+       *start = (n + 1) * cycle;
+
+       return 0;
+}
+
+/* Stream Gate Instance Set Descriptor */
+static int enetc_streamgate_hw_set(struct enetc_ndev_priv *priv,
+                                  struct enetc_psfp_gate *sgi,
+                                  u8 enable)
+{
+       struct enetc_cbd cbd = { .cmd = 0 };
+       struct sgi_table *sgi_config;
+       struct sgcl_conf *sgcl_config;
+       struct sgcl_data *sgcl_data;
+       struct sgce *sgce;
+       dma_addr_t dma;
+       u16 data_size;
+       int err, i;
+       u64 now;
+
+       cbd.index = cpu_to_le16(sgi->index);
+       cbd.cmd = 0;
+       cbd.cls = BDCR_CMD_STREAM_GCL;
+       cbd.status_flags = 0x80;
+
+       /* disable */
+       if (!enable)
+               return enetc_send_cmd(priv->si, &cbd);
+
+       if (!sgi->num_entries)
+               return 0;
+
+       if (sgi->num_entries > priv->psfp_cap.max_psfp_gatelist ||
+           !sgi->cycletime)
+               return -EINVAL;
+
+       /* enable */
+       sgi_config = &cbd.sgi_table;
+
+       /* Keep open before gate list start */
+       sgi_config->ocgtst = 0x80;
+
+       sgi_config->oipv = (sgi->init_ipv < 0) ?
+                               0x0 : ((sgi->init_ipv & 0x7) | 0x8);
+
+       sgi_config->en = 0x80;
+
+       /* Basic config */
+       err = enetc_send_cmd(priv->si, &cbd);
+       if (err)
+               return -EINVAL;
+
+       memset(&cbd, 0, sizeof(cbd));
+
+       cbd.index = cpu_to_le16(sgi->index);
+       cbd.cmd = 1;
+       cbd.cls = BDCR_CMD_STREAM_GCL;
+       cbd.status_flags = 0;
+
+       sgcl_config = &cbd.sgcl_conf;
+
+       sgcl_config->acl_len = (sgi->num_entries - 1) & 0x3;
+
+       data_size = struct_size(sgcl_data, sgcl, sgi->num_entries);
+
+       sgcl_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL);
+       if (!sgcl_data)
+               return -ENOMEM;
+
+       cbd.length = cpu_to_le16(data_size);
+
+       dma = dma_map_single(&priv->si->pdev->dev,
+                            sgcl_data, data_size,
+                            DMA_FROM_DEVICE);
+       if (dma_mapping_error(&priv->si->pdev->dev, dma)) {
+               netdev_err(priv->si->ndev, "DMA mapping failed!\n");
+               kfree(sgcl_data);
+               return -ENOMEM;
+       }
+
+       cbd.addr[0] = lower_32_bits(dma);
+       cbd.addr[1] = upper_32_bits(dma);
+
+       sgce = &sgcl_data->sgcl[0];
+
+       sgcl_config->agtst = 0x80;
+
+       sgcl_data->ct = cpu_to_le32(sgi->cycletime);
+       sgcl_data->cte = cpu_to_le32(sgi->cycletimext);
+
+       if (sgi->init_ipv >= 0)
+               sgcl_config->aipv = (sgi->init_ipv & 0x7) | 0x8;
+
+       for (i = 0; i < sgi->num_entries; i++) {
+               struct action_gate_entry *from = &sgi->entries[i];
+               struct sgce *to = &sgce[i];
+
+               if (from->gate_state)
+                       to->multi |= 0x10;
+
+               if (from->ipv >= 0)
+                       to->multi |= ((from->ipv & 0x7) << 5) | 0x08;
+
+               if (from->maxoctets >= 0) {
+                       to->multi |= 0x01;
+                       to->msdu[0] = from->maxoctets & 0xFF;
+                       to->msdu[1] = (from->maxoctets >> 8) & 0xFF;
+                       to->msdu[2] = (from->maxoctets >> 16) & 0xFF;
+               }
+
+               to->interval = cpu_to_le32(from->interval);
+       }
+
+       /* If basetime is less than now, calculate start time */
+       now = get_ptp_now(&priv->si->hw);
+
+       if (sgi->basetime < now) {
+               u64 start;
+
+               err = get_start_ns(now, sgi->cycletime, &start);
+               if (err)
+                       goto exit;
+               sgcl_data->btl = cpu_to_le32(lower_32_bits(start));
+               sgcl_data->bth = cpu_to_le32(upper_32_bits(start));
+       } else {
+               u32 hi, lo;
+
+               hi = upper_32_bits(sgi->basetime);
+               lo = lower_32_bits(sgi->basetime);
+               sgcl_data->bth = cpu_to_le32(hi);
+               sgcl_data->btl = cpu_to_le32(lo);
+       }
+
+       err = enetc_send_cmd(priv->si, &cbd);
+
+exit:
+       kfree(sgcl_data);
+
+       return err;
+}
+
+static struct enetc_stream_filter *enetc_get_stream_by_index(u32 index)
+{
+       struct enetc_stream_filter *f;
+
+       hlist_for_each_entry(f, &epsfp.stream_list, node)
+               if (f->sid.index == index)
+                       return f;
+
+       return NULL;
+}
+
+static struct enetc_psfp_gate *enetc_get_gate_by_index(u32 index)
+{
+       struct enetc_psfp_gate *g;
+
+       hlist_for_each_entry(g, &epsfp.psfp_gate_list, node)
+               if (g->index == index)
+                       return g;
+
+       return NULL;
+}
+
+static struct enetc_psfp_filter *enetc_get_filter_by_index(u32 index)
+{
+       struct enetc_psfp_filter *s;
+
+       hlist_for_each_entry(s, &epsfp.psfp_filter_list, node)
+               if (s->index == index)
+                       return s;
+
+       return NULL;
+}
+
+static struct enetc_psfp_filter
+       *enetc_psfp_check_sfi(struct enetc_psfp_filter *sfi)
+{
+       struct enetc_psfp_filter *s;
+
+       hlist_for_each_entry(s, &epsfp.psfp_filter_list, node)
+               if (s->gate_id == sfi->gate_id &&
+                   s->prio == sfi->prio &&
+                   s->meter_id == sfi->meter_id)
+                       return s;
+
+       return NULL;
+}
+
+static int enetc_get_free_index(struct enetc_ndev_priv *priv)
+{
+       u32 max_size = priv->psfp_cap.max_psfp_filter;
+       unsigned long index;
+
+       index = find_first_zero_bit(epsfp.psfp_sfi_bitmap, max_size);
+       if (index == max_size)
+               return -1;
+
+       return index;
+}
+
+static void stream_filter_unref(struct enetc_ndev_priv *priv, u32 index)
+{
+       struct enetc_psfp_filter *sfi;
+       u8 z;
+
+       sfi = enetc_get_filter_by_index(index);
+       WARN_ON(!sfi);
+       z = refcount_dec_and_test(&sfi->refcount);
+
+       if (z) {
+               enetc_streamfilter_hw_set(priv, sfi, false);
+               hlist_del(&sfi->node);
+               kfree(sfi);
+               clear_bit(index, epsfp.psfp_sfi_bitmap);
+       }
+}
+
+static void stream_gate_unref(struct enetc_ndev_priv *priv, u32 index)
+{
+       struct enetc_psfp_gate *sgi;
+       u8 z;
+
+       sgi = enetc_get_gate_by_index(index);
+       WARN_ON(!sgi);
+       z = refcount_dec_and_test(&sgi->refcount);
+       if (z) {
+               enetc_streamgate_hw_set(priv, sgi, false);
+               hlist_del(&sgi->node);
+               kfree(sgi);
+       }
+}
+
+static void remove_one_chain(struct enetc_ndev_priv *priv,
+                            struct enetc_stream_filter *filter)
+{
+       stream_gate_unref(priv, filter->sgi_index);
+       stream_filter_unref(priv, filter->sfi_index);
+
+       hlist_del(&filter->node);
+       kfree(filter);
+}
+
+static int enetc_psfp_hw_set(struct enetc_ndev_priv *priv,
+                            struct enetc_streamid *sid,
+                            struct enetc_psfp_filter *sfi,
+                            struct enetc_psfp_gate *sgi)
+{
+       int err;
+
+       err = enetc_streamid_hw_set(priv, sid, true);
+       if (err)
+               return err;
+
+       if (sfi) {
+               err = enetc_streamfilter_hw_set(priv, sfi, true);
+               if (err)
+                       goto revert_sid;
+       }
+
+       err = enetc_streamgate_hw_set(priv, sgi, true);
+       if (err)
+               goto revert_sfi;
+
+       return 0;
+
+revert_sfi:
+       if (sfi)
+               enetc_streamfilter_hw_set(priv, sfi, false);
+revert_sid:
+       enetc_streamid_hw_set(priv, sid, false);
+       return err;
+}
+
+static struct actions_fwd *enetc_check_flow_actions(u64 acts,
+                                                   unsigned int inputkeys)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(enetc_act_fwd); i++)
+               if (acts == enetc_act_fwd[i].actions &&
+                   inputkeys & enetc_act_fwd[i].keys)
+                       return &enetc_act_fwd[i];
+
+       return NULL;
+}
+
+static int enetc_psfp_parse_clsflower(struct enetc_ndev_priv *priv,
+                                     struct flow_cls_offload *f)
+{
+       struct flow_rule *rule = flow_cls_offload_flow_rule(f);
+       struct netlink_ext_ack *extack = f->common.extack;
+       struct enetc_stream_filter *filter, *old_filter;
+       struct enetc_psfp_filter *sfi, *old_sfi;
+       struct enetc_psfp_gate *sgi, *old_sgi;
+       struct flow_action_entry *entry;
+       struct action_gate_entry *e;
+       u8 sfi_overwrite = 0;
+       int entries_size;
+       int i, err;
+
+       if (f->common.chain_index >= priv->psfp_cap.max_streamid) {
+               NL_SET_ERR_MSG_MOD(extack, "No Stream identify resource!");
+               return -ENOSPC;
+       }
+
+       flow_action_for_each(i, entry, &rule->action)
+               if (entry->id == FLOW_ACTION_GATE)
+                       break;
+
+       if (entry->id != FLOW_ACTION_GATE)
+               return -EINVAL;
+
+       filter = kzalloc(sizeof(*filter), GFP_KERNEL);
+       if (!filter)
+               return -ENOMEM;
+
+       filter->sid.index = f->common.chain_index;
+
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+               struct flow_match_eth_addrs match;
+
+               flow_rule_match_eth_addrs(rule, &match);
+
+               if (!is_zero_ether_addr(match.mask->dst) &&
+                   !is_zero_ether_addr(match.mask->src)) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "Cannot match on both source and destination MAC");
+                       err = EINVAL;
+                       goto free_filter;
+               }
+
+               if (!is_zero_ether_addr(match.mask->dst)) {
+                       if (!is_broadcast_ether_addr(match.mask->dst)) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "Masked matching on destination MAC not supported");
+                               err = EINVAL;
+                               goto free_filter;
+                       }
+                       ether_addr_copy(filter->sid.dst_mac, match.key->dst);
+                       filter->sid.filtertype = STREAMID_TYPE_NULL;
+               }
+
+               if (!is_zero_ether_addr(match.mask->src)) {
+                       if (!is_broadcast_ether_addr(match.mask->src)) {
+                               NL_SET_ERR_MSG_MOD(extack,
+                                                  "Masked matching on source MAC not supported");
+                               err = EINVAL;
+                               goto free_filter;
+                       }
+                       ether_addr_copy(filter->sid.src_mac, match.key->src);
+                       filter->sid.filtertype = STREAMID_TYPE_SMAC;
+               }
+       } else {
+               NL_SET_ERR_MSG_MOD(extack, "Unsupported, must include ETH_ADDRS");
+               err = EINVAL;
+               goto free_filter;
+       }
+
+       if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+               struct flow_match_vlan match;
+
+               flow_rule_match_vlan(rule, &match);
+               if (match.mask->vlan_priority) {
+                       if (match.mask->vlan_priority !=
+                           (VLAN_PRIO_MASK >> VLAN_PRIO_SHIFT)) {
+                               NL_SET_ERR_MSG_MOD(extack, "Only full mask is supported for VLAN priority");
+                               err = -EINVAL;
+                               goto free_filter;
+                       }
+               }
+
+               if (match.mask->vlan_id) {
+                       if (match.mask->vlan_id != VLAN_VID_MASK) {
+                               NL_SET_ERR_MSG_MOD(extack, "Only full mask is supported for VLAN id");
+                               err = -EINVAL;
+                               goto free_filter;
+                       }
+
+                       filter->sid.vid = match.key->vlan_id;
+                       if (!filter->sid.vid)
+                               filter->sid.tagged = STREAMID_VLAN_UNTAGGED;
+                       else
+                               filter->sid.tagged = STREAMID_VLAN_TAGGED;
+               }
+       } else {
+               filter->sid.tagged = STREAMID_VLAN_ALL;
+       }
+
+       /* parsing gate action */
+       if (entry->gate.index >= priv->psfp_cap.max_psfp_gate) {
+               NL_SET_ERR_MSG_MOD(extack, "No Stream Gate resource!");
+               err = -ENOSPC;
+               goto free_filter;
+       }
+
+       if (entry->gate.num_entries >= priv->psfp_cap.max_psfp_gatelist) {
+               NL_SET_ERR_MSG_MOD(extack, "No Stream Gate resource!");
+               err = -ENOSPC;
+               goto free_filter;
+       }
+
+       entries_size = struct_size(sgi, entries, entry->gate.num_entries);
+       sgi = kzalloc(entries_size, GFP_KERNEL);
+       if (!sgi) {
+               err = -ENOMEM;
+               goto free_filter;
+       }
+
+       refcount_set(&sgi->refcount, 1);
+       sgi->index = entry->gate.index;
+       sgi->init_ipv = entry->gate.prio;
+       sgi->basetime = entry->gate.basetime;
+       sgi->cycletime = entry->gate.cycletime;
+       sgi->num_entries = entry->gate.num_entries;
+
+       e = sgi->entries;
+       for (i = 0; i < entry->gate.num_entries; i++) {
+               e[i].gate_state = entry->gate.entries[i].gate_state;
+               e[i].interval = entry->gate.entries[i].interval;
+               e[i].ipv = entry->gate.entries[i].ipv;
+               e[i].maxoctets = entry->gate.entries[i].maxoctets;
+       }
+
+       filter->sgi_index = sgi->index;
+
+       sfi = kzalloc(sizeof(*sfi), GFP_KERNEL);
+       if (!sfi) {
+               err = -ENOMEM;
+               goto free_gate;
+       }
+
+       refcount_set(&sfi->refcount, 1);
+       sfi->gate_id = sgi->index;
+
+       /* flow meter not support yet */
+       sfi->meter_id = ENETC_PSFP_WILDCARD;
+
+       /* prio ref the filter prio */
+       if (f->common.prio && f->common.prio <= BIT(3))
+               sfi->prio = f->common.prio - 1;
+       else
+               sfi->prio = ENETC_PSFP_WILDCARD;
+
+       old_sfi = enetc_psfp_check_sfi(sfi);
+       if (!old_sfi) {
+               int index;
+
+               index = enetc_get_free_index(priv);
+               if (sfi->handle < 0) {
+                       NL_SET_ERR_MSG_MOD(extack, "No Stream Filter resource!");
+                       err = -ENOSPC;
+                       goto free_sfi;
+               }
+
+               sfi->index = index;
+               sfi->handle = index + HANDLE_OFFSET;
+               /* Update the stream filter handle also */
+               filter->sid.handle = sfi->handle;
+               filter->sfi_index = sfi->index;
+               sfi_overwrite = 0;
+       } else {
+               filter->sfi_index = old_sfi->index;
+               filter->sid.handle = old_sfi->handle;
+               sfi_overwrite = 1;
+       }
+
+       err = enetc_psfp_hw_set(priv, &filter->sid,
+                               sfi_overwrite ? NULL : sfi, sgi);
+       if (err)
+               goto free_sfi;
+
+       spin_lock(&epsfp.psfp_lock);
+       /* Remove the old node if exist and update with a new node */
+       old_sgi = enetc_get_gate_by_index(filter->sgi_index);
+       if (old_sgi) {
+               refcount_set(&sgi->refcount,
+                            refcount_read(&old_sgi->refcount) + 1);
+               hlist_del(&old_sgi->node);
+               kfree(old_sgi);
+       }
+
+       hlist_add_head(&sgi->node, &epsfp.psfp_gate_list);
+
+       if (!old_sfi) {
+               hlist_add_head(&sfi->node, &epsfp.psfp_filter_list);
+               set_bit(sfi->index, epsfp.psfp_sfi_bitmap);
+       } else {
+               kfree(sfi);
+               refcount_inc(&old_sfi->refcount);
+       }
+
+       old_filter = enetc_get_stream_by_index(filter->sid.index);
+       if (old_filter)
+               remove_one_chain(priv, old_filter);
+
+       filter->stats.lastused = jiffies;
+       hlist_add_head(&filter->node, &epsfp.stream_list);
+
+       spin_unlock(&epsfp.psfp_lock);
+
+       return 0;
+
+free_sfi:
+       kfree(sfi);
+free_gate:
+       kfree(sgi);
+free_filter:
+       kfree(filter);
+
+       return err;
+}
+
+static int enetc_config_clsflower(struct enetc_ndev_priv *priv,
+                                 struct flow_cls_offload *cls_flower)
+{
+       struct flow_rule *rule = flow_cls_offload_flow_rule(cls_flower);
+       struct netlink_ext_ack *extack = cls_flower->common.extack;
+       struct flow_dissector *dissector = rule->match.dissector;
+       struct flow_action *action = &rule->action;
+       struct flow_action_entry *entry;
+       struct actions_fwd *fwd;
+       u64 actions = 0;
+       int i, err;
+
+       if (!flow_action_has_entries(action)) {
+               NL_SET_ERR_MSG_MOD(extack, "At least one action is needed");
+               return -EINVAL;
+       }
+
+       flow_action_for_each(i, entry, action)
+               actions |= BIT(entry->id);
+
+       fwd = enetc_check_flow_actions(actions, dissector->used_keys);
+       if (!fwd) {
+               NL_SET_ERR_MSG_MOD(extack, "Unsupported filter type!");
+               return -EOPNOTSUPP;
+       }
+
+       if (fwd->output & FILTER_ACTION_TYPE_PSFP) {
+               err = enetc_psfp_parse_clsflower(priv, cls_flower);
+               if (err) {
+                       NL_SET_ERR_MSG_MOD(extack, "Invalid PSFP inputs");
+                       return err;
+               }
+       } else {
+               NL_SET_ERR_MSG_MOD(extack, "Unsupported actions");
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static int enetc_psfp_destroy_clsflower(struct enetc_ndev_priv *priv,
+                                       struct flow_cls_offload *f)
+{
+       struct enetc_stream_filter *filter;
+       struct netlink_ext_ack *extack = f->common.extack;
+       int err;
+
+       if (f->common.chain_index >= priv->psfp_cap.max_streamid) {
+               NL_SET_ERR_MSG_MOD(extack, "No Stream identify resource!");
+               return -ENOSPC;
+       }
+
+       filter = enetc_get_stream_by_index(f->common.chain_index);
+       if (!filter)
+               return -EINVAL;
+
+       err = enetc_streamid_hw_set(priv, &filter->sid, false);
+       if (err)
+               return err;
+
+       remove_one_chain(priv, filter);
+
+       return 0;
+}
+
+static int enetc_destroy_clsflower(struct enetc_ndev_priv *priv,
+                                  struct flow_cls_offload *f)
+{
+       return enetc_psfp_destroy_clsflower(priv, f);
+}
+
+static int enetc_psfp_get_stats(struct enetc_ndev_priv *priv,
+                               struct flow_cls_offload *f)
+{
+       struct psfp_streamfilter_counters counters = {};
+       struct enetc_stream_filter *filter;
+       struct flow_stats stats = {};
+       int err;
+
+       filter = enetc_get_stream_by_index(f->common.chain_index);
+       if (!filter)
+               return -EINVAL;
+
+       err = enetc_streamcounter_hw_get(priv, filter->sfi_index, &counters);
+       if (err)
+               return -EINVAL;
+
+       spin_lock(&epsfp.psfp_lock);
+       stats.pkts = counters.matching_frames_count - filter->stats.pkts;
+       stats.lastused = filter->stats.lastused;
+       filter->stats.pkts += stats.pkts;
+       spin_unlock(&epsfp.psfp_lock);
+
+       flow_stats_update(&f->stats, 0x0, stats.pkts, stats.lastused,
+                         FLOW_ACTION_HW_STATS_DELAYED);
+
+       return 0;
+}
+
+static int enetc_setup_tc_cls_flower(struct enetc_ndev_priv *priv,
+                                    struct flow_cls_offload *cls_flower)
+{
+       switch (cls_flower->command) {
+       case FLOW_CLS_REPLACE:
+               return enetc_config_clsflower(priv, cls_flower);
+       case FLOW_CLS_DESTROY:
+               return enetc_destroy_clsflower(priv, cls_flower);
+       case FLOW_CLS_STATS:
+               return enetc_psfp_get_stats(priv, cls_flower);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static inline void clean_psfp_sfi_bitmap(void)
+{
+       bitmap_free(epsfp.psfp_sfi_bitmap);
+       epsfp.psfp_sfi_bitmap = NULL;
+}
+
+static void clean_stream_list(void)
+{
+       struct enetc_stream_filter *s;
+       struct hlist_node *tmp;
+
+       hlist_for_each_entry_safe(s, tmp, &epsfp.stream_list, node) {
+               hlist_del(&s->node);
+               kfree(s);
+       }
+}
+
+static void clean_sfi_list(void)
+{
+       struct enetc_psfp_filter *sfi;
+       struct hlist_node *tmp;
+
+       hlist_for_each_entry_safe(sfi, tmp, &epsfp.psfp_filter_list, node) {
+               hlist_del(&sfi->node);
+               kfree(sfi);
+       }
+}
+
+static void clean_sgi_list(void)
+{
+       struct enetc_psfp_gate *sgi;
+       struct hlist_node *tmp;
+
+       hlist_for_each_entry_safe(sgi, tmp, &epsfp.psfp_gate_list, node) {
+               hlist_del(&sgi->node);
+               kfree(sgi);
+       }
+}
+
+static void clean_psfp_all(void)
+{
+       /* Disable all list nodes and free all memory */
+       clean_sfi_list();
+       clean_sgi_list();
+       clean_stream_list();
+       epsfp.dev_bitmap = 0;
+       clean_psfp_sfi_bitmap();
+}
+
+int enetc_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
+                           void *cb_priv)
+{
+       struct net_device *ndev = cb_priv;
+
+       if (!tc_can_offload(ndev))
+               return -EOPNOTSUPP;
+
+       switch (type) {
+       case TC_SETUP_CLSFLOWER:
+               return enetc_setup_tc_cls_flower(netdev_priv(ndev), type_data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+int enetc_psfp_init(struct enetc_ndev_priv *priv)
+{
+       if (epsfp.psfp_sfi_bitmap)
+               return 0;
+
+       epsfp.psfp_sfi_bitmap = bitmap_zalloc(priv->psfp_cap.max_psfp_filter,
+                                             GFP_KERNEL);
+       if (!epsfp.psfp_sfi_bitmap)
+               return -ENOMEM;
+
+       spin_lock_init(&epsfp.psfp_lock);
+
+       if (list_empty(&enetc_block_cb_list))
+               epsfp.dev_bitmap = 0;
+
+       return 0;
+}
+
+int enetc_psfp_clean(struct enetc_ndev_priv *priv)
+{
+       if (!list_empty(&enetc_block_cb_list))
+               return -EBUSY;
+
+       clean_psfp_all();
+
+       return 0;
+}
+
+int enetc_setup_tc_psfp(struct net_device *ndev, void *type_data)
+{
+       struct enetc_ndev_priv *priv = netdev_priv(ndev);
+       struct flow_block_offload *f = type_data;
+       int err;
+
+       err = flow_block_cb_setup_simple(f, &enetc_block_cb_list,
+                                        enetc_setup_tc_block_cb,
+                                        ndev, ndev, true);
+       if (err)
+               return err;
+
+       switch (f->command) {
+       case FLOW_BLOCK_BIND:
+               set_bit(enetc_get_port(priv), &epsfp.dev_bitmap);
+               break;
+       case FLOW_BLOCK_UNBIND:
+               clear_bit(enetc_get_port(priv), &epsfp.dev_bitmap);
+               if (!epsfp.dev_bitmap)
+                       clean_psfp_all();
+               break;
+       }
+
+       return 0;
+}
index bd898f5..a6cdd5b 100644 (file)
@@ -376,8 +376,7 @@ struct bufdesc_ex {
 #define FEC_ENET_TS_AVAIL       ((uint)0x00010000)
 #define FEC_ENET_TS_TIMER       ((uint)0x00008000)
 
-#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
-#define FEC_NAPI_IMASK FEC_ENET_MII
+#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF)
 #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
 
 /* ENET interrupt coalescing macro define */
@@ -488,6 +487,12 @@ struct fec_enet_priv_rx_q {
        struct  sk_buff *rx_skbuff[RX_RING_SIZE];
 };
 
+struct fec_stop_mode_gpr {
+       struct regmap *gpr;
+       u8 reg;
+       u8 bit;
+};
+
 /* The FEC buffer descriptors track the ring buffers.  The rx_bd_base and
  * tx_bd_base always point to the base of the buffer descriptors.  The
  * cur_rx and cur_tx point to the currently available buffer.
@@ -537,7 +542,6 @@ struct fec_enet_private {
        int     link;
        int     full_duplex;
        int     speed;
-       struct  completion mdio_done;
        int     irq[FEC_IRQ_NUM];
        bool    bufdesc_ex;
        int     pause_flag;
@@ -562,6 +566,7 @@ struct fec_enet_private {
        int hwts_tx_en;
        struct delayed_work time_keep;
        struct regulator *reg_phy;
+       struct fec_stop_mode_gpr stop_gpr;
 
        unsigned int tx_align;
        unsigned int rx_align;
index c1c267b..2e20914 100644 (file)
@@ -62,6 +62,8 @@
 #include <linux/if_vlan.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/prefetch.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 #include <soc/imx/cpuidle.h>
 
 #include <asm/cacheflush.h>
@@ -84,6 +86,56 @@ static void fec_enet_itr_coal_init(struct net_device *ndev);
 #define FEC_ENET_OPD_V 0xFFF0
 #define FEC_MDIO_PM_TIMEOUT  100 /* ms */
 
+struct fec_devinfo {
+       u32 quirks;
+       u8 stop_gpr_reg;
+       u8 stop_gpr_bit;
+};
+
+static const struct fec_devinfo fec_imx25_info = {
+       .quirks = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR |
+                 FEC_QUIRK_HAS_FRREG,
+};
+
+static const struct fec_devinfo fec_imx27_info = {
+       .quirks = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG,
+};
+
+static const struct fec_devinfo fec_imx28_info = {
+       .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |
+                 FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC |
+                 FEC_QUIRK_HAS_FRREG,
+};
+
+static const struct fec_devinfo fec_imx6q_info = {
+       .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
+                 FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
+                 FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 |
+                 FEC_QUIRK_HAS_RACC,
+       .stop_gpr_reg = 0x34,
+       .stop_gpr_bit = 27,
+};
+
+static const struct fec_devinfo fec_mvf600_info = {
+       .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC,
+};
+
+static const struct fec_devinfo fec_imx6x_info = {
+       .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
+                 FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
+                 FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB |
+                 FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE |
+                 FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE,
+};
+
+static const struct fec_devinfo fec_imx6ul_info = {
+       .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
+                 FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
+                 FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 |
+                 FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC |
+                 FEC_QUIRK_HAS_COALESCE,
+};
+
 static struct platform_device_id fec_devtype[] = {
        {
                /* keep it for coldfire */
@@ -91,39 +143,25 @@ static struct platform_device_id fec_devtype[] = {
                .driver_data = 0,
        }, {
                .name = "imx25-fec",
-               .driver_data = FEC_QUIRK_USE_GASKET | FEC_QUIRK_MIB_CLEAR |
-                              FEC_QUIRK_HAS_FRREG,
+               .driver_data = (kernel_ulong_t)&fec_imx25_info,
        }, {
                .name = "imx27-fec",
-               .driver_data = FEC_QUIRK_MIB_CLEAR | FEC_QUIRK_HAS_FRREG,
+               .driver_data = (kernel_ulong_t)&fec_imx27_info,
        }, {
                .name = "imx28-fec",
-               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |
-                               FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC |
-                               FEC_QUIRK_HAS_FRREG,
+               .driver_data = (kernel_ulong_t)&fec_imx28_info,
        }, {
                .name = "imx6q-fec",
-               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
-                               FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
-                               FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR006358 |
-                               FEC_QUIRK_HAS_RACC,
+               .driver_data = (kernel_ulong_t)&fec_imx6q_info,
        }, {
                .name = "mvf600-fec",
-               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_RACC,
+               .driver_data = (kernel_ulong_t)&fec_mvf600_info,
        }, {
                .name = "imx6sx-fec",
-               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
-                               FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
-                               FEC_QUIRK_HAS_VLAN | FEC_QUIRK_HAS_AVB |
-                               FEC_QUIRK_ERR007885 | FEC_QUIRK_BUG_CAPTURE |
-                               FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE,
+               .driver_data = (kernel_ulong_t)&fec_imx6x_info,
        }, {
                .name = "imx6ul-fec",
-               .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT |
-                               FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM |
-                               FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 |
-                               FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC |
-                               FEC_QUIRK_HAS_COALESCE,
+               .driver_data = (kernel_ulong_t)&fec_imx6ul_info,
        }, {
                /* sentinel */
        }
@@ -938,8 +976,8 @@ fec_restart(struct net_device *ndev)
        writel((__force u32)cpu_to_be32(temp_mac[1]),
               fep->hwp + FEC_ADDR_HIGH);
 
-       /* Clear any outstanding interrupt. */
-       writel(0xffffffff, fep->hwp + FEC_IEVENT);
+       /* Clear any outstanding interrupt, except MDIO. */
+       writel((0xffffffff & ~FEC_ENET_MII), fep->hwp + FEC_IEVENT);
 
        fec_enet_bd_init(ndev);
 
@@ -1085,18 +1123,35 @@ fec_restart(struct net_device *ndev)
        if (fep->link)
                writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
        else
-               writel(FEC_ENET_MII, fep->hwp + FEC_IMASK);
+               writel(0, fep->hwp + FEC_IMASK);
 
        /* Init the interrupt coalescing */
        fec_enet_itr_coal_init(ndev);
 
 }
 
+static void fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled)
+{
+       struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
+       struct fec_stop_mode_gpr *stop_gpr = &fep->stop_gpr;
+
+       if (stop_gpr->gpr) {
+               if (enabled)
+                       regmap_update_bits(stop_gpr->gpr, stop_gpr->reg,
+                                          BIT(stop_gpr->bit),
+                                          BIT(stop_gpr->bit));
+               else
+                       regmap_update_bits(stop_gpr->gpr, stop_gpr->reg,
+                                          BIT(stop_gpr->bit), 0);
+       } else if (pdata && pdata->sleep_mode_enable) {
+               pdata->sleep_mode_enable(enabled);
+       }
+}
+
 static void
 fec_stop(struct net_device *ndev)
 {
        struct fec_enet_private *fep = netdev_priv(ndev);
-       struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
        u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
        u32 val;
 
@@ -1125,9 +1180,7 @@ fec_stop(struct net_device *ndev)
                val = readl(fep->hwp + FEC_ECNTRL);
                val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
                writel(val, fep->hwp + FEC_ECNTRL);
-
-               if (pdata && pdata->sleep_mode_enable)
-                       pdata->sleep_mode_enable(true);
+               fec_enet_stop_mode(fep, true);
        }
        writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
 
@@ -1599,6 +1652,10 @@ fec_enet_interrupt(int irq, void *dev_id)
        irqreturn_t ret = IRQ_NONE;
 
        int_events = readl(fep->hwp + FEC_IEVENT);
+
+       /* Don't clear MDIO events, we poll for those */
+       int_events &= ~FEC_ENET_MII;
+
        writel(int_events, fep->hwp + FEC_IEVENT);
        fec_enet_collect_events(fep, int_events);
 
@@ -1606,16 +1663,12 @@ fec_enet_interrupt(int irq, void *dev_id)
                ret = IRQ_HANDLED;
 
                if (napi_schedule_prep(&fep->napi)) {
-                       /* Disable the NAPI interrupts */
-                       writel(FEC_NAPI_IMASK, fep->hwp + FEC_IMASK);
+                       /* Disable interrupts */
+                       writel(0, fep->hwp + FEC_IMASK);
                        __napi_schedule(&fep->napi);
                }
        }
 
-       if (int_events & FEC_ENET_MII) {
-               ret = IRQ_HANDLED;
-               complete(&fep->mdio_done);
-       }
        return ret;
 }
 
@@ -1765,11 +1818,24 @@ static void fec_enet_adjust_link(struct net_device *ndev)
                phy_print_status(phy_dev);
 }
 
+static int fec_enet_mdio_wait(struct fec_enet_private *fep)
+{
+       uint ievent;
+       int ret;
+
+       ret = readl_poll_timeout_atomic(fep->hwp + FEC_IEVENT, ievent,
+                                       ievent & FEC_ENET_MII, 2, 30000);
+
+       if (!ret)
+               writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
+
+       return ret;
+}
+
 static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 {
        struct fec_enet_private *fep = bus->priv;
        struct device *dev = &fep->pdev->dev;
-       unsigned long time_left;
        int ret = 0, frame_start, frame_addr, frame_op;
        bool is_c45 = !!(regnum & MII_ADDR_C45);
 
@@ -1777,8 +1843,6 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
        if (ret < 0)
                return ret;
 
-       reinit_completion(&fep->mdio_done);
-
        if (is_c45) {
                frame_start = FEC_MMFR_ST_C45;
 
@@ -1790,11 +1854,9 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
                       fep->hwp + FEC_MII_DATA);
 
                /* wait for end of transfer */
-               time_left = wait_for_completion_timeout(&fep->mdio_done,
-                               usecs_to_jiffies(FEC_MII_TIMEOUT));
-               if (time_left == 0) {
+               ret = fec_enet_mdio_wait(fep);
+               if (ret) {
                        netdev_err(fep->netdev, "MDIO address write timeout\n");
-                       ret = -ETIMEDOUT;
                        goto out;
                }
 
@@ -1813,11 +1875,9 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
                FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
 
        /* wait for end of transfer */
-       time_left = wait_for_completion_timeout(&fep->mdio_done,
-                       usecs_to_jiffies(FEC_MII_TIMEOUT));
-       if (time_left == 0) {
+       ret = fec_enet_mdio_wait(fep);
+       if (ret) {
                netdev_err(fep->netdev, "MDIO read timeout\n");
-               ret = -ETIMEDOUT;
                goto out;
        }
 
@@ -1835,7 +1895,6 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
 {
        struct fec_enet_private *fep = bus->priv;
        struct device *dev = &fep->pdev->dev;
-       unsigned long time_left;
        int ret, frame_start, frame_addr;
        bool is_c45 = !!(regnum & MII_ADDR_C45);
 
@@ -1845,8 +1904,6 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
        else
                ret = 0;
 
-       reinit_completion(&fep->mdio_done);
-
        if (is_c45) {
                frame_start = FEC_MMFR_ST_C45;
 
@@ -1858,11 +1915,9 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
                       fep->hwp + FEC_MII_DATA);
 
                /* wait for end of transfer */
-               time_left = wait_for_completion_timeout(&fep->mdio_done,
-                       usecs_to_jiffies(FEC_MII_TIMEOUT));
-               if (time_left == 0) {
+               ret = fec_enet_mdio_wait(fep);
+               if (ret) {
                        netdev_err(fep->netdev, "MDIO address write timeout\n");
-                       ret = -ETIMEDOUT;
                        goto out;
                }
        } else {
@@ -1878,12 +1933,9 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
                fep->hwp + FEC_MII_DATA);
 
        /* wait for end of transfer */
-       time_left = wait_for_completion_timeout(&fep->mdio_done,
-                       usecs_to_jiffies(FEC_MII_TIMEOUT));
-       if (time_left == 0) {
+       ret = fec_enet_mdio_wait(fep);
+       if (ret)
                netdev_err(fep->netdev, "MDIO write timeout\n");
-               ret  = -ETIMEDOUT;
-       }
 
 out:
        pm_runtime_mark_last_busy(dev);
@@ -2012,9 +2064,11 @@ static int fec_enet_mii_init(struct platform_device *pdev)
        static struct mii_bus *fec0_mii_bus;
        struct net_device *ndev = platform_get_drvdata(pdev);
        struct fec_enet_private *fep = netdev_priv(ndev);
+       bool suppress_preamble = false;
        struct device_node *node;
        int err = -ENXIO;
        u32 mii_speed, holdtime;
+       u32 bus_freq;
 
        /*
         * The i.MX28 dual fec interfaces are not equal.
@@ -2042,15 +2096,23 @@ static int fec_enet_mii_init(struct platform_device *pdev)
                return -ENOENT;
        }
 
+       bus_freq = 2500000; /* 2.5MHz by default */
+       node = of_get_child_by_name(pdev->dev.of_node, "mdio");
+       if (node) {
+               of_property_read_u32(node, "clock-frequency", &bus_freq);
+               suppress_preamble = of_property_read_bool(node,
+                                                         "suppress-preamble");
+       }
+
        /*
-        * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
+        * Set MII speed (= clk_get_rate() / 2 * phy_speed)
         *
         * The formula for FEC MDC is 'ref_freq / (MII_SPEED x 2)' while
         * for ENET-MAC is 'ref_freq / ((MII_SPEED + 1) x 2)'.  The i.MX28
         * Reference Manual has an error on this, and gets fixed on i.MX6Q
         * document.
         */
-       mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000);
+       mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), bus_freq * 2);
        if (fep->quirks & FEC_QUIRK_ENET_MAC)
                mii_speed--;
        if (mii_speed > 63) {
@@ -2077,8 +2139,24 @@ static int fec_enet_mii_init(struct platform_device *pdev)
 
        fep->phy_speed = mii_speed << 1 | holdtime << 8;
 
+       if (suppress_preamble)
+               fep->phy_speed |= BIT(7);
+
+       /* Clear MMFR to avoid to generate MII event by writing MSCR.
+        * MII event generation condition:
+        * - writing MSCR:
+        *      - mmfr[31:0]_not_zero & mscr[7:0]_is_zero &
+        *        mscr_reg_data_in[7:0] != 0
+        * - writing MMFR:
+        *      - mscr[7:0]_not_zero
+        */
+       writel(0, fep->hwp + FEC_MII_DATA);
+
        writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
 
+       /* Clear any pending transaction complete indication */
+       writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
+
        fep->mii_bus = mdiobus_alloc();
        if (fep->mii_bus == NULL) {
                err = -ENOMEM;
@@ -2093,7 +2171,6 @@ static int fec_enet_mii_init(struct platform_device *pdev)
        fep->mii_bus->priv = fep;
        fep->mii_bus->parent = &pdev->dev;
 
-       node = of_get_child_by_name(pdev->dev.of_node, "mdio");
        err = of_mdiobus_register(fep->mii_bus, node);
        of_node_put(node);
        if (err)
@@ -3398,6 +3475,37 @@ static int fec_enet_get_irq_cnt(struct platform_device *pdev)
        return irq_cnt;
 }
 
+static int fec_enet_init_stop_mode(struct fec_enet_private *fep,
+                                  struct fec_devinfo *dev_info,
+                                  struct device_node *np)
+{
+       struct device_node *gpr_np;
+       int ret = 0;
+
+       if (!dev_info)
+               return 0;
+
+       gpr_np = of_parse_phandle(np, "gpr", 0);
+       if (!gpr_np)
+               return 0;
+
+       fep->stop_gpr.gpr = syscon_node_to_regmap(gpr_np);
+       if (IS_ERR(fep->stop_gpr.gpr)) {
+               dev_err(&fep->pdev->dev, "could not find gpr regmap\n");
+               ret = PTR_ERR(fep->stop_gpr.gpr);
+               fep->stop_gpr.gpr = NULL;
+               goto out;
+       }
+
+       fep->stop_gpr.reg = dev_info->stop_gpr_reg;
+       fep->stop_gpr.bit = dev_info->stop_gpr_bit;
+
+out:
+       of_node_put(gpr_np);
+
+       return ret;
+}
+
 static int
 fec_probe(struct platform_device *pdev)
 {
@@ -3413,6 +3521,7 @@ fec_probe(struct platform_device *pdev)
        int num_rx_qs;
        char irq_name[8];
        int irq_cnt;
+       struct fec_devinfo *dev_info;
 
        fec_enet_get_queue_num(pdev, &num_tx_qs, &num_rx_qs);
 
@@ -3430,7 +3539,9 @@ fec_probe(struct platform_device *pdev)
        of_id = of_match_device(fec_dt_ids, &pdev->dev);
        if (of_id)
                pdev->id_entry = of_id->data;
-       fep->quirks = pdev->id_entry->driver_data;
+       dev_info = (struct fec_devinfo *)pdev->id_entry->driver_data;
+       if (dev_info)
+               fep->quirks = dev_info->quirks;
 
        fep->netdev = ndev;
        fep->num_rx_queues = num_rx_qs;
@@ -3464,6 +3575,10 @@ fec_probe(struct platform_device *pdev)
        if (of_get_property(np, "fsl,magic-packet", NULL))
                fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET;
 
+       ret = fec_enet_init_stop_mode(fep, dev_info, np);
+       if (ret)
+               goto failed_stop_mode;
+
        phy_node = of_parse_phandle(np, "phy-handle", 0);
        if (!phy_node && of_phy_is_fixed_link(np)) {
                ret = of_phy_register_fixed_link(np);
@@ -3583,7 +3698,6 @@ fec_probe(struct platform_device *pdev)
                fep->irq[i] = irq;
        }
 
-       init_completion(&fep->mdio_done);
        ret = fec_enet_mii_init(pdev);
        if (ret)
                goto failed_mii_init;
@@ -3632,6 +3746,7 @@ failed_clk:
        if (of_phy_is_fixed_link(np))
                of_phy_deregister_fixed_link(np);
        of_node_put(phy_node);
+failed_stop_mode:
 failed_phy:
        dev_id--;
 failed_ioremap:
@@ -3709,7 +3824,6 @@ static int __maybe_unused fec_resume(struct device *dev)
 {
        struct net_device *ndev = dev_get_drvdata(dev);
        struct fec_enet_private *fep = netdev_priv(ndev);
-       struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
        int ret;
        int val;
 
@@ -3727,8 +3841,8 @@ static int __maybe_unused fec_resume(struct device *dev)
                        goto failed_clk;
                }
                if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) {
-                       if (pdata && pdata->sleep_mode_enable)
-                               pdata->sleep_mode_enable(false);
+                       fec_enet_stop_mode(fep, false);
+
                        val = readl(fep->hwp + FEC_ECNTRL);
                        val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
                        writel(val, fep->hwp + FEC_ECNTRL);
index 8aace2d..9a90794 100644 (file)
@@ -697,9 +697,9 @@ hns_mac_register_phydev(struct mii_bus *mdio, struct hns_mac_cb *mac_cb,
                return rc;
 
        if (!strcmp(phy_type, phy_modes(PHY_INTERFACE_MODE_XGMII)))
-               is_c45 = 1;
+               is_c45 = true;
        else if (!strcmp(phy_type, phy_modes(PHY_INTERFACE_MODE_SGMII)))
-               is_c45 = 0;
+               is_c45 = false;
        else
                return -ENODATA;
 
index 948e67e..21a7361 100644 (file)
@@ -45,6 +45,7 @@ enum HCLGE_MBX_OPCODE {
        HCLGE_MBX_GET_MEDIA_TYPE,       /* (VF -> PF) get media type */
        HCLGE_MBX_PUSH_PROMISC_INFO,    /* (PF -> VF) push vf promisc info */
        HCLGE_MBX_VF_UNINIT,            /* (VF -> PF) vf is unintializing */
+       HCLGE_MBX_HANDLE_VF_TBL,        /* (VF -> PF) store/clear hw table */
 
        HCLGE_MBX_GET_VF_FLR_STATUS = 200, /* (M7 -> PF) get vf flr status */
        HCLGE_MBX_PUSH_LINK_STATUS,     /* (M7 -> PF) get port link status */
@@ -70,6 +71,10 @@ enum hclge_mbx_vlan_cfg_subcode {
        HCLGE_MBX_GET_PORT_BASE_VLAN_STATE,     /* get port based vlan state */
 };
 
+enum hclge_mbx_tbl_cfg_subcode {
+       HCLGE_MBX_VPORT_LIST_CLEAR,
+};
+
 #define HCLGE_MBX_MAX_MSG_SIZE 14
 #define HCLGE_MBX_MAX_RESP_DATA_SIZE   8U
 #define HCLGE_MBX_MAX_RING_CHAIN_PARAM_NUM     4
index 5587605..5602bf2 100644 (file)
@@ -233,7 +233,6 @@ struct hnae3_ae_dev {
        struct list_head node;
        u32 flag;
        unsigned long hw_err_reset_req;
-       enum hnae3_reset_type reset_type;
        void *priv;
 };
 
@@ -270,6 +269,8 @@ struct hnae3_ae_dev {
  *   Set loopback
  * set_promisc_mode
  *   Set promisc mode
+ * request_update_promisc_mode
+ *   request to hclge(vf) to update promisc mode
  * set_mtu()
  *   set mtu
  * get_pauseparam()
@@ -354,8 +355,6 @@ struct hnae3_ae_dev {
  *   Set vlan filter config of Ports
  * set_vf_vlan_filter()
  *   Set vlan filter config of vf
- * restore_vlan_table()
- *   Restore vlan filter entries after reset
  * enable_hw_strip_rxvtag()
  *   Enable/disable hardware strip vlan tag of packets received
  * set_gro_en
@@ -375,6 +374,8 @@ struct hnae3_ae_dev {
  *   Set the max tx rate of specified vf.
  * set_vf_mac
  *   Configure the default MAC for specified VF
+ * get_module_eeprom
+ *   Get the optical module eeprom info.
  */
 struct hnae3_ae_ops {
        int (*init_ae_dev)(struct hnae3_ae_dev *ae_dev);
@@ -408,6 +409,7 @@ struct hnae3_ae_ops {
 
        int (*set_promisc_mode)(struct hnae3_handle *handle, bool en_uc_pmc,
                                bool en_mc_pmc);
+       void (*request_update_promisc_mode)(struct hnae3_handle *handle);
        int (*set_mtu)(struct hnae3_handle *handle, int new_mtu);
 
        void (*get_pauseparam)(struct hnae3_handle *handle,
@@ -525,7 +527,6 @@ struct hnae3_ae_ops {
                                struct ethtool_rxnfc *cmd);
        int (*get_fd_all_rules)(struct hnae3_handle *handle,
                                struct ethtool_rxnfc *cmd, u32 *rule_locs);
-       int (*restore_fd_rules)(struct hnae3_handle *handle);
        void (*enable_fd)(struct hnae3_handle *handle, bool enable);
        int (*add_arfs_entry)(struct hnae3_handle *handle, u16 queue_id,
                              u16 flow_id, struct flow_keys *fkeys);
@@ -539,7 +540,6 @@ struct hnae3_ae_ops {
        void (*set_timer_task)(struct hnae3_handle *handle, bool enable);
        int (*mac_connect_phy)(struct hnae3_handle *handle);
        void (*mac_disconnect_phy)(struct hnae3_handle *handle);
-       void (*restore_vlan_table)(struct hnae3_handle *handle);
        int (*get_vf_config)(struct hnae3_handle *handle, int vf,
                             struct ifla_vf_info *ivf);
        int (*set_vf_link_state)(struct hnae3_handle *handle, int vf,
@@ -550,6 +550,8 @@ struct hnae3_ae_ops {
        int (*set_vf_rate)(struct hnae3_handle *handle, int vf,
                           int min_tx_rate, int max_tx_rate, bool force);
        int (*set_vf_mac)(struct hnae3_handle *handle, int vf, u8 *p);
+       int (*get_module_eeprom)(struct hnae3_handle *handle, u32 offset,
+                                u32 len, u8 *data);
 };
 
 struct hnae3_dcb_ops {
index e1d8809..fe7fb56 100644 (file)
@@ -262,6 +262,8 @@ static void hns3_dbg_help(struct hnae3_handle *h)
        dev_info(&h->pdev->dev, "dump mac tnl status\n");
        dev_info(&h->pdev->dev, "dump loopback\n");
        dev_info(&h->pdev->dev, "dump qs shaper [qs id]\n");
+       dev_info(&h->pdev->dev, "dump uc mac list <func id>\n");
+       dev_info(&h->pdev->dev, "dump mc mac list <func id>\n");
 
        memset(printf_buf, 0, HNS3_DBG_BUF_LEN);
        strncat(printf_buf, "dump reg [[bios common] [ssu <port_id>]",
@@ -270,7 +272,7 @@ static void hns3_dbg_help(struct hnae3_handle *h)
                " [igu egu <port_id>] [rpu <tc_queue_num>]",
                HNS3_DBG_BUF_LEN - strlen(printf_buf) - 1);
        strncat(printf_buf + strlen(printf_buf),
-               " [rtc] [ppp] [rcb] [tqp <queue_num>]]\n",
+               " [rtc] [ppp] [rcb] [tqp <queue_num>] [mac]]\n",
                HNS3_DBG_BUF_LEN - strlen(printf_buf) - 1);
        dev_info(&h->pdev->dev, "%s", printf_buf);
 
index da98fd7..c79d6a3 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/aer.h>
 #include <linux/skbuff.h>
 #include <linux/sctp.h>
-#include <linux/vermagic.h>
 #include <net/gre.h>
 #include <net/ip6_checksum.h>
 #include <net/pkt_cls.h>
        } while (0)
 
 static void hns3_clear_all_ring(struct hnae3_handle *h, bool force);
-static void hns3_remove_hw_addr(struct net_device *netdev);
 
 static const char hns3_driver_name[] = "hns3";
-const char hns3_driver_version[] = VERMAGIC_STRING;
 static const char hns3_driver_string[] =
                        "Hisilicon Ethernet Network Driver for Hip08 Family";
 static const char hns3_copyright[] = "Copyright (c) 2017 Huawei Corporation.";
@@ -550,6 +547,13 @@ static int hns3_nic_uc_unsync(struct net_device *netdev,
 {
        struct hnae3_handle *h = hns3_get_handle(netdev);
 
+       /* need ignore the request of removing device address, because
+        * we store the device address and other addresses of uc list
+        * in the function's mac filter list.
+        */
+       if (ether_addr_equal(addr, netdev->dev_addr))
+               return 0;
+
        if (h->ae_algo->ops->rm_uc_addr)
                return h->ae_algo->ops->rm_uc_addr(h, addr);
 
@@ -597,34 +601,25 @@ static void hns3_nic_set_rx_mode(struct net_device *netdev)
 {
        struct hnae3_handle *h = hns3_get_handle(netdev);
        u8 new_flags;
-       int ret;
 
        new_flags = hns3_get_netdev_flags(netdev);
 
-       ret = __dev_uc_sync(netdev, hns3_nic_uc_sync, hns3_nic_uc_unsync);
-       if (ret) {
-               netdev_err(netdev, "sync uc address fail\n");
-               if (ret == -ENOSPC)
-                       new_flags |= HNAE3_OVERFLOW_UPE;
-       }
-
-       if (netdev->flags & IFF_MULTICAST) {
-               ret = __dev_mc_sync(netdev, hns3_nic_mc_sync,
-                                   hns3_nic_mc_unsync);
-               if (ret) {
-                       netdev_err(netdev, "sync mc address fail\n");
-                       if (ret == -ENOSPC)
-                               new_flags |= HNAE3_OVERFLOW_MPE;
-               }
-       }
+       __dev_uc_sync(netdev, hns3_nic_uc_sync, hns3_nic_uc_unsync);
+       __dev_mc_sync(netdev, hns3_nic_mc_sync, hns3_nic_mc_unsync);
 
        /* User mode Promisc mode enable and vlan filtering is disabled to
-        * let all packets in. MAC-VLAN Table overflow Promisc enabled and
-        * vlan fitering is enabled
+        * let all packets in.
         */
-       hns3_enable_vlan_filter(netdev, new_flags & HNAE3_VLAN_FLTR);
        h->netdev_flags = new_flags;
-       hns3_update_promisc_mode(netdev, new_flags);
+       hns3_request_update_promisc_mode(h);
+}
+
+void hns3_request_update_promisc_mode(struct hnae3_handle *handle)
+{
+       const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+
+       if (ops->request_update_promisc_mode)
+               ops->request_update_promisc_mode(handle);
 }
 
 int hns3_update_promisc_mode(struct net_device *netdev, u8 promisc_flags)
@@ -2107,7 +2102,6 @@ static int hns3_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        ae_dev->pdev = pdev;
        ae_dev->flag = ent->driver_data;
-       ae_dev->reset_type = HNAE3_NONE_RESET;
        hns3_get_dev_capability(pdev, ae_dev);
        pci_set_drvdata(pdev, ae_dev);
 
@@ -3909,9 +3903,11 @@ static int hns3_init_mac_addr(struct net_device *netdev)
                eth_hw_addr_random(netdev);
                dev_warn(priv->dev, "using random MAC address %pM\n",
                         netdev->dev_addr);
-       } else {
+       } else if (!ether_addr_equal(netdev->dev_addr, mac_addr_temp)) {
                ether_addr_copy(netdev->dev_addr, mac_addr_temp);
                ether_addr_copy(netdev->perm_addr, mac_addr_temp);
+       } else {
+               return 0;
        }
 
        if (h->ae_algo->ops->set_mac_addr)
@@ -3939,17 +3935,6 @@ static void hns3_uninit_phy(struct net_device *netdev)
                h->ae_algo->ops->mac_disconnect_phy(h);
 }
 
-static int hns3_restore_fd_rules(struct net_device *netdev)
-{
-       struct hnae3_handle *h = hns3_get_handle(netdev);
-       int ret = 0;
-
-       if (h->ae_algo->ops->restore_fd_rules)
-               ret = h->ae_algo->ops->restore_fd_rules(h);
-
-       return ret;
-}
-
 static void hns3_del_all_fd_rules(struct net_device *netdev, bool clear_list)
 {
        struct hnae3_handle *h = hns3_get_handle(netdev);
@@ -4121,8 +4106,6 @@ static void hns3_client_uninit(struct hnae3_handle *handle, bool reset)
        struct hns3_nic_priv *priv = netdev_priv(netdev);
        int ret;
 
-       hns3_remove_hw_addr(netdev);
-
        if (netdev->reg_state != NETREG_UNINITIALIZED)
                unregister_netdev(netdev);
 
@@ -4193,56 +4176,6 @@ static int hns3_client_setup_tc(struct hnae3_handle *handle, u8 tc)
        return hns3_nic_set_real_num_queue(ndev);
 }
 
-static int hns3_recover_hw_addr(struct net_device *ndev)
-{
-       struct netdev_hw_addr_list *list;
-       struct netdev_hw_addr *ha, *tmp;
-       int ret = 0;
-
-       netif_addr_lock_bh(ndev);
-       /* go through and sync uc_addr entries to the device */
-       list = &ndev->uc;
-       list_for_each_entry_safe(ha, tmp, &list->list, list) {
-               ret = hns3_nic_uc_sync(ndev, ha->addr);
-               if (ret)
-                       goto out;
-       }
-
-       /* go through and sync mc_addr entries to the device */
-       list = &ndev->mc;
-       list_for_each_entry_safe(ha, tmp, &list->list, list) {
-               ret = hns3_nic_mc_sync(ndev, ha->addr);
-               if (ret)
-                       goto out;
-       }
-
-out:
-       netif_addr_unlock_bh(ndev);
-       return ret;
-}
-
-static void hns3_remove_hw_addr(struct net_device *netdev)
-{
-       struct netdev_hw_addr_list *list;
-       struct netdev_hw_addr *ha, *tmp;
-
-       hns3_nic_uc_unsync(netdev, netdev->dev_addr);
-
-       netif_addr_lock_bh(netdev);
-       /* go through and unsync uc_addr entries to the device */
-       list = &netdev->uc;
-       list_for_each_entry_safe(ha, tmp, &list->list, list)
-               hns3_nic_uc_unsync(netdev, ha->addr);
-
-       /* go through and unsync mc_addr entries to the device */
-       list = &netdev->mc;
-       list_for_each_entry_safe(ha, tmp, &list->list, list)
-               if (ha->refcount > 1)
-                       hns3_nic_mc_unsync(netdev, ha->addr);
-
-       netif_addr_unlock_bh(netdev);
-}
-
 static void hns3_clear_tx_ring(struct hns3_enet_ring *ring)
 {
        while (ring->next_to_clean != ring->next_to_use) {
@@ -4401,7 +4334,6 @@ static void hns3_restore_coal(struct hns3_nic_priv *priv)
 
 static int hns3_reset_notify_down_enet(struct hnae3_handle *handle)
 {
-       struct hnae3_ae_dev *ae_dev = pci_get_drvdata(handle->pdev);
        struct hnae3_knic_private_info *kinfo = &handle->kinfo;
        struct net_device *ndev = kinfo->netdev;
        struct hns3_nic_priv *priv = netdev_priv(ndev);
@@ -4409,15 +4341,6 @@ static int hns3_reset_notify_down_enet(struct hnae3_handle *handle)
        if (test_and_set_bit(HNS3_NIC_STATE_RESETTING, &priv->state))
                return 0;
 
-       /* it is cumbersome for hardware to pick-and-choose entries for deletion
-        * from table space. Hence, for function reset software intervention is
-        * required to delete the entries
-        */
-       if (hns3_dev_ongoing_func_reset(ae_dev)) {
-               hns3_remove_hw_addr(ndev);
-               hns3_del_all_fd_rules(ndev, false);
-       }
-
        if (!netif_running(ndev))
                return 0;
 
@@ -4484,6 +4407,9 @@ static int hns3_reset_notify_init_enet(struct hnae3_handle *handle)
                goto err_init_irq_fail;
        }
 
+       if (!hns3_is_phys_func(handle->pdev))
+               hns3_init_mac_addr(netdev);
+
        ret = hns3_client_start(handle);
        if (ret) {
                dev_err(priv->dev, "hns3_client_start fail! ret=%d\n", ret);
@@ -4509,33 +4435,6 @@ err_put_ring:
        return ret;
 }
 
-static int hns3_reset_notify_restore_enet(struct hnae3_handle *handle)
-{
-       struct net_device *netdev = handle->kinfo.netdev;
-       bool vlan_filter_enable;
-       int ret;
-
-       ret = hns3_init_mac_addr(netdev);
-       if (ret)
-               return ret;
-
-       ret = hns3_recover_hw_addr(netdev);
-       if (ret)
-               return ret;
-
-       ret = hns3_update_promisc_mode(netdev, handle->netdev_flags);
-       if (ret)
-               return ret;
-
-       vlan_filter_enable = netdev->flags & IFF_PROMISC ? false : true;
-       hns3_enable_vlan_filter(netdev, vlan_filter_enable);
-
-       if (handle->ae_algo->ops->restore_vlan_table)
-               handle->ae_algo->ops->restore_vlan_table(handle);
-
-       return hns3_restore_fd_rules(netdev);
-}
-
 static int hns3_reset_notify_uninit_enet(struct hnae3_handle *handle)
 {
        struct net_device *netdev = handle->kinfo.netdev;
@@ -4585,9 +4484,6 @@ static int hns3_reset_notify(struct hnae3_handle *handle,
        case HNAE3_UNINIT_CLIENT:
                ret = hns3_reset_notify_uninit_enet(handle);
                break;
-       case HNAE3_RESTORE_CLIENT:
-               ret = hns3_reset_notify_restore_enet(handle);
-               break;
        default:
                break;
        }
@@ -4765,4 +4661,3 @@ 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 abefd7a..240ba06 100644 (file)
@@ -8,10 +8,6 @@
 
 #include "hnae3.h"
 
-#define HNS3_MOD_VERSION "1.0"
-
-extern const char hns3_driver_version[];
-
 enum hns3_nic_state {
        HNS3_NIC_STATE_TESTING,
        HNS3_NIC_STATE_RESETTING,
@@ -580,15 +576,6 @@ static inline void hns3_write_reg(void __iomem *base, u32 reg, u32 value)
        writel(value, reg_addr + reg);
 }
 
-static inline bool hns3_dev_ongoing_func_reset(struct hnae3_ae_dev *ae_dev)
-{
-       return (ae_dev && (ae_dev->reset_type == HNAE3_FUNC_RESET ||
-                          ae_dev->reset_type == HNAE3_FLR_RESET ||
-                          ae_dev->reset_type == HNAE3_VF_FUNC_RESET ||
-                          ae_dev->reset_type == HNAE3_VF_FULL_RESET ||
-                          ae_dev->reset_type == HNAE3_VF_PF_FUNC_RESET));
-}
-
 #define hns3_read_dev(a, reg) \
        hns3_read_reg((a)->io_base, (reg))
 
@@ -662,6 +649,7 @@ void hns3_set_vector_coalesce_rl(struct hns3_enet_tqp_vector *tqp_vector,
 
 void hns3_enable_vlan_filter(struct net_device *netdev, bool enable);
 int hns3_update_promisc_mode(struct net_device *netdev, u8 promisc_flags);
+void hns3_request_update_promisc_mode(struct hnae3_handle *handle);
 
 #ifdef CONFIG_HNS3_DCB
 void hns3_dcbnl_setup(struct hnae3_handle *handle);
index 28b81f2..1a105f2 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/etherdevice.h>
 #include <linux/string.h>
 #include <linux/phy.h>
+#include <linux/sfp.h>
 
 #include "hns3_enet.h"
 
@@ -12,6 +13,11 @@ struct hns3_stats {
        int stats_offset;
 };
 
+struct hns3_sfp_type {
+       u8 type;
+       u8 ext_type;
+};
+
 /* tqp related stats */
 #define HNS3_TQP_STAT(_string, _member)        {                       \
        .stats_string = _string,                                \
@@ -99,7 +105,7 @@ static int hns3_lp_setup(struct net_device *ndev, enum hnae3_loop loop, bool en)
                h->ae_algo->ops->set_promisc_mode(h, true, true);
        } else {
                /* recover promisc mode before loopback test */
-               hns3_update_promisc_mode(ndev, h->netdev_flags);
+               hns3_request_update_promisc_mode(h);
                vlan_filter_enable = ndev->flags & IFF_PROMISC ? false : true;
                hns3_enable_vlan_filter(ndev, vlan_filter_enable);
        }
@@ -546,10 +552,6 @@ static void hns3_get_drvinfo(struct net_device *netdev,
                return;
        }
 
-       strncpy(drvinfo->version, hns3_driver_version,
-               sizeof(drvinfo->version));
-       drvinfo->version[sizeof(drvinfo->version) - 1] = '\0';
-
        strncpy(drvinfo->driver, h->pdev->driver->name,
                sizeof(drvinfo->driver));
        drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0';
@@ -1390,6 +1392,73 @@ static int hns3_set_fecparam(struct net_device *netdev,
        return ops->set_fec(handle, fec_mode);
 }
 
+static int hns3_get_module_info(struct net_device *netdev,
+                               struct ethtool_modinfo *modinfo)
+{
+#define HNS3_SFF_8636_V1_3 0x03
+
+       struct hnae3_handle *handle = hns3_get_handle(netdev);
+       const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+       struct hns3_sfp_type sfp_type;
+       int ret;
+
+       if (handle->pdev->revision == 0x20 || !ops->get_module_eeprom)
+               return -EOPNOTSUPP;
+
+       memset(&sfp_type, 0, sizeof(sfp_type));
+       ret = ops->get_module_eeprom(handle, 0, sizeof(sfp_type) / sizeof(u8),
+                                    (u8 *)&sfp_type);
+       if (ret)
+               return ret;
+
+       switch (sfp_type.type) {
+       case SFF8024_ID_SFP:
+               modinfo->type = ETH_MODULE_SFF_8472;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
+               break;
+       case SFF8024_ID_QSFP_8438:
+               modinfo->type = ETH_MODULE_SFF_8436;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
+               break;
+       case SFF8024_ID_QSFP_8436_8636:
+               if (sfp_type.ext_type < HNS3_SFF_8636_V1_3) {
+                       modinfo->type = ETH_MODULE_SFF_8436;
+                       modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
+               } else {
+                       modinfo->type = ETH_MODULE_SFF_8636;
+                       modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
+               }
+               break;
+       case SFF8024_ID_QSFP28_8636:
+               modinfo->type = ETH_MODULE_SFF_8636;
+               modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
+               break;
+       default:
+               netdev_err(netdev, "Optical module unknown: %#x\n",
+                          sfp_type.type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int hns3_get_module_eeprom(struct net_device *netdev,
+                                 struct ethtool_eeprom *ee, u8 *data)
+{
+       struct hnae3_handle *handle = hns3_get_handle(netdev);
+       const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
+
+       if (handle->pdev->revision == 0x20 || !ops->get_module_eeprom)
+               return -EOPNOTSUPP;
+
+       if (!ee->len)
+               return -EINVAL;
+
+       memset(data, 0, ee->len);
+
+       return ops->get_module_eeprom(handle, ee->offset, ee->len, data);
+}
+
 #define HNS3_ETHTOOL_COALESCE  (ETHTOOL_COALESCE_USECS |               \
                                 ETHTOOL_COALESCE_USE_ADAPTIVE |        \
                                 ETHTOOL_COALESCE_RX_USECS_HIGH |       \
@@ -1453,6 +1522,8 @@ static const struct ethtool_ops hns3_ethtool_ops = {
        .set_msglevel = hns3_set_msglevel,
        .get_fecparam = hns3_get_fecparam,
        .set_fecparam = hns3_set_fecparam,
+       .get_module_info = hns3_get_module_info,
+       .get_module_eeprom = hns3_get_module_eeprom,
 };
 
 void hns3_ethtool_set_ops(struct net_device *netdev)
index 0fb61d4..6c28c8f 100644 (file)
@@ -4,6 +4,7 @@
 #
 
 ccflags-y := -I $(srctree)/drivers/net/ethernet/hisilicon/hns3
+ccflags-y += -I $(srctree)/$(src)
 
 obj-$(CONFIG_HNS3_HCLGE) += hclge.o
 hclge-objs = hclge_main.o hclge_cmd.o hclge_mdio.o hclge_tm.o hclge_mbx.o hclge_err.o  hclge_debugfs.o
index 96498d9..9a9d752 100644 (file)
@@ -270,6 +270,8 @@ enum hclge_opcode_type {
        HCLGE_OPC_M7_COMPAT_CFG         = 0x701A,
 
        /* SFP command */
+       HCLGE_OPC_GET_SFP_EEPROM        = 0x7100,
+       HCLGE_OPC_GET_SFP_EXIST         = 0x7101,
        HCLGE_OPC_GET_SFP_INFO          = 0x7104,
 
        /* Error INT commands */
@@ -733,31 +735,6 @@ struct hclge_mac_mgr_tbl_entry_cmd {
        u8      rsv3[2];
 };
 
-struct hclge_mac_vlan_add_cmd {
-       __le16  flags;
-       __le16  mac_addr_hi16;
-       __le32  mac_addr_lo32;
-       __le32  mac_addr_msk_hi32;
-       __le16  mac_addr_msk_lo16;
-       __le16  vlan_tag;
-       __le16  ingress_port;
-       __le16  egress_port;
-       u8      rsv[4];
-};
-
-#define HNS3_MAC_VLAN_CFG_FLAG_BIT 0
-struct hclge_mac_vlan_remove_cmd {
-       __le16  flags;
-       __le16  mac_addr_hi16;
-       __le32  mac_addr_lo32;
-       __le32  mac_addr_msk_hi32;
-       __le16  mac_addr_msk_lo16;
-       __le16  vlan_tag;
-       __le16  ingress_port;
-       __le16  egress_port;
-       u8      rsv[4];
-};
-
 struct hclge_vlan_filter_ctrl_cmd {
        u8 vlan_type;
        u8 vlan_fe;
@@ -1079,6 +1056,19 @@ struct hclge_firmware_compat_cmd {
        u8 rsv[20];
 };
 
+#define HCLGE_SFP_INFO_CMD_NUM 6
+#define HCLGE_SFP_INFO_BD0_LEN 20
+#define HCLGE_SFP_INFO_BDX_LEN 24
+#define HCLGE_SFP_INFO_MAX_LEN \
+       (HCLGE_SFP_INFO_BD0_LEN + \
+       (HCLGE_SFP_INFO_CMD_NUM - 1) * HCLGE_SFP_INFO_BDX_LEN)
+
+struct hclge_sfp_info_bd0_cmd {
+       __le16 offset;
+       __le16 read_len;
+       u8 data[HCLGE_SFP_INFO_BD0_LEN];
+};
+
 int hclge_cmd_init(struct hclge_dev *hdev);
 static inline void hclge_write_reg(void __iomem *base, u32 reg, u32 value)
 {
index 1722828..6cfa825 100644 (file)
@@ -143,7 +143,7 @@ static void hclge_dbg_dump_reg_common(struct hclge_dev *hdev,
                return;
        }
 
-       buf_len = sizeof(struct hclge_desc) * bd_num;
+       buf_len = sizeof(struct hclge_desc) * bd_num;
        desc_src = kzalloc(buf_len, GFP_KERNEL);
        if (!desc_src)
                return;
@@ -173,6 +173,114 @@ static void hclge_dbg_dump_reg_common(struct hclge_dev *hdev,
        kfree(desc_src);
 }
 
+static void hclge_dbg_dump_mac_enable_status(struct hclge_dev *hdev)
+{
+       struct hclge_config_mac_mode_cmd *req;
+       struct hclge_desc desc;
+       u32 loop_en;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAC_MODE, true);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "failed to dump mac enable status, ret = %d\n", ret);
+               return;
+       }
+
+       req = (struct hclge_config_mac_mode_cmd *)desc.data;
+       loop_en = le32_to_cpu(req->txrx_pad_fcs_loop_en);
+
+       dev_info(&hdev->pdev->dev, "config_mac_trans_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_TX_EN_B));
+       dev_info(&hdev->pdev->dev, "config_mac_rcv_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_RX_EN_B));
+       dev_info(&hdev->pdev->dev, "config_pad_trans_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_PAD_TX_B));
+       dev_info(&hdev->pdev->dev, "config_pad_rcv_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_PAD_RX_B));
+       dev_info(&hdev->pdev->dev, "config_1588_trans_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_1588_TX_B));
+       dev_info(&hdev->pdev->dev, "config_1588_rcv_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_1588_RX_B));
+       dev_info(&hdev->pdev->dev, "config_mac_app_loop_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_APP_LP_B));
+       dev_info(&hdev->pdev->dev, "config_mac_line_loop_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_LINE_LP_B));
+       dev_info(&hdev->pdev->dev, "config_mac_fcs_tx_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_FCS_TX_B));
+       dev_info(&hdev->pdev->dev, "config_mac_rx_oversize_truncate_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_RX_OVERSIZE_TRUNCATE_B));
+       dev_info(&hdev->pdev->dev, "config_mac_rx_fcs_strip_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_RX_FCS_STRIP_B));
+       dev_info(&hdev->pdev->dev, "config_mac_rx_fcs_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_RX_FCS_B));
+       dev_info(&hdev->pdev->dev, "config_mac_tx_under_min_err_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_TX_UNDER_MIN_ERR_B));
+       dev_info(&hdev->pdev->dev, "config_mac_tx_oversize_truncate_en: %#x\n",
+                hnae3_get_bit(loop_en, HCLGE_MAC_TX_OVERSIZE_TRUNCATE_B));
+}
+
+static void hclge_dbg_dump_mac_frame_size(struct hclge_dev *hdev)
+{
+       struct hclge_config_max_frm_size_cmd *req;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAX_FRM_SIZE, true);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "failed to dump mac frame size, ret = %d\n", ret);
+               return;
+       }
+
+       req = (struct hclge_config_max_frm_size_cmd *)desc.data;
+
+       dev_info(&hdev->pdev->dev, "max_frame_size: %u\n",
+                le16_to_cpu(req->max_frm_size));
+       dev_info(&hdev->pdev->dev, "min_frame_size: %u\n", req->min_frm_size);
+}
+
+static void hclge_dbg_dump_mac_speed_duplex(struct hclge_dev *hdev)
+{
+#define HCLGE_MAC_SPEED_SHIFT  0
+#define HCLGE_MAC_SPEED_MASK   GENMASK(5, 0)
+#define HCLGE_MAC_DUPLEX_SHIFT 7
+
+       struct hclge_config_mac_speed_dup_cmd *req;
+       struct hclge_desc desc;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_SPEED_DUP, true);
+
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "failed to dump mac speed duplex, ret = %d\n", ret);
+               return;
+       }
+
+       req = (struct hclge_config_mac_speed_dup_cmd *)desc.data;
+
+       dev_info(&hdev->pdev->dev, "speed: %#lx\n",
+                hnae3_get_field(req->speed_dup, HCLGE_MAC_SPEED_MASK,
+                                HCLGE_MAC_SPEED_SHIFT));
+       dev_info(&hdev->pdev->dev, "duplex: %#x\n",
+                hnae3_get_bit(req->speed_dup, HCLGE_MAC_DUPLEX_SHIFT));
+}
+
+static void hclge_dbg_dump_mac(struct hclge_dev *hdev)
+{
+       hclge_dbg_dump_mac_enable_status(hdev);
+
+       hclge_dbg_dump_mac_frame_size(hdev);
+
+       hclge_dbg_dump_mac_speed_duplex(hdev);
+}
+
 static void hclge_dbg_dump_dcb(struct hclge_dev *hdev, const char *cmd_buf)
 {
        struct device *dev = &hdev->pdev->dev;
@@ -304,6 +412,11 @@ static void hclge_dbg_dump_reg_cmd(struct hclge_dev *hdev, const char *cmd_buf)
                }
        }
 
+       if (strncmp(cmd_buf, "mac", strlen("mac")) == 0) {
+               hclge_dbg_dump_mac(hdev);
+               has_dump = true;
+       }
+
        if (strncmp(cmd_buf, "dcb", 3) == 0) {
                hclge_dbg_dump_dcb(hdev, &cmd_buf[sizeof("dcb")]);
                has_dump = true;
@@ -1328,6 +1441,49 @@ static void hclge_dbg_dump_qs_shaper(struct hclge_dev *hdev,
        hclge_dbg_dump_qs_shaper_single(hdev, qsid);
 }
 
+static int hclge_dbg_dump_mac_list(struct hclge_dev *hdev, const char *cmd_buf,
+                                  bool is_unicast)
+{
+       struct hclge_mac_node *mac_node, *tmp;
+       struct hclge_vport *vport;
+       struct list_head *list;
+       u32 func_id;
+       int ret;
+
+       ret = kstrtouint(cmd_buf, 0, &func_id);
+       if (ret < 0) {
+               dev_err(&hdev->pdev->dev,
+                       "dump mac list: bad command string, ret = %d\n", ret);
+               return -EINVAL;
+       }
+
+       if (func_id >= hdev->num_alloc_vport) {
+               dev_err(&hdev->pdev->dev,
+                       "function id(%u) is out of range(0-%u)\n", func_id,
+                       hdev->num_alloc_vport - 1);
+               return -EINVAL;
+       }
+
+       vport = &hdev->vport[func_id];
+
+       list = is_unicast ? &vport->uc_mac_list : &vport->mc_mac_list;
+
+       dev_info(&hdev->pdev->dev, "vport %u %s mac list:\n",
+                func_id, is_unicast ? "uc" : "mc");
+       dev_info(&hdev->pdev->dev, "mac address              state\n");
+
+       spin_lock_bh(&vport->mac_list_lock);
+
+       list_for_each_entry_safe(mac_node, tmp, list, node) {
+               dev_info(&hdev->pdev->dev, "%pM         %d\n",
+                        mac_node->mac_addr, mac_node->state);
+       }
+
+       spin_unlock_bh(&vport->mac_list_lock);
+
+       return 0;
+}
+
 int hclge_dbg_run_cmd(struct hnae3_handle *handle, const char *cmd_buf)
 {
 #define DUMP_REG       "dump reg"
@@ -1372,6 +1528,14 @@ int hclge_dbg_run_cmd(struct hnae3_handle *handle, const char *cmd_buf)
        } else if (strncmp(cmd_buf, "dump qs shaper", 14) == 0) {
                hclge_dbg_dump_qs_shaper(hdev,
                                         &cmd_buf[sizeof("dump qs shaper")]);
+       } else if (strncmp(cmd_buf, "dump uc mac list", 16) == 0) {
+               hclge_dbg_dump_mac_list(hdev,
+                                       &cmd_buf[sizeof("dump uc mac list")],
+                                       true);
+       } else if (strncmp(cmd_buf, "dump mc mac list", 16) == 0) {
+               hclge_dbg_dump_mac_list(hdev,
+                                       &cmd_buf[sizeof("dump mc mac list")],
+                                       false);
        } else {
                dev_info(&hdev->pdev->dev, "unknown command\n");
                return -EINVAL;
index a758f9a..71a54dd 100644 (file)
@@ -62,14 +62,16 @@ static int hclge_init_vlan_config(struct hclge_dev *hdev);
 static void hclge_sync_vlan_filter(struct hclge_dev *hdev);
 static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev);
 static bool hclge_get_hw_reset_stat(struct hnae3_handle *handle);
-static int hclge_set_umv_space(struct hclge_dev *hdev, u16 space_size,
-                              u16 *allocated_size, bool is_alloc);
 static void hclge_rfs_filter_expire(struct hclge_dev *hdev);
 static void hclge_clear_arfs_rules(struct hnae3_handle *handle);
 static enum hnae3_reset_type hclge_get_reset_level(struct hnae3_ae_dev *ae_dev,
                                                   unsigned long *addr);
 static int hclge_set_default_loopback(struct hclge_dev *hdev);
 
+static void hclge_sync_mac_table(struct hclge_dev *hdev);
+static void hclge_restore_hw_table(struct hclge_dev *hdev);
+static void hclge_sync_promisc_mode(struct hclge_dev *hdev);
+
 static struct hnae3_ae_algo ae_algo;
 
 static struct workqueue_struct *hclge_wq;
@@ -1687,6 +1689,7 @@ static int hclge_alloc_vport(struct hclge_dev *hdev)
                INIT_LIST_HEAD(&vport->vlan_list);
                INIT_LIST_HEAD(&vport->uc_mac_list);
                INIT_LIST_HEAD(&vport->mc_mac_list);
+               spin_lock_init(&vport->mac_list_lock);
 
                if (i == 0)
                        ret = hclge_vport_setup(vport, tqp_main_vport);
@@ -3729,22 +3732,13 @@ static int hclge_reset_stack(struct hclge_dev *hdev)
        if (ret)
                return ret;
 
-       ret = hclge_notify_client(hdev, HNAE3_INIT_CLIENT);
-       if (ret)
-               return ret;
-
-       return hclge_notify_client(hdev, HNAE3_RESTORE_CLIENT);
+       return hclge_notify_client(hdev, HNAE3_INIT_CLIENT);
 }
 
 static int hclge_reset_prepare(struct hclge_dev *hdev)
 {
-       struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
        int ret;
 
-       /* Initialize ae_dev reset status as well, in case enet layer wants to
-        * know if device is undergoing reset
-        */
-       ae_dev->reset_type = hdev->reset_type;
        hdev->rst_stats.reset_cnt++;
        /* perform reset of the stack & ae device for a client */
        ret = hclge_notify_roce_client(hdev, HNAE3_DOWN_CLIENT);
@@ -3806,7 +3800,6 @@ static int hclge_reset_rebuild(struct hclge_dev *hdev)
        hdev->last_reset_time = jiffies;
        hdev->rst_stats.reset_fail_cnt = 0;
        hdev->rst_stats.reset_done_cnt++;
-       ae_dev->reset_type = HNAE3_NONE_RESET;
        clear_bit(HCLGE_STATE_RST_FAIL, &hdev->state);
 
        /* if default_reset_request has a higher level reset request,
@@ -3973,6 +3966,8 @@ static void hclge_periodic_service_task(struct hclge_dev *hdev)
         * updated when it is triggered by mbx.
         */
        hclge_update_link_status(hdev);
+       hclge_sync_mac_table(hdev);
+       hclge_sync_promisc_mode(hdev);
 
        if (time_is_after_jiffies(hdev->last_serv_processed + HZ)) {
                delta = jiffies - hdev->last_serv_processed;
@@ -4722,7 +4717,8 @@ static int hclge_cmd_set_promisc_mode(struct hclge_dev *hdev,
        ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret)
                dev_err(&hdev->pdev->dev,
-                       "Set promisc mode fail, status is %d.\n", ret);
+                       "failed to set vport %d promisc mode, ret = %d.\n",
+                       param->vf_id, ret);
 
        return ret;
 }
@@ -4772,6 +4768,14 @@ static int hclge_set_promisc_mode(struct hnae3_handle *handle, bool en_uc_pmc,
                                            en_bc_pmc);
 }
 
+static void hclge_request_update_promisc_mode(struct hnae3_handle *handle)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+
+       set_bit(HCLGE_STATE_PROMISC_CHANGED, &hdev->state);
+}
+
 static int hclge_get_fd_mode(struct hclge_dev *hdev, u8 *fd_mode)
 {
        struct hclge_get_fd_mode_cmd *req;
@@ -4822,7 +4826,8 @@ static int hclge_get_fd_allocation(struct hclge_dev *hdev,
        return ret;
 }
 
-static int hclge_set_fd_key_config(struct hclge_dev *hdev, int stage_num)
+static int hclge_set_fd_key_config(struct hclge_dev *hdev,
+                                  enum HCLGE_FD_STAGE stage_num)
 {
        struct hclge_set_fd_key_config_cmd *req;
        struct hclge_fd_key_cfg *stage;
@@ -4876,9 +4881,6 @@ static int hclge_init_fd_config(struct hclge_dev *hdev)
                return -EOPNOTSUPP;
        }
 
-       hdev->fd_cfg.proto_support =
-               TCP_V4_FLOW | UDP_V4_FLOW | SCTP_V4_FLOW | TCP_V6_FLOW |
-               UDP_V6_FLOW | SCTP_V6_FLOW | IPV4_USER_FLOW | IPV6_USER_FLOW;
        key_cfg = &hdev->fd_cfg.key_cfg[HCLGE_FD_STAGE_1];
        key_cfg->key_sel = HCLGE_FD_KEY_BASE_ON_TUPLE,
        key_cfg->inner_sipv6_word_en = LOW_2_WORDS;
@@ -4892,11 +4894,9 @@ static int hclge_init_fd_config(struct hclge_dev *hdev)
                                BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
 
        /* If use max 400bit key, we can support tuples for ether type */
-       if (hdev->fd_cfg.max_key_length == MAX_KEY_LENGTH) {
-               hdev->fd_cfg.proto_support |= ETHER_FLOW;
+       if (hdev->fd_cfg.fd_mode == HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1)
                key_cfg->tuple_active |=
                                BIT(INNER_DST_MAC) | BIT(INNER_SRC_MAC);
-       }
 
        /* roce_type is used to filter roce frames
         * dst_vport is used to specify the rule
@@ -5006,8 +5006,6 @@ static bool hclge_fd_convert_tuple(u32 tuple_bit, u8 *key_x, u8 *key_y,
                return true;
 
        switch (tuple_bit) {
-       case 0:
-               return false;
        case BIT(INNER_DST_MAC):
                for (i = 0; i < ETH_ALEN; i++) {
                        calc_x(key_x[ETH_ALEN - 1 - i], rule->tuples.dst_mac[i],
@@ -5165,9 +5163,10 @@ static int hclge_config_key(struct hclge_dev *hdev, u8 stage,
        struct hclge_fd_key_cfg *key_cfg = &hdev->fd_cfg.key_cfg[stage];
        u8 key_x[MAX_KEY_BYTES], key_y[MAX_KEY_BYTES];
        u8 *cur_key_x, *cur_key_y;
-       unsigned int i;
-       int ret, tuple_size;
        u8 meta_data_region;
+       u8 tuple_size;
+       int ret;
+       u32 i;
 
        memset(key_x, 0, sizeof(key_x));
        memset(key_y, 0, sizeof(key_y));
@@ -5244,172 +5243,255 @@ static int hclge_config_action(struct hclge_dev *hdev, u8 stage,
        return hclge_fd_ad_config(hdev, stage, ad_data.ad_id, &ad_data);
 }
 
-static int hclge_fd_check_spec(struct hclge_dev *hdev,
-                              struct ethtool_rx_flow_spec *fs, u32 *unused)
+static int hclge_fd_check_tcpip4_tuple(struct ethtool_tcpip4_spec *spec,
+                                      u32 *unused_tuple)
 {
-       struct ethtool_tcpip4_spec *tcp_ip4_spec;
-       struct ethtool_usrip4_spec *usr_ip4_spec;
-       struct ethtool_tcpip6_spec *tcp_ip6_spec;
-       struct ethtool_usrip6_spec *usr_ip6_spec;
-       struct ethhdr *ether_spec;
-
-       if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1])
+       if (!spec || !unused_tuple)
                return -EINVAL;
 
-       if (!(fs->flow_type & hdev->fd_cfg.proto_support))
-               return -EOPNOTSUPP;
-
-       if ((fs->flow_type & FLOW_EXT) &&
-           (fs->h_ext.data[0] != 0 || fs->h_ext.data[1] != 0)) {
-               dev_err(&hdev->pdev->dev, "user-def bytes are not supported\n");
-               return -EOPNOTSUPP;
-       }
+       *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC);
 
-       switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
-       case SCTP_V4_FLOW:
-       case TCP_V4_FLOW:
-       case UDP_V4_FLOW:
-               tcp_ip4_spec = &fs->h_u.tcp_ip4_spec;
-               *unused |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC);
+       if (!spec->ip4src)
+               *unused_tuple |= BIT(INNER_SRC_IP);
 
-               if (!tcp_ip4_spec->ip4src)
-                       *unused |= BIT(INNER_SRC_IP);
+       if (!spec->ip4dst)
+               *unused_tuple |= BIT(INNER_DST_IP);
 
-               if (!tcp_ip4_spec->ip4dst)
-                       *unused |= BIT(INNER_DST_IP);
+       if (!spec->psrc)
+               *unused_tuple |= BIT(INNER_SRC_PORT);
 
-               if (!tcp_ip4_spec->psrc)
-                       *unused |= BIT(INNER_SRC_PORT);
+       if (!spec->pdst)
+               *unused_tuple |= BIT(INNER_DST_PORT);
 
-               if (!tcp_ip4_spec->pdst)
-                       *unused |= BIT(INNER_DST_PORT);
+       if (!spec->tos)
+               *unused_tuple |= BIT(INNER_IP_TOS);
 
-               if (!tcp_ip4_spec->tos)
-                       *unused |= BIT(INNER_IP_TOS);
+       return 0;
+}
 
-               break;
-       case IP_USER_FLOW:
-               usr_ip4_spec = &fs->h_u.usr_ip4_spec;
-               *unused |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
-                       BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
+static int hclge_fd_check_ip4_tuple(struct ethtool_usrip4_spec *spec,
+                                   u32 *unused_tuple)
+{
+       if (!spec || !unused_tuple)
+               return -EINVAL;
 
-               if (!usr_ip4_spec->ip4src)
-                       *unused |= BIT(INNER_SRC_IP);
+       *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
+               BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
 
-               if (!usr_ip4_spec->ip4dst)
-                       *unused |= BIT(INNER_DST_IP);
+       if (!spec->ip4src)
+               *unused_tuple |= BIT(INNER_SRC_IP);
 
-               if (!usr_ip4_spec->tos)
-                       *unused |= BIT(INNER_IP_TOS);
+       if (!spec->ip4dst)
+               *unused_tuple |= BIT(INNER_DST_IP);
 
-               if (!usr_ip4_spec->proto)
-                       *unused |= BIT(INNER_IP_PROTO);
+       if (!spec->tos)
+               *unused_tuple |= BIT(INNER_IP_TOS);
 
-               if (usr_ip4_spec->l4_4_bytes)
-                       return -EOPNOTSUPP;
+       if (!spec->proto)
+               *unused_tuple |= BIT(INNER_IP_PROTO);
 
-               if (usr_ip4_spec->ip_ver != ETH_RX_NFC_IP4)
-                       return -EOPNOTSUPP;
+       if (spec->l4_4_bytes)
+               return -EOPNOTSUPP;
 
-               break;
-       case SCTP_V6_FLOW:
-       case TCP_V6_FLOW:
-       case UDP_V6_FLOW:
-               tcp_ip6_spec = &fs->h_u.tcp_ip6_spec;
-               *unused |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
-                       BIT(INNER_IP_TOS);
+       if (spec->ip_ver != ETH_RX_NFC_IP4)
+               return -EOPNOTSUPP;
 
-               /* check whether src/dst ip address used */
-               if (!tcp_ip6_spec->ip6src[0] && !tcp_ip6_spec->ip6src[1] &&
-                   !tcp_ip6_spec->ip6src[2] && !tcp_ip6_spec->ip6src[3])
-                       *unused |= BIT(INNER_SRC_IP);
+       return 0;
+}
 
-               if (!tcp_ip6_spec->ip6dst[0] && !tcp_ip6_spec->ip6dst[1] &&
-                   !tcp_ip6_spec->ip6dst[2] && !tcp_ip6_spec->ip6dst[3])
-                       *unused |= BIT(INNER_DST_IP);
+static int hclge_fd_check_tcpip6_tuple(struct ethtool_tcpip6_spec *spec,
+                                      u32 *unused_tuple)
+{
+       if (!spec || !unused_tuple)
+               return -EINVAL;
 
-               if (!tcp_ip6_spec->psrc)
-                       *unused |= BIT(INNER_SRC_PORT);
+       *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
+               BIT(INNER_IP_TOS);
 
-               if (!tcp_ip6_spec->pdst)
-                       *unused |= BIT(INNER_DST_PORT);
+       /* check whether src/dst ip address used */
+       if (!spec->ip6src[0] && !spec->ip6src[1] &&
+           !spec->ip6src[2] && !spec->ip6src[3])
+               *unused_tuple |= BIT(INNER_SRC_IP);
 
-               if (tcp_ip6_spec->tclass)
-                       return -EOPNOTSUPP;
+       if (!spec->ip6dst[0] && !spec->ip6dst[1] &&
+           !spec->ip6dst[2] && !spec->ip6dst[3])
+               *unused_tuple |= BIT(INNER_DST_IP);
 
-               break;
-       case IPV6_USER_FLOW:
-               usr_ip6_spec = &fs->h_u.usr_ip6_spec;
-               *unused |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
-                       BIT(INNER_IP_TOS) | BIT(INNER_SRC_PORT) |
-                       BIT(INNER_DST_PORT);
+       if (!spec->psrc)
+               *unused_tuple |= BIT(INNER_SRC_PORT);
 
-               /* check whether src/dst ip address used */
-               if (!usr_ip6_spec->ip6src[0] && !usr_ip6_spec->ip6src[1] &&
-                   !usr_ip6_spec->ip6src[2] && !usr_ip6_spec->ip6src[3])
-                       *unused |= BIT(INNER_SRC_IP);
+       if (!spec->pdst)
+               *unused_tuple |= BIT(INNER_DST_PORT);
 
-               if (!usr_ip6_spec->ip6dst[0] && !usr_ip6_spec->ip6dst[1] &&
-                   !usr_ip6_spec->ip6dst[2] && !usr_ip6_spec->ip6dst[3])
-                       *unused |= BIT(INNER_DST_IP);
+       if (spec->tclass)
+               return -EOPNOTSUPP;
 
-               if (!usr_ip6_spec->l4_proto)
-                       *unused |= BIT(INNER_IP_PROTO);
+       return 0;
+}
 
-               if (usr_ip6_spec->tclass)
-                       return -EOPNOTSUPP;
+static int hclge_fd_check_ip6_tuple(struct ethtool_usrip6_spec *spec,
+                                   u32 *unused_tuple)
+{
+       if (!spec || !unused_tuple)
+               return -EINVAL;
 
-               if (usr_ip6_spec->l4_4_bytes)
-                       return -EOPNOTSUPP;
+       *unused_tuple |= BIT(INNER_SRC_MAC) | BIT(INNER_DST_MAC) |
+               BIT(INNER_IP_TOS) | BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT);
 
-               break;
-       case ETHER_FLOW:
-               ether_spec = &fs->h_u.ether_spec;
-               *unused |= BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) |
-                       BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT) |
-                       BIT(INNER_IP_TOS) | BIT(INNER_IP_PROTO);
+       /* check whether src/dst ip address used */
+       if (!spec->ip6src[0] && !spec->ip6src[1] &&
+           !spec->ip6src[2] && !spec->ip6src[3])
+               *unused_tuple |= BIT(INNER_SRC_IP);
 
-               if (is_zero_ether_addr(ether_spec->h_source))
-                       *unused |= BIT(INNER_SRC_MAC);
+       if (!spec->ip6dst[0] && !spec->ip6dst[1] &&
+           !spec->ip6dst[2] && !spec->ip6dst[3])
+               *unused_tuple |= BIT(INNER_DST_IP);
 
-               if (is_zero_ether_addr(ether_spec->h_dest))
-                       *unused |= BIT(INNER_DST_MAC);
+       if (!spec->l4_proto)
+               *unused_tuple |= BIT(INNER_IP_PROTO);
 
-               if (!ether_spec->h_proto)
-                       *unused |= BIT(INNER_ETH_TYPE);
+       if (spec->tclass)
+               return -EOPNOTSUPP;
 
-               break;
-       default:
+       if (spec->l4_4_bytes)
                return -EOPNOTSUPP;
-       }
 
-       if ((fs->flow_type & FLOW_EXT)) {
-               if (fs->h_ext.vlan_etype)
+       return 0;
+}
+
+static int hclge_fd_check_ether_tuple(struct ethhdr *spec, u32 *unused_tuple)
+{
+       if (!spec || !unused_tuple)
+               return -EINVAL;
+
+       *unused_tuple |= BIT(INNER_SRC_IP) | BIT(INNER_DST_IP) |
+               BIT(INNER_SRC_PORT) | BIT(INNER_DST_PORT) |
+               BIT(INNER_IP_TOS) | BIT(INNER_IP_PROTO);
+
+       if (is_zero_ether_addr(spec->h_source))
+               *unused_tuple |= BIT(INNER_SRC_MAC);
+
+       if (is_zero_ether_addr(spec->h_dest))
+               *unused_tuple |= BIT(INNER_DST_MAC);
+
+       if (!spec->h_proto)
+               *unused_tuple |= BIT(INNER_ETH_TYPE);
+
+       return 0;
+}
+
+static int hclge_fd_check_ext_tuple(struct hclge_dev *hdev,
+                                   struct ethtool_rx_flow_spec *fs,
+                                   u32 *unused_tuple)
+{
+       if (fs->flow_type & FLOW_EXT) {
+               if (fs->h_ext.vlan_etype) {
+                       dev_err(&hdev->pdev->dev, "vlan-etype is not supported!\n");
                        return -EOPNOTSUPP;
+               }
+
                if (!fs->h_ext.vlan_tci)
-                       *unused |= BIT(INNER_VLAN_TAG_FST);
+                       *unused_tuple |= BIT(INNER_VLAN_TAG_FST);
 
-               if (fs->m_ext.vlan_tci) {
-                       if (be16_to_cpu(fs->h_ext.vlan_tci) >= VLAN_N_VID)
-                               return -EINVAL;
+               if (fs->m_ext.vlan_tci &&
+                   be16_to_cpu(fs->h_ext.vlan_tci) >= VLAN_N_VID) {
+                       dev_err(&hdev->pdev->dev,
+                               "failed to config vlan_tci, invalid vlan_tci: %u, max is %u.\n",
+                               ntohs(fs->h_ext.vlan_tci), VLAN_N_VID - 1);
+                       return -EINVAL;
                }
        } else {
-               *unused |= BIT(INNER_VLAN_TAG_FST);
+               *unused_tuple |= BIT(INNER_VLAN_TAG_FST);
        }
 
        if (fs->flow_type & FLOW_MAC_EXT) {
-               if (!(hdev->fd_cfg.proto_support & ETHER_FLOW))
+               if (hdev->fd_cfg.fd_mode !=
+                   HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
+                       dev_err(&hdev->pdev->dev,
+                               "FLOW_MAC_EXT is not supported in current fd mode!\n");
                        return -EOPNOTSUPP;
+               }
 
                if (is_zero_ether_addr(fs->h_ext.h_dest))
-                       *unused |= BIT(INNER_DST_MAC);
+                       *unused_tuple |= BIT(INNER_DST_MAC);
                else
-                       *unused &= ~(BIT(INNER_DST_MAC));
+                       *unused_tuple &= ~BIT(INNER_DST_MAC);
        }
 
        return 0;
 }
 
+static int hclge_fd_check_spec(struct hclge_dev *hdev,
+                              struct ethtool_rx_flow_spec *fs,
+                              u32 *unused_tuple)
+{
+       u32 flow_type;
+       int ret;
+
+       if (fs->location >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
+               dev_err(&hdev->pdev->dev,
+                       "failed to config fd rules, invalid rule location: %u, max is %u\n.",
+                       fs->location,
+                       hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1] - 1);
+               return -EINVAL;
+       }
+
+       if ((fs->flow_type & FLOW_EXT) &&
+           (fs->h_ext.data[0] != 0 || fs->h_ext.data[1] != 0)) {
+               dev_err(&hdev->pdev->dev, "user-def bytes are not supported\n");
+               return -EOPNOTSUPP;
+       }
+
+       flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
+       switch (flow_type) {
+       case SCTP_V4_FLOW:
+       case TCP_V4_FLOW:
+       case UDP_V4_FLOW:
+               ret = hclge_fd_check_tcpip4_tuple(&fs->h_u.tcp_ip4_spec,
+                                                 unused_tuple);
+               break;
+       case IP_USER_FLOW:
+               ret = hclge_fd_check_ip4_tuple(&fs->h_u.usr_ip4_spec,
+                                              unused_tuple);
+               break;
+       case SCTP_V6_FLOW:
+       case TCP_V6_FLOW:
+       case UDP_V6_FLOW:
+               ret = hclge_fd_check_tcpip6_tuple(&fs->h_u.tcp_ip6_spec,
+                                                 unused_tuple);
+               break;
+       case IPV6_USER_FLOW:
+               ret = hclge_fd_check_ip6_tuple(&fs->h_u.usr_ip6_spec,
+                                              unused_tuple);
+               break;
+       case ETHER_FLOW:
+               if (hdev->fd_cfg.fd_mode !=
+                       HCLGE_FD_MODE_DEPTH_2K_WIDTH_400B_STAGE_1) {
+                       dev_err(&hdev->pdev->dev,
+                               "ETHER_FLOW is not supported in current fd mode!\n");
+                       return -EOPNOTSUPP;
+               }
+
+               ret = hclge_fd_check_ether_tuple(&fs->h_u.ether_spec,
+                                                unused_tuple);
+               break;
+       default:
+               dev_err(&hdev->pdev->dev,
+                       "unsupported protocol type, protocol type = %#x\n",
+                       flow_type);
+               return -EOPNOTSUPP;
+       }
+
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "failed to check flow union tuple, ret = %d\n",
+                       ret);
+               return ret;
+       }
+
+       return hclge_fd_check_ext_tuple(hdev, fs, unused_tuple);
+}
+
 static bool hclge_fd_rule_exist(struct hclge_dev *hdev, u16 location)
 {
        struct hclge_fd_rule *rule = NULL;
@@ -5618,7 +5700,7 @@ static int hclge_fd_get_tuple(struct hclge_dev *hdev,
                break;
        }
 
-       if ((fs->flow_type & FLOW_EXT)) {
+       if (fs->flow_type & FLOW_EXT) {
                rule->tuples.vlan_tag1 = be16_to_cpu(fs->h_ext.vlan_tci);
                rule->tuples_mask.vlan_tag1 = be16_to_cpu(fs->m_ext.vlan_tci);
        }
@@ -5673,22 +5755,23 @@ static int hclge_add_fd_entry(struct hnae3_handle *handle,
        u8 action;
        int ret;
 
-       if (!hnae3_dev_fd_supported(hdev))
+       if (!hnae3_dev_fd_supported(hdev)) {
+               dev_err(&hdev->pdev->dev,
+                       "flow table director is not supported\n");
                return -EOPNOTSUPP;
+       }
 
        if (!hdev->fd_en) {
-               dev_warn(&hdev->pdev->dev,
-                        "Please enable flow director first\n");
+               dev_err(&hdev->pdev->dev,
+                       "please enable flow director first\n");
                return -EOPNOTSUPP;
        }
 
        fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
 
        ret = hclge_fd_check_spec(hdev, fs, &unused);
-       if (ret) {
-               dev_err(&hdev->pdev->dev, "Check fd spec failed\n");
+       if (ret)
                return ret;
-       }
 
        if (fs->ring_cookie == RX_CLS_FLOW_DISC) {
                action = HCLGE_FD_ACTION_DROP_PACKET;
@@ -5729,7 +5812,6 @@ static int hclge_add_fd_entry(struct hnae3_handle *handle,
        }
 
        rule->flow_type = fs->flow_type;
-
        rule->location = fs->location;
        rule->unused_tuple = unused;
        rule->vf_id = dst_vport_id;
@@ -5877,178 +5959,131 @@ static int hclge_get_fd_rule_cnt(struct hnae3_handle *handle,
        return 0;
 }
 
-static int hclge_get_fd_rule_info(struct hnae3_handle *handle,
-                                 struct ethtool_rxnfc *cmd)
+static void hclge_fd_get_tcpip4_info(struct hclge_fd_rule *rule,
+                                    struct ethtool_tcpip4_spec *spec,
+                                    struct ethtool_tcpip4_spec *spec_mask)
 {
-       struct hclge_vport *vport = hclge_get_vport(handle);
-       struct hclge_fd_rule *rule = NULL;
-       struct hclge_dev *hdev = vport->back;
-       struct ethtool_rx_flow_spec *fs;
-       struct hlist_node *node2;
-
-       if (!hnae3_dev_fd_supported(hdev))
-               return -EOPNOTSUPP;
-
-       fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
-
-       spin_lock_bh(&hdev->fd_rule_lock);
-
-       hlist_for_each_entry_safe(rule, node2, &hdev->fd_rule_list, rule_node) {
-               if (rule->location >= fs->location)
-                       break;
-       }
-
-       if (!rule || fs->location != rule->location) {
-               spin_unlock_bh(&hdev->fd_rule_lock);
-
-               return -ENOENT;
-       }
-
-       fs->flow_type = rule->flow_type;
-       switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
-       case SCTP_V4_FLOW:
-       case TCP_V4_FLOW:
-       case UDP_V4_FLOW:
-               fs->h_u.tcp_ip4_spec.ip4src =
-                               cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]);
-               fs->m_u.tcp_ip4_spec.ip4src =
-                       rule->unused_tuple & BIT(INNER_SRC_IP) ?
+       spec->ip4src = cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]);
+       spec_mask->ip4src = rule->unused_tuple & BIT(INNER_SRC_IP) ?
                        0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]);
 
-               fs->h_u.tcp_ip4_spec.ip4dst =
-                               cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]);
-               fs->m_u.tcp_ip4_spec.ip4dst =
-                       rule->unused_tuple & BIT(INNER_DST_IP) ?
+       spec->ip4dst = cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]);
+       spec_mask->ip4dst = rule->unused_tuple & BIT(INNER_DST_IP) ?
                        0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]);
 
-               fs->h_u.tcp_ip4_spec.psrc = cpu_to_be16(rule->tuples.src_port);
-               fs->m_u.tcp_ip4_spec.psrc =
-                               rule->unused_tuple & BIT(INNER_SRC_PORT) ?
-                               0 : cpu_to_be16(rule->tuples_mask.src_port);
+       spec->psrc = cpu_to_be16(rule->tuples.src_port);
+       spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ?
+                       0 : cpu_to_be16(rule->tuples_mask.src_port);
 
-               fs->h_u.tcp_ip4_spec.pdst = cpu_to_be16(rule->tuples.dst_port);
-               fs->m_u.tcp_ip4_spec.pdst =
-                               rule->unused_tuple & BIT(INNER_DST_PORT) ?
-                               0 : cpu_to_be16(rule->tuples_mask.dst_port);
+       spec->pdst = cpu_to_be16(rule->tuples.dst_port);
+       spec_mask->pdst = rule->unused_tuple & BIT(INNER_DST_PORT) ?
+                       0 : cpu_to_be16(rule->tuples_mask.dst_port);
 
-               fs->h_u.tcp_ip4_spec.tos = rule->tuples.ip_tos;
-               fs->m_u.tcp_ip4_spec.tos =
-                               rule->unused_tuple & BIT(INNER_IP_TOS) ?
-                               0 : rule->tuples_mask.ip_tos;
+       spec->tos = rule->tuples.ip_tos;
+       spec_mask->tos = rule->unused_tuple & BIT(INNER_IP_TOS) ?
+                       0 : rule->tuples_mask.ip_tos;
+}
 
-               break;
-       case IP_USER_FLOW:
-               fs->h_u.usr_ip4_spec.ip4src =
-                               cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]);
-               fs->m_u.tcp_ip4_spec.ip4src =
-                       rule->unused_tuple & BIT(INNER_SRC_IP) ?
+static void hclge_fd_get_ip4_info(struct hclge_fd_rule *rule,
+                                 struct ethtool_usrip4_spec *spec,
+                                 struct ethtool_usrip4_spec *spec_mask)
+{
+       spec->ip4src = cpu_to_be32(rule->tuples.src_ip[IPV4_INDEX]);
+       spec_mask->ip4src = rule->unused_tuple & BIT(INNER_SRC_IP) ?
                        0 : cpu_to_be32(rule->tuples_mask.src_ip[IPV4_INDEX]);
 
-               fs->h_u.usr_ip4_spec.ip4dst =
-                               cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]);
-               fs->m_u.usr_ip4_spec.ip4dst =
-                       rule->unused_tuple & BIT(INNER_DST_IP) ?
+       spec->ip4dst = cpu_to_be32(rule->tuples.dst_ip[IPV4_INDEX]);
+       spec_mask->ip4dst = rule->unused_tuple & BIT(INNER_DST_IP) ?
                        0 : cpu_to_be32(rule->tuples_mask.dst_ip[IPV4_INDEX]);
 
-               fs->h_u.usr_ip4_spec.tos = rule->tuples.ip_tos;
-               fs->m_u.usr_ip4_spec.tos =
-                               rule->unused_tuple & BIT(INNER_IP_TOS) ?
-                               0 : rule->tuples_mask.ip_tos;
+       spec->tos = rule->tuples.ip_tos;
+       spec_mask->tos = rule->unused_tuple & BIT(INNER_IP_TOS) ?
+                       0 : rule->tuples_mask.ip_tos;
 
-               fs->h_u.usr_ip4_spec.proto = rule->tuples.ip_proto;
-               fs->m_u.usr_ip4_spec.proto =
-                               rule->unused_tuple & BIT(INNER_IP_PROTO) ?
-                               0 : rule->tuples_mask.ip_proto;
+       spec->proto = rule->tuples.ip_proto;
+       spec_mask->proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ?
+                       0 : rule->tuples_mask.ip_proto;
 
-               fs->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
+       spec->ip_ver = ETH_RX_NFC_IP4;
+}
 
-               break;
-       case SCTP_V6_FLOW:
-       case TCP_V6_FLOW:
-       case UDP_V6_FLOW:
-               cpu_to_be32_array(fs->h_u.tcp_ip6_spec.ip6src,
-                                 rule->tuples.src_ip, IPV6_SIZE);
-               if (rule->unused_tuple & BIT(INNER_SRC_IP))
-                       memset(fs->m_u.tcp_ip6_spec.ip6src, 0,
-                              sizeof(int) * IPV6_SIZE);
-               else
-                       cpu_to_be32_array(fs->m_u.tcp_ip6_spec.ip6src,
-                                         rule->tuples_mask.src_ip, IPV6_SIZE);
-
-               cpu_to_be32_array(fs->h_u.tcp_ip6_spec.ip6dst,
-                                 rule->tuples.dst_ip, IPV6_SIZE);
-               if (rule->unused_tuple & BIT(INNER_DST_IP))
-                       memset(fs->m_u.tcp_ip6_spec.ip6dst, 0,
-                              sizeof(int) * IPV6_SIZE);
-               else
-                       cpu_to_be32_array(fs->m_u.tcp_ip6_spec.ip6dst,
-                                         rule->tuples_mask.dst_ip, IPV6_SIZE);
+static void hclge_fd_get_tcpip6_info(struct hclge_fd_rule *rule,
+                                    struct ethtool_tcpip6_spec *spec,
+                                    struct ethtool_tcpip6_spec *spec_mask)
+{
+       cpu_to_be32_array(spec->ip6src,
+                         rule->tuples.src_ip, IPV6_SIZE);
+       cpu_to_be32_array(spec->ip6dst,
+                         rule->tuples.dst_ip, IPV6_SIZE);
+       if (rule->unused_tuple & BIT(INNER_SRC_IP))
+               memset(spec_mask->ip6src, 0, sizeof(spec_mask->ip6src));
+       else
+               cpu_to_be32_array(spec_mask->ip6src, rule->tuples_mask.src_ip,
+                                 IPV6_SIZE);
 
-               fs->h_u.tcp_ip6_spec.psrc = cpu_to_be16(rule->tuples.src_port);
-               fs->m_u.tcp_ip6_spec.psrc =
-                               rule->unused_tuple & BIT(INNER_SRC_PORT) ?
-                               0 : cpu_to_be16(rule->tuples_mask.src_port);
+       if (rule->unused_tuple & BIT(INNER_DST_IP))
+               memset(spec_mask->ip6dst, 0, sizeof(spec_mask->ip6dst));
+       else
+               cpu_to_be32_array(spec_mask->ip6dst, rule->tuples_mask.dst_ip,
+                                 IPV6_SIZE);
 
-               fs->h_u.tcp_ip6_spec.pdst = cpu_to_be16(rule->tuples.dst_port);
-               fs->m_u.tcp_ip6_spec.pdst =
-                               rule->unused_tuple & BIT(INNER_DST_PORT) ?
-                               0 : cpu_to_be16(rule->tuples_mask.dst_port);
+       spec->psrc = cpu_to_be16(rule->tuples.src_port);
+       spec_mask->psrc = rule->unused_tuple & BIT(INNER_SRC_PORT) ?
+                       0 : cpu_to_be16(rule->tuples_mask.src_port);
 
-               break;
-       case IPV6_USER_FLOW:
-               cpu_to_be32_array(fs->h_u.usr_ip6_spec.ip6src,
-                                 rule->tuples.src_ip, IPV6_SIZE);
-               if (rule->unused_tuple & BIT(INNER_SRC_IP))
-                       memset(fs->m_u.usr_ip6_spec.ip6src, 0,
-                              sizeof(int) * IPV6_SIZE);
-               else
-                       cpu_to_be32_array(fs->m_u.usr_ip6_spec.ip6src,
-                                         rule->tuples_mask.src_ip, IPV6_SIZE);
-
-               cpu_to_be32_array(fs->h_u.usr_ip6_spec.ip6dst,
-                                 rule->tuples.dst_ip, IPV6_SIZE);
-               if (rule->unused_tuple & BIT(INNER_DST_IP))
-                       memset(fs->m_u.usr_ip6_spec.ip6dst, 0,
-                              sizeof(int) * IPV6_SIZE);
-               else
-                       cpu_to_be32_array(fs->m_u.usr_ip6_spec.ip6dst,
-                                         rule->tuples_mask.dst_ip, IPV6_SIZE);
+       spec->pdst = cpu_to_be16(rule->tuples.dst_port);
+       spec_mask->pdst = rule->unused_tuple & BIT(INNER_DST_PORT) ?
+                       0 : cpu_to_be16(rule->tuples_mask.dst_port);
+}
 
-               fs->h_u.usr_ip6_spec.l4_proto = rule->tuples.ip_proto;
-               fs->m_u.usr_ip6_spec.l4_proto =
-                               rule->unused_tuple & BIT(INNER_IP_PROTO) ?
-                               0 : rule->tuples_mask.ip_proto;
+static void hclge_fd_get_ip6_info(struct hclge_fd_rule *rule,
+                                 struct ethtool_usrip6_spec *spec,
+                                 struct ethtool_usrip6_spec *spec_mask)
+{
+       cpu_to_be32_array(spec->ip6src, rule->tuples.src_ip, IPV6_SIZE);
+       cpu_to_be32_array(spec->ip6dst, rule->tuples.dst_ip, IPV6_SIZE);
+       if (rule->unused_tuple & BIT(INNER_SRC_IP))
+               memset(spec_mask->ip6src, 0, sizeof(spec_mask->ip6src));
+       else
+               cpu_to_be32_array(spec_mask->ip6src,
+                                 rule->tuples_mask.src_ip, IPV6_SIZE);
 
-               break;
-       case ETHER_FLOW:
-               ether_addr_copy(fs->h_u.ether_spec.h_source,
-                               rule->tuples.src_mac);
-               if (rule->unused_tuple & BIT(INNER_SRC_MAC))
-                       eth_zero_addr(fs->m_u.ether_spec.h_source);
-               else
-                       ether_addr_copy(fs->m_u.ether_spec.h_source,
-                                       rule->tuples_mask.src_mac);
+       if (rule->unused_tuple & BIT(INNER_DST_IP))
+               memset(spec_mask->ip6dst, 0, sizeof(spec_mask->ip6dst));
+       else
+               cpu_to_be32_array(spec_mask->ip6dst,
+                                 rule->tuples_mask.dst_ip, IPV6_SIZE);
 
-               ether_addr_copy(fs->h_u.ether_spec.h_dest,
-                               rule->tuples.dst_mac);
-               if (rule->unused_tuple & BIT(INNER_DST_MAC))
-                       eth_zero_addr(fs->m_u.ether_spec.h_dest);
-               else
-                       ether_addr_copy(fs->m_u.ether_spec.h_dest,
-                                       rule->tuples_mask.dst_mac);
+       spec->l4_proto = rule->tuples.ip_proto;
+       spec_mask->l4_proto = rule->unused_tuple & BIT(INNER_IP_PROTO) ?
+                       0 : rule->tuples_mask.ip_proto;
+}
 
-               fs->h_u.ether_spec.h_proto =
-                               cpu_to_be16(rule->tuples.ether_proto);
-               fs->m_u.ether_spec.h_proto =
-                               rule->unused_tuple & BIT(INNER_ETH_TYPE) ?
-                               0 : cpu_to_be16(rule->tuples_mask.ether_proto);
+static void hclge_fd_get_ether_info(struct hclge_fd_rule *rule,
+                                   struct ethhdr *spec,
+                                   struct ethhdr *spec_mask)
+{
+       ether_addr_copy(spec->h_source, rule->tuples.src_mac);
+       ether_addr_copy(spec->h_dest, rule->tuples.dst_mac);
 
-               break;
-       default:
-               spin_unlock_bh(&hdev->fd_rule_lock);
-               return -EOPNOTSUPP;
-       }
+       if (rule->unused_tuple & BIT(INNER_SRC_MAC))
+               eth_zero_addr(spec_mask->h_source);
+       else
+               ether_addr_copy(spec_mask->h_source, rule->tuples_mask.src_mac);
+
+       if (rule->unused_tuple & BIT(INNER_DST_MAC))
+               eth_zero_addr(spec_mask->h_dest);
+       else
+               ether_addr_copy(spec_mask->h_dest, rule->tuples_mask.dst_mac);
+
+       spec->h_proto = cpu_to_be16(rule->tuples.ether_proto);
+       spec_mask->h_proto = rule->unused_tuple & BIT(INNER_ETH_TYPE) ?
+                       0 : cpu_to_be16(rule->tuples_mask.ether_proto);
+}
 
+static void hclge_fd_get_ext_info(struct ethtool_rx_flow_spec *fs,
+                                 struct hclge_fd_rule *rule)
+{
        if (fs->flow_type & FLOW_EXT) {
                fs->h_ext.vlan_tci = cpu_to_be16(rule->tuples.vlan_tag1);
                fs->m_ext.vlan_tci =
@@ -6065,51 +6100,113 @@ static int hclge_get_fd_rule_info(struct hnae3_handle *handle,
                        ether_addr_copy(fs->m_u.ether_spec.h_dest,
                                        rule->tuples_mask.dst_mac);
        }
-
-       if (rule->action == HCLGE_FD_ACTION_DROP_PACKET) {
-               fs->ring_cookie = RX_CLS_FLOW_DISC;
-       } else {
-               u64 vf_id;
-
-               fs->ring_cookie = rule->queue_id;
-               vf_id = rule->vf_id;
-               vf_id <<= ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF;
-               fs->ring_cookie |= vf_id;
-       }
-
-       spin_unlock_bh(&hdev->fd_rule_lock);
-
-       return 0;
 }
 
-static int hclge_get_all_rules(struct hnae3_handle *handle,
-                              struct ethtool_rxnfc *cmd, u32 *rule_locs)
+static int hclge_get_fd_rule_info(struct hnae3_handle *handle,
+                                 struct ethtool_rxnfc *cmd)
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_fd_rule *rule = NULL;
        struct hclge_dev *hdev = vport->back;
-       struct hclge_fd_rule *rule;
+       struct ethtool_rx_flow_spec *fs;
        struct hlist_node *node2;
-       int cnt = 0;
 
        if (!hnae3_dev_fd_supported(hdev))
                return -EOPNOTSUPP;
 
-       cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1];
+       fs = (struct ethtool_rx_flow_spec *)&cmd->fs;
 
        spin_lock_bh(&hdev->fd_rule_lock);
-       hlist_for_each_entry_safe(rule, node2,
-                                 &hdev->fd_rule_list, rule_node) {
-               if (cnt == cmd->rule_cnt) {
-                       spin_unlock_bh(&hdev->fd_rule_lock);
-                       return -EMSGSIZE;
-               }
 
-               rule_locs[cnt] = rule->location;
-               cnt++;
+       hlist_for_each_entry_safe(rule, node2, &hdev->fd_rule_list, rule_node) {
+               if (rule->location >= fs->location)
+                       break;
        }
 
-       spin_unlock_bh(&hdev->fd_rule_lock);
-
+       if (!rule || fs->location != rule->location) {
+               spin_unlock_bh(&hdev->fd_rule_lock);
+
+               return -ENOENT;
+       }
+
+       fs->flow_type = rule->flow_type;
+       switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
+       case SCTP_V4_FLOW:
+       case TCP_V4_FLOW:
+       case UDP_V4_FLOW:
+               hclge_fd_get_tcpip4_info(rule, &fs->h_u.tcp_ip4_spec,
+                                        &fs->m_u.tcp_ip4_spec);
+               break;
+       case IP_USER_FLOW:
+               hclge_fd_get_ip4_info(rule, &fs->h_u.usr_ip4_spec,
+                                     &fs->m_u.usr_ip4_spec);
+               break;
+       case SCTP_V6_FLOW:
+       case TCP_V6_FLOW:
+       case UDP_V6_FLOW:
+               hclge_fd_get_tcpip6_info(rule, &fs->h_u.tcp_ip6_spec,
+                                        &fs->m_u.tcp_ip6_spec);
+               break;
+       case IPV6_USER_FLOW:
+               hclge_fd_get_ip6_info(rule, &fs->h_u.usr_ip6_spec,
+                                     &fs->m_u.usr_ip6_spec);
+               break;
+       /* The flow type of fd rule has been checked before adding in to rule
+        * list. As other flow types have been handled, it must be ETHER_FLOW
+        * for the default case
+        */
+       default:
+               hclge_fd_get_ether_info(rule, &fs->h_u.ether_spec,
+                                       &fs->m_u.ether_spec);
+               break;
+       }
+
+       hclge_fd_get_ext_info(fs, rule);
+
+       if (rule->action == HCLGE_FD_ACTION_DROP_PACKET) {
+               fs->ring_cookie = RX_CLS_FLOW_DISC;
+       } else {
+               u64 vf_id;
+
+               fs->ring_cookie = rule->queue_id;
+               vf_id = rule->vf_id;
+               vf_id <<= ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF;
+               fs->ring_cookie |= vf_id;
+       }
+
+       spin_unlock_bh(&hdev->fd_rule_lock);
+
+       return 0;
+}
+
+static int hclge_get_all_rules(struct hnae3_handle *handle,
+                              struct ethtool_rxnfc *cmd, u32 *rule_locs)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+       struct hclge_fd_rule *rule;
+       struct hlist_node *node2;
+       int cnt = 0;
+
+       if (!hnae3_dev_fd_supported(hdev))
+               return -EOPNOTSUPP;
+
+       cmd->data = hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1];
+
+       spin_lock_bh(&hdev->fd_rule_lock);
+       hlist_for_each_entry_safe(rule, node2,
+                                 &hdev->fd_rule_list, rule_node) {
+               if (cnt == cmd->rule_cnt) {
+                       spin_unlock_bh(&hdev->fd_rule_lock);
+                       return -EMSGSIZE;
+               }
+
+               rule_locs[cnt] = rule->location;
+               cnt++;
+       }
+
+       spin_unlock_bh(&hdev->fd_rule_lock);
+
        cmd->rule_cnt = cnt;
 
        return 0;
@@ -6202,7 +6299,6 @@ static int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id,
         */
        if (hdev->fd_active_type == HCLGE_FD_EP_ACTIVE) {
                spin_unlock_bh(&hdev->fd_rule_lock);
-
                return -EOPNOTSUPP;
        }
 
@@ -6216,14 +6312,12 @@ static int hclge_add_fd_entry_by_arfs(struct hnae3_handle *handle, u16 queue_id,
                bit_id = find_first_zero_bit(hdev->fd_bmap, MAX_FD_FILTER_NUM);
                if (bit_id >= hdev->fd_cfg.rule_num[HCLGE_FD_STAGE_1]) {
                        spin_unlock_bh(&hdev->fd_rule_lock);
-
                        return -ENOSPC;
                }
 
                rule = kzalloc(sizeof(*rule), GFP_ATOMIC);
                if (!rule) {
                        spin_unlock_bh(&hdev->fd_rule_lock);
-
                        return -ENOMEM;
                }
 
@@ -6834,8 +6928,22 @@ static void hclge_ae_stop(struct hnae3_handle *handle)
 
 int hclge_vport_start(struct hclge_vport *vport)
 {
+       struct hclge_dev *hdev = vport->back;
+
        set_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
        vport->last_active_jiffies = jiffies;
+
+       if (test_bit(vport->vport_id, hdev->vport_config_block)) {
+               if (vport->vport_id) {
+                       hclge_restore_mac_table_common(vport);
+                       hclge_restore_vport_vlan_table(vport);
+               } else {
+                       hclge_restore_hw_table(hdev);
+               }
+       }
+
+       clear_bit(vport->vport_id, hdev->vport_config_block);
+
        return 0;
 }
 
@@ -6872,17 +6980,11 @@ static int hclge_get_mac_vlan_cmd_status(struct hclge_vport *vport,
        }
 
        if (op == HCLGE_MAC_VLAN_ADD) {
-               if ((!resp_code) || (resp_code == 1)) {
+               if (!resp_code || resp_code == 1)
                        return 0;
-               } else if (resp_code == HCLGE_ADD_UC_OVERFLOW) {
-                       dev_err(&hdev->pdev->dev,
-                               "add mac addr failed for uc_overflow.\n");
-                       return -ENOSPC;
-               } else if (resp_code == HCLGE_ADD_MC_OVERFLOW) {
-                       dev_err(&hdev->pdev->dev,
-                               "add mac addr failed for mc_overflow.\n");
+               else if (resp_code == HCLGE_ADD_UC_OVERFLOW ||
+                        resp_code == HCLGE_ADD_MC_OVERFLOW)
                        return -ENOSPC;
-               }
 
                dev_err(&hdev->pdev->dev,
                        "add mac addr failed for undefined, code=%u.\n",
@@ -7106,52 +7208,8 @@ static int hclge_add_mac_vlan_tbl(struct hclge_vport *vport,
        return cfg_status;
 }
 
-static int hclge_init_umv_space(struct hclge_dev *hdev)
-{
-       u16 allocated_size = 0;
-       int ret;
-
-       ret = hclge_set_umv_space(hdev, hdev->wanted_umv_size, &allocated_size,
-                                 true);
-       if (ret)
-               return ret;
-
-       if (allocated_size < hdev->wanted_umv_size)
-               dev_warn(&hdev->pdev->dev,
-                        "Alloc umv space failed, want %u, get %u\n",
-                        hdev->wanted_umv_size, allocated_size);
-
-       mutex_init(&hdev->umv_mutex);
-       hdev->max_umv_size = allocated_size;
-       /* divide max_umv_size by (hdev->num_req_vfs + 2), in order to
-        * preserve some unicast mac vlan table entries shared by pf
-        * and its vfs.
-        */
-       hdev->priv_umv_size = hdev->max_umv_size / (hdev->num_req_vfs + 2);
-       hdev->share_umv_size = hdev->priv_umv_size +
-                       hdev->max_umv_size % (hdev->num_req_vfs + 2);
-
-       return 0;
-}
-
-static int hclge_uninit_umv_space(struct hclge_dev *hdev)
-{
-       int ret;
-
-       if (hdev->max_umv_size > 0) {
-               ret = hclge_set_umv_space(hdev, hdev->max_umv_size, NULL,
-                                         false);
-               if (ret)
-                       return ret;
-               hdev->max_umv_size = 0;
-       }
-       mutex_destroy(&hdev->umv_mutex);
-
-       return 0;
-}
-
 static int hclge_set_umv_space(struct hclge_dev *hdev, u16 space_size,
-                              u16 *allocated_size, bool is_alloc)
+                              u16 *allocated_size)
 {
        struct hclge_umv_spc_alc_cmd *req;
        struct hclge_desc desc;
@@ -7159,21 +7217,39 @@ static int hclge_set_umv_space(struct hclge_dev *hdev, u16 space_size,
 
        req = (struct hclge_umv_spc_alc_cmd *)desc.data;
        hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MAC_VLAN_ALLOCATE, false);
-       if (!is_alloc)
-               hnae3_set_bit(req->allocate, HCLGE_UMV_SPC_ALC_B, 1);
 
        req->space_size = cpu_to_le32(space_size);
 
        ret = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (ret) {
-               dev_err(&hdev->pdev->dev,
-                       "%s umv space failed for cmd_send, ret =%d\n",
-                       is_alloc ? "allocate" : "free", ret);
+               dev_err(&hdev->pdev->dev, "failed to set umv space, ret = %d\n",
+                       ret);
                return ret;
        }
 
-       if (is_alloc && allocated_size)
-               *allocated_size = le32_to_cpu(desc.data[1]);
+       *allocated_size = le32_to_cpu(desc.data[1]);
+
+       return 0;
+}
+
+static int hclge_init_umv_space(struct hclge_dev *hdev)
+{
+       u16 allocated_size = 0;
+       int ret;
+
+       ret = hclge_set_umv_space(hdev, hdev->wanted_umv_size, &allocated_size);
+       if (ret)
+               return ret;
+
+       if (allocated_size < hdev->wanted_umv_size)
+               dev_warn(&hdev->pdev->dev,
+                        "failed to alloc umv space, want %u, get %u\n",
+                        hdev->wanted_umv_size, allocated_size);
+
+       hdev->max_umv_size = allocated_size;
+       hdev->priv_umv_size = hdev->max_umv_size / (hdev->num_alloc_vport + 1);
+       hdev->share_umv_size = hdev->priv_umv_size +
+                       hdev->max_umv_size % (hdev->num_alloc_vport + 1);
 
        return 0;
 }
@@ -7188,21 +7264,25 @@ static void hclge_reset_umv_space(struct hclge_dev *hdev)
                vport->used_umv_num = 0;
        }
 
-       mutex_lock(&hdev->umv_mutex);
+       mutex_lock(&hdev->vport_lock);
        hdev->share_umv_size = hdev->priv_umv_size +
-                       hdev->max_umv_size % (hdev->num_req_vfs + 2);
-       mutex_unlock(&hdev->umv_mutex);
+                       hdev->max_umv_size % (hdev->num_alloc_vport + 1);
+       mutex_unlock(&hdev->vport_lock);
 }
 
-static bool hclge_is_umv_space_full(struct hclge_vport *vport)
+static bool hclge_is_umv_space_full(struct hclge_vport *vport, bool need_lock)
 {
        struct hclge_dev *hdev = vport->back;
        bool is_full;
 
-       mutex_lock(&hdev->umv_mutex);
+       if (need_lock)
+               mutex_lock(&hdev->vport_lock);
+
        is_full = (vport->used_umv_num >= hdev->priv_umv_size &&
                   hdev->share_umv_size == 0);
-       mutex_unlock(&hdev->umv_mutex);
+
+       if (need_lock)
+               mutex_unlock(&hdev->vport_lock);
 
        return is_full;
 }
@@ -7211,7 +7291,6 @@ static void hclge_update_umv_space(struct hclge_vport *vport, bool is_free)
 {
        struct hclge_dev *hdev = vport->back;
 
-       mutex_lock(&hdev->umv_mutex);
        if (is_free) {
                if (vport->used_umv_num > hdev->priv_umv_size)
                        hdev->share_umv_size++;
@@ -7224,7 +7303,99 @@ static void hclge_update_umv_space(struct hclge_vport *vport, bool is_free)
                        hdev->share_umv_size--;
                vport->used_umv_num++;
        }
-       mutex_unlock(&hdev->umv_mutex);
+}
+
+static struct hclge_mac_node *hclge_find_mac_node(struct list_head *list,
+                                                 const u8 *mac_addr)
+{
+       struct hclge_mac_node *mac_node, *tmp;
+
+       list_for_each_entry_safe(mac_node, tmp, list, node)
+               if (ether_addr_equal(mac_addr, mac_node->mac_addr))
+                       return mac_node;
+
+       return NULL;
+}
+
+static void hclge_update_mac_node(struct hclge_mac_node *mac_node,
+                                 enum HCLGE_MAC_NODE_STATE state)
+{
+       switch (state) {
+       /* from set_rx_mode or tmp_add_list */
+       case HCLGE_MAC_TO_ADD:
+               if (mac_node->state == HCLGE_MAC_TO_DEL)
+                       mac_node->state = HCLGE_MAC_ACTIVE;
+               break;
+       /* only from set_rx_mode */
+       case HCLGE_MAC_TO_DEL:
+               if (mac_node->state == HCLGE_MAC_TO_ADD) {
+                       list_del(&mac_node->node);
+                       kfree(mac_node);
+               } else {
+                       mac_node->state = HCLGE_MAC_TO_DEL;
+               }
+               break;
+       /* only from tmp_add_list, the mac_node->state won't be
+        * ACTIVE.
+        */
+       case HCLGE_MAC_ACTIVE:
+               if (mac_node->state == HCLGE_MAC_TO_ADD)
+                       mac_node->state = HCLGE_MAC_ACTIVE;
+
+               break;
+       }
+}
+
+int hclge_update_mac_list(struct hclge_vport *vport,
+                         enum HCLGE_MAC_NODE_STATE state,
+                         enum HCLGE_MAC_ADDR_TYPE mac_type,
+                         const unsigned char *addr)
+{
+       struct hclge_dev *hdev = vport->back;
+       struct hclge_mac_node *mac_node;
+       struct list_head *list;
+
+       list = (mac_type == HCLGE_MAC_ADDR_UC) ?
+               &vport->uc_mac_list : &vport->mc_mac_list;
+
+       spin_lock_bh(&vport->mac_list_lock);
+
+       /* if the mac addr is already in the mac list, no need to add a new
+        * one into it, just check the mac addr state, convert it to a new
+        * new state, or just remove it, or do nothing.
+        */
+       mac_node = hclge_find_mac_node(list, addr);
+       if (mac_node) {
+               hclge_update_mac_node(mac_node, state);
+               spin_unlock_bh(&vport->mac_list_lock);
+               set_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE, &vport->state);
+               return 0;
+       }
+
+       /* if this address is never added, unnecessary to delete */
+       if (state == HCLGE_MAC_TO_DEL) {
+               spin_unlock_bh(&vport->mac_list_lock);
+               dev_err(&hdev->pdev->dev,
+                       "failed to delete address %pM from mac list\n",
+                       addr);
+               return -ENOENT;
+       }
+
+       mac_node = kzalloc(sizeof(*mac_node), GFP_ATOMIC);
+       if (!mac_node) {
+               spin_unlock_bh(&vport->mac_list_lock);
+               return -ENOMEM;
+       }
+
+       set_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE, &vport->state);
+
+       mac_node->state = state;
+       ether_addr_copy(mac_node->mac_addr, addr);
+       list_add_tail(&mac_node->node, list);
+
+       spin_unlock_bh(&vport->mac_list_lock);
+
+       return 0;
 }
 
 static int hclge_add_uc_addr(struct hnae3_handle *handle,
@@ -7232,7 +7403,8 @@ static int hclge_add_uc_addr(struct hnae3_handle *handle,
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
 
-       return hclge_add_uc_addr_common(vport, addr);
+       return hclge_update_mac_list(vport, HCLGE_MAC_TO_ADD, HCLGE_MAC_ADDR_UC,
+                                    addr);
 }
 
 int hclge_add_uc_addr_common(struct hclge_vport *vport,
@@ -7271,15 +7443,19 @@ int hclge_add_uc_addr_common(struct hclge_vport *vport,
         */
        ret = hclge_lookup_mac_vlan_tbl(vport, &req, &desc, false);
        if (ret == -ENOENT) {
-               if (!hclge_is_umv_space_full(vport)) {
+               mutex_lock(&hdev->vport_lock);
+               if (!hclge_is_umv_space_full(vport, false)) {
                        ret = hclge_add_mac_vlan_tbl(vport, &req, NULL);
                        if (!ret)
                                hclge_update_umv_space(vport, false);
+                       mutex_unlock(&hdev->vport_lock);
                        return ret;
                }
+               mutex_unlock(&hdev->vport_lock);
 
-               dev_err(&hdev->pdev->dev, "UC MAC table full(%u)\n",
-                       hdev->priv_umv_size);
+               if (!(vport->overflow_promisc_flags & HNAE3_OVERFLOW_UPE))
+                       dev_err(&hdev->pdev->dev, "UC MAC table full(%u)\n",
+                               hdev->priv_umv_size);
 
                return -ENOSPC;
        }
@@ -7303,7 +7479,8 @@ static int hclge_rm_uc_addr(struct hnae3_handle *handle,
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
 
-       return hclge_rm_uc_addr_common(vport, addr);
+       return hclge_update_mac_list(vport, HCLGE_MAC_TO_DEL, HCLGE_MAC_ADDR_UC,
+                                    addr);
 }
 
 int hclge_rm_uc_addr_common(struct hclge_vport *vport,
@@ -7326,8 +7503,13 @@ int hclge_rm_uc_addr_common(struct hclge_vport *vport,
        hnae3_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0);
        hclge_prepare_mac_addr(&req, addr, false);
        ret = hclge_remove_mac_vlan_tbl(vport, &req);
-       if (!ret)
+       if (!ret) {
+               mutex_lock(&hdev->vport_lock);
                hclge_update_umv_space(vport, true);
+               mutex_unlock(&hdev->vport_lock);
+       } else if (ret == -ENOENT) {
+               ret = 0;
+       }
 
        return ret;
 }
@@ -7337,7 +7519,8 @@ static int hclge_add_mc_addr(struct hnae3_handle *handle,
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
 
-       return hclge_add_mc_addr_common(vport, addr);
+       return hclge_update_mac_list(vport, HCLGE_MAC_TO_ADD, HCLGE_MAC_ADDR_MC,
+                                    addr);
 }
 
 int hclge_add_mc_addr_common(struct hclge_vport *vport,
@@ -7369,7 +7552,9 @@ int hclge_add_mc_addr_common(struct hclge_vport *vport,
                return status;
        status = hclge_add_mac_vlan_tbl(vport, &req, desc);
 
-       if (status == -ENOSPC)
+       /* if already overflow, not to print each time */
+       if (status == -ENOSPC &&
+           !(vport->overflow_promisc_flags & HNAE3_OVERFLOW_MPE))
                dev_err(&hdev->pdev->dev, "mc mac vlan table is full\n");
 
        return status;
@@ -7380,7 +7565,8 @@ static int hclge_rm_mc_addr(struct hnae3_handle *handle,
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
 
-       return hclge_rm_mc_addr_common(vport, addr);
+       return hclge_update_mac_list(vport, HCLGE_MAC_TO_DEL, HCLGE_MAC_ADDR_MC,
+                                    addr);
 }
 
 int hclge_rm_mc_addr_common(struct hclge_vport *vport,
@@ -7415,111 +7601,354 @@ int hclge_rm_mc_addr_common(struct hclge_vport *vport,
                        /* Not all the vfid is zero, update the vfid */
                        status = hclge_add_mac_vlan_tbl(vport, &req, desc);
 
-       } else {
-               /* Maybe this mac address is in mta table, but it cannot be
-                * deleted here because an entry of mta represents an address
-                * range rather than a specific address. the delete action to
-                * all entries will take effect in update_mta_status called by
-                * hns3_nic_set_rx_mode.
-                */
+       } else if (status == -ENOENT) {
                status = 0;
        }
 
        return status;
 }
 
-void hclge_add_vport_mac_table(struct hclge_vport *vport, const u8 *mac_addr,
-                              enum HCLGE_MAC_ADDR_TYPE mac_type)
+static void hclge_sync_vport_mac_list(struct hclge_vport *vport,
+                                     struct list_head *list,
+                                     int (*sync)(struct hclge_vport *,
+                                                 const unsigned char *))
 {
-       struct hclge_vport_mac_addr_cfg *mac_cfg;
-       struct list_head *list;
+       struct hclge_mac_node *mac_node, *tmp;
+       int ret;
 
-       if (!vport->vport_id)
-               return;
+       list_for_each_entry_safe(mac_node, tmp, list, node) {
+               ret = sync(vport, mac_node->mac_addr);
+               if (!ret) {
+                       mac_node->state = HCLGE_MAC_ACTIVE;
+               } else {
+                       set_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE,
+                               &vport->state);
+                       break;
+               }
+       }
+}
 
-       mac_cfg = kzalloc(sizeof(*mac_cfg), GFP_KERNEL);
-       if (!mac_cfg)
-               return;
+static void hclge_unsync_vport_mac_list(struct hclge_vport *vport,
+                                       struct list_head *list,
+                                       int (*unsync)(struct hclge_vport *,
+                                                     const unsigned char *))
+{
+       struct hclge_mac_node *mac_node, *tmp;
+       int ret;
+
+       list_for_each_entry_safe(mac_node, tmp, list, node) {
+               ret = unsync(vport, mac_node->mac_addr);
+               if (!ret || ret == -ENOENT) {
+                       list_del(&mac_node->node);
+                       kfree(mac_node);
+               } else {
+                       set_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE,
+                               &vport->state);
+                       break;
+               }
+       }
+}
 
-       mac_cfg->hd_tbl_status = true;
-       memcpy(mac_cfg->mac_addr, mac_addr, ETH_ALEN);
+static bool hclge_sync_from_add_list(struct list_head *add_list,
+                                    struct list_head *mac_list)
+{
+       struct hclge_mac_node *mac_node, *tmp, *new_node;
+       bool all_added = true;
 
-       list = (mac_type == HCLGE_MAC_ADDR_UC) ?
-              &vport->uc_mac_list : &vport->mc_mac_list;
+       list_for_each_entry_safe(mac_node, tmp, add_list, node) {
+               if (mac_node->state == HCLGE_MAC_TO_ADD)
+                       all_added = false;
 
-       list_add_tail(&mac_cfg->node, list);
+               /* if the mac address from tmp_add_list is not in the
+                * uc/mc_mac_list, it means have received a TO_DEL request
+                * during the time window of adding the mac address into mac
+                * table. if mac_node state is ACTIVE, then change it to TO_DEL,
+                * then it will be removed at next time. else it must be TO_ADD,
+                * this address hasn't been added into mac table,
+                * so just remove the mac node.
+                */
+               new_node = hclge_find_mac_node(mac_list, mac_node->mac_addr);
+               if (new_node) {
+                       hclge_update_mac_node(new_node, mac_node->state);
+                       list_del(&mac_node->node);
+                       kfree(mac_node);
+               } else if (mac_node->state == HCLGE_MAC_ACTIVE) {
+                       mac_node->state = HCLGE_MAC_TO_DEL;
+                       list_del(&mac_node->node);
+                       list_add_tail(&mac_node->node, mac_list);
+               } else {
+                       list_del(&mac_node->node);
+                       kfree(mac_node);
+               }
+       }
+
+       return all_added;
 }
 
-void hclge_rm_vport_mac_table(struct hclge_vport *vport, const u8 *mac_addr,
-                             bool is_write_tbl,
-                             enum HCLGE_MAC_ADDR_TYPE mac_type)
+static void hclge_sync_from_del_list(struct list_head *del_list,
+                                    struct list_head *mac_list)
 {
-       struct hclge_vport_mac_addr_cfg *mac_cfg, *tmp;
-       struct list_head *list;
-       bool uc_flag, mc_flag;
+       struct hclge_mac_node *mac_node, *tmp, *new_node;
 
-       list = (mac_type == HCLGE_MAC_ADDR_UC) ?
-              &vport->uc_mac_list : &vport->mc_mac_list;
+       list_for_each_entry_safe(mac_node, tmp, del_list, node) {
+               new_node = hclge_find_mac_node(mac_list, mac_node->mac_addr);
+               if (new_node) {
+                       /* If the mac addr exists in the mac list, it means
+                        * received a new TO_ADD request during the time window
+                        * of configuring the mac address. For the mac node
+                        * state is TO_ADD, and the address is already in the
+                        * in the hardware(due to delete fail), so we just need
+                        * to change the mac node state to ACTIVE.
+                        */
+                       new_node->state = HCLGE_MAC_ACTIVE;
+                       list_del(&mac_node->node);
+                       kfree(mac_node);
+               } else {
+                       list_del(&mac_node->node);
+                       list_add_tail(&mac_node->node, mac_list);
+               }
+       }
+}
 
-       uc_flag = is_write_tbl && mac_type == HCLGE_MAC_ADDR_UC;
-       mc_flag = is_write_tbl && mac_type == HCLGE_MAC_ADDR_MC;
+static void hclge_update_overflow_flags(struct hclge_vport *vport,
+                                       enum HCLGE_MAC_ADDR_TYPE mac_type,
+                                       bool is_all_added)
+{
+       if (mac_type == HCLGE_MAC_ADDR_UC) {
+               if (is_all_added)
+                       vport->overflow_promisc_flags &= ~HNAE3_OVERFLOW_UPE;
+               else
+                       vport->overflow_promisc_flags |= HNAE3_OVERFLOW_UPE;
+       } else {
+               if (is_all_added)
+                       vport->overflow_promisc_flags &= ~HNAE3_OVERFLOW_MPE;
+               else
+                       vport->overflow_promisc_flags |= HNAE3_OVERFLOW_MPE;
+       }
+}
 
-       list_for_each_entry_safe(mac_cfg, tmp, list, node) {
-               if (ether_addr_equal(mac_cfg->mac_addr, mac_addr)) {
-                       if (uc_flag && mac_cfg->hd_tbl_status)
-                               hclge_rm_uc_addr_common(vport, mac_addr);
+static void hclge_sync_vport_mac_table(struct hclge_vport *vport,
+                                      enum HCLGE_MAC_ADDR_TYPE mac_type)
+{
+       struct hclge_mac_node *mac_node, *tmp, *new_node;
+       struct list_head tmp_add_list, tmp_del_list;
+       struct list_head *list;
+       bool all_added;
 
-                       if (mc_flag && mac_cfg->hd_tbl_status)
-                               hclge_rm_mc_addr_common(vport, mac_addr);
+       INIT_LIST_HEAD(&tmp_add_list);
+       INIT_LIST_HEAD(&tmp_del_list);
 
-                       list_del(&mac_cfg->node);
-                       kfree(mac_cfg);
+       /* move the mac addr to the tmp_add_list and tmp_del_list, then
+        * we can add/delete these mac addr outside the spin lock
+        */
+       list = (mac_type == HCLGE_MAC_ADDR_UC) ?
+               &vport->uc_mac_list : &vport->mc_mac_list;
+
+       spin_lock_bh(&vport->mac_list_lock);
+
+       list_for_each_entry_safe(mac_node, tmp, list, node) {
+               switch (mac_node->state) {
+               case HCLGE_MAC_TO_DEL:
+                       list_del(&mac_node->node);
+                       list_add_tail(&mac_node->node, &tmp_del_list);
+                       break;
+               case HCLGE_MAC_TO_ADD:
+                       new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC);
+                       if (!new_node)
+                               goto stop_traverse;
+                       ether_addr_copy(new_node->mac_addr, mac_node->mac_addr);
+                       new_node->state = mac_node->state;
+                       list_add_tail(&new_node->node, &tmp_add_list);
+                       break;
+               default:
                        break;
                }
        }
+
+stop_traverse:
+       spin_unlock_bh(&vport->mac_list_lock);
+
+       /* delete first, in order to get max mac table space for adding */
+       if (mac_type == HCLGE_MAC_ADDR_UC) {
+               hclge_unsync_vport_mac_list(vport, &tmp_del_list,
+                                           hclge_rm_uc_addr_common);
+               hclge_sync_vport_mac_list(vport, &tmp_add_list,
+                                         hclge_add_uc_addr_common);
+       } else {
+               hclge_unsync_vport_mac_list(vport, &tmp_del_list,
+                                           hclge_rm_mc_addr_common);
+               hclge_sync_vport_mac_list(vport, &tmp_add_list,
+                                         hclge_add_mc_addr_common);
+       }
+
+       /* if some mac addresses were added/deleted fail, move back to the
+        * mac_list, and retry at next time.
+        */
+       spin_lock_bh(&vport->mac_list_lock);
+
+       hclge_sync_from_del_list(&tmp_del_list, list);
+       all_added = hclge_sync_from_add_list(&tmp_add_list, list);
+
+       spin_unlock_bh(&vport->mac_list_lock);
+
+       hclge_update_overflow_flags(vport, mac_type, all_added);
+}
+
+static bool hclge_need_sync_mac_table(struct hclge_vport *vport)
+{
+       struct hclge_dev *hdev = vport->back;
+
+       if (test_bit(vport->vport_id, hdev->vport_config_block))
+               return false;
+
+       if (test_and_clear_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE, &vport->state))
+               return true;
+
+       return false;
+}
+
+static void hclge_sync_mac_table(struct hclge_dev *hdev)
+{
+       int i;
+
+       for (i = 0; i < hdev->num_alloc_vport; i++) {
+               struct hclge_vport *vport = &hdev->vport[i];
+
+               if (!hclge_need_sync_mac_table(vport))
+                       continue;
+
+               hclge_sync_vport_mac_table(vport, HCLGE_MAC_ADDR_UC);
+               hclge_sync_vport_mac_table(vport, HCLGE_MAC_ADDR_MC);
+       }
 }
 
 void hclge_rm_vport_all_mac_table(struct hclge_vport *vport, bool is_del_list,
                                  enum HCLGE_MAC_ADDR_TYPE mac_type)
 {
-       struct hclge_vport_mac_addr_cfg *mac_cfg, *tmp;
-       struct list_head *list;
+       int (*unsync)(struct hclge_vport *vport, const unsigned char *addr);
+       struct hclge_mac_node *mac_cfg, *tmp;
+       struct hclge_dev *hdev = vport->back;
+       struct list_head tmp_del_list, *list;
+       int ret;
 
-       list = (mac_type == HCLGE_MAC_ADDR_UC) ?
-              &vport->uc_mac_list : &vport->mc_mac_list;
+       if (mac_type == HCLGE_MAC_ADDR_UC) {
+               list = &vport->uc_mac_list;
+               unsync = hclge_rm_uc_addr_common;
+       } else {
+               list = &vport->mc_mac_list;
+               unsync = hclge_rm_mc_addr_common;
+       }
 
-       list_for_each_entry_safe(mac_cfg, tmp, list, node) {
-               if (mac_type == HCLGE_MAC_ADDR_UC && mac_cfg->hd_tbl_status)
-                       hclge_rm_uc_addr_common(vport, mac_cfg->mac_addr);
+       INIT_LIST_HEAD(&tmp_del_list);
 
-               if (mac_type == HCLGE_MAC_ADDR_MC && mac_cfg->hd_tbl_status)
-                       hclge_rm_mc_addr_common(vport, mac_cfg->mac_addr);
+       if (!is_del_list)
+               set_bit(vport->vport_id, hdev->vport_config_block);
 
-               mac_cfg->hd_tbl_status = false;
-               if (is_del_list) {
+       spin_lock_bh(&vport->mac_list_lock);
+
+       list_for_each_entry_safe(mac_cfg, tmp, list, node) {
+               switch (mac_cfg->state) {
+               case HCLGE_MAC_TO_DEL:
+               case HCLGE_MAC_ACTIVE:
                        list_del(&mac_cfg->node);
-                       kfree(mac_cfg);
+                       list_add_tail(&mac_cfg->node, &tmp_del_list);
+                       break;
+               case HCLGE_MAC_TO_ADD:
+                       if (is_del_list) {
+                               list_del(&mac_cfg->node);
+                               kfree(mac_cfg);
+                       }
+                       break;
                }
        }
+
+       spin_unlock_bh(&vport->mac_list_lock);
+
+       list_for_each_entry_safe(mac_cfg, tmp, &tmp_del_list, node) {
+               ret = unsync(vport, mac_cfg->mac_addr);
+               if (!ret || ret == -ENOENT) {
+                       /* clear all mac addr from hardware, but remain these
+                        * mac addr in the mac list, and restore them after
+                        * vf reset finished.
+                        */
+                       if (!is_del_list &&
+                           mac_cfg->state == HCLGE_MAC_ACTIVE) {
+                               mac_cfg->state = HCLGE_MAC_TO_ADD;
+                       } else {
+                               list_del(&mac_cfg->node);
+                               kfree(mac_cfg);
+                       }
+               } else if (is_del_list) {
+                       mac_cfg->state = HCLGE_MAC_TO_DEL;
+               }
+       }
+
+       spin_lock_bh(&vport->mac_list_lock);
+
+       hclge_sync_from_del_list(&tmp_del_list, list);
+
+       spin_unlock_bh(&vport->mac_list_lock);
+}
+
+/* remove all mac address when uninitailize */
+static void hclge_uninit_vport_mac_list(struct hclge_vport *vport,
+                                       enum HCLGE_MAC_ADDR_TYPE mac_type)
+{
+       struct hclge_mac_node *mac_node, *tmp;
+       struct hclge_dev *hdev = vport->back;
+       struct list_head tmp_del_list, *list;
+
+       INIT_LIST_HEAD(&tmp_del_list);
+
+       list = (mac_type == HCLGE_MAC_ADDR_UC) ?
+               &vport->uc_mac_list : &vport->mc_mac_list;
+
+       spin_lock_bh(&vport->mac_list_lock);
+
+       list_for_each_entry_safe(mac_node, tmp, list, node) {
+               switch (mac_node->state) {
+               case HCLGE_MAC_TO_DEL:
+               case HCLGE_MAC_ACTIVE:
+                       list_del(&mac_node->node);
+                       list_add_tail(&mac_node->node, &tmp_del_list);
+                       break;
+               case HCLGE_MAC_TO_ADD:
+                       list_del(&mac_node->node);
+                       kfree(mac_node);
+                       break;
+               }
+       }
+
+       spin_unlock_bh(&vport->mac_list_lock);
+
+       if (mac_type == HCLGE_MAC_ADDR_UC)
+               hclge_unsync_vport_mac_list(vport, &tmp_del_list,
+                                           hclge_rm_uc_addr_common);
+       else
+               hclge_unsync_vport_mac_list(vport, &tmp_del_list,
+                                           hclge_rm_mc_addr_common);
+
+       if (!list_empty(&tmp_del_list))
+               dev_warn(&hdev->pdev->dev,
+                        "uninit %s mac list for vport %u not completely.\n",
+                        mac_type == HCLGE_MAC_ADDR_UC ? "uc" : "mc",
+                        vport->vport_id);
+
+       list_for_each_entry_safe(mac_node, tmp, &tmp_del_list, node) {
+               list_del(&mac_node->node);
+               kfree(mac_node);
+       }
 }
 
-void hclge_uninit_vport_mac_table(struct hclge_dev *hdev)
+static void hclge_uninit_mac_table(struct hclge_dev *hdev)
 {
-       struct hclge_vport_mac_addr_cfg *mac, *tmp;
        struct hclge_vport *vport;
        int i;
 
        for (i = 0; i < hdev->num_alloc_vport; i++) {
                vport = &hdev->vport[i];
-               list_for_each_entry_safe(mac, tmp, &vport->uc_mac_list, node) {
-                       list_del(&mac->node);
-                       kfree(mac);
-               }
-
-               list_for_each_entry_safe(mac, tmp, &vport->mc_mac_list, node) {
-                       list_del(&mac->node);
-                       kfree(mac);
-               }
+               hclge_uninit_vport_mac_list(vport, HCLGE_MAC_ADDR_UC);
+               hclge_uninit_vport_mac_list(vport, HCLGE_MAC_ADDR_MC);
        }
 }
 
@@ -7683,12 +8112,57 @@ static void hclge_get_mac_addr(struct hnae3_handle *handle, u8 *p)
        ether_addr_copy(p, hdev->hw.mac.mac_addr);
 }
 
+int hclge_update_mac_node_for_dev_addr(struct hclge_vport *vport,
+                                      const u8 *old_addr, const u8 *new_addr)
+{
+       struct list_head *list = &vport->uc_mac_list;
+       struct hclge_mac_node *old_node, *new_node;
+
+       new_node = hclge_find_mac_node(list, new_addr);
+       if (!new_node) {
+               new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC);
+               if (!new_node)
+                       return -ENOMEM;
+
+               new_node->state = HCLGE_MAC_TO_ADD;
+               ether_addr_copy(new_node->mac_addr, new_addr);
+               list_add(&new_node->node, list);
+       } else {
+               if (new_node->state == HCLGE_MAC_TO_DEL)
+                       new_node->state = HCLGE_MAC_ACTIVE;
+
+               /* make sure the new addr is in the list head, avoid dev
+                * addr may be not re-added into mac table for the umv space
+                * limitation after global/imp reset which will clear mac
+                * table by hardware.
+                */
+               list_move(&new_node->node, list);
+       }
+
+       if (old_addr && !ether_addr_equal(old_addr, new_addr)) {
+               old_node = hclge_find_mac_node(list, old_addr);
+               if (old_node) {
+                       if (old_node->state == HCLGE_MAC_TO_ADD) {
+                               list_del(&old_node->node);
+                               kfree(old_node);
+                       } else {
+                               old_node->state = HCLGE_MAC_TO_DEL;
+                       }
+               }
+       }
+
+       set_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE, &vport->state);
+
+       return 0;
+}
+
 static int hclge_set_mac_addr(struct hnae3_handle *handle, void *p,
                              bool is_first)
 {
        const unsigned char *new_addr = (const unsigned char *)p;
        struct hclge_vport *vport = hclge_get_vport(handle);
        struct hclge_dev *hdev = vport->back;
+       unsigned char *old_addr = NULL;
        int ret;
 
        /* mac addr check */
@@ -7696,39 +8170,42 @@ static int hclge_set_mac_addr(struct hnae3_handle *handle, void *p,
            is_broadcast_ether_addr(new_addr) ||
            is_multicast_ether_addr(new_addr)) {
                dev_err(&hdev->pdev->dev,
-                       "Change uc mac err! invalid mac:%pM.\n",
+                       "change uc mac err! invalid mac: %pM.\n",
                         new_addr);
                return -EINVAL;
        }
 
-       if ((!is_first || is_kdump_kernel()) &&
-           hclge_rm_uc_addr(handle, hdev->hw.mac.mac_addr))
-               dev_warn(&hdev->pdev->dev,
-                        "remove old uc mac address fail.\n");
-
-       ret = hclge_add_uc_addr(handle, new_addr);
+       ret = hclge_pause_addr_cfg(hdev, new_addr);
        if (ret) {
                dev_err(&hdev->pdev->dev,
-                       "add uc mac address fail, ret =%d.\n",
+                       "failed to configure mac pause address, ret = %d\n",
                        ret);
-
-               if (!is_first &&
-                   hclge_add_uc_addr(handle, hdev->hw.mac.mac_addr))
-                       dev_err(&hdev->pdev->dev,
-                               "restore uc mac address fail.\n");
-
-               return -EIO;
+               return ret;
        }
 
-       ret = hclge_pause_addr_cfg(hdev, new_addr);
+       if (!is_first)
+               old_addr = hdev->hw.mac.mac_addr;
+
+       spin_lock_bh(&vport->mac_list_lock);
+       ret = hclge_update_mac_node_for_dev_addr(vport, old_addr, new_addr);
        if (ret) {
                dev_err(&hdev->pdev->dev,
-                       "configure mac pause address fail, ret =%d.\n",
-                       ret);
-               return -EIO;
-       }
+                       "failed to change the mac addr:%pM, ret = %d\n",
+                       new_addr, ret);
+               spin_unlock_bh(&vport->mac_list_lock);
+
+               if (!is_first)
+                       hclge_pause_addr_cfg(hdev, old_addr);
 
+               return ret;
+       }
+       /* we must update dev addr with spin lock protect, preventing dev addr
+        * being removed by set_rx_mode path.
+        */
        ether_addr_copy(hdev->hw.mac.mac_addr, new_addr);
+       spin_unlock_bh(&vport->mac_list_lock);
+
+       hclge_task_schedule(hdev, 0);
 
        return 0;
 }
@@ -8308,42 +8785,80 @@ void hclge_uninit_vport_vlan_table(struct hclge_dev *hdev)
        }
 }
 
-static void hclge_restore_vlan_table(struct hnae3_handle *handle)
+void hclge_restore_vport_vlan_table(struct hclge_vport *vport)
 {
-       struct hclge_vport *vport = hclge_get_vport(handle);
        struct hclge_vport_vlan_cfg *vlan, *tmp;
        struct hclge_dev *hdev = vport->back;
        u16 vlan_proto;
-       u16 state, vlan_id;
-       int i;
+       u16 vlan_id;
+       u16 state;
+       int ret;
 
-       for (i = 0; i < hdev->num_alloc_vport; i++) {
-               vport = &hdev->vport[i];
-               vlan_proto = vport->port_base_vlan_cfg.vlan_info.vlan_proto;
-               vlan_id = vport->port_base_vlan_cfg.vlan_info.vlan_tag;
-               state = vport->port_base_vlan_cfg.state;
+       vlan_proto = vport->port_base_vlan_cfg.vlan_info.vlan_proto;
+       vlan_id = vport->port_base_vlan_cfg.vlan_info.vlan_tag;
+       state = vport->port_base_vlan_cfg.state;
 
-               if (state != HNAE3_PORT_BASE_VLAN_DISABLE) {
-                       hclge_set_vlan_filter_hw(hdev, htons(vlan_proto),
-                                                vport->vport_id, vlan_id,
-                                                false);
-                       continue;
-               }
+       if (state != HNAE3_PORT_BASE_VLAN_DISABLE) {
+               clear_bit(vport->vport_id, hdev->vlan_table[vlan_id]);
+               hclge_set_vlan_filter_hw(hdev, htons(vlan_proto),
+                                        vport->vport_id, vlan_id,
+                                        false);
+               return;
+       }
 
-               list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node) {
-                       int ret;
+       list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node) {
+               ret = hclge_set_vlan_filter_hw(hdev, htons(ETH_P_8021Q),
+                                              vport->vport_id,
+                                              vlan->vlan_id, false);
+               if (ret)
+                       break;
+               vlan->hd_tbl_status = true;
+       }
+}
 
-                       if (!vlan->hd_tbl_status)
-                               continue;
-                       ret = hclge_set_vlan_filter_hw(hdev, htons(ETH_P_8021Q),
-                                                      vport->vport_id,
-                                                      vlan->vlan_id, false);
-                       if (ret)
-                               break;
+/* For global reset and imp reset, hardware will clear the mac table,
+ * so we change the mac address state from ACTIVE to TO_ADD, then they
+ * can be restored in the service task after reset complete. Furtherly,
+ * the mac addresses with state TO_DEL or DEL_FAIL are unnecessary to
+ * be restored after reset, so just remove these mac nodes from mac_list.
+ */
+static void hclge_mac_node_convert_for_reset(struct list_head *list)
+{
+       struct hclge_mac_node *mac_node, *tmp;
+
+       list_for_each_entry_safe(mac_node, tmp, list, node) {
+               if (mac_node->state == HCLGE_MAC_ACTIVE) {
+                       mac_node->state = HCLGE_MAC_TO_ADD;
+               } else if (mac_node->state == HCLGE_MAC_TO_DEL) {
+                       list_del(&mac_node->node);
+                       kfree(mac_node);
                }
        }
 }
 
+void hclge_restore_mac_table_common(struct hclge_vport *vport)
+{
+       spin_lock_bh(&vport->mac_list_lock);
+
+       hclge_mac_node_convert_for_reset(&vport->uc_mac_list);
+       hclge_mac_node_convert_for_reset(&vport->mc_mac_list);
+       set_bit(HCLGE_VPORT_STATE_MAC_TBL_CHANGE, &vport->state);
+
+       spin_unlock_bh(&vport->mac_list_lock);
+}
+
+static void hclge_restore_hw_table(struct hclge_dev *hdev)
+{
+       struct hclge_vport *vport = &hdev->vport[0];
+       struct hnae3_handle *handle = &vport->nic;
+
+       hclge_restore_mac_table_common(vport);
+       hclge_restore_vport_vlan_table(vport);
+       set_bit(HCLGE_STATE_PROMISC_CHANGED, &hdev->state);
+
+       hclge_restore_fd_entries(handle);
+}
+
 int hclge_en_hw_strip_rxvtag(struct hnae3_handle *handle, bool enable)
 {
        struct hclge_vport *vport = hclge_get_vport(handle);
@@ -9658,7 +10173,7 @@ static int hclge_set_vf_spoofchk(struct hnae3_handle *handle, int vf,
                dev_warn(&hdev->pdev->dev,
                         "vf %d vlan table is full, enable spoof check may cause its packet send fail\n",
                         vf);
-       else if (enable && hclge_is_umv_space_full(vport))
+       else if (enable && hclge_is_umv_space_full(vport, true))
                dev_warn(&hdev->pdev->dev,
                         "vf %d mac table is full, enable spoof check may cause its packet send fail\n",
                         vf);
@@ -9835,8 +10350,16 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
        set_bit(HCLGE_STATE_DOWN, &hdev->state);
 
        hclge_stats_clear(hdev);
-       memset(hdev->vlan_table, 0, sizeof(hdev->vlan_table));
-       memset(hdev->vf_vlan_full, 0, sizeof(hdev->vf_vlan_full));
+       /* NOTE: pf reset needn't to clear or restore pf and vf table entry.
+        * so here should not clean table in memory.
+        */
+       if (hdev->reset_type == HNAE3_IMP_RESET ||
+           hdev->reset_type == HNAE3_GLOBAL_RESET) {
+               memset(hdev->vlan_table, 0, sizeof(hdev->vlan_table));
+               memset(hdev->vf_vlan_full, 0, sizeof(hdev->vf_vlan_full));
+               bitmap_set(hdev->vport_config_block, 0, hdev->num_alloc_vport);
+               hclge_reset_umv_space(hdev);
+       }
 
        ret = hclge_cmd_init(hdev);
        if (ret) {
@@ -9850,8 +10373,6 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev)
                return ret;
        }
 
-       hclge_reset_umv_space(hdev);
-
        ret = hclge_mac_init(hdev);
        if (ret) {
                dev_err(&pdev->dev, "Mac init error, ret = %d\n", ret);
@@ -9947,12 +10468,11 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
        hclge_clear_vf_vlan(hdev);
        hclge_misc_affinity_teardown(hdev);
        hclge_state_uninit(hdev);
+       hclge_uninit_mac_table(hdev);
 
        if (mac->phydev)
                mdiobus_unregister(mac->mdio_bus);
 
-       hclge_uninit_umv_space(hdev);
-
        /* Disable MISC vector(vector0) */
        hclge_enable_vector(&hdev->misc_vector, false);
        synchronize_irq(hdev->misc_vector.vector_irq);
@@ -9966,7 +10486,6 @@ static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev)
        hclge_misc_irq_uninit(hdev);
        hclge_pci_uninit(hdev);
        mutex_destroy(&hdev->vport_lock);
-       hclge_uninit_vport_mac_table(hdev);
        hclge_uninit_vport_vlan_table(hdev);
        ae_dev->priv = NULL;
 }
@@ -10576,6 +11095,131 @@ static int hclge_gro_en(struct hnae3_handle *handle, bool enable)
        return hclge_config_gro(hdev, enable);
 }
 
+static void hclge_sync_promisc_mode(struct hclge_dev *hdev)
+{
+       struct hclge_vport *vport = &hdev->vport[0];
+       struct hnae3_handle *handle = &vport->nic;
+       u8 tmp_flags = 0;
+       int ret;
+
+       if (vport->last_promisc_flags != vport->overflow_promisc_flags) {
+               set_bit(HCLGE_STATE_PROMISC_CHANGED, &hdev->state);
+               vport->last_promisc_flags = vport->overflow_promisc_flags;
+       }
+
+       if (test_bit(HCLGE_STATE_PROMISC_CHANGED, &hdev->state)) {
+               tmp_flags = handle->netdev_flags | vport->last_promisc_flags;
+               ret = hclge_set_promisc_mode(handle, tmp_flags & HNAE3_UPE,
+                                            tmp_flags & HNAE3_MPE);
+               if (!ret) {
+                       clear_bit(HCLGE_STATE_PROMISC_CHANGED, &hdev->state);
+                       hclge_enable_vlan_filter(handle,
+                                                tmp_flags & HNAE3_VLAN_FLTR);
+               }
+       }
+}
+
+static bool hclge_module_existed(struct hclge_dev *hdev)
+{
+       struct hclge_desc desc;
+       u32 existed;
+       int ret;
+
+       hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GET_SFP_EXIST, true);
+       ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "failed to get SFP exist state, ret = %d\n", ret);
+               return false;
+       }
+
+       existed = le32_to_cpu(desc.data[0]);
+
+       return existed != 0;
+}
+
+/* need 6 bds(total 140 bytes) in one reading
+ * return the number of bytes actually read, 0 means read failed.
+ */
+static u16 hclge_get_sfp_eeprom_info(struct hclge_dev *hdev, u32 offset,
+                                    u32 len, u8 *data)
+{
+       struct hclge_desc desc[HCLGE_SFP_INFO_CMD_NUM];
+       struct hclge_sfp_info_bd0_cmd *sfp_info_bd0;
+       u16 read_len;
+       u16 copy_len;
+       int ret;
+       int i;
+
+       /* setup all 6 bds to read module eeprom info. */
+       for (i = 0; i < HCLGE_SFP_INFO_CMD_NUM; i++) {
+               hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_GET_SFP_EEPROM,
+                                          true);
+
+               /* bd0~bd4 need next flag */
+               if (i < HCLGE_SFP_INFO_CMD_NUM - 1)
+                       desc[i].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT);
+       }
+
+       /* setup bd0, this bd contains offset and read length. */
+       sfp_info_bd0 = (struct hclge_sfp_info_bd0_cmd *)desc[0].data;
+       sfp_info_bd0->offset = cpu_to_le16((u16)offset);
+       read_len = min_t(u16, len, HCLGE_SFP_INFO_MAX_LEN);
+       sfp_info_bd0->read_len = cpu_to_le16(read_len);
+
+       ret = hclge_cmd_send(&hdev->hw, desc, i);
+       if (ret) {
+               dev_err(&hdev->pdev->dev,
+                       "failed to get SFP eeprom info, ret = %d\n", ret);
+               return 0;
+       }
+
+       /* copy sfp info from bd0 to out buffer. */
+       copy_len = min_t(u16, len, HCLGE_SFP_INFO_BD0_LEN);
+       memcpy(data, sfp_info_bd0->data, copy_len);
+       read_len = copy_len;
+
+       /* copy sfp info from bd1~bd5 to out buffer if needed. */
+       for (i = 1; i < HCLGE_SFP_INFO_CMD_NUM; i++) {
+               if (read_len >= len)
+                       return read_len;
+
+               copy_len = min_t(u16, len - read_len, HCLGE_SFP_INFO_BDX_LEN);
+               memcpy(data + read_len, desc[i].data, copy_len);
+               read_len += copy_len;
+       }
+
+       return read_len;
+}
+
+static int hclge_get_module_eeprom(struct hnae3_handle *handle, u32 offset,
+                                  u32 len, u8 *data)
+{
+       struct hclge_vport *vport = hclge_get_vport(handle);
+       struct hclge_dev *hdev = vport->back;
+       u32 read_len = 0;
+       u16 data_len;
+
+       if (hdev->hw.mac.media_type != HNAE3_MEDIA_TYPE_FIBER)
+               return -EOPNOTSUPP;
+
+       if (!hclge_module_existed(hdev))
+               return -ENXIO;
+
+       while (read_len < len) {
+               data_len = hclge_get_sfp_eeprom_info(hdev,
+                                                    offset + read_len,
+                                                    len - read_len,
+                                                    data + read_len);
+               if (!data_len)
+                       return -EIO;
+
+               read_len += data_len;
+       }
+
+       return 0;
+}
+
 static const struct hnae3_ae_ops hclge_ops = {
        .init_ae_dev = hclge_init_ae_dev,
        .uninit_ae_dev = hclge_uninit_ae_dev,
@@ -10588,6 +11232,7 @@ static const struct hnae3_ae_ops hclge_ops = {
        .get_vector = hclge_get_vector,
        .put_vector = hclge_put_vector,
        .set_promisc_mode = hclge_set_promisc_mode,
+       .request_update_promisc_mode = hclge_request_update_promisc_mode,
        .set_loopback = hclge_set_loopback,
        .start = hclge_ae_start,
        .stop = hclge_ae_stop,
@@ -10649,7 +11294,6 @@ static const struct hnae3_ae_ops hclge_ops = {
        .get_fd_rule_cnt = hclge_get_fd_rule_cnt,
        .get_fd_rule_info = hclge_get_fd_rule_info,
        .get_fd_all_rules = hclge_get_all_rules,
-       .restore_fd_rules = hclge_restore_fd_entries,
        .enable_fd = hclge_enable_fd,
        .add_arfs_entry = hclge_add_fd_entry_by_arfs,
        .dbg_run_cmd = hclge_dbg_run_cmd,
@@ -10662,13 +11306,13 @@ static const struct hnae3_ae_ops hclge_ops = {
        .set_timer_task = hclge_set_timer_task,
        .mac_connect_phy = hclge_mac_connect_phy,
        .mac_disconnect_phy = hclge_mac_disconnect_phy,
-       .restore_vlan_table = hclge_restore_vlan_table,
        .get_vf_config = hclge_get_vf_config,
        .set_vf_link_state = hclge_set_vf_link_state,
        .set_vf_spoofchk = hclge_set_vf_spoofchk,
        .set_vf_trust = hclge_set_vf_trust,
        .set_vf_rate = hclge_set_vf_rate,
        .set_vf_mac = hclge_set_vf_mac,
+       .get_module_eeprom = hclge_get_module_eeprom,
 };
 
 static struct hnae3_ae_algo ae_algo = {
index 71df23d..913c4f6 100644 (file)
@@ -217,6 +217,7 @@ enum HCLGE_DEV_STATE {
        HCLGE_STATE_STATISTICS_UPDATING,
        HCLGE_STATE_CMD_DISABLE,
        HCLGE_STATE_LINK_UPDATING,
+       HCLGE_STATE_PROMISC_CHANGED,
        HCLGE_STATE_RST_FAIL,
        HCLGE_STATE_MAX
 };
@@ -580,7 +581,6 @@ struct hclge_fd_key_cfg {
 struct hclge_fd_cfg {
        u8 fd_mode;
        u16 max_key_length; /* use bit as unit */
-       u32 proto_support;
        u32 rule_num[MAX_STAGE_NUM]; /* rule entry number */
        u16 cnt_num[MAX_STAGE_NUM]; /* rule hit counter number */
        struct hclge_fd_key_cfg key_cfg[MAX_STAGE_NUM];
@@ -631,9 +631,15 @@ struct hclge_fd_ad_data {
        u16 rule_id;
 };
 
-struct hclge_vport_mac_addr_cfg {
+enum HCLGE_MAC_NODE_STATE {
+       HCLGE_MAC_TO_ADD,
+       HCLGE_MAC_TO_DEL,
+       HCLGE_MAC_ACTIVE
+};
+
+struct hclge_mac_node {
        struct list_head node;
-       int hd_tbl_status;
+       enum HCLGE_MAC_NODE_STATE state;
        u8 mac_addr[ETH_ALEN];
 };
 
@@ -806,6 +812,8 @@ struct hclge_dev {
        unsigned long vlan_table[VLAN_N_VID][BITS_TO_LONGS(HCLGE_VPORT_NUM)];
        unsigned long vf_vlan_full[BITS_TO_LONGS(HCLGE_VPORT_NUM)];
 
+       unsigned long vport_config_block[BITS_TO_LONGS(HCLGE_VPORT_NUM)];
+
        struct hclge_fd_cfg fd_cfg;
        struct hlist_head fd_rule_list;
        spinlock_t fd_rule_lock; /* protect fd_rule_list and fd_bmap */
@@ -823,7 +831,6 @@ struct hclge_dev {
        u16 priv_umv_size;
        /* unicast mac vlan space shared by PF and its VFs */
        u16 share_umv_size;
-       struct mutex umv_mutex; /* protect share_umv_size */
 
        DECLARE_KFIFO(mac_tnl_log, struct hclge_mac_tnl_stats,
                      HCLGE_MAC_TNL_LOG_SIZE);
@@ -867,6 +874,7 @@ struct hclge_rss_tuple_cfg {
 
 enum HCLGE_VPORT_STATE {
        HCLGE_VPORT_STATE_ALIVE,
+       HCLGE_VPORT_STATE_MAC_TBL_CHANGE,
        HCLGE_VPORT_STATE_MAX
 };
 
@@ -923,6 +931,10 @@ struct hclge_vport {
        u32 mps; /* Max packet size */
        struct hclge_vf_info vf_info;
 
+       u8 overflow_promisc_flags;
+       u8 last_promisc_flags;
+
+       spinlock_t mac_list_lock; /* protect mac address need to add/detele */
        struct list_head uc_mac_list;   /* Store VF unicast table */
        struct list_head mc_mac_list;   /* Store VF multicast table */
        struct list_head vlan_list;     /* Store VF vlan table */
@@ -978,16 +990,18 @@ int hclge_dbg_run_cmd(struct hnae3_handle *handle, const char *cmd_buf);
 u16 hclge_covert_handle_qid_global(struct hnae3_handle *handle, u16 queue_id);
 int hclge_notify_client(struct hclge_dev *hdev,
                        enum hnae3_reset_notify_type type);
-void hclge_add_vport_mac_table(struct hclge_vport *vport, const u8 *mac_addr,
-                              enum HCLGE_MAC_ADDR_TYPE mac_type);
-void hclge_rm_vport_mac_table(struct hclge_vport *vport, const u8 *mac_addr,
-                             bool is_write_tbl,
-                             enum HCLGE_MAC_ADDR_TYPE mac_type);
+int hclge_update_mac_list(struct hclge_vport *vport,
+                         enum HCLGE_MAC_NODE_STATE state,
+                         enum HCLGE_MAC_ADDR_TYPE mac_type,
+                         const unsigned char *addr);
+int hclge_update_mac_node_for_dev_addr(struct hclge_vport *vport,
+                                      const u8 *old_addr, const u8 *new_addr);
 void hclge_rm_vport_all_mac_table(struct hclge_vport *vport, bool is_del_list,
                                  enum HCLGE_MAC_ADDR_TYPE mac_type);
-void hclge_uninit_vport_mac_table(struct hclge_dev *hdev);
 void hclge_rm_vport_all_vlan_table(struct hclge_vport *vport, bool is_del_list);
 void hclge_uninit_vport_vlan_table(struct hclge_dev *hdev);
+void hclge_restore_mac_table_common(struct hclge_vport *vport);
+void hclge_restore_vport_vlan_table(struct hclge_vport *vport);
 int hclge_update_port_base_vlan_cfg(struct hclge_vport *vport, u16 state,
                                    struct hclge_vlan_info *vlan_info);
 int hclge_push_vf_port_base_vlan_info(struct hclge_vport *vport, u8 vfid,
index 7f24fcb..ac70faf 100644 (file)
@@ -5,6 +5,9 @@
 #include "hclge_mbx.h"
 #include "hnae3.h"
 
+#define CREATE_TRACE_POINTS
+#include "hclge_trace.h"
+
 static u16 hclge_errno_to_resp(int errno)
 {
        return abs(errno);
@@ -90,6 +93,8 @@ static int hclge_send_mbx_msg(struct hclge_vport *vport, u8 *msg, u16 msg_len,
 
        memcpy(&resp_pf_to_vf->msg.vf_mbx_msg_code, msg, msg_len);
 
+       trace_hclge_pf_mbx_send(hdev, resp_pf_to_vf);
+
        status = hclge_cmd_send(&hdev->hw, &desc, 1);
        if (status)
                dev_err(&hdev->pdev->dev,
@@ -270,26 +275,17 @@ static int hclge_set_vf_uc_mac_addr(struct hclge_vport *vport,
                if (!is_valid_ether_addr(mac_addr))
                        return -EINVAL;
 
-               hclge_rm_uc_addr_common(vport, old_addr);
-               status = hclge_add_uc_addr_common(vport, mac_addr);
-               if (status) {
-                       hclge_add_uc_addr_common(vport, old_addr);
-               } else {
-                       hclge_rm_vport_mac_table(vport, mac_addr,
-                                                false, HCLGE_MAC_ADDR_UC);
-                       hclge_add_vport_mac_table(vport, mac_addr,
-                                                 HCLGE_MAC_ADDR_UC);
-               }
+               spin_lock_bh(&vport->mac_list_lock);
+               status = hclge_update_mac_node_for_dev_addr(vport, old_addr,
+                                                           mac_addr);
+               spin_unlock_bh(&vport->mac_list_lock);
+               hclge_task_schedule(hdev, 0);
        } else if (mbx_req->msg.subcode == HCLGE_MBX_MAC_VLAN_UC_ADD) {
-               status = hclge_add_uc_addr_common(vport, mac_addr);
-               if (!status)
-                       hclge_add_vport_mac_table(vport, mac_addr,
-                                                 HCLGE_MAC_ADDR_UC);
+               status = hclge_update_mac_list(vport, HCLGE_MAC_TO_ADD,
+                                              HCLGE_MAC_ADDR_UC, mac_addr);
        } else if (mbx_req->msg.subcode == HCLGE_MBX_MAC_VLAN_UC_REMOVE) {
-               status = hclge_rm_uc_addr_common(vport, mac_addr);
-               if (!status)
-                       hclge_rm_vport_mac_table(vport, mac_addr,
-                                                false, HCLGE_MAC_ADDR_UC);
+               status = hclge_update_mac_list(vport, HCLGE_MAC_TO_DEL,
+                                              HCLGE_MAC_ADDR_UC, mac_addr);
        } else {
                dev_err(&hdev->pdev->dev,
                        "failed to set unicast mac addr, unknown subcode %u\n",
@@ -305,18 +301,13 @@ static int hclge_set_vf_mc_mac_addr(struct hclge_vport *vport,
 {
        const u8 *mac_addr = (const u8 *)(mbx_req->msg.data);
        struct hclge_dev *hdev = vport->back;
-       int status;
 
        if (mbx_req->msg.subcode == HCLGE_MBX_MAC_VLAN_MC_ADD) {
-               status = hclge_add_mc_addr_common(vport, mac_addr);
-               if (!status)
-                       hclge_add_vport_mac_table(vport, mac_addr,
-                                                 HCLGE_MAC_ADDR_MC);
+               hclge_update_mac_list(vport, HCLGE_MAC_TO_ADD,
+                                     HCLGE_MAC_ADDR_MC, mac_addr);
        } else if (mbx_req->msg.subcode == HCLGE_MBX_MAC_VLAN_MC_REMOVE) {
-               status = hclge_rm_mc_addr_common(vport, mac_addr);
-               if (!status)
-                       hclge_rm_vport_mac_table(vport, mac_addr,
-                                                false, HCLGE_MAC_ADDR_MC);
+               hclge_update_mac_list(vport, HCLGE_MAC_TO_DEL,
+                                     HCLGE_MAC_ADDR_MC, mac_addr);
        } else {
                dev_err(&hdev->pdev->dev,
                        "failed to set mcast mac addr, unknown subcode %u\n",
@@ -324,7 +315,7 @@ static int hclge_set_vf_mc_mac_addr(struct hclge_vport *vport,
                return -EIO;
        }
 
-       return status;
+       return 0;
 }
 
 int hclge_push_vf_port_base_vlan_info(struct hclge_vport *vport, u8 vfid,
@@ -638,6 +629,23 @@ static void hclge_handle_ncsi_error(struct hclge_dev *hdev)
        ae_dev->ops->reset_event(hdev->pdev, NULL);
 }
 
+static void hclge_handle_vf_tbl(struct hclge_vport *vport,
+                               struct hclge_mbx_vf_to_pf_cmd *mbx_req)
+{
+       struct hclge_dev *hdev = vport->back;
+       struct hclge_vf_vlan_cfg *msg_cmd;
+
+       msg_cmd = (struct hclge_vf_vlan_cfg *)&mbx_req->msg;
+       if (msg_cmd->subcode == HCLGE_MBX_VPORT_LIST_CLEAR) {
+               hclge_rm_vport_all_mac_table(vport, true, HCLGE_MAC_ADDR_UC);
+               hclge_rm_vport_all_mac_table(vport, true, HCLGE_MAC_ADDR_MC);
+               hclge_rm_vport_all_vlan_table(vport, true);
+       } else {
+               dev_warn(&hdev->pdev->dev, "Invalid cmd(%u)\n",
+                        msg_cmd->subcode);
+       }
+}
+
 void hclge_mbx_handler(struct hclge_dev *hdev)
 {
        struct hclge_cmq_ring *crq = &hdev->hw.cmq.crq;
@@ -645,6 +653,7 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
        struct hclge_mbx_vf_to_pf_cmd *req;
        struct hclge_vport *vport;
        struct hclge_desc *desc;
+       bool is_del = false;
        unsigned int flag;
        int ret = 0;
 
@@ -674,6 +683,8 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
 
                vport = &hdev->vport[req->mbx_src_vfid];
 
+               trace_hclge_pf_mbx_get(hdev, req);
+
                switch (req->msg.code) {
                case HCLGE_MBX_MAP_RING_TO_VECTOR:
                        ret = hclge_map_unmap_ring_to_vf_vector(vport, true,
@@ -760,11 +771,12 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
                        break;
                case HCLGE_MBX_GET_VF_FLR_STATUS:
                case HCLGE_MBX_VF_UNINIT:
-                       hclge_rm_vport_all_mac_table(vport, true,
+                       is_del = req->msg.code == HCLGE_MBX_VF_UNINIT;
+                       hclge_rm_vport_all_mac_table(vport, is_del,
                                                     HCLGE_MAC_ADDR_UC);
-                       hclge_rm_vport_all_mac_table(vport, true,
+                       hclge_rm_vport_all_mac_table(vport, is_del,
                                                     HCLGE_MAC_ADDR_MC);
-                       hclge_rm_vport_all_vlan_table(vport, true);
+                       hclge_rm_vport_all_vlan_table(vport, is_del);
                        break;
                case HCLGE_MBX_GET_MEDIA_TYPE:
                        hclge_get_vf_media_type(vport, &resp_msg);
@@ -778,6 +790,9 @@ void hclge_mbx_handler(struct hclge_dev *hdev)
                case HCLGE_MBX_NCSI_ERROR:
                        hclge_handle_ncsi_error(hdev);
                        break;
+               case HCLGE_MBX_HANDLE_VF_TBL:
+                       hclge_handle_vf_tbl(vport, req);
+                       break;
                default:
                        dev_err(&hdev->pdev->dev,
                                "un-supported mailbox message, code = %u\n",
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_trace.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_trace.h
new file mode 100644 (file)
index 0000000..5b0b71b
--- /dev/null
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2018-2020 Hisilicon Limited. */
+
+/* This must be outside ifdef _HCLGE_TRACE_H */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hns3
+
+#if !defined(_HCLGE_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _HCLGE_TRACE_H_
+
+#include <linux/tracepoint.h>
+
+#define PF_GET_MBX_LEN (sizeof(struct hclge_mbx_vf_to_pf_cmd) / sizeof(u32))
+#define PF_SEND_MBX_LEN        (sizeof(struct hclge_mbx_pf_to_vf_cmd) / sizeof(u32))
+
+TRACE_EVENT(hclge_pf_mbx_get,
+       TP_PROTO(
+               struct hclge_dev *hdev,
+               struct hclge_mbx_vf_to_pf_cmd *req),
+       TP_ARGS(hdev, req),
+
+       TP_STRUCT__entry(
+               __field(u8, vfid)
+               __field(u8, code)
+               __field(u8, subcode)
+               __string(pciname, pci_name(hdev->pdev))
+               __string(devname, &hdev->vport[0].nic.kinfo.netdev->name)
+               __array(u32, mbx_data, PF_GET_MBX_LEN)
+       ),
+
+       TP_fast_assign(
+               __entry->vfid = req->mbx_src_vfid;
+               __entry->code = req->msg.code;
+               __entry->subcode = req->msg.subcode;
+               __assign_str(pciname, pci_name(hdev->pdev));
+               __assign_str(devname, &hdev->vport[0].nic.kinfo.netdev->name);
+               memcpy(__entry->mbx_data, req,
+                      sizeof(struct hclge_mbx_vf_to_pf_cmd));
+       ),
+
+       TP_printk(
+               "%s %s vfid:%u code:%u subcode:%u data:%s",
+               __get_str(pciname), __get_str(devname), __entry->vfid,
+               __entry->code, __entry->subcode,
+               __print_array(__entry->mbx_data, PF_GET_MBX_LEN, sizeof(u32))
+       )
+);
+
+TRACE_EVENT(hclge_pf_mbx_send,
+       TP_PROTO(
+               struct hclge_dev *hdev,
+               struct hclge_mbx_pf_to_vf_cmd *req),
+       TP_ARGS(hdev, req),
+
+       TP_STRUCT__entry(
+               __field(u8, vfid)
+               __field(u16, code)
+               __string(pciname, pci_name(hdev->pdev))
+               __string(devname, &hdev->vport[0].nic.kinfo.netdev->name)
+               __array(u32, mbx_data, PF_SEND_MBX_LEN)
+       ),
+
+       TP_fast_assign(
+               __entry->vfid = req->dest_vfid;
+               __entry->code = req->msg.code;
+               __assign_str(pciname, pci_name(hdev->pdev));
+               __assign_str(devname, &hdev->vport[0].nic.kinfo.netdev->name);
+               memcpy(__entry->mbx_data, req,
+                      sizeof(struct hclge_mbx_pf_to_vf_cmd));
+       ),
+
+       TP_printk(
+               "%s %s vfid:%u code:%u data:%s",
+               __get_str(pciname), __get_str(devname), __entry->vfid,
+               __entry->code,
+               __print_array(__entry->mbx_data, PF_SEND_MBX_LEN, sizeof(u32))
+       )
+);
+
+#endif /* _HCLGE_TRACE_H_ */
+
+/* This must be outside ifdef _HCLGE_TRACE_H */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE hclge_trace
+#include <trace/define_trace.h>
index 53804d9..2c26ea6 100644 (file)
@@ -4,6 +4,7 @@
 #
 
 ccflags-y := -I $(srctree)/drivers/net/ethernet/hisilicon/hns3
+ccflags-y += -I $(srctree)/$(src)
 
 obj-$(CONFIG_HNS3_HCLGEVF) += hclgevf.o
 hclgevf-objs = hclgevf_main.o hclgevf_cmd.o hclgevf_mbx.o
index e02d427..32341dc 100644 (file)
@@ -1164,6 +1164,27 @@ static int hclgevf_set_promisc_mode(struct hnae3_handle *handle, bool en_uc_pmc,
                                            en_bc_pmc);
 }
 
+static void hclgevf_request_update_promisc_mode(struct hnae3_handle *handle)
+{
+       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+
+       set_bit(HCLGEVF_STATE_PROMISC_CHANGED, &hdev->state);
+}
+
+static void hclgevf_sync_promisc_mode(struct hclgevf_dev *hdev)
+{
+       struct hnae3_handle *handle = &hdev->nic;
+       bool en_uc_pmc = handle->netdev_flags & HNAE3_UPE;
+       bool en_mc_pmc = handle->netdev_flags & HNAE3_MPE;
+       int ret;
+
+       if (test_bit(HCLGEVF_STATE_PROMISC_CHANGED, &hdev->state)) {
+               ret = hclgevf_set_promisc_mode(handle, en_uc_pmc, en_mc_pmc);
+               if (!ret)
+                       clear_bit(HCLGEVF_STATE_PROMISC_CHANGED, &hdev->state);
+       }
+}
+
 static int hclgevf_tqp_enable(struct hclgevf_dev *hdev, unsigned int tqp_id,
                              int stream_id, bool enable)
 {
@@ -1245,10 +1266,12 @@ static int hclgevf_set_mac_addr(struct hnae3_handle *handle, void *p,
        int status;
 
        hclgevf_build_send_msg(&send_msg, HCLGE_MBX_SET_UNICAST, 0);
-       send_msg.subcode = is_first ? HCLGE_MBX_MAC_VLAN_UC_ADD :
-                       HCLGE_MBX_MAC_VLAN_UC_MODIFY;
+       send_msg.subcode = HCLGE_MBX_MAC_VLAN_UC_MODIFY;
        ether_addr_copy(send_msg.data, new_mac_addr);
-       ether_addr_copy(&send_msg.data[ETH_ALEN], old_mac_addr);
+       if (is_first && !hdev->has_pf_mac)
+               eth_zero_addr(&send_msg.data[ETH_ALEN]);
+       else
+               ether_addr_copy(&send_msg.data[ETH_ALEN], old_mac_addr);
        status = hclgevf_send_mbx_msg(hdev, &send_msg, true, NULL, 0);
        if (!status)
                ether_addr_copy(hdev->hw.mac.mac_addr, new_mac_addr);
@@ -1256,54 +1279,302 @@ static int hclgevf_set_mac_addr(struct hnae3_handle *handle, void *p,
        return status;
 }
 
-static int hclgevf_add_uc_addr(struct hnae3_handle *handle,
-                              const unsigned char *addr)
+static struct hclgevf_mac_addr_node *
+hclgevf_find_mac_node(struct list_head *list, const u8 *mac_addr)
+{
+       struct hclgevf_mac_addr_node *mac_node, *tmp;
+
+       list_for_each_entry_safe(mac_node, tmp, list, node)
+               if (ether_addr_equal(mac_addr, mac_node->mac_addr))
+                       return mac_node;
+
+       return NULL;
+}
+
+static void hclgevf_update_mac_node(struct hclgevf_mac_addr_node *mac_node,
+                                   enum HCLGEVF_MAC_NODE_STATE state)
+{
+       switch (state) {
+       /* from set_rx_mode or tmp_add_list */
+       case HCLGEVF_MAC_TO_ADD:
+               if (mac_node->state == HCLGEVF_MAC_TO_DEL)
+                       mac_node->state = HCLGEVF_MAC_ACTIVE;
+               break;
+       /* only from set_rx_mode */
+       case HCLGEVF_MAC_TO_DEL:
+               if (mac_node->state == HCLGEVF_MAC_TO_ADD) {
+                       list_del(&mac_node->node);
+                       kfree(mac_node);
+               } else {
+                       mac_node->state = HCLGEVF_MAC_TO_DEL;
+               }
+               break;
+       /* only from tmp_add_list, the mac_node->state won't be
+        * HCLGEVF_MAC_ACTIVE
+        */
+       case HCLGEVF_MAC_ACTIVE:
+               if (mac_node->state == HCLGEVF_MAC_TO_ADD)
+                       mac_node->state = HCLGEVF_MAC_ACTIVE;
+               break;
+       }
+}
+
+static int hclgevf_update_mac_list(struct hnae3_handle *handle,
+                                  enum HCLGEVF_MAC_NODE_STATE state,
+                                  enum HCLGEVF_MAC_ADDR_TYPE mac_type,
+                                  const unsigned char *addr)
 {
        struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
-       struct hclge_vf_to_pf_msg send_msg;
+       struct hclgevf_mac_addr_node *mac_node;
+       struct list_head *list;
 
-       hclgevf_build_send_msg(&send_msg, HCLGE_MBX_SET_UNICAST,
-                              HCLGE_MBX_MAC_VLAN_UC_ADD);
-       ether_addr_copy(send_msg.data, addr);
-       return hclgevf_send_mbx_msg(hdev, &send_msg, false, NULL, 0);
+       list = (mac_type == HCLGEVF_MAC_ADDR_UC) ?
+              &hdev->mac_table.uc_mac_list : &hdev->mac_table.mc_mac_list;
+
+       spin_lock_bh(&hdev->mac_table.mac_list_lock);
+
+       /* if the mac addr is already in the mac list, no need to add a new
+        * one into it, just check the mac addr state, convert it to a new
+        * new state, or just remove it, or do nothing.
+        */
+       mac_node = hclgevf_find_mac_node(list, addr);
+       if (mac_node) {
+               hclgevf_update_mac_node(mac_node, state);
+               spin_unlock_bh(&hdev->mac_table.mac_list_lock);
+               return 0;
+       }
+       /* if this address is never added, unnecessary to delete */
+       if (state == HCLGEVF_MAC_TO_DEL) {
+               spin_unlock_bh(&hdev->mac_table.mac_list_lock);
+               return -ENOENT;
+       }
+
+       mac_node = kzalloc(sizeof(*mac_node), GFP_ATOMIC);
+       if (!mac_node) {
+               spin_unlock_bh(&hdev->mac_table.mac_list_lock);
+               return -ENOMEM;
+       }
+
+       mac_node->state = state;
+       ether_addr_copy(mac_node->mac_addr, addr);
+       list_add_tail(&mac_node->node, list);
+
+       spin_unlock_bh(&hdev->mac_table.mac_list_lock);
+       return 0;
+}
+
+static int hclgevf_add_uc_addr(struct hnae3_handle *handle,
+                              const unsigned char *addr)
+{
+       return hclgevf_update_mac_list(handle, HCLGEVF_MAC_TO_ADD,
+                                      HCLGEVF_MAC_ADDR_UC, addr);
 }
 
 static int hclgevf_rm_uc_addr(struct hnae3_handle *handle,
                              const unsigned char *addr)
 {
-       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
-       struct hclge_vf_to_pf_msg send_msg;
-
-       hclgevf_build_send_msg(&send_msg, HCLGE_MBX_SET_UNICAST,
-                              HCLGE_MBX_MAC_VLAN_UC_REMOVE);
-       ether_addr_copy(send_msg.data, addr);
-       return hclgevf_send_mbx_msg(hdev, &send_msg, false, NULL, 0);
+       return hclgevf_update_mac_list(handle, HCLGEVF_MAC_TO_DEL,
+                                      HCLGEVF_MAC_ADDR_UC, addr);
 }
 
 static int hclgevf_add_mc_addr(struct hnae3_handle *handle,
                               const unsigned char *addr)
 {
-       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
-       struct hclge_vf_to_pf_msg send_msg;
-
-       hclgevf_build_send_msg(&send_msg, HCLGE_MBX_SET_MULTICAST,
-                              HCLGE_MBX_MAC_VLAN_MC_ADD);
-       ether_addr_copy(send_msg.data, addr);
-       return hclgevf_send_mbx_msg(hdev, &send_msg, false, NULL, 0);
+       return hclgevf_update_mac_list(handle, HCLGEVF_MAC_TO_ADD,
+                                      HCLGEVF_MAC_ADDR_MC, addr);
 }
 
 static int hclgevf_rm_mc_addr(struct hnae3_handle *handle,
                              const unsigned char *addr)
 {
-       struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle);
+       return hclgevf_update_mac_list(handle, HCLGEVF_MAC_TO_DEL,
+                                      HCLGEVF_MAC_ADDR_MC, addr);
+}
+
+static int hclgevf_add_del_mac_addr(struct hclgevf_dev *hdev,
+                                   struct hclgevf_mac_addr_node *mac_node,
+                                   enum HCLGEVF_MAC_ADDR_TYPE mac_type)
+{
        struct hclge_vf_to_pf_msg send_msg;
+       u8 code, subcode;
 
-       hclgevf_build_send_msg(&send_msg, HCLGE_MBX_SET_MULTICAST,
-                              HCLGE_MBX_MAC_VLAN_MC_REMOVE);
-       ether_addr_copy(send_msg.data, addr);
+       if (mac_type == HCLGEVF_MAC_ADDR_UC) {
+               code = HCLGE_MBX_SET_UNICAST;
+               if (mac_node->state == HCLGEVF_MAC_TO_ADD)
+                       subcode = HCLGE_MBX_MAC_VLAN_UC_ADD;
+               else
+                       subcode = HCLGE_MBX_MAC_VLAN_UC_REMOVE;
+       } else {
+               code = HCLGE_MBX_SET_MULTICAST;
+               if (mac_node->state == HCLGEVF_MAC_TO_ADD)
+                       subcode = HCLGE_MBX_MAC_VLAN_MC_ADD;
+               else
+                       subcode = HCLGE_MBX_MAC_VLAN_MC_REMOVE;
+       }
+
+       hclgevf_build_send_msg(&send_msg, code, subcode);
+       ether_addr_copy(send_msg.data, mac_node->mac_addr);
        return hclgevf_send_mbx_msg(hdev, &send_msg, false, NULL, 0);
 }
 
+static void hclgevf_config_mac_list(struct hclgevf_dev *hdev,
+                                   struct list_head *list,
+                                   enum HCLGEVF_MAC_ADDR_TYPE mac_type)
+{
+       struct hclgevf_mac_addr_node *mac_node, *tmp;
+       int ret;
+
+       list_for_each_entry_safe(mac_node, tmp, list, node) {
+               ret = hclgevf_add_del_mac_addr(hdev, mac_node, mac_type);
+               if  (ret) {
+                       dev_err(&hdev->pdev->dev,
+                               "failed to configure mac %pM, state = %d, ret = %d\n",
+                               mac_node->mac_addr, mac_node->state, ret);
+                       return;
+               }
+               if (mac_node->state == HCLGEVF_MAC_TO_ADD) {
+                       mac_node->state = HCLGEVF_MAC_ACTIVE;
+               } else {
+                       list_del(&mac_node->node);
+                       kfree(mac_node);
+               }
+       }
+}
+
+static void hclgevf_sync_from_add_list(struct list_head *add_list,
+                                      struct list_head *mac_list)
+{
+       struct hclgevf_mac_addr_node *mac_node, *tmp, *new_node;
+
+       list_for_each_entry_safe(mac_node, tmp, add_list, node) {
+               /* if the mac address from tmp_add_list is not in the
+                * uc/mc_mac_list, it means have received a TO_DEL request
+                * during the time window of sending mac config request to PF
+                * If mac_node state is ACTIVE, then change its state to TO_DEL,
+                * then it will be removed at next time. If is TO_ADD, it means
+                * send TO_ADD request failed, so just remove the mac node.
+                */
+               new_node = hclgevf_find_mac_node(mac_list, mac_node->mac_addr);
+               if (new_node) {
+                       hclgevf_update_mac_node(new_node, mac_node->state);
+                       list_del(&mac_node->node);
+                       kfree(mac_node);
+               } else if (mac_node->state == HCLGEVF_MAC_ACTIVE) {
+                       mac_node->state = HCLGEVF_MAC_TO_DEL;
+                       list_del(&mac_node->node);
+                       list_add_tail(&mac_node->node, mac_list);
+               } else {
+                       list_del(&mac_node->node);
+                       kfree(mac_node);
+               }
+       }
+}
+
+static void hclgevf_sync_from_del_list(struct list_head *del_list,
+                                      struct list_head *mac_list)
+{
+       struct hclgevf_mac_addr_node *mac_node, *tmp, *new_node;
+
+       list_for_each_entry_safe(mac_node, tmp, del_list, node) {
+               new_node = hclgevf_find_mac_node(mac_list, mac_node->mac_addr);
+               if (new_node) {
+                       /* If the mac addr is exist in the mac list, it means
+                        * received a new request TO_ADD during the time window
+                        * of sending mac addr configurrequest to PF, so just
+                        * change the mac state to ACTIVE.
+                        */
+                       new_node->state = HCLGEVF_MAC_ACTIVE;
+                       list_del(&mac_node->node);
+                       kfree(mac_node);
+               } else {
+                       list_del(&mac_node->node);
+                       list_add_tail(&mac_node->node, mac_list);
+               }
+       }
+}
+
+static void hclgevf_clear_list(struct list_head *list)
+{
+       struct hclgevf_mac_addr_node *mac_node, *tmp;
+
+       list_for_each_entry_safe(mac_node, tmp, list, node) {
+               list_del(&mac_node->node);
+               kfree(mac_node);
+       }
+}
+
+static void hclgevf_sync_mac_list(struct hclgevf_dev *hdev,
+                                 enum HCLGEVF_MAC_ADDR_TYPE mac_type)
+{
+       struct hclgevf_mac_addr_node *mac_node, *tmp, *new_node;
+       struct list_head tmp_add_list, tmp_del_list;
+       struct list_head *list;
+
+       INIT_LIST_HEAD(&tmp_add_list);
+       INIT_LIST_HEAD(&tmp_del_list);
+
+       /* move the mac addr to the tmp_add_list and tmp_del_list, then
+        * we can add/delete these mac addr outside the spin lock
+        */
+       list = (mac_type == HCLGEVF_MAC_ADDR_UC) ?
+               &hdev->mac_table.uc_mac_list : &hdev->mac_table.mc_mac_list;
+
+       spin_lock_bh(&hdev->mac_table.mac_list_lock);
+
+       list_for_each_entry_safe(mac_node, tmp, list, node) {
+               switch (mac_node->state) {
+               case HCLGEVF_MAC_TO_DEL:
+                       list_del(&mac_node->node);
+                       list_add_tail(&mac_node->node, &tmp_del_list);
+                       break;
+               case HCLGEVF_MAC_TO_ADD:
+                       new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC);
+                       if (!new_node)
+                               goto stop_traverse;
+
+                       ether_addr_copy(new_node->mac_addr, mac_node->mac_addr);
+                       new_node->state = mac_node->state;
+                       list_add_tail(&new_node->node, &tmp_add_list);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+stop_traverse:
+       spin_unlock_bh(&hdev->mac_table.mac_list_lock);
+
+       /* delete first, in order to get max mac table space for adding */
+       hclgevf_config_mac_list(hdev, &tmp_del_list, mac_type);
+       hclgevf_config_mac_list(hdev, &tmp_add_list, mac_type);
+
+       /* if some mac addresses were added/deleted fail, move back to the
+        * mac_list, and retry at next time.
+        */
+       spin_lock_bh(&hdev->mac_table.mac_list_lock);
+
+       hclgevf_sync_from_del_list(&tmp_del_list, list);
+       hclgevf_sync_from_add_list(&tmp_add_list, list);
+
+       spin_unlock_bh(&hdev->mac_table.mac_list_lock);
+}
+
+static void hclgevf_sync_mac_table(struct hclgevf_dev *hdev)
+{
+       hclgevf_sync_mac_list(hdev, HCLGEVF_MAC_ADDR_UC);
+       hclgevf_sync_mac_list(hdev, HCLGEVF_MAC_ADDR_MC);
+}
+
+static void hclgevf_uninit_mac_list(struct hclgevf_dev *hdev)
+{
+       spin_lock_bh(&hdev->mac_table.mac_list_lock);
+
+       hclgevf_clear_list(&hdev->mac_table.uc_mac_list);
+       hclgevf_clear_list(&hdev->mac_table.mc_mac_list);
+
+       spin_unlock_bh(&hdev->mac_table.mac_list_lock);
+}
+
 static int hclgevf_set_vlan_filter(struct hnae3_handle *handle,
                                   __be16 proto, u16 vlan_id,
                                   bool is_kill)
@@ -1506,10 +1777,6 @@ static int hclgevf_reset_stack(struct hclgevf_dev *hdev)
        if (ret)
                return ret;
 
-       ret = hclgevf_notify_client(hdev, HNAE3_RESTORE_CLIENT);
-       if (ret)
-               return ret;
-
        /* clear handshake status with IMP */
        hclgevf_reset_handshake(hdev, false);
 
@@ -1589,13 +1856,8 @@ static void hclgevf_reset_err_handle(struct hclgevf_dev *hdev)
 
 static int hclgevf_reset_prepare(struct hclgevf_dev *hdev)
 {
-       struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
        int ret;
 
-       /* Initialize ae_dev reset status as well, in case enet layer wants to
-        * know if device is undergoing reset
-        */
-       ae_dev->reset_type = hdev->reset_type;
        hdev->rst_stats.rst_cnt++;
 
        rtnl_lock();
@@ -1610,7 +1872,6 @@ static int hclgevf_reset_prepare(struct hclgevf_dev *hdev)
 
 static int hclgevf_reset_rebuild(struct hclgevf_dev *hdev)
 {
-       struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
        int ret;
 
        hdev->rst_stats.hw_rst_done_cnt++;
@@ -1625,7 +1886,6 @@ static int hclgevf_reset_rebuild(struct hclgevf_dev *hdev)
        }
 
        hdev->last_reset_time = jiffies;
-       ae_dev->reset_type = HNAE3_NONE_RESET;
        hdev->rst_stats.rst_done_cnt++;
        hdev->rst_stats.rst_fail_cnt = 0;
        clear_bit(HCLGEVF_STATE_RST_FAIL, &hdev->state);
@@ -1951,6 +2211,10 @@ static void hclgevf_periodic_service_task(struct hclgevf_dev *hdev)
 
        hclgevf_sync_vlan_filter(hdev);
 
+       hclgevf_sync_mac_table(hdev);
+
+       hclgevf_sync_promisc_mode(hdev);
+
        hdev->last_serv_processed = jiffies;
 
 out:
@@ -2313,6 +2577,10 @@ static void hclgevf_state_init(struct hclgevf_dev *hdev)
        mutex_init(&hdev->mbx_resp.mbx_mutex);
        sema_init(&hdev->reset_sem, 1);
 
+       spin_lock_init(&hdev->mac_table.mac_list_lock);
+       INIT_LIST_HEAD(&hdev->mac_table.uc_mac_list);
+       INIT_LIST_HEAD(&hdev->mac_table.mc_mac_list);
+
        /* bring the device down */
        set_bit(HCLGEVF_STATE_DOWN, &hdev->state);
 }
@@ -2695,6 +2963,15 @@ static int hclgevf_pci_reset(struct hclgevf_dev *hdev)
        return ret;
 }
 
+static int hclgevf_clear_vport_list(struct hclgevf_dev *hdev)
+{
+       struct hclge_vf_to_pf_msg send_msg;
+
+       hclgevf_build_send_msg(&send_msg, HCLGE_MBX_HANDLE_VF_TBL,
+                              HCLGE_MBX_VPORT_LIST_CLEAR);
+       return hclgevf_send_mbx_msg(hdev, &send_msg, false, NULL, 0);
+}
+
 static int hclgevf_reset_hdev(struct hclgevf_dev *hdev)
 {
        struct pci_dev *pdev = hdev->pdev;
@@ -2730,6 +3007,8 @@ static int hclgevf_reset_hdev(struct hclgevf_dev *hdev)
                return ret;
        }
 
+       set_bit(HCLGEVF_STATE_PROMISC_CHANGED, &hdev->state);
+
        dev_info(&hdev->pdev->dev, "Reset done\n");
 
        return 0;
@@ -2802,6 +3081,15 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev)
                goto err_config;
        }
 
+       /* ensure vf tbl list as empty before init*/
+       ret = hclgevf_clear_vport_list(hdev);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "failed to clear tbl list configuration, ret = %d.\n",
+                       ret);
+               goto err_config;
+       }
+
        ret = hclgevf_init_vlan_config(hdev);
        if (ret) {
                dev_err(&hdev->pdev->dev,
@@ -2846,6 +3134,7 @@ static void hclgevf_uninit_hdev(struct hclgevf_dev *hdev)
 
        hclgevf_pci_uninit(hdev);
        hclgevf_cmd_uninit(hdev);
+       hclgevf_uninit_mac_list(hdev);
 }
 
 static int hclgevf_init_ae_dev(struct hnae3_ae_dev *ae_dev)
@@ -3213,6 +3502,7 @@ static const struct hnae3_ae_ops hclgevf_ops = {
        .set_timer_task = hclgevf_set_timer_task,
        .get_link_mode = hclgevf_get_link_mode,
        .set_promisc_mode = hclgevf_set_promisc_mode,
+       .request_update_promisc_mode = hclgevf_request_update_promisc_mode,
 };
 
 static struct hnae3_ae_algo ae_algovf = {
index 3b88d86..f19583c 100644 (file)
@@ -148,6 +148,7 @@ enum hclgevf_states {
        HCLGEVF_STATE_MBX_HANDLING,
        HCLGEVF_STATE_CMD_DISABLE,
        HCLGEVF_STATE_LINK_UPDATING,
+       HCLGEVF_STATE_PROMISC_CHANGED,
        HCLGEVF_STATE_RST_FAIL,
 };
 
@@ -234,6 +235,29 @@ struct hclgevf_rst_stats {
        u32 rst_fail_cnt;               /* the number of VF reset fail */
 };
 
+enum HCLGEVF_MAC_ADDR_TYPE {
+       HCLGEVF_MAC_ADDR_UC,
+       HCLGEVF_MAC_ADDR_MC
+};
+
+enum HCLGEVF_MAC_NODE_STATE {
+       HCLGEVF_MAC_TO_ADD,
+       HCLGEVF_MAC_TO_DEL,
+       HCLGEVF_MAC_ACTIVE
+};
+
+struct hclgevf_mac_addr_node {
+       struct list_head node;
+       enum HCLGEVF_MAC_NODE_STATE state;
+       u8 mac_addr[ETH_ALEN];
+};
+
+struct hclgevf_mac_table_cfg {
+       spinlock_t mac_list_lock; /* protect mac address need to add/detele */
+       struct list_head uc_mac_list;
+       struct list_head mc_mac_list;
+};
+
 struct hclgevf_dev {
        struct pci_dev *pdev;
        struct hnae3_ae_dev *ae_dev;
@@ -282,6 +306,8 @@ struct hclgevf_dev {
 
        unsigned long vlan_del_fail_bmap[BITS_TO_LONGS(VLAN_N_VID)];
 
+       struct hclgevf_mac_table_cfg mac_table;
+
        bool mbx_event_pending;
        struct hclgevf_mbx_resp_status mbx_resp; /* mailbox response */
        struct hclgevf_mbx_arq_ring arq; /* mailbox async rx queue */
index 9b81549..5b2dcd9 100644 (file)
@@ -5,6 +5,9 @@
 #include "hclgevf_main.h"
 #include "hnae3.h"
 
+#define CREATE_TRACE_POINTS
+#include "hclgevf_trace.h"
+
 static int hclgevf_resp_to_errno(u16 resp_code)
 {
        return resp_code ? -resp_code : 0;
@@ -106,6 +109,8 @@ int hclgevf_send_mbx_msg(struct hclgevf_dev *hdev,
 
        memcpy(&req->msg, send_msg, sizeof(struct hclge_vf_to_pf_msg));
 
+       trace_hclge_vf_mbx_send(hdev, req);
+
        /* synchronous send */
        if (need_resp) {
                mutex_lock(&hdev->mbx_resp.mbx_mutex);
@@ -179,6 +184,8 @@ void hclgevf_mbx_handler(struct hclgevf_dev *hdev)
                        continue;
                }
 
+               trace_hclge_vf_mbx_get(hdev, req);
+
                /* synchronous messages are time critical and need preferential
                 * treatment. Therefore, we need to acknowledge all the sync
                 * responses as quickly as possible so that waiting tasks do not
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_trace.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_trace.h
new file mode 100644 (file)
index 0000000..e4bfb61
--- /dev/null
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2018-2019 Hisilicon Limited. */
+
+/* This must be outside ifdef _HCLGEVF_TRACE_H */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hns3
+
+#if !defined(_HCLGEVF_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _HCLGEVF_TRACE_H_
+
+#include <linux/tracepoint.h>
+
+#define VF_GET_MBX_LEN (sizeof(struct hclge_mbx_pf_to_vf_cmd) / sizeof(u32))
+#define VF_SEND_MBX_LEN        (sizeof(struct hclge_mbx_vf_to_pf_cmd) / sizeof(u32))
+
+TRACE_EVENT(hclge_vf_mbx_get,
+       TP_PROTO(
+               struct hclgevf_dev *hdev,
+               struct hclge_mbx_pf_to_vf_cmd *req),
+       TP_ARGS(hdev, req),
+
+       TP_STRUCT__entry(
+               __field(u8, vfid)
+               __field(u16, code)
+               __string(pciname, pci_name(hdev->pdev))
+               __string(devname, &hdev->nic.kinfo.netdev->name)
+               __array(u32, mbx_data, VF_GET_MBX_LEN)
+       ),
+
+       TP_fast_assign(
+               __entry->vfid = req->dest_vfid;
+               __entry->code = req->msg.code;
+               __assign_str(pciname, pci_name(hdev->pdev));
+               __assign_str(devname, &hdev->nic.kinfo.netdev->name);
+               memcpy(__entry->mbx_data, req,
+                      sizeof(struct hclge_mbx_pf_to_vf_cmd));
+       ),
+
+       TP_printk(
+               "%s %s vfid:%u code:%u data:%s",
+               __get_str(pciname), __get_str(devname), __entry->vfid,
+               __entry->code,
+               __print_array(__entry->mbx_data, VF_GET_MBX_LEN, sizeof(u32))
+       )
+);
+
+TRACE_EVENT(hclge_vf_mbx_send,
+       TP_PROTO(
+               struct hclgevf_dev *hdev,
+               struct hclge_mbx_vf_to_pf_cmd *req),
+       TP_ARGS(hdev, req),
+
+       TP_STRUCT__entry(
+               __field(u8, vfid)
+               __field(u8, code)
+               __field(u8, subcode)
+               __string(pciname, pci_name(hdev->pdev))
+               __string(devname, &hdev->nic.kinfo.netdev->name)
+               __array(u32, mbx_data, VF_SEND_MBX_LEN)
+       ),
+
+       TP_fast_assign(
+               __entry->vfid = req->mbx_src_vfid;
+               __entry->code = req->msg.code;
+               __entry->subcode = req->msg.subcode;
+               __assign_str(pciname, pci_name(hdev->pdev));
+               __assign_str(devname, &hdev->nic.kinfo.netdev->name);
+               memcpy(__entry->mbx_data, req,
+                      sizeof(struct hclge_mbx_vf_to_pf_cmd));
+       ),
+
+       TP_printk(
+               "%s %s vfid:%u code:%u subcode:%u data:%s",
+               __get_str(pciname), __get_str(devname), __entry->vfid,
+               __entry->code, __entry->subcode,
+               __print_array(__entry->mbx_data, VF_SEND_MBX_LEN, sizeof(u32))
+       )
+);
+
+#endif /* _HCLGEVF_TRACE_H_ */
+
+/* This must be outside ifdef _HCLGEVF_TRACE_H */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE hclgevf_trace
+#include <trace/define_trace.h>
index fe88ab8..32a011c 100644 (file)
@@ -4,4 +4,4 @@ obj-$(CONFIG_HINIC) += hinic.o
 hinic-y := hinic_main.o hinic_tx.o hinic_rx.o hinic_port.o hinic_hw_dev.o \
           hinic_hw_io.o hinic_hw_qp.o hinic_hw_cmdq.o hinic_hw_wq.o \
           hinic_hw_mgmt.o hinic_hw_api_cmd.o hinic_hw_eqs.o hinic_hw_if.o \
-          hinic_common.o hinic_ethtool.o
+          hinic_common.o hinic_ethtool.o hinic_hw_mbox.o hinic_sriov.o
index a209b14..a621ebb 100644 (file)
@@ -16,6 +16,7 @@
 #include "hinic_hw_dev.h"
 #include "hinic_tx.h"
 #include "hinic_rx.h"
+#include "hinic_sriov.h"
 
 #define HINIC_DRV_NAME          "hinic"
 
@@ -23,6 +24,7 @@ enum hinic_flags {
        HINIC_LINK_UP = BIT(0),
        HINIC_INTF_UP = BIT(1),
        HINIC_RSS_ENABLE = BIT(2),
+       HINIC_LINK_DOWN = BIT(3),
 };
 
 struct hinic_rx_mode_work {
@@ -78,6 +80,7 @@ struct hinic_dev {
        struct hinic_rss_type           rss_type;
        u8                              *rss_hkey_user;
        s32                             *rss_indir_user;
+       struct hinic_sriov_info sriov_info;
 };
 
 #endif
index 5f2d57d..33c5333 100644 (file)
@@ -64,7 +64,7 @@
 #define CMDQ_WQE_SIZE                   64
 #define CMDQ_DEPTH                      SZ_4K
 
-#define CMDQ_WQ_PAGE_SIZE               SZ_4K
+#define CMDQ_WQ_PAGE_SIZE               SZ_256K
 
 #define WQE_LCMD_SIZE                   64
 #define WQE_SCMD_SIZE                   64
@@ -705,7 +705,7 @@ static void cmdq_init_queue_ctxt(struct hinic_cmdq_ctxt *cmdq_ctxt,
        /* The data in the HW is in Big Endian Format */
        wq_first_page_paddr = be64_to_cpu(*wq->block_vaddr);
 
-       pfn = CMDQ_PFN(wq_first_page_paddr, wq->wq_page_size);
+       pfn = CMDQ_PFN(wq_first_page_paddr, SZ_4K);
 
        ctxt_info->curr_wqe_page_pfn =
                HINIC_CMDQ_CTXT_PAGE_INFO_SET(pfn, CURR_WQE_PAGE_PFN)   |
@@ -714,16 +714,19 @@ static void cmdq_init_queue_ctxt(struct hinic_cmdq_ctxt *cmdq_ctxt,
                HINIC_CMDQ_CTXT_PAGE_INFO_SET(1, CEQ_EN)                |
                HINIC_CMDQ_CTXT_PAGE_INFO_SET(cmdq->wrapped, WRAPPED);
 
-       /* block PFN - Read Modify Write */
-       cmdq_first_block_paddr = cmdq_pages->page_paddr;
+       if (wq->num_q_pages != 1) {
+               /* block PFN - Read Modify Write */
+               cmdq_first_block_paddr = cmdq_pages->page_paddr;
 
-       pfn = CMDQ_PFN(cmdq_first_block_paddr, wq->wq_page_size);
+               pfn = CMDQ_PFN(cmdq_first_block_paddr, wq->wq_page_size);
+       }
 
        ctxt_info->wq_block_pfn =
                HINIC_CMDQ_CTXT_BLOCK_INFO_SET(pfn, WQ_BLOCK_PFN) |
                HINIC_CMDQ_CTXT_BLOCK_INFO_SET(atomic_read(&wq->cons_idx), CI);
 
        cmdq_ctxt->func_idx = HINIC_HWIF_FUNC_IDX(cmdqs->hwif);
+       cmdq_ctxt->ppf_idx = HINIC_HWIF_PPF_IDX(cmdqs->hwif);
        cmdq_ctxt->cmdq_type  = cmdq->cmdq_type;
 }
 
@@ -795,11 +798,6 @@ static int init_cmdqs_ctxt(struct hinic_hwdev *hwdev,
        size_t cmdq_ctxts_size;
        int err;
 
-       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
-               dev_err(&pdev->dev, "Unsupported PCI function type\n");
-               return -EINVAL;
-       }
-
        cmdq_ctxts_size = HINIC_MAX_CMDQ_TYPES * sizeof(*cmdq_ctxts);
        cmdq_ctxts = devm_kzalloc(&pdev->dev, cmdq_ctxts_size, GFP_KERNEL);
        if (!cmdq_ctxts)
index 7a434b6..3e4b0ae 100644 (file)
@@ -122,7 +122,7 @@ struct hinic_cmdq_ctxt {
 
        u16     func_idx;
        u8      cmdq_type;
-       u8      rsvd1[1];
+       u8      ppf_idx;
 
        u8      rsvd2[4];
 
index cdec1d0..7e84e4e 100644 (file)
@@ -10,7 +10,7 @@
 /* HW interface registers */
 #define HINIC_CSR_FUNC_ATTR0_ADDR                       0x0
 #define HINIC_CSR_FUNC_ATTR1_ADDR                       0x4
-
+#define HINIC_CSR_FUNC_ATTR2_ADDR                      0x8
 #define HINIC_CSR_FUNC_ATTR4_ADDR                       0x10
 #define HINIC_CSR_FUNC_ATTR5_ADDR                       0x14
 
index c7c75b7..e5cab58 100644 (file)
@@ -15,7 +15,9 @@
 #include <linux/jiffies.h>
 #include <linux/log2.h>
 #include <linux/err.h>
+#include <linux/netdevice.h>
 
+#include "hinic_sriov.h"
 #include "hinic_hw_if.h"
 #include "hinic_hw_eqs.h"
 #include "hinic_hw_mgmt.h"
@@ -46,20 +48,6 @@ enum hw_ioctxt_set_cmdq_depth {
        HW_IOCTXT_SET_CMDQ_DEPTH_DEFAULT,
 };
 
-/* HW struct */
-struct hinic_dev_cap {
-       u8      status;
-       u8      version;
-       u8      rsvd0[6];
-
-       u8      rsvd1[5];
-       u8      intr_type;
-       u8      rsvd2[66];
-       u16     max_sqs;
-       u16     max_rqs;
-       u8      rsvd3[208];
-};
-
 /**
  * get_capability - convert device capabilities to NIC capabilities
  * @hwdev: the HW device to set and convert device capabilities for
@@ -67,16 +55,13 @@ struct hinic_dev_cap {
  *
  * Return 0 - Success, negative - Failure
  **/
-static int get_capability(struct hinic_hwdev *hwdev,
-                         struct hinic_dev_cap *dev_cap)
+static int parse_capability(struct hinic_hwdev *hwdev,
+                           struct hinic_dev_cap *dev_cap)
 {
        struct hinic_cap *nic_cap = &hwdev->nic_cap;
        int num_aeqs, num_ceqs, num_irqs;
 
-       if (!HINIC_IS_PF(hwdev->hwif) && !HINIC_IS_PPF(hwdev->hwif))
-               return -EINVAL;
-
-       if (dev_cap->intr_type != INTR_MSIX_TYPE)
+       if (!HINIC_IS_VF(hwdev->hwif) && dev_cap->intr_type != INTR_MSIX_TYPE)
                return -EFAULT;
 
        num_aeqs = HINIC_HWIF_NUM_AEQS(hwdev->hwif);
@@ -89,13 +74,19 @@ static int get_capability(struct hinic_hwdev *hwdev,
        if (nic_cap->num_qps > HINIC_Q_CTXT_MAX)
                nic_cap->num_qps = HINIC_Q_CTXT_MAX;
 
-       nic_cap->max_qps = dev_cap->max_sqs + 1;
-       if (nic_cap->max_qps != (dev_cap->max_rqs + 1))
-               return -EFAULT;
+       if (!HINIC_IS_VF(hwdev->hwif))
+               nic_cap->max_qps = dev_cap->max_sqs + 1;
+       else
+               nic_cap->max_qps = dev_cap->max_sqs;
 
        if (nic_cap->num_qps > nic_cap->max_qps)
                nic_cap->num_qps = nic_cap->max_qps;
 
+       if (!HINIC_IS_VF(hwdev->hwif)) {
+               nic_cap->max_vf = dev_cap->max_vf;
+               nic_cap->max_vf_qps = dev_cap->max_vf_sqs + 1;
+       }
+
        return 0;
 }
 
@@ -105,27 +96,26 @@ static int get_capability(struct hinic_hwdev *hwdev,
  *
  * Return 0 - Success, negative - Failure
  **/
-static int get_cap_from_fw(struct hinic_pfhwdev *pfhwdev)
+static int get_capability(struct hinic_pfhwdev *pfhwdev)
 {
        struct hinic_hwdev *hwdev = &pfhwdev->hwdev;
        struct hinic_hwif *hwif = hwdev->hwif;
        struct pci_dev *pdev = hwif->pdev;
        struct hinic_dev_cap dev_cap;
-       u16 in_len, out_len;
+       u16 out_len;
        int err;
 
-       in_len = 0;
        out_len = sizeof(dev_cap);
 
        err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_CFGM,
-                               HINIC_CFG_NIC_CAP, &dev_cap, in_len, &dev_cap,
-                               &out_len, HINIC_MGMT_MSG_SYNC);
+                               HINIC_CFG_NIC_CAP, &dev_cap, sizeof(dev_cap),
+                               &dev_cap, &out_len, HINIC_MGMT_MSG_SYNC);
        if (err) {
                dev_err(&pdev->dev, "Failed to get capability from FW\n");
                return err;
        }
 
-       return get_capability(hwdev, &dev_cap);
+       return parse_capability(hwdev, &dev_cap);
 }
 
 /**
@@ -144,15 +134,14 @@ static int get_dev_cap(struct hinic_hwdev *hwdev)
        switch (HINIC_FUNC_TYPE(hwif)) {
        case HINIC_PPF:
        case HINIC_PF:
+       case HINIC_VF:
                pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
-
-               err = get_cap_from_fw(pfhwdev);
+               err = get_capability(pfhwdev);
                if (err) {
-                       dev_err(&pdev->dev, "Failed to get capability from FW\n");
+                       dev_err(&pdev->dev, "Failed to get capability\n");
                        return err;
                }
                break;
-
        default:
                dev_err(&pdev->dev, "Unsupported PCI Function type\n");
                return -EINVAL;
@@ -225,15 +214,8 @@ static void disable_msix(struct hinic_hwdev *hwdev)
 int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd,
                       void *buf_in, u16 in_size, void *buf_out, u16 *out_size)
 {
-       struct hinic_hwif *hwif = hwdev->hwif;
-       struct pci_dev *pdev = hwif->pdev;
        struct hinic_pfhwdev *pfhwdev;
 
-       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
-               dev_err(&pdev->dev, "unsupported PCI Function type\n");
-               return -EINVAL;
-       }
-
        pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
 
        return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC, cmd,
@@ -252,14 +234,9 @@ static int init_fw_ctxt(struct hinic_hwdev *hwdev)
        struct hinic_hwif *hwif = hwdev->hwif;
        struct pci_dev *pdev = hwif->pdev;
        struct hinic_cmd_fw_ctxt fw_ctxt;
-       u16 out_size;
+       u16 out_size = sizeof(fw_ctxt);
        int err;
 
-       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
-               dev_err(&pdev->dev, "Unsupported PCI Function type\n");
-               return -EINVAL;
-       }
-
        fw_ctxt.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
        fw_ctxt.rx_buf_sz = HINIC_RX_BUF_SZ;
 
@@ -288,14 +265,8 @@ static int set_hw_ioctxt(struct hinic_hwdev *hwdev, unsigned int rq_depth,
 {
        struct hinic_hwif *hwif = hwdev->hwif;
        struct hinic_cmd_hw_ioctxt hw_ioctxt;
-       struct pci_dev *pdev = hwif->pdev;
        struct hinic_pfhwdev *pfhwdev;
 
-       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
-               dev_err(&pdev->dev, "Unsupported PCI Function type\n");
-               return -EINVAL;
-       }
-
        hw_ioctxt.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
        hw_ioctxt.ppf_idx = HINIC_HWIF_PPF_IDX(hwif);
 
@@ -374,11 +345,6 @@ static int clear_io_resources(struct hinic_hwdev *hwdev)
        struct hinic_pfhwdev *pfhwdev;
        int err;
 
-       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
-               dev_err(&pdev->dev, "Unsupported PCI Function type\n");
-               return -EINVAL;
-       }
-
        /* sleep 100ms to wait for firmware stopping I/O */
        msleep(100);
 
@@ -410,14 +376,8 @@ static int set_resources_state(struct hinic_hwdev *hwdev,
 {
        struct hinic_cmd_set_res_state res_state;
        struct hinic_hwif *hwif = hwdev->hwif;
-       struct pci_dev *pdev = hwif->pdev;
        struct hinic_pfhwdev *pfhwdev;
 
-       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
-               dev_err(&pdev->dev, "Unsupported PCI Function type\n");
-               return -EINVAL;
-       }
-
        res_state.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
        res_state.state = state;
 
@@ -441,8 +401,8 @@ static int get_base_qpn(struct hinic_hwdev *hwdev, u16 *base_qpn)
 {
        struct hinic_cmd_base_qpn cmd_base_qpn;
        struct hinic_hwif *hwif = hwdev->hwif;
+       u16 out_size = sizeof(cmd_base_qpn);
        struct pci_dev *pdev = hwif->pdev;
-       u16 out_size;
        int err;
 
        cmd_base_qpn.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
@@ -488,7 +448,7 @@ int hinic_hwdev_ifup(struct hinic_hwdev *hwdev)
        num_ceqs = HINIC_HWIF_NUM_CEQS(hwif);
 
        ceq_msix_entries = &hwdev->msix_entries[num_aeqs];
-
+       func_to_io->hwdev = hwdev;
        err = hinic_io_init(func_to_io, hwif, nic_cap->max_qps, num_ceqs,
                            ceq_msix_entries);
        if (err) {
@@ -558,17 +518,10 @@ void hinic_hwdev_cb_register(struct hinic_hwdev *hwdev,
                                             u16 in_size, void *buf_out,
                                             u16 *out_size))
 {
-       struct hinic_hwif *hwif = hwdev->hwif;
-       struct pci_dev *pdev = hwif->pdev;
        struct hinic_pfhwdev *pfhwdev;
        struct hinic_nic_cb *nic_cb;
        u8 cmd_cb;
 
-       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
-               dev_err(&pdev->dev, "unsupported PCI Function type\n");
-               return;
-       }
-
        pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
 
        cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE;
@@ -588,15 +541,12 @@ void hinic_hwdev_cb_unregister(struct hinic_hwdev *hwdev,
                               enum hinic_mgmt_msg_cmd cmd)
 {
        struct hinic_hwif *hwif = hwdev->hwif;
-       struct pci_dev *pdev = hwif->pdev;
        struct hinic_pfhwdev *pfhwdev;
        struct hinic_nic_cb *nic_cb;
        u8 cmd_cb;
 
-       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
-               dev_err(&pdev->dev, "unsupported PCI Function type\n");
+       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif))
                return;
-       }
 
        pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
 
@@ -676,10 +626,23 @@ static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev)
                return err;
        }
 
-       hinic_register_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC,
-                                  pfhwdev, nic_mgmt_msg_handler);
+       err = hinic_func_to_func_init(hwdev);
+       if (err) {
+               dev_err(&hwif->pdev->dev, "Failed to init mailbox\n");
+               hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt);
+               return err;
+       }
+
+       if (!HINIC_IS_VF(hwif))
+               hinic_register_mgmt_msg_cb(&pfhwdev->pf_to_mgmt,
+                                          HINIC_MOD_L2NIC, pfhwdev,
+                                          nic_mgmt_msg_handler);
+       else
+               hinic_register_vf_mbox_cb(hwdev, HINIC_MOD_L2NIC,
+                                         nic_mgmt_msg_handler);
 
        hinic_set_pf_action(hwif, HINIC_PF_MGMT_ACTIVE);
+
        return 0;
 }
 
@@ -693,7 +656,13 @@ static void free_pfhwdev(struct hinic_pfhwdev *pfhwdev)
 
        hinic_set_pf_action(hwdev->hwif, HINIC_PF_MGMT_INIT);
 
-       hinic_unregister_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC);
+       if (!HINIC_IS_VF(hwdev->hwif))
+               hinic_unregister_mgmt_msg_cb(&pfhwdev->pf_to_mgmt,
+                                            HINIC_MOD_L2NIC);
+       else
+               hinic_unregister_vf_mbox_cb(hwdev, HINIC_MOD_L2NIC);
+
+       hinic_func_to_func_free(hwdev);
 
        hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt);
 }
@@ -723,12 +692,6 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev)
                return ERR_PTR(err);
        }
 
-       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
-               dev_err(&pdev->dev, "Unsupported PCI Function type\n");
-               err = -EFAULT;
-               goto err_func_type;
-       }
-
        pfhwdev = devm_kzalloc(&pdev->dev, sizeof(*pfhwdev), GFP_KERNEL);
        if (!pfhwdev) {
                err = -ENOMEM;
@@ -772,6 +735,12 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev)
                goto err_dev_cap;
        }
 
+       err = hinic_vf_func_init(hwdev);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to init nic mbox\n");
+               goto err_vf_func_init;
+       }
+
        err = init_fw_ctxt(hwdev);
        if (err) {
                dev_err(&pdev->dev, "Failed to init function table\n");
@@ -788,6 +757,8 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev)
 
 err_resources_state:
 err_init_fw_ctxt:
+       hinic_vf_func_free(hwdev);
+err_vf_func_init:
 err_dev_cap:
        free_pfhwdev(pfhwdev);
 
@@ -799,7 +770,6 @@ err_aeqs_init:
 
 err_init_msix:
 err_pfhwdev_alloc:
-err_func_type:
        hinic_free_hwif(hwif);
        return ERR_PTR(err);
 }
@@ -930,15 +900,9 @@ int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq,
 {
        struct hinic_qp *qp = container_of(sq, struct hinic_qp, sq);
        struct hinic_hwif *hwif = hwdev->hwif;
-       struct pci_dev *pdev = hwif->pdev;
        struct hinic_pfhwdev *pfhwdev;
        struct hinic_cmd_hw_ci hw_ci;
 
-       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
-               dev_err(&pdev->dev, "Unsupported PCI Function type\n");
-               return -EINVAL;
-       }
-
        hw_ci.dma_attr_off  = 0;
        hw_ci.pending_limit = pending_limit;
        hw_ci.coalesc_timer = coalesc_timer;
index 66fd234..531d107 100644 (file)
 #include "hinic_hw_mgmt.h"
 #include "hinic_hw_qp.h"
 #include "hinic_hw_io.h"
+#include "hinic_hw_mbox.h"
 
 #define HINIC_MAX_QPS   32
 
 #define HINIC_MGMT_NUM_MSG_CMD  (HINIC_MGMT_MSG_CMD_MAX - \
                                 HINIC_MGMT_MSG_CMD_BASE)
 
+#define HINIC_PF_SET_VF_ALREADY                                0x4
+#define HINIC_MGMT_STATUS_EXIST                                0x6
+
 struct hinic_cap {
        u16     max_qps;
        u16     num_qps;
+       u8              max_vf;
+       u16     max_vf_qps;
 };
 
 enum hinic_port_cmd {
+       HINIC_PORT_CMD_VF_REGISTER = 0x0,
+       HINIC_PORT_CMD_VF_UNREGISTER = 0x1,
+
        HINIC_PORT_CMD_CHANGE_MTU       = 2,
 
        HINIC_PORT_CMD_ADD_VLAN         = 3,
@@ -83,10 +92,18 @@ enum hinic_port_cmd {
 
        HINIC_PORT_CMD_GET_GLOBAL_QPN   = 102,
 
+       HINIC_PORT_CMD_SET_VF_VLAN      = 106,
+
+       HINIC_PORT_CMD_CLR_VF_VLAN,
+
        HINIC_PORT_CMD_SET_TSO          = 112,
 
        HINIC_PORT_CMD_SET_RQ_IQ_MAP    = 115,
 
+       HINIC_PORT_CMD_LINK_STATUS_REPORT = 160,
+
+       HINIC_PORT_CMD_UPDATE_MAC = 164,
+
        HINIC_PORT_CMD_GET_CAP          = 170,
 
        HINIC_PORT_CMD_SET_LRO_TIMER    = 244,
@@ -191,6 +208,17 @@ struct hinic_cmd_set_res_state {
        u32     rsvd2;
 };
 
+struct hinic_ceq_ctrl_reg {
+       u8 status;
+       u8 version;
+       u8 rsvd0[6];
+
+       u16 func_id;
+       u16 q_id;
+       u32 ctrl0;
+       u32 ctrl1;
+};
+
 struct hinic_cmd_base_qpn {
        u8      status;
        u8      version;
@@ -225,6 +253,7 @@ struct hinic_hwdev {
 
        struct hinic_aeqs               aeqs;
        struct hinic_func_to_io         func_to_io;
+       struct hinic_mbox_func_to_func  *func_to_func;
 
        struct hinic_cap                nic_cap;
 };
@@ -246,6 +275,25 @@ struct hinic_pfhwdev {
        struct hinic_nic_cb             nic_cb[HINIC_MGMT_NUM_MSG_CMD];
 };
 
+struct hinic_dev_cap {
+       u8      status;
+       u8      version;
+       u8      rsvd0[6];
+
+       u8      rsvd1[5];
+       u8      intr_type;
+       u8      max_cos_id;
+       u8      er_id;
+       u8      port_id;
+       u8      max_vf;
+       u8      rsvd2[62];
+       u16     max_sqs;
+       u16     max_rqs;
+       u16     max_vf_sqs;
+       u16     max_vf_rqs;
+       u8      rsvd3[204];
+};
+
 void hinic_hwdev_cb_register(struct hinic_hwdev *hwdev,
                             enum hinic_mgmt_msg_cmd cmd, void *handle,
                             void (*handler)(void *handle, void *buf_in,
index c0b6bcb..397936c 100644 (file)
@@ -17,6 +17,7 @@
 #include <asm/byteorder.h>
 #include <asm/barrier.h>
 
+#include "hinic_hw_dev.h"
 #include "hinic_hw_csr.h"
 #include "hinic_hw_if.h"
 #include "hinic_hw_eqs.h"
@@ -416,11 +417,11 @@ static irqreturn_t ceq_interrupt(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static void set_ctrl0(struct hinic_eq *eq)
+static u32 get_ctrl0_val(struct hinic_eq *eq, u32 addr)
 {
        struct msix_entry *msix_entry = &eq->msix_entry;
        enum hinic_eq_type type = eq->type;
-       u32 addr, val, ctrl0;
+       u32 val, ctrl0;
 
        if (type == HINIC_AEQ) {
                /* RMW Ctrl0 */
@@ -440,9 +441,7 @@ static void set_ctrl0(struct hinic_eq *eq)
                        HINIC_AEQ_CTRL_0_SET(EQ_INT_MODE_ARMED, INT_MODE);
 
                val |= ctrl0;
-
-               hinic_hwif_write_reg(eq->hwif, addr, val);
-       } else if (type == HINIC_CEQ) {
+       } else {
                /* RMW Ctrl0 */
                addr = HINIC_CSR_CEQ_CTRL_0_ADDR(eq->q_id);
 
@@ -462,16 +461,28 @@ static void set_ctrl0(struct hinic_eq *eq)
                        HINIC_CEQ_CTRL_0_SET(EQ_INT_MODE_ARMED, INTR_MODE);
 
                val |= ctrl0;
-
-               hinic_hwif_write_reg(eq->hwif, addr, val);
        }
+       return val;
 }
 
-static void set_ctrl1(struct hinic_eq *eq)
+static void set_ctrl0(struct hinic_eq *eq)
 {
+       u32 val, addr;
+
+       if (eq->type == HINIC_AEQ)
+               addr = HINIC_CSR_AEQ_CTRL_0_ADDR(eq->q_id);
+       else
+               addr = HINIC_CSR_CEQ_CTRL_0_ADDR(eq->q_id);
+
+       val = get_ctrl0_val(eq, addr);
+
+       hinic_hwif_write_reg(eq->hwif, addr, val);
+}
+
+static u32 get_ctrl1_val(struct hinic_eq *eq, u32 addr)
+{
+       u32 page_size_val, elem_size, val, ctrl1;
        enum hinic_eq_type type = eq->type;
-       u32 page_size_val, elem_size;
-       u32 addr, val, ctrl1;
 
        if (type == HINIC_AEQ) {
                /* RMW Ctrl1 */
@@ -491,9 +502,7 @@ static void set_ctrl1(struct hinic_eq *eq)
                        HINIC_AEQ_CTRL_1_SET(page_size_val, PAGE_SIZE);
 
                val |= ctrl1;
-
-               hinic_hwif_write_reg(eq->hwif, addr, val);
-       } else if (type == HINIC_CEQ) {
+       } else {
                /* RMW Ctrl1 */
                addr = HINIC_CSR_CEQ_CTRL_1_ADDR(eq->q_id);
 
@@ -508,19 +517,70 @@ static void set_ctrl1(struct hinic_eq *eq)
                        HINIC_CEQ_CTRL_1_SET(page_size_val, PAGE_SIZE);
 
                val |= ctrl1;
+       }
+       return val;
+}
 
-               hinic_hwif_write_reg(eq->hwif, addr, val);
+static void set_ctrl1(struct hinic_eq *eq)
+{
+       u32 addr, val;
+
+       if (eq->type == HINIC_AEQ)
+               addr = HINIC_CSR_AEQ_CTRL_1_ADDR(eq->q_id);
+       else
+               addr = HINIC_CSR_CEQ_CTRL_1_ADDR(eq->q_id);
+
+       val = get_ctrl1_val(eq, addr);
+
+       hinic_hwif_write_reg(eq->hwif, addr, val);
+}
+
+static int set_ceq_ctrl_reg(struct hinic_eq *eq)
+{
+       struct hinic_ceq_ctrl_reg ceq_ctrl = {0};
+       struct hinic_hwdev *hwdev = eq->hwdev;
+       u16 out_size = sizeof(ceq_ctrl);
+       u16 in_size = sizeof(ceq_ctrl);
+       struct hinic_pfhwdev *pfhwdev;
+       u32 addr;
+       int err;
+
+       pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
+
+       addr = HINIC_CSR_CEQ_CTRL_0_ADDR(eq->q_id);
+       ceq_ctrl.ctrl0 = get_ctrl0_val(eq, addr);
+       addr = HINIC_CSR_CEQ_CTRL_1_ADDR(eq->q_id);
+       ceq_ctrl.ctrl1 = get_ctrl1_val(eq, addr);
+
+       ceq_ctrl.func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
+       ceq_ctrl.q_id = eq->q_id;
+
+       err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
+                               HINIC_COMM_CMD_CEQ_CTRL_REG_WR_BY_UP,
+                               &ceq_ctrl, in_size,
+                               &ceq_ctrl, &out_size, HINIC_MGMT_MSG_SYNC);
+       if (err || !out_size || ceq_ctrl.status) {
+               dev_err(&hwdev->hwif->pdev->dev,
+                       "Failed to set ceq %d ctrl reg, err: %d status: 0x%x, out_size: 0x%x\n",
+                       eq->q_id, err, ceq_ctrl.status, out_size);
+               return -EFAULT;
        }
+
+       return 0;
 }
 
 /**
  * set_eq_ctrls - setting eq's ctrl registers
  * @eq: the Event Queue for setting
  **/
-static void set_eq_ctrls(struct hinic_eq *eq)
+static int set_eq_ctrls(struct hinic_eq *eq)
 {
+       if (HINIC_IS_VF(eq->hwif) && eq->type == HINIC_CEQ)
+               return set_ceq_ctrl_reg(eq);
+
        set_ctrl0(eq);
        set_ctrl1(eq);
+       return 0;
 }
 
 /**
@@ -703,7 +763,12 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif,
                return -EINVAL;
        }
 
-       set_eq_ctrls(eq);
+       err = set_eq_ctrls(eq);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to set eq ctrls\n");
+               return err;
+       }
+
        eq_update_ci(eq, EQ_ARMED);
 
        err = alloc_eq_pages(eq);
@@ -859,6 +924,7 @@ int hinic_ceqs_init(struct hinic_ceqs *ceqs, struct hinic_hwif *hwif,
        ceqs->num_ceqs = num_ceqs;
 
        for (q_id = 0; q_id < num_ceqs; q_id++) {
+               ceqs->ceq[q_id].hwdev = ceqs->hwdev;
                err = init_eq(&ceqs->ceq[q_id], hwif, HINIC_CEQ, q_id, q_len,
                              page_size, msix_entries[q_id]);
                if (err) {
index d35f206..74b9ff9 100644 (file)
@@ -143,8 +143,9 @@ enum hinic_eq_type {
 };
 
 enum hinic_aeq_type {
+       HINIC_MBX_FROM_FUNC = 1,
        HINIC_MSG_FROM_MGMT_CPU = 2,
-
+       HINIC_MBX_SEND_RSLT = 5,
        HINIC_MAX_AEQ_EVENTS,
 };
 
@@ -171,7 +172,7 @@ struct hinic_eq_work {
 
 struct hinic_eq {
        struct hinic_hwif       *hwif;
-
+       struct hinic_hwdev      *hwdev;
        enum hinic_eq_type      type;
        int                     q_id;
        u32                     q_len;
@@ -219,7 +220,7 @@ struct hinic_ceq_cb {
 
 struct hinic_ceqs {
        struct hinic_hwif       *hwif;
-
+       struct hinic_hwdev              *hwdev;
        struct hinic_eq         ceq[HINIC_MAX_CEQS];
        int                     num_ceqs;
 
index 07bbfbf..3fbd2eb 100644 (file)
@@ -115,8 +115,12 @@ int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index)
  **/
 void hinic_set_pf_action(struct hinic_hwif *hwif, enum hinic_pf_action action)
 {
-       u32 attr5 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR5_ADDR);
+       u32 attr5;
 
+       if (HINIC_IS_VF(hwif))
+               return;
+
+       attr5 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR5_ADDR);
        attr5 = HINIC_FA5_CLEAR(attr5, PF_ACTION);
        attr5 |= HINIC_FA5_SET(action, PF_ACTION);
 
@@ -203,7 +207,8 @@ static int hwif_ready(struct hinic_hwif *hwif)
  * @attr0: the first attribute that was read from the hw
  * @attr1: the second attribute that was read from the hw
  **/
-static void set_hwif_attr(struct hinic_hwif *hwif, u32 attr0, u32 attr1)
+static void set_hwif_attr(struct hinic_hwif *hwif, u32 attr0, u32 attr1,
+                         u32 attr2)
 {
        hwif->attr.func_idx     = HINIC_FA0_GET(attr0, FUNC_IDX);
        hwif->attr.pf_idx       = HINIC_FA0_GET(attr0, PF_IDX);
@@ -214,6 +219,8 @@ static void set_hwif_attr(struct hinic_hwif *hwif, u32 attr0, u32 attr1)
        hwif->attr.num_ceqs = BIT(HINIC_FA1_GET(attr1, CEQS_PER_FUNC));
        hwif->attr.num_irqs = BIT(HINIC_FA1_GET(attr1, IRQS_PER_FUNC));
        hwif->attr.num_dma_attr = BIT(HINIC_FA1_GET(attr1, DMA_ATTR_PER_FUNC));
+       hwif->attr.global_vf_id_of_pf = HINIC_FA2_GET(attr2,
+                                                     GLOBAL_VF_ID_OF_PF);
 }
 
 /**
@@ -222,7 +229,7 @@ static void set_hwif_attr(struct hinic_hwif *hwif, u32 attr0, u32 attr1)
  **/
 static void read_hwif_attr(struct hinic_hwif *hwif)
 {
-       u32 addr, attr0, attr1;
+       u32 addr, attr0, attr1, attr2;
 
        addr   = HINIC_CSR_FUNC_ATTR0_ADDR;
        attr0  = hinic_hwif_read_reg(hwif, addr);
@@ -230,7 +237,10 @@ static void read_hwif_attr(struct hinic_hwif *hwif)
        addr   = HINIC_CSR_FUNC_ATTR1_ADDR;
        attr1  = hinic_hwif_read_reg(hwif, addr);
 
-       set_hwif_attr(hwif, attr0, attr1);
+       addr   = HINIC_CSR_FUNC_ATTR2_ADDR;
+       attr2  = hinic_hwif_read_reg(hwif, addr);
+
+       set_hwif_attr(hwif, attr0, attr1, attr2);
 }
 
 /**
@@ -309,6 +319,34 @@ static void dma_attr_init(struct hinic_hwif *hwif)
                     HINIC_PCIE_SNOOP, HINIC_PCIE_TPH_DISABLE);
 }
 
+u16 hinic_glb_pf_vf_offset(struct hinic_hwif *hwif)
+{
+       if (!hwif)
+               return 0;
+
+       return hwif->attr.global_vf_id_of_pf;
+}
+
+u16 hinic_global_func_id_hw(struct hinic_hwif *hwif)
+{
+       u32 addr, attr0;
+
+       addr   = HINIC_CSR_FUNC_ATTR0_ADDR;
+       attr0  = hinic_hwif_read_reg(hwif, addr);
+
+       return HINIC_FA0_GET(attr0, FUNC_IDX);
+}
+
+u16 hinic_pf_id_of_vf_hw(struct hinic_hwif *hwif)
+{
+       u32 addr, attr0;
+
+       addr   = HINIC_CSR_FUNC_ATTR0_ADDR;
+       attr0  = hinic_hwif_read_reg(hwif, addr);
+
+       return HINIC_FA0_GET(attr0, PF_IDX);
+}
+
 /**
  * hinic_init_hwif - initialize the hw interface
  * @hwif: the HW interface of a pci function device
index c7bb9ce..53bb89c 100644 (file)
@@ -35,6 +35,7 @@
 #define HINIC_FA0_FUNC_IDX_SHIFT                                0
 #define HINIC_FA0_PF_IDX_SHIFT                                  10
 #define HINIC_FA0_PCI_INTF_IDX_SHIFT                            14
+#define HINIC_FA0_VF_IN_PF_SHIFT                               16
 /* reserved members - off 16 */
 #define HINIC_FA0_FUNC_TYPE_SHIFT                               24
 
@@ -42,6 +43,7 @@
 #define HINIC_FA0_PF_IDX_MASK                                   0xF
 #define HINIC_FA0_PCI_INTF_IDX_MASK                             0x3
 #define HINIC_FA0_FUNC_TYPE_MASK                                0x1
+#define HINIC_FA0_VF_IN_PF_MASK                                        0xFF
 
 #define HINIC_FA0_GET(val, member)                              \
        (((val) >> HINIC_FA0_##member##_SHIFT) & HINIC_FA0_##member##_MASK)
 #define HINIC_FA1_GET(val, member)                              \
        (((val) >> HINIC_FA1_##member##_SHIFT) & HINIC_FA1_##member##_MASK)
 
+#define HINIC_FA2_GLOBAL_VF_ID_OF_PF_SHIFT     16
+#define HINIC_FA2_GLOBAL_VF_ID_OF_PF_MASK      0x3FF
+
+#define HINIC_FA2_GET(val, member)                             \
+       (((val) >> HINIC_FA2_##member##_SHIFT) & HINIC_FA2_##member##_MASK)
+
 #define HINIC_FA4_OUTBOUND_STATE_SHIFT                          0
 #define HINIC_FA4_DB_STATE_SHIFT                                1
 
 #define HINIC_HWIF_PPF_IDX(hwif)        ((hwif)->attr.ppf_idx)
 
 #define HINIC_FUNC_TYPE(hwif)           ((hwif)->attr.func_type)
+#define HINIC_IS_VF(hwif)               (HINIC_FUNC_TYPE(hwif) == HINIC_VF)
 #define HINIC_IS_PF(hwif)               (HINIC_FUNC_TYPE(hwif) == HINIC_PF)
 #define HINIC_IS_PPF(hwif)              (HINIC_FUNC_TYPE(hwif) == HINIC_PPF)
 
@@ -173,6 +182,7 @@ enum hinic_pcie_tph {
 
 enum hinic_func_type {
        HINIC_PF        = 0,
+       HINIC_VF            = 1,
        HINIC_PPF       = 2,
 };
 
@@ -223,6 +233,8 @@ struct hinic_func_attr {
        u8                      num_ceqs;
 
        u8                      num_dma_attr;
+
+       u16                                             global_vf_id_of_pf;
 };
 
 struct hinic_hwif {
@@ -271,6 +283,12 @@ enum hinic_db_state hinic_db_state_get(struct hinic_hwif *hwif);
 void hinic_db_state_set(struct hinic_hwif *hwif,
                        enum hinic_db_state db_state);
 
+u16 hinic_glb_pf_vf_offset(struct hinic_hwif *hwif);
+
+u16 hinic_global_func_id_hw(struct hinic_hwif *hwif);
+
+u16 hinic_pf_id_of_vf_hw(struct hinic_hwif *hwif);
+
 int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev);
 
 void hinic_free_hwif(struct hinic_hwif *hwif);
index d66f86f..a4581c9 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/io.h>
 #include <linux/err.h>
 
+#include "hinic_hw_dev.h"
 #include "hinic_hw_if.h"
 #include "hinic_hw_eqs.h"
 #include "hinic_hw_wqe.h"
@@ -34,6 +35,8 @@
 #define DB_IDX(db, db_base)             \
        (((unsigned long)(db) - (unsigned long)(db_base)) / HINIC_DB_PAGE_SIZE)
 
+#define HINIC_PAGE_SIZE_HW(pg_size)    ((u8)ilog2((u32)((pg_size) >> 12)))
+
 enum io_cmd {
        IO_CMD_MODIFY_QUEUE_CTXT = 0,
        IO_CMD_CLEAN_QUEUE_CTXT,
@@ -484,6 +487,33 @@ void hinic_io_destroy_qps(struct hinic_func_to_io *func_to_io, int num_qps)
        devm_kfree(&pdev->dev, func_to_io->qps);
 }
 
+int hinic_set_wq_page_size(struct hinic_hwdev *hwdev, u16 func_idx,
+                          u32 page_size)
+{
+       struct hinic_wq_page_size page_size_info = {0};
+       u16 out_size = sizeof(page_size_info);
+       struct hinic_pfhwdev *pfhwdev;
+       int err;
+
+       pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
+
+       page_size_info.func_idx = func_idx;
+       page_size_info.ppf_idx = HINIC_HWIF_PPF_IDX(hwdev->hwif);
+       page_size_info.page_size = HINIC_PAGE_SIZE_HW(page_size);
+
+       err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
+                               HINIC_COMM_CMD_PAGESIZE_SET, &page_size_info,
+                               sizeof(page_size_info), &page_size_info,
+                               &out_size, HINIC_MGMT_MSG_SYNC);
+       if (err || !out_size || page_size_info.status) {
+               dev_err(&hwdev->hwif->pdev->dev, "Failed to set wq page size, err: %d, status: 0x%x, out_size: 0x%0x\n",
+                       err, page_size_info.status, out_size);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
 /**
  * hinic_io_init - Initialize the IO components
  * @func_to_io: func to io channel that holds the IO components
@@ -506,6 +536,7 @@ int hinic_io_init(struct hinic_func_to_io *func_to_io,
        func_to_io->hwif = hwif;
        func_to_io->qps = NULL;
        func_to_io->max_qps = max_qps;
+       func_to_io->ceqs.hwdev = func_to_io->hwdev;
 
        err = hinic_ceqs_init(&func_to_io->ceqs, hwif, num_ceqs,
                              HINIC_DEFAULT_CEQ_LEN, HINIC_EQ_PAGE_SIZE,
@@ -541,6 +572,14 @@ int hinic_io_init(struct hinic_func_to_io *func_to_io,
                func_to_io->cmdq_db_area[cmdq] = db_area;
        }
 
+       err = hinic_set_wq_page_size(func_to_io->hwdev,
+                                    HINIC_HWIF_FUNC_IDX(hwif),
+                                    HINIC_DEFAULT_WQ_PAGE_SIZE);
+       if (err) {
+               dev_err(&func_to_io->hwif->pdev->dev, "Failed to set wq page size\n");
+               goto init_wq_pg_size_err;
+       }
+
        err = hinic_init_cmdqs(&func_to_io->cmdqs, hwif,
                               func_to_io->cmdq_db_area);
        if (err) {
@@ -551,6 +590,11 @@ int hinic_io_init(struct hinic_func_to_io *func_to_io,
        return 0;
 
 err_init_cmdqs:
+       if (!HINIC_IS_VF(func_to_io->hwif))
+               hinic_set_wq_page_size(func_to_io->hwdev,
+                                      HINIC_HWIF_FUNC_IDX(hwif),
+                                      HINIC_HW_WQ_PAGE_SIZE);
+init_wq_pg_size_err:
 err_db_area:
        for (type = HINIC_CMDQ_SYNC; type < cmdq; type++)
                return_db_area(func_to_io, func_to_io->cmdq_db_area[type]);
@@ -575,6 +619,11 @@ void hinic_io_free(struct hinic_func_to_io *func_to_io)
 
        hinic_free_cmdqs(&func_to_io->cmdqs);
 
+       if (!HINIC_IS_VF(func_to_io->hwif))
+               hinic_set_wq_page_size(func_to_io->hwdev,
+                                      HINIC_HWIF_FUNC_IDX(func_to_io->hwif),
+                                      HINIC_HW_WQ_PAGE_SIZE);
+
        for (cmdq = HINIC_CMDQ_SYNC; cmdq < HINIC_MAX_CMDQ_TYPES; cmdq++)
                return_db_area(func_to_io, func_to_io->cmdq_db_area[cmdq]);
 
index cac2b72..28c0594 100644 (file)
@@ -20,6 +20,8 @@
 
 #define HINIC_DB_PAGE_SIZE      SZ_4K
 #define HINIC_DB_SIZE           SZ_4M
+#define HINIC_HW_WQ_PAGE_SIZE  SZ_4K
+#define HINIC_DEFAULT_WQ_PAGE_SIZE SZ_256K
 
 #define HINIC_DB_MAX_AREAS      (HINIC_DB_SIZE / HINIC_DB_PAGE_SIZE)
 
@@ -47,7 +49,7 @@ struct hinic_free_db_area {
 
 struct hinic_func_to_io {
        struct hinic_hwif       *hwif;
-
+       struct hinic_hwdev      *hwdev;
        struct hinic_ceqs       ceqs;
 
        struct hinic_wqs        wqs;
@@ -69,8 +71,27 @@ struct hinic_func_to_io {
        void __iomem                    *cmdq_db_area[HINIC_MAX_CMDQ_TYPES];
 
        struct hinic_cmdqs              cmdqs;
+
+       u16                     max_vfs;
+       struct vf_data_storage  *vf_infos;
+       u8                      link_status;
 };
 
+struct hinic_wq_page_size {
+       u8      status;
+       u8      version;
+       u8      rsvd0[6];
+
+       u16     func_idx;
+       u8      ppf_idx;
+       u8      page_size;
+
+       u32     rsvd1;
+};
+
+int hinic_set_wq_page_size(struct hinic_hwdev *hwdev, u16 func_idx,
+                          u32 page_size);
+
 int hinic_io_create_qps(struct hinic_func_to_io *func_to_io,
                        u16 base_qpn, int num_qps,
                        struct msix_entry *sq_msix_entries,
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c
new file mode 100644 (file)
index 0000000..564fb22
--- /dev/null
@@ -0,0 +1,1210 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Huawei HiNIC PCI Express Linux driver
+ * Copyright(c) 2017 Huawei Technologies Co., Ltd
+ */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#include "hinic_hw_if.h"
+#include "hinic_hw_mgmt.h"
+#include "hinic_hw_csr.h"
+#include "hinic_hw_dev.h"
+#include "hinic_hw_mbox.h"
+
+#define HINIC_MBOX_INT_DST_FUNC_SHIFT                          0
+#define HINIC_MBOX_INT_DST_AEQN_SHIFT                          10
+#define HINIC_MBOX_INT_SRC_RESP_AEQN_SHIFT                     12
+#define HINIC_MBOX_INT_STAT_DMA_SHIFT                          14
+/* The size of data to be sended (unit of 4 bytes) */
+#define HINIC_MBOX_INT_TX_SIZE_SHIFT                           20
+/* SO_RO(strong order, relax order) */
+#define HINIC_MBOX_INT_STAT_DMA_SO_RO_SHIFT                    25
+#define HINIC_MBOX_INT_WB_EN_SHIFT                             28
+
+#define HINIC_MBOX_INT_DST_FUNC_MASK                           0x3FF
+#define HINIC_MBOX_INT_DST_AEQN_MASK                           0x3
+#define HINIC_MBOX_INT_SRC_RESP_AEQN_MASK                      0x3
+#define HINIC_MBOX_INT_STAT_DMA_MASK                           0x3F
+#define HINIC_MBOX_INT_TX_SIZE_MASK                            0x1F
+#define HINIC_MBOX_INT_STAT_DMA_SO_RO_MASK                     0x3
+#define HINIC_MBOX_INT_WB_EN_MASK                              0x1
+
+#define HINIC_MBOX_INT_SET(val, field) \
+                       (((val) & HINIC_MBOX_INT_##field##_MASK) << \
+                       HINIC_MBOX_INT_##field##_SHIFT)
+
+enum hinic_mbox_tx_status {
+       TX_NOT_DONE = 1,
+};
+
+#define HINIC_MBOX_CTRL_TRIGGER_AEQE_SHIFT                     0
+
+/* specifies the issue request for the message data.
+ * 0 - Tx request is done;
+ * 1 - Tx request is in process.
+ */
+#define HINIC_MBOX_CTRL_TX_STATUS_SHIFT                                1
+
+#define HINIC_MBOX_CTRL_TRIGGER_AEQE_MASK                      0x1
+#define HINIC_MBOX_CTRL_TX_STATUS_MASK                         0x1
+
+#define HINIC_MBOX_CTRL_SET(val, field)        \
+                       (((val) & HINIC_MBOX_CTRL_##field##_MASK) << \
+                       HINIC_MBOX_CTRL_##field##_SHIFT)
+
+#define HINIC_MBOX_HEADER_MSG_LEN_SHIFT                                0
+#define HINIC_MBOX_HEADER_MODULE_SHIFT                         11
+#define HINIC_MBOX_HEADER_SEG_LEN_SHIFT                                16
+#define HINIC_MBOX_HEADER_NO_ACK_SHIFT                         22
+#define HINIC_MBOX_HEADER_SEQID_SHIFT                          24
+#define HINIC_MBOX_HEADER_LAST_SHIFT                           30
+
+/* specifies the mailbox message direction
+ * 0 - send
+ * 1 - receive
+ */
+#define HINIC_MBOX_HEADER_DIRECTION_SHIFT                      31
+#define HINIC_MBOX_HEADER_CMD_SHIFT                            32
+#define HINIC_MBOX_HEADER_MSG_ID_SHIFT                         40
+#define HINIC_MBOX_HEADER_STATUS_SHIFT                         48
+#define HINIC_MBOX_HEADER_SRC_GLB_FUNC_IDX_SHIFT               54
+
+#define HINIC_MBOX_HEADER_MSG_LEN_MASK                         0x7FF
+#define HINIC_MBOX_HEADER_MODULE_MASK                          0x1F
+#define HINIC_MBOX_HEADER_SEG_LEN_MASK                         0x3F
+#define HINIC_MBOX_HEADER_NO_ACK_MASK                          0x1
+#define HINIC_MBOX_HEADER_SEQID_MASK                           0x3F
+#define HINIC_MBOX_HEADER_LAST_MASK                            0x1
+#define HINIC_MBOX_HEADER_DIRECTION_MASK                       0x1
+#define HINIC_MBOX_HEADER_CMD_MASK                             0xFF
+#define HINIC_MBOX_HEADER_MSG_ID_MASK                          0xFF
+#define HINIC_MBOX_HEADER_STATUS_MASK                          0x3F
+#define HINIC_MBOX_HEADER_SRC_GLB_FUNC_IDX_MASK                        0x3FF
+
+#define HINIC_MBOX_HEADER_GET(val, field)      \
+                       (((val) >> HINIC_MBOX_HEADER_##field##_SHIFT) & \
+                       HINIC_MBOX_HEADER_##field##_MASK)
+#define HINIC_MBOX_HEADER_SET(val, field)      \
+                       ((u64)((val) & HINIC_MBOX_HEADER_##field##_MASK) << \
+                       HINIC_MBOX_HEADER_##field##_SHIFT)
+
+#define MBOX_SEGLEN_MASK                       \
+               HINIC_MBOX_HEADER_SET(HINIC_MBOX_HEADER_SEG_LEN_MASK, SEG_LEN)
+
+#define HINIC_MBOX_SEG_LEN                     48
+#define HINIC_MBOX_COMP_TIME                   8000U
+#define MBOX_MSG_POLLING_TIMEOUT               8000
+
+#define HINIC_MBOX_DATA_SIZE                   2040
+
+#define MBOX_MAX_BUF_SZ                                2048UL
+#define MBOX_HEADER_SZ                         8
+
+#define MBOX_INFO_SZ                           4
+
+/* MBOX size is 64B, 8B for mbox_header, 4B reserved */
+#define MBOX_SEG_LEN                           48
+#define MBOX_SEG_LEN_ALIGN                     4
+#define MBOX_WB_STATUS_LEN                     16UL
+
+/* mbox write back status is 16B, only first 4B is used */
+#define MBOX_WB_STATUS_ERRCODE_MASK            0xFFFF
+#define MBOX_WB_STATUS_MASK                    0xFF
+#define MBOX_WB_ERROR_CODE_MASK                        0xFF00
+#define MBOX_WB_STATUS_FINISHED_SUCCESS                0xFF
+#define MBOX_WB_STATUS_FINISHED_WITH_ERR       0xFE
+#define MBOX_WB_STATUS_NOT_FINISHED            0x00
+
+#define MBOX_STATUS_FINISHED(wb)       \
+       (((wb) & MBOX_WB_STATUS_MASK) != MBOX_WB_STATUS_NOT_FINISHED)
+#define MBOX_STATUS_SUCCESS(wb)                \
+       (((wb) & MBOX_WB_STATUS_MASK) == MBOX_WB_STATUS_FINISHED_SUCCESS)
+#define MBOX_STATUS_ERRCODE(wb)                \
+       ((wb) & MBOX_WB_ERROR_CODE_MASK)
+
+#define SEQ_ID_START_VAL                       0
+#define SEQ_ID_MAX_VAL                         42
+
+#define DST_AEQ_IDX_DEFAULT_VAL                        0
+#define SRC_AEQ_IDX_DEFAULT_VAL                        0
+#define NO_DMA_ATTRIBUTE_VAL                   0
+
+#define HINIC_MGMT_RSP_AEQN                    0
+#define HINIC_MBOX_RSP_AEQN                    2
+#define HINIC_MBOX_RECV_AEQN                   0
+
+#define MBOX_MSG_NO_DATA_LEN                   1
+
+#define MBOX_BODY_FROM_HDR(header)     ((u8 *)(header) + MBOX_HEADER_SZ)
+#define MBOX_AREA(hwif)                        \
+       ((hwif)->cfg_regs_bar + HINIC_FUNC_CSR_MAILBOX_DATA_OFF)
+
+#define IS_PF_OR_PPF_SRC(src_func_idx) ((src_func_idx) < HINIC_MAX_PF_FUNCS)
+
+#define MBOX_RESPONSE_ERROR            0x1
+#define MBOX_MSG_ID_MASK               0xFF
+#define MBOX_MSG_ID(func_to_func)      ((func_to_func)->send_msg_id)
+#define MBOX_MSG_ID_INC(func_to_func_mbox) (MBOX_MSG_ID(func_to_func_mbox) = \
+                       (MBOX_MSG_ID(func_to_func_mbox) + 1) & MBOX_MSG_ID_MASK)
+
+#define FUNC_ID_OFF_SET_8B             8
+#define FUNC_ID_OFF_SET_10B            10
+
+/* max message counter wait to process for one function */
+#define HINIC_MAX_MSG_CNT_TO_PROCESS   10
+
+#define HINIC_QUEUE_MIN_DEPTH          6
+#define HINIC_QUEUE_MAX_DEPTH          12
+#define HINIC_MAX_RX_BUFFER_SIZE               15
+
+enum hinic_hwif_direction_type {
+       HINIC_HWIF_DIRECT_SEND  = 0,
+       HINIC_HWIF_RESPONSE     = 1,
+};
+
+enum mbox_send_mod {
+       MBOX_SEND_MSG_INT,
+};
+
+enum mbox_seg_type {
+       NOT_LAST_SEG,
+       LAST_SEG,
+};
+
+enum mbox_ordering_type {
+       STRONG_ORDER,
+};
+
+enum mbox_write_back_type {
+       WRITE_BACK = 1,
+};
+
+enum mbox_aeq_trig_type {
+       NOT_TRIGGER,
+       TRIGGER,
+};
+
+/**
+ * hinic_register_pf_mbox_cb - register mbox callback for pf
+ * @hwdev: the pointer to hw device
+ * @mod:       specific mod that the callback will handle
+ * @callback:  callback function
+ * Return: 0 - success, negative - failure
+ */
+int hinic_register_pf_mbox_cb(struct hinic_hwdev *hwdev,
+                             enum hinic_mod_type mod,
+                             hinic_pf_mbox_cb callback)
+{
+       struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func;
+
+       if (mod >= HINIC_MOD_MAX)
+               return -EFAULT;
+
+       func_to_func->pf_mbox_cb[mod] = callback;
+
+       set_bit(HINIC_PF_MBOX_CB_REG, &func_to_func->pf_mbox_cb_state[mod]);
+
+       return 0;
+}
+
+/**
+ * hinic_register_vf_mbox_cb - register mbox callback for vf
+ * @hwdev: the pointer to hw device
+ * @mod:       specific mod that the callback will handle
+ * @callback:  callback function
+ * Return: 0 - success, negative - failure
+ */
+int hinic_register_vf_mbox_cb(struct hinic_hwdev *hwdev,
+                             enum hinic_mod_type mod,
+                             hinic_vf_mbox_cb callback)
+{
+       struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func;
+
+       if (mod >= HINIC_MOD_MAX)
+               return -EFAULT;
+
+       func_to_func->vf_mbox_cb[mod] = callback;
+
+       set_bit(HINIC_VF_MBOX_CB_REG, &func_to_func->vf_mbox_cb_state[mod]);
+
+       return 0;
+}
+
+/**
+ * hinic_unregister_pf_mbox_cb - unregister the mbox callback for pf
+ * @hwdev:     the pointer to hw device
+ * @mod:       specific mod that the callback will handle
+ */
+void hinic_unregister_pf_mbox_cb(struct hinic_hwdev *hwdev,
+                                enum hinic_mod_type mod)
+{
+       struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func;
+
+       clear_bit(HINIC_PF_MBOX_CB_REG, &func_to_func->pf_mbox_cb_state[mod]);
+
+       while (test_bit(HINIC_PF_MBOX_CB_RUNNING,
+                       &func_to_func->pf_mbox_cb_state[mod]))
+               usleep_range(900, 1000);
+
+       func_to_func->pf_mbox_cb[mod] = NULL;
+}
+
+/**
+ * hinic_unregister_vf_mbox_cb - unregister the mbox callback for vf
+ * @hwdev:     the pointer to hw device
+ * @mod:       specific mod that the callback will handle
+ */
+void hinic_unregister_vf_mbox_cb(struct hinic_hwdev *hwdev,
+                                enum hinic_mod_type mod)
+{
+       struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func;
+
+       clear_bit(HINIC_VF_MBOX_CB_REG, &func_to_func->vf_mbox_cb_state[mod]);
+
+       while (test_bit(HINIC_VF_MBOX_CB_RUNNING,
+                       &func_to_func->vf_mbox_cb_state[mod]))
+               usleep_range(900, 1000);
+
+       func_to_func->vf_mbox_cb[mod] = NULL;
+}
+
+static int recv_vf_mbox_handler(struct hinic_mbox_func_to_func *func_to_func,
+                               struct hinic_recv_mbox *recv_mbox,
+                               void *buf_out, u16 *out_size)
+{
+       hinic_vf_mbox_cb cb;
+       int ret = 0;
+
+       if (recv_mbox->mod >= HINIC_MOD_MAX) {
+               dev_err(&func_to_func->hwif->pdev->dev, "Receive illegal mbox message, mod = %d\n",
+                       recv_mbox->mod);
+               return -EINVAL;
+       }
+
+       set_bit(HINIC_VF_MBOX_CB_RUNNING,
+               &func_to_func->vf_mbox_cb_state[recv_mbox->mod]);
+
+       cb = func_to_func->vf_mbox_cb[recv_mbox->mod];
+       if (cb && test_bit(HINIC_VF_MBOX_CB_REG,
+                          &func_to_func->vf_mbox_cb_state[recv_mbox->mod])) {
+               cb(func_to_func->hwdev, recv_mbox->cmd, recv_mbox->mbox,
+                  recv_mbox->mbox_len, buf_out, out_size);
+       } else {
+               dev_err(&func_to_func->hwif->pdev->dev, "VF mbox cb is not registered\n");
+               ret = -EINVAL;
+       }
+
+       clear_bit(HINIC_VF_MBOX_CB_RUNNING,
+                 &func_to_func->vf_mbox_cb_state[recv_mbox->mod]);
+
+       return ret;
+}
+
+static int
+recv_pf_from_vf_mbox_handler(struct hinic_mbox_func_to_func *func_to_func,
+                            struct hinic_recv_mbox *recv_mbox,
+                            u16 src_func_idx, void *buf_out,
+                            u16 *out_size)
+{
+       hinic_pf_mbox_cb cb;
+       u16 vf_id = 0;
+       int ret;
+
+       if (recv_mbox->mod >= HINIC_MOD_MAX) {
+               dev_err(&func_to_func->hwif->pdev->dev, "Receive illegal mbox message, mod = %d\n",
+                       recv_mbox->mod);
+               return -EINVAL;
+       }
+
+       set_bit(HINIC_PF_MBOX_CB_RUNNING,
+               &func_to_func->pf_mbox_cb_state[recv_mbox->mod]);
+
+       cb = func_to_func->pf_mbox_cb[recv_mbox->mod];
+       if (cb && test_bit(HINIC_PF_MBOX_CB_REG,
+                          &func_to_func->pf_mbox_cb_state[recv_mbox->mod])) {
+               vf_id = src_func_idx -
+                       hinic_glb_pf_vf_offset(func_to_func->hwif);
+               ret = cb(func_to_func->hwdev, vf_id, recv_mbox->cmd,
+                        recv_mbox->mbox, recv_mbox->mbox_len,
+                        buf_out, out_size);
+       } else {
+               dev_err(&func_to_func->hwif->pdev->dev, "PF mbox mod(0x%x) cb is not registered\n",
+                       recv_mbox->mod);
+               ret = -EINVAL;
+       }
+
+       clear_bit(HINIC_PF_MBOX_CB_RUNNING,
+                 &func_to_func->pf_mbox_cb_state[recv_mbox->mod]);
+
+       return ret;
+}
+
+static bool check_mbox_seq_id_and_seg_len(struct hinic_recv_mbox *recv_mbox,
+                                         u8 seq_id, u8 seg_len)
+{
+       if (seq_id > SEQ_ID_MAX_VAL || seg_len > MBOX_SEG_LEN)
+               return false;
+
+       if (seq_id == 0) {
+               recv_mbox->seq_id = seq_id;
+       } else {
+               if (seq_id != recv_mbox->seq_id + 1)
+                       return false;
+
+               recv_mbox->seq_id = seq_id;
+       }
+
+       return true;
+}
+
+static void resp_mbox_handler(struct hinic_mbox_func_to_func *func_to_func,
+                             struct hinic_recv_mbox *recv_mbox)
+{
+       spin_lock(&func_to_func->mbox_lock);
+       if (recv_mbox->msg_info.msg_id == func_to_func->send_msg_id &&
+           func_to_func->event_flag == EVENT_START)
+               complete(&recv_mbox->recv_done);
+       else
+               dev_err(&func_to_func->hwif->pdev->dev,
+                       "Mbox response timeout, current send msg id(0x%x), recv msg id(0x%x), status(0x%x)\n",
+                       func_to_func->send_msg_id, recv_mbox->msg_info.msg_id,
+                       recv_mbox->msg_info.status);
+       spin_unlock(&func_to_func->mbox_lock);
+}
+
+static void recv_func_mbox_handler(struct hinic_mbox_func_to_func *func_to_func,
+                                  struct hinic_recv_mbox *recv_mbox,
+                                  u16 src_func_idx);
+
+static void recv_func_mbox_work_handler(struct work_struct *work)
+{
+       struct hinic_mbox_work *mbox_work =
+                       container_of(work, struct hinic_mbox_work, work);
+       struct hinic_recv_mbox *recv_mbox;
+
+       recv_func_mbox_handler(mbox_work->func_to_func, mbox_work->recv_mbox,
+                              mbox_work->src_func_idx);
+
+       recv_mbox =
+               &mbox_work->func_to_func->mbox_send[mbox_work->src_func_idx];
+
+       atomic_dec(&recv_mbox->msg_cnt);
+
+       kfree(mbox_work);
+}
+
+static void recv_mbox_handler(struct hinic_mbox_func_to_func *func_to_func,
+                             void *header, struct hinic_recv_mbox *recv_mbox)
+{
+       void *mbox_body = MBOX_BODY_FROM_HDR(header);
+       struct hinic_recv_mbox *rcv_mbox_temp = NULL;
+       u64 mbox_header = *((u64 *)header);
+       struct hinic_mbox_work *mbox_work;
+       u8 seq_id, seg_len;
+       u16 src_func_idx;
+       int pos;
+
+       seq_id = HINIC_MBOX_HEADER_GET(mbox_header, SEQID);
+       seg_len = HINIC_MBOX_HEADER_GET(mbox_header, SEG_LEN);
+       src_func_idx = HINIC_MBOX_HEADER_GET(mbox_header, SRC_GLB_FUNC_IDX);
+
+       if (!check_mbox_seq_id_and_seg_len(recv_mbox, seq_id, seg_len)) {
+               dev_err(&func_to_func->hwif->pdev->dev,
+                       "Mailbox sequence and segment check fail, src func id: 0x%x, front id: 0x%x, current id: 0x%x, seg len: 0x%x\n",
+                       src_func_idx, recv_mbox->seq_id, seq_id, seg_len);
+               recv_mbox->seq_id = SEQ_ID_MAX_VAL;
+               return;
+       }
+
+       pos = seq_id * MBOX_SEG_LEN;
+       memcpy((u8 *)recv_mbox->mbox + pos, mbox_body,
+              HINIC_MBOX_HEADER_GET(mbox_header, SEG_LEN));
+
+       if (!HINIC_MBOX_HEADER_GET(mbox_header, LAST))
+               return;
+
+       recv_mbox->cmd = HINIC_MBOX_HEADER_GET(mbox_header, CMD);
+       recv_mbox->mod = HINIC_MBOX_HEADER_GET(mbox_header, MODULE);
+       recv_mbox->mbox_len = HINIC_MBOX_HEADER_GET(mbox_header, MSG_LEN);
+       recv_mbox->ack_type = HINIC_MBOX_HEADER_GET(mbox_header, NO_ACK);
+       recv_mbox->msg_info.msg_id = HINIC_MBOX_HEADER_GET(mbox_header, MSG_ID);
+       recv_mbox->msg_info.status = HINIC_MBOX_HEADER_GET(mbox_header, STATUS);
+       recv_mbox->seq_id = SEQ_ID_MAX_VAL;
+
+       if (HINIC_MBOX_HEADER_GET(mbox_header, DIRECTION) ==
+           HINIC_HWIF_RESPONSE) {
+               resp_mbox_handler(func_to_func, recv_mbox);
+               return;
+       }
+
+       if (atomic_read(&recv_mbox->msg_cnt) > HINIC_MAX_MSG_CNT_TO_PROCESS) {
+               dev_warn(&func_to_func->hwif->pdev->dev,
+                        "This function(%u) have %d message wait to process,can't add to work queue\n",
+                        src_func_idx, atomic_read(&recv_mbox->msg_cnt));
+               return;
+       }
+
+       rcv_mbox_temp = kmemdup(recv_mbox, sizeof(*rcv_mbox_temp), GFP_KERNEL);
+       if (!rcv_mbox_temp)
+               return;
+
+       rcv_mbox_temp->mbox = kmemdup(recv_mbox->mbox, MBOX_MAX_BUF_SZ,
+                                     GFP_KERNEL);
+       if (!rcv_mbox_temp->mbox)
+               goto err_alloc_rcv_mbox_msg;
+
+       rcv_mbox_temp->buf_out = kzalloc(MBOX_MAX_BUF_SZ, GFP_KERNEL);
+       if (!rcv_mbox_temp->buf_out)
+               goto err_alloc_rcv_mbox_buf;
+
+       mbox_work = kzalloc(sizeof(*mbox_work), GFP_KERNEL);
+       if (!mbox_work)
+               goto err_alloc_mbox_work;
+
+       mbox_work->func_to_func = func_to_func;
+       mbox_work->recv_mbox = rcv_mbox_temp;
+       mbox_work->src_func_idx = src_func_idx;
+
+       atomic_inc(&recv_mbox->msg_cnt);
+       INIT_WORK(&mbox_work->work, recv_func_mbox_work_handler);
+       queue_work(func_to_func->workq, &mbox_work->work);
+
+       return;
+
+err_alloc_mbox_work:
+       kfree(rcv_mbox_temp->buf_out);
+
+err_alloc_rcv_mbox_buf:
+       kfree(rcv_mbox_temp->mbox);
+
+err_alloc_rcv_mbox_msg:
+       kfree(rcv_mbox_temp);
+}
+
+void hinic_mbox_func_aeqe_handler(void *handle, void *header, u8 size)
+{
+       struct hinic_mbox_func_to_func *func_to_func;
+       u64 mbox_header = *((u64 *)header);
+       struct hinic_recv_mbox *recv_mbox;
+       u64 src, dir;
+
+       func_to_func = ((struct hinic_hwdev *)handle)->func_to_func;
+
+       dir = HINIC_MBOX_HEADER_GET(mbox_header, DIRECTION);
+       src = HINIC_MBOX_HEADER_GET(mbox_header, SRC_GLB_FUNC_IDX);
+
+       if (src >= HINIC_MAX_FUNCTIONS) {
+               dev_err(&func_to_func->hwif->pdev->dev,
+                       "Mailbox source function id:%u is invalid\n", (u32)src);
+               return;
+       }
+
+       recv_mbox = (dir == HINIC_HWIF_DIRECT_SEND) ?
+                   &func_to_func->mbox_send[src] :
+                   &func_to_func->mbox_resp[src];
+
+       recv_mbox_handler(func_to_func, (u64 *)header, recv_mbox);
+}
+
+void hinic_mbox_self_aeqe_handler(void *handle, void *header, u8 size)
+{
+       struct hinic_mbox_func_to_func *func_to_func;
+       struct hinic_send_mbox *send_mbox;
+
+       func_to_func = ((struct hinic_hwdev *)handle)->func_to_func;
+       send_mbox = &func_to_func->send_mbox;
+
+       complete(&send_mbox->send_done);
+}
+
+static void clear_mbox_status(struct hinic_send_mbox *mbox)
+{
+       *mbox->wb_status = 0;
+
+       /* clear mailbox write back status */
+       wmb();
+}
+
+static void mbox_copy_header(struct hinic_hwdev *hwdev,
+                            struct hinic_send_mbox *mbox, u64 *header)
+{
+       u32 i, idx_max = MBOX_HEADER_SZ / sizeof(u32);
+       u32 *data = (u32 *)header;
+
+       for (i = 0; i < idx_max; i++)
+               __raw_writel(*(data + i), mbox->data + i * sizeof(u32));
+}
+
+static void mbox_copy_send_data(struct hinic_hwdev *hwdev,
+                               struct hinic_send_mbox *mbox, void *seg,
+                               u16 seg_len)
+{
+       u8 mbox_max_buf[MBOX_SEG_LEN] = {0};
+       u32 data_len, chk_sz = sizeof(u32);
+       u32 *data = seg;
+       u32 i, idx_max;
+
+       /* The mbox message should be aligned in 4 bytes. */
+       if (seg_len % chk_sz) {
+               memcpy(mbox_max_buf, seg, seg_len);
+               data = (u32 *)mbox_max_buf;
+       }
+
+       data_len = seg_len;
+       idx_max = ALIGN(data_len, chk_sz) / chk_sz;
+
+       for (i = 0; i < idx_max; i++)
+               __raw_writel(*(data + i),
+                            mbox->data + MBOX_HEADER_SZ + i * sizeof(u32));
+}
+
+static void write_mbox_msg_attr(struct hinic_mbox_func_to_func *func_to_func,
+                               u16 dst_func, u16 dst_aeqn, u16 seg_len,
+                               int poll)
+{
+       u16 rsp_aeq = (dst_aeqn == 0) ? 0 : HINIC_MBOX_RSP_AEQN;
+       u32 mbox_int, mbox_ctrl;
+
+       mbox_int = HINIC_MBOX_INT_SET(dst_func, DST_FUNC) |
+                  HINIC_MBOX_INT_SET(dst_aeqn, DST_AEQN) |
+                  HINIC_MBOX_INT_SET(rsp_aeq, SRC_RESP_AEQN) |
+                  HINIC_MBOX_INT_SET(NO_DMA_ATTRIBUTE_VAL, STAT_DMA) |
+                  HINIC_MBOX_INT_SET(ALIGN(MBOX_SEG_LEN + MBOX_HEADER_SZ +
+                                     MBOX_INFO_SZ, MBOX_SEG_LEN_ALIGN) >> 2,
+                                     TX_SIZE) |
+                  HINIC_MBOX_INT_SET(STRONG_ORDER, STAT_DMA_SO_RO) |
+                  HINIC_MBOX_INT_SET(WRITE_BACK, WB_EN);
+
+       hinic_hwif_write_reg(func_to_func->hwif,
+                            HINIC_FUNC_CSR_MAILBOX_INT_OFFSET_OFF, mbox_int);
+
+       wmb(); /* writing the mbox int attributes */
+       mbox_ctrl = HINIC_MBOX_CTRL_SET(TX_NOT_DONE, TX_STATUS);
+
+       if (poll)
+               mbox_ctrl |= HINIC_MBOX_CTRL_SET(NOT_TRIGGER, TRIGGER_AEQE);
+       else
+               mbox_ctrl |= HINIC_MBOX_CTRL_SET(TRIGGER, TRIGGER_AEQE);
+
+       hinic_hwif_write_reg(func_to_func->hwif,
+                            HINIC_FUNC_CSR_MAILBOX_CONTROL_OFF, mbox_ctrl);
+}
+
+static void dump_mox_reg(struct hinic_hwdev *hwdev)
+{
+       u32 val;
+
+       val = hinic_hwif_read_reg(hwdev->hwif,
+                                 HINIC_FUNC_CSR_MAILBOX_CONTROL_OFF);
+       dev_err(&hwdev->hwif->pdev->dev, "Mailbox control reg: 0x%x\n", val);
+
+       val = hinic_hwif_read_reg(hwdev->hwif,
+                                 HINIC_FUNC_CSR_MAILBOX_INT_OFFSET_OFF);
+       dev_err(&hwdev->hwif->pdev->dev, "Mailbox interrupt offset: 0x%x\n",
+               val);
+}
+
+static u16 get_mbox_status(struct hinic_send_mbox *mbox)
+{
+       /* write back is 16B, but only use first 4B */
+       u64 wb_val = be64_to_cpu(*mbox->wb_status);
+
+       rmb(); /* verify reading before check */
+
+       return (u16)(wb_val & MBOX_WB_STATUS_ERRCODE_MASK);
+}
+
+static int
+wait_for_mbox_seg_completion(struct hinic_mbox_func_to_func *func_to_func,
+                            int poll, u16 *wb_status)
+{
+       struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox;
+       struct hinic_hwdev *hwdev = func_to_func->hwdev;
+       struct completion *done = &send_mbox->send_done;
+       u32 cnt = 0;
+       ulong jif;
+
+       if (poll) {
+               while (cnt < MBOX_MSG_POLLING_TIMEOUT) {
+                       *wb_status = get_mbox_status(send_mbox);
+                       if (MBOX_STATUS_FINISHED(*wb_status))
+                               break;
+
+                       usleep_range(900, 1000);
+                       cnt++;
+               }
+
+               if (cnt == MBOX_MSG_POLLING_TIMEOUT) {
+                       dev_err(&hwdev->hwif->pdev->dev, "Send mailbox segment timeout, wb status: 0x%x\n",
+                               *wb_status);
+                       dump_mox_reg(hwdev);
+                       return -ETIMEDOUT;
+               }
+       } else {
+               jif = msecs_to_jiffies(HINIC_MBOX_COMP_TIME);
+               if (!wait_for_completion_timeout(done, jif)) {
+                       dev_err(&hwdev->hwif->pdev->dev, "Send mailbox segment timeout\n");
+                       dump_mox_reg(hwdev);
+                       return -ETIMEDOUT;
+               }
+
+               *wb_status = get_mbox_status(send_mbox);
+       }
+
+       return 0;
+}
+
+static int send_mbox_seg(struct hinic_mbox_func_to_func *func_to_func,
+                        u64 header, u16 dst_func, void *seg, u16 seg_len,
+                        int poll, void *msg_info)
+{
+       struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox;
+       u16 seq_dir = HINIC_MBOX_HEADER_GET(header, DIRECTION);
+       struct hinic_hwdev *hwdev = func_to_func->hwdev;
+       struct completion *done = &send_mbox->send_done;
+       u8 num_aeqs = hwdev->hwif->attr.num_aeqs;
+       u16 dst_aeqn, wb_status = 0, errcode;
+
+       if (num_aeqs >= 4)
+               dst_aeqn = (seq_dir == HINIC_HWIF_DIRECT_SEND) ?
+                          HINIC_MBOX_RECV_AEQN : HINIC_MBOX_RSP_AEQN;
+       else
+               dst_aeqn = 0;
+
+       if (!poll)
+               init_completion(done);
+
+       clear_mbox_status(send_mbox);
+
+       mbox_copy_header(hwdev, send_mbox, &header);
+
+       mbox_copy_send_data(hwdev, send_mbox, seg, seg_len);
+
+       write_mbox_msg_attr(func_to_func, dst_func, dst_aeqn, seg_len, poll);
+
+       wmb(); /* writing the mbox msg attributes */
+
+       if (wait_for_mbox_seg_completion(func_to_func, poll, &wb_status))
+               return -ETIMEDOUT;
+
+       if (!MBOX_STATUS_SUCCESS(wb_status)) {
+               dev_err(&hwdev->hwif->pdev->dev, "Send mailbox segment to function %d error, wb status: 0x%x\n",
+                       dst_func, wb_status);
+               errcode = MBOX_STATUS_ERRCODE(wb_status);
+               return errcode ? errcode : -EFAULT;
+       }
+
+       return 0;
+}
+
+static int send_mbox_to_func(struct hinic_mbox_func_to_func *func_to_func,
+                            enum hinic_mod_type mod, u16 cmd, void *msg,
+                            u16 msg_len, u16 dst_func,
+                            enum hinic_hwif_direction_type direction,
+                            enum hinic_mbox_ack_type ack_type,
+                            struct mbox_msg_info *msg_info)
+{
+       struct hinic_hwdev *hwdev = func_to_func->hwdev;
+       u16 seg_len = MBOX_SEG_LEN;
+       u8 *msg_seg = (u8 *)msg;
+       u16 left = msg_len;
+       u32 seq_id = 0;
+       u64 header = 0;
+       int err = 0;
+
+       down(&func_to_func->msg_send_sem);
+
+       header = HINIC_MBOX_HEADER_SET(msg_len, MSG_LEN) |
+                HINIC_MBOX_HEADER_SET(mod, MODULE) |
+                HINIC_MBOX_HEADER_SET(seg_len, SEG_LEN) |
+                HINIC_MBOX_HEADER_SET(ack_type, NO_ACK) |
+                HINIC_MBOX_HEADER_SET(SEQ_ID_START_VAL, SEQID) |
+                HINIC_MBOX_HEADER_SET(NOT_LAST_SEG, LAST) |
+                HINIC_MBOX_HEADER_SET(direction, DIRECTION) |
+                HINIC_MBOX_HEADER_SET(cmd, CMD) |
+                /* The vf's offset to it's associated pf */
+                HINIC_MBOX_HEADER_SET(msg_info->msg_id, MSG_ID) |
+                HINIC_MBOX_HEADER_SET(msg_info->status, STATUS) |
+                HINIC_MBOX_HEADER_SET(hinic_global_func_id_hw(hwdev->hwif),
+                                      SRC_GLB_FUNC_IDX);
+
+       while (!(HINIC_MBOX_HEADER_GET(header, LAST))) {
+               if (left <= HINIC_MBOX_SEG_LEN) {
+                       header &= ~MBOX_SEGLEN_MASK;
+                       header |= HINIC_MBOX_HEADER_SET(left, SEG_LEN);
+                       header |= HINIC_MBOX_HEADER_SET(LAST_SEG, LAST);
+
+                       seg_len = left;
+               }
+
+               err = send_mbox_seg(func_to_func, header, dst_func, msg_seg,
+                                   seg_len, MBOX_SEND_MSG_INT, msg_info);
+               if (err) {
+                       dev_err(&hwdev->hwif->pdev->dev, "Failed to send mbox seg, seq_id=0x%llx\n",
+                               HINIC_MBOX_HEADER_GET(header, SEQID));
+                       goto err_send_mbox_seg;
+               }
+
+               left -= HINIC_MBOX_SEG_LEN;
+               msg_seg += HINIC_MBOX_SEG_LEN;
+
+               seq_id++;
+               header &= ~(HINIC_MBOX_HEADER_SET(HINIC_MBOX_HEADER_SEQID_MASK,
+                                                 SEQID));
+               header |= HINIC_MBOX_HEADER_SET(seq_id, SEQID);
+       }
+
+err_send_mbox_seg:
+       up(&func_to_func->msg_send_sem);
+
+       return err;
+}
+
+static void
+response_for_recv_func_mbox(struct hinic_mbox_func_to_func *func_to_func,
+                           struct hinic_recv_mbox *recv_mbox, int err,
+                           u16 out_size, u16 src_func_idx)
+{
+       struct mbox_msg_info msg_info = {0};
+
+       if (recv_mbox->ack_type == MBOX_ACK) {
+               msg_info.msg_id = recv_mbox->msg_info.msg_id;
+               if (err == HINIC_MBOX_PF_BUSY_ACTIVE_FW)
+                       msg_info.status = HINIC_MBOX_PF_BUSY_ACTIVE_FW;
+               else if (err == HINIC_MBOX_VF_CMD_ERROR)
+                       msg_info.status = HINIC_MBOX_VF_CMD_ERROR;
+               else if (err)
+                       msg_info.status = HINIC_MBOX_PF_SEND_ERR;
+
+               /* if no data needs to response, set out_size to 1 */
+               if (!out_size || err)
+                       out_size = MBOX_MSG_NO_DATA_LEN;
+
+               send_mbox_to_func(func_to_func, recv_mbox->mod, recv_mbox->cmd,
+                                 recv_mbox->buf_out, out_size, src_func_idx,
+                                 HINIC_HWIF_RESPONSE, MBOX_ACK,
+                                 &msg_info);
+       }
+}
+
+static void recv_func_mbox_handler(struct hinic_mbox_func_to_func *func_to_func,
+                                  struct hinic_recv_mbox *recv_mbox,
+                                  u16 src_func_idx)
+{
+       void *buf_out = recv_mbox->buf_out;
+       u16 out_size = MBOX_MAX_BUF_SZ;
+       int err = 0;
+
+       if (HINIC_IS_VF(func_to_func->hwif)) {
+               err = recv_vf_mbox_handler(func_to_func, recv_mbox, buf_out,
+                                          &out_size);
+       } else {
+               if (IS_PF_OR_PPF_SRC(src_func_idx))
+                       dev_warn(&func_to_func->hwif->pdev->dev,
+                                "Unsupported pf2pf mbox msg\n");
+               else
+                       err = recv_pf_from_vf_mbox_handler(func_to_func,
+                                                          recv_mbox,
+                                                          src_func_idx,
+                                                          buf_out, &out_size);
+       }
+
+       response_for_recv_func_mbox(func_to_func, recv_mbox, err, out_size,
+                                   src_func_idx);
+       kfree(recv_mbox->buf_out);
+       kfree(recv_mbox->mbox);
+       kfree(recv_mbox);
+}
+
+static void set_mbox_to_func_event(struct hinic_mbox_func_to_func *func_to_func,
+                                  enum mbox_event_state event_flag)
+{
+       spin_lock(&func_to_func->mbox_lock);
+       func_to_func->event_flag = event_flag;
+       spin_unlock(&func_to_func->mbox_lock);
+}
+
+static int mbox_resp_info_handler(struct hinic_mbox_func_to_func *func_to_func,
+                                 struct hinic_recv_mbox *mbox_for_resp,
+                                 enum hinic_mod_type mod, u16 cmd,
+                                 void *buf_out, u16 *out_size)
+{
+       int err;
+
+       if (mbox_for_resp->msg_info.status) {
+               err = mbox_for_resp->msg_info.status;
+               if (err != HINIC_MBOX_PF_BUSY_ACTIVE_FW)
+                       dev_err(&func_to_func->hwif->pdev->dev, "Mbox response error(0x%x)\n",
+                               mbox_for_resp->msg_info.status);
+               return err;
+       }
+
+       if (buf_out && out_size) {
+               if (*out_size < mbox_for_resp->mbox_len) {
+                       dev_err(&func_to_func->hwif->pdev->dev,
+                               "Invalid response mbox message length: %d for mod %d cmd %d, should less than: %d\n",
+                               mbox_for_resp->mbox_len, mod, cmd, *out_size);
+                       return -EFAULT;
+               }
+
+               if (mbox_for_resp->mbox_len)
+                       memcpy(buf_out, mbox_for_resp->mbox,
+                              mbox_for_resp->mbox_len);
+
+               *out_size = mbox_for_resp->mbox_len;
+       }
+
+       return 0;
+}
+
+int hinic_mbox_to_func(struct hinic_mbox_func_to_func *func_to_func,
+                      enum hinic_mod_type mod, u16 cmd, u16 dst_func,
+                      void *buf_in, u16 in_size, void *buf_out,
+                      u16 *out_size, u32 timeout)
+{
+       struct hinic_recv_mbox *mbox_for_resp;
+       struct mbox_msg_info msg_info = {0};
+       ulong timeo;
+       int err;
+
+       mbox_for_resp = &func_to_func->mbox_resp[dst_func];
+
+       down(&func_to_func->mbox_send_sem);
+
+       init_completion(&mbox_for_resp->recv_done);
+
+       msg_info.msg_id = MBOX_MSG_ID_INC(func_to_func);
+
+       set_mbox_to_func_event(func_to_func, EVENT_START);
+
+       err = send_mbox_to_func(func_to_func, mod, cmd, buf_in, in_size,
+                               dst_func, HINIC_HWIF_DIRECT_SEND, MBOX_ACK,
+                               &msg_info);
+       if (err) {
+               dev_err(&func_to_func->hwif->pdev->dev, "Send mailbox failed, msg_id: %d\n",
+                       msg_info.msg_id);
+               set_mbox_to_func_event(func_to_func, EVENT_FAIL);
+               goto err_send_mbox;
+       }
+
+       timeo = msecs_to_jiffies(timeout ? timeout : HINIC_MBOX_COMP_TIME);
+       if (!wait_for_completion_timeout(&mbox_for_resp->recv_done, timeo)) {
+               set_mbox_to_func_event(func_to_func, EVENT_TIMEOUT);
+               dev_err(&func_to_func->hwif->pdev->dev,
+                       "Send mbox msg timeout, msg_id: %d\n", msg_info.msg_id);
+               err = -ETIMEDOUT;
+               goto err_send_mbox;
+       }
+
+       set_mbox_to_func_event(func_to_func, EVENT_END);
+
+       err = mbox_resp_info_handler(func_to_func, mbox_for_resp, mod, cmd,
+                                    buf_out, out_size);
+
+err_send_mbox:
+       up(&func_to_func->mbox_send_sem);
+
+       return err;
+}
+
+static int mbox_func_params_valid(struct hinic_mbox_func_to_func *func_to_func,
+                                 void *buf_in, u16 in_size)
+{
+       if (in_size > HINIC_MBOX_DATA_SIZE) {
+               dev_err(&func_to_func->hwif->pdev->dev,
+                       "Mbox msg len(%d) exceed limit(%d)\n",
+                       in_size, HINIC_MBOX_DATA_SIZE);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int hinic_mbox_to_pf(struct hinic_hwdev *hwdev,
+                    enum hinic_mod_type mod, u8 cmd, void *buf_in,
+                    u16 in_size, void *buf_out, u16 *out_size, u32 timeout)
+{
+       struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func;
+       int err = mbox_func_params_valid(func_to_func, buf_in, in_size);
+
+       if (err)
+               return err;
+
+       if (!HINIC_IS_VF(hwdev->hwif)) {
+               dev_err(&hwdev->hwif->pdev->dev, "Params error, func_type: %d\n",
+                       HINIC_FUNC_TYPE(hwdev->hwif));
+               return -EINVAL;
+       }
+
+       return hinic_mbox_to_func(func_to_func, mod, cmd,
+                                 hinic_pf_id_of_vf_hw(hwdev->hwif), buf_in,
+                                 in_size, buf_out, out_size, timeout);
+}
+
+int hinic_mbox_to_vf(struct hinic_hwdev *hwdev,
+                    enum hinic_mod_type mod, u16 vf_id, u8 cmd, void *buf_in,
+                    u16 in_size, void *buf_out, u16 *out_size, u32 timeout)
+{
+       struct hinic_mbox_func_to_func *func_to_func;
+       u16 dst_func_idx;
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       func_to_func = hwdev->func_to_func;
+       err = mbox_func_params_valid(func_to_func, buf_in, in_size);
+       if (err)
+               return err;
+
+       if (HINIC_IS_VF(hwdev->hwif)) {
+               dev_err(&hwdev->hwif->pdev->dev, "Params error, func_type: %d\n",
+                       HINIC_FUNC_TYPE(hwdev->hwif));
+               return -EINVAL;
+       }
+
+       if (!vf_id) {
+               dev_err(&hwdev->hwif->pdev->dev,
+                       "VF id(%d) error!\n", vf_id);
+               return -EINVAL;
+       }
+
+       /* vf_offset_to_pf + vf_id is the vf's global function id of vf in
+        * this pf
+        */
+       dst_func_idx = hinic_glb_pf_vf_offset(hwdev->hwif) + vf_id;
+
+       return hinic_mbox_to_func(func_to_func, mod, cmd, dst_func_idx, buf_in,
+                                 in_size, buf_out, out_size, timeout);
+}
+
+static int init_mbox_info(struct hinic_recv_mbox *mbox_info)
+{
+       int err;
+
+       mbox_info->seq_id = SEQ_ID_MAX_VAL;
+
+       mbox_info->mbox = kzalloc(MBOX_MAX_BUF_SZ, GFP_KERNEL);
+       if (!mbox_info->mbox)
+               return -ENOMEM;
+
+       mbox_info->buf_out = kzalloc(MBOX_MAX_BUF_SZ, GFP_KERNEL);
+       if (!mbox_info->buf_out) {
+               err = -ENOMEM;
+               goto err_alloc_buf_out;
+       }
+
+       atomic_set(&mbox_info->msg_cnt, 0);
+
+       return 0;
+
+err_alloc_buf_out:
+       kfree(mbox_info->mbox);
+
+       return err;
+}
+
+static void clean_mbox_info(struct hinic_recv_mbox *mbox_info)
+{
+       kfree(mbox_info->buf_out);
+       kfree(mbox_info->mbox);
+}
+
+static int alloc_mbox_info(struct hinic_hwdev *hwdev,
+                          struct hinic_recv_mbox *mbox_info)
+{
+       u16 func_idx, i;
+       int err;
+
+       for (func_idx = 0; func_idx < HINIC_MAX_FUNCTIONS; func_idx++) {
+               err = init_mbox_info(&mbox_info[func_idx]);
+               if (err) {
+                       dev_err(&hwdev->hwif->pdev->dev, "Failed to init function %d mbox info\n",
+                               func_idx);
+                       goto err_init_mbox_info;
+               }
+       }
+
+       return 0;
+
+err_init_mbox_info:
+       for (i = 0; i < func_idx; i++)
+               clean_mbox_info(&mbox_info[i]);
+
+       return err;
+}
+
+static void free_mbox_info(struct hinic_recv_mbox *mbox_info)
+{
+       u16 func_idx;
+
+       for (func_idx = 0; func_idx < HINIC_MAX_FUNCTIONS; func_idx++)
+               clean_mbox_info(&mbox_info[func_idx]);
+}
+
+static void prepare_send_mbox(struct hinic_mbox_func_to_func *func_to_func)
+{
+       struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox;
+
+       send_mbox->data = MBOX_AREA(func_to_func->hwif);
+}
+
+static int alloc_mbox_wb_status(struct hinic_mbox_func_to_func *func_to_func)
+{
+       struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox;
+       struct hinic_hwdev *hwdev = func_to_func->hwdev;
+       u32 addr_h, addr_l;
+
+       send_mbox->wb_vaddr = dma_alloc_coherent(&hwdev->hwif->pdev->dev,
+                                                MBOX_WB_STATUS_LEN,
+                                                &send_mbox->wb_paddr,
+                                                GFP_KERNEL);
+       if (!send_mbox->wb_vaddr)
+               return -ENOMEM;
+
+       send_mbox->wb_status = send_mbox->wb_vaddr;
+
+       addr_h = upper_32_bits(send_mbox->wb_paddr);
+       addr_l = lower_32_bits(send_mbox->wb_paddr);
+
+       hinic_hwif_write_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_RESULT_H_OFF,
+                            addr_h);
+       hinic_hwif_write_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_RESULT_L_OFF,
+                            addr_l);
+
+       return 0;
+}
+
+static void free_mbox_wb_status(struct hinic_mbox_func_to_func *func_to_func)
+{
+       struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox;
+       struct hinic_hwdev *hwdev = func_to_func->hwdev;
+
+       hinic_hwif_write_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_RESULT_H_OFF,
+                            0);
+       hinic_hwif_write_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_RESULT_L_OFF,
+                            0);
+
+       dma_free_coherent(&hwdev->hwif->pdev->dev, MBOX_WB_STATUS_LEN,
+                         send_mbox->wb_vaddr,
+                         send_mbox->wb_paddr);
+}
+
+static int comm_pf_mbox_handler(void *handle, u16 vf_id, u8 cmd, void *buf_in,
+                               u16 in_size, void *buf_out, u16 *out_size)
+{
+       struct hinic_hwdev *hwdev = handle;
+       struct hinic_pfhwdev *pfhwdev;
+       int err = 0;
+
+       pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
+
+       if (cmd == HINIC_COMM_CMD_START_FLR) {
+               *out_size = 0;
+       } else {
+               err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
+                                       cmd, buf_in, in_size, buf_out, out_size,
+                                       HINIC_MGMT_MSG_SYNC);
+               if (err && err != HINIC_MBOX_PF_BUSY_ACTIVE_FW)
+                       dev_err(&hwdev->hwif->pdev->dev,
+                               "PF mbox common callback handler err: %d\n",
+                               err);
+       }
+
+       return err;
+}
+
+int hinic_func_to_func_init(struct hinic_hwdev *hwdev)
+{
+       struct hinic_mbox_func_to_func *func_to_func;
+       struct hinic_pfhwdev *pfhwdev;
+       int err;
+
+       pfhwdev =  container_of(hwdev, struct hinic_pfhwdev, hwdev);
+       func_to_func = kzalloc(sizeof(*func_to_func), GFP_KERNEL);
+       if (!func_to_func)
+               return -ENOMEM;
+
+       hwdev->func_to_func = func_to_func;
+       func_to_func->hwdev = hwdev;
+       func_to_func->hwif = hwdev->hwif;
+       sema_init(&func_to_func->mbox_send_sem, 1);
+       sema_init(&func_to_func->msg_send_sem, 1);
+       spin_lock_init(&func_to_func->mbox_lock);
+       func_to_func->workq = create_singlethread_workqueue(HINIC_MBOX_WQ_NAME);
+       if (!func_to_func->workq) {
+               dev_err(&hwdev->hwif->pdev->dev, "Failed to initialize MBOX workqueue\n");
+               err = -ENOMEM;
+               goto err_create_mbox_workq;
+       }
+
+       err = alloc_mbox_info(hwdev, func_to_func->mbox_send);
+       if (err) {
+               dev_err(&hwdev->hwif->pdev->dev, "Failed to alloc mem for mbox_active\n");
+               goto err_alloc_mbox_for_send;
+       }
+
+       err = alloc_mbox_info(hwdev, func_to_func->mbox_resp);
+       if (err) {
+               dev_err(&hwdev->hwif->pdev->dev, "Failed to alloc mem for mbox_passive\n");
+               goto err_alloc_mbox_for_resp;
+       }
+
+       err = alloc_mbox_wb_status(func_to_func);
+       if (err) {
+               dev_err(&hwdev->hwif->pdev->dev, "Failed to alloc mbox write back status\n");
+               goto err_alloc_wb_status;
+       }
+
+       prepare_send_mbox(func_to_func);
+
+       hinic_aeq_register_hw_cb(&hwdev->aeqs, HINIC_MBX_FROM_FUNC,
+                                &pfhwdev->hwdev, hinic_mbox_func_aeqe_handler);
+       hinic_aeq_register_hw_cb(&hwdev->aeqs, HINIC_MBX_SEND_RSLT,
+                                &pfhwdev->hwdev, hinic_mbox_self_aeqe_handler);
+
+       if (!HINIC_IS_VF(hwdev->hwif))
+               hinic_register_pf_mbox_cb(hwdev, HINIC_MOD_COMM,
+                                         comm_pf_mbox_handler);
+
+       return 0;
+
+err_alloc_wb_status:
+       free_mbox_info(func_to_func->mbox_resp);
+
+err_alloc_mbox_for_resp:
+       free_mbox_info(func_to_func->mbox_send);
+
+err_alloc_mbox_for_send:
+       destroy_workqueue(func_to_func->workq);
+
+err_create_mbox_workq:
+       kfree(func_to_func);
+
+       return err;
+}
+
+void hinic_func_to_func_free(struct hinic_hwdev *hwdev)
+{
+       struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func;
+
+       hinic_aeq_unregister_hw_cb(&hwdev->aeqs, HINIC_MBX_FROM_FUNC);
+       hinic_aeq_unregister_hw_cb(&hwdev->aeqs, HINIC_MBX_SEND_RSLT);
+
+       hinic_unregister_pf_mbox_cb(hwdev, HINIC_MOD_COMM);
+       /* destroy workqueue before free related mbox resources in case of
+        * illegal resource access
+        */
+       destroy_workqueue(func_to_func->workq);
+
+       free_mbox_wb_status(func_to_func);
+       free_mbox_info(func_to_func->mbox_resp);
+       free_mbox_info(func_to_func->mbox_send);
+
+       kfree(func_to_func);
+}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.h
new file mode 100644 (file)
index 0000000..7b18559
--- /dev/null
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Huawei HiNIC PCI Express Linux driver
+ * Copyright(c) 2017 Huawei Technologies Co., Ltd
+ */
+
+#ifndef HINIC_MBOX_H_
+#define HINIC_MBOX_H_
+
+#define HINIC_MBOX_PF_SEND_ERR         0x1
+#define HINIC_MBOX_PF_BUSY_ACTIVE_FW   0x2
+#define HINIC_MBOX_VF_CMD_ERROR                0x3
+
+#define HINIC_MAX_FUNCTIONS            512
+
+#define HINIC_MAX_PF_FUNCS             16
+
+#define HINIC_MBOX_WQ_NAME             "hinic_mbox"
+
+#define HINIC_FUNC_CSR_MAILBOX_DATA_OFF                        0x80
+#define HINIC_FUNC_CSR_MAILBOX_CONTROL_OFF             0x0100
+#define HINIC_FUNC_CSR_MAILBOX_INT_OFFSET_OFF          0x0104
+#define HINIC_FUNC_CSR_MAILBOX_RESULT_H_OFF            0x0108
+#define HINIC_FUNC_CSR_MAILBOX_RESULT_L_OFF            0x010C
+
+enum hinic_mbox_ack_type {
+       MBOX_ACK,
+       MBOX_NO_ACK,
+};
+
+struct mbox_msg_info {
+       u8 msg_id;
+       u8 status;
+};
+
+struct hinic_recv_mbox {
+       struct completion       recv_done;
+       void                    *mbox;
+       u8                      cmd;
+       enum hinic_mod_type     mod;
+       u16                     mbox_len;
+       void                    *buf_out;
+       enum hinic_mbox_ack_type ack_type;
+       struct mbox_msg_info    msg_info;
+       u8                      seq_id;
+       atomic_t                msg_cnt;
+};
+
+struct hinic_send_mbox {
+       struct completion       send_done;
+       u8                      *data;
+
+       u64                     *wb_status;
+       void                    *wb_vaddr;
+       dma_addr_t              wb_paddr;
+};
+
+typedef void (*hinic_vf_mbox_cb)(void *handle, u8 cmd, void *buf_in,
+                               u16 in_size, void *buf_out, u16 *out_size);
+typedef int (*hinic_pf_mbox_cb)(void *handle, u16 vf_id, u8 cmd, void *buf_in,
+                               u16 in_size, void *buf_out, u16 *out_size);
+
+enum mbox_event_state {
+       EVENT_START = 0,
+       EVENT_FAIL,
+       EVENT_TIMEOUT,
+       EVENT_END,
+};
+
+enum hinic_mbox_cb_state {
+       HINIC_VF_MBOX_CB_REG = 0,
+       HINIC_VF_MBOX_CB_RUNNING,
+       HINIC_PF_MBOX_CB_REG,
+       HINIC_PF_MBOX_CB_RUNNING,
+       HINIC_PPF_MBOX_CB_REG,
+       HINIC_PPF_MBOX_CB_RUNNING,
+       HINIC_PPF_TO_PF_MBOX_CB_REG,
+       HINIC_PPF_TO_PF_MBOX_CB_RUNNIG,
+};
+
+struct hinic_mbox_func_to_func {
+       struct hinic_hwdev      *hwdev;
+       struct hinic_hwif               *hwif;
+
+       struct semaphore        mbox_send_sem;
+       struct semaphore        msg_send_sem;
+       struct hinic_send_mbox  send_mbox;
+
+       struct workqueue_struct *workq;
+
+       struct hinic_recv_mbox  mbox_resp[HINIC_MAX_FUNCTIONS];
+       struct hinic_recv_mbox  mbox_send[HINIC_MAX_FUNCTIONS];
+
+       hinic_vf_mbox_cb        vf_mbox_cb[HINIC_MOD_MAX];
+       hinic_pf_mbox_cb        pf_mbox_cb[HINIC_MOD_MAX];
+       unsigned long           pf_mbox_cb_state[HINIC_MOD_MAX];
+       unsigned long           vf_mbox_cb_state[HINIC_MOD_MAX];
+
+       u8 send_msg_id;
+       enum mbox_event_state event_flag;
+
+       /* lock for mbox event flag */
+       spinlock_t mbox_lock;
+};
+
+struct hinic_mbox_work {
+       struct work_struct work;
+       u16 src_func_idx;
+       struct hinic_mbox_func_to_func *func_to_func;
+       struct hinic_recv_mbox *recv_mbox;
+};
+
+struct vf_cmd_msg_handle {
+       u8 cmd;
+       int (*cmd_msg_handler)(void *hwdev, u16 vf_id,
+                              void *buf_in, u16 in_size,
+                              void *buf_out, u16 *out_size);
+};
+
+int hinic_register_pf_mbox_cb(struct hinic_hwdev *hwdev,
+                             enum hinic_mod_type mod,
+                             hinic_pf_mbox_cb callback);
+
+int hinic_register_vf_mbox_cb(struct hinic_hwdev *hwdev,
+                             enum hinic_mod_type mod,
+                             hinic_vf_mbox_cb callback);
+
+void hinic_unregister_pf_mbox_cb(struct hinic_hwdev *hwdev,
+                                enum hinic_mod_type mod);
+
+void hinic_unregister_vf_mbox_cb(struct hinic_hwdev *hwdev,
+                                enum hinic_mod_type mod);
+
+void hinic_mbox_func_aeqe_handler(void *handle, void *header, u8 size);
+
+void hinic_mbox_self_aeqe_handler(void *handle, void *header, u8 size);
+
+int hinic_func_to_func_init(struct hinic_hwdev *hwdev);
+
+void hinic_func_to_func_free(struct hinic_hwdev *hwdev);
+
+int hinic_mbox_to_pf(struct hinic_hwdev *hwdev, enum hinic_mod_type mod,
+                    u8 cmd, void *buf_in, u16 in_size, void *buf_out,
+                    u16 *out_size, u32 timeout);
+
+int hinic_mbox_to_func(struct hinic_mbox_func_to_func *func_to_func,
+                      enum hinic_mod_type mod, u16 cmd, u16 dst_func,
+                      void *buf_in, u16 in_size, void *buf_out,
+                      u16 *out_size, u32 timeout);
+
+int hinic_mbox_to_vf(struct hinic_hwdev *hwdev,
+                    enum hinic_mod_type mod, u16 vf_id, u8 cmd, void *buf_in,
+                    u16 in_size, void *buf_out, u16 *out_size, u32 timeout);
+
+#endif
index 8995e32..eef855f 100644 (file)
@@ -353,7 +353,11 @@ int hinic_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt,
                return -EINVAL;
        }
 
-       return msg_to_mgmt_sync(pf_to_mgmt, mod, cmd, buf_in, in_size,
+       if (HINIC_IS_VF(hwif))
+               return hinic_mbox_to_pf(pf_to_mgmt->hwdev, mod, cmd, buf_in,
+                                       in_size, buf_out, out_size, 0);
+       else
+               return msg_to_mgmt_sync(pf_to_mgmt, mod, cmd, buf_in, in_size,
                                buf_out, out_size, MGMT_DIRECT_SEND,
                                MSG_NOT_RESP);
 }
@@ -390,8 +394,8 @@ static void mgmt_recv_msg_handler(struct hinic_pf_to_mgmt *pf_to_mgmt,
                            recv_msg->msg, recv_msg->msg_len,
                            buf_out, &out_size);
        else
-               dev_err(&pdev->dev, "No MGMT msg handler, mod = %d\n",
-                       recv_msg->mod);
+               dev_err(&pdev->dev, "No MGMT msg handler, mod: %d, cmd: %d\n",
+                       recv_msg->mod, recv_msg->cmd);
 
        mgmt_cb->state &= ~HINIC_MGMT_CB_RUNNING;
 
@@ -553,6 +557,10 @@ int hinic_pf_to_mgmt_init(struct hinic_pf_to_mgmt *pf_to_mgmt,
        int err;
 
        pf_to_mgmt->hwif = hwif;
+       pf_to_mgmt->hwdev = hwdev;
+
+       if (HINIC_IS_VF(hwif))
+               return 0;
 
        sema_init(&pf_to_mgmt->sync_msg_lock, 1);
        pf_to_mgmt->sync_msg_id = 0;
@@ -584,6 +592,9 @@ void hinic_pf_to_mgmt_free(struct hinic_pf_to_mgmt *pf_to_mgmt)
        struct hinic_pfhwdev *pfhwdev = mgmt_to_pfhwdev(pf_to_mgmt);
        struct hinic_hwdev *hwdev = &pfhwdev->hwdev;
 
+       if (HINIC_IS_VF(hwdev->hwif))
+               return;
+
        hinic_aeq_unregister_hw_cb(&hwdev->aeqs, HINIC_MSG_FROM_MGMT_CPU);
        hinic_api_cmd_free(pf_to_mgmt->cmd_chain);
 }
index 182fba1..c2b142c 100644 (file)
@@ -60,7 +60,9 @@ enum hinic_cfg_cmd {
 };
 
 enum hinic_comm_cmd {
+       HINIC_COMM_CMD_START_FLR          = 0x1,
        HINIC_COMM_CMD_IO_STATUS_GET    = 0x3,
+       HINIC_COMM_CMD_DMA_ATTR_SET         = 0x4,
 
        HINIC_COMM_CMD_CMDQ_CTXT_SET    = 0x10,
        HINIC_COMM_CMD_CMDQ_CTXT_GET    = 0x11,
@@ -74,7 +76,13 @@ enum hinic_comm_cmd {
 
        HINIC_COMM_CMD_IO_RES_CLEAR     = 0x29,
 
-       HINIC_COMM_CMD_MAX              = 0x32,
+       HINIC_COMM_CMD_CEQ_CTRL_REG_WR_BY_UP = 0x33,
+
+       HINIC_COMM_CMD_L2NIC_RESET              = 0x4b,
+
+       HINIC_COMM_CMD_PAGESIZE_SET     = 0x50,
+
+       HINIC_COMM_CMD_MAX              = 0x51,
 };
 
 enum hinic_mgmt_cb_state {
@@ -107,7 +115,7 @@ struct hinic_mgmt_cb {
 
 struct hinic_pf_to_mgmt {
        struct hinic_hwif               *hwif;
-
+       struct hinic_hwdev              *hwdev;
        struct semaphore                sync_msg_lock;
        u16                             sync_msg_id;
        u8                              *sync_msg_buf;
index be364b7..20c5c8e 100644 (file)
@@ -108,7 +108,12 @@ void hinic_sq_prepare_ctxt(struct hinic_sq_ctxt *sq_ctxt,
        wq_page_pfn_hi = upper_32_bits(wq_page_pfn);
        wq_page_pfn_lo = lower_32_bits(wq_page_pfn);
 
-       wq_block_pfn = HINIC_WQ_BLOCK_PFN(wq->block_paddr);
+       /* If only one page, use 0-level CLA */
+       if (wq->num_q_pages == 1)
+               wq_block_pfn = HINIC_WQ_BLOCK_PFN(wq_page_addr);
+       else
+               wq_block_pfn = HINIC_WQ_BLOCK_PFN(wq->block_paddr);
+
        wq_block_pfn_hi = upper_32_bits(wq_block_pfn);
        wq_block_pfn_lo = lower_32_bits(wq_block_pfn);
 
index 79091e1..c30d092 100644 (file)
@@ -38,8 +38,8 @@
 #define HINIC_SQ_WQEBB_SIZE                     64
 #define HINIC_RQ_WQEBB_SIZE                     32
 
-#define HINIC_SQ_PAGE_SIZE                      SZ_4K
-#define HINIC_RQ_PAGE_SIZE                      SZ_4K
+#define HINIC_SQ_PAGE_SIZE                      SZ_256K
+#define HINIC_RQ_PAGE_SIZE                      SZ_256K
 
 #define HINIC_SQ_DEPTH                          SZ_4K
 #define HINIC_RQ_DEPTH                          SZ_4K
index 0336321..5dc3743 100644 (file)
@@ -503,7 +503,7 @@ err_alloc_wq_pages:
  * Return 0 - Success, negative - Failure
  **/
 int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq,
-                     u16 wqebb_size, u16 wq_page_size, u16 q_depth,
+                     u16 wqebb_size, u32 wq_page_size, u16 q_depth,
                      u16 max_wqe_size)
 {
        struct hinic_hwif *hwif = wqs->hwif;
@@ -600,7 +600,7 @@ void hinic_wq_free(struct hinic_wqs *wqs, struct hinic_wq *wq)
  **/
 int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
                         struct hinic_wq *wq, struct hinic_hwif *hwif,
-                        int cmdq_blocks, u16 wqebb_size, u16 wq_page_size,
+                        int cmdq_blocks, u16 wqebb_size, u32 wq_page_size,
                         u16 q_depth, u16 max_wqe_size)
 {
        struct pci_dev *pdev = hwif->pdev;
@@ -768,7 +768,10 @@ struct hinic_hw_wqe *hinic_get_wqe(struct hinic_wq *wq, unsigned int wqe_size,
 
        *prod_idx = curr_prod_idx;
 
-       if (curr_pg != end_pg) {
+       /* If we only have one page, still need to get shadown wqe when
+        * wqe rolling-over page
+        */
+       if (curr_pg != end_pg || MASKED_WQE_IDX(wq, end_prod_idx) < *prod_idx) {
                void *shadow_addr = &wq->shadow_wqe[curr_pg * wq->max_wqe_size];
 
                copy_wqe_to_shadow(wq, shadow_addr, num_wqebbs, *prod_idx);
index 811eef7..b06f8c0 100644 (file)
@@ -26,7 +26,7 @@ struct hinic_wq {
        int             block_idx;
 
        u16             wqebb_size;
-       u16             wq_page_size;
+       u32             wq_page_size;
        u16             q_depth;
        u16             max_wqe_size;
        u16             num_wqebbs_per_page;
@@ -76,7 +76,7 @@ struct hinic_cmdq_pages {
 
 int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages,
                         struct hinic_wq *wq, struct hinic_hwif *hwif,
-                        int cmdq_blocks, u16 wqebb_size, u16 wq_page_size,
+                        int cmdq_blocks, u16 wqebb_size, u32 wq_page_size,
                         u16 q_depth, u16 max_wqe_size);
 
 void hinic_wqs_cmdq_free(struct hinic_cmdq_pages *cmdq_pages,
@@ -88,7 +88,7 @@ int hinic_wqs_alloc(struct hinic_wqs *wqs, int num_wqs,
 void hinic_wqs_free(struct hinic_wqs *wqs);
 
 int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq,
-                     u16 wqebb_size, u16 wq_page_size, u16 q_depth,
+                     u16 wqebb_size, u32 wq_page_size, u16 q_depth,
                      u16 max_wqe_size);
 
 void hinic_wq_free(struct hinic_wqs *wqs, struct hinic_wq *wq);
index 1356097..b66bb86 100644 (file)
@@ -29,6 +29,7 @@
 #include "hinic_tx.h"
 #include "hinic_rx.h"
 #include "hinic_dev.h"
+#include "hinic_sriov.h"
 
 MODULE_AUTHOR("Huawei Technologies CO., Ltd");
 MODULE_DESCRIPTION("Huawei Intelligent NIC driver");
@@ -46,6 +47,7 @@ MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)");
 #define HINIC_DEV_ID_DUAL_PORT_100GE        0x0200
 #define HINIC_DEV_ID_DUAL_PORT_100GE_MEZZ   0x0205
 #define HINIC_DEV_ID_QUAD_PORT_25GE_MEZZ    0x0210
+#define HINIC_DEV_ID_VF    0x375e
 
 #define HINIC_WQ_NAME                   "hinic_dev"
 
@@ -65,6 +67,8 @@ MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)");
 #define rx_mode_work_to_nic_dev(rx_mode_work) \
                container_of(rx_mode_work, struct hinic_dev, rx_mode_work)
 
+#define HINIC_WAIT_SRIOV_CFG_TIMEOUT   15000
+
 static int change_mac_addr(struct net_device *netdev, const u8 *addr);
 
 static int set_features(struct hinic_dev *nic_dev,
@@ -423,8 +427,9 @@ static int hinic_open(struct net_device *netdev)
                goto err_func_port_state;
        }
 
-       /* Wait up to 3 sec between port enable to link state */
-       msleep(3000);
+       if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
+               /* Wait up to 3 sec between port enable to link state */
+               msleep(3000);
 
        down(&nic_dev->mgmt_lock);
 
@@ -434,6 +439,9 @@ static int hinic_open(struct net_device *netdev)
                goto err_port_link;
        }
 
+       if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
+               hinic_notify_all_vfs_link_changed(nic_dev->hwdev, link_state);
+
        if (link_state == HINIC_LINK_STATE_UP)
                nic_dev->flags |= HINIC_LINK_UP;
 
@@ -497,6 +505,9 @@ static int hinic_close(struct net_device *netdev)
 
        up(&nic_dev->mgmt_lock);
 
+       if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
+               hinic_notify_all_vfs_link_changed(nic_dev->hwdev, 0);
+
        err = hinic_port_set_func_state(nic_dev, HINIC_FUNC_PORT_DISABLE);
        if (err) {
                netif_err(nic_dev, drv, netdev,
@@ -685,7 +696,7 @@ static int hinic_vlan_rx_add_vid(struct net_device *netdev,
        }
 
        err = hinic_port_add_mac(nic_dev, netdev->dev_addr, vid);
-       if (err) {
+       if (err && err != HINIC_PF_SET_VF_ALREADY) {
                netif_err(nic_dev, drv, netdev, "Failed to set mac\n");
                goto err_add_mac;
        }
@@ -737,8 +748,6 @@ static void set_rx_mode(struct work_struct *work)
        struct hinic_rx_mode_work *rx_mode_work = work_to_rx_mode_work(work);
        struct hinic_dev *nic_dev = rx_mode_work_to_nic_dev(rx_mode_work);
 
-       netif_info(nic_dev, drv, nic_dev->netdev, "set rx mode work\n");
-
        hinic_port_set_rx_mode(nic_dev, rx_mode_work->rx_mode);
 
        __dev_uc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr);
@@ -770,8 +779,26 @@ static void hinic_set_rx_mode(struct net_device *netdev)
 static void hinic_tx_timeout(struct net_device *netdev, unsigned int txqueue)
 {
        struct hinic_dev *nic_dev = netdev_priv(netdev);
+       u16 sw_pi, hw_ci, sw_ci;
+       struct hinic_sq *sq;
+       u16 num_sqs, q_id;
+
+       num_sqs = hinic_hwdev_num_qps(nic_dev->hwdev);
 
        netif_err(nic_dev, drv, netdev, "Tx timeout\n");
+
+       for (q_id = 0; q_id < num_sqs; q_id++) {
+               if (!netif_xmit_stopped(netdev_get_tx_queue(netdev, q_id)))
+                       continue;
+
+               sq = hinic_hwdev_get_sq(nic_dev->hwdev, q_id);
+               sw_pi = atomic_read(&sq->wq->prod_idx) & sq->wq->mask;
+               hw_ci = be16_to_cpu(*(u16 *)(sq->hw_ci_addr)) & sq->wq->mask;
+               sw_ci = atomic_read(&sq->wq->cons_idx) & sq->wq->mask;
+               netif_err(nic_dev, drv, netdev, "Txq%d: sw_pi: %d, hw_ci: %d, sw_ci: %d, napi->state: 0x%lx\n",
+                         q_id, sw_pi, hw_ci, sw_ci,
+                         nic_dev->txqs[q_id].napi.state);
+       }
 }
 
 static void hinic_get_stats64(struct net_device *netdev,
@@ -837,6 +864,26 @@ static const struct net_device_ops hinic_netdev_ops = {
        .ndo_get_stats64 = hinic_get_stats64,
        .ndo_fix_features = hinic_fix_features,
        .ndo_set_features = hinic_set_features,
+       .ndo_set_vf_mac = hinic_ndo_set_vf_mac,
+       .ndo_set_vf_vlan = hinic_ndo_set_vf_vlan,
+       .ndo_get_vf_config = hinic_ndo_get_vf_config,
+       .ndo_set_vf_trust = hinic_ndo_set_vf_trust,
+};
+
+static const struct net_device_ops hinicvf_netdev_ops = {
+       .ndo_open = hinic_open,
+       .ndo_stop = hinic_close,
+       .ndo_change_mtu = hinic_change_mtu,
+       .ndo_set_mac_address = hinic_set_mac_addr,
+       .ndo_validate_addr = eth_validate_addr,
+       .ndo_vlan_rx_add_vid = hinic_vlan_rx_add_vid,
+       .ndo_vlan_rx_kill_vid = hinic_vlan_rx_kill_vid,
+       .ndo_set_rx_mode = hinic_set_rx_mode,
+       .ndo_start_xmit = hinic_xmit_frame,
+       .ndo_tx_timeout = hinic_tx_timeout,
+       .ndo_get_stats64 = hinic_get_stats64,
+       .ndo_fix_features = hinic_fix_features,
+       .ndo_set_features = hinic_set_features,
 };
 
 static void netdev_features_init(struct net_device *netdev)
@@ -896,6 +943,10 @@ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size,
                netif_info(nic_dev, drv, nic_dev->netdev, "HINIC_Link is DOWN\n");
        }
 
+       if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
+               hinic_notify_all_vfs_link_changed(nic_dev->hwdev,
+                                                 link_status->link);
+
        ret_link_status = buf_out;
        ret_link_status->status = 0;
 
@@ -969,7 +1020,12 @@ static int nic_dev_init(struct pci_dev *pdev)
        }
 
        hinic_set_ethtool_ops(netdev);
-       netdev->netdev_ops = &hinic_netdev_ops;
+
+       if (!HINIC_IS_VF(hwdev->hwif))
+               netdev->netdev_ops = &hinic_netdev_ops;
+       else
+               netdev->netdev_ops = &hinicvf_netdev_ops;
+
        netdev->max_mtu = ETH_MAX_MTU;
 
        nic_dev = netdev_priv(netdev);
@@ -981,6 +1037,8 @@ static int nic_dev_init(struct pci_dev *pdev)
        nic_dev->rxqs = NULL;
        nic_dev->tx_weight = tx_weight;
        nic_dev->rx_weight = rx_weight;
+       nic_dev->sriov_info.hwdev = hwdev;
+       nic_dev->sriov_info.pdev = pdev;
 
        sema_init(&nic_dev->mgmt_lock, 1);
 
@@ -1007,11 +1065,25 @@ static int nic_dev_init(struct pci_dev *pdev)
        pci_set_drvdata(pdev, netdev);
 
        err = hinic_port_get_mac(nic_dev, netdev->dev_addr);
-       if (err)
-               dev_warn(&pdev->dev, "Failed to get mac address\n");
+       if (err) {
+               dev_err(&pdev->dev, "Failed to get mac address\n");
+               goto err_get_mac;
+       }
+
+       if (!is_valid_ether_addr(netdev->dev_addr)) {
+               if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) {
+                       dev_err(&pdev->dev, "Invalid MAC address\n");
+                       err = -EIO;
+                       goto err_add_mac;
+               }
+
+               dev_info(&pdev->dev, "Invalid MAC address %pM, using random\n",
+                        netdev->dev_addr);
+               eth_hw_addr_random(netdev);
+       }
 
        err = hinic_port_add_mac(nic_dev, netdev->dev_addr, 0);
-       if (err) {
+       if (err && err != HINIC_PF_SET_VF_ALREADY) {
                dev_err(&pdev->dev, "Failed to add mac\n");
                goto err_add_mac;
        }
@@ -1053,6 +1125,7 @@ err_set_features:
        cancel_work_sync(&rx_mode_work->work);
 
 err_set_mtu:
+err_get_mac:
 err_add_mac:
        pci_set_drvdata(pdev, NULL);
        destroy_workqueue(nic_dev->workq);
@@ -1126,12 +1199,37 @@ err_pci_regions:
        return err;
 }
 
+#define HINIC_WAIT_SRIOV_CFG_TIMEOUT   15000
+
+static void wait_sriov_cfg_complete(struct hinic_dev *nic_dev)
+{
+       struct hinic_sriov_info *sriov_info = &nic_dev->sriov_info;
+       u32 loop_cnt = 0;
+
+       set_bit(HINIC_FUNC_REMOVE, &sriov_info->state);
+       usleep_range(9900, 10000);
+
+       while (loop_cnt < HINIC_WAIT_SRIOV_CFG_TIMEOUT) {
+               if (!test_bit(HINIC_SRIOV_ENABLE, &sriov_info->state) &&
+                   !test_bit(HINIC_SRIOV_DISABLE, &sriov_info->state))
+                       return;
+
+               usleep_range(9900, 10000);
+               loop_cnt++;
+       }
+}
+
 static void hinic_remove(struct pci_dev *pdev)
 {
        struct net_device *netdev = pci_get_drvdata(pdev);
        struct hinic_dev *nic_dev = netdev_priv(netdev);
        struct hinic_rx_mode_work *rx_mode_work;
 
+       if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) {
+               wait_sriov_cfg_complete(nic_dev);
+               hinic_pci_sriov_disable(pdev);
+       }
+
        unregister_netdev(netdev);
 
        hinic_hwdev_cb_unregister(nic_dev->hwdev,
@@ -1144,6 +1242,8 @@ static void hinic_remove(struct pci_dev *pdev)
 
        destroy_workqueue(nic_dev->workq);
 
+       hinic_vf_func_free(nic_dev->hwdev);
+
        hinic_free_hwdev(nic_dev->hwdev);
 
        free_netdev(netdev);
@@ -1164,6 +1264,7 @@ static const struct pci_device_id hinic_pci_table[] = {
        { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_DUAL_PORT_100GE), 0},
        { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_DUAL_PORT_100GE_MEZZ), 0},
        { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_QUAD_PORT_25GE_MEZZ), 0},
+       { PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_VF), 0},
        { 0, 0}
 };
 MODULE_DEVICE_TABLE(pci, hinic_pci_table);
@@ -1174,6 +1275,7 @@ static struct pci_driver hinic_driver = {
        .probe          = hinic_probe,
        .remove         = hinic_remove,
        .shutdown       = hinic_shutdown,
+       .sriov_configure = hinic_pci_sriov_configure,
 };
 
 module_pci_driver(hinic_driver);
index 1e389a0..b7fe0ad 100644 (file)
@@ -37,20 +37,14 @@ enum mac_op {
 static int change_mac(struct hinic_dev *nic_dev, const u8 *addr,
                      u16 vlan_id, enum mac_op op)
 {
-       struct net_device *netdev = nic_dev->netdev;
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
        struct hinic_port_mac_cmd port_mac_cmd;
        struct hinic_hwif *hwif = hwdev->hwif;
+       u16 out_size = sizeof(port_mac_cmd);
        struct pci_dev *pdev = hwif->pdev;
        enum hinic_port_cmd cmd;
-       u16 out_size;
        int err;
 
-       if (vlan_id >= VLAN_N_VID) {
-               netif_err(nic_dev, drv, netdev, "Invalid VLAN number\n");
-               return -EINVAL;
-       }
-
        if (op == MAC_SET)
                cmd = HINIC_PORT_CMD_SET_MAC;
        else
@@ -63,12 +57,25 @@ static int change_mac(struct hinic_dev *nic_dev, const u8 *addr,
        err = hinic_port_msg_cmd(hwdev, cmd, &port_mac_cmd,
                                 sizeof(port_mac_cmd),
                                 &port_mac_cmd, &out_size);
-       if (err || (out_size != sizeof(port_mac_cmd)) || port_mac_cmd.status) {
+       if (err || out_size != sizeof(port_mac_cmd) ||
+           (port_mac_cmd.status  &&
+           port_mac_cmd.status != HINIC_PF_SET_VF_ALREADY &&
+           port_mac_cmd.status != HINIC_MGMT_STATUS_EXIST)) {
                dev_err(&pdev->dev, "Failed to change MAC, ret = %d\n",
                        port_mac_cmd.status);
                return -EFAULT;
        }
 
+       if (cmd == HINIC_PORT_CMD_SET_MAC && port_mac_cmd.status ==
+           HINIC_PF_SET_VF_ALREADY) {
+               dev_warn(&pdev->dev, "PF has already set VF mac, Ignore set operation\n");
+               return HINIC_PF_SET_VF_ALREADY;
+       }
+
+       if (cmd == HINIC_PORT_CMD_SET_MAC && port_mac_cmd.status ==
+           HINIC_MGMT_STATUS_EXIST)
+               dev_warn(&pdev->dev, "MAC is repeated. Ignore set operation\n");
+
        return 0;
 }
 
@@ -112,8 +119,8 @@ int hinic_port_get_mac(struct hinic_dev *nic_dev, u8 *addr)
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
        struct hinic_port_mac_cmd port_mac_cmd;
        struct hinic_hwif *hwif = hwdev->hwif;
+       u16 out_size = sizeof(port_mac_cmd);
        struct pci_dev *pdev = hwif->pdev;
-       u16 out_size;
        int err;
 
        port_mac_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
@@ -144,9 +151,9 @@ int hinic_port_set_mtu(struct hinic_dev *nic_dev, int new_mtu)
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
        struct hinic_port_mtu_cmd port_mtu_cmd;
        struct hinic_hwif *hwif = hwdev->hwif;
+       u16 out_size = sizeof(port_mtu_cmd);
        struct pci_dev *pdev = hwif->pdev;
        int err, max_frame;
-       u16 out_size;
 
        if (new_mtu < HINIC_MIN_MTU_SIZE) {
                netif_err(nic_dev, drv, netdev, "mtu < MIN MTU size");
@@ -248,14 +255,9 @@ int hinic_port_link_state(struct hinic_dev *nic_dev,
        struct hinic_hwif *hwif = hwdev->hwif;
        struct hinic_port_link_cmd link_cmd;
        struct pci_dev *pdev = hwif->pdev;
-       u16 out_size;
+       u16 out_size = sizeof(link_cmd);
        int err;
 
-       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
-               dev_err(&pdev->dev, "unsupported PCI Function type\n");
-               return -EINVAL;
-       }
-
        link_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
 
        err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_LINK_STATE,
@@ -284,13 +286,11 @@ int hinic_port_set_state(struct hinic_dev *nic_dev, enum hinic_port_state state)
        struct hinic_port_state_cmd port_state;
        struct hinic_hwif *hwif = hwdev->hwif;
        struct pci_dev *pdev = hwif->pdev;
-       u16 out_size;
+       u16 out_size = sizeof(port_state);
        int err;
 
-       if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
-               dev_err(&pdev->dev, "unsupported PCI Function type\n");
-               return -EINVAL;
-       }
+       if (HINIC_IS_VF(hwdev->hwif))
+               return 0;
 
        port_state.state = state;
 
@@ -320,7 +320,7 @@ int hinic_port_set_func_state(struct hinic_dev *nic_dev,
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
        struct hinic_hwif *hwif = hwdev->hwif;
        struct pci_dev *pdev = hwif->pdev;
-       u16 out_size;
+       u16 out_size = sizeof(func_state);
        int err;
 
        func_state.func_idx = HINIC_HWIF_FUNC_IDX(hwif);
@@ -351,7 +351,7 @@ int hinic_port_get_cap(struct hinic_dev *nic_dev,
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
        struct hinic_hwif *hwif = hwdev->hwif;
        struct pci_dev *pdev = hwif->pdev;
-       u16 out_size;
+       u16 out_size = sizeof(*port_cap);
        int err;
 
        port_cap->func_idx = HINIC_HWIF_FUNC_IDX(hwif);
@@ -382,7 +382,7 @@ int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state)
        struct hinic_hwif *hwif = hwdev->hwif;
        struct hinic_tso_config tso_cfg = {0};
        struct pci_dev *pdev = hwif->pdev;
-       u16 out_size;
+       u16 out_size = sizeof(tso_cfg);
        int err;
 
        tso_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
@@ -405,9 +405,9 @@ int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en)
 {
        struct hinic_checksum_offload rx_csum_cfg = {0};
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
+       u16 out_size = sizeof(rx_csum_cfg);
        struct hinic_hwif *hwif;
        struct pci_dev *pdev;
-       u16 out_size;
        int err;
 
        if (!hwdev)
@@ -443,6 +443,7 @@ int hinic_set_rx_vlan_offload(struct hinic_dev *nic_dev, u8 en)
        if (!hwdev)
                return -EINVAL;
 
+       out_size = sizeof(vlan_cfg);
        hwif = hwdev->hwif;
        pdev = hwif->pdev;
        vlan_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
@@ -465,8 +466,8 @@ int hinic_set_max_qnum(struct hinic_dev *nic_dev, u8 num_rqs)
 {
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
        struct hinic_hwif *hwif = hwdev->hwif;
-       struct pci_dev *pdev = hwif->pdev;
        struct hinic_rq_num rq_num = { 0 };
+       struct pci_dev *pdev = hwif->pdev;
        u16 out_size = sizeof(rq_num);
        int err;
 
@@ -491,8 +492,8 @@ static int hinic_set_rx_lro(struct hinic_dev *nic_dev, u8 ipv4_en, u8 ipv6_en,
                            u8 max_wqe_num)
 {
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
-       struct hinic_hwif *hwif = hwdev->hwif;
        struct hinic_lro_config lro_cfg = { 0 };
+       struct hinic_hwif *hwif = hwdev->hwif;
        struct pci_dev *pdev = hwif->pdev;
        u16 out_size = sizeof(lro_cfg);
        int err;
@@ -568,6 +569,9 @@ int hinic_set_rx_lro_state(struct hinic_dev *nic_dev, u8 lro_en,
        if (err)
                return err;
 
+       if (HINIC_IS_VF(nic_dev->hwdev->hwif))
+               return 0;
+
        err = hinic_set_rx_lro_timer(nic_dev, lro_timer);
        if (err)
                return err;
@@ -741,9 +745,9 @@ int hinic_get_rss_type(struct hinic_dev *nic_dev, u32 tmpl_idx,
 {
        struct hinic_rss_context_table ctx_tbl = { 0 };
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
+       u16 out_size = sizeof(ctx_tbl);
        struct hinic_hwif *hwif;
        struct pci_dev *pdev;
-       u16 out_size = sizeof(ctx_tbl);
        int err;
 
        if (!hwdev || !rss_type)
@@ -784,7 +788,7 @@ int hinic_rss_set_template_tbl(struct hinic_dev *nic_dev, u32 template_id,
        struct hinic_hwif *hwif = hwdev->hwif;
        struct hinic_rss_key rss_key = { 0 };
        struct pci_dev *pdev = hwif->pdev;
-       u16 out_size;
+       u16 out_size = sizeof(rss_key);
        int err;
 
        rss_key.func_id = HINIC_HWIF_FUNC_IDX(hwif);
@@ -809,9 +813,9 @@ int hinic_rss_get_template_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx,
 {
        struct hinic_rss_template_key temp_key = { 0 };
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
+       u16 out_size = sizeof(temp_key);
        struct hinic_hwif *hwif;
        struct pci_dev *pdev;
-       u16 out_size = sizeof(temp_key);
        int err;
 
        if (!hwdev || !temp)
@@ -844,7 +848,7 @@ int hinic_rss_set_hash_engine(struct hinic_dev *nic_dev, u8 template_id,
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
        struct hinic_hwif *hwif = hwdev->hwif;
        struct pci_dev *pdev = hwif->pdev;
-       u16 out_size;
+       u16 out_size = sizeof(rss_engine);
        int err;
 
        rss_engine.func_id = HINIC_HWIF_FUNC_IDX(hwif);
@@ -868,9 +872,9 @@ int hinic_rss_get_hash_engine(struct hinic_dev *nic_dev, u8 tmpl_idx, u8 *type)
 {
        struct hinic_rss_engine_type hash_type = { 0 };
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
+       u16 out_size = sizeof(hash_type);
        struct hinic_hwif *hwif;
        struct pci_dev *pdev;
-       u16 out_size = sizeof(hash_type);
        int err;
 
        if (!hwdev || !type)
@@ -901,7 +905,7 @@ int hinic_rss_cfg(struct hinic_dev *nic_dev, u8 rss_en, u8 template_id)
        struct hinic_rss_config rss_cfg = { 0 };
        struct hinic_hwif *hwif = hwdev->hwif;
        struct pci_dev *pdev = hwif->pdev;
-       u16 out_size;
+       u16 out_size = sizeof(rss_cfg);
        int err;
 
        rss_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
@@ -927,8 +931,8 @@ int hinic_rss_template_alloc(struct hinic_dev *nic_dev, u8 *tmpl_idx)
        struct hinic_rss_template_mgmt template_mgmt = { 0 };
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
        struct hinic_hwif *hwif = hwdev->hwif;
+       u16 out_size = sizeof(template_mgmt);
        struct pci_dev *pdev = hwif->pdev;
-       u16 out_size;
        int err;
 
        template_mgmt.func_id = HINIC_HWIF_FUNC_IDX(hwif);
@@ -953,8 +957,8 @@ int hinic_rss_template_free(struct hinic_dev *nic_dev, u8 tmpl_idx)
        struct hinic_rss_template_mgmt template_mgmt = { 0 };
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
        struct hinic_hwif *hwif = hwdev->hwif;
+       u16 out_size = sizeof(template_mgmt);
        struct pci_dev *pdev = hwif->pdev;
-       u16 out_size;
        int err;
 
        template_mgmt.func_id = HINIC_HWIF_FUNC_IDX(hwif);
@@ -1043,9 +1047,9 @@ int hinic_get_mgmt_version(struct hinic_dev *nic_dev, u8 *mgmt_ver)
 {
        struct hinic_hwdev *hwdev = nic_dev->hwdev;
        struct hinic_version_info up_ver = {0};
+       u16 out_size = sizeof(up_ver);
        struct hinic_hwif *hwif;
        struct pci_dev *pdev;
-       u16 out_size;
        int err;
 
        if (!hwdev)
index 44772fd..5ad04fb 100644 (file)
@@ -148,9 +148,9 @@ struct hinic_port_link_status {
        u8      version;
        u8      rsvd0[6];
 
-       u16     rsvd1;
+       u16     func_id;
        u8      link;
-       u8      rsvd2;
+       u8      port_id;
 };
 
 struct hinic_port_func_state_cmd {
index 815649e..af20d0d 100644 (file)
@@ -432,9 +432,11 @@ static int rx_poll(struct napi_struct *napi, int budget)
                return budget;
 
        napi_complete(napi);
-       hinic_hwdev_set_msix_state(nic_dev->hwdev,
-                                  rq->msix_entry,
-                                  HINIC_MSIX_ENABLE);
+
+       if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
+               hinic_hwdev_set_msix_state(nic_dev->hwdev,
+                                          rq->msix_entry,
+                                          HINIC_MSIX_ENABLE);
 
        return pkts;
 }
@@ -461,9 +463,10 @@ static irqreturn_t rx_irq(int irq, void *data)
 
        /* Disable the interrupt until napi will be completed */
        nic_dev = netdev_priv(rxq->netdev);
-       hinic_hwdev_set_msix_state(nic_dev->hwdev,
-                                  rq->msix_entry,
-                                  HINIC_MSIX_DISABLE);
+       if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
+               hinic_hwdev_set_msix_state(nic_dev->hwdev,
+                                          rq->msix_entry,
+                                          HINIC_MSIX_DISABLE);
 
        nic_dev = netdev_priv(rxq->netdev);
        hinic_hwdev_msix_cnt_set(nic_dev->hwdev, rq->msix_entry);
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_sriov.c b/drivers/net/ethernet/huawei/hinic/hinic_sriov.c
new file mode 100644 (file)
index 0000000..fd4aaf4
--- /dev/null
@@ -0,0 +1,1019 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Huawei HiNIC PCI Express Linux driver
+ * Copyright(c) 2017 Huawei Technologies Co., Ltd
+ */
+
+#include <linux/pci.h>
+#include <linux/if_vlan.h>
+#include <linux/interrupt.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+
+#include "hinic_hw_dev.h"
+#include "hinic_dev.h"
+#include "hinic_hw_mbox.h"
+#include "hinic_hw_cmdq.h"
+#include "hinic_port.h"
+#include "hinic_sriov.h"
+
+static unsigned char set_vf_link_state;
+module_param(set_vf_link_state, byte, 0444);
+MODULE_PARM_DESC(set_vf_link_state, "Set vf link state, 0 represents link auto, 1 represents link always up, 2 represents link always down. - default is 0.");
+
+#define HINIC_VLAN_PRIORITY_SHIFT 13
+#define HINIC_ADD_VLAN_IN_MAC 0x8000
+
+static int hinic_set_mac(struct hinic_hwdev *hwdev, const u8 *mac_addr,
+                        u16 vlan_id, u16 func_id)
+{
+       struct hinic_port_mac_cmd mac_info = {0};
+       u16 out_size = sizeof(mac_info);
+       int err;
+
+       mac_info.func_idx = func_id;
+       mac_info.vlan_id = vlan_id;
+       memcpy(mac_info.mac, mac_addr, ETH_ALEN);
+
+       err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_MAC, &mac_info,
+                                sizeof(mac_info), &mac_info, &out_size);
+       if (err || out_size != sizeof(mac_info) ||
+           (mac_info.status && mac_info.status != HINIC_PF_SET_VF_ALREADY &&
+           mac_info.status != HINIC_MGMT_STATUS_EXIST)) {
+               dev_err(&hwdev->func_to_io.hwif->pdev->dev, "Failed to change MAC, ret = %d\n",
+                       mac_info.status);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static void hinic_notify_vf_link_status(struct hinic_hwdev *hwdev, u16 vf_id,
+                                       u8 link_status)
+{
+       struct vf_data_storage *vf_infos = hwdev->func_to_io.vf_infos;
+       struct hinic_port_link_status link = {0};
+       u16 out_size = sizeof(link);
+       int err;
+
+       if (vf_infos[HW_VF_ID_TO_OS(vf_id)].registered) {
+               link.link = link_status;
+               link.func_id = hinic_glb_pf_vf_offset(hwdev->hwif) + vf_id;
+               err = hinic_mbox_to_vf(hwdev, HINIC_MOD_L2NIC,
+                                      vf_id, HINIC_PORT_CMD_LINK_STATUS_REPORT,
+                                      &link, sizeof(link),
+                                      &link, &out_size, 0);
+               if (err || !out_size || link.status)
+                       dev_err(&hwdev->hwif->pdev->dev,
+                               "Send link change event to VF %d failed, err: %d, status: 0x%x, out_size: 0x%x\n",
+                               HW_VF_ID_TO_OS(vf_id), err,
+                               link.status, out_size);
+       }
+}
+
+/* send link change event mbox msg to active vfs under the pf */
+void hinic_notify_all_vfs_link_changed(struct hinic_hwdev *hwdev,
+                                      u8 link_status)
+{
+       struct hinic_func_to_io *nic_io = &hwdev->func_to_io;
+       u16 i;
+
+       nic_io->link_status = link_status;
+       for (i = 1; i <= nic_io->max_vfs; i++) {
+               if (!nic_io->vf_infos[HW_VF_ID_TO_OS(i)].link_forced)
+                       hinic_notify_vf_link_status(hwdev, i,  link_status);
+       }
+}
+
+static u16 hinic_vf_info_vlanprio(struct hinic_hwdev *hwdev, int vf_id)
+{
+       struct hinic_func_to_io *nic_io = &hwdev->func_to_io;
+       u16 pf_vlan, vlanprio;
+       u8 pf_qos;
+
+       pf_vlan = nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_vlan;
+       pf_qos = nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_qos;
+       vlanprio = pf_vlan | pf_qos << HINIC_VLAN_PRIORITY_SHIFT;
+
+       return vlanprio;
+}
+
+static int hinic_set_vf_vlan(struct hinic_hwdev *hwdev, bool add, u16 vid,
+                            u8 qos, int vf_id)
+{
+       struct hinic_vf_vlan_config vf_vlan = {0};
+       u16 out_size = sizeof(vf_vlan);
+       int err;
+       u8 cmd;
+
+       /* VLAN 0 is a special case, don't allow it to be removed */
+       if (!vid && !add)
+               return 0;
+
+       vf_vlan.func_id = hinic_glb_pf_vf_offset(hwdev->hwif) + vf_id;
+       vf_vlan.vlan_id = vid;
+       vf_vlan.qos = qos;
+
+       if (add)
+               cmd = HINIC_PORT_CMD_SET_VF_VLAN;
+       else
+               cmd = HINIC_PORT_CMD_CLR_VF_VLAN;
+
+       err = hinic_port_msg_cmd(hwdev, cmd, &vf_vlan,
+                                sizeof(vf_vlan), &vf_vlan, &out_size);
+       if (err || !out_size || vf_vlan.status) {
+               dev_err(&hwdev->hwif->pdev->dev, "Failed to set VF %d vlan, err: %d, status: 0x%x, out size: 0x%x\n",
+                       HW_VF_ID_TO_OS(vf_id), err, vf_vlan.status, out_size);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int hinic_init_vf_config(struct hinic_hwdev *hwdev, u16 vf_id)
+{
+       struct vf_data_storage *vf_info;
+       u16 func_id, vlan_id;
+       int err = 0;
+
+       vf_info = hwdev->func_to_io.vf_infos + HW_VF_ID_TO_OS(vf_id);
+       if (vf_info->pf_set_mac) {
+               func_id = hinic_glb_pf_vf_offset(hwdev->hwif) + vf_id;
+
+               vlan_id = 0;
+
+               err = hinic_set_mac(hwdev, vf_info->vf_mac_addr, vlan_id,
+                                   func_id);
+               if (err) {
+                       dev_err(&hwdev->func_to_io.hwif->pdev->dev, "Failed to set VF %d MAC\n",
+                               HW_VF_ID_TO_OS(vf_id));
+                       return err;
+               }
+       }
+
+       if (hinic_vf_info_vlanprio(hwdev, vf_id)) {
+               err = hinic_set_vf_vlan(hwdev, true, vf_info->pf_vlan,
+                                       vf_info->pf_qos, vf_id);
+               if (err) {
+                       dev_err(&hwdev->hwif->pdev->dev, "Failed to add VF %d VLAN_QOS\n",
+                               HW_VF_ID_TO_OS(vf_id));
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static int hinic_register_vf_msg_handler(void *hwdev, u16 vf_id,
+                                        void *buf_in, u16 in_size,
+                                        void *buf_out, u16 *out_size)
+{
+       struct hinic_register_vf *register_info = buf_out;
+       struct hinic_hwdev *hw_dev = hwdev;
+       struct hinic_func_to_io *nic_io;
+       int err;
+
+       nic_io = &hw_dev->func_to_io;
+       if (vf_id > nic_io->max_vfs) {
+               dev_err(&hw_dev->hwif->pdev->dev, "Register VF id %d exceed limit[0-%d]\n",
+                       HW_VF_ID_TO_OS(vf_id), HW_VF_ID_TO_OS(nic_io->max_vfs));
+               register_info->status = EFAULT;
+               return -EFAULT;
+       }
+
+       *out_size = sizeof(*register_info);
+       err = hinic_init_vf_config(hw_dev, vf_id);
+       if (err) {
+               register_info->status = EFAULT;
+               return err;
+       }
+
+       nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].registered = true;
+
+       return 0;
+}
+
+static int hinic_unregister_vf_msg_handler(void *hwdev, u16 vf_id,
+                                          void *buf_in, u16 in_size,
+                                          void *buf_out, u16 *out_size)
+{
+       struct hinic_hwdev *hw_dev = hwdev;
+       struct hinic_func_to_io *nic_io;
+
+       nic_io = &hw_dev->func_to_io;
+       *out_size = 0;
+       if (vf_id > nic_io->max_vfs)
+               return 0;
+
+       nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].registered = false;
+
+       return 0;
+}
+
+static int hinic_change_vf_mtu_msg_handler(void *hwdev, u16 vf_id,
+                                          void *buf_in, u16 in_size,
+                                          void *buf_out, u16 *out_size)
+{
+       struct hinic_hwdev *hw_dev = hwdev;
+       int err;
+
+       err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_CHANGE_MTU, buf_in,
+                                in_size, buf_out, out_size);
+       if (err) {
+               dev_err(&hw_dev->hwif->pdev->dev, "Failed to set VF %u mtu\n",
+                       vf_id);
+               return err;
+       }
+
+       return 0;
+}
+
+static int hinic_get_vf_mac_msg_handler(void *hwdev, u16 vf_id,
+                                       void *buf_in, u16 in_size,
+                                       void *buf_out, u16 *out_size)
+{
+       struct hinic_port_mac_cmd *mac_info = buf_out;
+       struct hinic_hwdev *dev = hwdev;
+       struct hinic_func_to_io *nic_io;
+       struct vf_data_storage *vf_info;
+
+       nic_io = &dev->func_to_io;
+       vf_info = nic_io->vf_infos + HW_VF_ID_TO_OS(vf_id);
+
+       memcpy(mac_info->mac, vf_info->vf_mac_addr, ETH_ALEN);
+       mac_info->status = 0;
+       *out_size = sizeof(*mac_info);
+
+       return 0;
+}
+
+static int hinic_set_vf_mac_msg_handler(void *hwdev, u16 vf_id,
+                                       void *buf_in, u16 in_size,
+                                       void *buf_out, u16 *out_size)
+{
+       struct hinic_port_mac_cmd *mac_out = buf_out;
+       struct hinic_port_mac_cmd *mac_in = buf_in;
+       struct hinic_hwdev *hw_dev = hwdev;
+       struct hinic_func_to_io *nic_io;
+       struct vf_data_storage *vf_info;
+       int err;
+
+       nic_io =  &hw_dev->func_to_io;
+       vf_info = nic_io->vf_infos + HW_VF_ID_TO_OS(vf_id);
+       if (vf_info->pf_set_mac && !(vf_info->trust) &&
+           is_valid_ether_addr(mac_in->mac)) {
+               dev_warn(&hw_dev->hwif->pdev->dev, "PF has already set VF %d MAC address\n",
+                        HW_VF_ID_TO_OS(vf_id));
+               mac_out->status = HINIC_PF_SET_VF_ALREADY;
+               *out_size = sizeof(*mac_out);
+               return 0;
+       }
+
+       err = hinic_port_msg_cmd(hw_dev, HINIC_PORT_CMD_SET_MAC, buf_in,
+                                in_size, buf_out, out_size);
+       if ((err &&  err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) || !(*out_size)) {
+               dev_err(&hw_dev->hwif->pdev->dev,
+                       "Failed to set VF %d MAC address, err: %d, status: 0x%x, out size: 0x%x\n",
+                       HW_VF_ID_TO_OS(vf_id), err, mac_out->status, *out_size);
+               return -EFAULT;
+       }
+
+       return err;
+}
+
+static int hinic_del_vf_mac_msg_handler(void *hwdev, u16 vf_id,
+                                       void *buf_in, u16 in_size,
+                                       void *buf_out, u16 *out_size)
+{
+       struct hinic_port_mac_cmd *mac_out = buf_out;
+       struct hinic_port_mac_cmd *mac_in = buf_in;
+       struct hinic_hwdev *hw_dev = hwdev;
+       struct hinic_func_to_io *nic_io;
+       struct vf_data_storage *vf_info;
+       int err;
+
+       nic_io = &hw_dev->func_to_io;
+       vf_info = nic_io->vf_infos + HW_VF_ID_TO_OS(vf_id);
+       if (vf_info->pf_set_mac  && is_valid_ether_addr(mac_in->mac) &&
+           !memcmp(vf_info->vf_mac_addr, mac_in->mac, ETH_ALEN)) {
+               dev_warn(&hw_dev->hwif->pdev->dev, "PF has already set VF mac.\n");
+               mac_out->status = HINIC_PF_SET_VF_ALREADY;
+               *out_size = sizeof(*mac_out);
+               return 0;
+       }
+
+       err = hinic_port_msg_cmd(hw_dev, HINIC_PORT_CMD_DEL_MAC, buf_in,
+                                in_size, buf_out, out_size);
+       if ((err && err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) || !(*out_size)) {
+               dev_err(&hw_dev->hwif->pdev->dev, "Failed to delete VF %d MAC, err: %d, status: 0x%x, out size: 0x%x\n",
+                       HW_VF_ID_TO_OS(vf_id), err, mac_out->status, *out_size);
+               return -EFAULT;
+       }
+
+       return err;
+}
+
+static int hinic_get_vf_link_status_msg_handler(void *hwdev, u16 vf_id,
+                                               void *buf_in, u16 in_size,
+                                               void *buf_out, u16 *out_size)
+{
+       struct hinic_port_link_cmd *get_link = buf_out;
+       struct hinic_hwdev *hw_dev = hwdev;
+       struct vf_data_storage *vf_infos;
+       struct hinic_func_to_io *nic_io;
+       bool link_forced, link_up;
+
+       nic_io = &hw_dev->func_to_io;
+       vf_infos = nic_io->vf_infos;
+       link_forced = vf_infos[HW_VF_ID_TO_OS(vf_id)].link_forced;
+       link_up = vf_infos[HW_VF_ID_TO_OS(vf_id)].link_up;
+
+       if (link_forced)
+               get_link->state = link_up ?
+                       HINIC_LINK_STATE_UP : HINIC_LINK_STATE_DOWN;
+       else
+               get_link->state = nic_io->link_status;
+
+       get_link->status = 0;
+       *out_size = sizeof(*get_link);
+
+       return 0;
+}
+
+static struct vf_cmd_msg_handle nic_vf_cmd_msg_handler[] = {
+       {HINIC_PORT_CMD_VF_REGISTER, hinic_register_vf_msg_handler},
+       {HINIC_PORT_CMD_VF_UNREGISTER, hinic_unregister_vf_msg_handler},
+       {HINIC_PORT_CMD_CHANGE_MTU, hinic_change_vf_mtu_msg_handler},
+       {HINIC_PORT_CMD_GET_MAC, hinic_get_vf_mac_msg_handler},
+       {HINIC_PORT_CMD_SET_MAC, hinic_set_vf_mac_msg_handler},
+       {HINIC_PORT_CMD_DEL_MAC, hinic_del_vf_mac_msg_handler},
+       {HINIC_PORT_CMD_GET_LINK_STATE, hinic_get_vf_link_status_msg_handler},
+};
+
+#define CHECK_IPSU_15BIT       0X8000
+
+static
+struct hinic_sriov_info *hinic_get_sriov_info_by_pcidev(struct pci_dev *pdev)
+{
+       struct net_device *netdev = pci_get_drvdata(pdev);
+       struct hinic_dev *nic_dev = netdev_priv(netdev);
+
+       return &nic_dev->sriov_info;
+}
+
+static int hinic_check_mac_info(u8 status, u16 vlan_id)
+{
+       if ((status && status != HINIC_MGMT_STATUS_EXIST &&
+            status != HINIC_PF_SET_VF_ALREADY) ||
+           (vlan_id & CHECK_IPSU_15BIT &&
+            status == HINIC_MGMT_STATUS_EXIST))
+               return -EINVAL;
+
+       return 0;
+}
+
+#define HINIC_VLAN_ID_MASK     0x7FFF
+
+static int hinic_update_mac(struct hinic_hwdev *hwdev, u8 *old_mac,
+                           u8 *new_mac, u16 vlan_id, u16 func_id)
+{
+       struct hinic_port_mac_update mac_info = {0};
+       u16 out_size = sizeof(mac_info);
+       int err;
+
+       if (!hwdev || !old_mac || !new_mac)
+               return -EINVAL;
+
+       if ((vlan_id & HINIC_VLAN_ID_MASK) >= VLAN_N_VID) {
+               dev_err(&hwdev->hwif->pdev->dev, "Invalid VLAN number: %d\n",
+                       (vlan_id & HINIC_VLAN_ID_MASK));
+               return -EINVAL;
+       }
+
+       mac_info.func_id = func_id;
+       mac_info.vlan_id = vlan_id;
+       memcpy(mac_info.old_mac, old_mac, ETH_ALEN);
+       memcpy(mac_info.new_mac, new_mac, ETH_ALEN);
+
+       err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_UPDATE_MAC, &mac_info,
+                                sizeof(mac_info), &mac_info, &out_size);
+
+       if (err || !out_size ||
+           hinic_check_mac_info(mac_info.status, mac_info.vlan_id)) {
+               dev_err(&hwdev->hwif->pdev->dev,
+                       "Failed to update MAC, err: %d, status: 0x%x, out size: 0x%x\n",
+                       err, mac_info.status, out_size);
+               return -EINVAL;
+       }
+
+       if (mac_info.status == HINIC_PF_SET_VF_ALREADY) {
+               dev_warn(&hwdev->hwif->pdev->dev,
+                        "PF has already set VF MAC. Ignore update operation\n");
+               return HINIC_PF_SET_VF_ALREADY;
+       }
+
+       if (mac_info.status == HINIC_MGMT_STATUS_EXIST)
+               dev_warn(&hwdev->hwif->pdev->dev, "MAC is repeated. Ignore update operation\n");
+
+       return 0;
+}
+
+static void hinic_get_vf_config(struct hinic_hwdev *hwdev, u16 vf_id,
+                               struct ifla_vf_info *ivi)
+{
+       struct vf_data_storage *vfinfo;
+
+       vfinfo = hwdev->func_to_io.vf_infos + HW_VF_ID_TO_OS(vf_id);
+
+       ivi->vf = HW_VF_ID_TO_OS(vf_id);
+       memcpy(ivi->mac, vfinfo->vf_mac_addr, ETH_ALEN);
+       ivi->vlan = vfinfo->pf_vlan;
+       ivi->qos = vfinfo->pf_qos;
+       ivi->spoofchk = vfinfo->spoofchk;
+       ivi->trusted = vfinfo->trust;
+       ivi->max_tx_rate = vfinfo->max_rate;
+       ivi->min_tx_rate = vfinfo->min_rate;
+
+       if (!vfinfo->link_forced)
+               ivi->linkstate = IFLA_VF_LINK_STATE_AUTO;
+       else if (vfinfo->link_up)
+               ivi->linkstate = IFLA_VF_LINK_STATE_ENABLE;
+       else
+               ivi->linkstate = IFLA_VF_LINK_STATE_DISABLE;
+}
+
+int hinic_ndo_get_vf_config(struct net_device *netdev,
+                           int vf, struct ifla_vf_info *ivi)
+{
+       struct hinic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic_sriov_info *sriov_info;
+
+       sriov_info = &nic_dev->sriov_info;
+       if (vf >= sriov_info->num_vfs)
+               return -EINVAL;
+
+       hinic_get_vf_config(sriov_info->hwdev, OS_VF_ID_TO_HW(vf), ivi);
+
+       return 0;
+}
+
+static int hinic_set_vf_mac(struct hinic_hwdev *hwdev, int vf,
+                           unsigned char *mac_addr)
+{
+       struct hinic_func_to_io *nic_io = &hwdev->func_to_io;
+       struct vf_data_storage *vf_info;
+       u16 func_id;
+       int err;
+
+       vf_info = nic_io->vf_infos + HW_VF_ID_TO_OS(vf);
+
+       /* duplicate request, so just return success */
+       if (vf_info->pf_set_mac &&
+           !memcmp(vf_info->vf_mac_addr, mac_addr, ETH_ALEN))
+               return 0;
+
+       vf_info->pf_set_mac = true;
+
+       func_id = hinic_glb_pf_vf_offset(hwdev->hwif) + vf;
+       err = hinic_update_mac(hwdev, vf_info->vf_mac_addr,
+                              mac_addr, 0, func_id);
+       if (err) {
+               vf_info->pf_set_mac = false;
+               return err;
+       }
+
+       memcpy(vf_info->vf_mac_addr, mac_addr, ETH_ALEN);
+
+       return 0;
+}
+
+int hinic_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
+{
+       struct hinic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic_sriov_info *sriov_info;
+       int err;
+
+       sriov_info = &nic_dev->sriov_info;
+       if (!is_valid_ether_addr(mac) || vf >= sriov_info->num_vfs)
+               return -EINVAL;
+
+       err = hinic_set_vf_mac(sriov_info->hwdev, OS_VF_ID_TO_HW(vf), mac);
+       if (err)
+               return err;
+
+       netif_info(nic_dev, drv, netdev, "Setting MAC %pM on VF %d\n", mac, vf);
+       netif_info(nic_dev, drv, netdev, "Reload the VF driver to make this change effective.");
+
+       return 0;
+}
+
+static int hinic_add_vf_vlan(struct hinic_hwdev *hwdev, int vf_id,
+                            u16 vlan, u8 qos)
+{
+       struct hinic_func_to_io *nic_io = &hwdev->func_to_io;
+       int err;
+
+       err = hinic_set_vf_vlan(hwdev, true, vlan, qos, vf_id);
+       if (err)
+               return err;
+
+       nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_vlan = vlan;
+       nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_qos = qos;
+
+       dev_info(&hwdev->hwif->pdev->dev, "Setting VLAN %d, QOS 0x%x on VF %d\n",
+                vlan, qos, HW_VF_ID_TO_OS(vf_id));
+       return 0;
+}
+
+static int hinic_kill_vf_vlan(struct hinic_hwdev *hwdev, int vf_id)
+{
+       struct hinic_func_to_io *nic_io = &hwdev->func_to_io;
+       int err;
+
+       err = hinic_set_vf_vlan(hwdev, false,
+                               nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_vlan,
+                               nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_qos,
+                               vf_id);
+       if (err)
+               return err;
+
+       dev_info(&hwdev->hwif->pdev->dev, "Remove VLAN %d on VF %d\n",
+                nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_vlan,
+                HW_VF_ID_TO_OS(vf_id));
+
+       nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_vlan = 0;
+       nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].pf_qos = 0;
+
+       return 0;
+}
+
+static int hinic_update_mac_vlan(struct hinic_dev *nic_dev, u16 old_vlan,
+                                u16 new_vlan, int vf_id)
+{
+       struct vf_data_storage *vf_info;
+       u16 vlan_id;
+       int err;
+
+       if (!nic_dev || old_vlan >= VLAN_N_VID || new_vlan >= VLAN_N_VID)
+               return -EINVAL;
+
+       vf_info = nic_dev->hwdev->func_to_io.vf_infos + HW_VF_ID_TO_OS(vf_id);
+       if (!vf_info->pf_set_mac)
+               return 0;
+
+       vlan_id = old_vlan;
+       if (vlan_id)
+               vlan_id |= HINIC_ADD_VLAN_IN_MAC;
+
+       err = hinic_port_del_mac(nic_dev, vf_info->vf_mac_addr, vlan_id);
+       if (err) {
+               dev_err(&nic_dev->hwdev->hwif->pdev->dev, "Failed to delete VF %d MAC %pM vlan %d\n",
+                       HW_VF_ID_TO_OS(vf_id), vf_info->vf_mac_addr, old_vlan);
+               return err;
+       }
+
+       vlan_id = new_vlan;
+       if (vlan_id)
+               vlan_id |= HINIC_ADD_VLAN_IN_MAC;
+
+       err = hinic_port_add_mac(nic_dev, vf_info->vf_mac_addr, vlan_id);
+       if (err) {
+               dev_err(&nic_dev->hwdev->hwif->pdev->dev, "Failed to add VF %d MAC %pM vlan %d\n",
+                       HW_VF_ID_TO_OS(vf_id), vf_info->vf_mac_addr, new_vlan);
+               goto out;
+       }
+
+       return 0;
+
+out:
+       vlan_id = old_vlan;
+       if (vlan_id)
+               vlan_id |= HINIC_ADD_VLAN_IN_MAC;
+       hinic_port_add_mac(nic_dev, vf_info->vf_mac_addr, vlan_id);
+
+       return err;
+}
+
+static int set_hw_vf_vlan(struct hinic_dev *nic_dev,
+                         u16 cur_vlanprio, int vf, u16 vlan, u8 qos)
+{
+       u16 old_vlan = cur_vlanprio & VLAN_VID_MASK;
+       int err = 0;
+
+       if (vlan || qos) {
+               if (cur_vlanprio) {
+                       err = hinic_kill_vf_vlan(nic_dev->hwdev,
+                                                OS_VF_ID_TO_HW(vf));
+                       if (err) {
+                               dev_err(&nic_dev->sriov_info.pdev->dev, "Failed to delete vf %d old vlan %d\n",
+                                       vf, old_vlan);
+                               goto out;
+                       }
+               }
+               err = hinic_add_vf_vlan(nic_dev->hwdev,
+                                       OS_VF_ID_TO_HW(vf), vlan, qos);
+               if (err) {
+                       dev_err(&nic_dev->sriov_info.pdev->dev, "Failed to add vf %d new vlan %d\n",
+                               vf, vlan);
+                       goto out;
+               }
+       } else {
+               err = hinic_kill_vf_vlan(nic_dev->hwdev, OS_VF_ID_TO_HW(vf));
+               if (err) {
+                       dev_err(&nic_dev->sriov_info.pdev->dev, "Failed to delete vf %d vlan %d\n",
+                               vf, old_vlan);
+                       goto out;
+               }
+       }
+
+       err = hinic_update_mac_vlan(nic_dev, old_vlan, vlan,
+                                   OS_VF_ID_TO_HW(vf));
+
+out:
+       return err;
+}
+
+int hinic_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos,
+                         __be16 vlan_proto)
+{
+       struct hinic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic_sriov_info *sriov_info;
+       u16 vlanprio, cur_vlanprio;
+
+       sriov_info = &nic_dev->sriov_info;
+       if (vf >= sriov_info->num_vfs || vlan > 4095 || qos > 7)
+               return -EINVAL;
+       if (vlan_proto != htons(ETH_P_8021Q))
+               return -EPROTONOSUPPORT;
+       vlanprio = vlan | qos << HINIC_VLAN_PRIORITY_SHIFT;
+       cur_vlanprio = hinic_vf_info_vlanprio(nic_dev->hwdev,
+                                             OS_VF_ID_TO_HW(vf));
+       /* duplicate request, so just return success */
+       if (vlanprio == cur_vlanprio)
+               return 0;
+
+       return set_hw_vf_vlan(nic_dev, cur_vlanprio, vf, vlan, qos);
+}
+
+static int hinic_set_vf_trust(struct hinic_hwdev *hwdev, u16 vf_id,
+                             bool trust)
+{
+       struct vf_data_storage *vf_infos;
+       struct hinic_func_to_io *nic_io;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       nic_io = &hwdev->func_to_io;
+       vf_infos = nic_io->vf_infos;
+       vf_infos[vf_id].trust = trust;
+
+       return 0;
+}
+
+int hinic_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting)
+{
+       struct hinic_dev *adapter = netdev_priv(netdev);
+       struct hinic_sriov_info *sriov_info;
+       struct hinic_func_to_io *nic_io;
+       bool cur_trust;
+       int err;
+
+       sriov_info = &adapter->sriov_info;
+       nic_io = &adapter->hwdev->func_to_io;
+
+       if (vf >= sriov_info->num_vfs)
+               return -EINVAL;
+
+       cur_trust = nic_io->vf_infos[vf].trust;
+       /* same request, so just return success */
+       if ((setting && cur_trust) || (!setting && !cur_trust))
+               return 0;
+
+       err = hinic_set_vf_trust(adapter->hwdev, vf, setting);
+       if (!err)
+               dev_info(&sriov_info->pdev->dev, "Set VF %d trusted %s succeed\n",
+                        vf, setting ? "on" : "off");
+       else
+               dev_err(&sriov_info->pdev->dev, "Failed set VF %d trusted %s\n",
+                       vf, setting ? "on" : "off");
+
+       return err;
+}
+
+/* pf receive message from vf */
+static int nic_pf_mbox_handler(void *hwdev, u16 vf_id, u8 cmd, void *buf_in,
+                              u16 in_size, void *buf_out, u16 *out_size)
+{
+       struct vf_cmd_msg_handle *vf_msg_handle;
+       struct hinic_hwdev *dev = hwdev;
+       struct hinic_func_to_io *nic_io;
+       struct hinic_pfhwdev *pfhwdev;
+       int err = 0;
+       u32 i;
+
+       if (!hwdev)
+               return -EFAULT;
+
+       pfhwdev = container_of(dev, struct hinic_pfhwdev, hwdev);
+       nic_io = &dev->func_to_io;
+       for (i = 0; i < ARRAY_SIZE(nic_vf_cmd_msg_handler); i++) {
+               vf_msg_handle = &nic_vf_cmd_msg_handler[i];
+               if (cmd == vf_msg_handle->cmd &&
+                   vf_msg_handle->cmd_msg_handler) {
+                       err = vf_msg_handle->cmd_msg_handler(hwdev, vf_id,
+                                                            buf_in, in_size,
+                                                            buf_out,
+                                                            out_size);
+                       break;
+               }
+       }
+       if (i == ARRAY_SIZE(nic_vf_cmd_msg_handler))
+               err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC,
+                                       cmd, buf_in, in_size, buf_out,
+                                       out_size, HINIC_MGMT_MSG_SYNC);
+
+       if (err &&  err != HINIC_MBOX_PF_BUSY_ACTIVE_FW)
+               dev_err(&nic_io->hwif->pdev->dev, "PF receive VF L2NIC cmd: %d process error, err:%d\n",
+                       cmd, err);
+       return err;
+}
+
+static int cfg_mbx_pf_proc_vf_msg(void *hwdev, u16 vf_id, u8 cmd, void *buf_in,
+                                 u16 in_size, void *buf_out, u16 *out_size)
+{
+       struct hinic_dev_cap *dev_cap = buf_out;
+       struct hinic_hwdev *dev = hwdev;
+       struct hinic_cap *cap;
+
+       cap = &dev->nic_cap;
+       memset(dev_cap, 0, sizeof(*dev_cap));
+
+       dev_cap->max_vf = cap->max_vf;
+       dev_cap->max_sqs = cap->max_vf_qps;
+       dev_cap->max_rqs = cap->max_vf_qps;
+
+       *out_size = sizeof(*dev_cap);
+
+       return 0;
+}
+
+static int hinic_init_vf_infos(struct hinic_func_to_io *nic_io, u16 vf_id)
+{
+       struct vf_data_storage *vf_infos = nic_io->vf_infos;
+
+       if (set_vf_link_state > HINIC_IFLA_VF_LINK_STATE_DISABLE) {
+               dev_warn(&nic_io->hwif->pdev->dev, "Module Parameter set_vf_link_state value %d is out of range, resetting to %d\n",
+                        set_vf_link_state, HINIC_IFLA_VF_LINK_STATE_AUTO);
+               set_vf_link_state = HINIC_IFLA_VF_LINK_STATE_AUTO;
+       }
+
+       switch (set_vf_link_state) {
+       case HINIC_IFLA_VF_LINK_STATE_AUTO:
+               vf_infos[vf_id].link_forced = false;
+               break;
+       case HINIC_IFLA_VF_LINK_STATE_ENABLE:
+               vf_infos[vf_id].link_forced = true;
+               vf_infos[vf_id].link_up = true;
+               break;
+       case HINIC_IFLA_VF_LINK_STATE_DISABLE:
+               vf_infos[vf_id].link_forced = true;
+               vf_infos[vf_id].link_up = false;
+               break;
+       default:
+               dev_err(&nic_io->hwif->pdev->dev, "Invalid input parameter set_vf_link_state: %d\n",
+                       set_vf_link_state);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void hinic_clear_vf_infos(struct hinic_dev *nic_dev, u16 vf_id)
+{
+       struct vf_data_storage *vf_infos;
+       u16 func_id;
+
+       func_id = hinic_glb_pf_vf_offset(nic_dev->hwdev->hwif) + vf_id;
+       vf_infos = nic_dev->hwdev->func_to_io.vf_infos + HW_VF_ID_TO_OS(vf_id);
+       if (vf_infos->pf_set_mac)
+               hinic_port_del_mac(nic_dev, vf_infos->vf_mac_addr, 0);
+
+       if (hinic_vf_info_vlanprio(nic_dev->hwdev, vf_id))
+               hinic_kill_vf_vlan(nic_dev->hwdev, vf_id);
+
+       if (vf_infos->trust)
+               hinic_set_vf_trust(nic_dev->hwdev, vf_id, false);
+
+       memset(vf_infos, 0, sizeof(*vf_infos));
+       /* set vf_infos to default */
+       hinic_init_vf_infos(&nic_dev->hwdev->func_to_io, HW_VF_ID_TO_OS(vf_id));
+}
+
+static int hinic_deinit_vf_hw(struct hinic_sriov_info *sriov_info,
+                             u16 start_vf_id, u16 end_vf_id)
+{
+       struct hinic_dev *nic_dev;
+       u16 func_idx, idx;
+
+       nic_dev = container_of(sriov_info, struct hinic_dev, sriov_info);
+
+       for (idx = start_vf_id; idx <= end_vf_id; idx++) {
+               func_idx = hinic_glb_pf_vf_offset(nic_dev->hwdev->hwif) + idx;
+               hinic_set_wq_page_size(nic_dev->hwdev, func_idx,
+                                      HINIC_HW_WQ_PAGE_SIZE);
+               hinic_clear_vf_infos(nic_dev, idx);
+       }
+
+       return 0;
+}
+
+int hinic_vf_func_init(struct hinic_hwdev *hwdev)
+{
+       struct hinic_register_vf register_info = {0};
+       u16 out_size = sizeof(register_info);
+       struct hinic_func_to_io *nic_io;
+       int err = 0;
+       u32 size, i;
+
+       nic_io = &hwdev->func_to_io;
+
+       if (HINIC_IS_VF(hwdev->hwif)) {
+               err = hinic_mbox_to_pf(hwdev, HINIC_MOD_L2NIC,
+                                      HINIC_PORT_CMD_VF_REGISTER,
+                                      &register_info, sizeof(register_info),
+                                      &register_info, &out_size, 0);
+               if (err || register_info.status || !out_size) {
+                       dev_err(&hwdev->hwif->pdev->dev,
+                               "Failed to register VF, err: %d, status: 0x%x, out size: 0x%x\n",
+                               err, register_info.status, out_size);
+                       hinic_unregister_vf_mbox_cb(hwdev, HINIC_MOD_L2NIC);
+                       return -EIO;
+               }
+       } else {
+               err = hinic_register_pf_mbox_cb(hwdev, HINIC_MOD_CFGM,
+                                               cfg_mbx_pf_proc_vf_msg);
+               if (err) {
+                       dev_err(&hwdev->hwif->pdev->dev,
+                               "Register PF mailbox callback failed\n");
+                       return err;
+               }
+               nic_io->max_vfs = hwdev->nic_cap.max_vf;
+               size = sizeof(*nic_io->vf_infos) * nic_io->max_vfs;
+               if (size != 0) {
+                       nic_io->vf_infos = kzalloc(size, GFP_KERNEL);
+                       if (!nic_io->vf_infos) {
+                               err = -ENOMEM;
+                               goto out_free_nic_io;
+                       }
+
+                       for (i = 0; i < nic_io->max_vfs; i++) {
+                               err = hinic_init_vf_infos(nic_io, i);
+                               if (err)
+                                       goto err_init_vf_infos;
+                       }
+
+                       err = hinic_register_pf_mbox_cb(hwdev, HINIC_MOD_L2NIC,
+                                                       nic_pf_mbox_handler);
+                       if (err)
+                               goto err_register_pf_mbox_cb;
+               }
+       }
+
+       return 0;
+
+err_register_pf_mbox_cb:
+err_init_vf_infos:
+       kfree(nic_io->vf_infos);
+out_free_nic_io:
+       return err;
+}
+
+void hinic_vf_func_free(struct hinic_hwdev *hwdev)
+{
+       struct hinic_register_vf unregister = {0};
+       u16 out_size = sizeof(unregister);
+       int err;
+
+       if (HINIC_IS_VF(hwdev->hwif)) {
+               err = hinic_mbox_to_pf(hwdev, HINIC_MOD_L2NIC,
+                                      HINIC_PORT_CMD_VF_UNREGISTER,
+                                      &unregister, sizeof(unregister),
+                                      &unregister, &out_size, 0);
+               if (err || !out_size || unregister.status)
+                       dev_err(&hwdev->hwif->pdev->dev, "Failed to unregister VF, err: %d, status: 0x%x, out_size: 0x%x\n",
+                               err, unregister.status, out_size);
+       } else {
+               if (hwdev->func_to_io.vf_infos) {
+                       hinic_unregister_pf_mbox_cb(hwdev, HINIC_MOD_L2NIC);
+                       kfree(hwdev->func_to_io.vf_infos);
+               }
+       }
+}
+
+static int hinic_init_vf_hw(struct hinic_hwdev *hwdev, u16 start_vf_id,
+                           u16 end_vf_id)
+{
+       u16 i, func_idx;
+       int err;
+
+       /* vf use 256K as default wq page size, and can't change it */
+       for (i = start_vf_id; i <= end_vf_id; i++) {
+               func_idx = hinic_glb_pf_vf_offset(hwdev->hwif) + i;
+               err = hinic_set_wq_page_size(hwdev, func_idx,
+                                            HINIC_DEFAULT_WQ_PAGE_SIZE);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+int hinic_pci_sriov_disable(struct pci_dev *pdev)
+{
+       struct hinic_sriov_info *sriov_info;
+       u16 tmp_vfs;
+
+       sriov_info = hinic_get_sriov_info_by_pcidev(pdev);
+       /* if SR-IOV is already disabled then nothing will be done */
+       if (!sriov_info->sriov_enabled)
+               return 0;
+
+       set_bit(HINIC_SRIOV_DISABLE, &sriov_info->state);
+
+       /* 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(sriov_info->pdev)) {
+               clear_bit(HINIC_SRIOV_DISABLE, &sriov_info->state);
+               dev_warn(&pdev->dev, "Unloading driver while VFs are assigned - VFs will not be deallocated\n");
+               return -EPERM;
+       }
+       sriov_info->sriov_enabled = false;
+
+       /* disable iov and allow time for transactions to clear */
+       pci_disable_sriov(sriov_info->pdev);
+
+       tmp_vfs = (u16)sriov_info->num_vfs;
+       sriov_info->num_vfs = 0;
+       hinic_deinit_vf_hw(sriov_info, OS_VF_ID_TO_HW(0),
+                          OS_VF_ID_TO_HW(tmp_vfs - 1));
+
+       clear_bit(HINIC_SRIOV_DISABLE, &sriov_info->state);
+
+       return 0;
+}
+
+int hinic_pci_sriov_enable(struct pci_dev *pdev, int num_vfs)
+{
+       struct hinic_sriov_info *sriov_info;
+       int err;
+
+       sriov_info = hinic_get_sriov_info_by_pcidev(pdev);
+
+       if (test_and_set_bit(HINIC_SRIOV_ENABLE, &sriov_info->state)) {
+               dev_err(&pdev->dev,
+                       "SR-IOV enable in process, please wait, num_vfs %d\n",
+                       num_vfs);
+               return -EPERM;
+       }
+
+       err = hinic_init_vf_hw(sriov_info->hwdev, OS_VF_ID_TO_HW(0),
+                              OS_VF_ID_TO_HW((u16)num_vfs - 1));
+       if (err) {
+               dev_err(&sriov_info->pdev->dev,
+                       "Failed to init vf in hardware before enable sriov, error %d\n",
+                       err);
+               clear_bit(HINIC_SRIOV_ENABLE, &sriov_info->state);
+               return err;
+       }
+
+       err = pci_enable_sriov(sriov_info->pdev, num_vfs);
+       if (err) {
+               dev_err(&pdev->dev,
+                       "Failed to enable SR-IOV, error %d\n", err);
+               clear_bit(HINIC_SRIOV_ENABLE, &sriov_info->state);
+               return err;
+       }
+
+       sriov_info->sriov_enabled = true;
+       sriov_info->num_vfs = num_vfs;
+       clear_bit(HINIC_SRIOV_ENABLE, &sriov_info->state);
+
+       return num_vfs;
+}
+
+int hinic_pci_sriov_configure(struct pci_dev *dev, int num_vfs)
+{
+       struct hinic_sriov_info *sriov_info;
+
+       sriov_info = hinic_get_sriov_info_by_pcidev(dev);
+
+       if (test_bit(HINIC_FUNC_REMOVE, &sriov_info->state))
+               return -EBUSY;
+
+       if (!num_vfs)
+               return hinic_pci_sriov_disable(dev);
+       else
+               return hinic_pci_sriov_enable(dev, num_vfs);
+}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_sriov.h b/drivers/net/ethernet/huawei/hinic/hinic_sriov.h
new file mode 100644 (file)
index 0000000..64affc7
--- /dev/null
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Huawei HiNIC PCI Express Linux driver
+ * Copyright(c) 2017 Huawei Technologies Co., Ltd
+ */
+
+#ifndef HINIC_SRIOV_H
+#define HINIC_SRIOV_H
+
+#include "hinic_hw_dev.h"
+
+#define OS_VF_ID_TO_HW(os_vf_id) ((os_vf_id) + 1)
+#define HW_VF_ID_TO_OS(hw_vf_id) ((hw_vf_id) - 1)
+
+enum hinic_sriov_state {
+       HINIC_SRIOV_DISABLE,
+       HINIC_SRIOV_ENABLE,
+       HINIC_FUNC_REMOVE,
+};
+
+enum {
+       HINIC_IFLA_VF_LINK_STATE_AUTO,  /* link state of the uplink */
+       HINIC_IFLA_VF_LINK_STATE_ENABLE,        /* link always up */
+       HINIC_IFLA_VF_LINK_STATE_DISABLE,       /* link always down */
+};
+
+struct hinic_sriov_info {
+       struct pci_dev *pdev;
+       struct hinic_hwdev *hwdev;
+       bool sriov_enabled;
+       unsigned int num_vfs;
+       unsigned long state;
+};
+
+struct vf_data_storage {
+       u8 vf_mac_addr[ETH_ALEN];
+       bool registered;
+       bool pf_set_mac;
+       u16 pf_vlan;
+       u8 pf_qos;
+       u32 max_rate;
+       u32 min_rate;
+
+       bool link_forced;
+       bool link_up;           /* only valid if VF link is forced */
+       bool spoofchk;
+       bool trust;
+};
+
+struct hinic_register_vf {
+       u8      status;
+       u8      version;
+       u8      rsvd0[6];
+};
+
+struct hinic_port_mac_update {
+       u8      status;
+       u8      version;
+       u8      rsvd0[6];
+
+       u16     func_id;
+       u16     vlan_id;
+       u16     rsvd1;
+       u8      old_mac[ETH_ALEN];
+       u16     rsvd2;
+       u8      new_mac[ETH_ALEN];
+};
+
+struct hinic_vf_vlan_config {
+       u8 status;
+       u8 version;
+       u8 rsvd0[6];
+
+       u16 func_id;
+       u16 vlan_id;
+       u8  qos;
+       u8  rsvd1[7];
+};
+
+int hinic_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac);
+
+int hinic_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos,
+                         __be16 vlan_proto);
+
+int hinic_ndo_get_vf_config(struct net_device *netdev,
+                           int vf, struct ifla_vf_info *ivi);
+
+int hinic_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting);
+
+void hinic_notify_all_vfs_link_changed(struct hinic_hwdev *hwdev,
+                                      u8 link_status);
+
+int hinic_pci_sriov_disable(struct pci_dev *dev);
+
+int hinic_pci_sriov_enable(struct pci_dev *dev, int num_vfs);
+
+int hinic_vf_func_init(struct hinic_hwdev *hwdev);
+
+void hinic_vf_func_free(struct hinic_hwdev *hwdev);
+
+int hinic_pci_sriov_configure(struct pci_dev *dev, int num_vfs);
+
+#endif
index 3650164..4c66a0b 100644 (file)
@@ -673,9 +673,11 @@ static int free_tx_poll(struct napi_struct *napi, int budget)
 
        if (pkts < budget) {
                napi_complete(napi);
-               hinic_hwdev_set_msix_state(nic_dev->hwdev,
-                                          sq->msix_entry,
-                                          HINIC_MSIX_ENABLE);
+               if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
+                       hinic_hwdev_set_msix_state(nic_dev->hwdev,
+                                                  sq->msix_entry,
+                                                  HINIC_MSIX_ENABLE);
+
                return pkts;
        }
 
@@ -701,10 +703,11 @@ static irqreturn_t tx_irq(int irq, void *data)
 
        nic_dev = netdev_priv(txq->netdev);
 
-       /* Disable the interrupt until napi will be completed */
-       hinic_hwdev_set_msix_state(nic_dev->hwdev,
-                                  txq->sq->msix_entry,
-                                  HINIC_MSIX_DISABLE);
+       if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
+               /* Disable the interrupt until napi will be completed */
+               hinic_hwdev_set_msix_state(nic_dev->hwdev,
+                                          txq->sq->msix_entry,
+                                          HINIC_MSIX_DISABLE);
 
        hinic_hwdev_msix_cnt_set(nic_dev->hwdev, txq->sq->msix_entry);
 
index 4bd3324..3de549c 100644 (file)
@@ -2189,7 +2189,8 @@ static void __ibmvnic_reset(struct work_struct *work)
                                rc = do_hard_reset(adapter, rwi, reset_state);
                                rtnl_unlock();
                        }
-               } else {
+               } else if (!(rwi->reset_reason == VNIC_RESET_FATAL &&
+                               adapter->from_passive_init)) {
                        rc = do_reset(adapter, rwi, reset_state);
                }
                kfree(rwi);
index f710335..05bc6e2 100644 (file)
@@ -1476,7 +1476,7 @@ static bool e1000_check_64k_bound(struct e1000_adapter *adapter, void *start,
        if (hw->mac_type == e1000_82545 ||
            hw->mac_type == e1000_ce4100 ||
            hw->mac_type == e1000_82546) {
-               return ((begin ^ (end - 1)) >> 16) != 0 ? false : true;
+               return ((begin ^ (end - 1)) >> 16) == 0;
        }
 
        return true;
@@ -3136,8 +3136,9 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
                hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
                if (skb->data_len && hdr_len == len) {
                        switch (hw->mac_type) {
+                       case e1000_82544: {
                                unsigned int pull_size;
-                       case e1000_82544:
+
                                /* Make sure we have room to chop off 4 bytes,
                                 * and that the end alignment will work out to
                                 * this hardware's requirements
@@ -3158,6 +3159,7 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
                                }
                                len = skb_headlen(skb);
                                break;
+                       }
                        default:
                                /* do nothing */
                                break;
index 177c6da..e0b0748 100644 (file)
@@ -6404,6 +6404,31 @@ static void e1000e_s0ix_entry_flow(struct e1000_adapter *adapter)
        mac_data |= BIT(3);
        ew32(CTRL_EXT, mac_data);
 
+       /* Disable disconnected cable conditioning for Power Gating */
+       mac_data = er32(DPGFR);
+       mac_data |= BIT(2);
+       ew32(DPGFR, mac_data);
+
+       /* Don't wake from dynamic Power Gating with clock request */
+       mac_data = er32(FEXTNVM12);
+       mac_data |= BIT(12);
+       ew32(FEXTNVM12, mac_data);
+
+       /* Ungate PGCB clock */
+       mac_data = er32(FEXTNVM9);
+       mac_data |= BIT(28);
+       ew32(FEXTNVM9, mac_data);
+
+       /* Enable K1 off to enable mPHY Power Gating */
+       mac_data = er32(FEXTNVM6);
+       mac_data |= BIT(31);
+       ew32(FEXTNVM12, mac_data);
+
+       /* Enable mPHY power gating for any link and speed */
+       mac_data = er32(FEXTNVM8);
+       mac_data |= BIT(9);
+       ew32(FEXTNVM8, mac_data);
+
        /* Enable the Dynamic Clock Gating in the DMA and MAC */
        mac_data = er32(CTRL_EXT);
        mac_data |= E1000_CTRL_EXT_DMA_DYN_CLK_EN;
@@ -6433,6 +6458,35 @@ static void e1000e_s0ix_exit_flow(struct e1000_adapter *adapter)
        mac_data |= BIT(0);
        ew32(FEXTNVM7, mac_data);
 
+       /* Disable mPHY power gating for any link and speed */
+       mac_data = er32(FEXTNVM8);
+       mac_data &= ~BIT(9);
+       ew32(FEXTNVM8, mac_data);
+
+       /* Disable K1 off */
+       mac_data = er32(FEXTNVM6);
+       mac_data &= ~BIT(31);
+       ew32(FEXTNVM12, mac_data);
+
+       /* Disable Ungate PGCB clock */
+       mac_data = er32(FEXTNVM9);
+       mac_data &= ~BIT(28);
+       ew32(FEXTNVM9, mac_data);
+
+       /* Cancel not waking from dynamic
+        * Power Gating with clock request
+        */
+       mac_data = er32(FEXTNVM12);
+       mac_data &= ~BIT(12);
+       ew32(FEXTNVM12, mac_data);
+
+       /* Cancel disable disconnected cable conditioning
+        * for Power Gating
+        */
+       mac_data = er32(DPGFR);
+       mac_data &= ~BIT(2);
+       ew32(DPGFR, mac_data);
+
        /* Disable Dynamic Power Gating */
        mac_data = er32(CTRL_EXT);
        mac_data &= 0xFFFFFFF7;
index df59fd1..8165ba2 100644 (file)
 #define E1000_FEXTNVM5 0x00014 /* Future Extended NVM 5 - RW */
 #define E1000_FEXTNVM6 0x00010 /* Future Extended NVM 6 - RW */
 #define E1000_FEXTNVM7 0x000E4 /* Future Extended NVM 7 - RW */
+#define E1000_FEXTNVM8 0x5BB0  /* Future Extended NVM 8 - RW */
 #define E1000_FEXTNVM9 0x5BB4  /* Future Extended NVM 9 - RW */
 #define E1000_FEXTNVM11        0x5BBC  /* Future Extended NVM 11 - RW */
+#define E1000_FEXTNVM12        0x5BC0  /* Future Extended NVM 12 - RW */
 #define E1000_PCIEANACFG       0x00F18 /* PCIE Analog Config */
+#define E1000_DPGFR    0x00FAC /* Dynamic Power Gate Force Control Register */
 #define E1000_FCT      0x00030 /* Flow Control Type - RW */
 #define E1000_VET      0x00038 /* VLAN Ether Type - RW */
 #define E1000_ICR      0x000C0 /* Interrupt Cause Read - R/clr */
index 8c3e753..2a037ec 100644 (file)
@@ -1611,7 +1611,7 @@ static int i40e_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed,
                }
        }
        if (lut) {
-               bool pf_lut = vsi->type == I40E_VSI_MAIN ? true : false;
+               bool pf_lut = vsi->type == I40E_VSI_MAIN;
 
                ret = i40e_aq_set_rss_lut(hw, vsi->id, pf_lut, lut, lut_size);
                if (ret) {
@@ -11436,7 +11436,7 @@ static int i40e_get_rss_aq(struct i40e_vsi *vsi, const u8 *seed,
        }
 
        if (lut) {
-               bool pf_lut = vsi->type == I40E_VSI_MAIN ? true : false;
+               bool pf_lut = vsi->type == I40E_VSI_MAIN;
 
                ret = i40e_aq_get_rss_lut(hw, vsi->id, pf_lut, lut, lut_size);
                if (ret) {
index 42bac3e..e7a2671 100644 (file)
@@ -2962,8 +2962,10 @@ ice_add_prof(struct ice_hw *hw, enum ice_block blk, u64 id, u8 ptypes[],
 
        /* add profile info */
        prof = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*prof), GFP_KERNEL);
-       if (!prof)
+       if (!prof) {
+               status = ICE_ERR_NO_MEMORY;
                goto err_ice_add_prof;
+       }
 
        prof->profile_cookie = id;
        prof->prof_id = prof_id;
index e3c164c..3652f21 100644 (file)
@@ -8,4 +8,4 @@
 obj-$(CONFIG_IGC) += igc.o
 
 igc-objs := igc_main.o igc_mac.o igc_i225.o igc_base.o igc_nvm.o igc_phy.o \
-igc_ethtool.o igc_ptp.o igc_dump.o
+igc_ethtool.o igc_ptp.o igc_dump.o igc_tsn.o
index a1f845a..8ddc394 100644 (file)
 /* forward declaration */
 void igc_set_ethtool_ops(struct net_device *);
 
-struct igc_adapter;
-struct igc_ring;
+/* Transmit and receive queues */
+#define IGC_MAX_RX_QUEUES              4
+#define IGC_MAX_TX_QUEUES              4
+
+#define MAX_Q_VECTORS                  8
+#define MAX_STD_JUMBO_FRAME_SIZE       9216
+
+#define MAX_ETYPE_FILTER               (4 - 1)
+#define IGC_RETA_SIZE                  128
+
+struct igc_tx_queue_stats {
+       u64 packets;
+       u64 bytes;
+       u64 restart_queue;
+       u64 restart_queue2;
+};
+
+struct igc_rx_queue_stats {
+       u64 packets;
+       u64 bytes;
+       u64 drops;
+       u64 csum_err;
+       u64 alloc_failed;
+};
+
+struct igc_rx_packet_stats {
+       u64 ipv4_packets;      /* IPv4 headers processed */
+       u64 ipv4e_packets;     /* IPv4E headers with extensions processed */
+       u64 ipv6_packets;      /* IPv6 headers processed */
+       u64 ipv6e_packets;     /* IPv6E headers with extensions processed */
+       u64 tcp_packets;       /* TCP headers processed */
+       u64 udp_packets;       /* UDP headers processed */
+       u64 sctp_packets;      /* SCTP headers processed */
+       u64 nfs_packets;       /* NFS headers processe */
+       u64 other_packets;
+};
+
+struct igc_ring_container {
+       struct igc_ring *ring;          /* pointer to linked list of rings */
+       unsigned int total_bytes;       /* total bytes processed this int */
+       unsigned int total_packets;     /* total packets processed this int */
+       u16 work_limit;                 /* total work allowed per interrupt */
+       u8 count;                       /* total number of rings in vector */
+       u8 itr;                         /* current ITR setting for ring */
+};
+
+struct igc_ring {
+       struct igc_q_vector *q_vector;  /* backlink to q_vector */
+       struct net_device *netdev;      /* back pointer to net_device */
+       struct device *dev;             /* device for dma mapping */
+       union {                         /* array of buffer info structs */
+               struct igc_tx_buffer *tx_buffer_info;
+               struct igc_rx_buffer *rx_buffer_info;
+       };
+       void *desc;                     /* descriptor ring memory */
+       unsigned long flags;            /* ring specific flags */
+       void __iomem *tail;             /* pointer to ring tail register */
+       dma_addr_t dma;                 /* phys address of the ring */
+       unsigned int size;              /* length of desc. ring in bytes */
+
+       u16 count;                      /* number of desc. in the ring */
+       u8 queue_index;                 /* logical index of the ring*/
+       u8 reg_idx;                     /* physical index of the ring */
+       bool launchtime_enable;         /* true if LaunchTime is enabled */
+
+       u32 start_time;
+       u32 end_time;
+
+       /* everything past this point are written often */
+       u16 next_to_clean;
+       u16 next_to_use;
+       u16 next_to_alloc;
+
+       union {
+               /* TX */
+               struct {
+                       struct igc_tx_queue_stats tx_stats;
+                       struct u64_stats_sync tx_syncp;
+                       struct u64_stats_sync tx_syncp2;
+               };
+               /* RX */
+               struct {
+                       struct igc_rx_queue_stats rx_stats;
+                       struct igc_rx_packet_stats pkt_stats;
+                       struct u64_stats_sync rx_syncp;
+                       struct sk_buff *skb;
+               };
+       };
+} ____cacheline_internodealigned_in_smp;
+
+/* Board specific private data structure */
+struct igc_adapter {
+       struct net_device *netdev;
+
+       unsigned long state;
+       unsigned int flags;
+       unsigned int num_q_vectors;
+
+       struct msix_entry *msix_entries;
+
+       /* TX */
+       u16 tx_work_limit;
+       u32 tx_timeout_count;
+       int num_tx_queues;
+       struct igc_ring *tx_ring[IGC_MAX_TX_QUEUES];
+
+       /* RX */
+       int num_rx_queues;
+       struct igc_ring *rx_ring[IGC_MAX_RX_QUEUES];
+
+       struct timer_list watchdog_timer;
+       struct timer_list dma_err_timer;
+       struct timer_list phy_info_timer;
+
+       u32 wol;
+       u32 en_mng_pt;
+       u16 link_speed;
+       u16 link_duplex;
+
+       u8 port_num;
+
+       u8 __iomem *io_addr;
+       /* Interrupt Throttle Rate */
+       u32 rx_itr_setting;
+       u32 tx_itr_setting;
+
+       struct work_struct reset_task;
+       struct work_struct watchdog_task;
+       struct work_struct dma_err_task;
+       bool fc_autoneg;
+
+       u8 tx_timeout_factor;
+
+       int msg_enable;
+       u32 max_frame_size;
+       u32 min_frame_size;
+
+       ktime_t base_time;
+       ktime_t cycle_time;
+
+       /* OS defined structs */
+       struct pci_dev *pdev;
+       /* lock for statistics */
+       spinlock_t stats64_lock;
+       struct rtnl_link_stats64 stats64;
+
+       /* structs defined in igc_hw.h */
+       struct igc_hw hw;
+       struct igc_hw_stats stats;
+
+       struct igc_q_vector *q_vector[MAX_Q_VECTORS];
+       u32 eims_enable_mask;
+       u32 eims_other;
+
+       u16 tx_ring_count;
+       u16 rx_ring_count;
+
+       u32 tx_hwtstamp_timeouts;
+       u32 tx_hwtstamp_skipped;
+       u32 rx_hwtstamp_cleared;
+
+       u32 rss_queues;
+       u32 rss_indir_tbl_init;
+
+       /* RX network flow classification support */
+       struct hlist_head nfc_filter_list;
+       unsigned int nfc_filter_count;
+
+       /* lock for RX network flow classification filter */
+       spinlock_t nfc_lock;
+       bool etype_bitmap[MAX_ETYPE_FILTER];
+
+       struct igc_mac_addr *mac_table;
+
+       u8 rss_indir_tbl[IGC_RETA_SIZE];
+
+       unsigned long link_check_timeout;
+       struct igc_info ei;
+
+       struct ptp_clock *ptp_clock;
+       struct ptp_clock_info ptp_caps;
+       struct work_struct ptp_tx_work;
+       struct sk_buff *ptp_tx_skb;
+       struct hwtstamp_config tstamp_config;
+       unsigned long ptp_tx_start;
+       unsigned long last_rx_ptp_check;
+       unsigned long last_rx_timestamp;
+       unsigned int ptp_flags;
+       /* System time value lock */
+       spinlock_t tmreg_lock;
+       struct cyclecounter cc;
+       struct timecounter tc;
+};
 
 void igc_up(struct igc_adapter *adapter);
 void igc_down(struct igc_adapter *adapter);
@@ -36,10 +227,10 @@ void igc_write_rss_indir_tbl(struct igc_adapter *adapter);
 bool igc_has_link(struct igc_adapter *adapter);
 void igc_reset(struct igc_adapter *adapter);
 int igc_set_spd_dplx(struct igc_adapter *adapter, u32 spd, u8 dplx);
-int igc_add_mac_steering_filter(struct igc_adapter *adapter,
-                               const u8 *addr, u8 queue, u8 flags);
-int igc_del_mac_steering_filter(struct igc_adapter *adapter,
-                               const u8 *addr, u8 queue, u8 flags);
+int igc_add_mac_filter(struct igc_adapter *adapter, const u8 *addr,
+                      const s8 queue, const u8 flags);
+int igc_del_mac_filter(struct igc_adapter *adapter, const u8 *addr,
+                      const u8 flags);
 void igc_update_stats(struct igc_adapter *adapter);
 
 /* igc_dump declarations */
@@ -50,14 +241,10 @@ extern char igc_driver_name[];
 extern char igc_driver_version[];
 
 #define IGC_REGS_LEN                   740
-#define IGC_RETA_SIZE                  128
 
 /* flags controlling PTP/1588 function */
 #define IGC_PTP_ENABLED                BIT(0)
 
-/* Interrupt defines */
-#define IGC_START_ITR                  648 /* ~6000 ints/sec */
-
 /* Flags definitions */
 #define IGC_FLAG_HAS_MSI               BIT(0)
 #define IGC_FLAG_QUEUE_PAIRS           BIT(3)
@@ -70,6 +257,7 @@ extern char igc_driver_version[];
 #define IGC_FLAG_HAS_MSIX              BIT(13)
 #define IGC_FLAG_VLAN_PROMISC          BIT(15)
 #define IGC_FLAG_RX_LEGACY             BIT(16)
+#define IGC_FLAG_TSN_QBV_ENABLED       BIT(17)
 
 #define IGC_FLAG_RSS_FIELD_IPV4_UDP    BIT(6)
 #define IGC_FLAG_RSS_FIELD_IPV6_UDP    BIT(7)
@@ -78,6 +266,7 @@ extern char igc_driver_version[];
 #define IGC_MRQC_RSS_FIELD_IPV4_UDP    0x00400000
 #define IGC_MRQC_RSS_FIELD_IPV6_UDP    0x00800000
 
+/* Interrupt defines */
 #define IGC_START_ITR                  648 /* ~6000 ints/sec */
 #define IGC_4K_ITR                     980
 #define IGC_20K_ITR                    196
@@ -99,13 +288,6 @@ extern char igc_driver_version[];
 #define IGC_MIN_RXD            80
 #define IGC_MAX_RXD            4096
 
-/* Transmit and receive queues */
-#define IGC_MAX_RX_QUEUES              4
-#define IGC_MAX_TX_QUEUES              4
-
-#define MAX_Q_VECTORS                  8
-#define MAX_STD_JUMBO_FRAME_SIZE       9216
-
 /* Supported Rx Buffer Sizes */
 #define IGC_RXBUFFER_256               256
 #define IGC_RXBUFFER_2048              2048
@@ -232,83 +414,6 @@ struct igc_rx_buffer {
        __u16 pagecnt_bias;
 };
 
-struct igc_tx_queue_stats {
-       u64 packets;
-       u64 bytes;
-       u64 restart_queue;
-       u64 restart_queue2;
-};
-
-struct igc_rx_queue_stats {
-       u64 packets;
-       u64 bytes;
-       u64 drops;
-       u64 csum_err;
-       u64 alloc_failed;
-};
-
-struct igc_rx_packet_stats {
-       u64 ipv4_packets;      /* IPv4 headers processed */
-       u64 ipv4e_packets;     /* IPv4E headers with extensions processed */
-       u64 ipv6_packets;      /* IPv6 headers processed */
-       u64 ipv6e_packets;     /* IPv6E headers with extensions processed */
-       u64 tcp_packets;       /* TCP headers processed */
-       u64 udp_packets;       /* UDP headers processed */
-       u64 sctp_packets;      /* SCTP headers processed */
-       u64 nfs_packets;       /* NFS headers processe */
-       u64 other_packets;
-};
-
-struct igc_ring_container {
-       struct igc_ring *ring;          /* pointer to linked list of rings */
-       unsigned int total_bytes;       /* total bytes processed this int */
-       unsigned int total_packets;     /* total packets processed this int */
-       u16 work_limit;                 /* total work allowed per interrupt */
-       u8 count;                       /* total number of rings in vector */
-       u8 itr;                         /* current ITR setting for ring */
-};
-
-struct igc_ring {
-       struct igc_q_vector *q_vector;  /* backlink to q_vector */
-       struct net_device *netdev;      /* back pointer to net_device */
-       struct device *dev;             /* device for dma mapping */
-       union {                         /* array of buffer info structs */
-               struct igc_tx_buffer *tx_buffer_info;
-               struct igc_rx_buffer *rx_buffer_info;
-       };
-       void *desc;                     /* descriptor ring memory */
-       unsigned long flags;            /* ring specific flags */
-       void __iomem *tail;             /* pointer to ring tail register */
-       dma_addr_t dma;                 /* phys address of the ring */
-       unsigned int size;              /* length of desc. ring in bytes */
-
-       u16 count;                      /* number of desc. in the ring */
-       u8 queue_index;                 /* logical index of the ring*/
-       u8 reg_idx;                     /* physical index of the ring */
-       bool launchtime_enable;         /* true if LaunchTime is enabled */
-
-       /* everything past this point are written often */
-       u16 next_to_clean;
-       u16 next_to_use;
-       u16 next_to_alloc;
-
-       union {
-               /* TX */
-               struct {
-                       struct igc_tx_queue_stats tx_stats;
-                       struct u64_stats_sync tx_syncp;
-                       struct u64_stats_sync tx_syncp2;
-               };
-               /* RX */
-               struct {
-                       struct igc_rx_queue_stats rx_stats;
-                       struct igc_rx_packet_stats pkt_stats;
-                       struct u64_stats_sync rx_syncp;
-                       struct sk_buff *skb;
-               };
-       };
-} ____cacheline_internodealigned_in_smp;
-
 struct igc_q_vector {
        struct igc_adapter *adapter;    /* backlink */
        void __iomem *itr_register;
@@ -329,8 +434,6 @@ struct igc_q_vector {
        struct igc_ring ring[] ____cacheline_internodealigned_in_smp;
 };
 
-#define MAX_ETYPE_FILTER               (4 - 1)
-
 enum igc_filter_match_flags {
        IGC_FILTER_FLAG_ETHER_TYPE =    0x1,
        IGC_FILTER_FLAG_VLAN_TCI   =    0x2,
@@ -363,119 +466,16 @@ struct igc_nfc_filter {
 
 struct igc_mac_addr {
        u8 addr[ETH_ALEN];
-       u8 queue;
+       s8 queue;
        u8 state; /* bitmask */
 };
 
 #define IGC_MAC_STATE_DEFAULT          0x1
 #define IGC_MAC_STATE_IN_USE           0x2
 #define IGC_MAC_STATE_SRC_ADDR         0x4
-#define IGC_MAC_STATE_QUEUE_STEERING   0x8
 
 #define IGC_MAX_RXNFC_FILTERS          16
 
-/* Board specific private data structure */
-struct igc_adapter {
-       struct net_device *netdev;
-
-       unsigned long state;
-       unsigned int flags;
-       unsigned int num_q_vectors;
-
-       struct msix_entry *msix_entries;
-
-       /* TX */
-       u16 tx_work_limit;
-       u32 tx_timeout_count;
-       int num_tx_queues;
-       struct igc_ring *tx_ring[IGC_MAX_TX_QUEUES];
-
-       /* RX */
-       int num_rx_queues;
-       struct igc_ring *rx_ring[IGC_MAX_RX_QUEUES];
-
-       struct timer_list watchdog_timer;
-       struct timer_list dma_err_timer;
-       struct timer_list phy_info_timer;
-
-       u32 wol;
-       u32 en_mng_pt;
-       u16 link_speed;
-       u16 link_duplex;
-
-       u8 port_num;
-
-       u8 __iomem *io_addr;
-       /* Interrupt Throttle Rate */
-       u32 rx_itr_setting;
-       u32 tx_itr_setting;
-
-       struct work_struct reset_task;
-       struct work_struct watchdog_task;
-       struct work_struct dma_err_task;
-       bool fc_autoneg;
-
-       u8 tx_timeout_factor;
-
-       int msg_enable;
-       u32 max_frame_size;
-       u32 min_frame_size;
-
-       /* OS defined structs */
-       struct pci_dev *pdev;
-       /* lock for statistics */
-       spinlock_t stats64_lock;
-       struct rtnl_link_stats64 stats64;
-
-       /* structs defined in igc_hw.h */
-       struct igc_hw hw;
-       struct igc_hw_stats stats;
-
-       struct igc_q_vector *q_vector[MAX_Q_VECTORS];
-       u32 eims_enable_mask;
-       u32 eims_other;
-
-       u16 tx_ring_count;
-       u16 rx_ring_count;
-
-       u32 tx_hwtstamp_timeouts;
-       u32 tx_hwtstamp_skipped;
-       u32 rx_hwtstamp_cleared;
-
-       u32 rss_queues;
-       u32 rss_indir_tbl_init;
-
-       /* RX network flow classification support */
-       struct hlist_head nfc_filter_list;
-       struct hlist_head cls_flower_list;
-       unsigned int nfc_filter_count;
-
-       /* lock for RX network flow classification filter */
-       spinlock_t nfc_lock;
-       bool etype_bitmap[MAX_ETYPE_FILTER];
-
-       struct igc_mac_addr *mac_table;
-
-       u8 rss_indir_tbl[IGC_RETA_SIZE];
-
-       unsigned long link_check_timeout;
-       struct igc_info ei;
-
-       struct ptp_clock *ptp_clock;
-       struct ptp_clock_info ptp_caps;
-       struct work_struct ptp_tx_work;
-       struct sk_buff *ptp_tx_skb;
-       struct hwtstamp_config tstamp_config;
-       unsigned long ptp_tx_start;
-       unsigned long last_rx_ptp_check;
-       unsigned long last_rx_timestamp;
-       unsigned int ptp_flags;
-       /* System time value lock */
-       spinlock_t tmreg_lock;
-       struct cyclecounter cc;
-       struct timecounter tc;
-};
-
 /* igc_desc_unused - calculate if we have unused descriptors */
 static inline u16 igc_desc_unused(const struct igc_ring *ring)
 {
index 5a50644..f7fb18d 100644 (file)
@@ -212,6 +212,9 @@ static s32 igc_get_invariants_base(struct igc_hw *hw)
        case IGC_DEV_ID_I225_I:
        case IGC_DEV_ID_I220_V:
        case IGC_DEV_ID_I225_K:
+       case IGC_DEV_ID_I225_K2:
+       case IGC_DEV_ID_I225_LMVP:
+       case IGC_DEV_ID_I225_IT:
        case IGC_DEV_ID_I225_BLANK_NVM:
                mac->type = igc_i225;
                break;
index 4ddcccc..af0c03d 100644 (file)
@@ -44,9 +44,6 @@
 /* Wake Up Packet Memory stores the first 128 bytes of the wake up packet */
 #define IGC_WUPM_BYTES 128
 
-/* Physical Func Reset Done Indication */
-#define IGC_CTRL_EXT_LINK_MODE_MASK    0x00C00000
-
 /* Loop limit on how long we wait for auto-negotiation to complete */
 #define COPPER_LINK_UP_LIMIT           10
 #define PHY_AUTO_NEG_LIMIT             45
  * (RAR[15]) for our directed address used by controllers with
  * manageability enabled, allowing us room for 15 multicast addresses.
  */
+#define IGC_RAH_QSEL_MASK      0x000C0000
+#define IGC_RAH_QSEL_SHIFT     18
+#define IGC_RAH_QSEL_ENABLE    BIT(28)
 #define IGC_RAH_AV             0x80000000 /* Receive descriptor valid */
-#define IGC_RAH_POOL_1         0x00040000
+
 #define IGC_RAL_MAC_ADDR_LEN   4
 #define IGC_RAH_MAC_ADDR_LEN   2
 
@@ -94,8 +94,6 @@
 #define IGC_CTRL_RFCE          0x08000000  /* Receive Flow Control enable */
 #define IGC_CTRL_TFCE          0x10000000  /* Transmit flow control enable */
 
-#define IGC_CONNSW_AUTOSENSE_EN        0x1
-
 /* As per the EAS the maximum supported size is 9.5KB (9728 bytes) */
 #define MAX_JUMBO_FRAME_SIZE   0x2600
 
 #define I225_TXPBSIZE_DEFAULT  0x04000014 /* TXPBSIZE default */
 #define IGC_RXPBS_CFG_TS_EN    0x80000000 /* Timestamp in Rx buffer */
 
+#define IGC_TXPBSIZE_TSN       0x04145145 /* 5k bytes buffer for each queue */
+
+#define IGC_DTXMXPKTSZ_TSN     0x19 /* 1600 bytes of max TX DMA packet size */
+#define IGC_DTXMXPKTSZ_DEFAULT 0x98 /* 9728-byte Jumbo frames */
+
 /* Time Sync Interrupt Causes */
 #define IGC_TSICR_SYS_WRAP     BIT(0) /* SYSTIM Wrap around. */
 #define IGC_TSICR_TXTS         BIT(1) /* Transmit Timestamp. */
 #define IGC_TSYNCTXCTL_START_SYNC              0x80000000  /* initiate sync */
 #define IGC_TSYNCTXCTL_TXSYNSIG                        0x00000020  /* Sample TX tstamp in PHY sop */
 
+/* Transmit Scheduling */
+#define IGC_TQAVCTRL_TRANSMIT_MODE_TSN 0x00000001
+#define IGC_TQAVCTRL_ENHANCED_QAV      0x00000008
+
+#define IGC_TXQCTL_QUEUE_MODE_LAUNCHT  0x00000001
+#define IGC_TXQCTL_STRICT_CYCLE                0x00000002
+#define IGC_TXQCTL_STRICT_END          0x00000004
+
 /* Receive Checksum Control */
 #define IGC_RXCSUM_CRCOFL      0x00000800   /* CRC32 offload enable */
 #define IGC_RXCSUM_PCSD                0x00002000   /* packet checksum disabled */
 #define IGC_MDIC_READY         0x10000000
 #define IGC_MDIC_INT_EN                0x20000000
 #define IGC_MDIC_ERROR         0x40000000
-#define IGC_MDIC_DEST          0x80000000
 
 #define IGC_N0_QUEUE           -1
 
index f530fc2..0a8c4a7 100644 (file)
@@ -153,7 +153,7 @@ static void igc_get_regs(struct net_device *netdev,
 
        memset(p, 0, IGC_REGS_LEN * sizeof(u32));
 
-       regs->version = (1u << 24) | (hw->revision_id << 16) | hw->device_id;
+       regs->version = (2u << 24) | (hw->revision_id << 16) | hw->device_id;
 
        /* General Registers */
        regs_buff[0] = rd32(IGC_CTRL);
@@ -306,6 +306,15 @@ static void igc_get_regs(struct net_device *netdev,
                regs_buff[164 + i] = rd32(IGC_TDT(i));
        for (i = 0; i < 4; i++)
                regs_buff[168 + i] = rd32(IGC_TXDCTL(i));
+
+       /* XXX: Due to a bug few lines above, RAL and RAH registers are
+        * overwritten. To preserve the ABI, we write these registers again in
+        * regs_buff.
+        */
+       for (i = 0; i < 16; i++)
+               regs_buff[172 + i] = rd32(IGC_RAL(i));
+       for (i = 0; i < 16; i++)
+               regs_buff[188 + i] = rd32(IGC_RAH(i));
 }
 
 static void igc_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
@@ -1257,20 +1266,16 @@ int igc_add_filter(struct igc_adapter *adapter, struct igc_nfc_filter *input)
        }
 
        if (input->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) {
-               err = igc_add_mac_steering_filter(adapter,
-                                                 input->filter.dst_addr,
-                                                 input->action, 0);
-               err = min_t(int, err, 0);
+               err = igc_add_mac_filter(adapter, input->filter.dst_addr,
+                                        input->action, 0);
                if (err)
                        return err;
        }
 
        if (input->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR) {
-               err = igc_add_mac_steering_filter(adapter,
-                                                 input->filter.src_addr,
-                                                 input->action,
-                                                 IGC_MAC_STATE_SRC_ADDR);
-               err = min_t(int, err, 0);
+               err = igc_add_mac_filter(adapter, input->filter.src_addr,
+                                        input->action,
+                                        IGC_MAC_STATE_SRC_ADDR);
                if (err)
                        return err;
        }
@@ -1324,13 +1329,11 @@ int igc_erase_filter(struct igc_adapter *adapter, struct igc_nfc_filter *input)
                                           ntohs(input->filter.vlan_tci));
 
        if (input->filter.match_flags & IGC_FILTER_FLAG_SRC_MAC_ADDR)
-               igc_del_mac_steering_filter(adapter, input->filter.src_addr,
-                                           input->action,
-                                           IGC_MAC_STATE_SRC_ADDR);
+               igc_del_mac_filter(adapter, input->filter.src_addr,
+                                  IGC_MAC_STATE_SRC_ADDR);
 
        if (input->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR)
-               igc_del_mac_steering_filter(adapter, input->filter.dst_addr,
-                                           input->action, 0);
+               igc_del_mac_filter(adapter, input->filter.dst_addr, 0);
 
        return 0;
 }
index 90ac0e0..af34ae3 100644 (file)
@@ -21,6 +21,9 @@
 #define IGC_DEV_ID_I225_I                      0x15F8
 #define IGC_DEV_ID_I220_V                      0x15F7
 #define IGC_DEV_ID_I225_K                      0x3100
+#define IGC_DEV_ID_I225_K2                     0x3101
+#define IGC_DEV_ID_I225_LMVP                   0x5502
+#define IGC_DEV_ID_I225_IT                     0x0D9F
 #define IGC_DEV_ID_I225_BLANK_NVM              0x15FD
 
 /* Function pointers for the MAC. */
index 69fa1ce..9d5f828 100644 (file)
@@ -9,11 +9,13 @@
 #include <linux/udp.h>
 #include <linux/ip.h>
 #include <linux/pm_runtime.h>
+#include <net/pkt_sched.h>
 
 #include <net/ipv6.h>
 
 #include "igc.h"
 #include "igc_hw.h"
+#include "igc_tsn.h"
 
 #define DRV_VERSION    "0.0.1-k"
 #define DRV_SUMMARY    "Intel(R) 2.5G Ethernet Linux Driver"
@@ -45,6 +47,9 @@ static const struct pci_device_id igc_pci_tbl[] = {
        { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_I), board_base },
        { PCI_VDEVICE(INTEL, IGC_DEV_ID_I220_V), board_base },
        { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_K), board_base },
+       { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_K2), board_base },
+       { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_LMVP), board_base },
+       { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_IT), board_base },
        { PCI_VDEVICE(INTEL, IGC_DEV_ID_I225_BLANK_NVM), board_base },
        /* required last entry */
        {0, }
@@ -106,6 +111,9 @@ void igc_reset(struct igc_adapter *adapter)
        /* Re-enable PTP, where applicable. */
        igc_ptp_reset(adapter);
 
+       /* Re-enable TSN offloading, where applicable. */
+       igc_tsn_offload_apply(adapter);
+
        igc_get_phy_info(hw);
 }
 
@@ -757,48 +765,74 @@ static void igc_setup_tctl(struct igc_adapter *adapter)
 }
 
 /**
- * igc_rar_set_index - Sync RAL[index] and RAH[index] registers with MAC table
- * @adapter: address of board private structure
- * @index: Index of the RAR entry which need to be synced with MAC table
+ * igc_set_mac_filter_hw() - Set MAC address filter in hardware
+ * @adapter: Pointer to adapter where the filter should be set
+ * @index: Filter index
+ * @addr: Destination MAC address
+ * @queue: If non-negative, queue assignment feature is enabled and frames
+ *         matching the filter are enqueued onto 'queue'. Otherwise, queue
+ *         assignment is disabled.
  */
-static void igc_rar_set_index(struct igc_adapter *adapter, u32 index)
+static void igc_set_mac_filter_hw(struct igc_adapter *adapter, int index,
+                                 const u8 *addr, int queue)
 {
-       u8 *addr = adapter->mac_table[index].addr;
+       struct net_device *dev = adapter->netdev;
        struct igc_hw *hw = &adapter->hw;
-       u32 rar_low, rar_high;
+       u32 ral, rah;
 
-       /* HW expects these to be in network order when they are plugged
-        * into the registers which are little endian.  In order to guarantee
-        * that ordering we need to do an leXX_to_cpup here in order to be
-        * ready for the byteswap that occurs with writel
-        */
-       rar_low = le32_to_cpup((__le32 *)(addr));
-       rar_high = le16_to_cpup((__le16 *)(addr + 4));
+       if (WARN_ON(index >= hw->mac.rar_entry_count))
+               return;
 
-       /* Indicate to hardware the Address is Valid. */
-       if (adapter->mac_table[index].state & IGC_MAC_STATE_IN_USE) {
-               if (is_valid_ether_addr(addr))
-                       rar_high |= IGC_RAH_AV;
+       ral = le32_to_cpup((__le32 *)(addr));
+       rah = le16_to_cpup((__le16 *)(addr + 4));
 
-               rar_high |= IGC_RAH_POOL_1 <<
-                       adapter->mac_table[index].queue;
+       if (queue >= 0) {
+               rah &= ~IGC_RAH_QSEL_MASK;
+               rah |= (queue << IGC_RAH_QSEL_SHIFT);
+               rah |= IGC_RAH_QSEL_ENABLE;
        }
 
-       wr32(IGC_RAL(index), rar_low);
-       wrfl();
-       wr32(IGC_RAH(index), rar_high);
-       wrfl();
+       rah |= IGC_RAH_AV;
+
+       wr32(IGC_RAL(index), ral);
+       wr32(IGC_RAH(index), rah);
+
+       netdev_dbg(dev, "MAC address filter set in HW: index %d", index);
+}
+
+/**
+ * igc_clear_mac_filter_hw() - Clear MAC address filter in hardware
+ * @adapter: Pointer to adapter where the filter should be cleared
+ * @index: Filter index
+ */
+static void igc_clear_mac_filter_hw(struct igc_adapter *adapter, int index)
+{
+       struct net_device *dev = adapter->netdev;
+       struct igc_hw *hw = &adapter->hw;
+
+       if (WARN_ON(index >= hw->mac.rar_entry_count))
+               return;
+
+       wr32(IGC_RAL(index), 0);
+       wr32(IGC_RAH(index), 0);
+
+       netdev_dbg(dev, "MAC address filter cleared in HW: index %d", index);
 }
 
 /* Set default MAC address for the PF in the first RAR entry */
 static void igc_set_default_mac_filter(struct igc_adapter *adapter)
 {
        struct igc_mac_addr *mac_table = &adapter->mac_table[0];
+       struct net_device *dev = adapter->netdev;
+       u8 *addr = adapter->hw.mac.addr;
 
-       ether_addr_copy(mac_table->addr, adapter->hw.mac.addr);
+       netdev_dbg(dev, "Set default MAC address filter: address %pM", addr);
+
+       ether_addr_copy(mac_table->addr, addr);
        mac_table->state = IGC_MAC_STATE_DEFAULT | IGC_MAC_STATE_IN_USE;
+       mac_table->queue = -1;
 
-       igc_rar_set_index(adapter, 0);
+       igc_set_mac_filter_hw(adapter, 0, addr, mac_table->queue);
 }
 
 /**
@@ -864,6 +898,23 @@ static int igc_write_mc_addr_list(struct net_device *netdev)
        return netdev_mc_count(netdev);
 }
 
+static __le32 igc_tx_launchtime(struct igc_adapter *adapter, ktime_t txtime)
+{
+       ktime_t cycle_time = adapter->cycle_time;
+       ktime_t base_time = adapter->base_time;
+       u32 launchtime;
+
+       /* FIXME: when using ETF together with taprio, we may have a
+        * case where 'delta' is larger than the cycle_time, this may
+        * cause problems if we don't read the current value of
+        * IGC_BASET, as the value writen into the launchtime
+        * descriptor field may be misinterpreted.
+        */
+       div_s64_rem(ktime_sub_ns(txtime, base_time), cycle_time, &launchtime);
+
+       return cpu_to_le32(launchtime);
+}
+
 static void igc_tx_ctxtdesc(struct igc_ring *tx_ring,
                            struct igc_tx_buffer *first,
                            u32 vlan_macip_lens, u32 type_tucmd,
@@ -871,7 +922,6 @@ static void igc_tx_ctxtdesc(struct igc_ring *tx_ring,
 {
        struct igc_adv_tx_context_desc *context_desc;
        u16 i = tx_ring->next_to_use;
-       struct timespec64 ts;
 
        context_desc = IGC_TX_CTXTDESC(tx_ring, i);
 
@@ -893,9 +943,12 @@ static void igc_tx_ctxtdesc(struct igc_ring *tx_ring,
         * should have been handled by the upper layers.
         */
        if (tx_ring->launchtime_enable) {
-               ts = ktime_to_timespec64(first->skb->tstamp);
+               struct igc_adapter *adapter = netdev_priv(tx_ring->netdev);
+               ktime_t txtime = first->skb->tstamp;
+
                first->skb->tstamp = ktime_set(0, 0);
-               context_desc->launch_time = cpu_to_le32(ts.tv_nsec / 32);
+               context_desc->launch_time = igc_tx_launchtime(adapter,
+                                                             txtime);
        } else {
                context_desc->launch_time = 0;
        }
@@ -2133,129 +2186,148 @@ static void igc_nfc_filter_restore(struct igc_adapter *adapter)
        spin_unlock(&adapter->nfc_lock);
 }
 
-/* If the filter to be added and an already existing filter express
- * the same address and address type, it should be possible to only
- * override the other configurations, for example the queue to steer
- * traffic.
- */
-static bool igc_mac_entry_can_be_used(const struct igc_mac_addr *entry,
-                                     const u8 *addr, const u8 flags)
+static int igc_find_mac_filter(struct igc_adapter *adapter, const u8 *addr,
+                              u8 flags)
 {
-       if (!(entry->state & IGC_MAC_STATE_IN_USE))
-               return true;
+       int max_entries = adapter->hw.mac.rar_entry_count;
+       struct igc_mac_addr *entry;
+       int i;
 
-       if ((entry->state & IGC_MAC_STATE_SRC_ADDR) !=
-           (flags & IGC_MAC_STATE_SRC_ADDR))
-               return false;
+       for (i = 0; i < max_entries; i++) {
+               entry = &adapter->mac_table[i];
 
-       if (!ether_addr_equal(addr, entry->addr))
-               return false;
+               if (!(entry->state & IGC_MAC_STATE_IN_USE))
+                       continue;
+               if (!ether_addr_equal(addr, entry->addr))
+                       continue;
+               if ((entry->state & IGC_MAC_STATE_SRC_ADDR) !=
+                   (flags & IGC_MAC_STATE_SRC_ADDR))
+                       continue;
 
-       return true;
+               return i;
+       }
+
+       return -1;
 }
 
-/* Add a MAC filter for 'addr' directing matching traffic to 'queue',
- * 'flags' is used to indicate what kind of match is made, match is by
- * default for the destination address, if matching by source address
- * is desired the flag IGC_MAC_STATE_SRC_ADDR can be used.
- */
-static int igc_add_mac_filter(struct igc_adapter *adapter,
-                             const u8 *addr, const u8 queue)
+static int igc_get_avail_mac_filter_slot(struct igc_adapter *adapter)
 {
-       struct igc_hw *hw = &adapter->hw;
-       int rar_entries = hw->mac.rar_entry_count;
+       int max_entries = adapter->hw.mac.rar_entry_count;
+       struct igc_mac_addr *entry;
        int i;
 
-       if (is_zero_ether_addr(addr))
+       for (i = 0; i < max_entries; i++) {
+               entry = &adapter->mac_table[i];
+
+               if (!(entry->state & IGC_MAC_STATE_IN_USE))
+                       return i;
+       }
+
+       return -1;
+}
+
+/**
+ * igc_add_mac_filter() - Add MAC address filter
+ * @adapter: Pointer to adapter where the filter should be added
+ * @addr: MAC address
+ * @queue: If non-negative, queue assignment feature is enabled and frames
+ *         matching the filter are enqueued onto 'queue'. Otherwise, queue
+ *         assignment is disabled.
+ * @flags: Set IGC_MAC_STATE_SRC_ADDR bit to indicate @address is a source
+ *         address
+ *
+ * Return: 0 in case of success, negative errno code otherwise.
+ */
+int igc_add_mac_filter(struct igc_adapter *adapter, const u8 *addr,
+                      const s8 queue, const u8 flags)
+{
+       struct net_device *dev = adapter->netdev;
+       int index;
+
+       if (!is_valid_ether_addr(addr))
                return -EINVAL;
+       if (flags & IGC_MAC_STATE_SRC_ADDR)
+               return -ENOTSUPP;
 
-       /* Search for the first empty entry in the MAC table.
-        * Do not touch entries at the end of the table reserved for the VF MAC
-        * addresses.
-        */
-       for (i = 0; i < rar_entries; i++) {
-               if (!igc_mac_entry_can_be_used(&adapter->mac_table[i],
-                                              addr, 0))
-                       continue;
+       index = igc_find_mac_filter(adapter, addr, flags);
+       if (index >= 0)
+               goto update_queue_assignment;
 
-               ether_addr_copy(adapter->mac_table[i].addr, addr);
-               adapter->mac_table[i].queue = queue;
-               adapter->mac_table[i].state |= IGC_MAC_STATE_IN_USE;
+       index = igc_get_avail_mac_filter_slot(adapter);
+       if (index < 0)
+               return -ENOSPC;
 
-               igc_rar_set_index(adapter, i);
-               return i;
-       }
+       netdev_dbg(dev, "Add MAC address filter: index %d address %pM queue %d",
+                  index, addr, queue);
+
+       ether_addr_copy(adapter->mac_table[index].addr, addr);
+       adapter->mac_table[index].state |= IGC_MAC_STATE_IN_USE | flags;
+update_queue_assignment:
+       adapter->mac_table[index].queue = queue;
 
-       return -ENOSPC;
+       igc_set_mac_filter_hw(adapter, index, addr, queue);
+       return 0;
 }
 
-/* Remove a MAC filter for 'addr' directing matching traffic to
- * 'queue', 'flags' is used to indicate what kind of match need to be
- * removed, match is by default for the destination address, if
- * matching by source address is to be removed the flag
- * IGC_MAC_STATE_SRC_ADDR can be used.
+/**
+ * igc_del_mac_filter() - Delete MAC address filter
+ * @adapter: Pointer to adapter where the filter should be deleted from
+ * @addr: MAC address
+ * @flags: Set IGC_MAC_STATE_SRC_ADDR bit to indicate @address is a source
+ *         address
+ *
+ * Return: 0 in case of success, negative errno code otherwise.
  */
-static int igc_del_mac_filter(struct igc_adapter *adapter,
-                             const u8 *addr, const u8 queue)
+int igc_del_mac_filter(struct igc_adapter *adapter, const u8 *addr,
+                      const u8 flags)
 {
-       struct igc_hw *hw = &adapter->hw;
-       int rar_entries = hw->mac.rar_entry_count;
-       int i;
+       struct net_device *dev = adapter->netdev;
+       struct igc_mac_addr *entry;
+       int index;
 
-       if (is_zero_ether_addr(addr))
+       if (!is_valid_ether_addr(addr))
                return -EINVAL;
 
-       /* Search for matching entry in the MAC table based on given address
-        * and queue. Do not touch entries at the end of the table reserved
-        * for the VF MAC addresses.
-        */
-       for (i = 0; i < rar_entries; i++) {
-               if (!(adapter->mac_table[i].state & IGC_MAC_STATE_IN_USE))
-                       continue;
-               if (adapter->mac_table[i].state != 0)
-                       continue;
-               if (adapter->mac_table[i].queue != queue)
-                       continue;
-               if (!ether_addr_equal(adapter->mac_table[i].addr, addr))
-                       continue;
+       index = igc_find_mac_filter(adapter, addr, flags);
+       if (index < 0)
+               return -ENOENT;
 
-               /* When a filter for the default address is "deleted",
-                * we return it to its initial configuration
+       entry = &adapter->mac_table[index];
+
+       if (entry->state & IGC_MAC_STATE_DEFAULT) {
+               /* If this is the default filter, we don't actually delete it.
+                * We just reset to its default value i.e. disable queue
+                * assignment.
                 */
-               if (adapter->mac_table[i].state & IGC_MAC_STATE_DEFAULT) {
-                       adapter->mac_table[i].state =
-                               IGC_MAC_STATE_DEFAULT | IGC_MAC_STATE_IN_USE;
-                       adapter->mac_table[i].queue = 0;
-               } else {
-                       adapter->mac_table[i].state = 0;
-                       adapter->mac_table[i].queue = 0;
-                       memset(adapter->mac_table[i].addr, 0, ETH_ALEN);
-               }
+               netdev_dbg(dev, "Disable default MAC filter queue assignment");
 
-               igc_rar_set_index(adapter, i);
-               return 0;
+               entry->queue = -1;
+               igc_set_mac_filter_hw(adapter, 0, addr, entry->queue);
+       } else {
+               netdev_dbg(dev, "Delete MAC address filter: index %d address %pM",
+                          index, addr);
+
+               entry->state = 0;
+               entry->queue = -1;
+               memset(entry->addr, 0, ETH_ALEN);
+               igc_clear_mac_filter_hw(adapter, index);
        }
 
-       return -ENOENT;
+       return 0;
 }
 
 static int igc_uc_sync(struct net_device *netdev, const unsigned char *addr)
 {
        struct igc_adapter *adapter = netdev_priv(netdev);
-       int ret;
-
-       ret = igc_add_mac_filter(adapter, addr, adapter->num_rx_queues);
 
-       return min_t(int, ret, 0);
+       return igc_add_mac_filter(adapter, addr, -1, 0);
 }
 
 static int igc_uc_unsync(struct net_device *netdev, const unsigned char *addr)
 {
        struct igc_adapter *adapter = netdev_priv(netdev);
 
-       igc_del_mac_filter(adapter, addr, adapter->num_rx_queues);
-
-       return 0;
+       return igc_del_mac_filter(adapter, addr, 0);
 }
 
 /**
@@ -2325,7 +2397,9 @@ static void igc_configure(struct igc_adapter *adapter)
        igc_setup_mrqc(adapter);
        igc_setup_rctl(adapter);
 
+       igc_set_default_mac_filter(adapter);
        igc_nfc_filter_restore(adapter);
+
        igc_configure_tx(adapter);
        igc_configure_rx(adapter);
 
@@ -3458,9 +3532,6 @@ static void igc_nfc_filter_exit(struct igc_adapter *adapter)
        hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node)
                igc_erase_filter(adapter, rule);
 
-       hlist_for_each_entry(rule, &adapter->cls_flower_list, nfc_node)
-               igc_erase_filter(adapter, rule);
-
        spin_unlock(&adapter->nfc_lock);
 }
 
@@ -3689,106 +3760,6 @@ igc_features_check(struct sk_buff *skb, struct net_device *dev,
        return features;
 }
 
-/* Add a MAC filter for 'addr' directing matching traffic to 'queue',
- * 'flags' is used to indicate what kind of match is made, match is by
- * default for the destination address, if matching by source address
- * is desired the flag IGC_MAC_STATE_SRC_ADDR can be used.
- */
-static int igc_add_mac_filter_flags(struct igc_adapter *adapter,
-                                   const u8 *addr, const u8 queue,
-                                   const u8 flags)
-{
-       struct igc_hw *hw = &adapter->hw;
-       int rar_entries = hw->mac.rar_entry_count;
-       int i;
-
-       if (is_zero_ether_addr(addr))
-               return -EINVAL;
-
-       /* Search for the first empty entry in the MAC table.
-        * Do not touch entries at the end of the table reserved for the VF MAC
-        * addresses.
-        */
-       for (i = 0; i < rar_entries; i++) {
-               if (!igc_mac_entry_can_be_used(&adapter->mac_table[i],
-                                              addr, flags))
-                       continue;
-
-               ether_addr_copy(adapter->mac_table[i].addr, addr);
-               adapter->mac_table[i].queue = queue;
-               adapter->mac_table[i].state |= IGC_MAC_STATE_IN_USE | flags;
-
-               igc_rar_set_index(adapter, i);
-               return i;
-       }
-
-       return -ENOSPC;
-}
-
-int igc_add_mac_steering_filter(struct igc_adapter *adapter,
-                               const u8 *addr, u8 queue, u8 flags)
-{
-       return igc_add_mac_filter_flags(adapter, addr, queue,
-                                       IGC_MAC_STATE_QUEUE_STEERING | flags);
-}
-
-/* Remove a MAC filter for 'addr' directing matching traffic to
- * 'queue', 'flags' is used to indicate what kind of match need to be
- * removed, match is by default for the destination address, if
- * matching by source address is to be removed the flag
- * IGC_MAC_STATE_SRC_ADDR can be used.
- */
-static int igc_del_mac_filter_flags(struct igc_adapter *adapter,
-                                   const u8 *addr, const u8 queue,
-                                   const u8 flags)
-{
-       struct igc_hw *hw = &adapter->hw;
-       int rar_entries = hw->mac.rar_entry_count;
-       int i;
-
-       if (is_zero_ether_addr(addr))
-               return -EINVAL;
-
-       /* Search for matching entry in the MAC table based on given address
-        * and queue. Do not touch entries at the end of the table reserved
-        * for the VF MAC addresses.
-        */
-       for (i = 0; i < rar_entries; i++) {
-               if (!(adapter->mac_table[i].state & IGC_MAC_STATE_IN_USE))
-                       continue;
-               if ((adapter->mac_table[i].state & flags) != flags)
-                       continue;
-               if (adapter->mac_table[i].queue != queue)
-                       continue;
-               if (!ether_addr_equal(adapter->mac_table[i].addr, addr))
-                       continue;
-
-               /* When a filter for the default address is "deleted",
-                * we return it to its initial configuration
-                */
-               if (adapter->mac_table[i].state & IGC_MAC_STATE_DEFAULT) {
-                       adapter->mac_table[i].state =
-                               IGC_MAC_STATE_DEFAULT | IGC_MAC_STATE_IN_USE;
-               } else {
-                       adapter->mac_table[i].state = 0;
-                       adapter->mac_table[i].queue = 0;
-                       memset(adapter->mac_table[i].addr, 0, ETH_ALEN);
-               }
-
-               igc_rar_set_index(adapter, i);
-               return 0;
-       }
-
-       return -ENOENT;
-}
-
-int igc_del_mac_steering_filter(struct igc_adapter *adapter,
-                               const u8 *addr, u8 queue, u8 flags)
-{
-       return igc_del_mac_filter_flags(adapter, addr, queue,
-                                       IGC_MAC_STATE_QUEUE_STEERING | flags);
-}
-
 static void igc_tsync_interrupt(struct igc_adapter *adapter)
 {
        struct igc_hw *hw = &adapter->hw;
@@ -4009,7 +3980,6 @@ static void igc_watchdog_task(struct work_struct *work)
        struct igc_hw *hw = &adapter->hw;
        struct igc_phy_info *phy = &hw->phy;
        u16 phy_data, retry_count = 20;
-       u32 connsw;
        u32 link;
        int i;
 
@@ -4022,14 +3992,6 @@ static void igc_watchdog_task(struct work_struct *work)
                        link = false;
        }
 
-       /* Force link down if we have fiber to swap to */
-       if (adapter->flags & IGC_FLAG_MAS_ENABLE) {
-               if (hw->phy.media_type == igc_media_type_copper) {
-                       connsw = rd32(IGC_CONNSW);
-                       if (!(connsw & IGC_CONNSW_AUTOSENSE_EN))
-                               link = 0;
-               }
-       }
        if (link) {
                /* Cancel scheduled suspend requests. */
                pm_runtime_resume(netdev->dev.parent);
@@ -4491,6 +4453,158 @@ static int igc_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
        }
 }
 
+static int igc_save_launchtime_params(struct igc_adapter *adapter, int queue,
+                                     bool enable)
+{
+       struct igc_ring *ring;
+       int i;
+
+       if (queue < 0 || queue >= adapter->num_tx_queues)
+               return -EINVAL;
+
+       ring = adapter->tx_ring[queue];
+       ring->launchtime_enable = enable;
+
+       if (adapter->base_time)
+               return 0;
+
+       adapter->cycle_time = NSEC_PER_SEC;
+
+       for (i = 0; i < adapter->num_tx_queues; i++) {
+               ring = adapter->tx_ring[i];
+               ring->start_time = 0;
+               ring->end_time = NSEC_PER_SEC;
+       }
+
+       return 0;
+}
+
+static bool validate_schedule(const struct tc_taprio_qopt_offload *qopt)
+{
+       int queue_uses[IGC_MAX_TX_QUEUES] = { };
+       size_t n;
+
+       if (qopt->cycle_time_extension)
+               return false;
+
+       for (n = 0; n < qopt->num_entries; n++) {
+               const struct tc_taprio_sched_entry *e;
+               int i;
+
+               e = &qopt->entries[n];
+
+               /* i225 only supports "global" frame preemption
+                * settings.
+                */
+               if (e->command != TC_TAPRIO_CMD_SET_GATES)
+                       return false;
+
+               for (i = 0; i < IGC_MAX_TX_QUEUES; i++) {
+                       if (e->gate_mask & BIT(i))
+                               queue_uses[i]++;
+
+                       if (queue_uses[i] > 1)
+                               return false;
+               }
+       }
+
+       return true;
+}
+
+static int igc_tsn_enable_launchtime(struct igc_adapter *adapter,
+                                    struct tc_etf_qopt_offload *qopt)
+{
+       struct igc_hw *hw = &adapter->hw;
+       int err;
+
+       if (hw->mac.type != igc_i225)
+               return -EOPNOTSUPP;
+
+       err = igc_save_launchtime_params(adapter, qopt->queue, qopt->enable);
+       if (err)
+               return err;
+
+       return igc_tsn_offload_apply(adapter);
+}
+
+static int igc_save_qbv_schedule(struct igc_adapter *adapter,
+                                struct tc_taprio_qopt_offload *qopt)
+{
+       u32 start_time = 0, end_time = 0;
+       size_t n;
+
+       if (!qopt->enable) {
+               adapter->base_time = 0;
+               return 0;
+       }
+
+       if (adapter->base_time)
+               return -EALREADY;
+
+       if (!validate_schedule(qopt))
+               return -EINVAL;
+
+       adapter->cycle_time = qopt->cycle_time;
+       adapter->base_time = qopt->base_time;
+
+       /* FIXME: be a little smarter about cases when the gate for a
+        * queue stays open for more than one entry.
+        */
+       for (n = 0; n < qopt->num_entries; n++) {
+               struct tc_taprio_sched_entry *e = &qopt->entries[n];
+               int i;
+
+               end_time += e->interval;
+
+               for (i = 0; i < IGC_MAX_TX_QUEUES; i++) {
+                       struct igc_ring *ring = adapter->tx_ring[i];
+
+                       if (!(e->gate_mask & BIT(i)))
+                               continue;
+
+                       ring->start_time = start_time;
+                       ring->end_time = end_time;
+               }
+
+               start_time += e->interval;
+       }
+
+       return 0;
+}
+
+static int igc_tsn_enable_qbv_scheduling(struct igc_adapter *adapter,
+                                        struct tc_taprio_qopt_offload *qopt)
+{
+       struct igc_hw *hw = &adapter->hw;
+       int err;
+
+       if (hw->mac.type != igc_i225)
+               return -EOPNOTSUPP;
+
+       err = igc_save_qbv_schedule(adapter, qopt);
+       if (err)
+               return err;
+
+       return igc_tsn_offload_apply(adapter);
+}
+
+static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,
+                       void *type_data)
+{
+       struct igc_adapter *adapter = netdev_priv(dev);
+
+       switch (type) {
+       case TC_SETUP_QDISC_TAPRIO:
+               return igc_tsn_enable_qbv_scheduling(adapter, type_data);
+
+       case TC_SETUP_QDISC_ETF:
+               return igc_tsn_enable_launchtime(adapter, type_data);
+
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 static const struct net_device_ops igc_netdev_ops = {
        .ndo_open               = igc_open,
        .ndo_stop               = igc_close,
@@ -4503,6 +4617,7 @@ static const struct net_device_ops igc_netdev_ops = {
        .ndo_set_features       = igc_set_features,
        .ndo_features_check     = igc_features_check,
        .ndo_do_ioctl           = igc_ioctl,
+       .ndo_setup_tc           = igc_setup_tc,
 };
 
 /* PCIe configuration access */
@@ -4726,6 +4841,17 @@ static int igc_probe(struct pci_dev *pdev,
        netdev->features |= NETIF_F_RXCSUM;
        netdev->features |= NETIF_F_HW_CSUM;
        netdev->features |= NETIF_F_SCTP_CRC;
+       netdev->features |= NETIF_F_HW_TC;
+
+#define IGC_GSO_PARTIAL_FEATURES (NETIF_F_GSO_GRE | \
+                                 NETIF_F_GSO_GRE_CSUM | \
+                                 NETIF_F_GSO_IPXIP4 | \
+                                 NETIF_F_GSO_IPXIP6 | \
+                                 NETIF_F_GSO_UDP_TUNNEL | \
+                                 NETIF_F_GSO_UDP_TUNNEL_CSUM)
+
+       netdev->gso_partial_features = IGC_GSO_PARTIAL_FEATURES;
+       netdev->features |= NETIF_F_GSO_PARTIAL | IGC_GSO_PARTIAL_FEATURES;
 
        /* setup the private structure */
        err = igc_sw_init(adapter);
index d4af53a..6093cde 100644 (file)
 
 #define IGC_RXPBS      0x02404  /* Rx Packet Buffer Size - RW */
 
+/* Transmit Scheduling Registers */
+#define IGC_TQAVCTRL           0x3570
+#define IGC_TXQCTL(_n)         (0x3344 + 0x4 * (_n))
+#define IGC_BASET_L            0x3314
+#define IGC_BASET_H            0x3318
+#define IGC_QBVCYCLET          0x331C
+#define IGC_QBVCYCLET_S                0x3320
+
+#define IGC_STQT(_n)           (0x3324 + 0x4 * (_n))
+#define IGC_ENDQT(_n)          (0x3334 + 0x4 * (_n))
+#define IGC_DTXMXPKTSZ         0x355C
+
 /* System Time Registers */
 #define IGC_SYSTIML    0x0B600  /* System time register Low - RO */
 #define IGC_SYSTIMH    0x0B604  /* System time register High - RO */
diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c
new file mode 100644 (file)
index 0000000..174103c
--- /dev/null
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c)  2019 Intel Corporation */
+
+#include "igc.h"
+#include "igc_tsn.h"
+
+static bool is_any_launchtime(struct igc_adapter *adapter)
+{
+       int i;
+
+       for (i = 0; i < adapter->num_tx_queues; i++) {
+               struct igc_ring *ring = adapter->tx_ring[i];
+
+               if (ring->launchtime_enable)
+                       return true;
+       }
+
+       return false;
+}
+
+/* Returns the TSN specific registers to their default values after
+ * TSN offloading is disabled.
+ */
+static int igc_tsn_disable_offload(struct igc_adapter *adapter)
+{
+       struct igc_hw *hw = &adapter->hw;
+       u32 tqavctrl;
+       int i;
+
+       if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED))
+               return 0;
+
+       adapter->cycle_time = 0;
+
+       wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);
+       wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_DEFAULT);
+
+       tqavctrl = rd32(IGC_TQAVCTRL);
+       tqavctrl &= ~(IGC_TQAVCTRL_TRANSMIT_MODE_TSN |
+                     IGC_TQAVCTRL_ENHANCED_QAV);
+       wr32(IGC_TQAVCTRL, tqavctrl);
+
+       for (i = 0; i < adapter->num_tx_queues; i++) {
+               struct igc_ring *ring = adapter->tx_ring[i];
+
+               ring->start_time = 0;
+               ring->end_time = 0;
+               ring->launchtime_enable = false;
+
+               wr32(IGC_TXQCTL(i), 0);
+               wr32(IGC_STQT(i), 0);
+               wr32(IGC_ENDQT(i), NSEC_PER_SEC);
+       }
+
+       wr32(IGC_QBVCYCLET_S, NSEC_PER_SEC);
+       wr32(IGC_QBVCYCLET, NSEC_PER_SEC);
+
+       adapter->flags &= ~IGC_FLAG_TSN_QBV_ENABLED;
+
+       return 0;
+}
+
+static int igc_tsn_enable_offload(struct igc_adapter *adapter)
+{
+       struct igc_hw *hw = &adapter->hw;
+       u32 tqavctrl, baset_l, baset_h;
+       u32 sec, nsec, cycle;
+       ktime_t base_time, systim;
+       int i;
+
+       if (adapter->flags & IGC_FLAG_TSN_QBV_ENABLED)
+               return 0;
+
+       cycle = adapter->cycle_time;
+       base_time = adapter->base_time;
+
+       wr32(IGC_TSAUXC, 0);
+       wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_TSN);
+       wr32(IGC_TXPBS, IGC_TXPBSIZE_TSN);
+
+       tqavctrl = rd32(IGC_TQAVCTRL);
+       tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV;
+       wr32(IGC_TQAVCTRL, tqavctrl);
+
+       wr32(IGC_QBVCYCLET_S, cycle);
+       wr32(IGC_QBVCYCLET, cycle);
+
+       for (i = 0; i < adapter->num_tx_queues; i++) {
+               struct igc_ring *ring = adapter->tx_ring[i];
+               u32 txqctl = 0;
+
+               wr32(IGC_STQT(i), ring->start_time);
+               wr32(IGC_ENDQT(i), ring->end_time);
+
+               if (adapter->base_time) {
+                       /* If we have a base_time we are in "taprio"
+                        * mode and we need to be strict about the
+                        * cycles: only transmit a packet if it can be
+                        * completed during that cycle.
+                        */
+                       txqctl |= IGC_TXQCTL_STRICT_CYCLE |
+                               IGC_TXQCTL_STRICT_END;
+               }
+
+               if (ring->launchtime_enable)
+                       txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT;
+
+               wr32(IGC_TXQCTL(i), txqctl);
+       }
+
+       nsec = rd32(IGC_SYSTIML);
+       sec = rd32(IGC_SYSTIMH);
+
+       systim = ktime_set(sec, nsec);
+
+       if (ktime_compare(systim, base_time) > 0) {
+               s64 n;
+
+               n = div64_s64(ktime_sub_ns(systim, base_time), cycle);
+               base_time = ktime_add_ns(base_time, (n + 1) * cycle);
+       }
+
+       baset_h = div_s64_rem(base_time, NSEC_PER_SEC, &baset_l);
+
+       wr32(IGC_BASET_H, baset_h);
+       wr32(IGC_BASET_L, baset_l);
+
+       adapter->flags |= IGC_FLAG_TSN_QBV_ENABLED;
+
+       return 0;
+}
+
+int igc_tsn_offload_apply(struct igc_adapter *adapter)
+{
+       bool is_any_enabled = adapter->base_time || is_any_launchtime(adapter);
+
+       if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED) && !is_any_enabled)
+               return 0;
+
+       if (!is_any_enabled) {
+               int err = igc_tsn_disable_offload(adapter);
+
+               if (err < 0)
+                       return err;
+
+               /* The BASET registers aren't cleared when writing
+                * into them, force a reset if the interface is
+                * running.
+                */
+               if (netif_running(adapter->netdev))
+                       schedule_work(&adapter->reset_task);
+
+               return 0;
+       }
+
+       return igc_tsn_enable_offload(adapter);
+}
diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.h b/drivers/net/ethernet/intel/igc/igc_tsn.h
new file mode 100644 (file)
index 0000000..f76bc86
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c)  2020 Intel Corporation */
+
+#ifndef _IGC_TSN_H_
+#define _IGC_TSN_H_
+
+int igc_tsn_offload_apply(struct igc_adapter *adapter);
+
+#endif /* _IGC_BASE_H */
index 900affb..1645e4e 100644 (file)
@@ -276,7 +276,8 @@ static int xrx200_tx_housekeeping(struct napi_struct *napi, int budget)
        return pkts;
 }
 
-static int xrx200_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
+static netdev_tx_t xrx200_start_xmit(struct sk_buff *skb,
+                                    struct net_device *net_dev)
 {
        struct xrx200_priv *priv = netdev_priv(net_dev);
        struct xrx200_chan *ch = &priv->chan_tx;
index 81d2448..4d4b624 100644 (file)
@@ -666,11 +666,6 @@ static inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb)
        return 0;
 }
 
-static inline __be16 sum16_as_be(__sum16 sum)
-{
-       return (__force __be16)sum;
-}
-
 static int skb_tx_csum(struct mv643xx_eth_private *mp, struct sk_buff *skb,
                       u16 *l4i_chk, u32 *command, int length)
 {
index 5be61f7..5188977 100644 (file)
@@ -5383,7 +5383,7 @@ static int __init mvneta_driver_init(void)
 {
        int ret;
 
-       ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "net/mvmeta:online",
+       ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "net/mvneta:online",
                                      mvneta_cpu_online,
                                      mvneta_cpu_down_prepare);
        if (ret < 0)
index 8972cdd..7352244 100644 (file)
@@ -1428,6 +1428,9 @@ int mvpp2_ethtool_cls_rule_del(struct mvpp2_port *port,
        struct mvpp2_ethtool_fs *efs;
        int ret;
 
+       if (info->fs.location >= MVPP2_N_RFS_ENTRIES_PER_FLOW)
+               return -EINVAL;
+
        efs = port->rfs_rules[info->fs.location];
        if (!efs)
                return -EINVAL;
index 1fa60e9..2b5dad2 100644 (file)
@@ -4329,6 +4329,8 @@ static int mvpp2_ethtool_get_rxfh_context(struct net_device *dev, u32 *indir,
 
        if (!mvpp22_rss_is_supported())
                return -EOPNOTSUPP;
+       if (rss_context >= MVPP22_N_RSS_TABLES)
+               return -EINVAL;
 
        if (hfunc)
                *hfunc = ETH_RSS_HASH_CRC32;
index 018c283..0b1c653 100644 (file)
@@ -309,7 +309,7 @@ static inline void __iomem *otx2_get_regaddr(struct otx2_nic *nic, u64 offset)
        default:
                blkaddr = BLKADDR_RVUM;
                break;
-       };
+       }
 
        offset &= ~(RVU_FUNC_BLKADDR_MASK << RVU_FUNC_BLKADDR_SHIFT);
        offset |= (blkaddr << RVU_FUNC_BLKADDR_SHIFT);
index 411e5ea..6478656 100644 (file)
@@ -1856,13 +1856,17 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        num_vec = pci_msix_vec_count(pdev);
        hw->irq_name = devm_kmalloc_array(&hw->pdev->dev, num_vec, NAME_SIZE,
                                          GFP_KERNEL);
-       if (!hw->irq_name)
+       if (!hw->irq_name) {
+               err = -ENOMEM;
                goto err_free_netdev;
+       }
 
        hw->affinity_mask = devm_kcalloc(&hw->pdev->dev, num_vec,
                                         sizeof(cpumask_var_t), GFP_KERNEL);
-       if (!hw->affinity_mask)
+       if (!hw->affinity_mask) {
+               err = -ENOMEM;
                goto err_free_netdev;
+       }
 
        /* Map CSRs */
        pf->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
index 8d28f90..f6a1f86 100644 (file)
@@ -65,6 +65,17 @@ u32 mtk_r32(struct mtk_eth *eth, unsigned reg)
        return __raw_readl(eth->base + reg);
 }
 
+static u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned reg)
+{
+       u32 val;
+
+       val = mtk_r32(eth, reg);
+       val &= ~mask;
+       val |= set;
+       mtk_w32(eth, val, reg);
+       return reg;
+}
+
 static int mtk_mdio_busy_wait(struct mtk_eth *eth)
 {
        unsigned long t_start = jiffies;
@@ -193,7 +204,7 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
        struct mtk_mac *mac = container_of(config, struct mtk_mac,
                                           phylink_config);
        struct mtk_eth *eth = mac->hw;
-       u32 mcr_cur, mcr_new, sid;
+       u32 mcr_cur, mcr_new, sid, i;
        int val, ge_mode, err;
 
        /* MT76x8 has no hardware settings between for the MAC */
@@ -255,6 +266,17 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
                                    PHY_INTERFACE_MODE_TRGMII)
                                        mtk_gmac0_rgmii_adjust(mac->hw,
                                                               state->speed);
+
+                               /* mt7623_pad_clk_setup */
+                               for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
+                                       mtk_w32(mac->hw,
+                                               TD_DM_DRVP(8) | TD_DM_DRVN(8),
+                                               TRGMII_TD_ODT(i));
+
+                               /* Assert/release MT7623 RXC reset */
+                               mtk_m32(mac->hw, 0, RXC_RST | RXC_DQSISEL,
+                                       TRGMII_RCK_CTRL);
+                               mtk_m32(mac->hw, RXC_RST, 0, TRGMII_RCK_CTRL);
                        }
                }
 
@@ -1100,7 +1122,7 @@ static void mtk_stop_queue(struct mtk_eth *eth)
        }
 }
 
-static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct mtk_mac *mac = netdev_priv(dev);
        struct mtk_eth *eth = mac->hw;
index 85830fe..454cfcd 100644 (file)
 #define DQSI0(x)               ((x << 0) & GENMASK(6, 0))
 #define DQSI1(x)               ((x << 8) & GENMASK(14, 8))
 #define RXCTL_DMWTLAT(x)       ((x << 16) & GENMASK(18, 16))
+#define RXC_RST                        BIT(31)
 #define RXC_DQSISEL            BIT(30)
 #define RCK_CTRL_RGMII_1000    (RXC_DQSISEL | RXCTL_DMWTLAT(2) | DQSI1(16))
 #define RCK_CTRL_RGMII_10_100  RXCTL_DMWTLAT(2)
 
+#define NUM_TRGMII_CTRL                5
+
 /* TRGMII RXC control register */
 #define TRGMII_TCK_CTRL                0x10340
 #define TXCTL_DMWTLAT(x)       ((x << 16) & GENMASK(18, 16))
 #define TCK_CTRL_RGMII_1000    TXCTL_DMWTLAT(2)
 #define TCK_CTRL_RGMII_10_100  (TXC_INV | TXCTL_DMWTLAT(2))
 
+/* TRGMII TX Drive Strength */
+#define TRGMII_TD_ODT(i)       (0x10354 + 8 * (i))
+#define  TD_DM_DRVP(x)         ((x) & 0xf)
+#define  TD_DM_DRVN(x)         (((x) & 0xf) << 4)
+
 /* TRGMII Interface mode register */
 #define INTF_MODE              0x10390
 #define TRGMII_INTF_DIS                BIT(0)
index 73eae80..ac5468b 100644 (file)
@@ -197,6 +197,7 @@ int mlx4_crdump_collect(struct mlx4_dev *dev)
        err = devlink_region_snapshot_id_get(devlink, &id);
        if (err) {
                mlx4_err(dev, "crdump: devlink get snapshot id err %d\n", err);
+               iounmap(cr_space);
                return err;
        }
 
index 8a5ea25..b816154 100644 (file)
@@ -1235,7 +1235,6 @@ static int mlx4_en_get_rxfh(struct net_device *dev, u32 *ring_index, u8 *key,
        struct mlx4_en_priv *priv = netdev_priv(dev);
        u32 n = mlx4_en_get_rxfh_indir_size(dev);
        u32 i, rss_rings;
-       int err = 0;
 
        rss_rings = priv->prof->rss_rings ?: n;
        rss_rings = rounddown_pow_of_two(rss_rings);
@@ -1249,7 +1248,7 @@ static int mlx4_en_get_rxfh(struct net_device *dev, u32 *ring_index, u8 *key,
                memcpy(key, priv->rss_key, MLX4_EN_RSS_KEY_SIZE);
        if (hfunc)
                *hfunc = priv->rss_hash_fn;
-       return err;
+       return 0;
 }
 
 static int mlx4_en_set_rxfh(struct net_device *dev, const u32 *ring_index,
@@ -1393,7 +1392,6 @@ static int mlx4_en_ethtool_add_mac_rule(struct ethtool_rxnfc *cmd,
                                        struct mlx4_spec_list *spec_l2,
                                        unsigned char *mac)
 {
-       int err = 0;
        __be64 mac_msk = cpu_to_be64(MLX4_MAC_MASK << 16);
 
        spec_l2->id = MLX4_NET_TRANS_RULE_ID_ETH;
@@ -1408,7 +1406,7 @@ static int mlx4_en_ethtool_add_mac_rule(struct ethtool_rxnfc *cmd,
 
        list_add_tail(&spec_l2->list, rule_list_h);
 
-       return err;
+       return 0;
 }
 
 static int mlx4_en_ethtool_add_mac_rule_by_ipv4(struct mlx4_en_priv *priv,
index db3552f..7871392 100644 (file)
@@ -946,7 +946,7 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget)
                xdp_tx_cq = priv->tx_cq[TX_XDP][cq->ring];
                if (xdp_tx_cq->xdp_busy) {
                        clean_complete = mlx4_en_process_tx_cq(dev, xdp_tx_cq,
-                                                              budget);
+                                                              budget) < budget;
                        xdp_tx_cq->xdp_busy = !clean_complete;
                }
        }
index 4d5ca30..9dff7b0 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <linux/moduleparam.h>
+#include <linux/indirect_call_wrapper.h>
 
 #include "mlx4_en.h"
 
@@ -261,6 +262,10 @@ static void mlx4_en_stamp_wqe(struct mlx4_en_priv *priv,
        }
 }
 
+INDIRECT_CALLABLE_DECLARE(u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv,
+                                                  struct mlx4_en_tx_ring *ring,
+                                                  int index, u64 timestamp,
+                                                  int napi_mode));
 
 u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv,
                         struct mlx4_en_tx_ring *ring,
@@ -329,6 +334,11 @@ u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv,
        return tx_info->nr_txbb;
 }
 
+INDIRECT_CALLABLE_DECLARE(u32 mlx4_en_recycle_tx_desc(struct mlx4_en_priv *priv,
+                                                     struct mlx4_en_tx_ring *ring,
+                                                     int index, u64 timestamp,
+                                                     int napi_mode));
+
 u32 mlx4_en_recycle_tx_desc(struct mlx4_en_priv *priv,
                            struct mlx4_en_tx_ring *ring,
                            int index, u64 timestamp,
@@ -382,8 +392,8 @@ int mlx4_en_free_tx_buf(struct net_device *dev, struct mlx4_en_tx_ring *ring)
        return cnt;
 }
 
-bool mlx4_en_process_tx_cq(struct net_device *dev,
-                          struct mlx4_en_cq *cq, int napi_budget)
+int mlx4_en_process_tx_cq(struct net_device *dev,
+                         struct mlx4_en_cq *cq, int napi_budget)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_cq *mcq = &cq->mcq;
@@ -405,7 +415,7 @@ bool mlx4_en_process_tx_cq(struct net_device *dev,
        u32 ring_cons;
 
        if (unlikely(!priv->port_up))
-               return true;
+               return 0;
 
        netdev_txq_bql_complete_prefetchw(ring->tx_queue);
 
@@ -449,7 +459,9 @@ bool mlx4_en_process_tx_cq(struct net_device *dev,
                                timestamp = mlx4_en_get_cqe_ts(cqe);
 
                        /* free next descriptor */
-                       last_nr_txbb = ring->free_tx_desc(
+                       last_nr_txbb = INDIRECT_CALL_2(ring->free_tx_desc,
+                                                      mlx4_en_free_tx_desc,
+                                                      mlx4_en_recycle_tx_desc,
                                        priv, ring, ring_index,
                                        timestamp, napi_budget);
 
@@ -480,7 +492,7 @@ bool mlx4_en_process_tx_cq(struct net_device *dev,
        WRITE_ONCE(ring->cons, ring_cons + txbbs_skipped);
 
        if (cq->type == TX_XDP)
-               return done < budget;
+               return done;
 
        netdev_tx_completed_queue(ring->tx_queue, packets, bytes);
 
@@ -492,7 +504,7 @@ bool mlx4_en_process_tx_cq(struct net_device *dev,
                ring->wake_queue++;
        }
 
-       return done < budget;
+       return done;
 }
 
 void mlx4_en_tx_irq(struct mlx4_cq *mcq)
@@ -512,14 +524,14 @@ int mlx4_en_poll_tx_cq(struct napi_struct *napi, int budget)
        struct mlx4_en_cq *cq = container_of(napi, struct mlx4_en_cq, napi);
        struct net_device *dev = cq->dev;
        struct mlx4_en_priv *priv = netdev_priv(dev);
-       bool clean_complete;
+       int work_done;
 
-       clean_complete = mlx4_en_process_tx_cq(dev, cq, budget);
-       if (!clean_complete)
+       work_done = mlx4_en_process_tx_cq(dev, cq, budget);
+       if (work_done >= budget)
                return budget;
 
-       napi_complete(napi);
-       mlx4_en_arm_cq(priv, cq);
+       if (napi_complete_done(napi, work_done))
+               mlx4_en_arm_cq(priv, cq);
 
        return 0;
 }
index 5716c3d..c72c4e1 100644 (file)
@@ -2550,6 +2550,7 @@ static int mlx4_allocate_default_counters(struct mlx4_dev *dev)
 
                if (!err || err == -ENOSPC) {
                        priv->def_counter[port] = idx;
+                       err = 0;
                } else if (err == -ENOENT) {
                        err = 0;
                        continue;
@@ -2600,7 +2601,8 @@ int mlx4_counter_alloc(struct mlx4_dev *dev, u32 *idx, u8 usage)
                                   MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED);
                if (!err)
                        *idx = get_param_l(&out_param);
-
+               if (WARN_ON(err == -ENOSPC))
+                       err = -EINVAL;
                return err;
        }
        return __mlx4_counter_alloc(dev, idx);
index 630f159..9f56036 100644 (file)
@@ -737,8 +737,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev,
                          int budget);
 int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget);
 int mlx4_en_poll_tx_cq(struct napi_struct *napi, int budget);
-bool mlx4_en_process_tx_cq(struct net_device *dev,
-                          struct mlx4_en_cq *cq, int napi_budget);
+int mlx4_en_process_tx_cq(struct net_device *dev,
+                         struct mlx4_en_cq *cq, int napi_budget);
 u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv,
                         struct mlx4_en_tx_ring *ring,
                         int index, u64 timestamp,
index 312e0a1..7d69a30 100644 (file)
@@ -7,10 +7,10 @@ config MLX5_CORE
        tristate "Mellanox 5th generation network adapters (ConnectX series) core driver"
        depends on PCI
        select NET_DEVLINK
-       imply PTP_1588_CLOCK
-       imply VXLAN
-       imply MLXFW
-       imply PCI_HYPERV_INTERFACE
+       depends on VXLAN || !VXLAN
+       depends on MLXFW || !MLXFW
+       depends on PTP_1588_CLOCK || !PTP_1588_CLOCK
+       depends on PCI_HYPERV_INTERFACE || !PCI_HYPERV_INTERFACE
        default n
        ---help---
          Core driver for low level functionality of the ConnectX-4 and
index 6d32915..d3c7dbd 100644 (file)
@@ -12,7 +12,7 @@ obj-$(CONFIG_MLX5_CORE) += mlx5_core.o
 # mlx5 core basic
 #
 mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \
-               health.o mcg.o cq.o alloc.o qp.o port.o mr.o pd.o \
+               health.o mcg.o cq.o alloc.o port.o mr.o pd.o \
                transobj.o vport.o sriov.o fs_cmd.o fs_core.o pci_irq.o \
                fs_counters.o rl.o lag.o dev.o events.o wq.o lib/gid.o \
                lib/devcom.o lib/pci_vsc.o lib/dm.o diag/fs_tracepoint.o \
index eddc34e..8a4985d 100644 (file)
@@ -58,12 +58,21 @@ int mlx5_accel_ipsec_counters_read(struct mlx5_core_dev *mdev, u64 *counters,
 
 void *mlx5_accel_esp_create_hw_context(struct mlx5_core_dev *mdev,
                                       struct mlx5_accel_esp_xfrm *xfrm,
-                                      const __be32 saddr[4],
-                                      const __be32 daddr[4],
-                                      const __be32 spi, bool is_ipv6)
+                                      u32 *sa_handle)
 {
-       return mlx5_fpga_ipsec_create_sa_ctx(mdev, xfrm, saddr, daddr,
-                                            spi, is_ipv6);
+       __be32 saddr[4] = {}, daddr[4] = {};
+
+       if (!xfrm->attrs.is_ipv6) {
+               saddr[3] = xfrm->attrs.saddr.a4;
+               daddr[3] = xfrm->attrs.daddr.a4;
+       } else {
+               memcpy(saddr, xfrm->attrs.saddr.a6, sizeof(saddr));
+               memcpy(daddr, xfrm->attrs.daddr.a6, sizeof(daddr));
+       }
+
+       return mlx5_fpga_ipsec_create_sa_ctx(mdev, xfrm, saddr,
+                                            daddr, xfrm->attrs.spi,
+                                            xfrm->attrs.is_ipv6, sa_handle);
 }
 
 void mlx5_accel_esp_free_hw_context(void *context)
index 530e428..e897476 100644 (file)
@@ -48,9 +48,7 @@ int mlx5_accel_ipsec_counters_read(struct mlx5_core_dev *mdev, u64 *counters,
 
 void *mlx5_accel_esp_create_hw_context(struct mlx5_core_dev *mdev,
                                       struct mlx5_accel_esp_xfrm *xfrm,
-                                      const __be32 saddr[4],
-                                      const __be32 daddr[4],
-                                      const __be32 spi, bool is_ipv6);
+                                      u32 *sa_handle);
 void mlx5_accel_esp_free_hw_context(void *context);
 
 int mlx5_accel_ipsec_init(struct mlx5_core_dev *mdev);
@@ -64,9 +62,7 @@ void mlx5_accel_ipsec_cleanup(struct mlx5_core_dev *mdev);
 static inline void *
 mlx5_accel_esp_create_hw_context(struct mlx5_core_dev *mdev,
                                 struct mlx5_accel_esp_xfrm *xfrm,
-                                const __be32 saddr[4],
-                                const __be32 daddr[4],
-                                const __be32 spi, bool is_ipv6)
+                                u32 *sa_handle)
 {
        return NULL;
 }
index 34cba97..cede5bd 100644 (file)
@@ -888,7 +888,6 @@ static void cmd_work_handler(struct work_struct *work)
        }
 
        cmd->ent_arr[ent->idx] = ent;
-       set_bit(MLX5_CMD_ENT_STATE_PENDING_COMP, &ent->state);
        lay = get_inst(cmd, ent->idx);
        ent->lay = lay;
        memset(lay, 0, sizeof(*lay));
@@ -910,6 +909,7 @@ static void cmd_work_handler(struct work_struct *work)
 
        if (ent->callback)
                schedule_delayed_work(&ent->cb_timeout_work, cb_timeout);
+       set_bit(MLX5_CMD_ENT_STATE_PENDING_COMP, &ent->state);
 
        /* Skip sending command to fw if internal error */
        if (pci_channel_offline(dev->pdev) ||
@@ -922,6 +922,10 @@ static void cmd_work_handler(struct work_struct *work)
                MLX5_SET(mbox_out, ent->out, syndrome, drv_synd);
 
                mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true);
+               /* no doorbell, no need to keep the entry */
+               free_ent(cmd, ent->idx);
+               if (ent->callback)
+                       free_cmd(ent);
                return;
        }
 
index 818edc6..8379b24 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/module.h>
 #include <linux/hardirq.h>
 #include <linux/mlx5/driver.h>
-#include <linux/mlx5/cmd.h>
 #include <rdma/ib_verbs.h>
 #include <linux/mlx5/cq.h>
 #include "mlx5_core.h"
@@ -91,8 +90,7 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
                        u32 *in, int inlen, u32 *out, int outlen)
 {
        int eqn = MLX5_GET(cqc, MLX5_ADDR_OF(create_cq_in, in, cq_context), c_eqn);
-       u32 dout[MLX5_ST_SZ_DW(destroy_cq_out)];
-       u32 din[MLX5_ST_SZ_DW(destroy_cq_in)];
+       u32 din[MLX5_ST_SZ_DW(destroy_cq_in)] = {};
        struct mlx5_eq_comp *eq;
        int err;
 
@@ -142,20 +140,17 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
 err_cq_add:
        mlx5_eq_del_cq(&eq->core, cq);
 err_cmd:
-       memset(din, 0, sizeof(din));
-       memset(dout, 0, sizeof(dout));
        MLX5_SET(destroy_cq_in, din, opcode, MLX5_CMD_OP_DESTROY_CQ);
        MLX5_SET(destroy_cq_in, din, cqn, cq->cqn);
        MLX5_SET(destroy_cq_in, din, uid, cq->uid);
-       mlx5_cmd_exec(dev, din, sizeof(din), dout, sizeof(dout));
+       mlx5_cmd_exec_in(dev, destroy_cq, din);
        return err;
 }
 EXPORT_SYMBOL(mlx5_core_create_cq);
 
 int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq)
 {
-       u32 out[MLX5_ST_SZ_DW(destroy_cq_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(destroy_cq_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_cq_in)] = {};
        int err;
 
        mlx5_eq_del_cq(mlx5_get_async_eq(dev), cq);
@@ -164,7 +159,7 @@ int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq)
        MLX5_SET(destroy_cq_in, in, opcode, MLX5_CMD_OP_DESTROY_CQ);
        MLX5_SET(destroy_cq_in, in, cqn, cq->cqn);
        MLX5_SET(destroy_cq_in, in, uid, cq->uid);
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_in(dev, destroy_cq, in);
        if (err)
                return err;
 
@@ -179,20 +174,20 @@ int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq)
 EXPORT_SYMBOL(mlx5_core_destroy_cq);
 
 int mlx5_core_query_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
-                      u32 *out, int outlen)
+                      u32 *out)
 {
-       u32 in[MLX5_ST_SZ_DW(query_cq_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(query_cq_in)] = {};
 
        MLX5_SET(query_cq_in, in, opcode, MLX5_CMD_OP_QUERY_CQ);
        MLX5_SET(query_cq_in, in, cqn, cq->cqn);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
+       return mlx5_cmd_exec_inout(dev, query_cq, in, out);
 }
 EXPORT_SYMBOL(mlx5_core_query_cq);
 
 int mlx5_core_modify_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
                        u32 *in, int inlen)
 {
-       u32 out[MLX5_ST_SZ_DW(modify_cq_out)] = {0};
+       u32 out[MLX5_ST_SZ_DW(modify_cq_out)] = {};
 
        MLX5_SET(modify_cq_in, in, opcode, MLX5_CMD_OP_MODIFY_CQ);
        MLX5_SET(modify_cq_in, in, uid, cq->uid);
@@ -205,7 +200,7 @@ int mlx5_core_modify_cq_moderation(struct mlx5_core_dev *dev,
                                   u16 cq_period,
                                   u16 cq_max_count)
 {
-       u32 in[MLX5_ST_SZ_DW(modify_cq_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(modify_cq_in)] = {};
        void *cqc;
 
        MLX5_SET(modify_cq_in, in, cqn, cq->cqn);
index 04854e5..6409090 100644 (file)
@@ -101,15 +101,15 @@ void mlx5_unregister_debugfs(void)
 
 void mlx5_qp_debugfs_init(struct mlx5_core_dev *dev)
 {
-       atomic_set(&dev->num_qps, 0);
-
        dev->priv.qp_debugfs = debugfs_create_dir("QPs",  dev->priv.dbg_root);
 }
+EXPORT_SYMBOL(mlx5_qp_debugfs_init);
 
 void mlx5_qp_debugfs_cleanup(struct mlx5_core_dev *dev)
 {
        debugfs_remove_recursive(dev->priv.qp_debugfs);
 }
+EXPORT_SYMBOL(mlx5_qp_debugfs_cleanup);
 
 void mlx5_eq_debugfs_init(struct mlx5_core_dev *dev)
 {
@@ -202,42 +202,37 @@ void mlx5_cq_debugfs_cleanup(struct mlx5_core_dev *dev)
 static u64 qp_read_field(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp,
                         int index, int *is_str)
 {
-       int outlen = MLX5_ST_SZ_BYTES(query_qp_out);
-       struct mlx5_qp_context *ctx;
+       u32 out[MLX5_ST_SZ_BYTES(query_qp_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(query_qp_in)] = {};
        u64 param = 0;
-       u32 *out;
+       int state;
+       u32 *qpc;
        int err;
-       int no_sq;
 
-       out = kzalloc(outlen, GFP_KERNEL);
-       if (!out)
-               return param;
-
-       err = mlx5_core_qp_query(dev, qp, out, outlen);
-       if (err) {
-               mlx5_core_warn(dev, "failed to query qp err=%d\n", err);
-               goto out;
-       }
+       MLX5_SET(query_qp_in, in, opcode, MLX5_CMD_OP_QUERY_QP);
+       MLX5_SET(query_qp_in, in, qpn, qp->qpn);
+       err = mlx5_cmd_exec_inout(dev, query_qp, in, out);
+       if (err)
+               return 0;
 
        *is_str = 0;
 
-       /* FIXME: use MLX5_GET rather than mlx5_qp_context manual struct */
-       ctx = (struct mlx5_qp_context *)MLX5_ADDR_OF(query_qp_out, out, qpc);
-
+       qpc = MLX5_ADDR_OF(query_qp_out, out, qpc);
        switch (index) {
        case QP_PID:
                param = qp->pid;
                break;
        case QP_STATE:
-               param = (unsigned long)mlx5_qp_state_str(be32_to_cpu(ctx->flags) >> 28);
+               state = MLX5_GET(qpc, qpc, state);
+               param = (unsigned long)mlx5_qp_state_str(state);
                *is_str = 1;
                break;
        case QP_XPORT:
-               param = (unsigned long)mlx5_qp_type_str((be32_to_cpu(ctx->flags) >> 16) & 0xff);
+               param = (unsigned long)mlx5_qp_type_str(MLX5_GET(qpc, qpc, st));
                *is_str = 1;
                break;
        case QP_MTU:
-               switch (ctx->mtu_msgmax >> 5) {
+               switch (MLX5_GET(qpc, qpc, mtu)) {
                case IB_MTU_256:
                        param = 256;
                        break;
@@ -258,46 +253,31 @@ static u64 qp_read_field(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp,
                }
                break;
        case QP_N_RECV:
-               param = 1 << ((ctx->rq_size_stride >> 3) & 0xf);
+               param = 1 << MLX5_GET(qpc, qpc, log_rq_size);
                break;
        case QP_RECV_SZ:
-               param = 1 << ((ctx->rq_size_stride & 7) + 4);
+               param = 1 << (MLX5_GET(qpc, qpc, log_rq_stride) + 4);
                break;
        case QP_N_SEND:
-               no_sq = be16_to_cpu(ctx->sq_crq_size) >> 15;
-               if (!no_sq)
-                       param = 1 << (be16_to_cpu(ctx->sq_crq_size) >> 11);
-               else
-                       param = 0;
+               if (!MLX5_GET(qpc, qpc, no_sq))
+                       param = 1 << MLX5_GET(qpc, qpc, log_sq_size);
                break;
        case QP_LOG_PG_SZ:
-               param = (be32_to_cpu(ctx->log_pg_sz_remote_qpn) >> 24) & 0x1f;
-               param += 12;
+               param = MLX5_GET(qpc, qpc, log_page_size) + 12;
                break;
        case QP_RQPN:
-               param = be32_to_cpu(ctx->log_pg_sz_remote_qpn) & 0xffffff;
+               param = MLX5_GET(qpc, qpc, remote_qpn);
                break;
        }
 
-out:
-       kfree(out);
        return param;
 }
 
-static int mlx5_core_eq_query(struct mlx5_core_dev *dev, struct mlx5_eq *eq,
-                             u32 *out, int outlen)
-{
-       u32 in[MLX5_ST_SZ_DW(query_eq_in)] = {};
-
-       MLX5_SET(query_eq_in, in, opcode, MLX5_CMD_OP_QUERY_EQ);
-       MLX5_SET(query_eq_in, in, eq_number, eq->eqn);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
-}
-
 static u64 eq_read_field(struct mlx5_core_dev *dev, struct mlx5_eq *eq,
                         int index)
 {
        int outlen = MLX5_ST_SZ_BYTES(query_eq_out);
+       u32 in[MLX5_ST_SZ_DW(query_eq_in)] = {};
        u64 param = 0;
        void *ctx;
        u32 *out;
@@ -307,7 +287,9 @@ static u64 eq_read_field(struct mlx5_core_dev *dev, struct mlx5_eq *eq,
        if (!out)
                return param;
 
-       err = mlx5_core_eq_query(dev, eq, out, outlen);
+       MLX5_SET(query_eq_in, in, opcode, MLX5_CMD_OP_QUERY_EQ);
+       MLX5_SET(query_eq_in, in, eq_number, eq->eqn);
+       err = mlx5_cmd_exec_inout(dev, query_eq, in, out);
        if (err) {
                mlx5_core_warn(dev, "failed to query eq\n");
                goto out;
@@ -344,7 +326,7 @@ static u64 cq_read_field(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
        if (!out)
                return param;
 
-       err = mlx5_core_query_cq(dev, cq, out, outlen);
+       err = mlx5_core_query_cq(dev, cq, out);
        if (err) {
                mlx5_core_warn(dev, "failed to query cq\n");
                goto out;
@@ -461,6 +443,7 @@ int mlx5_debug_qp_add(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp)
 
        return err;
 }
+EXPORT_SYMBOL(mlx5_debug_qp_add);
 
 void mlx5_debug_qp_remove(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp)
 {
@@ -470,6 +453,7 @@ void mlx5_debug_qp_remove(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp)
        if (qp->dbg)
                rem_res_tree(qp->dbg);
 }
+EXPORT_SYMBOL(mlx5_debug_qp_remove);
 
 int mlx5_debug_eq_add(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
 {
index bdeb291..e94f0c4 100644 (file)
@@ -23,7 +23,10 @@ static int mlx5_devlink_flash_update(struct devlink *devlink,
        if (err)
                return err;
 
-       return mlx5_firmware_flash(dev, fw, extack);
+       err = mlx5_firmware_flash(dev, fw, extack);
+       release_firmware(fw);
+
+       return err;
 }
 
 static u8 mlx5_fw_ver_major(u32 version)
index c9c9b47..a755127 100644 (file)
@@ -684,7 +684,7 @@ static void mlx5_fw_tracer_handle_traces(struct work_struct *work)
                get_block_timestamp(tracer, &tmp_trace_block[TRACES_PER_BLOCK - 1]);
 
        while (block_timestamp > tracer->last_timestamp) {
-               /* Check block override if its not the first block */
+               /* Check block override if it's not the first block */
                if (!tracer->last_timestamp) {
                        u64 *ts_event;
                        /* To avoid block override be the HW in case of buffer
@@ -935,7 +935,7 @@ struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev)
                return NULL;
        }
 
-       tracer = kzalloc(sizeof(*tracer), GFP_KERNEL);
+       tracer = kvzalloc(sizeof(*tracer), GFP_KERNEL);
        if (!tracer)
                return ERR_PTR(-ENOMEM);
 
@@ -982,7 +982,7 @@ destroy_workqueue:
        tracer->dev = NULL;
        destroy_workqueue(tracer->work_queue);
 free_tracer:
-       kfree(tracer);
+       kvfree(tracer);
        return ERR_PTR(err);
 }
 
@@ -1061,7 +1061,7 @@ void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer)
        mlx5_fw_tracer_destroy_log_buf(tracer);
        flush_workqueue(tracer->work_queue);
        destroy_workqueue(tracer->work_queue);
-       kfree(tracer);
+       kvfree(tracer);
 }
 
 static int fw_tracer_event(struct notifier_block *nb, unsigned long action, void *data)
index d2228e3..a894ea9 100644 (file)
@@ -8,33 +8,13 @@ bool mlx5_read_embedded_cpu(struct mlx5_core_dev *dev)
        return (ioread32be(&dev->iseg->initializing) >> MLX5_ECPU_BIT_NUM) & 1;
 }
 
-static int mlx5_peer_pf_enable_hca(struct mlx5_core_dev *dev)
-{
-       u32 out[MLX5_ST_SZ_DW(enable_hca_out)] = {};
-       u32 in[MLX5_ST_SZ_DW(enable_hca_in)]   = {};
-
-       MLX5_SET(enable_hca_in, in, opcode, MLX5_CMD_OP_ENABLE_HCA);
-       MLX5_SET(enable_hca_in, in, function_id, 0);
-       MLX5_SET(enable_hca_in, in, embedded_cpu_function, 0);
-       return mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
-}
-
-static int mlx5_peer_pf_disable_hca(struct mlx5_core_dev *dev)
-{
-       u32 out[MLX5_ST_SZ_DW(disable_hca_out)] = {};
-       u32 in[MLX5_ST_SZ_DW(disable_hca_in)]   = {};
-
-       MLX5_SET(disable_hca_in, in, opcode, MLX5_CMD_OP_DISABLE_HCA);
-       MLX5_SET(disable_hca_in, in, function_id, 0);
-       MLX5_SET(disable_hca_in, in, embedded_cpu_function, 0);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-
 static int mlx5_peer_pf_init(struct mlx5_core_dev *dev)
 {
+       u32 in[MLX5_ST_SZ_DW(enable_hca_in)] = {};
        int err;
 
-       err = mlx5_peer_pf_enable_hca(dev);
+       MLX5_SET(enable_hca_in, in, opcode, MLX5_CMD_OP_ENABLE_HCA);
+       err = mlx5_cmd_exec_in(dev, enable_hca, in);
        if (err)
                mlx5_core_err(dev, "Failed to enable peer PF HCA err(%d)\n",
                              err);
@@ -44,9 +24,11 @@ static int mlx5_peer_pf_init(struct mlx5_core_dev *dev)
 
 static void mlx5_peer_pf_cleanup(struct mlx5_core_dev *dev)
 {
+       u32 in[MLX5_ST_SZ_DW(disable_hca_in)] = {};
        int err;
 
-       err = mlx5_peer_pf_disable_hca(dev);
+       MLX5_SET(disable_hca_in, in, opcode, MLX5_CMD_OP_DISABLE_HCA);
+       err = mlx5_cmd_exec_in(dev, disable_hca, in);
        if (err) {
                mlx5_core_err(dev, "Failed to disable peer PF HCA err(%d)\n",
                              err);
index 12a61bf..0864b76 100644 (file)
@@ -367,9 +367,10 @@ enum {
        MLX5E_SQ_STATE_AM,
        MLX5E_SQ_STATE_TLS,
        MLX5E_SQ_STATE_VLAN_NEED_L2_INLINE,
+       MLX5E_SQ_STATE_PENDING_XSK_TX,
 };
 
-struct mlx5e_sq_wqe_info {
+struct mlx5e_icosq_wqe_info {
        u8  opcode;
        u8 num_wqebbs;
 
@@ -551,7 +552,7 @@ struct mlx5e_icosq {
 
        /* write@xmit, read@completion */
        struct {
-               struct mlx5e_sq_wqe_info *ico_wqe;
+               struct mlx5e_icosq_wqe_info *wqe_info;
        } db;
 
        /* read only */
@@ -960,7 +961,7 @@ void mlx5e_page_release_dynamic(struct mlx5e_rq *rq,
 void mlx5e_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe);
 void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe);
 bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq);
-void mlx5e_poll_ico_cq(struct mlx5e_cq *cq);
+int mlx5e_poll_ico_cq(struct mlx5e_cq *cq);
 bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq);
 void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix);
 void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix);
@@ -1012,7 +1013,7 @@ int mlx5e_redirect_rqt(struct mlx5e_priv *priv, u32 rqtn, int sz,
 void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_rss_params *rss_params,
                                    const struct mlx5e_tirc_config *ttconfig,
                                    void *tirc, bool inner);
-void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen);
+void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in);
 struct mlx5e_tirc_config mlx5e_tirc_get_default_config(enum mlx5e_traffic_types tt);
 
 struct mlx5e_xsk_param;
@@ -1102,8 +1103,8 @@ void mlx5e_dcbnl_init_app(struct mlx5e_priv *priv);
 void mlx5e_dcbnl_delete_app(struct mlx5e_priv *priv);
 #endif
 
-int mlx5e_create_tir(struct mlx5_core_dev *mdev,
-                    struct mlx5e_tir *tir, u32 *in, int inlen);
+int mlx5e_create_tir(struct mlx5_core_dev *mdev, struct mlx5e_tir *tir,
+                    u32 *in);
 void mlx5e_destroy_tir(struct mlx5_core_dev *mdev,
                       struct mlx5e_tir *tir);
 int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev);
index 3a199a0..7283443 100644 (file)
@@ -43,7 +43,7 @@ int mlx5e_reporter_cq_diagnose(struct mlx5e_cq *cq, struct devlink_fmsg *fmsg)
        void *cqc;
        int err;
 
-       err = mlx5_core_query_cq(priv->mdev, &cq->mcq, out, sizeof(out));
+       err = mlx5_core_query_cq(priv->mdev, &cq->mcq, out);
        if (err)
                return err;
 
index 7cd5b02..8fe8b4d 100644 (file)
@@ -38,12 +38,11 @@ int mlx5e_monitor_counter_supported(struct mlx5e_priv *priv)
 
 void mlx5e_monitor_counter_arm(struct mlx5e_priv *priv)
 {
-       u32  in[MLX5_ST_SZ_DW(arm_monitor_counter_in)]  = {};
-       u32 out[MLX5_ST_SZ_DW(arm_monitor_counter_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(arm_monitor_counter_in)] = {};
 
        MLX5_SET(arm_monitor_counter_in, in, opcode,
                 MLX5_CMD_OP_ARM_MONITOR_COUNTER);
-       mlx5_cmd_exec(priv->mdev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(priv->mdev, arm_monitor_counter, in);
 }
 
 static void mlx5e_monitor_counters_work(struct work_struct *work)
@@ -66,19 +65,6 @@ static int mlx5e_monitor_event_handler(struct notifier_block *nb,
        return NOTIFY_OK;
 }
 
-static void mlx5e_monitor_counter_start(struct mlx5e_priv *priv)
-{
-       MLX5_NB_INIT(&priv->monitor_counters_nb, mlx5e_monitor_event_handler,
-                    MONITOR_COUNTER);
-       mlx5_eq_notifier_register(priv->mdev, &priv->monitor_counters_nb);
-}
-
-static void mlx5e_monitor_counter_stop(struct mlx5e_priv *priv)
-{
-       mlx5_eq_notifier_unregister(priv->mdev, &priv->monitor_counters_nb);
-       cancel_work_sync(&priv->monitor_counters_work);
-}
-
 static int fill_monitor_counter_ppcnt_set1(int cnt, u32 *in)
 {
        enum mlx5_monitor_counter_ppcnt ppcnt_cnt;
@@ -118,8 +104,7 @@ static void mlx5e_set_monitor_counter(struct mlx5e_priv *priv)
        int num_q_counters      = MLX5_CAP_GEN(mdev, num_q_monitor_counters);
        int num_ppcnt_counters  = !MLX5_CAP_PCAM_REG(mdev, ppcnt) ? 0 :
                                  MLX5_CAP_GEN(mdev, num_ppcnt_monitor_counters);
-       u32  in[MLX5_ST_SZ_DW(set_monitor_counter_in)]  = {};
-       u32 out[MLX5_ST_SZ_DW(set_monitor_counter_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(set_monitor_counter_in)] = {};
        int q_counter = priv->q_counter;
        int cnt = 0;
 
@@ -136,34 +121,31 @@ static void mlx5e_set_monitor_counter(struct mlx5e_priv *priv)
        MLX5_SET(set_monitor_counter_in, in, opcode,
                 MLX5_CMD_OP_SET_MONITOR_COUNTER);
 
-       mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(mdev, set_monitor_counter, in);
 }
 
 /* check if mlx5e_monitor_counter_supported before calling this function*/
 void mlx5e_monitor_counter_init(struct mlx5e_priv *priv)
 {
        INIT_WORK(&priv->monitor_counters_work, mlx5e_monitor_counters_work);
-       mlx5e_monitor_counter_start(priv);
+       MLX5_NB_INIT(&priv->monitor_counters_nb, mlx5e_monitor_event_handler,
+                    MONITOR_COUNTER);
+       mlx5_eq_notifier_register(priv->mdev, &priv->monitor_counters_nb);
+
        mlx5e_set_monitor_counter(priv);
        mlx5e_monitor_counter_arm(priv);
        queue_work(priv->wq, &priv->update_stats_work);
 }
 
-static void mlx5e_monitor_counter_disable(struct mlx5e_priv *priv)
+/* check if mlx5e_monitor_counter_supported before calling this function*/
+void mlx5e_monitor_counter_cleanup(struct mlx5e_priv *priv)
 {
-       u32  in[MLX5_ST_SZ_DW(set_monitor_counter_in)]  = {};
-       u32 out[MLX5_ST_SZ_DW(set_monitor_counter_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(set_monitor_counter_in)] = {};
 
-       MLX5_SET(set_monitor_counter_in, in, num_of_counters, 0);
        MLX5_SET(set_monitor_counter_in, in, opcode,
                 MLX5_CMD_OP_SET_MONITOR_COUNTER);
 
-       mlx5_cmd_exec(priv->mdev, in, sizeof(in), out, sizeof(out));
-}
-
-/* check if mlx5e_monitor_counter_supported before calling this function*/
-void mlx5e_monitor_counter_cleanup(struct mlx5e_priv *priv)
-{
-       mlx5e_monitor_counter_disable(priv);
-       mlx5e_monitor_counter_stop(priv);
+       mlx5_cmd_exec_in(priv->mdev, set_monitor_counter, in);
+       mlx5_eq_notifier_unregister(priv->mdev, &priv->monitor_counters_nb);
+       cancel_work_sync(&priv->monitor_counters_work);
 }
index ad3e3a6..5568ded 100644 (file)
@@ -12,6 +12,7 @@
 #include <net/flow_offload.h>
 #include <net/netfilter/nf_flow_table.h>
 #include <linux/workqueue.h>
+#include <linux/xarray.h>
 
 #include "esw/chains.h"
 #include "en/tc_ct.h"
@@ -35,7 +36,7 @@ struct mlx5_tc_ct_priv {
        struct mlx5_eswitch *esw;
        const struct net_device *netdev;
        struct idr fte_ids;
-       struct idr tuple_ids;
+       struct xarray tuple_ids;
        struct rhashtable zone_ht;
        struct mlx5_flow_table *ct;
        struct mlx5_flow_table *ct_nat;
@@ -67,16 +68,12 @@ struct mlx5_ct_ft {
        struct nf_flowtable *nf_ft;
        struct mlx5_tc_ct_priv *ct_priv;
        struct rhashtable ct_entries_ht;
-       struct list_head ct_entries_list;
 };
 
 struct mlx5_ct_entry {
-       struct list_head list;
        u16 zone;
        struct rhash_head node;
-       struct flow_rule *flow_rule;
        struct mlx5_fc *counter;
-       unsigned long lastuse;
        unsigned long cookie;
        unsigned long restore_cookie;
        struct mlx5_ct_zone_rule zone_rules[2];
@@ -240,7 +237,7 @@ mlx5_tc_ct_entry_del_rule(struct mlx5_tc_ct_priv *ct_priv,
 
        mlx5_eswitch_del_offloaded_rule(esw, zone_rule->rule, attr);
        mlx5_modify_header_dealloc(esw->dev, attr->modify_hdr);
-       idr_remove(&ct_priv->tuple_ids, zone_rule->tupleid);
+       xa_erase(&ct_priv->tuple_ids, zone_rule->tupleid);
 }
 
 static void
@@ -385,7 +382,7 @@ mlx5_tc_ct_entry_create_nat(struct mlx5_tc_ct_priv *ct_priv,
        char *modact;
        int err, i;
 
-       action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto);
+       action_size = MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto);
 
        flow_action_for_each(i, act, flow_action) {
                switch (act->id) {
@@ -485,7 +482,7 @@ mlx5_tc_ct_entry_add_rule(struct mlx5_tc_ct_priv *ct_priv,
        struct mlx5_esw_flow_attr *attr = &zone_rule->attr;
        struct mlx5_eswitch *esw = ct_priv->esw;
        struct mlx5_flow_spec *spec = NULL;
-       u32 tupleid = 1;
+       u32 tupleid;
        int err;
 
        zone_rule->nat = nat;
@@ -495,12 +492,12 @@ mlx5_tc_ct_entry_add_rule(struct mlx5_tc_ct_priv *ct_priv,
                return -ENOMEM;
 
        /* Get tuple unique id */
-       err = idr_alloc_u32(&ct_priv->tuple_ids, zone_rule, &tupleid,
-                           TUPLE_ID_MAX, GFP_KERNEL);
+       err = xa_alloc(&ct_priv->tuple_ids, &tupleid, zone_rule,
+                      XA_LIMIT(1, TUPLE_ID_MAX), GFP_KERNEL);
        if (err) {
                netdev_warn(ct_priv->netdev,
                            "Failed to allocate tuple id, err: %d\n", err);
-               goto err_idr_alloc;
+               goto err_xa_alloc;
        }
        zone_rule->tupleid = tupleid;
 
@@ -541,8 +538,8 @@ mlx5_tc_ct_entry_add_rule(struct mlx5_tc_ct_priv *ct_priv,
 err_rule:
        mlx5_modify_header_dealloc(esw->dev, attr->modify_hdr);
 err_mod_hdr:
-       idr_remove(&ct_priv->tuple_ids, zone_rule->tupleid);
-err_idr_alloc:
+       xa_erase(&ct_priv->tuple_ids, zone_rule->tupleid);
+err_xa_alloc:
        kfree(spec);
        return err;
 }
@@ -604,7 +601,6 @@ mlx5_tc_ct_block_flow_offload_add(struct mlx5_ct_ft *ft,
                return -ENOMEM;
 
        entry->zone = ft->zone;
-       entry->flow_rule = flow_rule;
        entry->cookie = flow->cookie;
        entry->restore_cookie = meta_action->ct_metadata.cookie;
 
@@ -617,8 +613,6 @@ mlx5_tc_ct_block_flow_offload_add(struct mlx5_ct_ft *ft,
        if (err)
                goto err_insert;
 
-       list_add(&entry->list, &ft->ct_entries_list);
-
        return 0;
 
 err_insert:
@@ -646,7 +640,6 @@ mlx5_tc_ct_block_flow_offload_del(struct mlx5_ct_ft *ft,
        WARN_ON(rhashtable_remove_fast(&ft->ct_entries_ht,
                                       &entry->node,
                                       cts_ht_params));
-       list_del(&entry->list);
        kfree(entry);
 
        return 0;
@@ -691,7 +684,7 @@ mlx5_tc_ct_block_flow_offload(enum tc_setup_type type, void *type_data,
                return mlx5_tc_ct_block_flow_offload_stats(ft, f);
        default:
                break;
-       };
+       }
 
        return -EOPNOTSUPP;
 }
@@ -818,7 +811,6 @@ mlx5_tc_ct_add_ft_cb(struct mlx5_tc_ct_priv *ct_priv, u16 zone,
        ft->zone = zone;
        ft->nf_ft = nf_ft;
        ft->ct_priv = ct_priv;
-       INIT_LIST_HEAD(&ft->ct_entries_list);
        refcount_set(&ft->refcount, 1);
 
        err = rhashtable_init(&ft->ct_entries_ht, &cts_ht_params);
@@ -847,12 +839,12 @@ err_init:
 }
 
 static void
-mlx5_tc_ct_flush_ft(struct mlx5_tc_ct_priv *ct_priv, struct mlx5_ct_ft *ft)
+mlx5_tc_ct_flush_ft_entry(void *ptr, void *arg)
 {
-       struct mlx5_ct_entry *entry;
+       struct mlx5_tc_ct_priv *ct_priv = arg;
+       struct mlx5_ct_entry *entry = ptr;
 
-       list_for_each_entry(entry, &ft->ct_entries_list, list)
-               mlx5_tc_ct_entry_del_rules(ft->ct_priv, entry);
+       mlx5_tc_ct_entry_del_rules(ct_priv, entry);
 }
 
 static void
@@ -863,9 +855,10 @@ mlx5_tc_ct_del_ft_cb(struct mlx5_tc_ct_priv *ct_priv, struct mlx5_ct_ft *ft)
 
        nf_flow_table_offload_del_cb(ft->nf_ft,
                                     mlx5_tc_ct_block_flow_offload, ft);
-       mlx5_tc_ct_flush_ft(ct_priv, ft);
        rhashtable_remove_fast(&ct_priv->zone_ht, &ft->node, zone_params);
-       rhashtable_destroy(&ft->ct_entries_ht);
+       rhashtable_free_and_destroy(&ft->ct_entries_ht,
+                                   mlx5_tc_ct_flush_ft_entry,
+                                   ct_priv);
        kfree(ft);
 }
 
@@ -1135,7 +1128,7 @@ mlx5_tc_ct_flow_offload(struct mlx5e_priv *priv,
 {
        bool clear_action = attr->ct_attr.ct_action & TCA_CT_ACT_CLEAR;
        struct mlx5_tc_ct_priv *ct_priv = mlx5_tc_ct_get_ct_priv(priv);
-       struct mlx5_flow_handle *rule;
+       struct mlx5_flow_handle *rule = ERR_PTR(-EINVAL);
        int err;
 
        if (!ct_priv)
@@ -1304,7 +1297,7 @@ mlx5_tc_ct_init(struct mlx5_rep_uplink_priv *uplink_priv)
        }
 
        idr_init(&ct_priv->fte_ids);
-       idr_init(&ct_priv->tuple_ids);
+       xa_init_flags(&ct_priv->tuple_ids, XA_FLAGS_ALLOC1);
        mutex_init(&ct_priv->control_lock);
        rhashtable_init(&ct_priv->zone_ht, &zone_params);
 
@@ -1339,7 +1332,7 @@ mlx5_tc_ct_clean(struct mlx5_rep_uplink_priv *uplink_priv)
 
        rhashtable_destroy(&ct_priv->zone_ht);
        mutex_destroy(&ct_priv->control_lock);
-       idr_destroy(&ct_priv->tuple_ids);
+       xa_destroy(&ct_priv->tuple_ids);
        idr_destroy(&ct_priv->fte_ids);
        kfree(ct_priv);
 
@@ -1357,7 +1350,7 @@ mlx5e_tc_ct_restore_flow(struct mlx5_rep_uplink_priv *uplink_priv,
        if (!ct_priv || !tupleid)
                return true;
 
-       zone_rule = idr_find(&ct_priv->tuple_ids, tupleid);
+       zone_rule = xa_load(&ct_priv->tuple_ids, tupleid);
        if (!zone_rule)
                return false;
 
index f07b139..89fe655 100644 (file)
@@ -33,19 +33,19 @@ mlx5e_wqc_has_room_for(struct mlx5_wq_cyc *wq, u16 cc, u16 pc, u16 n)
        return (mlx5_wq_cyc_ctr2ix(wq, cc - pc) >= n) || (cc == pc);
 }
 
-static inline void *
-mlx5e_sq_fetch_wqe(struct mlx5e_txqsq *sq, size_t size, u16 *pi)
+static inline void *mlx5e_fetch_wqe(struct mlx5_wq_cyc *wq, u16 pi, size_t wqe_size)
 {
-       struct mlx5_wq_cyc *wq = &sq->wq;
        void *wqe;
 
-       *pi  = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
-       wqe = mlx5_wq_cyc_get_wqe(wq, *pi);
-       memset(wqe, 0, size);
+       wqe = mlx5_wq_cyc_get_wqe(wq, pi);
+       memset(wqe, 0, wqe_size);
 
        return wqe;
 }
 
+#define MLX5E_TX_FETCH_WQE(sq, pi) \
+       ((struct mlx5e_tx_wqe *)mlx5e_fetch_wqe(&(sq)->wq, pi, sizeof(struct mlx5e_tx_wqe)))
+
 static inline struct mlx5e_tx_wqe *
 mlx5e_post_nop(struct mlx5_wq_cyc *wq, u32 sqn, u16 *pc)
 {
@@ -81,6 +81,62 @@ mlx5e_post_nop_fence(struct mlx5_wq_cyc *wq, u32 sqn, u16 *pc)
        return wqe;
 }
 
+static inline u16 mlx5e_txqsq_get_next_pi(struct mlx5e_txqsq *sq, u16 size)
+{
+       struct mlx5_wq_cyc *wq = &sq->wq;
+       u16 pi, contig_wqebbs;
+
+       pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
+       contig_wqebbs = mlx5_wq_cyc_get_contig_wqebbs(wq, pi);
+       if (unlikely(contig_wqebbs < size)) {
+               struct mlx5e_tx_wqe_info *wi, *edge_wi;
+
+               wi = &sq->db.wqe_info[pi];
+               edge_wi = wi + contig_wqebbs;
+
+               /* Fill SQ frag edge with NOPs to avoid WQE wrapping two pages. */
+               for (; wi < edge_wi; wi++) {
+                       *wi = (struct mlx5e_tx_wqe_info) {
+                               .num_wqebbs = 1,
+                       };
+                       mlx5e_post_nop(wq, sq->sqn, &sq->pc);
+               }
+               sq->stats->nop += contig_wqebbs;
+
+               pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
+       }
+
+       return pi;
+}
+
+static inline u16 mlx5e_icosq_get_next_pi(struct mlx5e_icosq *sq, u16 size)
+{
+       struct mlx5_wq_cyc *wq = &sq->wq;
+       u16 pi, contig_wqebbs;
+
+       pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
+       contig_wqebbs = mlx5_wq_cyc_get_contig_wqebbs(wq, pi);
+       if (unlikely(contig_wqebbs < size)) {
+               struct mlx5e_icosq_wqe_info *wi, *edge_wi;
+
+               wi = &sq->db.wqe_info[pi];
+               edge_wi = wi + contig_wqebbs;
+
+               /* Fill SQ frag edge with NOPs to avoid WQE wrapping two pages. */
+               for (; wi < edge_wi; wi++) {
+                       *wi = (struct mlx5e_icosq_wqe_info) {
+                               .opcode = MLX5_OPCODE_NOP,
+                               .num_wqebbs = 1,
+                       };
+                       mlx5e_post_nop(wq, sq->sqn, &sq->pc);
+               }
+
+               pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
+       }
+
+       return pi;
+}
+
 static inline void
 mlx5e_fill_sq_frag_edge(struct mlx5e_txqsq *sq, struct mlx5_wq_cyc *wq,
                        u16 pi, u16 nnops)
@@ -102,7 +158,7 @@ static inline void
 mlx5e_notify_hw(struct mlx5_wq_cyc *wq, u16 pc, void __iomem *uar_map,
                struct mlx5_wqe_ctrl_seg *ctrl)
 {
-       ctrl->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE;
+       ctrl->fm_ce_se |= MLX5_WQE_CTRL_CQ_UPDATE;
        /* ensure wqe is visible to device before updating doorbell record */
        dma_wmb();
 
@@ -189,6 +245,22 @@ static inline void mlx5e_rqwq_reset(struct mlx5e_rq *rq)
        }
 }
 
+static inline void mlx5e_dump_error_cqe(struct mlx5e_cq *cq, u32 sqn,
+                                       struct mlx5_err_cqe *err_cqe)
+{
+       struct mlx5_cqwq *wq = &cq->wq;
+       u32 ci;
+
+       ci = mlx5_cqwq_ctr2ix(wq, wq->cc - 1);
+
+       netdev_err(cq->channel->netdev,
+                  "Error cqe on cqn 0x%x, ci 0x%x, sqn 0x%x, opcode 0x%x, syndrome 0x%x, vendor syndrome 0x%x\n",
+                  cq->mcq.cqn, ci, sqn,
+                  get_cqe_opcode((struct mlx5_cqe64 *)err_cqe),
+                  err_cqe->syndrome, err_cqe->vendor_err_synd);
+       mlx5_dump_err_cqe(cq->mdev, err_cqe);
+}
+
 /* SW parser related functions */
 
 struct mlx5e_swp_spec {
index f049e0a..c4a7fb4 100644 (file)
@@ -178,20 +178,43 @@ xdp_abort:
        }
 }
 
-static void mlx5e_xdp_mpwqe_session_start(struct mlx5e_xdpsq *sq)
+static u16 mlx5e_xdpsq_get_next_pi(struct mlx5e_xdpsq *sq, u16 size)
 {
-       struct mlx5e_xdp_mpwqe *session = &sq->mpwqe;
-       struct mlx5e_xdpsq_stats *stats = sq->stats;
        struct mlx5_wq_cyc *wq = &sq->wq;
        u16 pi, contig_wqebbs;
 
        pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
        contig_wqebbs = mlx5_wq_cyc_get_contig_wqebbs(wq, pi);
+       if (unlikely(contig_wqebbs < size)) {
+               struct mlx5e_xdp_wqe_info *wi, *edge_wi;
+
+               wi = &sq->db.wqe_info[pi];
+               edge_wi = wi + contig_wqebbs;
+
+               /* Fill SQ frag edge with NOPs to avoid WQE wrapping two pages. */
+               for (; wi < edge_wi; wi++) {
+                       *wi = (struct mlx5e_xdp_wqe_info) {
+                               .num_wqebbs = 1,
+                               .num_pkts = 0,
+                       };
+                       mlx5e_post_nop(wq, sq->sqn, &sq->pc);
+               }
+               sq->stats->nops += contig_wqebbs;
+
+               pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
+       }
 
-       if (unlikely(contig_wqebbs < MLX5_SEND_WQE_MAX_WQEBBS))
-               mlx5e_fill_xdpsq_frag_edge(sq, wq, pi, contig_wqebbs);
+       return pi;
+}
+
+static void mlx5e_xdp_mpwqe_session_start(struct mlx5e_xdpsq *sq)
+{
+       struct mlx5e_xdp_mpwqe *session = &sq->mpwqe;
+       struct mlx5e_xdpsq_stats *stats = sq->stats;
+       u16 pi;
 
-       session->wqe = mlx5e_xdpsq_fetch_wqe(sq, &pi);
+       pi = mlx5e_xdpsq_get_next_pi(sq, MLX5_SEND_WQE_MAX_WQEBBS);
+       session->wqe = MLX5E_TX_FETCH_WQE(sq, pi);
 
        prefetchw(session->wqe->data);
        session->ds_count  = MLX5E_XDP_TX_EMPTY_DS_COUNT;
@@ -408,22 +431,15 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq)
 
        i = 0;
        do {
-               u16 wqe_counter;
+               struct mlx5e_xdp_wqe_info *wi;
+               u16 wqe_counter, ci;
                bool last_wqe;
 
                mlx5_cqwq_pop(&cq->wq);
 
                wqe_counter = be16_to_cpu(cqe->wqe_counter);
 
-               if (unlikely(get_cqe_opcode(cqe) != MLX5_CQE_REQ))
-                       netdev_WARN_ONCE(sq->channel->netdev,
-                                        "Bad OP in XDPSQ CQE: 0x%x\n",
-                                        get_cqe_opcode(cqe));
-
                do {
-                       struct mlx5e_xdp_wqe_info *wi;
-                       u16 ci;
-
                        last_wqe = (sqcc == wqe_counter);
                        ci = mlx5_wq_cyc_ctr2ix(&sq->wq, sqcc);
                        wi = &sq->db.wqe_info[ci];
@@ -432,6 +448,15 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq)
 
                        mlx5e_free_xdpsq_desc(sq, wi, &xsk_frames, true);
                } while (!last_wqe);
+
+               if (unlikely(get_cqe_opcode(cqe) != MLX5_CQE_REQ)) {
+                       netdev_WARN_ONCE(sq->channel->netdev,
+                                        "Bad OP in XDPSQ CQE: 0x%x\n",
+                                        get_cqe_opcode(cqe));
+                       mlx5e_dump_error_cqe(&sq->cq, sq->sqn,
+                                            (struct mlx5_err_cqe *)cqe);
+                       mlx5_wq_cyc_wqe_dump(&sq->wq, ci, wi->num_wqebbs);
+               }
        } while ((++i < MLX5E_TX_CQ_POLL_BUDGET) && (cqe = mlx5_cqwq_get_cqe(&cq->wq)));
 
        if (xsk_frames)
index d7587f4..ed6f045 100644 (file)
@@ -137,23 +137,6 @@ mlx5e_xdp_no_room_for_inline_pkt(struct mlx5e_xdp_mpwqe *session)
               session->ds_count + MLX5E_XDP_INLINE_WQE_MAX_DS_CNT > MLX5E_XDP_MPW_MAX_NUM_DS;
 }
 
-static inline void
-mlx5e_fill_xdpsq_frag_edge(struct mlx5e_xdpsq *sq, struct mlx5_wq_cyc *wq,
-                          u16 pi, u16 nnops)
-{
-       struct mlx5e_xdp_wqe_info *edge_wi, *wi = &sq->db.wqe_info[pi];
-
-       edge_wi = wi + nnops;
-       /* fill sq frag edge with nops to avoid wqe wrapping two pages */
-       for (; wi < edge_wi; wi++) {
-               wi->num_wqebbs = 1;
-               wi->num_pkts   = 0;
-               mlx5e_post_nop(wq, sq->sqn, &sq->pc);
-       }
-
-       sq->stats->nops += nnops;
-}
-
 static inline void
 mlx5e_xdp_mpwqe_add_dseg(struct mlx5e_xdpsq *sq,
                         struct mlx5e_xdp_xmit_data *xdptxd,
@@ -186,19 +169,6 @@ mlx5e_xdp_mpwqe_add_dseg(struct mlx5e_xdpsq *sq,
        session->ds_count++;
 }
 
-static inline struct mlx5e_tx_wqe *
-mlx5e_xdpsq_fetch_wqe(struct mlx5e_xdpsq *sq, u16 *pi)
-{
-       struct mlx5_wq_cyc *wq = &sq->wq;
-       struct mlx5e_tx_wqe *wqe;
-
-       *pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
-       wqe = mlx5_wq_cyc_get_wqe(wq, *pi);
-       memset(wqe, 0, sizeof(*wqe));
-
-       return wqe;
-}
-
 static inline void
 mlx5e_xdpi_fifo_push(struct mlx5e_xdp_info_fifo *fifo,
                     struct mlx5e_xdp_info *xi)
index fe2d596..3bcdb5b 100644 (file)
@@ -33,6 +33,9 @@ int mlx5e_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags)
                if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &c->xskicosq.state)))
                        return 0;
 
+               if (test_and_set_bit(MLX5E_SQ_STATE_PENDING_XSK_TX, &c->xskicosq.state))
+                       return 0;
+
                spin_lock(&c->xskicosq_lock);
                mlx5e_trigger_irq(&c->xskicosq);
                spin_unlock(&c->xskicosq_lock);
index 3022463..a6f65d4 100644 (file)
@@ -42,6 +42,8 @@
 #include "en/txrx.h"
 
 #if IS_ENABLED(CONFIG_GENEVE)
+#include <net/geneve.h>
+
 static inline bool mlx5_geneve_tx_allowed(struct mlx5_core_dev *mdev)
 {
        return mlx5_tx_swp_supported(mdev);
index 29626c6..92eb3ba 100644 (file)
@@ -75,18 +75,23 @@ struct xfrm_state *mlx5e_ipsec_sadb_rx_lookup(struct mlx5e_ipsec *ipsec,
        return ret;
 }
 
-static int mlx5e_ipsec_sadb_rx_add(struct mlx5e_ipsec_sa_entry *sa_entry)
+static int  mlx5e_ipsec_sadb_rx_add(struct mlx5e_ipsec_sa_entry *sa_entry,
+                                   unsigned int handle)
 {
        struct mlx5e_ipsec *ipsec = sa_entry->ipsec;
+       struct mlx5e_ipsec_sa_entry *_sa_entry;
        unsigned long flags;
-       int ret;
 
-       ret = ida_simple_get(&ipsec->halloc, 1, 0, GFP_KERNEL);
-       if (ret < 0)
-               return ret;
+       rcu_read_lock();
+       hash_for_each_possible_rcu(ipsec->sadb_rx, _sa_entry, hlist, handle)
+               if (_sa_entry->handle == handle) {
+                       rcu_read_unlock();
+                       return  -EEXIST;
+               }
+       rcu_read_unlock();
 
        spin_lock_irqsave(&ipsec->sadb_rx_lock, flags);
-       sa_entry->handle = ret;
+       sa_entry->handle = handle;
        hash_add_rcu(ipsec->sadb_rx, &sa_entry->hlist, sa_entry->handle);
        spin_unlock_irqrestore(&ipsec->sadb_rx_lock, flags);
 
@@ -103,15 +108,6 @@ static void mlx5e_ipsec_sadb_rx_del(struct mlx5e_ipsec_sa_entry *sa_entry)
        spin_unlock_irqrestore(&ipsec->sadb_rx_lock, flags);
 }
 
-static void mlx5e_ipsec_sadb_rx_free(struct mlx5e_ipsec_sa_entry *sa_entry)
-{
-       struct mlx5e_ipsec *ipsec = sa_entry->ipsec;
-
-       /* xfrm already doing sync rcu between del and free callbacks */
-
-       ida_simple_remove(&ipsec->halloc, sa_entry->handle);
-}
-
 static bool mlx5e_ipsec_update_esn_state(struct mlx5e_ipsec_sa_entry *sa_entry)
 {
        struct xfrm_replay_state_esn *replay_esn;
@@ -199,6 +195,14 @@ mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry,
        attrs->flags |= (x->props.mode == XFRM_MODE_TRANSPORT) ?
                        MLX5_ACCEL_ESP_FLAGS_TRANSPORT :
                        MLX5_ACCEL_ESP_FLAGS_TUNNEL;
+
+       /* spi */
+       attrs->spi = x->id.spi;
+
+       /* source , destination ips */
+       memcpy(&attrs->saddr, x->props.saddr.a6, sizeof(attrs->saddr));
+       memcpy(&attrs->daddr, x->id.daddr.a6, sizeof(attrs->daddr));
+       attrs->is_ipv6 = (x->props.family != AF_INET);
 }
 
 static inline int mlx5e_xfrm_validate_state(struct xfrm_state *x)
@@ -284,8 +288,7 @@ static int mlx5e_xfrm_add_state(struct xfrm_state *x)
        struct net_device *netdev = x->xso.dev;
        struct mlx5_accel_esp_xfrm_attrs attrs;
        struct mlx5e_priv *priv;
-       __be32 saddr[4] = {0}, daddr[4] = {0}, spi;
-       bool is_ipv6 = false;
+       unsigned int sa_handle;
        int err;
 
        priv = netdev_priv(netdev);
@@ -303,20 +306,6 @@ static int mlx5e_xfrm_add_state(struct xfrm_state *x)
        sa_entry->x = x;
        sa_entry->ipsec = priv->ipsec;
 
-       /* Add the SA to handle processed incoming packets before the add SA
-        * completion was received
-        */
-       if (x->xso.flags & XFRM_OFFLOAD_INBOUND) {
-               err = mlx5e_ipsec_sadb_rx_add(sa_entry);
-               if (err) {
-                       netdev_info(netdev, "Failed adding to SADB_RX: %d\n", err);
-                       goto err_entry;
-               }
-       } else {
-               sa_entry->set_iv_op = (x->props.flags & XFRM_STATE_ESN) ?
-                               mlx5e_ipsec_set_iv_esn : mlx5e_ipsec_set_iv;
-       }
-
        /* check esn */
        mlx5e_ipsec_update_esn_state(sa_entry);
 
@@ -327,41 +316,38 @@ static int mlx5e_xfrm_add_state(struct xfrm_state *x)
                                           MLX5_ACCEL_XFRM_FLAG_REQUIRE_METADATA);
        if (IS_ERR(sa_entry->xfrm)) {
                err = PTR_ERR(sa_entry->xfrm);
-               goto err_sadb_rx;
+               goto err_sa_entry;
        }
 
        /* create hw context */
-       if (x->props.family == AF_INET) {
-               saddr[3] = x->props.saddr.a4;
-               daddr[3] = x->id.daddr.a4;
-       } else {
-               memcpy(saddr, x->props.saddr.a6, sizeof(saddr));
-               memcpy(daddr, x->id.daddr.a6, sizeof(daddr));
-               is_ipv6 = true;
-       }
-       spi = x->id.spi;
        sa_entry->hw_context =
                        mlx5_accel_esp_create_hw_context(priv->mdev,
                                                         sa_entry->xfrm,
-                                                        saddr, daddr, spi,
-                                                        is_ipv6);
+                                                        &sa_handle);
        if (IS_ERR(sa_entry->hw_context)) {
                err = PTR_ERR(sa_entry->hw_context);
                goto err_xfrm;
        }
 
+       if (x->xso.flags & XFRM_OFFLOAD_INBOUND) {
+               err = mlx5e_ipsec_sadb_rx_add(sa_entry, sa_handle);
+               if (err)
+                       goto err_hw_ctx;
+       } else {
+               sa_entry->set_iv_op = (x->props.flags & XFRM_STATE_ESN) ?
+                               mlx5e_ipsec_set_iv_esn : mlx5e_ipsec_set_iv;
+       }
+
        x->xso.offload_handle = (unsigned long)sa_entry;
        goto out;
 
+err_hw_ctx:
+       mlx5_accel_esp_free_hw_context(sa_entry->hw_context);
 err_xfrm:
        mlx5_accel_esp_destroy_xfrm(sa_entry->xfrm);
-err_sadb_rx:
-       if (x->xso.flags & XFRM_OFFLOAD_INBOUND) {
-               mlx5e_ipsec_sadb_rx_del(sa_entry);
-               mlx5e_ipsec_sadb_rx_free(sa_entry);
-       }
-err_entry:
+err_sa_entry:
        kfree(sa_entry);
+
 out:
        return err;
 }
@@ -390,9 +376,6 @@ static void mlx5e_xfrm_free_state(struct xfrm_state *x)
                mlx5_accel_esp_destroy_xfrm(sa_entry->xfrm);
        }
 
-       if (x->xso.flags & XFRM_OFFLOAD_INBOUND)
-               mlx5e_ipsec_sadb_rx_free(sa_entry);
-
        kfree(sa_entry);
 }
 
index 93bf10e..c85151a 100644 (file)
@@ -109,11 +109,6 @@ int mlx5e_ipsec_init(struct mlx5e_priv *priv);
 void mlx5e_ipsec_cleanup(struct mlx5e_priv *priv);
 void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv);
 
-int mlx5e_ipsec_get_count(struct mlx5e_priv *priv);
-int mlx5e_ipsec_get_strings(struct mlx5e_priv *priv, uint8_t *data);
-void mlx5e_ipsec_update_stats(struct mlx5e_priv *priv);
-int mlx5e_ipsec_get_stats(struct mlx5e_priv *priv, u64 *data);
-
 struct xfrm_state *mlx5e_ipsec_sadb_rx_lookup(struct mlx5e_ipsec *dev,
                                              unsigned int handle);
 
@@ -136,26 +131,6 @@ static inline void mlx5e_ipsec_build_netdev(struct mlx5e_priv *priv)
 {
 }
 
-static inline int mlx5e_ipsec_get_count(struct mlx5e_priv *priv)
-{
-       return 0;
-}
-
-static inline int mlx5e_ipsec_get_strings(struct mlx5e_priv *priv,
-                                         uint8_t *data)
-{
-       return 0;
-}
-
-static inline void mlx5e_ipsec_update_stats(struct mlx5e_priv *priv)
-{
-}
-
-static inline int mlx5e_ipsec_get_stats(struct mlx5e_priv *priv, u64 *data)
-{
-       return 0;
-}
-
 #endif
 
 #endif /* __MLX5E_IPSEC_H__ */
index 6fea592..6c5c54b 100644 (file)
@@ -38,6 +38,7 @@
 #include "accel/ipsec.h"
 #include "fpga/sdk.h"
 #include "en_accel/ipsec.h"
+#include "fpga/ipsec.h"
 
 static const struct counter_desc mlx5e_ipsec_hw_stats_desc[] = {
        { MLX5E_DECLARE_STAT(struct mlx5e_ipsec_stats, ipsec_dec_in_packets) },
@@ -73,61 +74,74 @@ static const struct counter_desc mlx5e_ipsec_sw_stats_desc[] = {
 #define NUM_IPSEC_HW_COUNTERS ARRAY_SIZE(mlx5e_ipsec_hw_stats_desc)
 #define NUM_IPSEC_SW_COUNTERS ARRAY_SIZE(mlx5e_ipsec_sw_stats_desc)
 
-#define NUM_IPSEC_COUNTERS (NUM_IPSEC_HW_COUNTERS + NUM_IPSEC_SW_COUNTERS)
-
-int mlx5e_ipsec_get_count(struct mlx5e_priv *priv)
+static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(ipsec_sw)
 {
-       if (!priv->ipsec)
-               return 0;
-
-       return NUM_IPSEC_COUNTERS;
+       return NUM_IPSEC_SW_COUNTERS;
 }
 
-int mlx5e_ipsec_get_strings(struct mlx5e_priv *priv, uint8_t *data)
-{
-       unsigned int i, idx = 0;
+static inline MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ipsec_sw) {}
 
-       if (!priv->ipsec)
-               return 0;
+static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(ipsec_sw)
+{
+       unsigned int i;
 
-       for (i = 0; i < NUM_IPSEC_HW_COUNTERS; i++)
-               strcpy(data + (idx++) * ETH_GSTRING_LEN,
-                      mlx5e_ipsec_hw_stats_desc[i].format);
+       if (priv->ipsec)
+               for (i = 0; i < NUM_IPSEC_SW_COUNTERS; i++)
+                       strcpy(data + (idx++) * ETH_GSTRING_LEN,
+                              mlx5e_ipsec_sw_stats_desc[i].format);
+       return idx;
+}
 
-       for (i = 0; i < NUM_IPSEC_SW_COUNTERS; i++)
-               strcpy(data + (idx++) * ETH_GSTRING_LEN,
-                      mlx5e_ipsec_sw_stats_desc[i].format);
+static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(ipsec_sw)
+{
+       int i;
 
-       return NUM_IPSEC_COUNTERS;
+       if (priv->ipsec)
+               for (i = 0; i < NUM_IPSEC_SW_COUNTERS; i++)
+                       data[idx++] = MLX5E_READ_CTR_ATOMIC64(&priv->ipsec->sw_stats,
+                                                             mlx5e_ipsec_sw_stats_desc, i);
+       return idx;
 }
 
-void mlx5e_ipsec_update_stats(struct mlx5e_priv *priv)
+static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(ipsec_hw)
 {
-       int ret;
+       return (mlx5_fpga_ipsec_device_caps(priv->mdev)) ? NUM_IPSEC_HW_COUNTERS : 0;
+}
 
-       if (!priv->ipsec)
-               return;
+static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ipsec_hw)
+{
+       int ret = 0;
 
-       ret = mlx5_accel_ipsec_counters_read(priv->mdev, (u64 *)&priv->ipsec->stats,
-                                            NUM_IPSEC_HW_COUNTERS);
+       if (priv->ipsec)
+               ret = mlx5_accel_ipsec_counters_read(priv->mdev, (u64 *)&priv->ipsec->stats,
+                                                    NUM_IPSEC_HW_COUNTERS);
        if (ret)
                memset(&priv->ipsec->stats, 0, sizeof(priv->ipsec->stats));
 }
 
-int mlx5e_ipsec_get_stats(struct mlx5e_priv *priv, u64 *data)
+static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(ipsec_hw)
 {
-       int i, idx = 0;
-
-       if (!priv->ipsec)
-               return 0;
+       unsigned int i;
 
-       for (i = 0; i < NUM_IPSEC_HW_COUNTERS; i++)
-               data[idx++] = MLX5E_READ_CTR64_CPU(&priv->ipsec->stats,
-                                                  mlx5e_ipsec_hw_stats_desc, i);
+       if (priv->ipsec && mlx5_fpga_ipsec_device_caps(priv->mdev))
+               for (i = 0; i < NUM_IPSEC_HW_COUNTERS; i++)
+                       strcpy(data + (idx++) * ETH_GSTRING_LEN,
+                              mlx5e_ipsec_hw_stats_desc[i].format);
 
-       for (i = 0; i < NUM_IPSEC_SW_COUNTERS; i++)
-               data[idx++] = MLX5E_READ_CTR_ATOMIC64(&priv->ipsec->sw_stats,
-                                                     mlx5e_ipsec_sw_stats_desc, i);
+       return idx;
+}
 
-       return NUM_IPSEC_COUNTERS;
+static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(ipsec_hw)
+{
+       int i;
+
+       if (priv->ipsec && mlx5_fpga_ipsec_device_caps(priv->mdev))
+               for (i = 0; i < NUM_IPSEC_HW_COUNTERS; i++)
+                       data[idx++] = MLX5E_READ_CTR64_CPU(&priv->ipsec->stats,
+                                                          mlx5e_ipsec_hw_stats_desc,
+                                                          i);
+       return idx;
 }
+
+MLX5E_DEFINE_STATS_GRP(ipsec_sw, 0);
+MLX5E_DEFINE_STATS_GRP(ipsec_hw, 0);
index 63116be..9daaec2 100644 (file)
@@ -27,6 +27,14 @@ struct mlx5e_dump_wqe {
        struct mlx5_wqe_data_seg data;
 };
 
+#define MLX5E_TLS_FETCH_UMR_WQE(sq, pi) \
+       ((struct mlx5e_umr_wqe *)mlx5e_fetch_wqe(&(sq)->wq, pi, MLX5E_KTLS_STATIC_UMR_WQE_SZ))
+#define MLX5E_TLS_FETCH_PROGRESS_WQE(sq, pi) \
+       ((struct mlx5e_tx_wqe *)mlx5e_fetch_wqe(&(sq)->wq, pi, MLX5E_KTLS_PROGRESS_WQE_SZ))
+#define MLX5E_TLS_FETCH_DUMP_WQE(sq, pi) \
+       ((struct mlx5e_dump_wqe *)mlx5e_fetch_wqe(&(sq)->wq, pi, \
+                                                 sizeof(struct mlx5e_dump_wqe)))
+
 #define MLX5E_KTLS_DUMP_WQEBBS \
        (DIV_ROUND_UP(sizeof(struct mlx5e_dump_wqe), MLX5_SEND_WQE_BB))
 
index 52a5662..ba97393 100644 (file)
@@ -137,7 +137,8 @@ post_static_params(struct mlx5e_txqsq *sq,
        struct mlx5e_umr_wqe *umr_wqe;
        u16 pi;
 
-       umr_wqe = mlx5e_sq_fetch_wqe(sq, MLX5E_KTLS_STATIC_UMR_WQE_SZ, &pi);
+       pi = mlx5_wq_cyc_ctr2ix(&sq->wq, sq->pc);
+       umr_wqe = MLX5E_TLS_FETCH_UMR_WQE(sq, pi);
        build_static_params(umr_wqe, sq->pc, sq->sqn, priv_tx, fence);
        tx_fill_wi(sq, pi, MLX5E_KTLS_STATIC_WQEBBS, 0, NULL);
        sq->pc += MLX5E_KTLS_STATIC_WQEBBS;
@@ -151,7 +152,8 @@ post_progress_params(struct mlx5e_txqsq *sq,
        struct mlx5e_tx_wqe *wqe;
        u16 pi;
 
-       wqe = mlx5e_sq_fetch_wqe(sq, MLX5E_KTLS_PROGRESS_WQE_SZ, &pi);
+       pi = mlx5_wq_cyc_ctr2ix(&sq->wq, sq->pc);
+       wqe = MLX5E_TLS_FETCH_PROGRESS_WQE(sq, pi);
        build_progress_params(wqe, sq->pc, sq->sqn, priv_tx, fence);
        tx_fill_wi(sq, pi, MLX5E_KTLS_PROGRESS_WQEBBS, 0, NULL);
        sq->pc += MLX5E_KTLS_PROGRESS_WQEBBS;
@@ -163,14 +165,8 @@ mlx5e_ktls_tx_post_param_wqes(struct mlx5e_txqsq *sq,
                              bool skip_static_post, bool fence_first_post)
 {
        bool progress_fence = skip_static_post || !fence_first_post;
-       struct mlx5_wq_cyc *wq = &sq->wq;
-       u16 contig_wqebbs_room, pi;
 
-       pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
-       contig_wqebbs_room = mlx5_wq_cyc_get_contig_wqebbs(wq, pi);
-       if (unlikely(contig_wqebbs_room <
-                    MLX5E_KTLS_STATIC_WQEBBS + MLX5E_KTLS_PROGRESS_WQEBBS))
-               mlx5e_fill_sq_frag_edge(sq, wq, pi, contig_wqebbs_room);
+       mlx5e_txqsq_get_next_pi(sq, MLX5E_KTLS_STATIC_WQEBBS + MLX5E_KTLS_PROGRESS_WQEBBS);
 
        if (!skip_static_post)
                post_static_params(sq, priv_tx, fence_first_post);
@@ -278,7 +274,8 @@ tx_post_resync_dump(struct mlx5e_txqsq *sq, skb_frag_t *frag, u32 tisn, bool fir
        int fsz;
        u16 pi;
 
-       wqe = mlx5e_sq_fetch_wqe(sq, sizeof(*wqe), &pi);
+       pi = mlx5_wq_cyc_ctr2ix(&sq->wq, sq->pc);
+       wqe = MLX5E_TLS_FETCH_DUMP_WQE(sq, pi);
 
        ds_cnt = sizeof(*wqe) / MLX5_SEND_WQE_DS;
 
@@ -343,10 +340,8 @@ mlx5e_ktls_tx_handle_ooo(struct mlx5e_ktls_offload_context_tx *priv_tx,
                         u32 seq)
 {
        struct mlx5e_sq_stats *stats = sq->stats;
-       struct mlx5_wq_cyc *wq = &sq->wq;
        enum mlx5e_ktls_sync_retval ret;
        struct tx_sync_info info = {};
-       u16 contig_wqebbs_room, pi;
        u8 num_wqebbs;
        int i = 0;
 
@@ -377,11 +372,7 @@ mlx5e_ktls_tx_handle_ooo(struct mlx5e_ktls_offload_context_tx *priv_tx,
        }
 
        num_wqebbs = mlx5e_ktls_dumps_num_wqebbs(sq, info.nr_frags, info.sync_len);
-       pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
-       contig_wqebbs_room = mlx5_wq_cyc_get_contig_wqebbs(wq, pi);
-
-       if (unlikely(contig_wqebbs_room < num_wqebbs))
-               mlx5e_fill_sq_frag_edge(sq, wq, pi, contig_wqebbs_room);
+       mlx5e_txqsq_get_next_pi(sq, num_wqebbs);
 
        for (; i < info.nr_frags; i++) {
                unsigned int orig_fsz, frag_offset = 0, n = 0;
@@ -449,7 +440,8 @@ struct sk_buff *mlx5e_ktls_handle_tx_skb(struct net_device *netdev,
 
        if (unlikely(mlx5e_ktls_tx_offload_test_and_clear_pending(priv_tx))) {
                mlx5e_ktls_tx_post_param_wqes(sq, priv_tx, false, false);
-               *wqe = mlx5e_sq_fetch_wqe(sq, sizeof(**wqe), pi);
+               *pi = mlx5_wq_cyc_ctr2ix(&sq->wq, sq->pc);
+               *wqe = MLX5E_TX_FETCH_WQE(sq, *pi);
                stats->tls_ctx++;
        }
 
@@ -460,7 +452,8 @@ struct sk_buff *mlx5e_ktls_handle_tx_skb(struct net_device *netdev,
 
                switch (ret) {
                case MLX5E_KTLS_SYNC_DONE:
-                       *wqe = mlx5e_sq_fetch_wqe(sq, sizeof(**wqe), pi);
+                       *pi = mlx5_wq_cyc_ctr2ix(&sq->wq, sq->pc);
+                       *wqe = MLX5E_TX_FETCH_WQE(sq, *pi);
                        break;
                case MLX5E_KTLS_SYNC_SKIP_NO_DATA:
                        if (likely(!skb->decrypted))
index ef1ed15..1d7ddeb 100644 (file)
@@ -248,7 +248,8 @@ mlx5e_tls_handle_ooo(struct mlx5e_tls_offload_context_tx *context,
        mlx5e_tls_complete_sync_skb(skb, nskb, tcp_seq, headln,
                                    cpu_to_be64(info.rcd_sn));
        mlx5e_sq_xmit(sq, nskb, *wqe, *pi, true);
-       *wqe = mlx5e_sq_fetch_wqe(sq, sizeof(**wqe), pi);
+       *pi = mlx5_wq_cyc_ctr2ix(&sq->wq, sq->pc);
+       *wqe = MLX5E_TX_FETCH_WQE(sq, *pi);
        return skb;
 
 err_out:
index f7890e0..af3228b 100644 (file)
  * Global resources are common to all the netdevices crated on the same nic.
  */
 
-int mlx5e_create_tir(struct mlx5_core_dev *mdev,
-                    struct mlx5e_tir *tir, u32 *in, int inlen)
+int mlx5e_create_tir(struct mlx5_core_dev *mdev, struct mlx5e_tir *tir, u32 *in)
 {
        int err;
 
-       err = mlx5_core_create_tir(mdev, in, inlen, &tir->tirn);
+       err = mlx5_core_create_tir(mdev, in, &tir->tirn);
        if (err)
                return err;
 
@@ -167,7 +166,7 @@ int mlx5e_refresh_tirs(struct mlx5e_priv *priv, bool enable_uc_lb)
        mutex_lock(&mdev->mlx5e_res.td.list_lock);
        list_for_each_entry(tir, &mdev->mlx5e_res.td.tirs_list, list) {
                tirn = tir->tirn;
-               err = mlx5_core_modify_tir(mdev, tirn, in, inlen);
+               err = mlx5_core_modify_tir(mdev, tirn, in);
                if (err)
                        goto out;
        }
index 6d703dd..0279bb7 100644 (file)
@@ -432,7 +432,7 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv,
 
        if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
                *cur_params = new_channels.params;
-               mlx5e_num_channels_changed(priv);
+               err = mlx5e_num_channels_changed(priv);
                goto out;
        }
 
@@ -1204,7 +1204,7 @@ int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir,
        }
 
        if (hash_changed)
-               mlx5e_modify_tirs_hash(priv, in, inlen);
+               mlx5e_modify_tirs_hash(priv, in);
 
        mutex_unlock(&priv->state_lock);
 
index 3bc2ac3..83c9b2b 100644 (file)
@@ -858,7 +858,7 @@ static int mlx5e_set_rss_hash_opt(struct mlx5e_priv *priv,
                goto out;
 
        priv->rss_params.rx_hash_fields[tt] = rx_hash_field;
-       mlx5e_modify_tirs_hash(priv, in, inlen);
+       mlx5e_modify_tirs_hash(priv, in);
 
 out:
        mutex_unlock(&priv->state_lock);
index dd7f338..048a4f8 100644 (file)
@@ -233,7 +233,7 @@ static inline void mlx5e_build_umr_wqe(struct mlx5e_rq *rq,
        cseg->qpn_ds    = cpu_to_be32((sq->sqn << MLX5_WQE_CTRL_QPN_SHIFT) |
                                      ds_cnt);
        cseg->fm_ce_se  = MLX5_WQE_CTRL_CQ_UPDATE;
-       cseg->imm       = rq->mkey_be;
+       cseg->umr_mkey  = rq->mkey_be;
 
        ucseg->flags = MLX5_UMR_TRANSLATION_OFFSET_EN | MLX5_UMR_INLINE;
        ucseg->xlt_octowords =
@@ -721,7 +721,7 @@ int mlx5e_modify_rq_state(struct mlx5e_rq *rq, int curr_state, int next_state)
        MLX5_SET(modify_rq_in, in, rq_state, curr_state);
        MLX5_SET(rqc, rqc, state, next_state);
 
-       err = mlx5_core_modify_rq(mdev, rq->rqn, in, inlen);
+       err = mlx5_core_modify_rq(mdev, rq->rqn, in);
 
        kvfree(in);
 
@@ -752,7 +752,7 @@ static int mlx5e_modify_rq_scatter_fcs(struct mlx5e_rq *rq, bool enable)
        MLX5_SET(rqc, rqc, scatter_fcs, enable);
        MLX5_SET(rqc, rqc, state, MLX5_RQC_STATE_RDY);
 
-       err = mlx5_core_modify_rq(mdev, rq->rqn, in, inlen);
+       err = mlx5_core_modify_rq(mdev, rq->rqn, in);
 
        kvfree(in);
 
@@ -781,7 +781,7 @@ static int mlx5e_modify_rq_vsd(struct mlx5e_rq *rq, bool vsd)
        MLX5_SET(rqc, rqc, vsd, vsd);
        MLX5_SET(rqc, rqc, state, MLX5_RQC_STATE_RDY);
 
-       err = mlx5_core_modify_rq(mdev, rq->rqn, in, inlen);
+       err = mlx5_core_modify_rq(mdev, rq->rqn, in);
 
        kvfree(in);
 
@@ -1027,17 +1027,17 @@ static void mlx5e_free_xdpsq(struct mlx5e_xdpsq *sq)
 
 static void mlx5e_free_icosq_db(struct mlx5e_icosq *sq)
 {
-       kvfree(sq->db.ico_wqe);
+       kvfree(sq->db.wqe_info);
 }
 
 static int mlx5e_alloc_icosq_db(struct mlx5e_icosq *sq, int numa)
 {
        int wq_sz = mlx5_wq_cyc_get_size(&sq->wq);
+       size_t size;
 
-       sq->db.ico_wqe = kvzalloc_node(array_size(wq_sz,
-                                                 sizeof(*sq->db.ico_wqe)),
-                                      GFP_KERNEL, numa);
-       if (!sq->db.ico_wqe)
+       size = array_size(wq_sz, sizeof(*sq->db.wqe_info));
+       sq->db.wqe_info = kvzalloc_node(size, GFP_KERNEL, numa);
+       if (!sq->db.wqe_info)
                return -ENOMEM;
 
        return 0;
@@ -1259,7 +1259,7 @@ int mlx5e_modify_sq(struct mlx5_core_dev *mdev, u32 sqn,
                MLX5_SET(sqc,  sqc, packet_pacing_rate_limit_index, p->rl_index);
        }
 
-       err = mlx5_core_modify_sq(mdev, sqn, in, inlen);
+       err = mlx5_core_modify_sq(mdev, sqn, in);
 
        kvfree(in);
 
@@ -2698,7 +2698,7 @@ static void mlx5e_update_rx_hash_fields(struct mlx5e_tirc_config *ttconfig,
        ttconfig->rx_hash_fields = rx_hash_fields;
 }
 
-void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen)
+void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in)
 {
        void *tirc = MLX5_ADDR_OF(modify_tir_in, in, ctx);
        struct mlx5e_rss_params *rss = &priv->rss_params;
@@ -2714,7 +2714,7 @@ void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen)
                mlx5e_update_rx_hash_fields(&ttconfig, tt,
                                            rss->rx_hash_fields[tt]);
                mlx5e_build_indir_tir_ctx_hash(rss, &ttconfig, tirc, false);
-               mlx5_core_modify_tir(mdev, priv->indir_tir[tt].tirn, in, inlen);
+               mlx5_core_modify_tir(mdev, priv->indir_tir[tt].tirn, in);
        }
 
        if (!mlx5e_tunnel_inner_ft_supported(priv->mdev))
@@ -2725,8 +2725,7 @@ void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen)
                mlx5e_update_rx_hash_fields(&ttconfig, tt,
                                            rss->rx_hash_fields[tt]);
                mlx5e_build_indir_tir_ctx_hash(rss, &ttconfig, tirc, true);
-               mlx5_core_modify_tir(mdev, priv->inner_indir_tir[tt].tirn, in,
-                                    inlen);
+               mlx5_core_modify_tir(mdev, priv->inner_indir_tir[tt].tirn, in);
        }
 }
 
@@ -2752,15 +2751,13 @@ static int mlx5e_modify_tirs_lro(struct mlx5e_priv *priv)
        mlx5e_build_tir_ctx_lro(&priv->channels.params, tirc);
 
        for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
-               err = mlx5_core_modify_tir(mdev, priv->indir_tir[tt].tirn, in,
-                                          inlen);
+               err = mlx5_core_modify_tir(mdev, priv->indir_tir[tt].tirn, in);
                if (err)
                        goto free_in;
        }
 
        for (ix = 0; ix < priv->max_nch; ix++) {
-               err = mlx5_core_modify_tir(mdev, priv->direct_tir[ix].tirn,
-                                          in, inlen);
+               err = mlx5_core_modify_tir(mdev, priv->direct_tir[ix].tirn, in);
                if (err)
                        goto free_in;
        }
@@ -2839,11 +2836,8 @@ void mlx5e_set_netdev_mtu_boundaries(struct mlx5e_priv *priv)
                                ETH_MAX_MTU);
 }
 
-static void mlx5e_netdev_set_tcs(struct net_device *netdev)
+static void mlx5e_netdev_set_tcs(struct net_device *netdev, u16 nch, u8 ntc)
 {
-       struct mlx5e_priv *priv = netdev_priv(netdev);
-       int nch = priv->channels.params.num_channels;
-       int ntc = priv->channels.params.num_tc;
        int tc;
 
        netdev_reset_tc(netdev);
@@ -2860,15 +2854,47 @@ static void mlx5e_netdev_set_tcs(struct net_device *netdev)
                netdev_set_tc_queue(netdev, tc, nch, 0);
 }
 
-static void mlx5e_update_netdev_queues(struct mlx5e_priv *priv, u16 count)
+static int mlx5e_update_netdev_queues(struct mlx5e_priv *priv)
 {
-       int num_txqs = count * priv->channels.params.num_tc;
-       int num_rxqs = count * priv->profile->rq_groups;
        struct net_device *netdev = priv->netdev;
+       int num_txqs, num_rxqs, nch, ntc;
+       int old_num_txqs, old_ntc;
+       int err;
+
+       old_num_txqs = netdev->real_num_tx_queues;
+       old_ntc = netdev->num_tc;
+
+       nch = priv->channels.params.num_channels;
+       ntc = priv->channels.params.num_tc;
+       num_txqs = nch * ntc;
+       num_rxqs = nch * priv->profile->rq_groups;
+
+       mlx5e_netdev_set_tcs(netdev, nch, ntc);
+
+       err = netif_set_real_num_tx_queues(netdev, num_txqs);
+       if (err) {
+               netdev_warn(netdev, "netif_set_real_num_tx_queues failed, %d\n", err);
+               goto err_tcs;
+       }
+       err = netif_set_real_num_rx_queues(netdev, num_rxqs);
+       if (err) {
+               netdev_warn(netdev, "netif_set_real_num_rx_queues failed, %d\n", err);
+               goto err_txqs;
+       }
+
+       return 0;
 
-       mlx5e_netdev_set_tcs(netdev);
-       netif_set_real_num_tx_queues(netdev, num_txqs);
-       netif_set_real_num_rx_queues(netdev, num_rxqs);
+err_txqs:
+       /* netif_set_real_num_rx_queues could fail only when nch increased. Only
+        * one of nch and ntc is changed in this function. That means, the call
+        * to netif_set_real_num_tx_queues below should not fail, because it
+        * decreases the number of TX queues.
+        */
+       WARN_ON_ONCE(netif_set_real_num_tx_queues(netdev, old_num_txqs));
+
+err_tcs:
+       mlx5e_netdev_set_tcs(netdev, old_num_txqs / old_ntc, old_ntc);
+       return err;
 }
 
 static void mlx5e_set_default_xps_cpumasks(struct mlx5e_priv *priv,
@@ -2895,8 +2921,12 @@ static void mlx5e_set_default_xps_cpumasks(struct mlx5e_priv *priv,
 int mlx5e_num_channels_changed(struct mlx5e_priv *priv)
 {
        u16 count = priv->channels.params.num_channels;
+       int err;
+
+       err = mlx5e_update_netdev_queues(priv);
+       if (err)
+               return err;
 
-       mlx5e_update_netdev_queues(priv, count);
        mlx5e_set_default_xps_cpumasks(priv, &priv->channels.params);
 
        if (!netif_is_rxfh_configured(priv->netdev))
@@ -3214,7 +3244,7 @@ int mlx5e_create_tis(struct mlx5_core_dev *mdev, void *in, u32 *tisn)
        if (mlx5_lag_is_lacp_owner(mdev))
                MLX5_SET(tisc, tisc, strict_lag_tx_port_affinity, 1);
 
-       return mlx5_core_create_tis(mdev, in, MLX5_ST_SZ_BYTES(create_tis_in), tisn);
+       return mlx5_core_create_tis(mdev, in, tisn);
 }
 
 void mlx5e_destroy_tis(struct mlx5_core_dev *mdev, u32 tisn)
@@ -3332,7 +3362,7 @@ int mlx5e_create_indirect_tirs(struct mlx5e_priv *priv, bool inner_ttc)
                tir = &priv->indir_tir[tt];
                tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
                mlx5e_build_indir_tir_ctx(priv, tt, tirc);
-               err = mlx5e_create_tir(priv->mdev, tir, in, inlen);
+               err = mlx5e_create_tir(priv->mdev, tir, in);
                if (err) {
                        mlx5_core_warn(priv->mdev, "create indirect tirs failed, %d\n", err);
                        goto err_destroy_inner_tirs;
@@ -3347,7 +3377,7 @@ int mlx5e_create_indirect_tirs(struct mlx5e_priv *priv, bool inner_ttc)
                tir = &priv->inner_indir_tir[i];
                tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
                mlx5e_build_inner_indir_tir_ctx(priv, i, tirc);
-               err = mlx5e_create_tir(priv->mdev, tir, in, inlen);
+               err = mlx5e_create_tir(priv->mdev, tir, in);
                if (err) {
                        mlx5_core_warn(priv->mdev, "create inner indirect tirs failed, %d\n", err);
                        goto err_destroy_inner_tirs;
@@ -3390,7 +3420,7 @@ int mlx5e_create_direct_tirs(struct mlx5e_priv *priv, struct mlx5e_tir *tirs)
                tir = &tirs[ix];
                tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
                mlx5e_build_direct_tir_ctx(priv, tir->rqt.rqtn, tirc);
-               err = mlx5e_create_tir(priv->mdev, tir, in, inlen);
+               err = mlx5e_create_tir(priv->mdev, tir, in);
                if (unlikely(err))
                        goto err_destroy_ch_tirs;
        }
@@ -3583,7 +3613,12 @@ mlx5e_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
        struct mlx5e_vport_stats *vstats = &priv->stats.vport;
        struct mlx5e_pport_stats *pstats = &priv->stats.pport;
 
-       if (!mlx5e_monitor_counter_supported(priv)) {
+       /* In switchdev mode, monitor counters doesn't monitor
+        * rx/tx stats of 802_3. The update stats mechanism
+        * should keep the 802_3 layout counters updated
+        */
+       if (!mlx5e_monitor_counter_supported(priv) ||
+           mlx5e_is_uplink_rep(priv)) {
                /* update HW stats in background for next time */
                mlx5e_queue_update_stats(priv);
        }
@@ -4997,29 +5032,40 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev)
 
 void mlx5e_create_q_counters(struct mlx5e_priv *priv)
 {
+       u32 out[MLX5_ST_SZ_DW(alloc_q_counter_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(alloc_q_counter_in)] = {};
        struct mlx5_core_dev *mdev = priv->mdev;
        int err;
 
-       err = mlx5_core_alloc_q_counter(mdev, &priv->q_counter);
-       if (err) {
-               mlx5_core_warn(mdev, "alloc queue counter failed, %d\n", err);
-               priv->q_counter = 0;
-       }
+       MLX5_SET(alloc_q_counter_in, in, opcode, MLX5_CMD_OP_ALLOC_Q_COUNTER);
+       err = mlx5_cmd_exec_inout(mdev, alloc_q_counter, in, out);
+       if (!err)
+               priv->q_counter =
+                       MLX5_GET(alloc_q_counter_out, out, counter_set_id);
 
-       err = mlx5_core_alloc_q_counter(mdev, &priv->drop_rq_q_counter);
-       if (err) {
-               mlx5_core_warn(mdev, "alloc drop RQ counter failed, %d\n", err);
-               priv->drop_rq_q_counter = 0;
-       }
+       err = mlx5_cmd_exec_inout(mdev, alloc_q_counter, in, out);
+       if (!err)
+               priv->drop_rq_q_counter =
+                       MLX5_GET(alloc_q_counter_out, out, counter_set_id);
 }
 
 void mlx5e_destroy_q_counters(struct mlx5e_priv *priv)
 {
-       if (priv->q_counter)
-               mlx5_core_dealloc_q_counter(priv->mdev, priv->q_counter);
+       u32 in[MLX5_ST_SZ_DW(dealloc_q_counter_in)] = {};
+
+       MLX5_SET(dealloc_q_counter_in, in, opcode,
+                MLX5_CMD_OP_DEALLOC_Q_COUNTER);
+       if (priv->q_counter) {
+               MLX5_SET(dealloc_q_counter_in, in, counter_set_id,
+                        priv->q_counter);
+               mlx5_cmd_exec_in(priv->mdev, dealloc_q_counter, in);
+       }
 
-       if (priv->drop_rq_q_counter)
-               mlx5_core_dealloc_q_counter(priv->mdev, priv->drop_rq_q_counter);
+       if (priv->drop_rq_q_counter) {
+               MLX5_SET(dealloc_q_counter_in, in, counter_set_id,
+                        priv->drop_rq_q_counter);
+               mlx5_cmd_exec_in(priv->mdev, dealloc_q_counter, in);
+       }
 }
 
 static int mlx5e_nic_init(struct mlx5_core_dev *mdev,
@@ -5358,9 +5404,11 @@ int mlx5e_attach_netdev(struct mlx5e_priv *priv)
         */
        if (take_rtnl)
                rtnl_lock();
-       mlx5e_num_channels_changed(priv);
+       err = mlx5e_num_channels_changed(priv);
        if (take_rtnl)
                rtnl_unlock();
+       if (err)
+               goto out;
 
        err = profile->init_tx(priv);
        if (err)
@@ -5526,8 +5574,8 @@ static void mlx5e_remove(struct mlx5_core_dev *mdev, void *vpriv)
 #ifdef CONFIG_MLX5_CORE_EN_DCB
        mlx5e_dcbnl_delete_app(priv);
 #endif
-       mlx5e_devlink_port_unregister(priv);
        unregister_netdev(priv->netdev);
+       mlx5e_devlink_port_unregister(priv);
        mlx5e_detach(mdev, vpriv);
        mlx5e_destroy_netdev(priv);
 }
index 2a0243e..1eac7a5 100644 (file)
@@ -1773,19 +1773,14 @@ static void mlx5e_cleanup_rep_rx(struct mlx5e_priv *priv)
 
 static int mlx5e_init_ul_rep_rx(struct mlx5e_priv *priv)
 {
-       int err = mlx5e_init_rep_rx(priv);
-
-       if (err)
-               return err;
-
        mlx5e_create_q_counters(priv);
-       return 0;
+       return mlx5e_init_rep_rx(priv);
 }
 
 static void mlx5e_cleanup_ul_rep_rx(struct mlx5e_priv *priv)
 {
-       mlx5e_destroy_q_counters(priv);
        mlx5e_cleanup_rep_rx(priv);
+       mlx5e_destroy_q_counters(priv);
 }
 
 static int mlx5e_init_uplink_rep_tx(struct mlx5e_rep_priv *rpriv)
@@ -2050,31 +2045,28 @@ static int register_devlink_port(struct mlx5_core_dev *dev,
        struct mlx5_eswitch_rep *rep = rpriv->rep;
        struct netdev_phys_item_id ppid = {};
        unsigned int dl_port_index = 0;
+       u16 pfnum;
 
        if (!is_devlink_port_supported(dev, rpriv))
                return 0;
 
        mlx5e_rep_get_port_parent_id(rpriv->netdev, &ppid);
+       dl_port_index = vport_to_devlink_port_index(dev, rep->vport);
+       pfnum = PCI_FUNC(dev->pdev->devfn);
 
-       if (rep->vport == MLX5_VPORT_UPLINK) {
+       if (rep->vport == MLX5_VPORT_UPLINK)
                devlink_port_attrs_set(&rpriv->dl_port,
                                       DEVLINK_PORT_FLAVOUR_PHYSICAL,
-                                      PCI_FUNC(dev->pdev->devfn), false, 0,
+                                      pfnum, false, 0,
                                       &ppid.id[0], ppid.id_len);
-               dl_port_index = vport_to_devlink_port_index(dev, rep->vport);
-       } else if (rep->vport == MLX5_VPORT_PF) {
+       else if (rep->vport == MLX5_VPORT_PF)
                devlink_port_attrs_pci_pf_set(&rpriv->dl_port,
                                              &ppid.id[0], ppid.id_len,
-                                             dev->pdev->devfn);
-               dl_port_index = rep->vport;
-       } else if (mlx5_eswitch_is_vf_vport(dev->priv.eswitch,
-                                           rpriv->rep->vport)) {
+                                             pfnum);
+       else if (mlx5_eswitch_is_vf_vport(dev->priv.eswitch, rpriv->rep->vport))
                devlink_port_attrs_pci_vf_set(&rpriv->dl_port,
                                              &ppid.id[0], ppid.id_len,
-                                             dev->pdev->devfn,
-                                             rep->vport - 1);
-               dl_port_index = vport_to_devlink_port_index(dev, rep->vport);
-       }
+                                             pfnum, rep->vport - 1);
 
        return devlink_port_register(devlink, &rpriv->dl_port, dl_port_index);
 }
index 6173faf..d9a5a66 100644 (file)
@@ -468,22 +468,6 @@ static void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq, u8 n)
        mlx5_wq_ll_update_db_record(wq);
 }
 
-static inline void mlx5e_fill_icosq_frag_edge(struct mlx5e_icosq *sq,
-                                             struct mlx5_wq_cyc *wq,
-                                             u16 pi, u16 nnops)
-{
-       struct mlx5e_sq_wqe_info *edge_wi, *wi = &sq->db.ico_wqe[pi];
-
-       edge_wi = wi + nnops;
-
-       /* fill sq frag edge with nops to avoid wqe wrapping two pages */
-       for (; wi < edge_wi; wi++) {
-               wi->opcode = MLX5_OPCODE_NOP;
-               wi->num_wqebbs = 1;
-               mlx5e_post_nop(wq, sq->sqn, &sq->pc);
-       }
-}
-
 static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
 {
        struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix];
@@ -492,7 +476,7 @@ static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
        struct mlx5_wq_cyc *wq = &sq->wq;
        struct mlx5e_umr_wqe *umr_wqe;
        u16 xlt_offset = ix << (MLX5E_LOG_ALIGNED_MPWQE_PPW - 1);
-       u16 pi, contig_wqebbs_room;
+       u16 pi;
        int err;
        int i;
 
@@ -502,13 +486,7 @@ static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
                goto err;
        }
 
-       pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
-       contig_wqebbs_room = mlx5_wq_cyc_get_contig_wqebbs(wq, pi);
-       if (unlikely(contig_wqebbs_room < MLX5E_UMR_WQEBBS)) {
-               mlx5e_fill_icosq_frag_edge(sq, wq, pi, contig_wqebbs_room);
-               pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
-       }
-
+       pi = mlx5e_icosq_get_next_pi(sq, MLX5E_UMR_WQEBBS);
        umr_wqe = mlx5_wq_cyc_get_wqe(wq, pi);
        memcpy(umr_wqe, &rq->mpwqe.umr_wqe, offsetof(struct mlx5e_umr_wqe, inline_mtts));
 
@@ -527,9 +505,9 @@ static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
                            MLX5_OPCODE_UMR);
        umr_wqe->uctrl.xlt_offset = cpu_to_be16(xlt_offset);
 
-       sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_UMR;
-       sq->db.ico_wqe[pi].num_wqebbs = MLX5E_UMR_WQEBBS;
-       sq->db.ico_wqe[pi].umr.rq = rq;
+       sq->db.wqe_info[pi].opcode = MLX5_OPCODE_UMR;
+       sq->db.wqe_info[pi].num_wqebbs = MLX5E_UMR_WQEBBS;
+       sq->db.wqe_info[pi].umr.rq = rq;
        sq->pc += MLX5E_UMR_WQEBBS;
 
        sq->doorbell_cseg = &umr_wqe->ctrl;
@@ -589,7 +567,7 @@ bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq)
        return !!err;
 }
 
-void mlx5e_poll_ico_cq(struct mlx5e_cq *cq)
+int mlx5e_poll_ico_cq(struct mlx5e_cq *cq)
 {
        struct mlx5e_icosq *sq = container_of(cq, struct mlx5e_icosq, cq);
        struct mlx5_cqe64 *cqe;
@@ -597,11 +575,11 @@ void mlx5e_poll_ico_cq(struct mlx5e_cq *cq)
        int i;
 
        if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state)))
-               return;
+               return 0;
 
        cqe = mlx5_cqwq_get_cqe(&cq->wq);
        if (likely(!cqe))
-               return;
+               return 0;
 
        /* sq->cc must be updated only after mlx5_cqwq_update_db_record(),
         * otherwise a cq overrun may occur
@@ -618,19 +596,21 @@ void mlx5e_poll_ico_cq(struct mlx5e_cq *cq)
                wqe_counter = be16_to_cpu(cqe->wqe_counter);
 
                do {
-                       struct mlx5e_sq_wqe_info *wi;
+                       struct mlx5e_icosq_wqe_info *wi;
                        u16 ci;
 
                        last_wqe = (sqcc == wqe_counter);
 
                        ci = mlx5_wq_cyc_ctr2ix(&sq->wq, sqcc);
-                       wi = &sq->db.ico_wqe[ci];
+                       wi = &sq->db.wqe_info[ci];
                        sqcc += wi->num_wqebbs;
 
                        if (last_wqe && unlikely(get_cqe_opcode(cqe) != MLX5_CQE_REQ)) {
                                netdev_WARN_ONCE(cq->channel->netdev,
                                                 "Bad OP in ICOSQ CQE: 0x%x\n",
                                                 get_cqe_opcode(cqe));
+                               mlx5e_dump_error_cqe(&sq->cq, sq->sqn,
+                                                    (struct mlx5_err_cqe *)cqe);
                                if (!test_and_set_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state))
                                        queue_work(cq->channel->priv->wq, &sq->recover_work);
                                break;
@@ -650,6 +630,8 @@ void mlx5e_poll_ico_cq(struct mlx5e_cq *cq)
        sq->cc = sqcc;
 
        mlx5_cqwq_update_db_record(&cq->wq);
+
+       return i;
 }
 
 bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq)
index 30b216d..f009fe0 100644 (file)
@@ -32,8 +32,8 @@
 
 #include "lib/mlx5.h"
 #include "en.h"
-#include "en_accel/ipsec.h"
 #include "en_accel/tls.h"
+#include "en_accel/en_accel.h"
 
 static unsigned int stats_grps_num(struct mlx5e_priv *priv)
 {
@@ -411,18 +411,29 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(qcnt)
 static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(qcnt)
 {
        struct mlx5e_qcounter_stats *qcnt = &priv->stats.qcnt;
-       u32 out[MLX5_ST_SZ_DW(query_q_counter_out)];
+       u32 out[MLX5_ST_SZ_DW(query_q_counter_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(query_q_counter_in)] = {};
+       int ret;
+
+       MLX5_SET(query_q_counter_in, in, opcode, MLX5_CMD_OP_QUERY_Q_COUNTER);
+
+       if (priv->q_counter) {
+               MLX5_SET(query_q_counter_in, in, counter_set_id,
+                        priv->q_counter);
+               ret = mlx5_cmd_exec_inout(priv->mdev, query_q_counter, in, out);
+               if (!ret)
+                       qcnt->rx_out_of_buffer = MLX5_GET(query_q_counter_out,
+                                                         out, out_of_buffer);
+       }
 
-       if (priv->q_counter &&
-           !mlx5_core_query_q_counter(priv->mdev, priv->q_counter, 0, out,
-                                      sizeof(out)))
-               qcnt->rx_out_of_buffer = MLX5_GET(query_q_counter_out,
-                                                 out, out_of_buffer);
-       if (priv->drop_rq_q_counter &&
-           !mlx5_core_query_q_counter(priv->mdev, priv->drop_rq_q_counter, 0,
-                                      out, sizeof(out)))
-               qcnt->rx_if_down_packets = MLX5_GET(query_q_counter_out, out,
-                                                   out_of_buffer);
+       if (priv->drop_rq_q_counter) {
+               MLX5_SET(query_q_counter_in, in, counter_set_id,
+                        priv->drop_rq_q_counter);
+               ret = mlx5_cmd_exec_inout(priv->mdev, query_q_counter, in, out);
+               if (!ret)
+                       qcnt->rx_if_down_packets = MLX5_GET(query_q_counter_out,
+                                                           out, out_of_buffer);
+       }
 }
 
 #define VNIC_ENV_OFF(c) MLX5_BYTE_OFF(query_vnic_env_out, c)
@@ -480,18 +491,14 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(vnic_env)
 static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(vnic_env)
 {
        u32 *out = (u32 *)priv->stats.vnic.query_vnic_env_out;
-       int outlen = MLX5_ST_SZ_BYTES(query_vnic_env_out);
-       u32 in[MLX5_ST_SZ_DW(query_vnic_env_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(query_vnic_env_in)] = {};
        struct mlx5_core_dev *mdev = priv->mdev;
 
        if (!MLX5_CAP_GEN(priv->mdev, nic_receive_steering_discard))
                return;
 
-       MLX5_SET(query_vnic_env_in, in, opcode,
-                MLX5_CMD_OP_QUERY_VNIC_ENV);
-       MLX5_SET(query_vnic_env_in, in, op_mod, 0);
-       MLX5_SET(query_vnic_env_in, in, other_vport, 0);
-       mlx5_cmd_exec(mdev, in, sizeof(in), out, outlen);
+       MLX5_SET(query_vnic_env_in, in, opcode, MLX5_CMD_OP_QUERY_VNIC_ENV);
+       mlx5_cmd_exec_inout(mdev, query_vnic_env, in, out);
 }
 
 #define VPORT_COUNTER_OFF(c) MLX5_BYTE_OFF(query_vport_counter_out, c)
@@ -566,15 +573,12 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(vport)
 
 static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(vport)
 {
-       int outlen = MLX5_ST_SZ_BYTES(query_vport_counter_out);
        u32 *out = (u32 *)priv->stats.vport.query_vport_out;
-       u32 in[MLX5_ST_SZ_DW(query_vport_counter_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(query_vport_counter_in)] = {};
        struct mlx5_core_dev *mdev = priv->mdev;
 
        MLX5_SET(query_vport_counter_in, in, opcode, MLX5_CMD_OP_QUERY_VPORT_COUNTER);
-       MLX5_SET(query_vport_counter_in, in, op_mod, 0);
-       MLX5_SET(query_vport_counter_in, in, other_vport, 0);
-       mlx5_cmd_exec(mdev, in, sizeof(in), out, outlen);
+       mlx5_cmd_exec_inout(mdev, query_vport_counter, in, out);
 }
 
 #define PPORT_802_3_OFF(c) \
@@ -1424,27 +1428,6 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(pme)
 
 static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(pme) { return; }
 
-static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(ipsec)
-{
-       return mlx5e_ipsec_get_count(priv);
-}
-
-static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(ipsec)
-{
-       return idx + mlx5e_ipsec_get_strings(priv,
-                                            data + idx * ETH_GSTRING_LEN);
-}
-
-static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(ipsec)
-{
-       return idx + mlx5e_ipsec_get_stats(priv, data + idx);
-}
-
-static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ipsec)
-{
-       mlx5e_ipsec_update_stats(priv);
-}
-
 static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(tls)
 {
        return mlx5e_tls_get_count(priv);
@@ -1714,7 +1697,6 @@ MLX5E_DEFINE_STATS_GRP(pme, 0);
 MLX5E_DEFINE_STATS_GRP(channels, 0);
 MLX5E_DEFINE_STATS_GRP(per_port_buff_congest, 0);
 MLX5E_DEFINE_STATS_GRP(eth_ext, 0);
-static MLX5E_DEFINE_STATS_GRP(ipsec, 0);
 static MLX5E_DEFINE_STATS_GRP(tls, 0);
 
 /* The stats groups order is opposite to the update_stats() order calls */
@@ -1731,7 +1713,10 @@ mlx5e_stats_grp_t mlx5e_nic_stats_grps[] = {
        &MLX5E_STATS_GRP(pcie),
        &MLX5E_STATS_GRP(per_prio),
        &MLX5E_STATS_GRP(pme),
-       &MLX5E_STATS_GRP(ipsec),
+#ifdef CONFIG_MLX5_EN_IPSEC
+       &MLX5E_STATS_GRP(ipsec_sw),
+       &MLX5E_STATS_GRP(ipsec_hw),
+#endif
        &MLX5E_STATS_GRP(tls),
        &MLX5E_STATS_GRP(channels),
        &MLX5E_STATS_GRP(per_port_buff_congest),
index 092b39f..2b83ba9 100644 (file)
@@ -390,5 +390,7 @@ extern MLX5E_DECLARE_STATS_GRP(per_prio);
 extern MLX5E_DECLARE_STATS_GRP(pme);
 extern MLX5E_DECLARE_STATS_GRP(channels);
 extern MLX5E_DECLARE_STATS_GRP(per_port_buff_congest);
+extern MLX5E_DECLARE_STATS_GRP(ipsec_hw);
+extern MLX5E_DECLARE_STATS_GRP(ipsec_sw);
 
 #endif /* __MLX5_EN_STATS_H__ */
index 438128d..77397aa 100644 (file)
@@ -61,7 +61,7 @@
 #include "lib/geneve.h"
 #include "diag/en_tc_tracepoint.h"
 
-#define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto)
+#define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)
 
 struct mlx5_nic_flow_attr {
        u32 action;
@@ -171,6 +171,11 @@ struct tunnel_match_key {
        int filter_ifindex;
 };
 
+struct tunnel_match_enc_opts {
+       struct flow_dissector_key_enc_opts key;
+       struct flow_dissector_key_enc_opts mask;
+};
+
 /* Tunnel_id mapping is TUNNEL_INFO_BITS + ENC_OPTS_BITS.
  * Upper TUNNEL_INFO_BITS for general tunnel info.
  * Lower ENC_OPTS_BITS bits for enc_opts.
@@ -568,7 +573,7 @@ struct mlx5_core_dev *mlx5e_hairpin_get_mdev(struct net *net, int ifindex)
 
 static int mlx5e_hairpin_create_transport(struct mlx5e_hairpin *hp)
 {
-       u32 in[MLX5_ST_SZ_DW(create_tir_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(create_tir_in)] = {};
        void *tirc;
        int err;
 
@@ -582,7 +587,7 @@ static int mlx5e_hairpin_create_transport(struct mlx5e_hairpin *hp)
        MLX5_SET(tirc, tirc, inline_rqn, hp->pair->rqn[0]);
        MLX5_SET(tirc, tirc, transport_domain, hp->tdn);
 
-       err = mlx5_core_create_tir(hp->func_mdev, in, MLX5_ST_SZ_BYTES(create_tir_in), &hp->tirn);
+       err = mlx5_core_create_tir(hp->func_mdev, in, &hp->tirn);
        if (err)
                goto create_tir_err;
 
@@ -666,7 +671,7 @@ static int mlx5e_hairpin_create_indirect_tirs(struct mlx5e_hairpin *hp)
                mlx5e_build_indir_tir_ctx_hash(&priv->rss_params, &ttconfig, tirc, false);
 
                err = mlx5_core_create_tir(hp->func_mdev, in,
-                                          MLX5_ST_SZ_BYTES(create_tir_in), &hp->indir_tirn[tt]);
+                                          &hp->indir_tirn[tt]);
                if (err) {
                        mlx5_core_warn(hp->func_mdev, "create indirect tirs failed, %d\n", err);
                        goto err_destroy_tirs;
@@ -1343,7 +1348,8 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
        if (err)
                return err;
 
-       if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
+       if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR &&
+           !(attr->ct_attr.ct_action & TCA_CT_ACT_CLEAR)) {
                err = mlx5e_attach_mod_hdr(priv, flow, parse_attr);
                dealloc_mod_hdr_actions(&parse_attr->mod_hdr_acts);
                if (err)
@@ -1823,9 +1829,7 @@ enc_opts_is_dont_care_or_full_match(struct mlx5e_priv *priv,
                        *dont_care = false;
 
                        if (opt->opt_class != U16_MAX ||
-                           opt->type != U8_MAX ||
-                           memchr_inv(opt->opt_data, 0xFF,
-                                      opt->length * 4)) {
+                           opt->type != U8_MAX) {
                                NL_SET_ERR_MSG(extack,
                                               "Partial match of tunnel options in chain > 0 isn't supported");
                                netdev_warn(priv->netdev,
@@ -1862,6 +1866,7 @@ static int mlx5e_get_flow_tunnel_id(struct mlx5e_priv *priv,
        struct mlx5_esw_flow_attr *attr = flow->esw_attr;
        struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts;
        struct flow_match_enc_opts enc_opts_match;
+       struct tunnel_match_enc_opts tun_enc_opts;
        struct mlx5_rep_uplink_priv *uplink_priv;
        struct mlx5e_rep_priv *uplink_rpriv;
        struct tunnel_match_key tunnel_key;
@@ -1904,8 +1909,14 @@ static int mlx5e_get_flow_tunnel_id(struct mlx5e_priv *priv,
                goto err_enc_opts;
 
        if (!enc_opts_is_dont_care) {
+               memset(&tun_enc_opts, 0, sizeof(tun_enc_opts));
+               memcpy(&tun_enc_opts.key, enc_opts_match.key,
+                      sizeof(*enc_opts_match.key));
+               memcpy(&tun_enc_opts.mask, enc_opts_match.mask,
+                      sizeof(*enc_opts_match.mask));
+
                err = mapping_add(uplink_priv->tunnel_enc_opts_mapping,
-                                 enc_opts_match.key, &enc_opts_id);
+                                 &tun_enc_opts, &enc_opts_id);
                if (err)
                        goto err_enc_opts;
        }
@@ -2660,7 +2671,7 @@ static int offload_pedit_fields(struct mlx5e_priv *priv,
        set_vals = &hdrs[0].vals;
        add_vals = &hdrs[1].vals;
 
-       action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto);
+       action_size = MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto);
 
        for (i = 0; i < ARRAY_SIZE(fields); i++) {
                bool skip;
@@ -2793,7 +2804,7 @@ int alloc_mod_hdr_actions(struct mlx5_core_dev *mdev,
        if (mod_hdr_acts->num_actions < mod_hdr_acts->max_actions)
                return 0;
 
-       action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto);
+       action_size = MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto);
 
        max_hw_actions = mlx5e_flow_namespace_max_modify_action(mdev,
                                                                namespace);
@@ -3558,12 +3569,13 @@ static int add_vlan_pop_action(struct mlx5e_priv *priv,
                               struct mlx5_esw_flow_attr *attr,
                               u32 *action)
 {
-       int nest_level = attr->parse_attr->filter_dev->lower_level;
        struct flow_action_entry vlan_act = {
                .id = FLOW_ACTION_VLAN_POP,
        };
-       int err = 0;
+       int nest_level, err = 0;
 
+       nest_level = attr->parse_attr->filter_dev->lower_level -
+                                               priv->netdev->lower_level;
        while (nest_level--) {
                err = parse_tc_vlan_action(priv, &vlan_act, attr, action);
                if (err)
@@ -4705,7 +4717,7 @@ void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv)
 
 int mlx5e_tc_esw_init(struct rhashtable *tc_ht)
 {
-       const size_t sz_enc_opts = sizeof(struct flow_dissector_key_enc_opts);
+       const size_t sz_enc_opts = sizeof(struct tunnel_match_enc_opts);
        struct mlx5_rep_uplink_priv *uplink_priv;
        struct mlx5e_rep_priv *priv;
        struct mapping_ctx *mapping;
@@ -4800,7 +4812,7 @@ static bool mlx5e_restore_tunnel(struct mlx5e_priv *priv, struct sk_buff *skb,
                                 u32 tunnel_id)
 {
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
-       struct flow_dissector_key_enc_opts enc_opts = {};
+       struct tunnel_match_enc_opts enc_opts = {};
        struct mlx5_rep_uplink_priv *uplink_priv;
        struct mlx5e_rep_priv *uplink_rpriv;
        struct metadata_dst *tun_dst;
@@ -4838,7 +4850,7 @@ static bool mlx5e_restore_tunnel(struct mlx5e_priv *priv, struct sk_buff *skb,
                }
        }
 
-       tun_dst = tun_rx_dst(enc_opts.len);
+       tun_dst = tun_rx_dst(enc_opts.key.len);
        if (!tun_dst) {
                WARN_ON_ONCE(true);
                return false;
@@ -4852,9 +4864,11 @@ static bool mlx5e_restore_tunnel(struct mlx5e_priv *priv, struct sk_buff *skb,
                           key32_to_tunnel_id(key.enc_key_id.keyid),
                           TUNNEL_KEY);
 
-       if (enc_opts.len)
-               ip_tunnel_info_opts_set(&tun_dst->u.tun_info, enc_opts.data,
-                                       enc_opts.len, enc_opts.dst_opt_type);
+       if (enc_opts.key.len)
+               ip_tunnel_info_opts_set(&tun_dst->u.tun_info,
+                                       enc_opts.key.data,
+                                       enc_opts.key.len,
+                                       enc_opts.key.dst_opt_type);
 
        skb_dst_set(skb, (struct dst_entry *)tun_dst);
        dev = dev_get_by_index(&init_net, key.filter_ifindex);
@@ -4891,7 +4905,7 @@ bool mlx5e_tc_rep_update_skb(struct mlx5_cqe64 *cqe,
        reg_c0 = (be32_to_cpu(cqe->sop_drop_qpn) & MLX5E_TC_FLOW_ID_MASK);
        if (reg_c0 == MLX5_FS_DEFAULT_FLOW_TAG)
                reg_c0 = 0;
-       reg_c1 = be32_to_cpu(cqe->imm_inval_pkey);
+       reg_c1 = be32_to_cpu(cqe->ft_metadata);
 
        if (!reg_c0)
                return true;
index fd6b2a1..583e1b2 100644 (file)
@@ -324,7 +324,8 @@ netdev_tx_t mlx5e_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb,
                struct mlx5_wqe_ctrl_seg cur_ctrl = wqe->ctrl;
 #endif
                mlx5e_fill_sq_frag_edge(sq, wq, pi, contig_wqebbs_room);
-               wqe = mlx5e_sq_fetch_wqe(sq, sizeof(*wqe), &pi);
+               pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
+               wqe = MLX5E_TX_FETCH_WQE(sq, pi);
 #ifdef CONFIG_MLX5_EN_IPSEC
                wqe->eth = cur_eth;
 #endif
@@ -389,7 +390,8 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev)
        u16 pi;
 
        sq = priv->txq2sq[skb_get_queue_mapping(skb)];
-       wqe = mlx5e_sq_fetch_wqe(sq, sizeof(*wqe), &pi);
+       pi = mlx5_wq_cyc_ctr2ix(&sq->wq, sq->pc);
+       wqe = MLX5E_TX_FETCH_WQE(sq, pi);
 
        /* might send skbs and update wqe and pi */
        skb = mlx5e_accel_handle_tx(skb, sq, dev, &wqe, &pi);
@@ -399,22 +401,6 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev)
        return mlx5e_sq_xmit(sq, skb, wqe, pi, netdev_xmit_more());
 }
 
-static void mlx5e_dump_error_cqe(struct mlx5e_txqsq *sq,
-                                struct mlx5_err_cqe *err_cqe)
-{
-       struct mlx5_cqwq *wq = &sq->cq.wq;
-       u32 ci;
-
-       ci = mlx5_cqwq_ctr2ix(wq, wq->cc - 1);
-
-       netdev_err(sq->channel->netdev,
-                  "Error cqe on cqn 0x%x, ci 0x%x, sqn 0x%x, opcode 0x%x, syndrome 0x%x, vendor syndrome 0x%x\n",
-                  sq->cq.mcq.cqn, ci, sq->sqn,
-                  get_cqe_opcode((struct mlx5_cqe64 *)err_cqe),
-                  err_cqe->syndrome, err_cqe->vendor_err_synd);
-       mlx5_dump_err_cqe(sq->cq.mdev, err_cqe);
-}
-
 bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
 {
        struct mlx5e_sq_stats *stats;
@@ -501,7 +487,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
                if (unlikely(get_cqe_opcode(cqe) == MLX5_CQE_REQ_ERR)) {
                        if (!test_and_set_bit(MLX5E_SQ_STATE_RECOVERING,
                                              &sq->state)) {
-                               mlx5e_dump_error_cqe(sq,
+                               mlx5e_dump_error_cqe(&sq->cq, sq->sqn,
                                                     (struct mlx5_err_cqe *)cqe);
                                mlx5_wq_cyc_wqe_dump(&sq->wq, ci, wi->num_wqebbs);
                                queue_work(cq->channel->priv->wq,
@@ -586,7 +572,6 @@ netdev_tx_t mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb,
                          struct mlx5_av *av, u32 dqpn, u32 dqkey,
                          bool xmit_more)
 {
-       struct mlx5_wq_cyc *wq = &sq->wq;
        struct mlx5i_tx_wqe *wqe;
 
        struct mlx5_wqe_datagram_seg *datagram;
@@ -596,9 +581,9 @@ netdev_tx_t mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb,
        struct mlx5e_tx_wqe_info *wi;
 
        struct mlx5e_sq_stats *stats = sq->stats;
-       u16 headlen, ihs, pi, contig_wqebbs_room;
        u16 ds_cnt, ds_cnt_inl = 0;
        u8 num_wqebbs, opcode;
+       u16 headlen, ihs, pi;
        u32 num_bytes;
        int num_dma;
        __be16 mss;
@@ -634,14 +619,8 @@ netdev_tx_t mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb,
        }
 
        num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS);
-       pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
-       contig_wqebbs_room = mlx5_wq_cyc_get_contig_wqebbs(wq, pi);
-       if (unlikely(contig_wqebbs_room < num_wqebbs)) {
-               mlx5e_fill_sq_frag_edge(sq, wq, pi, contig_wqebbs_room);
-               pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
-       }
-
-       mlx5i_sq_fetch_wqe(sq, &wqe, pi);
+       pi = mlx5e_txqsq_get_next_pi(sq, num_wqebbs);
+       wqe = MLX5I_SQ_FETCH_WQE(sq, pi);
 
        /* fill wqe */
        wi       = &sq->db.wqe_info[pi];
index 87c49e7..869fd58 100644 (file)
@@ -78,8 +78,8 @@ void mlx5e_trigger_irq(struct mlx5e_icosq *sq)
        struct mlx5e_tx_wqe *nopwqe;
        u16 pi = mlx5_wq_cyc_ctr2ix(wq, sq->pc);
 
-       sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP;
-       sq->db.ico_wqe[pi].num_wqebbs = 1;
+       sq->db.wqe_info[pi].opcode = MLX5_OPCODE_NOP;
+       sq->db.wqe_info[pi].num_wqebbs = 1;
        nopwqe = mlx5e_post_nop(wq, sq->sqn, &sq->pc);
        mlx5e_notify_hw(wq, sq->pc, sq->uar_map, &nopwqe->ctrl);
 }
@@ -152,7 +152,11 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
                                mlx5e_post_rx_wqes,
                                rq);
        if (xsk_open) {
-               mlx5e_poll_ico_cq(&c->xskicosq.cq);
+               if (mlx5e_poll_ico_cq(&c->xskicosq.cq))
+                       /* Don't clear the flag if nothing was polled to prevent
+                        * queueing more WQEs and overflowing XSKICOSQ.
+                        */
+                       clear_bit(MLX5E_SQ_STATE_PENDING_XSK_TX, &c->xskicosq.state);
                busy |= mlx5e_poll_xdpsq_cq(&xsksq->cq);
                busy_xsk |= mlx5e_napi_xsk_post(xsksq, xskrq);
        }
index cccea3a..4d974b5 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/mlx5/driver.h>
 #include <linux/mlx5/vport.h>
 #include <linux/mlx5/eq.h>
-#include <linux/mlx5/cmd.h>
 #ifdef CONFIG_RFS_ACCEL
 #include <linux/cpu_rmap.h>
 #endif
@@ -102,12 +101,11 @@ struct mlx5_eq_table {
 
 static int mlx5_cmd_destroy_eq(struct mlx5_core_dev *dev, u8 eqn)
 {
-       u32 out[MLX5_ST_SZ_DW(destroy_eq_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(destroy_eq_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_eq_in)] = {};
 
        MLX5_SET(destroy_eq_in, in, opcode, MLX5_CMD_OP_DESTROY_EQ);
        MLX5_SET(destroy_eq_in, in, eq_number, eqn);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, destroy_eq, in);
 }
 
 /* caller must eventually call mlx5_cq_put on the returned cq */
index 0290010..d5bf908 100644 (file)
@@ -274,7 +274,7 @@ mlx5_esw_chains_destroy_fdb_table(struct mlx5_eswitch *esw,
 static int
 create_fdb_chain_restore(struct fdb_chain *fdb_chain)
 {
-       char modact[MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto)];
+       char modact[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)];
        struct mlx5_eswitch *esw = fdb_chain->esw;
        struct mlx5_modify_hdr *mod_hdr;
        u32 index;
index 7f618a4..c5eb4e7 100644 (file)
@@ -84,8 +84,7 @@ mlx5_eswitch_get_vport(struct mlx5_eswitch *esw, u16 vport_num)
 static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport,
                                        u32 events_mask)
 {
-       int in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)]   = {0};
-       int out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)] = {};
        void *nic_vport_ctx;
 
        MLX5_SET(modify_nic_vport_context_in, in,
@@ -108,40 +107,24 @@ static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport,
                MLX5_SET(nic_vport_context, nic_vport_ctx,
                         event_on_promisc_change, 1);
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, modify_nic_vport_context, in);
 }
 
 /* E-Switch vport context HW commands */
 int mlx5_eswitch_modify_esw_vport_context(struct mlx5_core_dev *dev, u16 vport,
-                                         bool other_vport,
-                                         void *in, int inlen)
+                                         bool other_vport, void *in)
 {
-       u32 out[MLX5_ST_SZ_DW(modify_esw_vport_context_out)] = {0};
-
        MLX5_SET(modify_esw_vport_context_in, in, opcode,
                 MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT);
        MLX5_SET(modify_esw_vport_context_in, in, vport_number, vport);
        MLX5_SET(modify_esw_vport_context_in, in, other_vport, other_vport);
-       return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
-}
-
-int mlx5_eswitch_query_esw_vport_context(struct mlx5_core_dev *dev, u16 vport,
-                                        bool other_vport,
-                                        void *out, int outlen)
-{
-       u32 in[MLX5_ST_SZ_DW(query_esw_vport_context_in)] = {};
-
-       MLX5_SET(query_esw_vport_context_in, in, opcode,
-                MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT);
-       MLX5_SET(modify_esw_vport_context_in, in, vport_number, vport);
-       MLX5_SET(modify_esw_vport_context_in, in, other_vport, other_vport);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
+       return mlx5_cmd_exec_in(dev, modify_esw_vport_context, in);
 }
 
 static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u16 vport,
                                  u16 vlan, u8 qos, u8 set_flags)
 {
-       u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)] = {};
 
        if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
            !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
@@ -170,8 +153,7 @@ static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u16 vport,
        MLX5_SET(modify_esw_vport_context_in, in,
                 field_select.vport_cvlan_insert, 1);
 
-       return mlx5_eswitch_modify_esw_vport_context(dev, vport, true,
-                                                    in, sizeof(in));
+       return mlx5_eswitch_modify_esw_vport_context(dev, vport, true, in);
 }
 
 /* E-Switch FDB */
@@ -1901,7 +1883,7 @@ const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev)
        MLX5_SET(query_esw_functions_in, in, opcode,
                 MLX5_CMD_OP_QUERY_ESW_FUNCTIONS);
 
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
+       err = mlx5_cmd_exec_inout(dev, query_esw_functions, in, out);
        if (!err)
                return out;
 
@@ -2783,8 +2765,8 @@ int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,
 {
        struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
        int outlen = MLX5_ST_SZ_BYTES(query_vport_counter_out);
-       u32 in[MLX5_ST_SZ_DW(query_vport_counter_in)] = {0};
-       struct mlx5_vport_drop_stats stats = {0};
+       u32 in[MLX5_ST_SZ_DW(query_vport_counter_in)] = {};
+       struct mlx5_vport_drop_stats stats = {};
        int err = 0;
        u32 *out;
 
@@ -2801,7 +2783,7 @@ int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,
        MLX5_SET(query_vport_counter_in, in, vport_number, vport->vport);
        MLX5_SET(query_vport_counter_in, in, other_vport, 1);
 
-       err = mlx5_cmd_exec(esw->dev, in, sizeof(in), out, outlen);
+       err = mlx5_cmd_exec_inout(esw->dev, query_vport_counter, in, out);
        if (err)
                goto free_out;
 
index 39f42f9..4a1c6c7 100644 (file)
@@ -329,11 +329,7 @@ int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,
 void mlx5_eswitch_del_send_to_vport_rule(struct mlx5_flow_handle *rule);
 
 int mlx5_eswitch_modify_esw_vport_context(struct mlx5_core_dev *dev, u16 vport,
-                                         bool other_vport,
-                                         void *in, int inlen);
-int mlx5_eswitch_query_esw_vport_context(struct mlx5_core_dev *dev, u16 vport,
-                                        bool other_vport,
-                                        void *out, int outlen);
+                                         bool other_vport, void *in);
 
 struct mlx5_flow_spec;
 struct mlx5_esw_flow_attr;
@@ -403,7 +399,6 @@ enum {
        MLX5_ESW_ATTR_FLAG_VLAN_HANDLED  = BIT(0),
        MLX5_ESW_ATTR_FLAG_SLOW_PATH     = BIT(1),
        MLX5_ESW_ATTR_FLAG_NO_IN_PORT    = BIT(2),
-       MLX5_ESW_ATTR_FLAG_HAIRPIN       = BIT(3),
 };
 
 struct mlx5_esw_flow_attr {
index f171eb2..57ac2ef 100644 (file)
@@ -300,7 +300,6 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
        bool split = !!(attr->split_count);
        struct mlx5_flow_handle *rule;
        struct mlx5_flow_table *fdb;
-       bool hairpin = false;
        int j, i = 0;
 
        if (esw->mode != MLX5_ESWITCH_OFFLOADS)
@@ -398,21 +397,16 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw,
                goto err_esw_get;
        }
 
-       if (mlx5_eswitch_termtbl_required(esw, attr, &flow_act, spec)) {
+       if (mlx5_eswitch_termtbl_required(esw, attr, &flow_act, spec))
                rule = mlx5_eswitch_add_termtbl_rule(esw, fdb, spec, attr,
                                                     &flow_act, dest, i);
-               hairpin = true;
-       } else {
+       else
                rule = mlx5_add_flow_rules(fdb, spec, &flow_act, dest, i);
-       }
        if (IS_ERR(rule))
                goto err_add_rule;
        else
                atomic64_inc(&esw->offloads.num_flows);
 
-       if (hairpin)
-               attr->flags |= MLX5_ESW_ATTR_FLAG_HAIRPIN;
-
        return rule;
 
 err_add_rule:
@@ -501,7 +495,7 @@ __mlx5_eswitch_del_rule(struct mlx5_eswitch *esw,
 
        mlx5_del_flow_rules(rule);
 
-       if (attr->flags & MLX5_ESW_ATTR_FLAG_HAIRPIN) {
+       if (!(attr->flags & MLX5_ESW_ATTR_FLAG_SLOW_PATH)) {
                /* unref the term table */
                for (i = 0; i < MLX5_MAX_FLOW_FWD_VPORTS; i++) {
                        if (attr->dests[i].termtbl)
@@ -790,7 +784,8 @@ static bool mlx5_eswitch_reg_c1_loopback_supported(struct mlx5_eswitch *esw)
 static int esw_set_passing_vport_metadata(struct mlx5_eswitch *esw, bool enable)
 {
        u32 out[MLX5_ST_SZ_DW(query_esw_vport_context_out)] = {};
-       u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)] = {};
+       u32 min[MLX5_ST_SZ_DW(modify_esw_vport_context_in)] = {};
+       u32 in[MLX5_ST_SZ_DW(query_esw_vport_context_in)] = {};
        u8 curr, wanted;
        int err;
 
@@ -798,8 +793,9 @@ static int esw_set_passing_vport_metadata(struct mlx5_eswitch *esw, bool enable)
            !mlx5_eswitch_vport_match_metadata_enabled(esw))
                return 0;
 
-       err = mlx5_eswitch_query_esw_vport_context(esw->dev, 0, false,
-                                                  out, sizeof(out));
+       MLX5_SET(query_esw_vport_context_in, in, opcode,
+                MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT);
+       err = mlx5_cmd_exec_inout(esw->dev, query_esw_vport_context, in, out);
        if (err)
                return err;
 
@@ -814,14 +810,12 @@ static int esw_set_passing_vport_metadata(struct mlx5_eswitch *esw, bool enable)
        else
                curr &= ~wanted;
 
-       MLX5_SET(modify_esw_vport_context_in, in,
+       MLX5_SET(modify_esw_vport_context_in, min,
                 esw_vport_context.fdb_to_vport_reg_c_id, curr);
-
-       MLX5_SET(modify_esw_vport_context_in, in,
+       MLX5_SET(modify_esw_vport_context_in, min,
                 field_select.fdb_to_vport_reg_c_id, 1);
 
-       err = mlx5_eswitch_modify_esw_vport_context(esw->dev, 0, false, in,
-                                                   sizeof(in));
+       err = mlx5_eswitch_modify_esw_vport_context(esw->dev, 0, false, min);
        if (!err) {
                if (enable && (curr & MLX5_FDB_TO_VPORT_REG_C_1))
                        esw->flags |= MLX5_ESWITCH_REG_C1_LOOPBACK_ENABLED;
@@ -1474,7 +1468,7 @@ query_vports:
 out:
        *mode = mlx5_mode;
        return 0;
-}       
+}
 
 static void esw_destroy_restore_table(struct mlx5_eswitch *esw)
 {
@@ -1490,7 +1484,7 @@ static void esw_destroy_restore_table(struct mlx5_eswitch *esw)
 
 static int esw_create_restore_table(struct mlx5_eswitch *esw)
 {
-       u8 modact[MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto)] = {};
+       u8 modact[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};
        int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
        struct mlx5_flow_table_attr ft_attr = {};
        struct mlx5_core_dev *dev = esw->dev;
@@ -1556,9 +1550,9 @@ static int esw_create_restore_table(struct mlx5_eswitch *esw)
                                           MLX5_FLOW_NAMESPACE_KERNEL, 1,
                                           modact);
        if (IS_ERR(mod_hdr)) {
+               err = PTR_ERR(mod_hdr);
                esw_warn(dev, "Failed to create restore mod header, err: %d\n",
                         err);
-               err = PTR_ERR(mod_hdr);
                goto err_mod_hdr;
        }
 
@@ -1900,7 +1894,7 @@ static int esw_vport_ingress_prio_tag_config(struct mlx5_eswitch *esw,
 static int esw_vport_add_ingress_acl_modify_metadata(struct mlx5_eswitch *esw,
                                                     struct mlx5_vport *vport)
 {
-       u8 action[MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto)] = {};
+       u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};
        struct mlx5_flow_act flow_act = {};
        int err = 0;
        u32 key;
@@ -2225,10 +2219,12 @@ static int esw_offloads_steering_init(struct mlx5_eswitch *esw)
                total_vports = num_vfs + MLX5_SPECIAL_VPORTS(esw->dev);
 
        memset(&esw->fdb_table.offloads, 0, sizeof(struct offloads_fdb));
+       mutex_init(&esw->fdb_table.offloads.vports.lock);
+       hash_init(esw->fdb_table.offloads.vports.table);
 
        err = esw_create_uplink_offloads_acl_tables(esw);
        if (err)
-               return err;
+               goto create_acl_err;
 
        err = esw_create_offloads_table(esw, total_vports);
        if (err)
@@ -2246,9 +2242,6 @@ static int esw_offloads_steering_init(struct mlx5_eswitch *esw)
        if (err)
                goto create_fg_err;
 
-       mutex_init(&esw->fdb_table.offloads.vports.lock);
-       hash_init(esw->fdb_table.offloads.vports.table);
-
        return 0;
 
 create_fg_err:
@@ -2259,18 +2252,19 @@ create_restore_err:
        esw_destroy_offloads_table(esw);
 create_offloads_err:
        esw_destroy_uplink_offloads_acl_tables(esw);
-
+create_acl_err:
+       mutex_destroy(&esw->fdb_table.offloads.vports.lock);
        return err;
 }
 
 static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw)
 {
-       mutex_destroy(&esw->fdb_table.offloads.vports.lock);
        esw_destroy_vport_rx_group(esw);
        esw_destroy_offloads_fdb_tables(esw);
        esw_destroy_restore_table(esw);
        esw_destroy_offloads_table(esw);
        esw_destroy_uplink_offloads_acl_tables(esw);
+       mutex_destroy(&esw->fdb_table.offloads.vports.lock);
 }
 
 static void
@@ -2383,9 +2377,9 @@ int esw_offloads_enable(struct mlx5_eswitch *esw)
 err_vports:
        esw_offloads_unload_rep(esw, MLX5_VPORT_UPLINK);
 err_uplink:
-       esw_set_passing_vport_metadata(esw, false);
-err_steering_init:
        esw_offloads_steering_cleanup(esw);
+err_steering_init:
+       esw_set_passing_vport_metadata(esw, false);
 err_vport_metadata:
        mlx5_rdma_disable_roce(esw->dev);
        mutex_destroy(&esw->offloads.termtbl_mutex);
index c0fd221..9a37077 100644 (file)
@@ -31,7 +31,6 @@
  */
 
 #include <linux/etherdevice.h>
-#include <linux/mlx5/cmd.h>
 #include <linux/mlx5/driver.h>
 #include <linux/mlx5/device.h>
 
@@ -143,15 +142,15 @@ int mlx5_fpga_query(struct mlx5_core_dev *dev, struct mlx5_fpga_query *query)
 int mlx5_fpga_create_qp(struct mlx5_core_dev *dev, void *fpga_qpc,
                        u32 *fpga_qpn)
 {
-       u32 in[MLX5_ST_SZ_DW(fpga_create_qp_in)] = {0};
-       u32 out[MLX5_ST_SZ_DW(fpga_create_qp_out)];
+       u32 out[MLX5_ST_SZ_DW(fpga_create_qp_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(fpga_create_qp_in)] = {};
        int ret;
 
        MLX5_SET(fpga_create_qp_in, in, opcode, MLX5_CMD_OP_FPGA_CREATE_QP);
        memcpy(MLX5_ADDR_OF(fpga_create_qp_in, in, fpga_qpc), fpga_qpc,
               MLX5_FLD_SZ_BYTES(fpga_create_qp_in, fpga_qpc));
 
-       ret = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       ret = mlx5_cmd_exec_inout(dev, fpga_create_qp, in, out);
        if (ret)
                return ret;
 
@@ -165,8 +164,7 @@ int mlx5_fpga_modify_qp(struct mlx5_core_dev *dev, u32 fpga_qpn,
                        enum mlx5_fpga_qpc_field_select fields,
                        void *fpga_qpc)
 {
-       u32 in[MLX5_ST_SZ_DW(fpga_modify_qp_in)] = {0};
-       u32 out[MLX5_ST_SZ_DW(fpga_modify_qp_out)];
+       u32 in[MLX5_ST_SZ_DW(fpga_modify_qp_in)] = {};
 
        MLX5_SET(fpga_modify_qp_in, in, opcode, MLX5_CMD_OP_FPGA_MODIFY_QP);
        MLX5_SET(fpga_modify_qp_in, in, field_select, fields);
@@ -174,20 +172,20 @@ int mlx5_fpga_modify_qp(struct mlx5_core_dev *dev, u32 fpga_qpn,
        memcpy(MLX5_ADDR_OF(fpga_modify_qp_in, in, fpga_qpc), fpga_qpc,
               MLX5_FLD_SZ_BYTES(fpga_modify_qp_in, fpga_qpc));
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, fpga_modify_qp, in);
 }
 
 int mlx5_fpga_query_qp(struct mlx5_core_dev *dev,
                       u32 fpga_qpn, void *fpga_qpc)
 {
-       u32 in[MLX5_ST_SZ_DW(fpga_query_qp_in)] = {0};
-       u32 out[MLX5_ST_SZ_DW(fpga_query_qp_out)];
+       u32 out[MLX5_ST_SZ_DW(fpga_query_qp_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(fpga_query_qp_in)] = {};
        int ret;
 
        MLX5_SET(fpga_query_qp_in, in, opcode, MLX5_CMD_OP_FPGA_QUERY_QP);
        MLX5_SET(fpga_query_qp_in, in, fpga_qpn, fpga_qpn);
 
-       ret = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       ret = mlx5_cmd_exec_inout(dev, fpga_query_qp, in, out);
        if (ret)
                return ret;
 
@@ -198,20 +196,19 @@ int mlx5_fpga_query_qp(struct mlx5_core_dev *dev,
 
 int mlx5_fpga_destroy_qp(struct mlx5_core_dev *dev, u32 fpga_qpn)
 {
-       u32 in[MLX5_ST_SZ_DW(fpga_destroy_qp_in)] = {0};
-       u32 out[MLX5_ST_SZ_DW(fpga_destroy_qp_out)];
+       u32 in[MLX5_ST_SZ_DW(fpga_destroy_qp_in)] = {};
 
        MLX5_SET(fpga_destroy_qp_in, in, opcode, MLX5_CMD_OP_FPGA_DESTROY_QP);
        MLX5_SET(fpga_destroy_qp_in, in, fpga_qpn, fpga_qpn);
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, fpga_destroy_qp, in);
 }
 
 int mlx5_fpga_query_qp_counters(struct mlx5_core_dev *dev, u32 fpga_qpn,
                                bool clear, struct mlx5_fpga_qp_counters *data)
 {
-       u32 in[MLX5_ST_SZ_DW(fpga_query_qp_counters_in)] = {0};
-       u32 out[MLX5_ST_SZ_DW(fpga_query_qp_counters_out)];
+       u32 out[MLX5_ST_SZ_DW(fpga_query_qp_counters_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(fpga_query_qp_counters_in)] = {};
        int ret;
 
        MLX5_SET(fpga_query_qp_counters_in, in, opcode,
@@ -219,7 +216,7 @@ int mlx5_fpga_query_qp_counters(struct mlx5_core_dev *dev, u32 fpga_qpn,
        MLX5_SET(fpga_query_qp_counters_in, in, clear, clear);
        MLX5_SET(fpga_query_qp_counters_in, in, fpga_qpn, fpga_qpn);
 
-       ret = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       ret = mlx5_cmd_exec_inout(dev, fpga_query_qp_counters, in, out);
        if (ret)
                return ret;
 
index 6102113..182d3ac 100644 (file)
@@ -165,7 +165,7 @@ static void mlx5_fpga_conn_post_send(struct mlx5_fpga_conn *conn,
        ctrl->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE;
        ctrl->opmod_idx_opcode = cpu_to_be32(((conn->qp.sq.pc & 0xffff) << 8) |
                                             MLX5_OPCODE_SEND);
-       ctrl->qpn_ds = cpu_to_be32(size | (conn->qp.mqp.qpn << 8));
+       ctrl->qpn_ds = cpu_to_be32(size | (conn->qp.qpn << 8));
 
        conn->qp.sq.pc++;
        conn->qp.sq.bufs[ix] = buf;
@@ -362,23 +362,6 @@ static void mlx5_fpga_conn_arm_cq(struct mlx5_fpga_conn *conn)
                    conn->fdev->conn_res.uar->map, conn->cq.wq.cc);
 }
 
-static void mlx5_fpga_conn_cq_event(struct mlx5_core_cq *mcq,
-                                   enum mlx5_event event)
-{
-       struct mlx5_fpga_conn *conn;
-
-       conn = container_of(mcq, struct mlx5_fpga_conn, cq.mcq);
-       mlx5_fpga_warn(conn->fdev, "CQ event %u on CQ #%u\n", event, mcq->cqn);
-}
-
-static void mlx5_fpga_conn_event(struct mlx5_core_qp *mqp, int event)
-{
-       struct mlx5_fpga_conn *conn;
-
-       conn = container_of(mqp, struct mlx5_fpga_conn, qp.mqp);
-       mlx5_fpga_warn(conn->fdev, "QP event %u on QP #%u\n", event, mqp->qpn);
-}
-
 static inline void mlx5_fpga_conn_cqes(struct mlx5_fpga_conn *conn,
                                       unsigned int budget)
 {
@@ -493,7 +476,6 @@ static int mlx5_fpga_conn_create_cq(struct mlx5_fpga_conn *conn, int cq_size)
        *conn->cq.mcq.arm_db    = 0;
        conn->cq.mcq.vector     = 0;
        conn->cq.mcq.comp       = mlx5_fpga_conn_cq_complete;
-       conn->cq.mcq.event      = mlx5_fpga_conn_cq_event;
        conn->cq.mcq.irqn       = irqn;
        conn->cq.mcq.uar        = fdev->conn_res.uar;
        tasklet_init(&conn->cq.tasklet, mlx5_fpga_conn_cq_tasklet,
@@ -534,8 +516,9 @@ static int mlx5_fpga_conn_create_qp(struct mlx5_fpga_conn *conn,
                                    unsigned int tx_size, unsigned int rx_size)
 {
        struct mlx5_fpga_device *fdev = conn->fdev;
+       u32 out[MLX5_ST_SZ_DW(create_qp_out)] = {};
        struct mlx5_core_dev *mdev = fdev->mdev;
-       u32 temp_qpc[MLX5_ST_SZ_DW(qpc)] = {0};
+       u32 temp_qpc[MLX5_ST_SZ_DW(qpc)] = {};
        void *in = NULL, *qpc;
        int err, inlen;
 
@@ -600,12 +583,13 @@ static int mlx5_fpga_conn_create_qp(struct mlx5_fpga_conn *conn,
        mlx5_fill_page_frag_array(&conn->qp.wq_ctrl.buf,
                                  (__be64 *)MLX5_ADDR_OF(create_qp_in, in, pas));
 
-       err = mlx5_core_create_qp(mdev, &conn->qp.mqp, in, inlen);
+       MLX5_SET(create_qp_in, in, opcode, MLX5_CMD_OP_CREATE_QP);
+       err = mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out));
        if (err)
                goto err_sq_bufs;
 
-       conn->qp.mqp.event = mlx5_fpga_conn_event;
-       mlx5_fpga_dbg(fdev, "Created QP #0x%x\n", conn->qp.mqp.qpn);
+       conn->qp.qpn = MLX5_GET(create_qp_out, out, qpn);
+       mlx5_fpga_dbg(fdev, "Created QP #0x%x\n", conn->qp.qpn);
 
        goto out;
 
@@ -658,7 +642,13 @@ static void mlx5_fpga_conn_flush_send_bufs(struct mlx5_fpga_conn *conn)
 
 static void mlx5_fpga_conn_destroy_qp(struct mlx5_fpga_conn *conn)
 {
-       mlx5_core_destroy_qp(conn->fdev->mdev, &conn->qp.mqp);
+       struct mlx5_core_dev *dev = conn->fdev->mdev;
+       u32 in[MLX5_ST_SZ_DW(destroy_qp_in)] = {};
+
+       MLX5_SET(destroy_qp_in, in, opcode, MLX5_CMD_OP_DESTROY_QP);
+       MLX5_SET(destroy_qp_in, in, qpn, conn->qp.qpn);
+       mlx5_cmd_exec_in(dev, destroy_qp, in);
+
        mlx5_fpga_conn_free_recv_bufs(conn);
        mlx5_fpga_conn_flush_send_bufs(conn);
        kvfree(conn->qp.sq.bufs);
@@ -666,30 +656,29 @@ static void mlx5_fpga_conn_destroy_qp(struct mlx5_fpga_conn *conn)
        mlx5_wq_destroy(&conn->qp.wq_ctrl);
 }
 
-static inline int mlx5_fpga_conn_reset_qp(struct mlx5_fpga_conn *conn)
+static int mlx5_fpga_conn_reset_qp(struct mlx5_fpga_conn *conn)
 {
        struct mlx5_core_dev *mdev = conn->fdev->mdev;
+       u32 in[MLX5_ST_SZ_DW(qp_2rst_in)] = {};
+
+       mlx5_fpga_dbg(conn->fdev, "Modifying QP %u to RST\n", conn->qp.qpn);
 
-       mlx5_fpga_dbg(conn->fdev, "Modifying QP %u to RST\n", conn->qp.mqp.qpn);
+       MLX5_SET(qp_2rst_in, in, opcode, MLX5_CMD_OP_2RST_QP);
+       MLX5_SET(qp_2rst_in, in, qpn, conn->qp.qpn);
 
-       return mlx5_core_qp_modify(mdev, MLX5_CMD_OP_2RST_QP, 0, NULL,
-                                  &conn->qp.mqp);
+       return mlx5_cmd_exec_in(mdev, qp_2rst, in);
 }
 
-static inline int mlx5_fpga_conn_init_qp(struct mlx5_fpga_conn *conn)
+static int mlx5_fpga_conn_init_qp(struct mlx5_fpga_conn *conn)
 {
+       u32 in[MLX5_ST_SZ_DW(rst2init_qp_in)] = {};
        struct mlx5_fpga_device *fdev = conn->fdev;
        struct mlx5_core_dev *mdev = fdev->mdev;
-       u32 *qpc = NULL;
-       int err;
+       u32 *qpc;
 
-       mlx5_fpga_dbg(conn->fdev, "Modifying QP %u to INIT\n", conn->qp.mqp.qpn);
+       mlx5_fpga_dbg(conn->fdev, "Modifying QP %u to INIT\n", conn->qp.qpn);
 
-       qpc = kzalloc(MLX5_ST_SZ_BYTES(qpc), GFP_KERNEL);
-       if (!qpc) {
-               err = -ENOMEM;
-               goto out;
-       }
+       qpc = MLX5_ADDR_OF(rst2init_qp_in, in, qpc);
 
        MLX5_SET(qpc, qpc, st, MLX5_QP_ST_RC);
        MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED);
@@ -700,32 +689,22 @@ static inline int mlx5_fpga_conn_init_qp(struct mlx5_fpga_conn *conn)
        MLX5_SET(qpc, qpc, cqn_rcv, conn->cq.mcq.cqn);
        MLX5_SET64(qpc, qpc, dbr_addr, conn->qp.wq_ctrl.db.dma);
 
-       err = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_RST2INIT_QP, 0, qpc,
-                                 &conn->qp.mqp);
-       if (err) {
-               mlx5_fpga_warn(fdev, "qp_modify RST2INIT failed: %d\n", err);
-               goto out;
-       }
+       MLX5_SET(rst2init_qp_in, in, opcode, MLX5_CMD_OP_RST2INIT_QP);
+       MLX5_SET(rst2init_qp_in, in, qpn, conn->qp.qpn);
 
-out:
-       kfree(qpc);
-       return err;
+       return mlx5_cmd_exec_in(mdev, rst2init_qp, in);
 }
 
-static inline int mlx5_fpga_conn_rtr_qp(struct mlx5_fpga_conn *conn)
+static int mlx5_fpga_conn_rtr_qp(struct mlx5_fpga_conn *conn)
 {
+       u32 in[MLX5_ST_SZ_DW(init2rtr_qp_in)] = {};
        struct mlx5_fpga_device *fdev = conn->fdev;
        struct mlx5_core_dev *mdev = fdev->mdev;
-       u32 *qpc = NULL;
-       int err;
+       u32 *qpc;
 
        mlx5_fpga_dbg(conn->fdev, "QP RTR\n");
 
-       qpc = kzalloc(MLX5_ST_SZ_BYTES(qpc), GFP_KERNEL);
-       if (!qpc) {
-               err = -ENOMEM;
-               goto out;
-       }
+       qpc = MLX5_ADDR_OF(init2rtr_qp_in, in, qpc);
 
        MLX5_SET(qpc, qpc, mtu, MLX5_QPC_MTU_1K_BYTES);
        MLX5_SET(qpc, qpc, log_msg_max, (u8)MLX5_CAP_GEN(mdev, log_max_msg));
@@ -745,33 +724,22 @@ static inline int mlx5_fpga_conn_rtr_qp(struct mlx5_fpga_conn *conn)
               MLX5_ADDR_OF(fpga_qpc, conn->fpga_qpc, fpga_ip),
               MLX5_FLD_SZ_BYTES(qpc, primary_address_path.rgid_rip));
 
-       err = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_INIT2RTR_QP, 0, qpc,
-                                 &conn->qp.mqp);
-       if (err) {
-               mlx5_fpga_warn(fdev, "qp_modify RST2INIT failed: %d\n", err);
-               goto out;
-       }
+       MLX5_SET(init2rtr_qp_in, in, opcode, MLX5_CMD_OP_INIT2RTR_QP);
+       MLX5_SET(init2rtr_qp_in, in, qpn, conn->qp.qpn);
 
-out:
-       kfree(qpc);
-       return err;
+       return mlx5_cmd_exec_in(mdev, init2rtr_qp, in);
 }
 
-static inline int mlx5_fpga_conn_rts_qp(struct mlx5_fpga_conn *conn)
+static int mlx5_fpga_conn_rts_qp(struct mlx5_fpga_conn *conn)
 {
        struct mlx5_fpga_device *fdev = conn->fdev;
+       u32 in[MLX5_ST_SZ_DW(rtr2rts_qp_in)] = {};
        struct mlx5_core_dev *mdev = fdev->mdev;
-       u32 *qpc = NULL;
-       u32 opt_mask;
-       int err;
+       u32 *qpc;
 
        mlx5_fpga_dbg(conn->fdev, "QP RTS\n");
 
-       qpc = kzalloc(MLX5_ST_SZ_BYTES(qpc), GFP_KERNEL);
-       if (!qpc) {
-               err = -ENOMEM;
-               goto out;
-       }
+       qpc = MLX5_ADDR_OF(rtr2rts_qp_in, in, qpc);
 
        MLX5_SET(qpc, qpc, log_ack_req_freq, 8);
        MLX5_SET(qpc, qpc, min_rnr_nak, 0x12);
@@ -781,17 +749,11 @@ static inline int mlx5_fpga_conn_rts_qp(struct mlx5_fpga_conn *conn)
        MLX5_SET(qpc, qpc, retry_count, 7);
        MLX5_SET(qpc, qpc, rnr_retry, 7); /* Infinite retry if RNR NACK */
 
-       opt_mask = MLX5_QP_OPTPAR_RNR_TIMEOUT;
-       err = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_RTR2RTS_QP, opt_mask, qpc,
-                                 &conn->qp.mqp);
-       if (err) {
-               mlx5_fpga_warn(fdev, "qp_modify RST2INIT failed: %d\n", err);
-               goto out;
-       }
+       MLX5_SET(rtr2rts_qp_in, in, opcode, MLX5_CMD_OP_RTR2RTS_QP);
+       MLX5_SET(rtr2rts_qp_in, in, qpn, conn->qp.qpn);
+       MLX5_SET(rtr2rts_qp_in, in, opt_param_mask, MLX5_QP_OPTPAR_RNR_TIMEOUT);
 
-out:
-       kfree(qpc);
-       return err;
+       return mlx5_cmd_exec_in(mdev, rtr2rts_qp, in);
 }
 
 static int mlx5_fpga_conn_connect(struct mlx5_fpga_conn *conn)
@@ -931,7 +893,7 @@ struct mlx5_fpga_conn *mlx5_fpga_conn_create(struct mlx5_fpga_device *fdev,
        MLX5_SET(fpga_qpc, conn->fpga_qpc, next_rcv_psn, 1);
        MLX5_SET(fpga_qpc, conn->fpga_qpc, next_send_psn, 0);
        MLX5_SET(fpga_qpc, conn->fpga_qpc, pkey, MLX5_FPGA_PKEY);
-       MLX5_SET(fpga_qpc, conn->fpga_qpc, remote_qpn, conn->qp.mqp.qpn);
+       MLX5_SET(fpga_qpc, conn->fpga_qpc, remote_qpn, conn->qp.qpn);
        MLX5_SET(fpga_qpc, conn->fpga_qpc, rnr_retry, 7);
        MLX5_SET(fpga_qpc, conn->fpga_qpc, retry_count, 7);
 
@@ -972,19 +934,11 @@ out:
 
 void mlx5_fpga_conn_destroy(struct mlx5_fpga_conn *conn)
 {
-       struct mlx5_fpga_device *fdev = conn->fdev;
-       struct mlx5_core_dev *mdev = fdev->mdev;
-       int err = 0;
-
        conn->qp.active = false;
        tasklet_disable(&conn->cq.tasklet);
        synchronize_irq(conn->cq.mcq.irqn);
 
        mlx5_fpga_destroy_qp(conn->fdev->mdev, conn->fpga_qpn);
-       err = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_2ERR_QP, 0, NULL,
-                                 &conn->qp.mqp);
-       if (err)
-               mlx5_fpga_warn(fdev, "qp_modify 2ERR failed: %d\n", err);
        mlx5_fpga_conn_destroy_qp(conn);
        mlx5_fpga_conn_destroy_cq(conn);
 
index 634ae10..5116e86 100644 (file)
@@ -65,7 +65,7 @@ struct mlx5_fpga_conn {
                int sgid_index;
                struct mlx5_wq_qp wq;
                struct mlx5_wq_ctrl wq_ctrl;
-               struct mlx5_core_qp mqp;
+               u32 qpn;
                struct {
                        spinlock_t lock; /* Protects all SQ state */
                        unsigned int pc;
index b794888..b463787 100644 (file)
@@ -65,6 +65,7 @@ struct mlx5_fpga_esp_xfrm;
 struct mlx5_fpga_ipsec_sa_ctx {
        struct rhash_head               hash;
        struct mlx5_ifc_fpga_ipsec_sa   hw_sa;
+       u32                             sa_handle;
        struct mlx5_core_dev            *dev;
        struct mlx5_fpga_esp_xfrm       *fpga_xfrm;
 };
@@ -119,6 +120,8 @@ struct mlx5_fpga_ipsec {
         */
        struct rb_root rules_rb;
        struct mutex rules_rb_lock; /* rules lock */
+
+       struct ida halloc;
 };
 
 static bool mlx5_fpga_is_ipsec_device(struct mlx5_core_dev *mdev)
@@ -602,7 +605,7 @@ static bool mlx5_is_fpga_ipsec_rule(struct mlx5_core_dev *dev,
                                    const u32 *match_c,
                                    const u32 *match_v)
 {
-       u32 ipsec_dev_caps = mlx5_accel_ipsec_device_caps(dev);
+       u32 ipsec_dev_caps = mlx5_fpga_ipsec_device_caps(dev);
        bool ipv6_flow;
 
        ipv6_flow = mlx5_fs_is_outer_ipv6_flow(dev, match_c, match_v);
@@ -666,7 +669,8 @@ void *mlx5_fpga_ipsec_create_sa_ctx(struct mlx5_core_dev *mdev,
                                    struct mlx5_accel_esp_xfrm *accel_xfrm,
                                    const __be32 saddr[4],
                                    const __be32 daddr[4],
-                                   const __be32 spi, bool is_ipv6)
+                                   const __be32 spi, bool is_ipv6,
+                                   u32 *sa_handle)
 {
        struct mlx5_fpga_ipsec_sa_ctx *sa_ctx;
        struct mlx5_fpga_esp_xfrm *fpga_xfrm =
@@ -704,6 +708,17 @@ void *mlx5_fpga_ipsec_create_sa_ctx(struct mlx5_core_dev *mdev,
                goto exists;
        }
 
+       if (accel_xfrm->attrs.action == MLX5_ACCEL_ESP_ACTION_DECRYPT) {
+               err = ida_simple_get(&fipsec->halloc, 1, 0, GFP_KERNEL);
+               if (err < 0) {
+                       context = ERR_PTR(err);
+                       goto exists;
+               }
+
+               sa_ctx->sa_handle = err;
+               if (sa_handle)
+                       *sa_handle = sa_ctx->sa_handle;
+       }
        /* This is unbounded fpga_xfrm, try to add to hash */
        mutex_lock(&fipsec->sa_hash_lock);
 
@@ -744,7 +759,8 @@ delete_hash:
                                       rhash_sa));
 unlock_hash:
        mutex_unlock(&fipsec->sa_hash_lock);
-
+       if (accel_xfrm->attrs.action == MLX5_ACCEL_ESP_ACTION_DECRYPT)
+               ida_simple_remove(&fipsec->halloc, sa_ctx->sa_handle);
 exists:
        mutex_unlock(&fpga_xfrm->lock);
        kfree(sa_ctx);
@@ -816,7 +832,7 @@ mlx5_fpga_ipsec_fs_create_sa_ctx(struct mlx5_core_dev *mdev,
        /* create */
        return mlx5_fpga_ipsec_create_sa_ctx(mdev, accel_xfrm,
                                             saddr, daddr,
-                                            spi, is_ipv6);
+                                            spi, is_ipv6, NULL);
 }
 
 static void
@@ -836,6 +852,10 @@ mlx5_fpga_ipsec_release_sa_ctx(struct mlx5_fpga_ipsec_sa_ctx *sa_ctx)
                return;
        }
 
+       if (sa_ctx->fpga_xfrm->accel_xfrm.attrs.action &
+           MLX5_ACCEL_ESP_ACTION_DECRYPT)
+               ida_simple_remove(&fipsec->halloc, sa_ctx->sa_handle);
+
        mutex_lock(&fipsec->sa_hash_lock);
        WARN_ON(rhashtable_remove_fast(&fipsec->sa_hash, &sa_ctx->hash,
                                       rhash_sa));
@@ -1299,6 +1319,8 @@ int mlx5_fpga_ipsec_init(struct mlx5_core_dev *mdev)
                goto err_destroy_hash;
        }
 
+       ida_init(&fdev->ipsec->halloc);
+
        return 0;
 
 err_destroy_hash:
@@ -1331,6 +1353,7 @@ void mlx5_fpga_ipsec_cleanup(struct mlx5_core_dev *mdev)
        if (!mlx5_fpga_is_ipsec_device(mdev))
                return;
 
+       ida_destroy(&fdev->ipsec->halloc);
        destroy_rules_rb(&fdev->ipsec->rules_rb);
        rhashtable_destroy(&fdev->ipsec->sa_hash);
 
index 382985e..9ba637f 100644 (file)
@@ -37,6 +37,7 @@
 #include "accel/ipsec.h"
 #include "fs_cmd.h"
 
+#ifdef CONFIG_MLX5_FPGA_IPSEC
 u32 mlx5_fpga_ipsec_device_caps(struct mlx5_core_dev *mdev);
 unsigned int mlx5_fpga_ipsec_counters_count(struct mlx5_core_dev *mdev);
 int mlx5_fpga_ipsec_counters_read(struct mlx5_core_dev *mdev, u64 *counters,
@@ -46,7 +47,8 @@ void *mlx5_fpga_ipsec_create_sa_ctx(struct mlx5_core_dev *mdev,
                                    struct mlx5_accel_esp_xfrm *accel_xfrm,
                                    const __be32 saddr[4],
                                    const __be32 daddr[4],
-                                   const __be32 spi, bool is_ipv6);
+                                   const __be32 spi, bool is_ipv6,
+                                   u32 *sa_handle);
 void mlx5_fpga_ipsec_delete_sa_ctx(void *context);
 
 int mlx5_fpga_ipsec_init(struct mlx5_core_dev *mdev);
@@ -63,5 +65,17 @@ int mlx5_fpga_esp_modify_xfrm(struct mlx5_accel_esp_xfrm *xfrm,
 
 const struct mlx5_flow_cmds *
 mlx5_fs_cmd_get_default_ipsec_fpga_cmds(enum fs_flow_table_type type);
+#else
+static inline u32 mlx5_fpga_ipsec_device_caps(struct mlx5_core_dev *mdev)
+{
+       return 0;
+}
 
-#endif /* __MLX5_FPGA_SADB_H__ */
+static inline const struct mlx5_flow_cmds *
+mlx5_fs_cmd_get_default_ipsec_fpga_cmds(enum fs_flow_table_type type)
+{
+       return mlx5_fs_cmd_get_default(type);
+}
+
+#endif /* CONFIG_MLX5_FPGA_IPSEC */
+#endif /* __MLX5_FPGA_IPSEC_H__ */
index 9004869..1a8e826 100644 (file)
@@ -155,8 +155,7 @@ static int mlx5_cmd_update_root_ft(struct mlx5_flow_root_namespace *ns,
                                   struct mlx5_flow_table *ft, u32 underlay_qpn,
                                   bool disconnect)
 {
-       u32 in[MLX5_ST_SZ_DW(set_flow_table_root_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(set_flow_table_root_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(set_flow_table_root_in)] = {};
        struct mlx5_core_dev *dev = ns->dev;
 
        if ((MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_IB) &&
@@ -167,13 +166,10 @@ static int mlx5_cmd_update_root_ft(struct mlx5_flow_root_namespace *ns,
                 MLX5_CMD_OP_SET_FLOW_TABLE_ROOT);
        MLX5_SET(set_flow_table_root_in, in, table_type, ft->type);
 
-       if (disconnect) {
+       if (disconnect)
                MLX5_SET(set_flow_table_root_in, in, op_mod, 1);
-               MLX5_SET(set_flow_table_root_in, in, table_id, 0);
-       } else {
-               MLX5_SET(set_flow_table_root_in, in, op_mod, 0);
+       else
                MLX5_SET(set_flow_table_root_in, in, table_id, ft->id);
-       }
 
        MLX5_SET(set_flow_table_root_in, in, underlay_qpn, underlay_qpn);
        if (ft->vport) {
@@ -181,7 +177,7 @@ static int mlx5_cmd_update_root_ft(struct mlx5_flow_root_namespace *ns,
                MLX5_SET(set_flow_table_root_in, in, other_vport, 1);
        }
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, set_flow_table_root, in);
 }
 
 static int mlx5_cmd_create_flow_table(struct mlx5_flow_root_namespace *ns,
@@ -192,8 +188,8 @@ static int mlx5_cmd_create_flow_table(struct mlx5_flow_root_namespace *ns,
        int en_encap = !!(ft->flags & MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT);
        int en_decap = !!(ft->flags & MLX5_FLOW_TABLE_TUNNEL_EN_DECAP);
        int term = !!(ft->flags & MLX5_FLOW_TABLE_TERMINATION);
-       u32 out[MLX5_ST_SZ_DW(create_flow_table_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(create_flow_table_in)]   = {0};
+       u32 out[MLX5_ST_SZ_DW(create_flow_table_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(create_flow_table_in)] = {};
        struct mlx5_core_dev *dev = ns->dev;
        int err;
 
@@ -239,7 +235,7 @@ static int mlx5_cmd_create_flow_table(struct mlx5_flow_root_namespace *ns,
                break;
        }
 
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(dev, create_flow_table, in, out);
        if (!err)
                ft->id = MLX5_GET(create_flow_table_out, out,
                                  table_id);
@@ -249,8 +245,7 @@ static int mlx5_cmd_create_flow_table(struct mlx5_flow_root_namespace *ns,
 static int mlx5_cmd_destroy_flow_table(struct mlx5_flow_root_namespace *ns,
                                       struct mlx5_flow_table *ft)
 {
-       u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(destroy_flow_table_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)] = {};
        struct mlx5_core_dev *dev = ns->dev;
 
        MLX5_SET(destroy_flow_table_in, in, opcode,
@@ -262,15 +257,14 @@ static int mlx5_cmd_destroy_flow_table(struct mlx5_flow_root_namespace *ns,
                MLX5_SET(destroy_flow_table_in, in, other_vport, 1);
        }
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, destroy_flow_table, in);
 }
 
 static int mlx5_cmd_modify_flow_table(struct mlx5_flow_root_namespace *ns,
                                      struct mlx5_flow_table *ft,
                                      struct mlx5_flow_table *next_ft)
 {
-       u32 in[MLX5_ST_SZ_DW(modify_flow_table_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(modify_flow_table_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(modify_flow_table_in)] = {};
        struct mlx5_core_dev *dev = ns->dev;
 
        MLX5_SET(modify_flow_table_in, in, opcode,
@@ -310,7 +304,7 @@ static int mlx5_cmd_modify_flow_table(struct mlx5_flow_root_namespace *ns,
                }
        }
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, modify_flow_table, in);
 }
 
 static int mlx5_cmd_create_flow_group(struct mlx5_flow_root_namespace *ns,
@@ -318,8 +312,7 @@ static int mlx5_cmd_create_flow_group(struct mlx5_flow_root_namespace *ns,
                                      u32 *in,
                                      struct mlx5_flow_group *fg)
 {
-       u32 out[MLX5_ST_SZ_DW(create_flow_group_out)] = {0};
-       int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+       u32 out[MLX5_ST_SZ_DW(create_flow_group_out)] = {};
        struct mlx5_core_dev *dev = ns->dev;
        int err;
 
@@ -332,7 +325,7 @@ static int mlx5_cmd_create_flow_group(struct mlx5_flow_root_namespace *ns,
                MLX5_SET(create_flow_group_in, in, other_vport, 1);
        }
 
-       err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
+       err = mlx5_cmd_exec_inout(dev, create_flow_group, in, out);
        if (!err)
                fg->id = MLX5_GET(create_flow_group_out, out,
                                  group_id);
@@ -343,8 +336,7 @@ static int mlx5_cmd_destroy_flow_group(struct mlx5_flow_root_namespace *ns,
                                       struct mlx5_flow_table *ft,
                                       struct mlx5_flow_group *fg)
 {
-       u32 out[MLX5_ST_SZ_DW(destroy_flow_group_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)] = {};
        struct mlx5_core_dev *dev = ns->dev;
 
        MLX5_SET(destroy_flow_group_in, in, opcode,
@@ -357,7 +349,7 @@ static int mlx5_cmd_destroy_flow_group(struct mlx5_flow_root_namespace *ns,
                MLX5_SET(destroy_flow_group_in, in, other_vport, 1);
        }
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, destroy_flow_group, in);
 }
 
 static int mlx5_set_extended_dest(struct mlx5_core_dev *dev,
@@ -600,8 +592,7 @@ static int mlx5_cmd_delete_fte(struct mlx5_flow_root_namespace *ns,
                               struct mlx5_flow_table *ft,
                               struct fs_fte *fte)
 {
-       u32 out[MLX5_ST_SZ_DW(delete_fte_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(delete_fte_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(delete_fte_in)] = {};
        struct mlx5_core_dev *dev = ns->dev;
 
        MLX5_SET(delete_fte_in, in, opcode, MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY);
@@ -613,22 +604,22 @@ static int mlx5_cmd_delete_fte(struct mlx5_flow_root_namespace *ns,
                MLX5_SET(delete_fte_in, in, other_vport, 1);
        }
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, delete_fte, in);
 }
 
 int mlx5_cmd_fc_bulk_alloc(struct mlx5_core_dev *dev,
                           enum mlx5_fc_bulk_alloc_bitmask alloc_bitmask,
                           u32 *id)
 {
-       u32 in[MLX5_ST_SZ_DW(alloc_flow_counter_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(alloc_flow_counter_out)] = {0};
+       u32 out[MLX5_ST_SZ_DW(alloc_flow_counter_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(alloc_flow_counter_in)] = {};
        int err;
 
        MLX5_SET(alloc_flow_counter_in, in, opcode,
                 MLX5_CMD_OP_ALLOC_FLOW_COUNTER);
        MLX5_SET(alloc_flow_counter_in, in, flow_counter_bulk, alloc_bitmask);
 
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(dev, alloc_flow_counter, in, out);
        if (!err)
                *id = MLX5_GET(alloc_flow_counter_out, out, flow_counter_id);
        return err;
@@ -641,21 +632,20 @@ int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u32 *id)
 
 int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u32 id)
 {
-       u32 in[MLX5_ST_SZ_DW(dealloc_flow_counter_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(dealloc_flow_counter_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(dealloc_flow_counter_in)] = {};
 
        MLX5_SET(dealloc_flow_counter_in, in, opcode,
                 MLX5_CMD_OP_DEALLOC_FLOW_COUNTER);
        MLX5_SET(dealloc_flow_counter_in, in, flow_counter_id, id);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, dealloc_flow_counter, in);
 }
 
 int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u32 id,
                      u64 *packets, u64 *bytes)
 {
        u32 out[MLX5_ST_SZ_BYTES(query_flow_counter_out) +
-               MLX5_ST_SZ_BYTES(traffic_counter)]   = {0};
-       u32 in[MLX5_ST_SZ_DW(query_flow_counter_in)] = {0};
+               MLX5_ST_SZ_BYTES(traffic_counter)] = {};
+       u32 in[MLX5_ST_SZ_DW(query_flow_counter_in)] = {};
        void *stats;
        int err = 0;
 
@@ -683,11 +673,10 @@ int mlx5_cmd_fc_bulk_query(struct mlx5_core_dev *dev, u32 base_id, int bulk_len,
                           u32 *out)
 {
        int outlen = mlx5_cmd_fc_get_bulk_query_out_len(bulk_len);
-       u32 in[MLX5_ST_SZ_DW(query_flow_counter_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(query_flow_counter_in)] = {};
 
        MLX5_SET(query_flow_counter_in, in, opcode,
                 MLX5_CMD_OP_QUERY_FLOW_COUNTER);
-       MLX5_SET(query_flow_counter_in, in, op_mod, 0);
        MLX5_SET(query_flow_counter_in, in, flow_counter_id, base_id);
        MLX5_SET(query_flow_counter_in, in, num_of_counters, bulk_len);
        return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
@@ -700,7 +689,7 @@ static int mlx5_cmd_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns,
                                          enum mlx5_flow_namespace_type namespace,
                                          struct mlx5_pkt_reformat *pkt_reformat)
 {
-       u32 out[MLX5_ST_SZ_DW(alloc_packet_reformat_context_out)];
+       u32 out[MLX5_ST_SZ_DW(alloc_packet_reformat_context_out)] = {};
        struct mlx5_core_dev *dev = ns->dev;
        void *packet_reformat_context_in;
        int max_encap_size;
@@ -732,7 +721,6 @@ static int mlx5_cmd_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns,
                                reformat_data);
        inlen = reformat - (void *)in  + size;
 
-       memset(in, 0, inlen);
        MLX5_SET(alloc_packet_reformat_context_in, in, opcode,
                 MLX5_CMD_OP_ALLOC_PACKET_REFORMAT_CONTEXT);
        MLX5_SET(packet_reformat_context_in, packet_reformat_context_in,
@@ -741,7 +729,6 @@ static int mlx5_cmd_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns,
                 reformat_type, reformat_type);
        memcpy(reformat, reformat_data, size);
 
-       memset(out, 0, sizeof(out));
        err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
 
        pkt_reformat->id = MLX5_GET(alloc_packet_reformat_context_out,
@@ -753,17 +740,15 @@ static int mlx5_cmd_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns,
 static void mlx5_cmd_packet_reformat_dealloc(struct mlx5_flow_root_namespace *ns,
                                             struct mlx5_pkt_reformat *pkt_reformat)
 {
-       u32 in[MLX5_ST_SZ_DW(dealloc_packet_reformat_context_in)];
-       u32 out[MLX5_ST_SZ_DW(dealloc_packet_reformat_context_out)];
+       u32 in[MLX5_ST_SZ_DW(dealloc_packet_reformat_context_in)] = {};
        struct mlx5_core_dev *dev = ns->dev;
 
-       memset(in, 0, sizeof(in));
        MLX5_SET(dealloc_packet_reformat_context_in, in, opcode,
                 MLX5_CMD_OP_DEALLOC_PACKET_REFORMAT_CONTEXT);
        MLX5_SET(dealloc_packet_reformat_context_in, in, packet_reformat_id,
                 pkt_reformat->id);
 
-       mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(dev, dealloc_packet_reformat_context, in);
 }
 
 static int mlx5_cmd_modify_header_alloc(struct mlx5_flow_root_namespace *ns,
@@ -771,7 +756,7 @@ static int mlx5_cmd_modify_header_alloc(struct mlx5_flow_root_namespace *ns,
                                        void *modify_actions,
                                        struct mlx5_modify_hdr *modify_hdr)
 {
-       u32 out[MLX5_ST_SZ_DW(alloc_modify_header_context_out)];
+       u32 out[MLX5_ST_SZ_DW(alloc_modify_header_context_out)] = {};
        int max_actions, actions_size, inlen, err;
        struct mlx5_core_dev *dev = ns->dev;
        void *actions_in;
@@ -806,7 +791,7 @@ static int mlx5_cmd_modify_header_alloc(struct mlx5_flow_root_namespace *ns,
                return -EOPNOTSUPP;
        }
 
-       actions_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto) * num_actions;
+       actions_size = MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto) * num_actions;
        inlen = MLX5_ST_SZ_BYTES(alloc_modify_header_context_in) + actions_size;
 
        in = kzalloc(inlen, GFP_KERNEL);
@@ -821,7 +806,6 @@ static int mlx5_cmd_modify_header_alloc(struct mlx5_flow_root_namespace *ns,
        actions_in = MLX5_ADDR_OF(alloc_modify_header_context_in, in, actions);
        memcpy(actions_in, modify_actions, actions_size);
 
-       memset(out, 0, sizeof(out));
        err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
 
        modify_hdr->id = MLX5_GET(alloc_modify_header_context_out, out, modify_header_id);
@@ -832,17 +816,15 @@ static int mlx5_cmd_modify_header_alloc(struct mlx5_flow_root_namespace *ns,
 static void mlx5_cmd_modify_header_dealloc(struct mlx5_flow_root_namespace *ns,
                                           struct mlx5_modify_hdr *modify_hdr)
 {
-       u32 in[MLX5_ST_SZ_DW(dealloc_modify_header_context_in)];
-       u32 out[MLX5_ST_SZ_DW(dealloc_modify_header_context_out)];
+       u32 in[MLX5_ST_SZ_DW(dealloc_modify_header_context_in)] = {};
        struct mlx5_core_dev *dev = ns->dev;
 
-       memset(in, 0, sizeof(in));
        MLX5_SET(dealloc_modify_header_context_in, in, opcode,
                 MLX5_CMD_OP_DEALLOC_MODIFY_HEADER_CONTEXT);
        MLX5_SET(dealloc_modify_header_context_in, in, modify_header_id,
                 modify_hdr->id);
 
-       mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(dev, dealloc_modify_header_context, in);
 }
 
 static const struct mlx5_flow_cmds mlx5_flow_cmds = {
index d5defe0..2da45e9 100644 (file)
@@ -2359,7 +2359,7 @@ static struct mlx5_flow_root_namespace
        struct mlx5_flow_root_namespace *root_ns;
        struct mlx5_flow_namespace *ns;
 
-       if (mlx5_accel_ipsec_device_caps(steering->dev) & MLX5_ACCEL_IPSEC_CAP_DEVICE &&
+       if (mlx5_fpga_ipsec_device_caps(steering->dev) & MLX5_ACCEL_IPSEC_CAP_DEVICE &&
            (table_type == FS_FT_NIC_RX || table_type == FS_FT_NIC_TX))
                cmds = mlx5_fs_cmd_get_default_ipsec_fpga_cmds(table_type);
 
@@ -2943,7 +2943,8 @@ int mlx5_init_fs(struct mlx5_core_dev *dev)
                        goto err;
        }
 
-       if (MLX5_IPSEC_DEV(dev) || MLX5_CAP_FLOWTABLE_NIC_TX(dev, ft_support)) {
+       if (mlx5_fpga_ipsec_device_caps(steering->dev) & MLX5_ACCEL_IPSEC_CAP_DEVICE ||
+           MLX5_CAP_FLOWTABLE_NIC_TX(dev, ft_support)) {
                err = init_egress_root_ns(steering);
                if (err)
                        goto err;
index 90e3d02..a5fbe73 100644 (file)
@@ -31,7 +31,6 @@
  */
 
 #include <linux/mlx5/driver.h>
-#include <linux/mlx5/cmd.h>
 #include <linux/mlx5/eswitch.h>
 #include <linux/module.h>
 #include "mlx5_core.h"
@@ -68,26 +67,19 @@ enum {
        MCQI_FW_STORED_VERSION  = 1,
 };
 
-static int mlx5_cmd_query_adapter(struct mlx5_core_dev *dev, u32 *out,
-                                 int outlen)
-{
-       u32 in[MLX5_ST_SZ_DW(query_adapter_in)] = {0};
-
-       MLX5_SET(query_adapter_in, in, opcode, MLX5_CMD_OP_QUERY_ADAPTER);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
-}
-
 int mlx5_query_board_id(struct mlx5_core_dev *dev)
 {
        u32 *out;
        int outlen = MLX5_ST_SZ_BYTES(query_adapter_out);
+       u32 in[MLX5_ST_SZ_DW(query_adapter_in)] = {};
        int err;
 
        out = kzalloc(outlen, GFP_KERNEL);
        if (!out)
                return -ENOMEM;
 
-       err = mlx5_cmd_query_adapter(dev, out, outlen);
+       MLX5_SET(query_adapter_in, in, opcode, MLX5_CMD_OP_QUERY_ADAPTER);
+       err = mlx5_cmd_exec_inout(dev, query_adapter, in, out);
        if (err)
                goto out;
 
@@ -106,13 +98,15 @@ int mlx5_core_query_vendor_id(struct mlx5_core_dev *mdev, u32 *vendor_id)
 {
        u32 *out;
        int outlen = MLX5_ST_SZ_BYTES(query_adapter_out);
+       u32 in[MLX5_ST_SZ_DW(query_adapter_in)] = {};
        int err;
 
        out = kzalloc(outlen, GFP_KERNEL);
        if (!out)
                return -ENOMEM;
 
-       err = mlx5_cmd_query_adapter(mdev, out, outlen);
+       MLX5_SET(query_adapter_in, in, opcode, MLX5_CMD_OP_QUERY_ADAPTER);
+       err = mlx5_cmd_exec_inout(mdev, query_adapter, in, out);
        if (err)
                goto out;
 
@@ -260,8 +254,7 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev)
 
 int mlx5_cmd_init_hca(struct mlx5_core_dev *dev, uint32_t *sw_owner_id)
 {
-       u32 out[MLX5_ST_SZ_DW(init_hca_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(init_hca_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(init_hca_in)] = {};
        int i;
 
        MLX5_SET(init_hca_in, in, opcode, MLX5_CMD_OP_INIT_HCA);
@@ -272,16 +265,15 @@ int mlx5_cmd_init_hca(struct mlx5_core_dev *dev, uint32_t *sw_owner_id)
                                       sw_owner_id[i]);
        }
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, init_hca, in);
 }
 
 int mlx5_cmd_teardown_hca(struct mlx5_core_dev *dev)
 {
-       u32 out[MLX5_ST_SZ_DW(teardown_hca_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(teardown_hca_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(teardown_hca_in)] = {};
 
        MLX5_SET(teardown_hca_in, in, opcode, MLX5_CMD_OP_TEARDOWN_HCA);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, teardown_hca, in);
 }
 
 int mlx5_cmd_force_teardown_hca(struct mlx5_core_dev *dev)
@@ -316,8 +308,8 @@ int mlx5_cmd_force_teardown_hca(struct mlx5_core_dev *dev)
 int mlx5_cmd_fast_teardown_hca(struct mlx5_core_dev *dev)
 {
        unsigned long end, delay_ms = MLX5_FAST_TEARDOWN_WAIT_MS;
-       u32 out[MLX5_ST_SZ_DW(teardown_hca_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(teardown_hca_in)] = {0};
+       u32 out[MLX5_ST_SZ_DW(teardown_hca_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(teardown_hca_in)] = {};
        int state;
        int ret;
 
@@ -330,7 +322,7 @@ int mlx5_cmd_fast_teardown_hca(struct mlx5_core_dev *dev)
        MLX5_SET(teardown_hca_in, in, profile,
                 MLX5_TEARDOWN_HCA_IN_PROFILE_PREPARE_FAST_TEARDOWN);
 
-       ret = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       ret = mlx5_cmd_exec_inout(dev, teardown_hca, in, out);
        if (ret)
                return ret;
 
index fa1665c..c0cfbab 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/vmalloc.h>
 #include <linux/hardirq.h>
 #include <linux/mlx5/driver.h>
-#include <linux/mlx5/cmd.h>
 #include "mlx5_core.h"
 #include "lib/eq.h"
 #include "lib/mlx5.h"
@@ -243,7 +242,7 @@ recover_from_sw_reset:
                if (mlx5_get_nic_state(dev) == MLX5_NIC_IFC_DISABLED)
                        break;
 
-               cond_resched();
+               msleep(20);
        } while (!time_after(jiffies, end));
 
        if (mlx5_get_nic_state(dev) != MLX5_NIC_IFC_DISABLED) {
index 673aaa8..068578b 100644 (file)
@@ -160,45 +160,54 @@ int mlx5i_init_underlay_qp(struct mlx5e_priv *priv)
 {
        struct mlx5_core_dev *mdev = priv->mdev;
        struct mlx5i_priv *ipriv = priv->ppriv;
-       struct mlx5_core_qp *qp = &ipriv->qp;
-       struct mlx5_qp_context *context;
        int ret;
 
-       /* QP states */
-       context = kzalloc(sizeof(*context), GFP_KERNEL);
-       if (!context)
-               return -ENOMEM;
+       {
+               u32 in[MLX5_ST_SZ_DW(rst2init_qp_in)] = {};
+               u32 *qpc;
 
-       context->flags = cpu_to_be32(MLX5_QP_PM_MIGRATED << 11);
-       context->pri_path.port = 1;
-       context->pri_path.pkey_index = cpu_to_be16(ipriv->pkey_index);
-       context->qkey = cpu_to_be32(IB_DEFAULT_Q_KEY);
+               qpc = MLX5_ADDR_OF(rst2init_qp_in, in, qpc);
 
-       ret = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_RST2INIT_QP, 0, context, qp);
-       if (ret) {
-               mlx5_core_err(mdev, "Failed to modify qp RST2INIT, err: %d\n", ret);
-               goto err_qp_modify_to_err;
-       }
-       memset(context, 0, sizeof(*context));
+               MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED);
+               MLX5_SET(qpc, qpc, primary_address_path.pkey_index,
+                        ipriv->pkey_index);
+               MLX5_SET(qpc, qpc, primary_address_path.vhca_port_num, 1);
+               MLX5_SET(qpc, qpc, q_key, IB_DEFAULT_Q_KEY);
 
-       ret = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_INIT2RTR_QP, 0, context, qp);
-       if (ret) {
-               mlx5_core_err(mdev, "Failed to modify qp INIT2RTR, err: %d\n", ret);
-               goto err_qp_modify_to_err;
+               MLX5_SET(rst2init_qp_in, in, opcode, MLX5_CMD_OP_RST2INIT_QP);
+               MLX5_SET(rst2init_qp_in, in, qpn, ipriv->qpn);
+               ret = mlx5_cmd_exec_in(mdev, rst2init_qp, in);
+               if (ret)
+                       goto err_qp_modify_to_err;
        }
-
-       ret = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_RTR2RTS_QP, 0, context, qp);
-       if (ret) {
-               mlx5_core_err(mdev, "Failed to modify qp RTR2RTS, err: %d\n", ret);
-               goto err_qp_modify_to_err;
+       {
+               u32 in[MLX5_ST_SZ_DW(init2rtr_qp_in)] = {};
+
+               MLX5_SET(init2rtr_qp_in, in, opcode, MLX5_CMD_OP_INIT2RTR_QP);
+               MLX5_SET(init2rtr_qp_in, in, qpn, ipriv->qpn);
+               ret = mlx5_cmd_exec_in(mdev, init2rtr_qp, in);
+               if (ret)
+                       goto err_qp_modify_to_err;
+       }
+       {
+               u32 in[MLX5_ST_SZ_DW(rtr2rts_qp_in)] = {};
+
+               MLX5_SET(rtr2rts_qp_in, in, opcode, MLX5_CMD_OP_RTR2RTS_QP);
+               MLX5_SET(rtr2rts_qp_in, in, qpn, ipriv->qpn);
+               ret = mlx5_cmd_exec_in(mdev, rtr2rts_qp, in);
+               if (ret)
+                       goto err_qp_modify_to_err;
        }
-
-       kfree(context);
        return 0;
 
 err_qp_modify_to_err:
-       mlx5_core_qp_modify(mdev, MLX5_CMD_OP_2ERR_QP, 0, &context, qp);
-       kfree(context);
+       {
+               u32 in[MLX5_ST_SZ_DW(qp_2err_in)] = {};
+
+               MLX5_SET(qp_2err_in, in, opcode, MLX5_CMD_OP_2ERR_QP);
+               MLX5_SET(qp_2err_in, in, qpn, ipriv->qpn);
+               mlx5_cmd_exec_in(mdev, qp_2err, in);
+       }
        return ret;
 }
 
@@ -206,30 +215,24 @@ void mlx5i_uninit_underlay_qp(struct mlx5e_priv *priv)
 {
        struct mlx5i_priv *ipriv = priv->ppriv;
        struct mlx5_core_dev *mdev = priv->mdev;
-       struct mlx5_qp_context context;
-       int err;
+       u32 in[MLX5_ST_SZ_DW(qp_2rst_in)] = {};
 
-       err = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_2RST_QP, 0, &context,
-                                 &ipriv->qp);
-       if (err)
-               mlx5_core_err(mdev, "Failed to modify qp 2RST, err: %d\n", err);
+       MLX5_SET(qp_2rst_in, in, opcode, MLX5_CMD_OP_2RST_QP);
+       MLX5_SET(qp_2rst_in, in, qpn, ipriv->qpn);
+       mlx5_cmd_exec_in(mdev, qp_2rst, in);
 }
 
 #define MLX5_QP_ENHANCED_ULP_STATELESS_MODE 2
 
-int mlx5i_create_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp *qp)
+int mlx5i_create_underlay_qp(struct mlx5e_priv *priv)
 {
-       u32 *in = NULL;
+       u32 out[MLX5_ST_SZ_DW(create_qp_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(create_qp_in)] = {};
+       struct mlx5i_priv *ipriv = priv->ppriv;
        void *addr_path;
        int ret = 0;
-       int inlen;
        void *qpc;
 
-       inlen = MLX5_ST_SZ_BYTES(create_qp_in);
-       in = kvzalloc(inlen, GFP_KERNEL);
-       if (!in)
-               return -ENOMEM;
-
        qpc = MLX5_ADDR_OF(create_qp_in, in, qpc);
        MLX5_SET(qpc, qpc, st, MLX5_QP_ST_UD);
        MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED);
@@ -240,20 +243,23 @@ int mlx5i_create_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp *qp
        MLX5_SET(ads, addr_path, vhca_port_num, 1);
        MLX5_SET(ads, addr_path, grh, 1);
 
-       ret = mlx5_core_create_qp(mdev, qp, in, inlen);
-       if (ret) {
-               mlx5_core_err(mdev, "Failed creating IPoIB QP err : %d\n", ret);
-               goto out;
-       }
+       MLX5_SET(create_qp_in, in, opcode, MLX5_CMD_OP_CREATE_QP);
+       ret = mlx5_cmd_exec_inout(priv->mdev, create_qp, in, out);
+       if (ret)
+               return ret;
 
-out:
-       kvfree(in);
-       return ret;
+       ipriv->qpn = MLX5_GET(create_qp_out, out, qpn);
+
+       return 0;
 }
 
-void mlx5i_destroy_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp *qp)
+void mlx5i_destroy_underlay_qp(struct mlx5_core_dev *mdev, u32 qpn)
 {
-       mlx5_core_destroy_qp(mdev, qp);
+       u32 in[MLX5_ST_SZ_DW(destroy_qp_in)] = {};
+
+       MLX5_SET(destroy_qp_in, in, opcode, MLX5_CMD_OP_DESTROY_QP);
+       MLX5_SET(destroy_qp_in, in, qpn, qpn);
+       mlx5_cmd_exec_in(mdev, destroy_qp, in);
 }
 
 int mlx5i_create_tis(struct mlx5_core_dev *mdev, u32 underlay_qpn, u32 *tisn)
@@ -273,13 +279,13 @@ static int mlx5i_init_tx(struct mlx5e_priv *priv)
        struct mlx5i_priv *ipriv = priv->ppriv;
        int err;
 
-       err = mlx5i_create_underlay_qp(priv->mdev, &ipriv->qp);
+       err = mlx5i_create_underlay_qp(priv);
        if (err) {
                mlx5_core_warn(priv->mdev, "create underlay QP failed, %d\n", err);
                return err;
        }
 
-       err = mlx5i_create_tis(priv->mdev, ipriv->qp.qpn, &priv->tisn[0][0]);
+       err = mlx5i_create_tis(priv->mdev, ipriv->qpn, &priv->tisn[0][0]);
        if (err) {
                mlx5_core_warn(priv->mdev, "create tis failed, %d\n", err);
                goto err_destroy_underlay_qp;
@@ -288,7 +294,7 @@ static int mlx5i_init_tx(struct mlx5e_priv *priv)
        return 0;
 
 err_destroy_underlay_qp:
-       mlx5i_destroy_underlay_qp(priv->mdev, &ipriv->qp);
+       mlx5i_destroy_underlay_qp(priv->mdev, ipriv->qpn);
        return err;
 }
 
@@ -297,7 +303,7 @@ static void mlx5i_cleanup_tx(struct mlx5e_priv *priv)
        struct mlx5i_priv *ipriv = priv->ppriv;
 
        mlx5e_destroy_tis(priv->mdev, priv->tisn[0][0]);
-       mlx5i_destroy_underlay_qp(priv->mdev, &ipriv->qp);
+       mlx5i_destroy_underlay_qp(priv->mdev, ipriv->qpn);
 }
 
 static int mlx5i_create_flow_steering(struct mlx5e_priv *priv)
@@ -500,12 +506,12 @@ int mlx5i_dev_init(struct net_device *dev)
        struct mlx5i_priv    *ipriv  = priv->ppriv;
 
        /* Set dev address using underlay QP */
-       dev->dev_addr[1] = (ipriv->qp.qpn >> 16) & 0xff;
-       dev->dev_addr[2] = (ipriv->qp.qpn >>  8) & 0xff;
-       dev->dev_addr[3] = (ipriv->qp.qpn) & 0xff;
+       dev->dev_addr[1] = (ipriv->qpn >> 16) & 0xff;
+       dev->dev_addr[2] = (ipriv->qpn >>  8) & 0xff;
+       dev->dev_addr[3] = (ipriv->qpn) & 0xff;
 
        /* Add QPN to net-device mapping to HT */
-       mlx5i_pkey_add_qpn(dev ,ipriv->qp.qpn);
+       mlx5i_pkey_add_qpn(dev, ipriv->qpn);
 
        return 0;
 }
@@ -532,7 +538,7 @@ void mlx5i_dev_cleanup(struct net_device *dev)
        mlx5i_uninit_underlay_qp(priv);
 
        /* Delete QPN to net-device mapping from HT */
-       mlx5i_pkey_del_qpn(dev, ipriv->qp.qpn);
+       mlx5i_pkey_del_qpn(dev, ipriv->qpn);
 }
 
 static int mlx5i_open(struct net_device *netdev)
@@ -552,7 +558,7 @@ static int mlx5i_open(struct net_device *netdev)
                goto err_clear_state_opened_flag;
        }
 
-       err = mlx5_fs_add_rx_underlay_qpn(mdev, ipriv->qp.qpn);
+       err = mlx5_fs_add_rx_underlay_qpn(mdev, ipriv->qpn);
        if (err) {
                mlx5_core_warn(mdev, "attach underlay qp to ft failed, %d\n", err);
                goto err_reset_qp;
@@ -569,7 +575,7 @@ static int mlx5i_open(struct net_device *netdev)
        return 0;
 
 err_remove_fs_underlay_qp:
-       mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qp.qpn);
+       mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qpn);
 err_reset_qp:
        mlx5i_uninit_underlay_qp(epriv);
 err_clear_state_opened_flag:
@@ -595,7 +601,7 @@ static int mlx5i_close(struct net_device *netdev)
        clear_bit(MLX5E_STATE_OPENED, &epriv->state);
 
        netif_carrier_off(epriv->netdev);
-       mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qp.qpn);
+       mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qpn);
        mlx5e_deactivate_priv_channels(epriv);
        mlx5e_close_channels(&epriv->channels);
        mlx5i_uninit_underlay_qp(epriv);
@@ -614,11 +620,12 @@ static int mlx5i_attach_mcast(struct net_device *netdev, struct ib_device *hca,
        struct mlx5i_priv    *ipriv = epriv->ppriv;
        int err;
 
-       mlx5_core_dbg(mdev, "attaching QPN 0x%x, MGID %pI6\n", ipriv->qp.qpn, gid->raw);
-       err = mlx5_core_attach_mcg(mdev, gid, ipriv->qp.qpn);
+       mlx5_core_dbg(mdev, "attaching QPN 0x%x, MGID %pI6\n", ipriv->qpn,
+                     gid->raw);
+       err = mlx5_core_attach_mcg(mdev, gid, ipriv->qpn);
        if (err)
                mlx5_core_warn(mdev, "failed attaching QPN 0x%x, MGID %pI6\n",
-                              ipriv->qp.qpn, gid->raw);
+                              ipriv->qpn, gid->raw);
 
        if (set_qkey) {
                mlx5_core_dbg(mdev, "%s setting qkey 0x%x\n",
@@ -637,12 +644,13 @@ static int mlx5i_detach_mcast(struct net_device *netdev, struct ib_device *hca,
        struct mlx5i_priv    *ipriv = epriv->ppriv;
        int err;
 
-       mlx5_core_dbg(mdev, "detaching QPN 0x%x, MGID %pI6\n", ipriv->qp.qpn, gid->raw);
+       mlx5_core_dbg(mdev, "detaching QPN 0x%x, MGID %pI6\n", ipriv->qpn,
+                     gid->raw);
 
-       err = mlx5_core_detach_mcg(mdev, gid, ipriv->qp.qpn);
+       err = mlx5_core_detach_mcg(mdev, gid, ipriv->qpn);
        if (err)
                mlx5_core_dbg(mdev, "failed detaching QPN 0x%x, MGID %pI6\n",
-                             ipriv->qp.qpn, gid->raw);
+                             ipriv->qpn, gid->raw);
 
        return err;
 }
index de7e01a..7844ab5 100644 (file)
@@ -51,7 +51,7 @@ extern const struct ethtool_ops mlx5i_pkey_ethtool_ops;
 /* ipoib rdma netdev's private data structure */
 struct mlx5i_priv {
        struct rdma_netdev rn; /* keep this first */
-       struct mlx5_core_qp qp;
+       u32 qpn;
        bool   sub_interface;
        u32    qkey;
        u16    pkey_index;
@@ -62,8 +62,8 @@ struct mlx5i_priv {
 int mlx5i_create_tis(struct mlx5_core_dev *mdev, u32 underlay_qpn, u32 *tisn);
 
 /* Underlay QP create/destroy functions */
-int mlx5i_create_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp *qp);
-void mlx5i_destroy_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp *qp);
+int mlx5i_create_underlay_qp(struct mlx5e_priv *priv);
+void mlx5i_destroy_underlay_qp(struct mlx5_core_dev *mdev, u32 qpn);
 
 /* Underlay QP state modification init/uninit functions */
 int mlx5i_init_underlay_qp(struct mlx5e_priv *priv);
@@ -110,15 +110,8 @@ struct mlx5i_tx_wqe {
        struct mlx5_wqe_data_seg     data[];
 };
 
-static inline void mlx5i_sq_fetch_wqe(struct mlx5e_txqsq *sq,
-                                     struct mlx5i_tx_wqe **wqe,
-                                     u16 pi)
-{
-       struct mlx5_wq_cyc *wq = &sq->wq;
-
-       *wqe = mlx5_wq_cyc_get_wqe(wq, pi);
-       memset(*wqe, 0, sizeof(**wqe));
-}
+#define MLX5I_SQ_FETCH_WQE(sq, pi) \
+       ((struct mlx5i_tx_wqe *)mlx5e_fetch_wqe(&(sq)->wq, pi, sizeof(struct mlx5i_tx_wqe)))
 
 netdev_tx_t mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb,
                          struct mlx5_av *av, u32 dqpn, u32 dqkey,
index 96e6418..b9af37a 100644 (file)
@@ -204,13 +204,13 @@ static int mlx5i_pkey_open(struct net_device *netdev)
                goto err_release_lock;
        }
 
-       err = mlx5_fs_add_rx_underlay_qpn(mdev, ipriv->qp.qpn);
+       err = mlx5_fs_add_rx_underlay_qpn(mdev, ipriv->qpn);
        if (err) {
                mlx5_core_warn(mdev, "attach child underlay qp to ft failed, %d\n", err);
                goto err_unint_underlay_qp;
        }
 
-       err = mlx5i_create_tis(mdev, ipriv->qp.qpn, &epriv->tisn[0][0]);
+       err = mlx5i_create_tis(mdev, ipriv->qpn, &epriv->tisn[0][0]);
        if (err) {
                mlx5_core_warn(mdev, "create child tis failed, %d\n", err);
                goto err_remove_rx_uderlay_qp;
@@ -230,7 +230,7 @@ static int mlx5i_pkey_open(struct net_device *netdev)
 err_clear_state_opened_flag:
        mlx5e_destroy_tis(mdev, epriv->tisn[0][0]);
 err_remove_rx_uderlay_qp:
-       mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qp.qpn);
+       mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qpn);
 err_unint_underlay_qp:
        mlx5i_uninit_underlay_qp(epriv);
 err_release_lock:
@@ -253,7 +253,7 @@ static int mlx5i_pkey_close(struct net_device *netdev)
        clear_bit(MLX5E_STATE_OPENED, &priv->state);
 
        netif_carrier_off(priv->netdev);
-       mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qp.qpn);
+       mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qpn);
        mlx5i_uninit_underlay_qp(priv);
        mlx5e_deactivate_priv_channels(priv);
        mlx5e_close_channels(&priv->channels);
@@ -307,23 +307,20 @@ static void mlx5i_pkey_cleanup(struct mlx5e_priv *priv)
 
 static int mlx5i_pkey_init_tx(struct mlx5e_priv *priv)
 {
-       struct mlx5i_priv *ipriv = priv->ppriv;
        int err;
 
-       err = mlx5i_create_underlay_qp(priv->mdev, &ipriv->qp);
-       if (err) {
+       err = mlx5i_create_underlay_qp(priv);
+       if (err)
                mlx5_core_warn(priv->mdev, "create child underlay QP failed, %d\n", err);
-               return err;
-       }
 
-       return 0;
+       return err;
 }
 
 static void mlx5i_pkey_cleanup_tx(struct mlx5e_priv *priv)
 {
        struct mlx5i_priv *ipriv = priv->ppriv;
 
-       mlx5i_destroy_underlay_qp(priv->mdev, &ipriv->qp);
+       mlx5i_destroy_underlay_qp(priv->mdev, ipriv->qpn);
 }
 
 static int mlx5i_pkey_init_rx(struct mlx5e_priv *priv)
index 93052b0..c6ad5ca 100644 (file)
@@ -47,8 +47,7 @@ static DEFINE_MUTEX(lag_mutex);
 static int mlx5_cmd_create_lag(struct mlx5_core_dev *dev, u8 remap_port1,
                               u8 remap_port2)
 {
-       u32   in[MLX5_ST_SZ_DW(create_lag_in)]   = {0};
-       u32   out[MLX5_ST_SZ_DW(create_lag_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(create_lag_in)] = {};
        void *lag_ctx = MLX5_ADDR_OF(create_lag_in, in, ctx);
 
        MLX5_SET(create_lag_in, in, opcode, MLX5_CMD_OP_CREATE_LAG);
@@ -56,14 +55,13 @@ static int mlx5_cmd_create_lag(struct mlx5_core_dev *dev, u8 remap_port1,
        MLX5_SET(lagc, lag_ctx, tx_remap_affinity_1, remap_port1);
        MLX5_SET(lagc, lag_ctx, tx_remap_affinity_2, remap_port2);
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, create_lag, in);
 }
 
 static int mlx5_cmd_modify_lag(struct mlx5_core_dev *dev, u8 remap_port1,
                               u8 remap_port2)
 {
-       u32   in[MLX5_ST_SZ_DW(modify_lag_in)]   = {0};
-       u32   out[MLX5_ST_SZ_DW(modify_lag_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(modify_lag_in)] = {};
        void *lag_ctx = MLX5_ADDR_OF(modify_lag_in, in, ctx);
 
        MLX5_SET(modify_lag_in, in, opcode, MLX5_CMD_OP_MODIFY_LAG);
@@ -72,52 +70,29 @@ static int mlx5_cmd_modify_lag(struct mlx5_core_dev *dev, u8 remap_port1,
        MLX5_SET(lagc, lag_ctx, tx_remap_affinity_1, remap_port1);
        MLX5_SET(lagc, lag_ctx, tx_remap_affinity_2, remap_port2);
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-
-static int mlx5_cmd_destroy_lag(struct mlx5_core_dev *dev)
-{
-       u32  in[MLX5_ST_SZ_DW(destroy_lag_in)]  = {0};
-       u32 out[MLX5_ST_SZ_DW(destroy_lag_out)] = {0};
-
-       MLX5_SET(destroy_lag_in, in, opcode, MLX5_CMD_OP_DESTROY_LAG);
-
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, modify_lag, in);
 }
 
 int mlx5_cmd_create_vport_lag(struct mlx5_core_dev *dev)
 {
-       u32  in[MLX5_ST_SZ_DW(create_vport_lag_in)]  = {0};
-       u32 out[MLX5_ST_SZ_DW(create_vport_lag_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(create_vport_lag_in)] = {};
 
        MLX5_SET(create_vport_lag_in, in, opcode, MLX5_CMD_OP_CREATE_VPORT_LAG);
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, create_vport_lag, in);
 }
 EXPORT_SYMBOL(mlx5_cmd_create_vport_lag);
 
 int mlx5_cmd_destroy_vport_lag(struct mlx5_core_dev *dev)
 {
-       u32  in[MLX5_ST_SZ_DW(destroy_vport_lag_in)]  = {0};
-       u32 out[MLX5_ST_SZ_DW(destroy_vport_lag_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_vport_lag_in)] = {};
 
        MLX5_SET(destroy_vport_lag_in, in, opcode, MLX5_CMD_OP_DESTROY_VPORT_LAG);
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, destroy_vport_lag, in);
 }
 EXPORT_SYMBOL(mlx5_cmd_destroy_vport_lag);
 
-static int mlx5_cmd_query_cong_counter(struct mlx5_core_dev *dev,
-                                      bool reset, void *out, int out_size)
-{
-       u32 in[MLX5_ST_SZ_DW(query_cong_statistics_in)] = { };
-
-       MLX5_SET(query_cong_statistics_in, in, opcode,
-                MLX5_CMD_OP_QUERY_CONG_STATISTICS);
-       MLX5_SET(query_cong_statistics_in, in, clear, reset);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, out_size);
-}
-
 int mlx5_lag_dev_get_netdev_idx(struct mlx5_lag *ldev,
                                struct net_device *ndev)
 {
@@ -232,12 +207,14 @@ int mlx5_activate_lag(struct mlx5_lag *ldev,
 static int mlx5_deactivate_lag(struct mlx5_lag *ldev)
 {
        struct mlx5_core_dev *dev0 = ldev->pf[MLX5_LAG_P1].dev;
+       u32 in[MLX5_ST_SZ_DW(destroy_lag_in)] = {};
        bool roce_lag = __mlx5_lag_is_roce(ldev);
        int err;
 
        ldev->flags &= ~MLX5_LAG_MODE_FLAGS;
 
-       err = mlx5_cmd_destroy_lag(dev0);
+       MLX5_SET(destroy_lag_in, in, opcode, MLX5_CMD_OP_DESTROY_LAG);
+       err = mlx5_cmd_exec_in(dev0, destroy_lag, in);
        if (err) {
                if (roce_lag) {
                        mlx5_core_err(dev0,
@@ -758,7 +735,12 @@ int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,
        }
 
        for (i = 0; i < num_ports; ++i) {
-               ret = mlx5_cmd_query_cong_counter(mdev[i], false, out, outlen);
+               u32 in[MLX5_ST_SZ_DW(query_cong_statistics_in)] = {};
+
+               MLX5_SET(query_cong_statistics_in, in, opcode,
+                        MLX5_CMD_OP_QUERY_CONG_STATISTICS);
+               ret = mlx5_cmd_exec_inout(mdev[i], query_cong_statistics, in,
+                                         out);
                if (ret)
                        goto unlock;
 
index 6cbccba..3d5e57f 100644 (file)
@@ -90,7 +90,8 @@ void mlx5_dm_cleanup(struct mlx5_core_dev *dev)
 }
 
 int mlx5_dm_sw_icm_alloc(struct mlx5_core_dev *dev, enum mlx5_sw_icm_type type,
-                        u64 length, u16 uid, phys_addr_t *addr, u32 *obj_id)
+                        u64 length, u32 log_alignment, u16 uid,
+                        phys_addr_t *addr, u32 *obj_id)
 {
        u32 num_blocks = DIV_ROUND_UP_ULL(length, MLX5_SW_ICM_BLOCK_SIZE(dev));
        u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {};
@@ -99,6 +100,7 @@ int mlx5_dm_sw_icm_alloc(struct mlx5_core_dev *dev, enum mlx5_sw_icm_type type,
        unsigned long *block_map;
        u64 icm_start_addr;
        u32 log_icm_size;
+       u64 align_mask;
        u32 max_blocks;
        u64 block_idx;
        void *sw_icm;
@@ -136,11 +138,14 @@ int mlx5_dm_sw_icm_alloc(struct mlx5_core_dev *dev, enum mlx5_sw_icm_type type,
                return -EOPNOTSUPP;
 
        max_blocks = BIT(log_icm_size - MLX5_LOG_SW_ICM_BLOCK_SIZE(dev));
+
+       if (log_alignment < MLX5_LOG_SW_ICM_BLOCK_SIZE(dev))
+               log_alignment = MLX5_LOG_SW_ICM_BLOCK_SIZE(dev);
+       align_mask = BIT(log_alignment - MLX5_LOG_SW_ICM_BLOCK_SIZE(dev)) - 1;
+
        spin_lock(&dm->lock);
-       block_idx = bitmap_find_next_zero_area(block_map,
-                                              max_blocks,
-                                              0,
-                                              num_blocks, 0);
+       block_idx = bitmap_find_next_zero_area(block_map, max_blocks, 0,
+                                              num_blocks, align_mask);
 
        if (block_idx < max_blocks)
                bitmap_set(block_map,
index 4be4d2d..4aaca74 100644 (file)
@@ -27,7 +27,6 @@ struct mlx5_eq {
        __be32 __iomem          *doorbell;
        u32                     cons_index;
        struct mlx5_frag_buf    buf;
-       int                     size;
        unsigned int            vecidx;
        unsigned int            irqn;
        u8                      eqn;
index 7722a3f..a68738c 100644 (file)
@@ -124,8 +124,7 @@ int mlx5_core_roce_gid_set(struct mlx5_core_dev *dev, unsigned int index,
                           const u8 *mac, bool vlan, u16 vlan_id, u8 port_num)
 {
 #define MLX5_SET_RA(p, f, v) MLX5_SET(roce_addr_layout, p, f, v)
-       u32  in[MLX5_ST_SZ_DW(set_roce_address_in)] = {0};
-       u32 out[MLX5_ST_SZ_DW(set_roce_address_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(set_roce_address_in)] = {};
        void *in_addr = MLX5_ADDR_OF(set_roce_address_in, in, roce_address);
        char *addr_l3_addr = MLX5_ADDR_OF(roce_addr_layout, in_addr,
                                          source_l3_address);
@@ -153,6 +152,6 @@ int mlx5_core_roce_gid_set(struct mlx5_core_dev *dev, unsigned int index,
 
        MLX5_SET(set_roce_address_in, in, roce_address_index, index);
        MLX5_SET(set_roce_address_in, in, opcode, MLX5_CMD_OP_SET_ROCE_ADDRESS);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, set_roce_address, in);
 }
 EXPORT_SYMBOL(mlx5_core_roce_gid_set);
index 3118e8d..fd8449f 100644 (file)
@@ -40,8 +40,7 @@
 /* HW L2 Table (MPFS) management */
 static int set_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index, u8 *mac)
 {
-       u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(set_l2_table_entry_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)] = {};
        u8 *in_mac_addr;
 
        MLX5_SET(set_l2_table_entry_in, in, opcode, MLX5_CMD_OP_SET_L2_TABLE_ENTRY);
@@ -50,17 +49,16 @@ static int set_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index, u8 *mac)
        in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address);
        ether_addr_copy(&in_mac_addr[2], mac);
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, set_l2_table_entry, in);
 }
 
 static int del_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index)
 {
-       u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(delete_l2_table_entry_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)] = {};
 
        MLX5_SET(delete_l2_table_entry_in, in, opcode, MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY);
        MLX5_SET(delete_l2_table_entry_in, in, table_index, index);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, delete_l2_table_entry, in);
 }
 
 /* UC L2 table hash node */
index 48b5c84..8809a65 100644 (file)
@@ -4,7 +4,6 @@
 #include <linux/module.h>
 #include <linux/mlx5/driver.h>
 #include <linux/mlx5/port.h>
-#include <linux/mlx5/cmd.h>
 #include "mlx5_core.h"
 #include "lib/port_tun.h"
 
index 148b55c..82c766a 100644 (file)
@@ -60,24 +60,22 @@ static inline u8 mlx5_vxlan_max_udp_ports(struct mlx5_core_dev *mdev)
 
 static int mlx5_vxlan_core_add_port_cmd(struct mlx5_core_dev *mdev, u16 port)
 {
-       u32 in[MLX5_ST_SZ_DW(add_vxlan_udp_dport_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(add_vxlan_udp_dport_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(add_vxlan_udp_dport_in)] = {};
 
        MLX5_SET(add_vxlan_udp_dport_in, in, opcode,
                 MLX5_CMD_OP_ADD_VXLAN_UDP_DPORT);
        MLX5_SET(add_vxlan_udp_dport_in, in, vxlan_udp_port, port);
-       return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(mdev, add_vxlan_udp_dport, in);
 }
 
 static int mlx5_vxlan_core_del_port_cmd(struct mlx5_core_dev *mdev, u16 port)
 {
-       u32 in[MLX5_ST_SZ_DW(delete_vxlan_udp_dport_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(delete_vxlan_udp_dport_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(delete_vxlan_udp_dport_in)] = {};
 
        MLX5_SET(delete_vxlan_udp_dport_in, in, opcode,
                 MLX5_CMD_OP_DELETE_VXLAN_UDP_DPORT);
        MLX5_SET(delete_vxlan_udp_dport_in, in, vxlan_udp_port, port);
-       return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(mdev, delete_vxlan_udp_dport, in);
 }
 
 static struct mlx5_vxlan_port*
index 7af4210..742ba01 100644 (file)
@@ -206,8 +206,7 @@ static void mlx5_set_driver_version(struct mlx5_core_dev *dev)
 {
        int driver_ver_sz = MLX5_FLD_SZ_BYTES(set_driver_version_in,
                                              driver_version);
-       u8 in[MLX5_ST_SZ_BYTES(set_driver_version_in)] = {0};
-       u8 out[MLX5_ST_SZ_BYTES(set_driver_version_out)] = {0};
+       u8 in[MLX5_ST_SZ_BYTES(set_driver_version_in)] = {};
        int remaining_size = driver_ver_sz;
        char *string;
 
@@ -234,7 +233,7 @@ static void mlx5_set_driver_version(struct mlx5_core_dev *dev)
        MLX5_SET(set_driver_version_in, in, opcode,
                 MLX5_CMD_OP_SET_DRIVER_VERSION);
 
-       mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(dev, set_driver_version, in);
 }
 
 static int set_dma_caps(struct pci_dev *pdev)
@@ -366,7 +365,7 @@ static int mlx5_core_get_caps_mode(struct mlx5_core_dev *dev,
 
        MLX5_SET(query_hca_cap_in, in, opcode, MLX5_CMD_OP_QUERY_HCA_CAP);
        MLX5_SET(query_hca_cap_in, in, op_mod, opmod);
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, out_sz);
+       err = mlx5_cmd_exec_inout(dev, query_hca_cap, in, out);
        if (err) {
                mlx5_core_warn(dev,
                               "QUERY_HCA_CAP : type(%x) opmode(%x) Failed(%d)\n",
@@ -407,30 +406,25 @@ int mlx5_core_get_caps(struct mlx5_core_dev *dev, enum mlx5_cap_type cap_type)
        return mlx5_core_get_caps_mode(dev, cap_type, HCA_CAP_OPMOD_GET_MAX);
 }
 
-static int set_caps(struct mlx5_core_dev *dev, void *in, int in_sz, int opmod)
+static int set_caps(struct mlx5_core_dev *dev, void *in, int opmod)
 {
-       u32 out[MLX5_ST_SZ_DW(set_hca_cap_out)] = {0};
-
        MLX5_SET(set_hca_cap_in, in, opcode, MLX5_CMD_OP_SET_HCA_CAP);
        MLX5_SET(set_hca_cap_in, in, op_mod, opmod << 1);
-       return mlx5_cmd_exec(dev, in, in_sz, out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, set_hca_cap, in);
 }
 
-static int handle_hca_cap_atomic(struct mlx5_core_dev *dev)
+static int handle_hca_cap_atomic(struct mlx5_core_dev *dev, void *set_ctx)
 {
-       void *set_ctx;
        void *set_hca_cap;
-       int set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in);
        int req_endianness;
        int err;
 
-       if (MLX5_CAP_GEN(dev, atomic)) {
-               err = mlx5_core_get_caps(dev, MLX5_CAP_ATOMIC);
-               if (err)
-                       return err;
-       } else {
+       if (!MLX5_CAP_GEN(dev, atomic))
                return 0;
-       }
+
+       err = mlx5_core_get_caps(dev, MLX5_CAP_ATOMIC);
+       if (err)
+               return err;
 
        req_endianness =
                MLX5_CAP_ATOMIC(dev,
@@ -439,27 +433,18 @@ static int handle_hca_cap_atomic(struct mlx5_core_dev *dev)
        if (req_endianness != MLX5_ATOMIC_REQ_MODE_HOST_ENDIANNESS)
                return 0;
 
-       set_ctx = kzalloc(set_sz, GFP_KERNEL);
-       if (!set_ctx)
-               return -ENOMEM;
-
        set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx, capability);
 
        /* Set requestor to host endianness */
        MLX5_SET(atomic_caps, set_hca_cap, atomic_req_8B_endianness_mode,
                 MLX5_ATOMIC_REQ_MODE_HOST_ENDIANNESS);
 
-       err = set_caps(dev, set_ctx, set_sz, MLX5_SET_HCA_CAP_OP_MOD_ATOMIC);
-
-       kfree(set_ctx);
-       return err;
+       return set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_ATOMIC);
 }
 
-static int handle_hca_cap_odp(struct mlx5_core_dev *dev)
+static int handle_hca_cap_odp(struct mlx5_core_dev *dev, void *set_ctx)
 {
        void *set_hca_cap;
-       void *set_ctx;
-       int set_sz;
        bool do_set = false;
        int err;
 
@@ -471,11 +456,6 @@ static int handle_hca_cap_odp(struct mlx5_core_dev *dev)
        if (err)
                return err;
 
-       set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in);
-       set_ctx = kzalloc(set_sz, GFP_KERNEL);
-       if (!set_ctx)
-               return -ENOMEM;
-
        set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx, capability);
        memcpy(set_hca_cap, dev->caps.hca_cur[MLX5_CAP_ODP],
               MLX5_ST_SZ_BYTES(odp_cap));
@@ -504,30 +484,21 @@ static int handle_hca_cap_odp(struct mlx5_core_dev *dev)
        ODP_CAP_SET_MAX(dev, dc_odp_caps.read);
        ODP_CAP_SET_MAX(dev, dc_odp_caps.atomic);
 
-       if (do_set)
-               err = set_caps(dev, set_ctx, set_sz,
-                              MLX5_SET_HCA_CAP_OP_MOD_ODP);
-
-       kfree(set_ctx);
+       if (!do_set)
+               return 0;
 
-       return err;
+       return set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_ODP);
 }
 
-static int handle_hca_cap(struct mlx5_core_dev *dev)
+static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx)
 {
-       void *set_ctx = NULL;
        struct mlx5_profile *prof = dev->profile;
-       int err = -ENOMEM;
-       int set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in);
        void *set_hca_cap;
-
-       set_ctx = kzalloc(set_sz, GFP_KERNEL);
-       if (!set_ctx)
-               goto query_ex;
+       int err;
 
        err = mlx5_core_get_caps(dev, MLX5_CAP_GENERAL);
        if (err)
-               goto query_ex;
+               return err;
 
        set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx,
                                   capability);
@@ -578,37 +549,76 @@ static int handle_hca_cap(struct mlx5_core_dev *dev)
                         num_vhca_ports,
                         MLX5_CAP_GEN_MAX(dev, num_vhca_ports));
 
-       err = set_caps(dev, set_ctx, set_sz,
-                      MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE);
+       if (MLX5_CAP_GEN_MAX(dev, release_all_pages))
+               MLX5_SET(cmd_hca_cap, set_hca_cap, release_all_pages, 1);
 
-query_ex:
-       kfree(set_ctx);
+       return set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE);
+}
+
+static int handle_hca_cap_roce(struct mlx5_core_dev *dev, void *set_ctx)
+{
+       void *set_hca_cap;
+       int err;
+
+       if (!MLX5_CAP_GEN(dev, roce))
+               return 0;
+
+       err = mlx5_core_get_caps(dev, MLX5_CAP_ROCE);
+       if (err)
+               return err;
+
+       if (MLX5_CAP_ROCE(dev, sw_r_roce_src_udp_port) ||
+           !MLX5_CAP_ROCE_MAX(dev, sw_r_roce_src_udp_port))
+               return 0;
+
+       set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx, capability);
+       memcpy(set_hca_cap, dev->caps.hca_cur[MLX5_CAP_ROCE],
+              MLX5_ST_SZ_BYTES(roce_cap));
+       MLX5_SET(roce_cap, set_hca_cap, sw_r_roce_src_udp_port, 1);
+
+       err = set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_ROCE);
        return err;
 }
 
 static int set_hca_cap(struct mlx5_core_dev *dev)
 {
+       int set_sz = MLX5_ST_SZ_BYTES(set_hca_cap_in);
+       void *set_ctx;
        int err;
 
-       err = handle_hca_cap(dev);
+       set_ctx = kzalloc(set_sz, GFP_KERNEL);
+       if (!set_ctx)
+               return -ENOMEM;
+
+       err = handle_hca_cap(dev, set_ctx);
        if (err) {
                mlx5_core_err(dev, "handle_hca_cap failed\n");
                goto out;
        }
 
-       err = handle_hca_cap_atomic(dev);
+       memset(set_ctx, 0, set_sz);
+       err = handle_hca_cap_atomic(dev, set_ctx);
        if (err) {
                mlx5_core_err(dev, "handle_hca_cap_atomic failed\n");
                goto out;
        }
 
-       err = handle_hca_cap_odp(dev);
+       memset(set_ctx, 0, set_sz);
+       err = handle_hca_cap_odp(dev, set_ctx);
        if (err) {
                mlx5_core_err(dev, "handle_hca_cap_odp failed\n");
                goto out;
        }
 
+       memset(set_ctx, 0, set_sz);
+       err = handle_hca_cap_roce(dev, set_ctx);
+       if (err) {
+               mlx5_core_err(dev, "handle_hca_cap_roce failed\n");
+               goto out;
+       }
+
 out:
+       kfree(set_ctx);
        return err;
 }
 
@@ -642,26 +652,24 @@ static int mlx5_core_set_hca_defaults(struct mlx5_core_dev *dev)
 
 int mlx5_core_enable_hca(struct mlx5_core_dev *dev, u16 func_id)
 {
-       u32 out[MLX5_ST_SZ_DW(enable_hca_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(enable_hca_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(enable_hca_in)] = {};
 
        MLX5_SET(enable_hca_in, in, opcode, MLX5_CMD_OP_ENABLE_HCA);
        MLX5_SET(enable_hca_in, in, function_id, func_id);
        MLX5_SET(enable_hca_in, in, embedded_cpu_function,
                 dev->caps.embedded_cpu);
-       return mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, enable_hca, in);
 }
 
 int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id)
 {
-       u32 out[MLX5_ST_SZ_DW(disable_hca_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(disable_hca_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(disable_hca_in)] = {};
 
        MLX5_SET(disable_hca_in, in, opcode, MLX5_CMD_OP_DISABLE_HCA);
        MLX5_SET(disable_hca_in, in, function_id, func_id);
        MLX5_SET(enable_hca_in, in, embedded_cpu_function,
                 dev->caps.embedded_cpu);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, disable_hca, in);
 }
 
 u64 mlx5_read_internal_timer(struct mlx5_core_dev *dev,
@@ -686,14 +694,13 @@ u64 mlx5_read_internal_timer(struct mlx5_core_dev *dev,
 
 static int mlx5_core_set_issi(struct mlx5_core_dev *dev)
 {
-       u32 query_in[MLX5_ST_SZ_DW(query_issi_in)]   = {0};
-       u32 query_out[MLX5_ST_SZ_DW(query_issi_out)] = {0};
+       u32 query_out[MLX5_ST_SZ_DW(query_issi_out)] = {};
+       u32 query_in[MLX5_ST_SZ_DW(query_issi_in)] = {};
        u32 sup_issi;
        int err;
 
        MLX5_SET(query_issi_in, query_in, opcode, MLX5_CMD_OP_QUERY_ISSI);
-       err = mlx5_cmd_exec(dev, query_in, sizeof(query_in),
-                           query_out, sizeof(query_out));
+       err = mlx5_cmd_exec_inout(dev, query_issi, query_in, query_out);
        if (err) {
                u32 syndrome;
                u8 status;
@@ -713,13 +720,11 @@ static int mlx5_core_set_issi(struct mlx5_core_dev *dev)
        sup_issi = MLX5_GET(query_issi_out, query_out, supported_issi_dw0);
 
        if (sup_issi & (1 << 1)) {
-               u32 set_in[MLX5_ST_SZ_DW(set_issi_in)]   = {0};
-               u32 set_out[MLX5_ST_SZ_DW(set_issi_out)] = {0};
+               u32 set_in[MLX5_ST_SZ_DW(set_issi_in)] = {};
 
                MLX5_SET(set_issi_in, set_in, opcode, MLX5_CMD_OP_SET_ISSI);
                MLX5_SET(set_issi_in, set_in, current_issi, 1);
-               err = mlx5_cmd_exec(dev, set_in, sizeof(set_in),
-                                   set_out, sizeof(set_out));
+               err = mlx5_cmd_exec_in(dev, set_issi, set_in);
                if (err) {
                        mlx5_core_err(dev, "Failed to set ISSI to 1 err(%d)\n",
                                      err);
@@ -782,7 +787,7 @@ static int mlx5_pci_init(struct mlx5_core_dev *dev, struct pci_dev *pdev,
        }
 
        mlx5_pci_vsc_init(dev);
-
+       dev->caps.embedded_cpu = mlx5_read_embedded_cpu(dev);
        return 0;
 
 err_clr_master:
@@ -836,8 +841,6 @@ static int mlx5_init_once(struct mlx5_core_dev *dev)
 
        mlx5_cq_debugfs_init(dev);
 
-       mlx5_init_qp_table(dev);
-
        mlx5_init_reserved_gids(dev);
 
        mlx5_init_clock(dev);
@@ -896,7 +899,6 @@ err_rl_cleanup:
 err_tables_cleanup:
        mlx5_geneve_destroy(dev->geneve);
        mlx5_vxlan_destroy(dev->vxlan);
-       mlx5_cleanup_qp_table(dev);
        mlx5_cq_debugfs_cleanup(dev);
        mlx5_events_cleanup(dev);
 err_eq_cleanup:
@@ -924,7 +926,6 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev)
        mlx5_vxlan_destroy(dev->vxlan);
        mlx5_cleanup_clock(dev);
        mlx5_cleanup_reserved_gids(dev);
-       mlx5_cleanup_qp_table(dev);
        mlx5_cq_debugfs_cleanup(dev);
        mlx5_events_cleanup(dev);
        mlx5_eq_table_cleanup(dev);
@@ -1180,7 +1181,6 @@ int mlx5_load_one(struct mlx5_core_dev *dev, bool boot)
 {
        int err = 0;
 
-       dev->caps.embedded_cpu = mlx5_read_embedded_cpu(dev);
        mutex_lock(&dev->intf_state_mutex);
        if (test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state)) {
                mlx5_core_warn(dev, "interface is up, NOP\n");
index ba2b09c..e019d68 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mlx5/driver.h>
-#include <linux/mlx5/cmd.h>
 #include <rdma/ib_verbs.h>
 #include "mlx5_core.h"
 
 int mlx5_core_attach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn)
 {
-       u32 out[MLX5_ST_SZ_DW(attach_to_mcg_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(attach_to_mcg_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(attach_to_mcg_in)] = {};
        void *gid;
 
        MLX5_SET(attach_to_mcg_in, in, opcode, MLX5_CMD_OP_ATTACH_TO_MCG);
        MLX5_SET(attach_to_mcg_in, in, qpn, qpn);
        gid = MLX5_ADDR_OF(attach_to_mcg_in, in, multicast_gid);
        memcpy(gid, mgid, sizeof(*mgid));
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, attach_to_mcg, in);
 }
 EXPORT_SYMBOL(mlx5_core_attach_mcg);
 
 int mlx5_core_detach_mcg(struct mlx5_core_dev *dev, union ib_gid *mgid, u32 qpn)
 {
-       u32 out[MLX5_ST_SZ_DW(detach_from_mcg_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(detach_from_mcg_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(detach_from_mcg_in)] = {};
        void *gid;
 
        MLX5_SET(detach_from_mcg_in, in, opcode, MLX5_CMD_OP_DETACH_FROM_MCG);
        MLX5_SET(detach_from_mcg_in, in, qpn, qpn);
        gid = MLX5_ADDR_OF(detach_from_mcg_in, in, multicast_gid);
        memcpy(gid, mgid, sizeof(*mgid));
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, detach_from_mcg, in);
 }
 EXPORT_SYMBOL(mlx5_core_detach_mcg);
index 366f2cb..9eb51f0 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mlx5/driver.h>
-#include <linux/mlx5/cmd.h>
 #include "mlx5_core.h"
 
 int mlx5_core_create_mkey(struct mlx5_core_dev *dev,
                          struct mlx5_core_mkey *mkey,
                          u32 *in, int inlen)
 {
-       u32 lout[MLX5_ST_SZ_DW(create_mkey_out)] = {0};
+       u32 lout[MLX5_ST_SZ_DW(create_mkey_out)] = {};
        u32 mkey_index;
        void *mkc;
        int err;
@@ -66,19 +65,18 @@ EXPORT_SYMBOL(mlx5_core_create_mkey);
 int mlx5_core_destroy_mkey(struct mlx5_core_dev *dev,
                           struct mlx5_core_mkey *mkey)
 {
-       u32 out[MLX5_ST_SZ_DW(destroy_mkey_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(destroy_mkey_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_mkey_in)] = {};
 
        MLX5_SET(destroy_mkey_in, in, opcode, MLX5_CMD_OP_DESTROY_MKEY);
        MLX5_SET(destroy_mkey_in, in, mkey_index, mlx5_mkey_to_idx(mkey->key));
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, destroy_mkey, in);
 }
 EXPORT_SYMBOL(mlx5_core_destroy_mkey);
 
 int mlx5_core_query_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mkey *mkey,
                         u32 *out, int outlen)
 {
-       u32 in[MLX5_ST_SZ_DW(query_mkey_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(query_mkey_in)] = {};
 
        memset(out, 0, outlen);
        MLX5_SET(query_mkey_in, in, opcode, MLX5_CMD_OP_QUERY_MKEY);
@@ -100,8 +98,8 @@ static inline u32 mlx5_get_psv(u32 *out, int psv_index)
 int mlx5_core_create_psv(struct mlx5_core_dev *dev, u32 pdn,
                         int npsvs, u32 *sig_index)
 {
-       u32 out[MLX5_ST_SZ_DW(create_psv_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(create_psv_in)]   = {0};
+       u32 out[MLX5_ST_SZ_DW(create_psv_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(create_psv_in)] = {};
        int i, err;
 
        if (npsvs > MLX5_MAX_PSVS)
@@ -111,7 +109,7 @@ int mlx5_core_create_psv(struct mlx5_core_dev *dev, u32 pdn,
        MLX5_SET(create_psv_in, in, pd, pdn);
        MLX5_SET(create_psv_in, in, num_psv, npsvs);
 
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(dev, create_psv, in, out);
        if (err)
                return err;
 
@@ -124,11 +122,10 @@ EXPORT_SYMBOL(mlx5_core_create_psv);
 
 int mlx5_core_destroy_psv(struct mlx5_core_dev *dev, int psv_num)
 {
-       u32 out[MLX5_ST_SZ_DW(destroy_psv_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(destroy_psv_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_psv_in)] = {};
 
        MLX5_SET(destroy_psv_in, in, opcode, MLX5_CMD_OP_DESTROY_PSV);
        MLX5_SET(destroy_psv_in, in, psvn, psv_num);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, destroy_psv, in);
 }
 EXPORT_SYMBOL(mlx5_core_destroy_psv);
index 91bd258..8ce78f4 100644 (file)
@@ -35,7 +35,6 @@
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/mlx5/driver.h>
-#include <linux/mlx5/cmd.h>
 #include "mlx5_core.h"
 #include "lib/eq.h"
 
@@ -51,6 +50,7 @@ struct mlx5_pages_req {
        u8      ec_function;
        s32     npages;
        struct work_struct work;
+       u8      release_all;
 };
 
 struct fw_page {
@@ -136,8 +136,8 @@ static struct fw_page *find_fw_page(struct mlx5_core_dev *dev, u64 addr)
 static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_id,
                                s32 *npages, int boot)
 {
-       u32 out[MLX5_ST_SZ_DW(query_pages_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(query_pages_in)]   = {0};
+       u32 out[MLX5_ST_SZ_DW(query_pages_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(query_pages_in)] = {};
        int err;
 
        MLX5_SET(query_pages_in, in, opcode, MLX5_CMD_OP_QUERY_PAGES);
@@ -146,7 +146,7 @@ static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_id,
                 MLX5_QUERY_PAGES_IN_OP_MOD_INIT_PAGES);
        MLX5_SET(query_pages_in, in, embedded_cpu_function, mlx5_core_is_ecpf(dev));
 
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(dev, query_pages, in, out);
        if (err)
                return err;
 
@@ -182,25 +182,17 @@ static int alloc_4k(struct mlx5_core_dev *dev, u64 *addr)
 
 #define MLX5_U64_4K_PAGE_MASK ((~(u64)0U) << PAGE_SHIFT)
 
-static void free_4k(struct mlx5_core_dev *dev, u64 addr)
+static void free_fwp(struct mlx5_core_dev *dev, struct fw_page *fwp)
 {
-       struct fw_page *fwp;
-       int n;
-
-       fwp = find_fw_page(dev, addr & MLX5_U64_4K_PAGE_MASK);
-       if (!fwp) {
-               mlx5_core_warn(dev, "page not found\n");
-               return;
-       }
+       int n = (fwp->addr & ~MLX5_U64_4K_PAGE_MASK) >> MLX5_ADAPTER_PAGE_SHIFT;
 
-       n = (addr & ~MLX5_U64_4K_PAGE_MASK) >> MLX5_ADAPTER_PAGE_SHIFT;
        fwp->free_count++;
        set_bit(n, &fwp->bitmask);
        if (fwp->free_count == MLX5_NUM_4K_IN_PAGE) {
                rb_erase(&fwp->rb_node, &dev->priv.page_root);
                if (fwp->free_count != 1)
                        list_del(&fwp->list);
-               dma_unmap_page(dev->device, addr & MLX5_U64_4K_PAGE_MASK,
+               dma_unmap_page(dev->device, fwp->addr & MLX5_U64_4K_PAGE_MASK,
                               PAGE_SIZE, DMA_BIDIRECTIONAL);
                __free_page(fwp->page);
                kfree(fwp);
@@ -209,6 +201,18 @@ static void free_4k(struct mlx5_core_dev *dev, u64 addr)
        }
 }
 
+static void free_addr(struct mlx5_core_dev *dev, u64 addr)
+{
+       struct fw_page *fwp;
+
+       fwp = find_fw_page(dev, addr & MLX5_U64_4K_PAGE_MASK);
+       if (!fwp) {
+               mlx5_core_warn_rl(dev, "page not found\n");
+               return;
+       }
+       free_fwp(dev, fwp);
+}
+
 static int alloc_system_page(struct mlx5_core_dev *dev, u16 func_id)
 {
        struct device *device = dev->device;
@@ -257,8 +261,7 @@ err_mapping:
 static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_id,
                             bool ec_function)
 {
-       u32 out[MLX5_ST_SZ_DW(manage_pages_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(manage_pages_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(manage_pages_in)] = {};
        int err;
 
        MLX5_SET(manage_pages_in, in, opcode, MLX5_CMD_OP_MANAGE_PAGES);
@@ -266,7 +269,7 @@ static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_id,
        MLX5_SET(manage_pages_in, in, function_id, func_id);
        MLX5_SET(manage_pages_in, in, embedded_cpu_function, ec_function);
 
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_in(dev, manage_pages, in);
        if (err)
                mlx5_core_warn(dev, "page notify failed func_id(%d) err(%d)\n",
                               func_id, err);
@@ -331,7 +334,7 @@ retry:
 
 out_4k:
        for (i--; i >= 0; i--)
-               free_4k(dev, MLX5_GET64(manage_pages_in, in, pas[i]));
+               free_addr(dev, MLX5_GET64(manage_pages_in, in, pas[i]));
 out_free:
        kvfree(in);
        if (notify_fail)
@@ -339,6 +342,33 @@ out_free:
        return err;
 }
 
+static void release_all_pages(struct mlx5_core_dev *dev, u32 func_id,
+                             bool ec_function)
+{
+       struct rb_node *p;
+       int npages = 0;
+
+       p = rb_first(&dev->priv.page_root);
+       while (p) {
+               struct fw_page *fwp = rb_entry(p, struct fw_page, rb_node);
+
+               p = rb_next(p);
+               if (fwp->func_id != func_id)
+                       continue;
+               free_fwp(dev, fwp);
+               npages++;
+       }
+
+       dev->priv.fw_pages -= npages;
+       if (func_id)
+               dev->priv.vfs_pages -= npages;
+       else if (mlx5_core_is_ecpf(dev) && !ec_function)
+               dev->priv.peer_pf_pages -= npages;
+
+       mlx5_core_dbg(dev, "npages %d, ec_function %d, func_id 0x%x\n",
+                     npages, ec_function, func_id);
+}
+
 static int reclaim_pages_cmd(struct mlx5_core_dev *dev,
                             u32 *in, int in_size, u32 *out, int out_size)
 {
@@ -374,7 +404,7 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
                         int *nclaimed, bool ec_function)
 {
        int outlen = MLX5_ST_SZ_BYTES(manage_pages_out);
-       u32 in[MLX5_ST_SZ_DW(manage_pages_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(manage_pages_in)] = {};
        int num_claimed;
        u32 *out;
        int err;
@@ -410,7 +440,7 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
        }
 
        for (i = 0; i < num_claimed; i++)
-               free_4k(dev, MLX5_GET64(manage_pages_out, out, pas[i]));
+               free_addr(dev, MLX5_GET64(manage_pages_out, out, pas[i]));
 
        if (nclaimed)
                *nclaimed = num_claimed;
@@ -432,7 +462,9 @@ static void pages_work_handler(struct work_struct *work)
        struct mlx5_core_dev *dev = req->dev;
        int err = 0;
 
-       if (req->npages < 0)
+       if (req->release_all)
+               release_all_pages(dev, req->func_id, req->ec_function);
+       else if (req->npages < 0)
                err = reclaim_pages(dev, req->func_id, -1 * req->npages, NULL,
                                    req->ec_function);
        else if (req->npages > 0)
@@ -447,6 +479,7 @@ static void pages_work_handler(struct work_struct *work)
 
 enum {
        EC_FUNCTION_MASK = 0x8000,
+       RELEASE_ALL_PAGES_MASK = 0x4000,
 };
 
 static int req_pages_handler(struct notifier_block *nb,
@@ -457,6 +490,7 @@ static int req_pages_handler(struct notifier_block *nb,
        struct mlx5_priv *priv;
        struct mlx5_eqe *eqe;
        bool ec_function;
+       bool release_all;
        u16 func_id;
        s32 npages;
 
@@ -467,8 +501,10 @@ static int req_pages_handler(struct notifier_block *nb,
        func_id = be16_to_cpu(eqe->data.req_pages.func_id);
        npages  = be32_to_cpu(eqe->data.req_pages.num_pages);
        ec_function = be16_to_cpu(eqe->data.req_pages.ec_function) & EC_FUNCTION_MASK;
-       mlx5_core_dbg(dev, "page request for func 0x%x, npages %d\n",
-                     func_id, npages);
+       release_all = be16_to_cpu(eqe->data.req_pages.ec_function) &
+                     RELEASE_ALL_PAGES_MASK;
+       mlx5_core_dbg(dev, "page request for func 0x%x, npages %d, release_all %d\n",
+                     func_id, npages, release_all);
        req = kzalloc(sizeof(*req), GFP_ATOMIC);
        if (!req) {
                mlx5_core_warn(dev, "failed to allocate pages request\n");
@@ -479,6 +515,7 @@ static int req_pages_handler(struct notifier_block *nb,
        req->func_id = func_id;
        req->npages = npages;
        req->ec_function = ec_function;
+       req->release_all = release_all;
        INIT_WORK(&req->work, pages_work_handler);
        queue_work(dev->priv.pg_wq, &req->work);
        return NOTIFY_OK;
index bd830d8..aabc53a 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mlx5/driver.h>
-#include <linux/mlx5/cmd.h>
 #include "mlx5_core.h"
 
 int mlx5_core_alloc_pd(struct mlx5_core_dev *dev, u32 *pdn)
 {
-       u32 out[MLX5_ST_SZ_DW(alloc_pd_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(alloc_pd_in)]   = {0};
+       u32 out[MLX5_ST_SZ_DW(alloc_pd_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(alloc_pd_in)] = {};
        int err;
 
        MLX5_SET(alloc_pd_in, in, opcode, MLX5_CMD_OP_ALLOC_PD);
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(dev, alloc_pd, in, out);
        if (!err)
                *pdn = MLX5_GET(alloc_pd_out, out, pd);
        return err;
@@ -52,11 +51,10 @@ EXPORT_SYMBOL(mlx5_core_alloc_pd);
 
 int mlx5_core_dealloc_pd(struct mlx5_core_dev *dev, u32 pdn)
 {
-       u32 out[MLX5_ST_SZ_DW(dealloc_pd_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(dealloc_pd_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(dealloc_pd_in)] = {};
 
        MLX5_SET(dealloc_pd_in, in, opcode, MLX5_CMD_OP_DEALLOC_PD);
        MLX5_SET(dealloc_pd_in, in, pd, pdn);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, dealloc_pd, in);
 }
 EXPORT_SYMBOL(mlx5_core_dealloc_pd);
index cc262b3..9f829e6 100644 (file)
@@ -763,24 +763,23 @@ EXPORT_SYMBOL_GPL(mlx5_query_port_ets_rate_limit);
 
 int mlx5_set_port_wol(struct mlx5_core_dev *mdev, u8 wol_mode)
 {
-       u32 in[MLX5_ST_SZ_DW(set_wol_rol_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(set_wol_rol_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(set_wol_rol_in)] = {};
 
        MLX5_SET(set_wol_rol_in, in, opcode, MLX5_CMD_OP_SET_WOL_ROL);
        MLX5_SET(set_wol_rol_in, in, wol_mode_valid, 1);
        MLX5_SET(set_wol_rol_in, in, wol_mode, wol_mode);
-       return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(mdev, set_wol_rol, in);
 }
 EXPORT_SYMBOL_GPL(mlx5_set_port_wol);
 
 int mlx5_query_port_wol(struct mlx5_core_dev *mdev, u8 *wol_mode)
 {
-       u32 in[MLX5_ST_SZ_DW(query_wol_rol_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(query_wol_rol_out)] = {0};
+       u32 out[MLX5_ST_SZ_DW(query_wol_rol_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(query_wol_rol_in)] = {};
        int err;
 
        MLX5_SET(query_wol_rol_in, in, opcode, MLX5_CMD_OP_QUERY_WOL_ROL);
-       err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(mdev, query_wol_rol, in, out);
        if (!err)
                *wol_mode = MLX5_GET(query_wol_rol_out, out, wol_mode);
 
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c
deleted file mode 100644 (file)
index c3aea4c..0000000
+++ /dev/null
@@ -1,737 +0,0 @@
-/*
- * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
- *
- * This software is available to you under a choice of one of two
- * licenses.  You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available from the file
- * COPYING in the main directory of this source tree, or the
- * OpenIB.org BSD license below:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      - Redistributions of source code must retain the above
- *        copyright notice, this list of conditions and the following
- *        disclaimer.
- *
- *      - Redistributions in binary form must reproduce the above
- *        copyright notice, this list of conditions and the following
- *        disclaimer in the documentation and/or other materials
- *        provided with the distribution.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include <linux/gfp.h>
-#include <linux/export.h>
-#include <linux/mlx5/cmd.h>
-#include <linux/mlx5/qp.h>
-#include <linux/mlx5/driver.h>
-#include <linux/mlx5/transobj.h>
-
-#include "mlx5_core.h"
-#include "lib/eq.h"
-
-static int mlx5_core_drain_dct(struct mlx5_core_dev *dev,
-                              struct mlx5_core_dct *dct);
-
-static struct mlx5_core_rsc_common *
-mlx5_get_rsc(struct mlx5_qp_table *table, u32 rsn)
-{
-       struct mlx5_core_rsc_common *common;
-       unsigned long flags;
-
-       spin_lock_irqsave(&table->lock, flags);
-
-       common = radix_tree_lookup(&table->tree, rsn);
-       if (common)
-               refcount_inc(&common->refcount);
-
-       spin_unlock_irqrestore(&table->lock, flags);
-
-       return common;
-}
-
-void mlx5_core_put_rsc(struct mlx5_core_rsc_common *common)
-{
-       if (refcount_dec_and_test(&common->refcount))
-               complete(&common->free);
-}
-
-static u64 qp_allowed_event_types(void)
-{
-       u64 mask;
-
-       mask = BIT(MLX5_EVENT_TYPE_PATH_MIG) |
-              BIT(MLX5_EVENT_TYPE_COMM_EST) |
-              BIT(MLX5_EVENT_TYPE_SQ_DRAINED) |
-              BIT(MLX5_EVENT_TYPE_SRQ_LAST_WQE) |
-              BIT(MLX5_EVENT_TYPE_WQ_CATAS_ERROR) |
-              BIT(MLX5_EVENT_TYPE_PATH_MIG_FAILED) |
-              BIT(MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR) |
-              BIT(MLX5_EVENT_TYPE_WQ_ACCESS_ERROR);
-
-       return mask;
-}
-
-static u64 rq_allowed_event_types(void)
-{
-       u64 mask;
-
-       mask = BIT(MLX5_EVENT_TYPE_SRQ_LAST_WQE) |
-              BIT(MLX5_EVENT_TYPE_WQ_CATAS_ERROR);
-
-       return mask;
-}
-
-static u64 sq_allowed_event_types(void)
-{
-       return BIT(MLX5_EVENT_TYPE_WQ_CATAS_ERROR);
-}
-
-static u64 dct_allowed_event_types(void)
-{
-       return BIT(MLX5_EVENT_TYPE_DCT_DRAINED);
-}
-
-static bool is_event_type_allowed(int rsc_type, int event_type)
-{
-       switch (rsc_type) {
-       case MLX5_EVENT_QUEUE_TYPE_QP:
-               return BIT(event_type) & qp_allowed_event_types();
-       case MLX5_EVENT_QUEUE_TYPE_RQ:
-               return BIT(event_type) & rq_allowed_event_types();
-       case MLX5_EVENT_QUEUE_TYPE_SQ:
-               return BIT(event_type) & sq_allowed_event_types();
-       case MLX5_EVENT_QUEUE_TYPE_DCT:
-               return BIT(event_type) & dct_allowed_event_types();
-       default:
-               WARN(1, "Event arrived for unknown resource type");
-               return false;
-       }
-}
-
-static int rsc_event_notifier(struct notifier_block *nb,
-                             unsigned long type, void *data)
-{
-       struct mlx5_core_rsc_common *common;
-       struct mlx5_qp_table *table;
-       struct mlx5_core_dev *dev;
-       struct mlx5_core_dct *dct;
-       u8 event_type = (u8)type;
-       struct mlx5_core_qp *qp;
-       struct mlx5_priv *priv;
-       struct mlx5_eqe *eqe;
-       u32 rsn;
-
-       switch (event_type) {
-       case MLX5_EVENT_TYPE_DCT_DRAINED:
-               eqe = data;
-               rsn = be32_to_cpu(eqe->data.dct.dctn) & 0xffffff;
-               rsn |= (MLX5_RES_DCT << MLX5_USER_INDEX_LEN);
-               break;
-       case MLX5_EVENT_TYPE_PATH_MIG:
-       case MLX5_EVENT_TYPE_COMM_EST:
-       case MLX5_EVENT_TYPE_SQ_DRAINED:
-       case MLX5_EVENT_TYPE_SRQ_LAST_WQE:
-       case MLX5_EVENT_TYPE_WQ_CATAS_ERROR:
-       case MLX5_EVENT_TYPE_PATH_MIG_FAILED:
-       case MLX5_EVENT_TYPE_WQ_INVAL_REQ_ERROR:
-       case MLX5_EVENT_TYPE_WQ_ACCESS_ERROR:
-               eqe = data;
-               rsn = be32_to_cpu(eqe->data.qp_srq.qp_srq_n) & 0xffffff;
-               rsn |= (eqe->data.qp_srq.type << MLX5_USER_INDEX_LEN);
-               break;
-       default:
-               return NOTIFY_DONE;
-       }
-
-       table = container_of(nb, struct mlx5_qp_table, nb);
-       priv  = container_of(table, struct mlx5_priv, qp_table);
-       dev   = container_of(priv, struct mlx5_core_dev, priv);
-
-       mlx5_core_dbg(dev, "event (%d) arrived on resource 0x%x\n", eqe->type, rsn);
-
-       common = mlx5_get_rsc(table, rsn);
-       if (!common) {
-               mlx5_core_dbg(dev, "Async event for unknown resource 0x%x\n", rsn);
-               return NOTIFY_OK;
-       }
-
-       if (!is_event_type_allowed((rsn >> MLX5_USER_INDEX_LEN), event_type)) {
-               mlx5_core_warn(dev, "event 0x%.2x is not allowed on resource 0x%.8x\n",
-                              event_type, rsn);
-               goto out;
-       }
-
-       switch (common->res) {
-       case MLX5_RES_QP:
-       case MLX5_RES_RQ:
-       case MLX5_RES_SQ:
-               qp = (struct mlx5_core_qp *)common;
-               qp->event(qp, event_type);
-               break;
-       case MLX5_RES_DCT:
-               dct = (struct mlx5_core_dct *)common;
-               if (event_type == MLX5_EVENT_TYPE_DCT_DRAINED)
-                       complete(&dct->drained);
-               break;
-       default:
-               mlx5_core_warn(dev, "invalid resource type for 0x%x\n", rsn);
-       }
-out:
-       mlx5_core_put_rsc(common);
-
-       return NOTIFY_OK;
-}
-
-static int create_resource_common(struct mlx5_core_dev *dev,
-                                 struct mlx5_core_qp *qp,
-                                 int rsc_type)
-{
-       struct mlx5_qp_table *table = &dev->priv.qp_table;
-       int err;
-
-       qp->common.res = rsc_type;
-       spin_lock_irq(&table->lock);
-       err = radix_tree_insert(&table->tree,
-                               qp->qpn | (rsc_type << MLX5_USER_INDEX_LEN),
-                               qp);
-       spin_unlock_irq(&table->lock);
-       if (err)
-               return err;
-
-       refcount_set(&qp->common.refcount, 1);
-       init_completion(&qp->common.free);
-       qp->pid = current->pid;
-
-       return 0;
-}
-
-static void destroy_resource_common(struct mlx5_core_dev *dev,
-                                   struct mlx5_core_qp *qp)
-{
-       struct mlx5_qp_table *table = &dev->priv.qp_table;
-       unsigned long flags;
-
-       spin_lock_irqsave(&table->lock, flags);
-       radix_tree_delete(&table->tree,
-                         qp->qpn | (qp->common.res << MLX5_USER_INDEX_LEN));
-       spin_unlock_irqrestore(&table->lock, flags);
-       mlx5_core_put_rsc((struct mlx5_core_rsc_common *)qp);
-       wait_for_completion(&qp->common.free);
-}
-
-static int _mlx5_core_destroy_dct(struct mlx5_core_dev *dev,
-                                 struct mlx5_core_dct *dct, bool need_cleanup)
-{
-       u32 out[MLX5_ST_SZ_DW(destroy_dct_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(destroy_dct_in)]   = {0};
-       struct mlx5_core_qp *qp = &dct->mqp;
-       int err;
-
-       err = mlx5_core_drain_dct(dev, dct);
-       if (err) {
-               if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
-                       goto destroy;
-               } else {
-                       mlx5_core_warn(
-                               dev, "failed drain DCT 0x%x with error 0x%x\n",
-                               qp->qpn, err);
-                       return err;
-               }
-       }
-       wait_for_completion(&dct->drained);
-destroy:
-       if (need_cleanup)
-               destroy_resource_common(dev, &dct->mqp);
-       MLX5_SET(destroy_dct_in, in, opcode, MLX5_CMD_OP_DESTROY_DCT);
-       MLX5_SET(destroy_dct_in, in, dctn, qp->qpn);
-       MLX5_SET(destroy_dct_in, in, uid, qp->uid);
-       err = mlx5_cmd_exec(dev, (void *)&in, sizeof(in),
-                           (void *)&out, sizeof(out));
-       return err;
-}
-
-int mlx5_core_create_dct(struct mlx5_core_dev *dev,
-                        struct mlx5_core_dct *dct,
-                        u32 *in, int inlen,
-                        u32 *out, int outlen)
-{
-       struct mlx5_core_qp *qp = &dct->mqp;
-       int err;
-
-       init_completion(&dct->drained);
-       MLX5_SET(create_dct_in, in, opcode, MLX5_CMD_OP_CREATE_DCT);
-
-       err = mlx5_cmd_exec(dev, in, inlen, out, outlen);
-       if (err) {
-               mlx5_core_warn(dev, "create DCT failed, ret %d\n", err);
-               return err;
-       }
-
-       qp->qpn = MLX5_GET(create_dct_out, out, dctn);
-       qp->uid = MLX5_GET(create_dct_in, in, uid);
-       err = create_resource_common(dev, qp, MLX5_RES_DCT);
-       if (err)
-               goto err_cmd;
-
-       return 0;
-err_cmd:
-       _mlx5_core_destroy_dct(dev, dct, false);
-       return err;
-}
-EXPORT_SYMBOL_GPL(mlx5_core_create_dct);
-
-int mlx5_core_create_qp(struct mlx5_core_dev *dev,
-                       struct mlx5_core_qp *qp,
-                       u32 *in, int inlen)
-{
-       u32 out[MLX5_ST_SZ_DW(create_qp_out)] = {0};
-       u32 dout[MLX5_ST_SZ_DW(destroy_qp_out)];
-       u32 din[MLX5_ST_SZ_DW(destroy_qp_in)];
-       int err;
-
-       MLX5_SET(create_qp_in, in, opcode, MLX5_CMD_OP_CREATE_QP);
-
-       err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
-       if (err)
-               return err;
-
-       qp->uid = MLX5_GET(create_qp_in, in, uid);
-       qp->qpn = MLX5_GET(create_qp_out, out, qpn);
-       mlx5_core_dbg(dev, "qpn = 0x%x\n", qp->qpn);
-
-       err = create_resource_common(dev, qp, MLX5_RES_QP);
-       if (err)
-               goto err_cmd;
-
-       err = mlx5_debug_qp_add(dev, qp);
-       if (err)
-               mlx5_core_dbg(dev, "failed adding QP 0x%x to debug file system\n",
-                             qp->qpn);
-
-       atomic_inc(&dev->num_qps);
-
-       return 0;
-
-err_cmd:
-       memset(din, 0, sizeof(din));
-       memset(dout, 0, sizeof(dout));
-       MLX5_SET(destroy_qp_in, din, opcode, MLX5_CMD_OP_DESTROY_QP);
-       MLX5_SET(destroy_qp_in, din, qpn, qp->qpn);
-       MLX5_SET(destroy_qp_in, din, uid, qp->uid);
-       mlx5_cmd_exec(dev, din, sizeof(din), dout, sizeof(dout));
-       return err;
-}
-EXPORT_SYMBOL_GPL(mlx5_core_create_qp);
-
-static int mlx5_core_drain_dct(struct mlx5_core_dev *dev,
-                              struct mlx5_core_dct *dct)
-{
-       u32 out[MLX5_ST_SZ_DW(drain_dct_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(drain_dct_in)]   = {0};
-       struct mlx5_core_qp *qp = &dct->mqp;
-
-       MLX5_SET(drain_dct_in, in, opcode, MLX5_CMD_OP_DRAIN_DCT);
-       MLX5_SET(drain_dct_in, in, dctn, qp->qpn);
-       MLX5_SET(drain_dct_in, in, uid, qp->uid);
-       return mlx5_cmd_exec(dev, (void *)&in, sizeof(in),
-                            (void *)&out, sizeof(out));
-}
-
-int mlx5_core_destroy_dct(struct mlx5_core_dev *dev,
-                         struct mlx5_core_dct *dct)
-{
-       return _mlx5_core_destroy_dct(dev, dct, true);
-}
-EXPORT_SYMBOL_GPL(mlx5_core_destroy_dct);
-
-int mlx5_core_destroy_qp(struct mlx5_core_dev *dev,
-                        struct mlx5_core_qp *qp)
-{
-       u32 out[MLX5_ST_SZ_DW(destroy_qp_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(destroy_qp_in)]   = {0};
-       int err;
-
-       mlx5_debug_qp_remove(dev, qp);
-
-       destroy_resource_common(dev, qp);
-
-       MLX5_SET(destroy_qp_in, in, opcode, MLX5_CMD_OP_DESTROY_QP);
-       MLX5_SET(destroy_qp_in, in, qpn, qp->qpn);
-       MLX5_SET(destroy_qp_in, in, uid, qp->uid);
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-       if (err)
-               return err;
-
-       atomic_dec(&dev->num_qps);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(mlx5_core_destroy_qp);
-
-int mlx5_core_set_delay_drop(struct mlx5_core_dev *dev,
-                            u32 timeout_usec)
-{
-       u32 out[MLX5_ST_SZ_DW(set_delay_drop_params_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(set_delay_drop_params_in)]   = {0};
-
-       MLX5_SET(set_delay_drop_params_in, in, opcode,
-                MLX5_CMD_OP_SET_DELAY_DROP_PARAMS);
-       MLX5_SET(set_delay_drop_params_in, in, delay_drop_timeout,
-                timeout_usec / 100);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-EXPORT_SYMBOL_GPL(mlx5_core_set_delay_drop);
-
-struct mbox_info {
-       u32 *in;
-       u32 *out;
-       int inlen;
-       int outlen;
-};
-
-static int mbox_alloc(struct mbox_info *mbox, int inlen, int outlen)
-{
-       mbox->inlen  = inlen;
-       mbox->outlen = outlen;
-       mbox->in = kzalloc(mbox->inlen, GFP_KERNEL);
-       mbox->out = kzalloc(mbox->outlen, GFP_KERNEL);
-       if (!mbox->in || !mbox->out) {
-               kfree(mbox->in);
-               kfree(mbox->out);
-               return -ENOMEM;
-       }
-
-       return 0;
-}
-
-static void mbox_free(struct mbox_info *mbox)
-{
-       kfree(mbox->in);
-       kfree(mbox->out);
-}
-
-static int modify_qp_mbox_alloc(struct mlx5_core_dev *dev, u16 opcode, int qpn,
-                               u32 opt_param_mask, void *qpc,
-                               struct mbox_info *mbox, u16 uid)
-{
-       mbox->out = NULL;
-       mbox->in = NULL;
-
-#define MBOX_ALLOC(mbox, typ)  \
-       mbox_alloc(mbox, MLX5_ST_SZ_BYTES(typ##_in), MLX5_ST_SZ_BYTES(typ##_out))
-
-#define MOD_QP_IN_SET(typ, in, _opcode, _qpn, _uid)                            \
-       do {                                                                   \
-               MLX5_SET(typ##_in, in, opcode, _opcode);                       \
-               MLX5_SET(typ##_in, in, qpn, _qpn);                             \
-               MLX5_SET(typ##_in, in, uid, _uid);                             \
-       } while (0)
-
-#define MOD_QP_IN_SET_QPC(typ, in, _opcode, _qpn, _opt_p, _qpc, _uid)          \
-       do {                                                                   \
-               MOD_QP_IN_SET(typ, in, _opcode, _qpn, _uid);                   \
-               MLX5_SET(typ##_in, in, opt_param_mask, _opt_p);                \
-               memcpy(MLX5_ADDR_OF(typ##_in, in, qpc), _qpc,                  \
-                      MLX5_ST_SZ_BYTES(qpc));                                 \
-       } while (0)
-
-       switch (opcode) {
-       /* 2RST & 2ERR */
-       case MLX5_CMD_OP_2RST_QP:
-               if (MBOX_ALLOC(mbox, qp_2rst))
-                       return -ENOMEM;
-               MOD_QP_IN_SET(qp_2rst, mbox->in, opcode, qpn, uid);
-               break;
-       case MLX5_CMD_OP_2ERR_QP:
-               if (MBOX_ALLOC(mbox, qp_2err))
-                       return -ENOMEM;
-               MOD_QP_IN_SET(qp_2err, mbox->in, opcode, qpn, uid);
-               break;
-
-       /* MODIFY with QPC */
-       case MLX5_CMD_OP_RST2INIT_QP:
-               if (MBOX_ALLOC(mbox, rst2init_qp))
-                       return -ENOMEM;
-               MOD_QP_IN_SET_QPC(rst2init_qp, mbox->in, opcode, qpn,
-                                 opt_param_mask, qpc, uid);
-               break;
-       case MLX5_CMD_OP_INIT2RTR_QP:
-               if (MBOX_ALLOC(mbox, init2rtr_qp))
-                       return -ENOMEM;
-               MOD_QP_IN_SET_QPC(init2rtr_qp, mbox->in, opcode, qpn,
-                                 opt_param_mask, qpc, uid);
-               break;
-       case MLX5_CMD_OP_RTR2RTS_QP:
-               if (MBOX_ALLOC(mbox, rtr2rts_qp))
-                       return -ENOMEM;
-               MOD_QP_IN_SET_QPC(rtr2rts_qp, mbox->in, opcode, qpn,
-                                 opt_param_mask, qpc, uid);
-               break;
-       case MLX5_CMD_OP_RTS2RTS_QP:
-               if (MBOX_ALLOC(mbox, rts2rts_qp))
-                       return -ENOMEM;
-               MOD_QP_IN_SET_QPC(rts2rts_qp, mbox->in, opcode, qpn,
-                                 opt_param_mask, qpc, uid);
-               break;
-       case MLX5_CMD_OP_SQERR2RTS_QP:
-               if (MBOX_ALLOC(mbox, sqerr2rts_qp))
-                       return -ENOMEM;
-               MOD_QP_IN_SET_QPC(sqerr2rts_qp, mbox->in, opcode, qpn,
-                                 opt_param_mask, qpc, uid);
-               break;
-       case MLX5_CMD_OP_INIT2INIT_QP:
-               if (MBOX_ALLOC(mbox, init2init_qp))
-                       return -ENOMEM;
-               MOD_QP_IN_SET_QPC(init2init_qp, mbox->in, opcode, qpn,
-                                 opt_param_mask, qpc, uid);
-               break;
-       default:
-               mlx5_core_err(dev, "Unknown transition for modify QP: OP(0x%x) QPN(0x%x)\n",
-                             opcode, qpn);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-int mlx5_core_qp_modify(struct mlx5_core_dev *dev, u16 opcode,
-                       u32 opt_param_mask, void *qpc,
-                       struct mlx5_core_qp *qp)
-{
-       struct mbox_info mbox;
-       int err;
-
-       err = modify_qp_mbox_alloc(dev, opcode, qp->qpn,
-                                  opt_param_mask, qpc, &mbox, qp->uid);
-       if (err)
-               return err;
-
-       err = mlx5_cmd_exec(dev, mbox.in, mbox.inlen, mbox.out, mbox.outlen);
-       mbox_free(&mbox);
-       return err;
-}
-EXPORT_SYMBOL_GPL(mlx5_core_qp_modify);
-
-void mlx5_init_qp_table(struct mlx5_core_dev *dev)
-{
-       struct mlx5_qp_table *table = &dev->priv.qp_table;
-
-       memset(table, 0, sizeof(*table));
-       spin_lock_init(&table->lock);
-       INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
-       mlx5_qp_debugfs_init(dev);
-
-       table->nb.notifier_call = rsc_event_notifier;
-       mlx5_notifier_register(dev, &table->nb);
-}
-
-void mlx5_cleanup_qp_table(struct mlx5_core_dev *dev)
-{
-       struct mlx5_qp_table *table = &dev->priv.qp_table;
-
-       mlx5_notifier_unregister(dev, &table->nb);
-       mlx5_qp_debugfs_cleanup(dev);
-}
-
-int mlx5_core_qp_query(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp,
-                      u32 *out, int outlen)
-{
-       u32 in[MLX5_ST_SZ_DW(query_qp_in)] = {0};
-
-       MLX5_SET(query_qp_in, in, opcode, MLX5_CMD_OP_QUERY_QP);
-       MLX5_SET(query_qp_in, in, qpn, qp->qpn);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
-}
-EXPORT_SYMBOL_GPL(mlx5_core_qp_query);
-
-int mlx5_core_dct_query(struct mlx5_core_dev *dev, struct mlx5_core_dct *dct,
-                       u32 *out, int outlen)
-{
-       u32 in[MLX5_ST_SZ_DW(query_dct_in)] = {0};
-       struct mlx5_core_qp *qp = &dct->mqp;
-
-       MLX5_SET(query_dct_in, in, opcode, MLX5_CMD_OP_QUERY_DCT);
-       MLX5_SET(query_dct_in, in, dctn, qp->qpn);
-
-       return mlx5_cmd_exec(dev, (void *)&in, sizeof(in),
-                            (void *)out, outlen);
-}
-EXPORT_SYMBOL_GPL(mlx5_core_dct_query);
-
-int mlx5_core_xrcd_alloc(struct mlx5_core_dev *dev, u32 *xrcdn)
-{
-       u32 out[MLX5_ST_SZ_DW(alloc_xrcd_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(alloc_xrcd_in)]   = {0};
-       int err;
-
-       MLX5_SET(alloc_xrcd_in, in, opcode, MLX5_CMD_OP_ALLOC_XRCD);
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-       if (!err)
-               *xrcdn = MLX5_GET(alloc_xrcd_out, out, xrcd);
-       return err;
-}
-EXPORT_SYMBOL_GPL(mlx5_core_xrcd_alloc);
-
-int mlx5_core_xrcd_dealloc(struct mlx5_core_dev *dev, u32 xrcdn)
-{
-       u32 out[MLX5_ST_SZ_DW(dealloc_xrcd_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(dealloc_xrcd_in)]   = {0};
-
-       MLX5_SET(dealloc_xrcd_in, in, opcode, MLX5_CMD_OP_DEALLOC_XRCD);
-       MLX5_SET(dealloc_xrcd_in, in, xrcd, xrcdn);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-EXPORT_SYMBOL_GPL(mlx5_core_xrcd_dealloc);
-
-static void destroy_rq_tracked(struct mlx5_core_dev *dev, u32 rqn, u16 uid)
-{
-       u32 in[MLX5_ST_SZ_DW(destroy_rq_in)]   = {};
-       u32 out[MLX5_ST_SZ_DW(destroy_rq_out)] = {};
-
-       MLX5_SET(destroy_rq_in, in, opcode, MLX5_CMD_OP_DESTROY_RQ);
-       MLX5_SET(destroy_rq_in, in, rqn, rqn);
-       MLX5_SET(destroy_rq_in, in, uid, uid);
-       mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-
-int mlx5_core_create_rq_tracked(struct mlx5_core_dev *dev, u32 *in, int inlen,
-                               struct mlx5_core_qp *rq)
-{
-       int err;
-       u32 rqn;
-
-       err = mlx5_core_create_rq(dev, in, inlen, &rqn);
-       if (err)
-               return err;
-
-       rq->uid = MLX5_GET(create_rq_in, in, uid);
-       rq->qpn = rqn;
-       err = create_resource_common(dev, rq, MLX5_RES_RQ);
-       if (err)
-               goto err_destroy_rq;
-
-       return 0;
-
-err_destroy_rq:
-       destroy_rq_tracked(dev, rq->qpn, rq->uid);
-
-       return err;
-}
-EXPORT_SYMBOL(mlx5_core_create_rq_tracked);
-
-void mlx5_core_destroy_rq_tracked(struct mlx5_core_dev *dev,
-                                 struct mlx5_core_qp *rq)
-{
-       destroy_resource_common(dev, rq);
-       destroy_rq_tracked(dev, rq->qpn, rq->uid);
-}
-EXPORT_SYMBOL(mlx5_core_destroy_rq_tracked);
-
-static void destroy_sq_tracked(struct mlx5_core_dev *dev, u32 sqn, u16 uid)
-{
-       u32 in[MLX5_ST_SZ_DW(destroy_sq_in)]   = {};
-       u32 out[MLX5_ST_SZ_DW(destroy_sq_out)] = {};
-
-       MLX5_SET(destroy_sq_in, in, opcode, MLX5_CMD_OP_DESTROY_SQ);
-       MLX5_SET(destroy_sq_in, in, sqn, sqn);
-       MLX5_SET(destroy_sq_in, in, uid, uid);
-       mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-
-int mlx5_core_create_sq_tracked(struct mlx5_core_dev *dev, u32 *in, int inlen,
-                               struct mlx5_core_qp *sq)
-{
-       int err;
-       u32 sqn;
-
-       err = mlx5_core_create_sq(dev, in, inlen, &sqn);
-       if (err)
-               return err;
-
-       sq->uid = MLX5_GET(create_sq_in, in, uid);
-       sq->qpn = sqn;
-       err = create_resource_common(dev, sq, MLX5_RES_SQ);
-       if (err)
-               goto err_destroy_sq;
-
-       return 0;
-
-err_destroy_sq:
-       destroy_sq_tracked(dev, sq->qpn, sq->uid);
-
-       return err;
-}
-EXPORT_SYMBOL(mlx5_core_create_sq_tracked);
-
-void mlx5_core_destroy_sq_tracked(struct mlx5_core_dev *dev,
-                                 struct mlx5_core_qp *sq)
-{
-       destroy_resource_common(dev, sq);
-       destroy_sq_tracked(dev, sq->qpn, sq->uid);
-}
-EXPORT_SYMBOL(mlx5_core_destroy_sq_tracked);
-
-int mlx5_core_alloc_q_counter(struct mlx5_core_dev *dev, u16 *counter_id)
-{
-       u32 in[MLX5_ST_SZ_DW(alloc_q_counter_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(alloc_q_counter_out)] = {0};
-       int err;
-
-       MLX5_SET(alloc_q_counter_in, in, opcode, MLX5_CMD_OP_ALLOC_Q_COUNTER);
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-       if (!err)
-               *counter_id = MLX5_GET(alloc_q_counter_out, out,
-                                      counter_set_id);
-       return err;
-}
-EXPORT_SYMBOL_GPL(mlx5_core_alloc_q_counter);
-
-int mlx5_core_dealloc_q_counter(struct mlx5_core_dev *dev, u16 counter_id)
-{
-       u32 in[MLX5_ST_SZ_DW(dealloc_q_counter_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(dealloc_q_counter_out)] = {0};
-
-       MLX5_SET(dealloc_q_counter_in, in, opcode,
-                MLX5_CMD_OP_DEALLOC_Q_COUNTER);
-       MLX5_SET(dealloc_q_counter_in, in, counter_set_id, counter_id);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-}
-EXPORT_SYMBOL_GPL(mlx5_core_dealloc_q_counter);
-
-int mlx5_core_query_q_counter(struct mlx5_core_dev *dev, u16 counter_id,
-                             int reset, void *out, int out_size)
-{
-       u32 in[MLX5_ST_SZ_DW(query_q_counter_in)] = {0};
-
-       MLX5_SET(query_q_counter_in, in, opcode, MLX5_CMD_OP_QUERY_Q_COUNTER);
-       MLX5_SET(query_q_counter_in, in, clear, reset);
-       MLX5_SET(query_q_counter_in, in, counter_set_id, counter_id);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, out_size);
-}
-EXPORT_SYMBOL_GPL(mlx5_core_query_q_counter);
-
-struct mlx5_core_rsc_common *mlx5_core_res_hold(struct mlx5_core_dev *dev,
-                                               int res_num,
-                                               enum mlx5_res_type res_type)
-{
-       u32 rsn = res_num | (res_type << MLX5_USER_INDEX_LEN);
-       struct mlx5_qp_table *table = &dev->priv.qp_table;
-
-       return mlx5_get_rsc(table, rsn);
-}
-EXPORT_SYMBOL_GPL(mlx5_core_res_hold);
-
-void mlx5_core_res_put(struct mlx5_core_rsc_common *res)
-{
-       mlx5_core_put_rsc(res);
-}
-EXPORT_SYMBOL_GPL(mlx5_core_res_put);
index f3b29d9..99039c4 100644 (file)
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mlx5/driver.h>
-#include <linux/mlx5/cmd.h>
 #include "mlx5_core.h"
 
 /* Scheduling element fw management */
 int mlx5_create_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
                                       void *ctx, u32 *element_id)
 {
-       u32 in[MLX5_ST_SZ_DW(create_scheduling_element_in)]  = {0};
-       u32 out[MLX5_ST_SZ_DW(create_scheduling_element_in)] = {0};
+       u32 out[MLX5_ST_SZ_DW(create_scheduling_element_in)] = {};
+       u32 in[MLX5_ST_SZ_DW(create_scheduling_element_in)] = {};
        void *schedc;
        int err;
 
@@ -53,7 +52,7 @@ int mlx5_create_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
                 hierarchy);
        memcpy(schedc, ctx, MLX5_ST_SZ_BYTES(scheduling_context));
 
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(dev, create_scheduling_element, in, out);
        if (err)
                return err;
 
@@ -66,8 +65,7 @@ int mlx5_modify_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
                                       void *ctx, u32 element_id,
                                       u32 modify_bitmask)
 {
-       u32 in[MLX5_ST_SZ_DW(modify_scheduling_element_in)]  = {0};
-       u32 out[MLX5_ST_SZ_DW(modify_scheduling_element_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(modify_scheduling_element_in)] = {};
        void *schedc;
 
        schedc = MLX5_ADDR_OF(modify_scheduling_element_in, in,
@@ -82,14 +80,13 @@ int mlx5_modify_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
                 hierarchy);
        memcpy(schedc, ctx, MLX5_ST_SZ_BYTES(scheduling_context));
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, modify_scheduling_element, in);
 }
 
 int mlx5_destroy_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
                                        u32 element_id)
 {
-       u32 in[MLX5_ST_SZ_DW(destroy_scheduling_element_in)]  = {0};
-       u32 out[MLX5_ST_SZ_DW(destroy_scheduling_element_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_scheduling_element_in)] = {};
 
        MLX5_SET(destroy_scheduling_element_in, in, opcode,
                 MLX5_CMD_OP_DESTROY_SCHEDULING_ELEMENT);
@@ -98,7 +95,7 @@ int mlx5_destroy_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy,
        MLX5_SET(destroy_scheduling_element_in, in, scheduling_hierarchy,
                 hierarchy);
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, destroy_scheduling_element, in);
 }
 
 static bool mlx5_rl_are_equal_raw(struct mlx5_rl_entry *entry, void *rl_in,
@@ -145,8 +142,7 @@ static struct mlx5_rl_entry *find_rl_entry(struct mlx5_rl_table *table,
 static int mlx5_set_pp_rate_limit_cmd(struct mlx5_core_dev *dev,
                                      struct mlx5_rl_entry *entry, bool set)
 {
-       u32 in[MLX5_ST_SZ_DW(set_pp_rate_limit_in)]   = {};
-       u32 out[MLX5_ST_SZ_DW(set_pp_rate_limit_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(set_pp_rate_limit_in)] = {};
        void *pp_context;
 
        pp_context = MLX5_ADDR_OF(set_pp_rate_limit_in, in, ctx);
@@ -156,7 +152,7 @@ static int mlx5_set_pp_rate_limit_cmd(struct mlx5_core_dev *dev,
        MLX5_SET(set_pp_rate_limit_in, in, rate_limit_index, entry->index);
        if (set)
                memcpy(pp_context, entry->rl_raw, sizeof(entry->rl_raw));
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, set_pp_rate_limit, in);
 }
 
 bool mlx5_rl_is_in_range(struct mlx5_core_dev *dev, u32 rate)
index 461b393..6bd34b2 100644 (file)
@@ -18,7 +18,7 @@ int mlx5dr_cmd_query_esw_vport_context(struct mlx5_core_dev *mdev,
        MLX5_SET(query_esw_vport_context_in, in, other_vport, other_vport);
        MLX5_SET(query_esw_vport_context_in, in, vport_number, vport_number);
 
-       err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(mdev, query_esw_vport_context, in, out);
        if (err)
                return err;
 
@@ -51,7 +51,7 @@ int mlx5dr_cmd_query_gvmi(struct mlx5_core_dev *mdev, bool other_vport,
                 MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE << 1 |
                 HCA_CAP_OPMOD_GET_CUR);
 
-       err = mlx5_cmd_exec(mdev, in, sizeof(in), out, out_size);
+       err = mlx5_cmd_exec_inout(mdev, query_hca_cap, in, out);
        if (err) {
                kfree(out);
                return err;
@@ -141,7 +141,7 @@ int mlx5dr_cmd_query_flow_table(struct mlx5_core_dev *dev,
        MLX5_SET(query_flow_table_in, in, table_type, type);
        MLX5_SET(query_flow_table_in, in, table_id, table_id);
 
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(dev, query_flow_table, in, out);
        if (err)
                return err;
 
@@ -158,12 +158,11 @@ int mlx5dr_cmd_query_flow_table(struct mlx5_core_dev *dev,
 
 int mlx5dr_cmd_sync_steering(struct mlx5_core_dev *mdev)
 {
-       u32 out[MLX5_ST_SZ_DW(sync_steering_out)] = {};
        u32 in[MLX5_ST_SZ_DW(sync_steering_in)] = {};
 
        MLX5_SET(sync_steering_in, in, opcode, MLX5_CMD_OP_SYNC_STEERING);
 
-       return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(mdev, sync_steering, in);
 }
 
 int mlx5dr_cmd_set_fte_modify_and_vport(struct mlx5_core_dev *mdev,
@@ -214,14 +213,13 @@ int mlx5dr_cmd_del_flow_table_entry(struct mlx5_core_dev *mdev,
                                    u32 table_type,
                                    u32 table_id)
 {
-       u32 out[MLX5_ST_SZ_DW(delete_fte_out)] = {};
        u32 in[MLX5_ST_SZ_DW(delete_fte_in)] = {};
 
        MLX5_SET(delete_fte_in, in, opcode, MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY);
        MLX5_SET(delete_fte_in, in, table_type, table_type);
        MLX5_SET(delete_fte_in, in, table_id, table_id);
 
-       return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(mdev, delete_fte, in);
 }
 
 int mlx5dr_cmd_alloc_modify_header(struct mlx5_core_dev *mdev,
@@ -263,7 +261,6 @@ out:
 int mlx5dr_cmd_dealloc_modify_header(struct mlx5_core_dev *mdev,
                                     u32 modify_header_id)
 {
-       u32 out[MLX5_ST_SZ_DW(dealloc_modify_header_context_out)] = {};
        u32 in[MLX5_ST_SZ_DW(dealloc_modify_header_context_in)] = {};
 
        MLX5_SET(dealloc_modify_header_context_in, in, opcode,
@@ -271,7 +268,7 @@ int mlx5dr_cmd_dealloc_modify_header(struct mlx5_core_dev *mdev,
        MLX5_SET(dealloc_modify_header_context_in, in, modify_header_id,
                 modify_header_id);
 
-       return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(mdev, dealloc_modify_header_context, in);
 }
 
 int mlx5dr_cmd_create_empty_flow_group(struct mlx5_core_dev *mdev,
@@ -292,7 +289,7 @@ int mlx5dr_cmd_create_empty_flow_group(struct mlx5_core_dev *mdev,
        MLX5_SET(create_flow_group_in, in, table_type, table_type);
        MLX5_SET(create_flow_group_in, in, table_id, table_id);
 
-       err = mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out));
+       err = mlx5_cmd_exec_inout(mdev, create_flow_group, in, out);
        if (err)
                goto out;
 
@@ -309,14 +306,14 @@ int mlx5dr_cmd_destroy_flow_group(struct mlx5_core_dev *mdev,
                                  u32 group_id)
 {
        u32 in[MLX5_ST_SZ_DW(destroy_flow_group_in)] = {};
-       u32 out[MLX5_ST_SZ_DW(destroy_flow_group_out)] = {};
 
-       MLX5_SET(create_flow_group_in, in, opcode, MLX5_CMD_OP_DESTROY_FLOW_GROUP);
+       MLX5_SET(destroy_flow_group_in, in, opcode,
+                MLX5_CMD_OP_DESTROY_FLOW_GROUP);
        MLX5_SET(destroy_flow_group_in, in, table_type, table_type);
        MLX5_SET(destroy_flow_group_in, in, table_id, table_id);
        MLX5_SET(destroy_flow_group_in, in, group_id, group_id);
 
-       return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(mdev, destroy_flow_group, in);
 }
 
 int mlx5dr_cmd_create_flow_table(struct mlx5_core_dev *mdev,
@@ -360,7 +357,7 @@ int mlx5dr_cmd_create_flow_table(struct mlx5_core_dev *mdev,
        MLX5_SET(create_flow_table_in, in, flow_table_context.reformat_en,
                 attr->reformat_en);
 
-       err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(mdev, create_flow_table, in, out);
        if (err)
                return err;
 
@@ -379,7 +376,6 @@ int mlx5dr_cmd_destroy_flow_table(struct mlx5_core_dev *mdev,
                                  u32 table_id,
                                  u32 table_type)
 {
-       u32 out[MLX5_ST_SZ_DW(destroy_flow_table_out)] = {};
        u32 in[MLX5_ST_SZ_DW(destroy_flow_table_in)] = {};
 
        MLX5_SET(destroy_flow_table_in, in, opcode,
@@ -387,7 +383,7 @@ int mlx5dr_cmd_destroy_flow_table(struct mlx5_core_dev *mdev,
        MLX5_SET(destroy_flow_table_in, in, table_type, table_type);
        MLX5_SET(destroy_flow_table_in, in, table_id, table_id);
 
-       return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(mdev, destroy_flow_table, in);
 }
 
 int mlx5dr_cmd_create_reformat_ctx(struct mlx5_core_dev *mdev,
@@ -434,7 +430,6 @@ int mlx5dr_cmd_create_reformat_ctx(struct mlx5_core_dev *mdev,
 void mlx5dr_cmd_destroy_reformat_ctx(struct mlx5_core_dev *mdev,
                                     u32 reformat_id)
 {
-       u32 out[MLX5_ST_SZ_DW(dealloc_packet_reformat_context_out)] = {};
        u32 in[MLX5_ST_SZ_DW(dealloc_packet_reformat_context_in)] = {};
 
        MLX5_SET(dealloc_packet_reformat_context_in, in, opcode,
@@ -442,7 +437,7 @@ void mlx5dr_cmd_destroy_reformat_ctx(struct mlx5_core_dev *mdev,
        MLX5_SET(dealloc_packet_reformat_context_in, in, packet_reformat_id,
                 reformat_id);
 
-       mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(mdev, dealloc_packet_reformat_context, in);
 }
 
 int mlx5dr_cmd_query_gid(struct mlx5_core_dev *mdev, u8 vhca_port_num,
@@ -458,7 +453,7 @@ int mlx5dr_cmd_query_gid(struct mlx5_core_dev *mdev, u8 vhca_port_num,
        MLX5_SET(query_roce_address_in, in, roce_address_index, index);
        MLX5_SET(query_roce_address_in, in, vhca_port_num, vhca_port_num);
 
-       err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(mdev, query_roce_address, in, out);
        if (err)
                return err;
 
index 30d2d73..cc33515 100644 (file)
@@ -95,13 +95,12 @@ static int dr_icm_create_dm_mkey(struct mlx5_core_dev *mdev,
 }
 
 static struct mlx5dr_icm_mr *
-dr_icm_pool_mr_create(struct mlx5dr_icm_pool *pool,
-                     enum mlx5_sw_icm_type type,
-                     size_t align_base)
+dr_icm_pool_mr_create(struct mlx5dr_icm_pool *pool)
 {
        struct mlx5_core_dev *mdev = pool->dmn->mdev;
+       enum mlx5_sw_icm_type dm_type;
        struct mlx5dr_icm_mr *icm_mr;
-       size_t align_diff;
+       size_t log_align_base;
        int err;
 
        icm_mr = kvzalloc(sizeof(*icm_mr), GFP_KERNEL);
@@ -111,14 +110,22 @@ dr_icm_pool_mr_create(struct mlx5dr_icm_pool *pool,
        icm_mr->pool = pool;
        INIT_LIST_HEAD(&icm_mr->mr_list);
 
-       icm_mr->dm.type = type;
-
-       /* 2^log_biggest_table * entry-size * double-for-alignment */
        icm_mr->dm.length = mlx5dr_icm_pool_chunk_size_to_byte(pool->max_log_chunk_sz,
-                                                              pool->icm_type) * 2;
+                                                              pool->icm_type);
+
+       if (pool->icm_type == DR_ICM_TYPE_STE) {
+               dm_type = MLX5_SW_ICM_TYPE_STEERING;
+               log_align_base = ilog2(icm_mr->dm.length);
+       } else {
+               dm_type = MLX5_SW_ICM_TYPE_HEADER_MODIFY;
+               /* Align base is 64B */
+               log_align_base = ilog2(DR_ICM_MODIFY_HDR_ALIGN_BASE);
+       }
+       icm_mr->dm.type = dm_type;
 
-       err = mlx5_dm_sw_icm_alloc(mdev, icm_mr->dm.type, icm_mr->dm.length, 0,
-                                  &icm_mr->dm.addr, &icm_mr->dm.obj_id);
+       err = mlx5_dm_sw_icm_alloc(mdev, icm_mr->dm.type, icm_mr->dm.length,
+                                  log_align_base, 0, &icm_mr->dm.addr,
+                                  &icm_mr->dm.obj_id);
        if (err) {
                mlx5dr_err(pool->dmn, "Failed to allocate SW ICM memory, err (%d)\n", err);
                goto free_icm_mr;
@@ -137,15 +144,18 @@ dr_icm_pool_mr_create(struct mlx5dr_icm_pool *pool,
 
        icm_mr->icm_start_addr = icm_mr->dm.addr;
 
-       /* align_base is always a power of 2 */
-       align_diff = icm_mr->icm_start_addr & (align_base - 1);
-       if (align_diff)
-               icm_mr->used_length = align_base - align_diff;
+       if (icm_mr->icm_start_addr & (BIT(log_align_base) - 1)) {
+               mlx5dr_err(pool->dmn, "Failed to get Aligned ICM mem (asked: %zu)\n",
+                          log_align_base);
+               goto free_mkey;
+       }
 
        list_add_tail(&icm_mr->mr_list, &pool->icm_mr_list);
 
        return icm_mr;
 
+free_mkey:
+       mlx5_core_destroy_mkey(mdev, &icm_mr->mkey);
 free_dm:
        mlx5_dm_sw_icm_dealloc(mdev, icm_mr->dm.type, icm_mr->dm.length, 0,
                               icm_mr->dm.addr, icm_mr->dm.obj_id);
@@ -200,24 +210,11 @@ static int dr_icm_chunks_create(struct mlx5dr_icm_bucket *bucket)
        struct mlx5dr_icm_pool *pool = bucket->pool;
        struct mlx5dr_icm_mr *icm_mr = NULL;
        struct mlx5dr_icm_chunk *chunk;
-       enum mlx5_sw_icm_type dm_type;
-       size_t align_base;
        int i, err = 0;
 
        mr_req_size = bucket->num_of_entries * bucket->entry_size;
        mr_row_size = mlx5dr_icm_pool_chunk_size_to_byte(pool->max_log_chunk_sz,
                                                         pool->icm_type);
-
-       if (pool->icm_type == DR_ICM_TYPE_STE) {
-               dm_type = MLX5_SW_ICM_TYPE_STEERING;
-               /* Align base is the biggest chunk size / row size */
-               align_base = mr_row_size;
-       } else {
-               dm_type = MLX5_SW_ICM_TYPE_HEADER_MODIFY;
-               /* Align base is 64B */
-               align_base = DR_ICM_MODIFY_HDR_ALIGN_BASE;
-       }
-
        mutex_lock(&pool->mr_mutex);
        if (!list_empty(&pool->icm_mr_list)) {
                icm_mr = list_last_entry(&pool->icm_mr_list,
@@ -228,7 +225,7 @@ static int dr_icm_chunks_create(struct mlx5dr_icm_bucket *bucket)
        }
 
        if (!icm_mr || mr_free_size < mr_row_size) {
-               icm_mr = dr_icm_pool_mr_create(pool, dm_type, align_base);
+               icm_mr = dr_icm_pool_mr_create(pool);
                if (!icm_mr) {
                        err = -ENOMEM;
                        goto out_err;
index c0ab9cf..b8d97d4 100644 (file)
@@ -100,14 +100,10 @@ static int dr_poll_cq(struct mlx5dr_cq *dr_cq, int ne)
        return err == CQ_POLL_ERR ? err : npolled;
 }
 
-static void dr_qp_event(struct mlx5_core_qp *mqp, int event)
-{
-       pr_info("DR QP event %u on QP #%u\n", event, mqp->qpn);
-}
-
 static struct mlx5dr_qp *dr_create_rc_qp(struct mlx5_core_dev *mdev,
                                         struct dr_qp_init_attr *attr)
 {
+       u32 out[MLX5_ST_SZ_DW(create_qp_out)] = {};
        u32 temp_qpc[MLX5_ST_SZ_DW(qpc)] = {};
        struct mlx5_wq_param wqp;
        struct mlx5dr_qp *dr_qp;
@@ -180,14 +176,12 @@ static struct mlx5dr_qp *dr_create_rc_qp(struct mlx5_core_dev *mdev,
                                  (__be64 *)MLX5_ADDR_OF(create_qp_in,
                                                         in, pas));
 
-       err = mlx5_core_create_qp(mdev, &dr_qp->mqp, in, inlen);
+       MLX5_SET(create_qp_in, in, opcode, MLX5_CMD_OP_CREATE_QP);
+       err = mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out));
+       dr_qp->qpn = MLX5_GET(create_qp_out, out, qpn);
        kfree(in);
-
-       if (err) {
-               mlx5_core_warn(mdev, " Can't create QP\n");
+       if (err)
                goto err_in;
-       }
-       dr_qp->mqp.event = dr_qp_event;
        dr_qp->uar = attr->uar;
 
        return dr_qp;
@@ -204,7 +198,12 @@ err_wq:
 static void dr_destroy_qp(struct mlx5_core_dev *mdev,
                          struct mlx5dr_qp *dr_qp)
 {
-       mlx5_core_destroy_qp(mdev, &dr_qp->mqp);
+       u32 in[MLX5_ST_SZ_DW(destroy_qp_in)] = {};
+
+       MLX5_SET(destroy_qp_in, in, opcode, MLX5_CMD_OP_DESTROY_QP);
+       MLX5_SET(destroy_qp_in, in, qpn, dr_qp->qpn);
+       mlx5_cmd_exec_in(mdev, destroy_qp, in);
+
        kfree(dr_qp->sq.wqe_head);
        mlx5_wq_destroy(&dr_qp->wq_ctrl);
        kfree(dr_qp);
@@ -242,7 +241,7 @@ static void dr_rdma_segments(struct mlx5dr_qp *dr_qp, u64 remote_addr,
                MLX5_WQE_CTRL_CQ_UPDATE : 0;
        wq_ctrl->opmod_idx_opcode = cpu_to_be32(((dr_qp->sq.pc & 0xffff) << 8) |
                                                opcode);
-       wq_ctrl->qpn_ds = cpu_to_be32(size | dr_qp->mqp.qpn << 8);
+       wq_ctrl->qpn_ds = cpu_to_be32(size | dr_qp->qpn << 8);
        wq_raddr = (void *)(wq_ctrl + 1);
        wq_raddr->raddr = cpu_to_be64(remote_addr);
        wq_raddr->rkey = cpu_to_be32(rkey);
@@ -585,8 +584,10 @@ static int dr_modify_qp_rst2init(struct mlx5_core_dev *mdev,
        MLX5_SET(qpc, qpc, rre, 1);
        MLX5_SET(qpc, qpc, rwe, 1);
 
-       return mlx5_core_qp_modify(mdev, MLX5_CMD_OP_RST2INIT_QP, 0, qpc,
-                                  &dr_qp->mqp);
+       MLX5_SET(rst2init_qp_in, in, opcode, MLX5_CMD_OP_RST2INIT_QP);
+       MLX5_SET(rst2init_qp_in, in, qpn, dr_qp->qpn);
+
+       return mlx5_cmd_exec_in(mdev, rst2init_qp, in);
 }
 
 static int dr_cmd_modify_qp_rtr2rts(struct mlx5_core_dev *mdev,
@@ -598,14 +599,15 @@ static int dr_cmd_modify_qp_rtr2rts(struct mlx5_core_dev *mdev,
 
        qpc  = MLX5_ADDR_OF(rtr2rts_qp_in, in, qpc);
 
-       MLX5_SET(rtr2rts_qp_in, in, qpn, dr_qp->mqp.qpn);
+       MLX5_SET(rtr2rts_qp_in, in, qpn, dr_qp->qpn);
 
-       MLX5_SET(qpc, qpc, log_ack_req_freq, 0);
        MLX5_SET(qpc, qpc, retry_count, attr->retry_cnt);
        MLX5_SET(qpc, qpc, rnr_retry, attr->rnr_retry);
 
-       return mlx5_core_qp_modify(mdev, MLX5_CMD_OP_RTR2RTS_QP, 0, qpc,
-                                  &dr_qp->mqp);
+       MLX5_SET(rtr2rts_qp_in, in, opcode, MLX5_CMD_OP_RTR2RTS_QP);
+       MLX5_SET(rtr2rts_qp_in, in, qpn, dr_qp->qpn);
+
+       return mlx5_cmd_exec_in(mdev, rtr2rts_qp, in);
 }
 
 static int dr_cmd_modify_qp_init2rtr(struct mlx5_core_dev *mdev,
@@ -617,7 +619,7 @@ static int dr_cmd_modify_qp_init2rtr(struct mlx5_core_dev *mdev,
 
        qpc = MLX5_ADDR_OF(init2rtr_qp_in, in, qpc);
 
-       MLX5_SET(init2rtr_qp_in, in, qpn, dr_qp->mqp.qpn);
+       MLX5_SET(init2rtr_qp_in, in, qpn, dr_qp->qpn);
 
        MLX5_SET(qpc, qpc, mtu, attr->mtu);
        MLX5_SET(qpc, qpc, log_msg_max, DR_CHUNK_SIZE_MAX - 1);
@@ -636,8 +638,10 @@ static int dr_cmd_modify_qp_init2rtr(struct mlx5_core_dev *mdev,
        MLX5_SET(qpc, qpc, primary_address_path.vhca_port_num, attr->port_num);
        MLX5_SET(qpc, qpc, min_rnr_nak, 1);
 
-       return mlx5_core_qp_modify(mdev, MLX5_CMD_OP_INIT2RTR_QP, 0, qpc,
-                                  &dr_qp->mqp);
+       MLX5_SET(init2rtr_qp_in, in, opcode, MLX5_CMD_OP_INIT2RTR_QP);
+       MLX5_SET(init2rtr_qp_in, in, qpn, dr_qp->qpn);
+
+       return mlx5_cmd_exec_in(mdev, init2rtr_qp, in);
 }
 
 static int dr_prepare_qp_to_rts(struct mlx5dr_domain *dmn)
@@ -663,7 +667,7 @@ static int dr_prepare_qp_to_rts(struct mlx5dr_domain *dmn)
                return ret;
 
        rtr_attr.mtu            = mtu;
-       rtr_attr.qp_num         = dr_qp->mqp.qpn;
+       rtr_attr.qp_num         = dr_qp->qpn;
        rtr_attr.min_rnr_timer  = 12;
        rtr_attr.port_num       = port;
        rtr_attr.sgid_index     = gid_index;
@@ -689,10 +693,10 @@ static int dr_prepare_qp_to_rts(struct mlx5dr_domain *dmn)
        return 0;
 }
 
-static void dr_cq_event(struct mlx5_core_cq *mcq,
-                       enum mlx5_event event)
+static void dr_cq_complete(struct mlx5_core_cq *mcq,
+                          struct mlx5_eqe *eqe)
 {
-       pr_info("CQ event %u on CQ #%u\n", event, mcq->cqn);
+       pr_err("CQ completion CQ: #%u\n", mcq->cqn);
 }
 
 static struct mlx5dr_cq *dr_create_cq(struct mlx5_core_dev *mdev,
@@ -755,7 +759,7 @@ static struct mlx5dr_cq *dr_create_cq(struct mlx5_core_dev *mdev,
        pas = (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas);
        mlx5_fill_page_frag_array(&cq->wq_ctrl.buf, pas);
 
-       cq->mcq.event = dr_cq_event;
+       cq->mcq.comp  = dr_cq_complete;
 
        err = mlx5_core_create_cq(mdev, &cq->mcq, in, inlen, out, sizeof(out));
        kvfree(in);
@@ -767,7 +771,12 @@ static struct mlx5dr_cq *dr_create_cq(struct mlx5_core_dev *mdev,
        cq->mcq.set_ci_db = cq->wq_ctrl.db.db;
        cq->mcq.arm_db = cq->wq_ctrl.db.db + 1;
        *cq->mcq.set_ci_db = 0;
-       *cq->mcq.arm_db = 0;
+
+       /* set no-zero value, in order to avoid the HW to run db-recovery on
+        * CQ that used in polling mode.
+        */
+       *cq->mcq.arm_db = cpu_to_be32(2 << 28);
+
        cq->mcq.vector = 0;
        cq->mcq.irqn = irqn;
        cq->mcq.uar = uar;
index 3fa7399..9847832 100644 (file)
@@ -990,7 +990,7 @@ struct mlx5dr_qp {
        struct mlx5_wq_qp wq;
        struct mlx5_uars_page *uar;
        struct mlx5_wq_ctrl wq_ctrl;
-       struct mlx5_core_qp mqp;
+       u32 qpn;
        struct {
                unsigned int pc;
                unsigned int cc;
index 3b3f5b9..8887b24 100644 (file)
@@ -576,7 +576,7 @@ static int mlx5_cmd_dr_modify_header_alloc(struct mlx5_flow_root_namespace *ns,
        struct mlx5dr_action *action;
        size_t actions_sz;
 
-       actions_sz = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto) *
+       actions_sz = MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto) *
                num_actions;
        action = mlx5dr_action_create_modify_header(dr_domain, 0,
                                                    actions_sz,
index b106850..01cc00a 100644 (file)
 
 int mlx5_core_alloc_transport_domain(struct mlx5_core_dev *dev, u32 *tdn)
 {
-       u32 in[MLX5_ST_SZ_DW(alloc_transport_domain_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(alloc_transport_domain_out)] = {0};
+       u32 out[MLX5_ST_SZ_DW(alloc_transport_domain_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(alloc_transport_domain_in)] = {};
        int err;
 
        MLX5_SET(alloc_transport_domain_in, in, opcode,
                 MLX5_CMD_OP_ALLOC_TRANSPORT_DOMAIN);
 
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(dev, alloc_transport_domain, in, out);
        if (!err)
                *tdn = MLX5_GET(alloc_transport_domain_out, out,
                                transport_domain);
@@ -54,19 +54,18 @@ EXPORT_SYMBOL(mlx5_core_alloc_transport_domain);
 
 void mlx5_core_dealloc_transport_domain(struct mlx5_core_dev *dev, u32 tdn)
 {
-       u32 in[MLX5_ST_SZ_DW(dealloc_transport_domain_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(dealloc_transport_domain_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(dealloc_transport_domain_in)] = {};
 
        MLX5_SET(dealloc_transport_domain_in, in, opcode,
                 MLX5_CMD_OP_DEALLOC_TRANSPORT_DOMAIN);
        MLX5_SET(dealloc_transport_domain_in, in, transport_domain, tdn);
-       mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(dev, dealloc_transport_domain, in);
 }
 EXPORT_SYMBOL(mlx5_core_dealloc_transport_domain);
 
 int mlx5_core_create_rq(struct mlx5_core_dev *dev, u32 *in, int inlen, u32 *rqn)
 {
-       u32 out[MLX5_ST_SZ_DW(create_rq_out)] = {0};
+       u32 out[MLX5_ST_SZ_DW(create_rq_out)] = {};
        int err;
 
        MLX5_SET(create_rq_in, in, opcode, MLX5_CMD_OP_CREATE_RQ);
@@ -78,44 +77,39 @@ int mlx5_core_create_rq(struct mlx5_core_dev *dev, u32 *in, int inlen, u32 *rqn)
 }
 EXPORT_SYMBOL(mlx5_core_create_rq);
 
-int mlx5_core_modify_rq(struct mlx5_core_dev *dev, u32 rqn, u32 *in, int inlen)
+int mlx5_core_modify_rq(struct mlx5_core_dev *dev, u32 rqn, u32 *in)
 {
-       u32 out[MLX5_ST_SZ_DW(modify_rq_out)];
-
        MLX5_SET(modify_rq_in, in, rqn, rqn);
        MLX5_SET(modify_rq_in, in, opcode, MLX5_CMD_OP_MODIFY_RQ);
 
-       memset(out, 0, sizeof(out));
-       return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, modify_rq, in);
 }
 EXPORT_SYMBOL(mlx5_core_modify_rq);
 
 void mlx5_core_destroy_rq(struct mlx5_core_dev *dev, u32 rqn)
 {
-       u32 in[MLX5_ST_SZ_DW(destroy_rq_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(destroy_rq_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_rq_in)] = {};
 
        MLX5_SET(destroy_rq_in, in, opcode, MLX5_CMD_OP_DESTROY_RQ);
        MLX5_SET(destroy_rq_in, in, rqn, rqn);
-       mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(dev, destroy_rq, in);
 }
 EXPORT_SYMBOL(mlx5_core_destroy_rq);
 
 int mlx5_core_query_rq(struct mlx5_core_dev *dev, u32 rqn, u32 *out)
 {
-       u32 in[MLX5_ST_SZ_DW(query_rq_in)] = {0};
-       int outlen = MLX5_ST_SZ_BYTES(query_rq_out);
+       u32 in[MLX5_ST_SZ_DW(query_rq_in)] = {};
 
        MLX5_SET(query_rq_in, in, opcode, MLX5_CMD_OP_QUERY_RQ);
        MLX5_SET(query_rq_in, in, rqn, rqn);
 
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
+       return mlx5_cmd_exec_inout(dev, query_rq, in, out);
 }
 EXPORT_SYMBOL(mlx5_core_query_rq);
 
 int mlx5_core_create_sq(struct mlx5_core_dev *dev, u32 *in, int inlen, u32 *sqn)
 {
-       u32 out[MLX5_ST_SZ_DW(create_sq_out)] = {0};
+       u32 out[MLX5_ST_SZ_DW(create_sq_out)] = {};
        int err;
 
        MLX5_SET(create_sq_in, in, opcode, MLX5_CMD_OP_CREATE_SQ);
@@ -126,34 +120,30 @@ int mlx5_core_create_sq(struct mlx5_core_dev *dev, u32 *in, int inlen, u32 *sqn)
        return err;
 }
 
-int mlx5_core_modify_sq(struct mlx5_core_dev *dev, u32 sqn, u32 *in, int inlen)
+int mlx5_core_modify_sq(struct mlx5_core_dev *dev, u32 sqn, u32 *in)
 {
-       u32 out[MLX5_ST_SZ_DW(modify_sq_out)] = {0};
-
        MLX5_SET(modify_sq_in, in, sqn, sqn);
        MLX5_SET(modify_sq_in, in, opcode, MLX5_CMD_OP_MODIFY_SQ);
-       return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, modify_sq, in);
 }
 EXPORT_SYMBOL(mlx5_core_modify_sq);
 
 void mlx5_core_destroy_sq(struct mlx5_core_dev *dev, u32 sqn)
 {
-       u32 in[MLX5_ST_SZ_DW(destroy_sq_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(destroy_sq_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_sq_in)] = {};
 
        MLX5_SET(destroy_sq_in, in, opcode, MLX5_CMD_OP_DESTROY_SQ);
        MLX5_SET(destroy_sq_in, in, sqn, sqn);
-       mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(dev, destroy_sq, in);
 }
 
 int mlx5_core_query_sq(struct mlx5_core_dev *dev, u32 sqn, u32 *out)
 {
-       u32 in[MLX5_ST_SZ_DW(query_sq_in)] = {0};
-       int outlen = MLX5_ST_SZ_BYTES(query_sq_out);
+       u32 in[MLX5_ST_SZ_DW(query_sq_in)] = {};
 
        MLX5_SET(query_sq_in, in, opcode, MLX5_CMD_OP_QUERY_SQ);
        MLX5_SET(query_sq_in, in, sqn, sqn);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
+       return mlx5_cmd_exec_inout(dev, query_sq, in, out);
 }
 EXPORT_SYMBOL(mlx5_core_query_sq);
 
@@ -182,24 +172,13 @@ out:
 }
 EXPORT_SYMBOL_GPL(mlx5_core_query_sq_state);
 
-int mlx5_core_create_tir_out(struct mlx5_core_dev *dev,
-                            u32 *in, int inlen,
-                            u32 *out, int outlen)
-{
-       MLX5_SET(create_tir_in, in, opcode, MLX5_CMD_OP_CREATE_TIR);
-
-       return mlx5_cmd_exec(dev, in, inlen, out, outlen);
-}
-EXPORT_SYMBOL(mlx5_core_create_tir_out);
-
-int mlx5_core_create_tir(struct mlx5_core_dev *dev, u32 *in, int inlen,
-                        u32 *tirn)
+int mlx5_core_create_tir(struct mlx5_core_dev *dev, u32 *in, u32 *tirn)
 {
        u32 out[MLX5_ST_SZ_DW(create_tir_out)] = {};
        int err;
 
-       err = mlx5_core_create_tir_out(dev, in, inlen,
-                                      out, sizeof(out));
+       MLX5_SET(create_tir_in, in, opcode, MLX5_CMD_OP_CREATE_TIR);
+       err = mlx5_cmd_exec_inout(dev, create_tir, in, out);
        if (!err)
                *tirn = MLX5_GET(create_tir_out, out, tirn);
 
@@ -207,35 +186,30 @@ int mlx5_core_create_tir(struct mlx5_core_dev *dev, u32 *in, int inlen,
 }
 EXPORT_SYMBOL(mlx5_core_create_tir);
 
-int mlx5_core_modify_tir(struct mlx5_core_dev *dev, u32 tirn, u32 *in,
-                        int inlen)
+int mlx5_core_modify_tir(struct mlx5_core_dev *dev, u32 tirn, u32 *in)
 {
-       u32 out[MLX5_ST_SZ_DW(modify_tir_out)] = {0};
-
        MLX5_SET(modify_tir_in, in, tirn, tirn);
        MLX5_SET(modify_tir_in, in, opcode, MLX5_CMD_OP_MODIFY_TIR);
-       return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, modify_tir, in);
 }
 
 void mlx5_core_destroy_tir(struct mlx5_core_dev *dev, u32 tirn)
 {
-       u32 in[MLX5_ST_SZ_DW(destroy_tir_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(destroy_tir_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_tir_in)] = {};
 
        MLX5_SET(destroy_tir_in, in, opcode, MLX5_CMD_OP_DESTROY_TIR);
        MLX5_SET(destroy_tir_in, in, tirn, tirn);
-       mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(dev, destroy_tir, in);
 }
 EXPORT_SYMBOL(mlx5_core_destroy_tir);
 
-int mlx5_core_create_tis(struct mlx5_core_dev *dev, u32 *in, int inlen,
-                        u32 *tisn)
+int mlx5_core_create_tis(struct mlx5_core_dev *dev, u32 *in, u32 *tisn)
 {
-       u32 out[MLX5_ST_SZ_DW(create_tis_out)] = {0};
+       u32 out[MLX5_ST_SZ_DW(create_tis_out)] = {};
        int err;
 
        MLX5_SET(create_tis_in, in, opcode, MLX5_CMD_OP_CREATE_TIS);
-       err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
+       err = mlx5_cmd_exec_inout(dev, create_tis, in, out);
        if (!err)
                *tisn = MLX5_GET(create_tis_out, out, tisn);
 
@@ -243,33 +217,29 @@ int mlx5_core_create_tis(struct mlx5_core_dev *dev, u32 *in, int inlen,
 }
 EXPORT_SYMBOL(mlx5_core_create_tis);
 
-int mlx5_core_modify_tis(struct mlx5_core_dev *dev, u32 tisn, u32 *in,
-                        int inlen)
+int mlx5_core_modify_tis(struct mlx5_core_dev *dev, u32 tisn, u32 *in)
 {
-       u32 out[MLX5_ST_SZ_DW(modify_tis_out)] = {0};
-
        MLX5_SET(modify_tis_in, in, tisn, tisn);
        MLX5_SET(modify_tis_in, in, opcode, MLX5_CMD_OP_MODIFY_TIS);
 
-       return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, modify_tis, in);
 }
 EXPORT_SYMBOL(mlx5_core_modify_tis);
 
 void mlx5_core_destroy_tis(struct mlx5_core_dev *dev, u32 tisn)
 {
-       u32 in[MLX5_ST_SZ_DW(destroy_tis_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(destroy_tis_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_tis_in)] = {};
 
        MLX5_SET(destroy_tis_in, in, opcode, MLX5_CMD_OP_DESTROY_TIS);
        MLX5_SET(destroy_tis_in, in, tisn, tisn);
-       mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(dev, destroy_tis, in);
 }
 EXPORT_SYMBOL(mlx5_core_destroy_tis);
 
 int mlx5_core_create_rqt(struct mlx5_core_dev *dev, u32 *in, int inlen,
                         u32 *rqtn)
 {
-       u32 out[MLX5_ST_SZ_DW(create_rqt_out)] = {0};
+       u32 out[MLX5_ST_SZ_DW(create_rqt_out)] = {};
        int err;
 
        MLX5_SET(create_rqt_in, in, opcode, MLX5_CMD_OP_CREATE_RQT);
@@ -284,7 +254,7 @@ EXPORT_SYMBOL(mlx5_core_create_rqt);
 int mlx5_core_modify_rqt(struct mlx5_core_dev *dev, u32 rqtn, u32 *in,
                         int inlen)
 {
-       u32 out[MLX5_ST_SZ_DW(modify_rqt_out)] = {0};
+       u32 out[MLX5_ST_SZ_DW(modify_rqt_out)] = {};
 
        MLX5_SET(modify_rqt_in, in, rqtn, rqtn);
        MLX5_SET(modify_rqt_in, in, opcode, MLX5_CMD_OP_MODIFY_RQT);
@@ -293,12 +263,11 @@ int mlx5_core_modify_rqt(struct mlx5_core_dev *dev, u32 rqtn, u32 *in,
 
 void mlx5_core_destroy_rqt(struct mlx5_core_dev *dev, u32 rqtn)
 {
-       u32 in[MLX5_ST_SZ_DW(destroy_rqt_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(destroy_rqt_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(destroy_rqt_in)] = {};
 
        MLX5_SET(destroy_rqt_in, in, opcode, MLX5_CMD_OP_DESTROY_RQT);
        MLX5_SET(destroy_rqt_in, in, rqtn, rqtn);
-       mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(dev, destroy_rqt, in);
 }
 EXPORT_SYMBOL(mlx5_core_destroy_rqt);
 
@@ -383,7 +352,7 @@ static int mlx5_hairpin_modify_rq(struct mlx5_core_dev *func_mdev, u32 rqn,
                                  int curr_state, int next_state,
                                  u16 peer_vhca, u32 peer_sq)
 {
-       u32 in[MLX5_ST_SZ_DW(modify_rq_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(modify_rq_in)] = {};
        void *rqc;
 
        rqc = MLX5_ADDR_OF(modify_rq_in, in, ctx);
@@ -396,8 +365,7 @@ static int mlx5_hairpin_modify_rq(struct mlx5_core_dev *func_mdev, u32 rqn,
        MLX5_SET(modify_rq_in, in, rq_state, curr_state);
        MLX5_SET(rqc, rqc, state, next_state);
 
-       return mlx5_core_modify_rq(func_mdev, rqn,
-                                  in, MLX5_ST_SZ_BYTES(modify_rq_in));
+       return mlx5_core_modify_rq(func_mdev, rqn, in);
 }
 
 static int mlx5_hairpin_modify_sq(struct mlx5_core_dev *peer_mdev, u32 sqn,
@@ -417,8 +385,7 @@ static int mlx5_hairpin_modify_sq(struct mlx5_core_dev *peer_mdev, u32 sqn,
        MLX5_SET(modify_sq_in, in, sq_state, curr_state);
        MLX5_SET(sqc, sqc, state, next_state);
 
-       return mlx5_core_modify_sq(peer_mdev, sqn,
-                                  in, MLX5_ST_SZ_BYTES(modify_sq_in));
+       return mlx5_core_modify_sq(peer_mdev, sqn, in);
 }
 
 static int mlx5_hairpin_pair_queues(struct mlx5_hairpin *hp)
index 0d00622..da481a7 100644 (file)
 #include <linux/module.h>
 #include <linux/io-mapping.h>
 #include <linux/mlx5/driver.h>
-#include <linux/mlx5/cmd.h>
 #include "mlx5_core.h"
 
 int mlx5_cmd_alloc_uar(struct mlx5_core_dev *dev, u32 *uarn)
 {
-       u32 out[MLX5_ST_SZ_DW(alloc_uar_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(alloc_uar_in)]   = {0};
+       u32 out[MLX5_ST_SZ_DW(alloc_uar_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(alloc_uar_in)] = {};
        int err;
 
        MLX5_SET(alloc_uar_in, in, opcode, MLX5_CMD_OP_ALLOC_UAR);
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(dev, alloc_uar, in, out);
        if (!err)
                *uarn = MLX5_GET(alloc_uar_out, out, uar);
        return err;
@@ -53,12 +52,11 @@ EXPORT_SYMBOL(mlx5_cmd_alloc_uar);
 
 int mlx5_cmd_free_uar(struct mlx5_core_dev *dev, u32 uarn)
 {
-       u32 out[MLX5_ST_SZ_DW(dealloc_uar_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(dealloc_uar_in)]   = {0};
+       u32 in[MLX5_ST_SZ_DW(dealloc_uar_in)] = {};
 
        MLX5_SET(dealloc_uar_in, in, opcode, MLX5_CMD_OP_DEALLOC_UAR);
        MLX5_SET(dealloc_uar_in, in, uar, uarn);
-       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev, dealloc_uar, in);
 }
 EXPORT_SYMBOL(mlx5_cmd_free_uar);
 
index 23f879d..c107d92 100644 (file)
 /* Mutex to hold while enabling or disabling RoCE */
 static DEFINE_MUTEX(mlx5_roce_en_lock);
 
-static int _mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod,
-                                  u16 vport, u32 *out, int outlen)
+u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
 {
-       u32 in[MLX5_ST_SZ_DW(query_vport_state_in)] = {0};
+       u32 out[MLX5_ST_SZ_DW(query_vport_state_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(query_vport_state_in)] = {};
+       int err;
 
        MLX5_SET(query_vport_state_in, in, opcode,
                 MLX5_CMD_OP_QUERY_VPORT_STATE);
@@ -52,14 +53,9 @@ static int _mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod,
        if (vport)
                MLX5_SET(query_vport_state_in, in, other_vport, 1);
 
-       return mlx5_cmd_exec(mdev, in, sizeof(in), out, outlen);
-}
-
-u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
-{
-       u32 out[MLX5_ST_SZ_DW(query_vport_state_out)] = {0};
-
-       _mlx5_query_vport_state(mdev, opmod, vport, out, sizeof(out));
+       err = mlx5_cmd_exec_inout(mdev, query_vport_state, in, out);
+       if (err)
+               return 0;
 
        return MLX5_GET(query_vport_state_out, out, state);
 }
@@ -67,8 +63,7 @@ u8 mlx5_query_vport_state(struct mlx5_core_dev *mdev, u8 opmod, u16 vport)
 int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
                                  u16 vport, u8 other_vport, u8 state)
 {
-       u32 in[MLX5_ST_SZ_DW(modify_vport_state_in)]   = {0};
-       u32 out[MLX5_ST_SZ_DW(modify_vport_state_out)] = {0};
+       u32 in[MLX5_ST_SZ_DW(modify_vport_state_in)] = {};
 
        MLX5_SET(modify_vport_state_in, in, opcode,
                 MLX5_CMD_OP_MODIFY_VPORT_STATE);
@@ -77,13 +72,13 @@ int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
        MLX5_SET(modify_vport_state_in, in, other_vport, other_vport);
        MLX5_SET(modify_vport_state_in, in, admin_state, state);
 
-       return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(mdev, modify_vport_state, in);
 }
 
 static int mlx5_query_nic_vport_context(struct mlx5_core_dev *mdev, u16 vport,
-                                       u32 *out, int outlen)
+                                       u32 *out)
 {
-       u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)] = {0};
+       u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)] = {};
 
        MLX5_SET(query_nic_vport_context_in, in, opcode,
                 MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT);
@@ -91,26 +86,16 @@ static int mlx5_query_nic_vport_context(struct mlx5_core_dev *mdev, u16 vport,
        if (vport)
                MLX5_SET(query_nic_vport_context_in, in, other_vport, 1);
 
-       return mlx5_cmd_exec(mdev, in, sizeof(in), out, outlen);
-}
-
-static int mlx5_modify_nic_vport_context(struct mlx5_core_dev *mdev, void *in,
-                                        int inlen)
-{
-       u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)] = {0};
-
-       MLX5_SET(modify_nic_vport_context_in, in, opcode,
-                MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
-       return mlx5_cmd_exec(mdev, in, inlen, out, sizeof(out));
+       return mlx5_cmd_exec_inout(mdev, query_nic_vport_context, in, out);
 }
 
 int mlx5_query_nic_vport_min_inline(struct mlx5_core_dev *mdev,
                                    u16 vport, u8 *min_inline)
 {
-       u32 out[MLX5_ST_SZ_DW(query_nic_vport_context_out)] = {0};
+       u32 out[MLX5_ST_SZ_DW(query_nic_vport_context_out)] = {};
        int err;
 
-       err = mlx5_query_nic_vport_context(mdev, vport, out, sizeof(out));
+       err = mlx5_query_nic_vport_context(mdev, vport, out);
        if (!err)
                *min_inline = MLX5_GET(query_nic_vport_context_out, out,
                                       nic_vport_context.min_wqe_inline_mode);
@@ -139,8 +124,7 @@ EXPORT_SYMBOL_GPL(mlx5_query_min_inline);
 int mlx5_modify_nic_vport_min_inline(struct mlx5_core_dev *mdev,
                                     u16 vport, u8 min_inline)
 {
-       u32 in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)] = {0};
-       int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in);
+       u32 in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)] = {};
        void *nic_vport_ctx;
 
        MLX5_SET(modify_nic_vport_context_in, in,
@@ -152,23 +136,20 @@ int mlx5_modify_nic_vport_min_inline(struct mlx5_core_dev *mdev,
                                     in, nic_vport_context);
        MLX5_SET(nic_vport_context, nic_vport_ctx,
                 min_wqe_inline_mode, min_inline);
+       MLX5_SET(modify_nic_vport_context_in, in, opcode,
+                MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
 
-       return mlx5_modify_nic_vport_context(mdev, in, inlen);
+       return mlx5_cmd_exec_in(mdev, modify_nic_vport_context, in);
 }
 
 int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev,
                                     u16 vport, bool other, u8 *addr)
 {
-       int outlen = MLX5_ST_SZ_BYTES(query_nic_vport_context_out);
+       u32 out[MLX5_ST_SZ_DW(query_nic_vport_context_out)] = {};
        u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)] = {};
        u8 *out_addr;
-       u32 *out;
        int err;
 
-       out = kvzalloc(outlen, GFP_KERNEL);
-       if (!out)
-               return -ENOMEM;
-
        out_addr = MLX5_ADDR_OF(query_nic_vport_context_out, out,
                                nic_vport_context.permanent_address);
 
@@ -177,11 +158,10 @@ int mlx5_query_nic_vport_mac_address(struct mlx5_core_dev *mdev,
        MLX5_SET(query_nic_vport_context_in, in, vport_number, vport);
        MLX5_SET(query_nic_vport_context_in, in, other_vport, other);
 
-       err = mlx5_cmd_exec(mdev, in, sizeof(in), out, outlen);
+       err = mlx5_cmd_exec_inout(mdev, query_nic_vport_context, in, out);
        if (!err)
                ether_addr_copy(addr, &out_addr[2]);
 
-       kvfree(out);
        return err;
 }
 EXPORT_SYMBOL_GPL(mlx5_query_nic_vport_mac_address);
@@ -216,8 +196,10 @@ int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *mdev,
                                permanent_address);
 
        ether_addr_copy(&perm_mac[2], addr);
+       MLX5_SET(modify_nic_vport_context_in, in, opcode,
+                MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
 
-       err = mlx5_modify_nic_vport_context(mdev, in, inlen);
+       err = mlx5_cmd_exec_in(mdev, modify_nic_vport_context, in);
 
        kvfree(in);
 
@@ -235,7 +217,7 @@ int mlx5_query_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 *mtu)
        if (!out)
                return -ENOMEM;
 
-       err = mlx5_query_nic_vport_context(mdev, 0, out, outlen);
+       err = mlx5_query_nic_vport_context(mdev, 0, out);
        if (!err)
                *mtu = MLX5_GET(query_nic_vport_context_out, out,
                                nic_vport_context.mtu);
@@ -257,8 +239,10 @@ int mlx5_modify_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 mtu)
 
        MLX5_SET(modify_nic_vport_context_in, in, field_select.mtu, 1);
        MLX5_SET(modify_nic_vport_context_in, in, nic_vport_context.mtu, mtu);
+       MLX5_SET(modify_nic_vport_context_in, in, opcode,
+                MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
 
-       err = mlx5_modify_nic_vport_context(mdev, in, inlen);
+       err = mlx5_cmd_exec_in(mdev, modify_nic_vport_context, in);
 
        kvfree(in);
        return err;
@@ -292,7 +276,7 @@ int mlx5_query_nic_vport_mac_list(struct mlx5_core_dev *dev,
                req_list_size = max_list_size;
        }
 
-       out_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
+       out_sz = MLX5_ST_SZ_BYTES(query_nic_vport_context_in) +
                        req_list_size * MLX5_ST_SZ_BYTES(mac_address_layout);
 
        out = kzalloc(out_sz, GFP_KERNEL);
@@ -332,7 +316,7 @@ int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev,
                                   u8 addr_list[][ETH_ALEN],
                                   int list_size)
 {
-       u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)];
+       u32 out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)] = {};
        void *nic_vport_ctx;
        int max_list_size;
        int in_sz;
@@ -350,7 +334,6 @@ int mlx5_modify_nic_vport_mac_list(struct mlx5_core_dev *dev,
        in_sz = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in) +
                list_size * MLX5_ST_SZ_BYTES(mac_address_layout);
 
-       memset(out, 0, sizeof(out));
        in = kzalloc(in_sz, GFP_KERNEL);
        if (!in)
                return -ENOMEM;
@@ -442,7 +425,7 @@ int mlx5_query_nic_vport_system_image_guid(struct mlx5_core_dev *mdev,
        if (!out)
                return -ENOMEM;
 
-       mlx5_query_nic_vport_context(mdev, 0, out, outlen);
+       mlx5_query_nic_vport_context(mdev, 0, out);
 
        *system_image_guid = MLX5_GET64(query_nic_vport_context_out, out,
                                        nic_vport_context.system_image_guid);
@@ -462,7 +445,7 @@ int mlx5_query_nic_vport_node_guid(struct mlx5_core_dev *mdev, u64 *node_guid)
        if (!out)
                return -ENOMEM;
 
-       mlx5_query_nic_vport_context(mdev, 0, out, outlen);
+       mlx5_query_nic_vport_context(mdev, 0, out);
 
        *node_guid = MLX5_GET64(query_nic_vport_context_out, out,
                                nic_vport_context.node_guid);
@@ -498,8 +481,10 @@ int mlx5_modify_nic_vport_node_guid(struct mlx5_core_dev *mdev,
        nic_vport_context = MLX5_ADDR_OF(modify_nic_vport_context_in,
                                         in, nic_vport_context);
        MLX5_SET64(nic_vport_context, nic_vport_context, node_guid, node_guid);
+       MLX5_SET(modify_nic_vport_context_in, in, opcode,
+                MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
 
-       err = mlx5_modify_nic_vport_context(mdev, in, inlen);
+       err = mlx5_cmd_exec_in(mdev, modify_nic_vport_context, in);
 
        kvfree(in);
 
@@ -516,7 +501,7 @@ int mlx5_query_nic_vport_qkey_viol_cntr(struct mlx5_core_dev *mdev,
        if (!out)
                return -ENOMEM;
 
-       mlx5_query_nic_vport_context(mdev, 0, out, outlen);
+       mlx5_query_nic_vport_context(mdev, 0, out);
 
        *qkey_viol_cntr = MLX5_GET(query_nic_vport_context_out, out,
                                   nic_vport_context.qkey_violation_counter);
@@ -664,7 +649,7 @@ int mlx5_query_hca_vport_context(struct mlx5_core_dev *dev,
                                 struct mlx5_hca_vport_context *rep)
 {
        int out_sz = MLX5_ST_SZ_BYTES(query_hca_vport_context_out);
-       int in[MLX5_ST_SZ_DW(query_hca_vport_context_in)] = {0};
+       int in[MLX5_ST_SZ_DW(query_hca_vport_context_in)] = {};
        int is_group_manager;
        void *out;
        void *ctx;
@@ -691,7 +676,7 @@ int mlx5_query_hca_vport_context(struct mlx5_core_dev *dev,
        if (MLX5_CAP_GEN(dev, num_ports) == 2)
                MLX5_SET(query_hca_vport_context_in, in, port_num, port_num);
 
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out,  out_sz);
+       err = mlx5_cmd_exec_inout(dev, query_hca_vport_context, in, out);
        if (err)
                goto ex;
 
@@ -788,7 +773,7 @@ int mlx5_query_nic_vport_promisc(struct mlx5_core_dev *mdev,
        if (!out)
                return -ENOMEM;
 
-       err = mlx5_query_nic_vport_context(mdev, vport, out, outlen);
+       err = mlx5_query_nic_vport_context(mdev, vport, out);
        if (err)
                goto out;
 
@@ -825,8 +810,10 @@ int mlx5_modify_nic_vport_promisc(struct mlx5_core_dev *mdev,
                 nic_vport_context.promisc_mc, promisc_mc);
        MLX5_SET(modify_nic_vport_context_in, in,
                 nic_vport_context.promisc_all, promisc_all);
+       MLX5_SET(modify_nic_vport_context_in, in, opcode,
+                MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
 
-       err = mlx5_modify_nic_vport_context(mdev, in, inlen);
+       err = mlx5_cmd_exec_in(mdev, modify_nic_vport_context, in);
 
        kvfree(in);
 
@@ -865,8 +852,10 @@ int mlx5_nic_vport_update_local_lb(struct mlx5_core_dev *mdev, bool enable)
        if (MLX5_CAP_GEN(mdev, disable_local_lb_uc))
                MLX5_SET(modify_nic_vport_context_in, in,
                         field_select.disable_uc_local_lb, 1);
+       MLX5_SET(modify_nic_vport_context_in, in, opcode,
+                MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
 
-       err = mlx5_modify_nic_vport_context(mdev, in, inlen);
+       err = mlx5_cmd_exec_in(mdev, modify_nic_vport_context, in);
 
        if (!err)
                mlx5_core_dbg(mdev, "%s local_lb\n",
@@ -888,7 +877,7 @@ int mlx5_nic_vport_query_local_lb(struct mlx5_core_dev *mdev, bool *status)
        if (!out)
                return -ENOMEM;
 
-       err = mlx5_query_nic_vport_context(mdev, 0, out, outlen);
+       err = mlx5_query_nic_vport_context(mdev, 0, out);
        if (err)
                goto out;
 
@@ -925,8 +914,10 @@ static int mlx5_nic_vport_update_roce_state(struct mlx5_core_dev *mdev,
        MLX5_SET(modify_nic_vport_context_in, in, field_select.roce_en, 1);
        MLX5_SET(modify_nic_vport_context_in, in, nic_vport_context.roce_en,
                 state);
+       MLX5_SET(modify_nic_vport_context_in, in, opcode,
+                MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
 
-       err = mlx5_modify_nic_vport_context(mdev, in, inlen);
+       err = mlx5_cmd_exec_in(mdev, modify_nic_vport_context, in);
 
        kvfree(in);
 
@@ -965,16 +956,15 @@ int mlx5_nic_vport_disable_roce(struct mlx5_core_dev *mdev)
        mutex_unlock(&mlx5_roce_en_lock);
        return err;
 }
-EXPORT_SYMBOL_GPL(mlx5_nic_vport_disable_roce);
+EXPORT_SYMBOL(mlx5_nic_vport_disable_roce);
 
 int mlx5_core_query_vport_counter(struct mlx5_core_dev *dev, u8 other_vport,
-                                 int vf, u8 port_num, void *out,
-                                 size_t out_sz)
+                                 int vf, u8 port_num, void *out)
 {
-       int     in_sz = MLX5_ST_SZ_BYTES(query_vport_counter_in);
-       int     is_group_manager;
-       void   *in;
-       int     err;
+       int in_sz = MLX5_ST_SZ_BYTES(query_vport_counter_in);
+       int is_group_manager;
+       void *in;
+       int err;
 
        is_group_manager = MLX5_CAP_GEN(dev, vport_group_manager);
        in = kvzalloc(in_sz, GFP_KERNEL);
@@ -997,7 +987,7 @@ int mlx5_core_query_vport_counter(struct mlx5_core_dev *dev, u8 other_vport,
        if (MLX5_CAP_GEN(dev, num_ports) == 2)
                MLX5_SET(query_vport_counter_in, in, port_num, port_num);
 
-       err = mlx5_cmd_exec(dev, in, in_sz, out,  out_sz);
+       err = mlx5_cmd_exec_inout(dev, query_vport_counter, in, out);
 free:
        kvfree(in);
        return err;
@@ -1008,8 +998,8 @@ int mlx5_query_vport_down_stats(struct mlx5_core_dev *mdev, u16 vport,
                                u8 other_vport, u64 *rx_discard_vport_down,
                                u64 *tx_discard_vport_down)
 {
-       u32 out[MLX5_ST_SZ_DW(query_vnic_env_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(query_vnic_env_in)] = {0};
+       u32 out[MLX5_ST_SZ_DW(query_vnic_env_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(query_vnic_env_in)] = {};
        int err;
 
        MLX5_SET(query_vnic_env_in, in, opcode,
@@ -1018,7 +1008,7 @@ int mlx5_query_vport_down_stats(struct mlx5_core_dev *mdev, u16 vport,
        MLX5_SET(query_vnic_env_in, in, vport_number, vport);
        MLX5_SET(query_vnic_env_in, in, other_vport, other_vport);
 
-       err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(mdev, query_vnic_env, in, out);
        if (err)
                return err;
 
@@ -1035,11 +1025,10 @@ int mlx5_core_modify_hca_vport_context(struct mlx5_core_dev *dev,
                                       struct mlx5_hca_vport_context *req)
 {
        int in_sz = MLX5_ST_SZ_BYTES(modify_hca_vport_context_in);
-       u8 out[MLX5_ST_SZ_BYTES(modify_hca_vport_context_out)];
        int is_group_manager;
+       void *ctx;
        void *in;
        int err;
-       void *ctx;
 
        mlx5_core_dbg(dev, "vf %d\n", vf);
        is_group_manager = MLX5_CAP_GEN(dev, vport_group_manager);
@@ -1047,7 +1036,6 @@ int mlx5_core_modify_hca_vport_context(struct mlx5_core_dev *dev,
        if (!in)
                return -ENOMEM;
 
-       memset(out, 0, sizeof(out));
        MLX5_SET(modify_hca_vport_context_in, in, opcode, MLX5_CMD_OP_MODIFY_HCA_VPORT_CONTEXT);
        if (other_vport) {
                if (is_group_manager) {
@@ -1074,7 +1062,7 @@ int mlx5_core_modify_hca_vport_context(struct mlx5_core_dev *dev,
        MLX5_SET(hca_vport_context, ctx, cap_mask1, req->cap_mask1);
        MLX5_SET(hca_vport_context, ctx, cap_mask1_field_select,
                 req->cap_mask1_perm);
-       err = mlx5_cmd_exec(dev, in, in_sz, out, sizeof(out));
+       err = mlx5_cmd_exec_in(dev, modify_hca_vport_context, in);
 ex:
        kfree(in);
        return err;
@@ -1103,8 +1091,10 @@ int mlx5_nic_vport_affiliate_multiport(struct mlx5_core_dev *master_mdev,
        MLX5_SET(modify_nic_vport_context_in, in,
                 nic_vport_context.affiliation_criteria,
                 MLX5_CAP_GEN(port_mdev, affiliate_nic_vport_criteria));
+       MLX5_SET(modify_nic_vport_context_in, in, opcode,
+                MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
 
-       err = mlx5_modify_nic_vport_context(port_mdev, in, inlen);
+       err = mlx5_cmd_exec_in(port_mdev, modify_nic_vport_context, in);
        if (err)
                mlx5_nic_vport_disable_roce(port_mdev);
 
@@ -1129,8 +1119,10 @@ int mlx5_nic_vport_unaffiliate_multiport(struct mlx5_core_dev *port_mdev)
                 nic_vport_context.affiliated_vhca_id, 0);
        MLX5_SET(modify_nic_vport_context_in, in,
                 nic_vport_context.affiliation_criteria, 0);
+       MLX5_SET(modify_nic_vport_context_in, in, opcode,
+                MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
 
-       err = mlx5_modify_nic_vport_context(port_mdev, in, inlen);
+       err = mlx5_cmd_exec_in(port_mdev, modify_nic_vport_context, in);
        if (!err)
                mlx5_nic_vport_disable_roce(port_mdev);
 
@@ -1170,4 +1162,4 @@ u16 mlx5_eswitch_get_total_vports(const struct mlx5_core_dev *dev)
 {
        return MLX5_SPECIAL_VPORTS(dev) + mlx5_core_max_vfs(dev);
 }
-EXPORT_SYMBOL(mlx5_eswitch_get_total_vports);
+EXPORT_SYMBOL_GPL(mlx5_eswitch_get_total_vports);
index 046a0cb..7a04c62 100644 (file)
@@ -76,7 +76,7 @@ static int mlxfw_fsm_state_err(struct mlxfw_dev *mlxfw_dev,
        case MLXFW_FSM_STATE_ERR_MAX:
                MLXFW_ERR_MSG(mlxfw_dev, extack, "unknown error", err);
                break;
-       };
+       }
 
        return mlxfw_fsm_state_errno[fsm_state_err];
 };
@@ -159,7 +159,7 @@ mlxfw_fsm_reactivate_err(struct mlxfw_dev *mlxfw_dev,
        case MLXFW_FSM_REACTIVATE_STATUS_MAX:
                MLXFW_REACT_ERR("unexpected error", err);
                break;
-       };
+       }
        return -EREMOTEIO;
 };
 
index 0e86a58..4aeabb3 100644 (file)
@@ -21,6 +21,7 @@ mlxsw_spectrum-objs           := spectrum.o spectrum_buffers.o \
                                   spectrum_acl_atcam.o spectrum_acl_erp.o \
                                   spectrum1_acl_tcam.o spectrum2_acl_tcam.o \
                                   spectrum_acl_bloom_filter.o spectrum_acl.o \
+                                  spectrum_flow.o spectrum_matchall.o \
                                   spectrum_flower.o spectrum_cnt.o \
                                   spectrum_fid.o spectrum_ipip.o \
                                   spectrum_acl_flex_actions.o \
index 70a104e..c3d0431 100644 (file)
@@ -380,7 +380,7 @@ struct mlxsw_afa_block *mlxsw_afa_block_create(struct mlxsw_afa *mlxsw_afa)
 
        block = kzalloc(sizeof(*block), GFP_KERNEL);
        if (!block)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
        INIT_LIST_HEAD(&block->resource_list);
        block->afa = mlxsw_afa;
 
@@ -408,7 +408,7 @@ err_second_set_create:
        mlxsw_afa_set_destroy(block->first_set);
 err_first_set_create:
        kfree(block);
-       return NULL;
+       return ERR_PTR(-ENOMEM);
 }
 EXPORT_SYMBOL(mlxsw_afa_block_create);
 
index 9b39b8e..3c3db1c 100644 (file)
@@ -3203,7 +3203,7 @@ MLXSW_ITEM32_INDEXED(reg, iedr, rec_type, MLXSW_REG_IEDR_BASE_LEN, 24, 8,
  * Size of entries do be deleted. The unit is 1 entry, regardless of entry type.
  * Access: OP
  */
-MLXSW_ITEM32_INDEXED(reg, iedr, rec_size, MLXSW_REG_IEDR_BASE_LEN, 0, 11,
+MLXSW_ITEM32_INDEXED(reg, iedr, rec_size, MLXSW_REG_IEDR_BASE_LEN, 0, 13,
                     MLXSW_REG_IEDR_REC_LEN, 0x00, false);
 
 /* reg_iedr_rec_index_start
index 24ca8d5..f78bde8 100644 (file)
@@ -25,9 +25,7 @@
 #include <linux/log2.h>
 #include <net/switchdev.h>
 #include <net/pkt_cls.h>
-#include <net/tc_act/tc_mirred.h>
 #include <net/netevent.h>
-#include <net/tc_act/tc_sample.h>
 #include <net/addrconf.h>
 
 #include "spectrum.h"
@@ -582,16 +580,6 @@ static int mlxsw_sp_base_mac_get(struct mlxsw_sp *mlxsw_sp)
        return 0;
 }
 
-static int mlxsw_sp_port_sample_set(struct mlxsw_sp_port *mlxsw_sp_port,
-                                   bool enable, u32 rate)
-{
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
-       char mpsc_pl[MLXSW_REG_MPSC_LEN];
-
-       mlxsw_reg_mpsc_pack(mpsc_pl, mlxsw_sp_port->local_port, enable, rate);
-       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpsc), mpsc_pl);
-}
-
 static int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port,
                                          bool is_up)
 {
@@ -1362,412 +1350,6 @@ static int mlxsw_sp_port_kill_vid(struct net_device *dev,
        return 0;
 }
 
-static struct mlxsw_sp_port_mall_tc_entry *
-mlxsw_sp_port_mall_tc_entry_find(struct mlxsw_sp_port *port,
-                                unsigned long cookie) {
-       struct mlxsw_sp_port_mall_tc_entry *mall_tc_entry;
-
-       list_for_each_entry(mall_tc_entry, &port->mall_tc_list, list)
-               if (mall_tc_entry->cookie == cookie)
-                       return mall_tc_entry;
-
-       return NULL;
-}
-
-static int
-mlxsw_sp_port_add_cls_matchall_mirror(struct mlxsw_sp_port *mlxsw_sp_port,
-                                     struct mlxsw_sp_port_mall_mirror_tc_entry *mirror,
-                                     const struct flow_action_entry *act,
-                                     bool ingress)
-{
-       enum mlxsw_sp_span_type span_type;
-
-       if (!act->dev) {
-               netdev_err(mlxsw_sp_port->dev, "Could not find requested device\n");
-               return -EINVAL;
-       }
-
-       mirror->ingress = ingress;
-       span_type = ingress ? MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
-       return mlxsw_sp_span_mirror_add(mlxsw_sp_port, act->dev, span_type,
-                                       true, &mirror->span_id);
-}
-
-static void
-mlxsw_sp_port_del_cls_matchall_mirror(struct mlxsw_sp_port *mlxsw_sp_port,
-                                     struct mlxsw_sp_port_mall_mirror_tc_entry *mirror)
-{
-       enum mlxsw_sp_span_type span_type;
-
-       span_type = mirror->ingress ?
-                       MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
-       mlxsw_sp_span_mirror_del(mlxsw_sp_port, mirror->span_id,
-                                span_type, true);
-}
-
-static int
-mlxsw_sp_port_add_cls_matchall_sample(struct mlxsw_sp_port *mlxsw_sp_port,
-                                     struct tc_cls_matchall_offload *cls,
-                                     const struct flow_action_entry *act,
-                                     bool ingress)
-{
-       int err;
-
-       if (!mlxsw_sp_port->sample)
-               return -EOPNOTSUPP;
-       if (rtnl_dereference(mlxsw_sp_port->sample->psample_group)) {
-               netdev_err(mlxsw_sp_port->dev, "sample already active\n");
-               return -EEXIST;
-       }
-       if (act->sample.rate > MLXSW_REG_MPSC_RATE_MAX) {
-               netdev_err(mlxsw_sp_port->dev, "sample rate not supported\n");
-               return -EOPNOTSUPP;
-       }
-
-       rcu_assign_pointer(mlxsw_sp_port->sample->psample_group,
-                          act->sample.psample_group);
-       mlxsw_sp_port->sample->truncate = act->sample.truncate;
-       mlxsw_sp_port->sample->trunc_size = act->sample.trunc_size;
-       mlxsw_sp_port->sample->rate = act->sample.rate;
-
-       err = mlxsw_sp_port_sample_set(mlxsw_sp_port, true, act->sample.rate);
-       if (err)
-               goto err_port_sample_set;
-       return 0;
-
-err_port_sample_set:
-       RCU_INIT_POINTER(mlxsw_sp_port->sample->psample_group, NULL);
-       return err;
-}
-
-static void
-mlxsw_sp_port_del_cls_matchall_sample(struct mlxsw_sp_port *mlxsw_sp_port)
-{
-       if (!mlxsw_sp_port->sample)
-               return;
-
-       mlxsw_sp_port_sample_set(mlxsw_sp_port, false, 1);
-       RCU_INIT_POINTER(mlxsw_sp_port->sample->psample_group, NULL);
-}
-
-static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port,
-                                         struct tc_cls_matchall_offload *f,
-                                         bool ingress)
-{
-       struct mlxsw_sp_port_mall_tc_entry *mall_tc_entry;
-       __be16 protocol = f->common.protocol;
-       struct flow_action_entry *act;
-       int err;
-
-       if (!flow_offload_has_one_action(&f->rule->action)) {
-               netdev_err(mlxsw_sp_port->dev, "only singular actions are supported\n");
-               return -EOPNOTSUPP;
-       }
-
-       mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL);
-       if (!mall_tc_entry)
-               return -ENOMEM;
-       mall_tc_entry->cookie = f->cookie;
-
-       act = &f->rule->action.entries[0];
-
-       if (act->id == FLOW_ACTION_MIRRED && protocol == htons(ETH_P_ALL)) {
-               struct mlxsw_sp_port_mall_mirror_tc_entry *mirror;
-
-               mall_tc_entry->type = MLXSW_SP_PORT_MALL_MIRROR;
-               mirror = &mall_tc_entry->mirror;
-               err = mlxsw_sp_port_add_cls_matchall_mirror(mlxsw_sp_port,
-                                                           mirror, act,
-                                                           ingress);
-       } else if (act->id == FLOW_ACTION_SAMPLE &&
-                  protocol == htons(ETH_P_ALL)) {
-               mall_tc_entry->type = MLXSW_SP_PORT_MALL_SAMPLE;
-               err = mlxsw_sp_port_add_cls_matchall_sample(mlxsw_sp_port, f,
-                                                           act, ingress);
-       } else {
-               err = -EOPNOTSUPP;
-       }
-
-       if (err)
-               goto err_add_action;
-
-       list_add_tail(&mall_tc_entry->list, &mlxsw_sp_port->mall_tc_list);
-       return 0;
-
-err_add_action:
-       kfree(mall_tc_entry);
-       return err;
-}
-
-static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port,
-                                          struct tc_cls_matchall_offload *f)
-{
-       struct mlxsw_sp_port_mall_tc_entry *mall_tc_entry;
-
-       mall_tc_entry = mlxsw_sp_port_mall_tc_entry_find(mlxsw_sp_port,
-                                                        f->cookie);
-       if (!mall_tc_entry) {
-               netdev_dbg(mlxsw_sp_port->dev, "tc entry not found on port\n");
-               return;
-       }
-       list_del(&mall_tc_entry->list);
-
-       switch (mall_tc_entry->type) {
-       case MLXSW_SP_PORT_MALL_MIRROR:
-               mlxsw_sp_port_del_cls_matchall_mirror(mlxsw_sp_port,
-                                                     &mall_tc_entry->mirror);
-               break;
-       case MLXSW_SP_PORT_MALL_SAMPLE:
-               mlxsw_sp_port_del_cls_matchall_sample(mlxsw_sp_port);
-               break;
-       default:
-               WARN_ON(1);
-       }
-
-       kfree(mall_tc_entry);
-}
-
-static int mlxsw_sp_setup_tc_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port,
-                                         struct tc_cls_matchall_offload *f,
-                                         bool ingress)
-{
-       switch (f->command) {
-       case TC_CLSMATCHALL_REPLACE:
-               return mlxsw_sp_port_add_cls_matchall(mlxsw_sp_port, f,
-                                                     ingress);
-       case TC_CLSMATCHALL_DESTROY:
-               mlxsw_sp_port_del_cls_matchall(mlxsw_sp_port, f);
-               return 0;
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
-static int
-mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_acl_block *acl_block,
-                            struct flow_cls_offload *f)
-{
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_acl_block_mlxsw_sp(acl_block);
-
-       switch (f->command) {
-       case FLOW_CLS_REPLACE:
-               return mlxsw_sp_flower_replace(mlxsw_sp, acl_block, f);
-       case FLOW_CLS_DESTROY:
-               mlxsw_sp_flower_destroy(mlxsw_sp, acl_block, f);
-               return 0;
-       case FLOW_CLS_STATS:
-               return mlxsw_sp_flower_stats(mlxsw_sp, acl_block, f);
-       case FLOW_CLS_TMPLT_CREATE:
-               return mlxsw_sp_flower_tmplt_create(mlxsw_sp, acl_block, f);
-       case FLOW_CLS_TMPLT_DESTROY:
-               mlxsw_sp_flower_tmplt_destroy(mlxsw_sp, acl_block, f);
-               return 0;
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
-static int mlxsw_sp_setup_tc_block_cb_matchall(enum tc_setup_type type,
-                                              void *type_data,
-                                              void *cb_priv, bool ingress)
-{
-       struct mlxsw_sp_port *mlxsw_sp_port = cb_priv;
-
-       switch (type) {
-       case TC_SETUP_CLSMATCHALL:
-               if (!tc_cls_can_offload_and_chain0(mlxsw_sp_port->dev,
-                                                  type_data))
-                       return -EOPNOTSUPP;
-
-               return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, type_data,
-                                                     ingress);
-       case TC_SETUP_CLSFLOWER:
-               return 0;
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
-static int mlxsw_sp_setup_tc_block_cb_matchall_ig(enum tc_setup_type type,
-                                                 void *type_data,
-                                                 void *cb_priv)
-{
-       return mlxsw_sp_setup_tc_block_cb_matchall(type, type_data,
-                                                  cb_priv, true);
-}
-
-static int mlxsw_sp_setup_tc_block_cb_matchall_eg(enum tc_setup_type type,
-                                                 void *type_data,
-                                                 void *cb_priv)
-{
-       return mlxsw_sp_setup_tc_block_cb_matchall(type, type_data,
-                                                  cb_priv, false);
-}
-
-static int mlxsw_sp_setup_tc_block_cb_flower(enum tc_setup_type type,
-                                            void *type_data, void *cb_priv)
-{
-       struct mlxsw_sp_acl_block *acl_block = cb_priv;
-
-       switch (type) {
-       case TC_SETUP_CLSMATCHALL:
-               return 0;
-       case TC_SETUP_CLSFLOWER:
-               if (mlxsw_sp_acl_block_disabled(acl_block))
-                       return -EOPNOTSUPP;
-
-               return mlxsw_sp_setup_tc_cls_flower(acl_block, type_data);
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
-static void mlxsw_sp_tc_block_flower_release(void *cb_priv)
-{
-       struct mlxsw_sp_acl_block *acl_block = cb_priv;
-
-       mlxsw_sp_acl_block_destroy(acl_block);
-}
-
-static LIST_HEAD(mlxsw_sp_block_cb_list);
-
-static int
-mlxsw_sp_setup_tc_block_flower_bind(struct mlxsw_sp_port *mlxsw_sp_port,
-                                   struct flow_block_offload *f, bool ingress)
-{
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
-       struct mlxsw_sp_acl_block *acl_block;
-       struct flow_block_cb *block_cb;
-       bool register_block = false;
-       int err;
-
-       block_cb = flow_block_cb_lookup(f->block,
-                                       mlxsw_sp_setup_tc_block_cb_flower,
-                                       mlxsw_sp);
-       if (!block_cb) {
-               acl_block = mlxsw_sp_acl_block_create(mlxsw_sp, f->net);
-               if (!acl_block)
-                       return -ENOMEM;
-               block_cb = flow_block_cb_alloc(mlxsw_sp_setup_tc_block_cb_flower,
-                                              mlxsw_sp, acl_block,
-                                              mlxsw_sp_tc_block_flower_release);
-               if (IS_ERR(block_cb)) {
-                       mlxsw_sp_acl_block_destroy(acl_block);
-                       err = PTR_ERR(block_cb);
-                       goto err_cb_register;
-               }
-               register_block = true;
-       } else {
-               acl_block = flow_block_cb_priv(block_cb);
-       }
-       flow_block_cb_incref(block_cb);
-       err = mlxsw_sp_acl_block_bind(mlxsw_sp, acl_block,
-                                     mlxsw_sp_port, ingress, f->extack);
-       if (err)
-               goto err_block_bind;
-
-       if (ingress)
-               mlxsw_sp_port->ing_acl_block = acl_block;
-       else
-               mlxsw_sp_port->eg_acl_block = acl_block;
-
-       if (register_block) {
-               flow_block_cb_add(block_cb, f);
-               list_add_tail(&block_cb->driver_list, &mlxsw_sp_block_cb_list);
-       }
-
-       return 0;
-
-err_block_bind:
-       if (!flow_block_cb_decref(block_cb))
-               flow_block_cb_free(block_cb);
-err_cb_register:
-       return err;
-}
-
-static void
-mlxsw_sp_setup_tc_block_flower_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
-                                     struct flow_block_offload *f, bool ingress)
-{
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
-       struct mlxsw_sp_acl_block *acl_block;
-       struct flow_block_cb *block_cb;
-       int err;
-
-       block_cb = flow_block_cb_lookup(f->block,
-                                       mlxsw_sp_setup_tc_block_cb_flower,
-                                       mlxsw_sp);
-       if (!block_cb)
-               return;
-
-       if (ingress)
-               mlxsw_sp_port->ing_acl_block = NULL;
-       else
-               mlxsw_sp_port->eg_acl_block = NULL;
-
-       acl_block = flow_block_cb_priv(block_cb);
-       err = mlxsw_sp_acl_block_unbind(mlxsw_sp, acl_block,
-                                       mlxsw_sp_port, ingress);
-       if (!err && !flow_block_cb_decref(block_cb)) {
-               flow_block_cb_remove(block_cb, f);
-               list_del(&block_cb->driver_list);
-       }
-}
-
-static int mlxsw_sp_setup_tc_block(struct mlxsw_sp_port *mlxsw_sp_port,
-                                  struct flow_block_offload *f)
-{
-       struct flow_block_cb *block_cb;
-       flow_setup_cb_t *cb;
-       bool ingress;
-       int err;
-
-       if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
-               cb = mlxsw_sp_setup_tc_block_cb_matchall_ig;
-               ingress = true;
-       } else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
-               cb = mlxsw_sp_setup_tc_block_cb_matchall_eg;
-               ingress = false;
-       } else {
-               return -EOPNOTSUPP;
-       }
-
-       f->driver_block_list = &mlxsw_sp_block_cb_list;
-
-       switch (f->command) {
-       case FLOW_BLOCK_BIND:
-               if (flow_block_cb_is_busy(cb, mlxsw_sp_port,
-                                         &mlxsw_sp_block_cb_list))
-                       return -EBUSY;
-
-               block_cb = flow_block_cb_alloc(cb, mlxsw_sp_port,
-                                              mlxsw_sp_port, NULL);
-               if (IS_ERR(block_cb))
-                       return PTR_ERR(block_cb);
-               err = mlxsw_sp_setup_tc_block_flower_bind(mlxsw_sp_port, f,
-                                                         ingress);
-               if (err) {
-                       flow_block_cb_free(block_cb);
-                       return err;
-               }
-               flow_block_cb_add(block_cb, f);
-               list_add_tail(&block_cb->driver_list, &mlxsw_sp_block_cb_list);
-               return 0;
-       case FLOW_BLOCK_UNBIND:
-               mlxsw_sp_setup_tc_block_flower_unbind(mlxsw_sp_port,
-                                                     f, ingress);
-               block_cb = flow_block_cb_lookup(f->block, cb, mlxsw_sp_port);
-               if (!block_cb)
-                       return -ENOENT;
-
-               flow_block_cb_remove(block_cb, f);
-               list_del(&block_cb->driver_list);
-               return 0;
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
 static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type,
                             void *type_data)
 {
@@ -1791,23 +1373,21 @@ static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type,
        }
 }
 
-
 static int mlxsw_sp_feature_hw_tc(struct net_device *dev, bool enable)
 {
        struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
 
        if (!enable) {
-               if (mlxsw_sp_acl_block_rule_count(mlxsw_sp_port->ing_acl_block) ||
-                   mlxsw_sp_acl_block_rule_count(mlxsw_sp_port->eg_acl_block) ||
-                   !list_empty(&mlxsw_sp_port->mall_tc_list)) {
+               if (mlxsw_sp_flow_block_rule_count(mlxsw_sp_port->ing_flow_block) ||
+                   mlxsw_sp_flow_block_rule_count(mlxsw_sp_port->eg_flow_block)) {
                        netdev_err(dev, "Active offloaded tc filters, can't turn hw_tc_offload off\n");
                        return -EINVAL;
                }
-               mlxsw_sp_acl_block_disable_inc(mlxsw_sp_port->ing_acl_block);
-               mlxsw_sp_acl_block_disable_inc(mlxsw_sp_port->eg_acl_block);
+               mlxsw_sp_flow_block_disable_inc(mlxsw_sp_port->ing_flow_block);
+               mlxsw_sp_flow_block_disable_inc(mlxsw_sp_port->eg_flow_block);
        } else {
-               mlxsw_sp_acl_block_disable_dec(mlxsw_sp_port->ing_acl_block);
-               mlxsw_sp_acl_block_disable_dec(mlxsw_sp_port->eg_acl_block);
+               mlxsw_sp_flow_block_disable_dec(mlxsw_sp_port->ing_flow_block);
+               mlxsw_sp_flow_block_disable_dec(mlxsw_sp_port->eg_flow_block);
        }
        return 0;
 }
@@ -3695,7 +3275,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
        mlxsw_sp_port->mapping = *port_mapping;
        mlxsw_sp_port->link.autoneg = 1;
        INIT_LIST_HEAD(&mlxsw_sp_port->vlans_list);
-       INIT_LIST_HEAD(&mlxsw_sp_port->mall_tc_list);
 
        mlxsw_sp_port->pcpu_stats =
                netdev_alloc_pcpu_stats(struct mlxsw_sp_port_pcpu_stats);
@@ -3704,13 +3283,6 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
                goto err_alloc_stats;
        }
 
-       mlxsw_sp_port->sample = kzalloc(sizeof(*mlxsw_sp_port->sample),
-                                       GFP_KERNEL);
-       if (!mlxsw_sp_port->sample) {
-               err = -ENOMEM;
-               goto err_alloc_sample;
-       }
-
        INIT_DELAYED_WORK(&mlxsw_sp_port->periodic_hw_stats.update_dw,
                          &update_stats_cache);
 
@@ -3897,8 +3469,6 @@ err_dev_addr_init:
 err_port_swid_set:
        mlxsw_sp_port_module_unmap(mlxsw_sp_port);
 err_port_module_map:
-       kfree(mlxsw_sp_port->sample);
-err_alloc_sample:
        free_percpu(mlxsw_sp_port->pcpu_stats);
 err_alloc_stats:
        free_netdev(dev);
@@ -3926,7 +3496,6 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
        mlxsw_sp_port_tc_mc_mode_set(mlxsw_sp_port, false);
        mlxsw_sp_port_swid_set(mlxsw_sp_port, MLXSW_PORT_SWID_DISABLED_PORT);
        mlxsw_sp_port_module_unmap(mlxsw_sp_port);
-       kfree(mlxsw_sp_port->sample);
        free_percpu(mlxsw_sp_port->pcpu_stats);
        WARN_ON_ONCE(!list_empty(&mlxsw_sp_port->vlans_list));
        free_netdev(mlxsw_sp_port->dev);
@@ -4413,7 +3982,7 @@ static void mlxsw_sp_rx_listener_sample_func(struct sk_buff *skb, u8 local_port,
 {
        struct mlxsw_sp *mlxsw_sp = priv;
        struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port];
-       struct psample_group *psample_group;
+       struct mlxsw_sp_port_sample *sample;
        u32 size;
 
        if (unlikely(!mlxsw_sp_port)) {
@@ -4421,22 +3990,14 @@ static void mlxsw_sp_rx_listener_sample_func(struct sk_buff *skb, u8 local_port,
                                     local_port);
                goto out;
        }
-       if (unlikely(!mlxsw_sp_port->sample)) {
-               dev_warn_ratelimited(mlxsw_sp->bus_info->dev, "Port %d: sample skb received on unsupported port\n",
-                                    local_port);
-               goto out;
-       }
-
-       size = mlxsw_sp_port->sample->truncate ?
-                 mlxsw_sp_port->sample->trunc_size : skb->len;
 
        rcu_read_lock();
-       psample_group = rcu_dereference(mlxsw_sp_port->sample->psample_group);
-       if (!psample_group)
+       sample = rcu_dereference(mlxsw_sp_port->sample);
+       if (!sample)
                goto out_unlock;
-       psample_sample_packet(psample_group, skb, size,
-                             mlxsw_sp_port->dev->ifindex, 0,
-                             mlxsw_sp_port->sample->rate);
+       size = sample->truncate ? sample->trunc_size : skb->len;
+       psample_sample_packet(sample->psample_group, skb, size,
+                             mlxsw_sp_port->dev->ifindex, 0, sample->rate);
 out_unlock:
        rcu_read_unlock();
 out:
index ca56e72..a12ca67 100644 (file)
@@ -109,25 +109,6 @@ struct mlxsw_sp_mid {
        unsigned long *ports_in_mid; /* bits array */
 };
 
-enum mlxsw_sp_port_mall_action_type {
-       MLXSW_SP_PORT_MALL_MIRROR,
-       MLXSW_SP_PORT_MALL_SAMPLE,
-};
-
-struct mlxsw_sp_port_mall_mirror_tc_entry {
-       int span_id;
-       bool ingress;
-};
-
-struct mlxsw_sp_port_mall_tc_entry {
-       struct list_head list;
-       unsigned long cookie;
-       enum mlxsw_sp_port_mall_action_type type;
-       union {
-               struct mlxsw_sp_port_mall_mirror_tc_entry mirror;
-       };
-};
-
 struct mlxsw_sp_sb;
 struct mlxsw_sp_bridge;
 struct mlxsw_sp_router;
@@ -211,7 +192,7 @@ struct mlxsw_sp_port_pcpu_stats {
 };
 
 struct mlxsw_sp_port_sample {
-       struct psample_group __rcu *psample_group;
+       struct psample_group *psample_group;
        u32 trunc_size;
        u32 rate;
        bool truncate;
@@ -274,21 +255,19 @@ struct mlxsw_sp_port {
                                               * the same localport can have
                                               * different mapping.
                                               */
-       /* TC handles */
-       struct list_head mall_tc_list;
        struct {
                #define MLXSW_HW_STATS_UPDATE_TIME HZ
                struct rtnl_link_stats64 stats;
                struct mlxsw_sp_port_xstats xstats;
                struct delayed_work update_dw;
        } periodic_hw_stats;
-       struct mlxsw_sp_port_sample *sample;
+       struct mlxsw_sp_port_sample __rcu *sample;
        struct list_head vlans_list;
        struct mlxsw_sp_port_vlan *default_vlan;
        struct mlxsw_sp_qdisc_state *qdisc;
        unsigned acl_rule_count;
-       struct mlxsw_sp_acl_block *ing_acl_block;
-       struct mlxsw_sp_acl_block *eg_acl_block;
+       struct mlxsw_sp_flow_block *ing_flow_block;
+       struct mlxsw_sp_flow_block *eg_flow_block;
        struct {
                struct delayed_work shaper_dw;
                struct hwtstamp_config hwtstamp_config;
@@ -654,17 +633,10 @@ struct mlxsw_sp_acl_rule_info {
        unsigned int counter_index;
 };
 
-struct mlxsw_sp_acl_block;
-struct mlxsw_sp_acl_ruleset;
-
-/* spectrum_acl.c */
-enum mlxsw_sp_acl_profile {
-       MLXSW_SP_ACL_PROFILE_FLOWER,
-       MLXSW_SP_ACL_PROFILE_MR,
-};
-
-struct mlxsw_sp_acl_block {
+/* spectrum_flow.c */
+struct mlxsw_sp_flow_block {
        struct list_head binding_list;
+       struct list_head mall_list;
        struct mlxsw_sp_acl_ruleset *ruleset_zero;
        struct mlxsw_sp *mlxsw_sp;
        unsigned int rule_count;
@@ -676,35 +648,92 @@ struct mlxsw_sp_acl_block {
        struct net *net;
 };
 
+struct mlxsw_sp_flow_block_binding {
+       struct list_head list;
+       struct net_device *dev;
+       struct mlxsw_sp_port *mlxsw_sp_port;
+       bool ingress;
+};
+
+static inline struct mlxsw_sp *
+mlxsw_sp_flow_block_mlxsw_sp(struct mlxsw_sp_flow_block *block)
+{
+       return block->mlxsw_sp;
+}
+
+static inline unsigned int
+mlxsw_sp_flow_block_rule_count(const struct mlxsw_sp_flow_block *block)
+{
+       return block ? block->rule_count : 0;
+}
+
+static inline void
+mlxsw_sp_flow_block_disable_inc(struct mlxsw_sp_flow_block *block)
+{
+       if (block)
+               block->disable_count++;
+}
+
+static inline void
+mlxsw_sp_flow_block_disable_dec(struct mlxsw_sp_flow_block *block)
+{
+       if (block)
+               block->disable_count--;
+}
+
+static inline bool
+mlxsw_sp_flow_block_disabled(const struct mlxsw_sp_flow_block *block)
+{
+       return block->disable_count;
+}
+
+static inline bool
+mlxsw_sp_flow_block_is_egress_bound(const struct mlxsw_sp_flow_block *block)
+{
+       return block->egress_binding_count;
+}
+
+static inline bool
+mlxsw_sp_flow_block_is_ingress_bound(const struct mlxsw_sp_flow_block *block)
+{
+       return block->ingress_binding_count;
+}
+
+static inline bool
+mlxsw_sp_flow_block_is_mixed_bound(const struct mlxsw_sp_flow_block *block)
+{
+       return block->ingress_binding_count && block->egress_binding_count;
+}
+
+struct mlxsw_sp_flow_block *mlxsw_sp_flow_block_create(struct mlxsw_sp *mlxsw_sp,
+                                                      struct net *net);
+void mlxsw_sp_flow_block_destroy(struct mlxsw_sp_flow_block *block);
+int mlxsw_sp_setup_tc_block(struct mlxsw_sp_port *mlxsw_sp_port,
+                           struct flow_block_offload *f);
+
+/* spectrum_acl.c */
+struct mlxsw_sp_acl_ruleset;
+
+enum mlxsw_sp_acl_profile {
+       MLXSW_SP_ACL_PROFILE_FLOWER,
+       MLXSW_SP_ACL_PROFILE_MR,
+};
+
 struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl);
-struct mlxsw_sp *mlxsw_sp_acl_block_mlxsw_sp(struct mlxsw_sp_acl_block *block);
-unsigned int
-mlxsw_sp_acl_block_rule_count(const struct mlxsw_sp_acl_block *block);
-void mlxsw_sp_acl_block_disable_inc(struct mlxsw_sp_acl_block *block);
-void mlxsw_sp_acl_block_disable_dec(struct mlxsw_sp_acl_block *block);
-bool mlxsw_sp_acl_block_disabled(const struct mlxsw_sp_acl_block *block);
-struct mlxsw_sp_acl_block *mlxsw_sp_acl_block_create(struct mlxsw_sp *mlxsw_sp,
-                                                    struct net *net);
-void mlxsw_sp_acl_block_destroy(struct mlxsw_sp_acl_block *block);
-int mlxsw_sp_acl_block_bind(struct mlxsw_sp *mlxsw_sp,
-                           struct mlxsw_sp_acl_block *block,
-                           struct mlxsw_sp_port *mlxsw_sp_port,
-                           bool ingress,
-                           struct netlink_ext_ack *extack);
-int mlxsw_sp_acl_block_unbind(struct mlxsw_sp *mlxsw_sp,
-                             struct mlxsw_sp_acl_block *block,
-                             struct mlxsw_sp_port *mlxsw_sp_port,
-                             bool ingress);
-bool mlxsw_sp_acl_block_is_egress_bound(const struct mlxsw_sp_acl_block *block);
-bool mlxsw_sp_acl_block_is_ingress_bound(const struct mlxsw_sp_acl_block *block);
-bool mlxsw_sp_acl_block_is_mixed_bound(const struct mlxsw_sp_acl_block *block);
+
+int mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp,
+                             struct mlxsw_sp_flow_block *block,
+                             struct mlxsw_sp_flow_block_binding *binding);
+void mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,
+                                struct mlxsw_sp_flow_block *block,
+                                struct mlxsw_sp_flow_block_binding *binding);
 struct mlxsw_sp_acl_ruleset *
 mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp *mlxsw_sp,
-                           struct mlxsw_sp_acl_block *block, u32 chain_index,
+                           struct mlxsw_sp_flow_block *block, u32 chain_index,
                            enum mlxsw_sp_acl_profile profile);
 struct mlxsw_sp_acl_ruleset *
 mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp,
-                        struct mlxsw_sp_acl_block *block, u32 chain_index,
+                        struct mlxsw_sp_flow_block *block, u32 chain_index,
                         enum mlxsw_sp_acl_profile profile,
                         struct mlxsw_afk_element_usage *tmplt_elusage);
 void mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp,
@@ -736,7 +765,7 @@ int mlxsw_sp_acl_rulei_act_drop(struct mlxsw_sp_acl_rule_info *rulei,
 int mlxsw_sp_acl_rulei_act_trap(struct mlxsw_sp_acl_rule_info *rulei);
 int mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp,
                                  struct mlxsw_sp_acl_rule_info *rulei,
-                                 struct mlxsw_sp_acl_block *block,
+                                 struct mlxsw_sp_flow_block *block,
                                  struct net_device *out_dev,
                                  struct netlink_ext_ack *extack);
 int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp,
@@ -857,21 +886,31 @@ extern const struct mlxsw_afa_ops mlxsw_sp2_act_afa_ops;
 extern const struct mlxsw_afk_ops mlxsw_sp1_afk_ops;
 extern const struct mlxsw_afk_ops mlxsw_sp2_afk_ops;
 
+/* spectrum_matchall.c */
+int mlxsw_sp_mall_replace(struct mlxsw_sp_flow_block *block,
+                         struct tc_cls_matchall_offload *f);
+void mlxsw_sp_mall_destroy(struct mlxsw_sp_flow_block *block,
+                          struct tc_cls_matchall_offload *f);
+int mlxsw_sp_mall_port_bind(struct mlxsw_sp_flow_block *block,
+                           struct mlxsw_sp_port *mlxsw_sp_port);
+void mlxsw_sp_mall_port_unbind(struct mlxsw_sp_flow_block *block,
+                              struct mlxsw_sp_port *mlxsw_sp_port);
+
 /* spectrum_flower.c */
 int mlxsw_sp_flower_replace(struct mlxsw_sp *mlxsw_sp,
-                           struct mlxsw_sp_acl_block *block,
+                           struct mlxsw_sp_flow_block *block,
                            struct flow_cls_offload *f);
 void mlxsw_sp_flower_destroy(struct mlxsw_sp *mlxsw_sp,
-                            struct mlxsw_sp_acl_block *block,
+                            struct mlxsw_sp_flow_block *block,
                             struct flow_cls_offload *f);
 int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp,
-                         struct mlxsw_sp_acl_block *block,
+                         struct mlxsw_sp_flow_block *block,
                          struct flow_cls_offload *f);
 int mlxsw_sp_flower_tmplt_create(struct mlxsw_sp *mlxsw_sp,
-                                struct mlxsw_sp_acl_block *block,
+                                struct mlxsw_sp_flow_block *block,
                                 struct flow_cls_offload *f);
 void mlxsw_sp_flower_tmplt_destroy(struct mlxsw_sp *mlxsw_sp,
-                                  struct mlxsw_sp_acl_block *block,
+                                  struct mlxsw_sp_flow_block *block,
                                   struct flow_cls_offload *f);
 
 /* spectrum_qdisc.c */
index 6c66a0f..ad69913 100644 (file)
@@ -88,8 +88,8 @@ static int mlxsw_sp2_acl_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv,
         * to be written using PEFA register to all indexes for all regions.
         */
        afa_block = mlxsw_afa_block_create(mlxsw_sp->afa);
-       if (!afa_block) {
-               err = -ENOMEM;
+       if (IS_ERR(afa_block)) {
+               err = PTR_ERR(afa_block);
                goto err_afa_block;
        }
        err = mlxsw_afa_block_continue(afa_block);
index e31ec75..a11d911 100644 (file)
@@ -9,7 +9,7 @@
 
 struct mlxsw_sp2_mr_tcam {
        struct mlxsw_sp *mlxsw_sp;
-       struct mlxsw_sp_acl_block *acl_block;
+       struct mlxsw_sp_flow_block *flow_block;
        struct mlxsw_sp_acl_ruleset *ruleset4;
        struct mlxsw_sp_acl_ruleset *ruleset6;
 };
@@ -61,7 +61,7 @@ static int mlxsw_sp2_mr_tcam_ipv4_init(struct mlxsw_sp2_mr_tcam *mr_tcam)
                                     mlxsw_sp2_mr_tcam_usage_ipv4,
                                     ARRAY_SIZE(mlxsw_sp2_mr_tcam_usage_ipv4));
        mr_tcam->ruleset4 = mlxsw_sp_acl_ruleset_get(mr_tcam->mlxsw_sp,
-                                                    mr_tcam->acl_block,
+                                                    mr_tcam->flow_block,
                                                     MLXSW_SP_L3_PROTO_IPV4,
                                                     MLXSW_SP_ACL_PROFILE_MR,
                                                     &elusage);
@@ -111,7 +111,7 @@ static int mlxsw_sp2_mr_tcam_ipv6_init(struct mlxsw_sp2_mr_tcam *mr_tcam)
                                     mlxsw_sp2_mr_tcam_usage_ipv6,
                                     ARRAY_SIZE(mlxsw_sp2_mr_tcam_usage_ipv6));
        mr_tcam->ruleset6 = mlxsw_sp_acl_ruleset_get(mr_tcam->mlxsw_sp,
-                                                    mr_tcam->acl_block,
+                                                    mr_tcam->flow_block,
                                                     MLXSW_SP_L3_PROTO_IPV6,
                                                     MLXSW_SP_ACL_PROFILE_MR,
                                                     &elusage);
@@ -289,8 +289,8 @@ static int mlxsw_sp2_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv)
        int err;
 
        mr_tcam->mlxsw_sp = mlxsw_sp;
-       mr_tcam->acl_block = mlxsw_sp_acl_block_create(mlxsw_sp, NULL);
-       if (!mr_tcam->acl_block)
+       mr_tcam->flow_block = mlxsw_sp_flow_block_create(mlxsw_sp, NULL);
+       if (!mr_tcam->flow_block)
                return -ENOMEM;
 
        err = mlxsw_sp2_mr_tcam_ipv4_init(mr_tcam);
@@ -306,7 +306,7 @@ static int mlxsw_sp2_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv)
 err_ipv6_init:
        mlxsw_sp2_mr_tcam_ipv4_fini(mr_tcam);
 err_ipv4_init:
-       mlxsw_sp_acl_block_destroy(mr_tcam->acl_block);
+       mlxsw_sp_flow_block_destroy(mr_tcam->flow_block);
        return err;
 }
 
@@ -316,7 +316,7 @@ static void mlxsw_sp2_mr_tcam_fini(void *priv)
 
        mlxsw_sp2_mr_tcam_ipv6_fini(mr_tcam);
        mlxsw_sp2_mr_tcam_ipv4_fini(mr_tcam);
-       mlxsw_sp_acl_block_destroy(mr_tcam->acl_block);
+       mlxsw_sp_flow_block_destroy(mr_tcam->flow_block);
 }
 
 const struct mlxsw_sp_mr_tcam_ops mlxsw_sp2_mr_tcam_ops = {
index 67ee880..c61f78e 100644 (file)
@@ -40,15 +40,8 @@ struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl)
        return acl->afk;
 }
 
-struct mlxsw_sp_acl_block_binding {
-       struct list_head list;
-       struct net_device *dev;
-       struct mlxsw_sp_port *mlxsw_sp_port;
-       bool ingress;
-};
-
 struct mlxsw_sp_acl_ruleset_ht_key {
-       struct mlxsw_sp_acl_block *block;
+       struct mlxsw_sp_flow_block *block;
        u32 chain_index;
        const struct mlxsw_sp_acl_profile_ops *ops;
 };
@@ -94,49 +87,6 @@ struct mlxsw_sp_fid *mlxsw_sp_acl_dummy_fid(struct mlxsw_sp *mlxsw_sp)
        return mlxsw_sp->acl->dummy_fid;
 }
 
-struct mlxsw_sp *mlxsw_sp_acl_block_mlxsw_sp(struct mlxsw_sp_acl_block *block)
-{
-       return block->mlxsw_sp;
-}
-
-unsigned int
-mlxsw_sp_acl_block_rule_count(const struct mlxsw_sp_acl_block *block)
-{
-       return block ? block->rule_count : 0;
-}
-
-void mlxsw_sp_acl_block_disable_inc(struct mlxsw_sp_acl_block *block)
-{
-       if (block)
-               block->disable_count++;
-}
-
-void mlxsw_sp_acl_block_disable_dec(struct mlxsw_sp_acl_block *block)
-{
-       if (block)
-               block->disable_count--;
-}
-
-bool mlxsw_sp_acl_block_disabled(const struct mlxsw_sp_acl_block *block)
-{
-       return block->disable_count;
-}
-
-bool mlxsw_sp_acl_block_is_egress_bound(const struct mlxsw_sp_acl_block *block)
-{
-       return block->egress_binding_count;
-}
-
-bool mlxsw_sp_acl_block_is_ingress_bound(const struct mlxsw_sp_acl_block *block)
-{
-       return block->ingress_binding_count;
-}
-
-bool mlxsw_sp_acl_block_is_mixed_bound(const struct mlxsw_sp_acl_block *block)
-{
-       return block->ingress_binding_count && block->egress_binding_count;
-}
-
 static bool
 mlxsw_sp_acl_ruleset_is_singular(const struct mlxsw_sp_acl_ruleset *ruleset)
 {
@@ -144,10 +94,9 @@ mlxsw_sp_acl_ruleset_is_singular(const struct mlxsw_sp_acl_ruleset *ruleset)
        return ruleset->ref_count == 2;
 }
 
-static int
-mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp,
-                         struct mlxsw_sp_acl_block *block,
-                         struct mlxsw_sp_acl_block_binding *binding)
+int mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp,
+                             struct mlxsw_sp_flow_block *block,
+                             struct mlxsw_sp_flow_block_binding *binding)
 {
        struct mlxsw_sp_acl_ruleset *ruleset = block->ruleset_zero;
        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
@@ -156,10 +105,9 @@ mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp,
                                 binding->mlxsw_sp_port, binding->ingress);
 }
 
-static void
-mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,
-                           struct mlxsw_sp_acl_block *block,
-                           struct mlxsw_sp_acl_block_binding *binding)
+void mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,
+                                struct mlxsw_sp_flow_block *block,
+                                struct mlxsw_sp_flow_block_binding *binding)
 {
        struct mlxsw_sp_acl_ruleset *ruleset = block->ruleset_zero;
        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
@@ -168,18 +116,12 @@ mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp,
                            binding->mlxsw_sp_port, binding->ingress);
 }
 
-static bool
-mlxsw_sp_acl_ruleset_block_bound(const struct mlxsw_sp_acl_block *block)
-{
-       return block->ruleset_zero;
-}
-
 static int
 mlxsw_sp_acl_ruleset_block_bind(struct mlxsw_sp *mlxsw_sp,
                                struct mlxsw_sp_acl_ruleset *ruleset,
-                               struct mlxsw_sp_acl_block *block)
+                               struct mlxsw_sp_flow_block *block)
 {
-       struct mlxsw_sp_acl_block_binding *binding;
+       struct mlxsw_sp_flow_block_binding *binding;
        int err;
 
        block->ruleset_zero = ruleset;
@@ -202,122 +144,18 @@ rollback:
 static void
 mlxsw_sp_acl_ruleset_block_unbind(struct mlxsw_sp *mlxsw_sp,
                                  struct mlxsw_sp_acl_ruleset *ruleset,
-                                 struct mlxsw_sp_acl_block *block)
+                                 struct mlxsw_sp_flow_block *block)
 {
-       struct mlxsw_sp_acl_block_binding *binding;
+       struct mlxsw_sp_flow_block_binding *binding;
 
        list_for_each_entry(binding, &block->binding_list, list)
                mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding);
        block->ruleset_zero = NULL;
 }
 
-struct mlxsw_sp_acl_block *mlxsw_sp_acl_block_create(struct mlxsw_sp *mlxsw_sp,
-                                                    struct net *net)
-{
-       struct mlxsw_sp_acl_block *block;
-
-       block = kzalloc(sizeof(*block), GFP_KERNEL);
-       if (!block)
-               return NULL;
-       INIT_LIST_HEAD(&block->binding_list);
-       block->mlxsw_sp = mlxsw_sp;
-       block->net = net;
-       return block;
-}
-
-void mlxsw_sp_acl_block_destroy(struct mlxsw_sp_acl_block *block)
-{
-       WARN_ON(!list_empty(&block->binding_list));
-       kfree(block);
-}
-
-static struct mlxsw_sp_acl_block_binding *
-mlxsw_sp_acl_block_lookup(struct mlxsw_sp_acl_block *block,
-                         struct mlxsw_sp_port *mlxsw_sp_port, bool ingress)
-{
-       struct mlxsw_sp_acl_block_binding *binding;
-
-       list_for_each_entry(binding, &block->binding_list, list)
-               if (binding->mlxsw_sp_port == mlxsw_sp_port &&
-                   binding->ingress == ingress)
-                       return binding;
-       return NULL;
-}
-
-int mlxsw_sp_acl_block_bind(struct mlxsw_sp *mlxsw_sp,
-                           struct mlxsw_sp_acl_block *block,
-                           struct mlxsw_sp_port *mlxsw_sp_port,
-                           bool ingress,
-                           struct netlink_ext_ack *extack)
-{
-       struct mlxsw_sp_acl_block_binding *binding;
-       int err;
-
-       if (WARN_ON(mlxsw_sp_acl_block_lookup(block, mlxsw_sp_port, ingress)))
-               return -EEXIST;
-
-       if (ingress && block->ingress_blocker_rule_count) {
-               NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to ingress because it contains unsupported rules");
-               return -EOPNOTSUPP;
-       }
-
-       if (!ingress && block->egress_blocker_rule_count) {
-               NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to egress because it contains unsupported rules");
-               return -EOPNOTSUPP;
-       }
-
-       binding = kzalloc(sizeof(*binding), GFP_KERNEL);
-       if (!binding)
-               return -ENOMEM;
-       binding->mlxsw_sp_port = mlxsw_sp_port;
-       binding->ingress = ingress;
-
-       if (mlxsw_sp_acl_ruleset_block_bound(block)) {
-               err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, block, binding);
-               if (err)
-                       goto err_ruleset_bind;
-       }
-
-       if (ingress)
-               block->ingress_binding_count++;
-       else
-               block->egress_binding_count++;
-       list_add(&binding->list, &block->binding_list);
-       return 0;
-
-err_ruleset_bind:
-       kfree(binding);
-       return err;
-}
-
-int mlxsw_sp_acl_block_unbind(struct mlxsw_sp *mlxsw_sp,
-                             struct mlxsw_sp_acl_block *block,
-                             struct mlxsw_sp_port *mlxsw_sp_port,
-                             bool ingress)
-{
-       struct mlxsw_sp_acl_block_binding *binding;
-
-       binding = mlxsw_sp_acl_block_lookup(block, mlxsw_sp_port, ingress);
-       if (!binding)
-               return -ENOENT;
-
-       list_del(&binding->list);
-
-       if (ingress)
-               block->ingress_binding_count--;
-       else
-               block->egress_binding_count--;
-
-       if (mlxsw_sp_acl_ruleset_block_bound(block))
-               mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding);
-
-       kfree(binding);
-       return 0;
-}
-
 static struct mlxsw_sp_acl_ruleset *
 mlxsw_sp_acl_ruleset_create(struct mlxsw_sp *mlxsw_sp,
-                           struct mlxsw_sp_acl_block *block, u32 chain_index,
+                           struct mlxsw_sp_flow_block *block, u32 chain_index,
                            const struct mlxsw_sp_acl_profile_ops *ops,
                            struct mlxsw_afk_element_usage *tmplt_elusage)
 {
@@ -388,7 +226,7 @@ static void mlxsw_sp_acl_ruleset_ref_dec(struct mlxsw_sp *mlxsw_sp,
 
 static struct mlxsw_sp_acl_ruleset *
 __mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp_acl *acl,
-                             struct mlxsw_sp_acl_block *block, u32 chain_index,
+                             struct mlxsw_sp_flow_block *block, u32 chain_index,
                              const struct mlxsw_sp_acl_profile_ops *ops)
 {
        struct mlxsw_sp_acl_ruleset_ht_key ht_key;
@@ -403,7 +241,7 @@ __mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp_acl *acl,
 
 struct mlxsw_sp_acl_ruleset *
 mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp *mlxsw_sp,
-                           struct mlxsw_sp_acl_block *block, u32 chain_index,
+                           struct mlxsw_sp_flow_block *block, u32 chain_index,
                            enum mlxsw_sp_acl_profile profile)
 {
        const struct mlxsw_sp_acl_profile_ops *ops;
@@ -421,7 +259,7 @@ mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp *mlxsw_sp,
 
 struct mlxsw_sp_acl_ruleset *
 mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp,
-                        struct mlxsw_sp_acl_block *block, u32 chain_index,
+                        struct mlxsw_sp_flow_block *block, u32 chain_index,
                         enum mlxsw_sp_acl_profile profile,
                         struct mlxsw_afk_element_usage *tmplt_elusage)
 {
@@ -464,7 +302,7 @@ mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl,
 
        rulei = kzalloc(sizeof(*rulei), GFP_KERNEL);
        if (!rulei)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        if (afa_block) {
                rulei->act_block = afa_block;
@@ -584,11 +422,11 @@ int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp,
 
 int mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp,
                                  struct mlxsw_sp_acl_rule_info *rulei,
-                                 struct mlxsw_sp_acl_block *block,
+                                 struct mlxsw_sp_flow_block *block,
                                  struct net_device *out_dev,
                                  struct netlink_ext_ack *extack)
 {
-       struct mlxsw_sp_acl_block_binding *binding;
+       struct mlxsw_sp_flow_block_binding *binding;
        struct mlxsw_sp_port *in_port;
 
        if (!list_is_singular(&block->binding_list)) {
@@ -596,7 +434,7 @@ int mlxsw_sp_acl_rulei_act_mirror(struct mlxsw_sp *mlxsw_sp,
                return -EOPNOTSUPP;
        }
        binding = list_first_entry(&block->binding_list,
-                                  struct mlxsw_sp_acl_block_binding, list);
+                                  struct mlxsw_sp_flow_block_binding, list);
        in_port = binding->mlxsw_sp_port;
 
        return mlxsw_afa_block_append_mirror(rulei->act_block,
@@ -818,7 +656,7 @@ int mlxsw_sp_acl_rule_add(struct mlxsw_sp *mlxsw_sp,
 {
        struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
-       struct mlxsw_sp_acl_block *block = ruleset->ht_key.block;
+       struct mlxsw_sp_flow_block *block = ruleset->ht_key.block;
        int err;
 
        err = ops->rule_add(mlxsw_sp, ruleset->priv, rule->priv, rule->rulei);
@@ -862,18 +700,17 @@ void mlxsw_sp_acl_rule_del(struct mlxsw_sp *mlxsw_sp,
 {
        struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset;
        const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops;
-       struct mlxsw_sp_acl_block *block = ruleset->ht_key.block;
+       struct mlxsw_sp_flow_block *block = ruleset->ht_key.block;
 
        block->egress_blocker_rule_count -= rule->rulei->egress_bind_blocker;
        block->ingress_blocker_rule_count -= rule->rulei->ingress_bind_blocker;
-       ruleset->ht_key.block->rule_count--;
+       block->rule_count--;
        mutex_lock(&mlxsw_sp->acl->rules_lock);
        list_del(&rule->list);
        mutex_unlock(&mlxsw_sp->acl->rules_lock);
        if (!ruleset->ht_key.chain_index &&
            mlxsw_sp_acl_ruleset_is_singular(ruleset))
-               mlxsw_sp_acl_ruleset_block_unbind(mlxsw_sp, ruleset,
-                                                 ruleset->ht_key.block);
+               mlxsw_sp_acl_ruleset_block_unbind(mlxsw_sp, ruleset, block);
        rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node,
                               mlxsw_sp_acl_rule_ht_params);
        ops->rule_del(mlxsw_sp, rule->priv);
index e47d1d2..73d5601 100644 (file)
@@ -136,28 +136,35 @@ mlxsw_sp_act_mirror_add(void *priv, u8 local_in_port,
                        const struct net_device *out_dev,
                        bool ingress, int *p_span_id)
 {
-       struct mlxsw_sp_port *in_port;
+       struct mlxsw_sp_port *mlxsw_sp_port;
        struct mlxsw_sp *mlxsw_sp = priv;
-       enum mlxsw_sp_span_type type;
+       int err;
 
-       type = ingress ? MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
-       in_port = mlxsw_sp->ports[local_in_port];
+       err = mlxsw_sp_span_agent_get(mlxsw_sp, out_dev, p_span_id);
+       if (err)
+               return err;
+
+       mlxsw_sp_port = mlxsw_sp->ports[local_in_port];
+       err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, ingress);
+       if (err)
+               goto err_analyzed_port_get;
 
-       return mlxsw_sp_span_mirror_add(in_port, out_dev, type,
-                                       false, p_span_id);
+       return 0;
+
+err_analyzed_port_get:
+       mlxsw_sp_span_agent_put(mlxsw_sp, *p_span_id);
+       return err;
 }
 
 static void
 mlxsw_sp_act_mirror_del(void *priv, u8 local_in_port, int span_id, bool ingress)
 {
+       struct mlxsw_sp_port *mlxsw_sp_port;
        struct mlxsw_sp *mlxsw_sp = priv;
-       struct mlxsw_sp_port *in_port;
-       enum mlxsw_sp_span_type type;
-
-       type = ingress ? MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
-       in_port = mlxsw_sp->ports[local_in_port];
 
-       mlxsw_sp_span_mirror_del(in_port, span_id, type, false);
+       mlxsw_sp_port = mlxsw_sp->ports[local_in_port];
+       mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress);
+       mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
 }
 
 const struct mlxsw_afa_ops mlxsw_sp1_act_afa_ops = {
index 430da69..a6e30e0 100644 (file)
@@ -986,8 +986,9 @@ mlxsw_sp_acl_tcam_vchunk_create(struct mlxsw_sp *mlxsw_sp,
                                unsigned int priority,
                                struct mlxsw_afk_element_usage *elusage)
 {
+       struct mlxsw_sp_acl_tcam_vchunk *vchunk, *vchunk2;
        struct mlxsw_sp_acl_tcam_vregion *vregion;
-       struct mlxsw_sp_acl_tcam_vchunk *vchunk;
+       struct list_head *pos;
        int err;
 
        if (priority == MLXSW_SP_ACL_TCAM_CATCHALL_PRIO)
@@ -1025,7 +1026,14 @@ mlxsw_sp_acl_tcam_vchunk_create(struct mlxsw_sp *mlxsw_sp,
        }
 
        mlxsw_sp_acl_tcam_rehash_ctx_vregion_changed(vregion);
-       list_add_tail(&vchunk->list, &vregion->vchunk_list);
+
+       /* Position the vchunk inside the list according to priority */
+       list_for_each(pos, &vregion->vchunk_list) {
+               vchunk2 = list_entry(pos, typeof(*vchunk2), list);
+               if (vchunk2->priority > priority)
+                       break;
+       }
+       list_add_tail(&vchunk->list, pos);
        mutex_unlock(&vregion->lock);
 
        return vchunk;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flow.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flow.c
new file mode 100644 (file)
index 0000000..ecab581
--- /dev/null
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2017-2020 Mellanox Technologies. All rights reserved */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <net/net_namespace.h>
+
+#include "spectrum.h"
+
+struct mlxsw_sp_flow_block *
+mlxsw_sp_flow_block_create(struct mlxsw_sp *mlxsw_sp, struct net *net)
+{
+       struct mlxsw_sp_flow_block *block;
+
+       block = kzalloc(sizeof(*block), GFP_KERNEL);
+       if (!block)
+               return NULL;
+       INIT_LIST_HEAD(&block->binding_list);
+       INIT_LIST_HEAD(&block->mall_list);
+       block->mlxsw_sp = mlxsw_sp;
+       block->net = net;
+       return block;
+}
+
+void mlxsw_sp_flow_block_destroy(struct mlxsw_sp_flow_block *block)
+{
+       WARN_ON(!list_empty(&block->binding_list));
+       kfree(block);
+}
+
+static struct mlxsw_sp_flow_block_binding *
+mlxsw_sp_flow_block_lookup(struct mlxsw_sp_flow_block *block,
+                          struct mlxsw_sp_port *mlxsw_sp_port, bool ingress)
+{
+       struct mlxsw_sp_flow_block_binding *binding;
+
+       list_for_each_entry(binding, &block->binding_list, list)
+               if (binding->mlxsw_sp_port == mlxsw_sp_port &&
+                   binding->ingress == ingress)
+                       return binding;
+       return NULL;
+}
+
+static bool
+mlxsw_sp_flow_block_ruleset_bound(const struct mlxsw_sp_flow_block *block)
+{
+       return block->ruleset_zero;
+}
+
+static int mlxsw_sp_flow_block_bind(struct mlxsw_sp *mlxsw_sp,
+                                   struct mlxsw_sp_flow_block *block,
+                                   struct mlxsw_sp_port *mlxsw_sp_port,
+                                   bool ingress,
+                                   struct netlink_ext_ack *extack)
+{
+       struct mlxsw_sp_flow_block_binding *binding;
+       int err;
+
+       if (WARN_ON(mlxsw_sp_flow_block_lookup(block, mlxsw_sp_port, ingress)))
+               return -EEXIST;
+
+       if (ingress && block->ingress_blocker_rule_count) {
+               NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to ingress because it contains unsupported rules");
+               return -EOPNOTSUPP;
+       }
+
+       if (!ingress && block->egress_blocker_rule_count) {
+               NL_SET_ERR_MSG_MOD(extack, "Block cannot be bound to egress because it contains unsupported rules");
+               return -EOPNOTSUPP;
+       }
+
+       err = mlxsw_sp_mall_port_bind(block, mlxsw_sp_port);
+       if (err)
+               return err;
+
+       binding = kzalloc(sizeof(*binding), GFP_KERNEL);
+       if (!binding) {
+               err = -ENOMEM;
+               goto err_binding_alloc;
+       }
+       binding->mlxsw_sp_port = mlxsw_sp_port;
+       binding->ingress = ingress;
+
+       if (mlxsw_sp_flow_block_ruleset_bound(block)) {
+               err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, block, binding);
+               if (err)
+                       goto err_ruleset_bind;
+       }
+
+       if (ingress)
+               block->ingress_binding_count++;
+       else
+               block->egress_binding_count++;
+       list_add(&binding->list, &block->binding_list);
+       return 0;
+
+err_ruleset_bind:
+       kfree(binding);
+err_binding_alloc:
+       mlxsw_sp_mall_port_unbind(block, mlxsw_sp_port);
+
+       return err;
+}
+
+static int mlxsw_sp_flow_block_unbind(struct mlxsw_sp *mlxsw_sp,
+                                     struct mlxsw_sp_flow_block *block,
+                                     struct mlxsw_sp_port *mlxsw_sp_port,
+                                     bool ingress)
+{
+       struct mlxsw_sp_flow_block_binding *binding;
+
+       binding = mlxsw_sp_flow_block_lookup(block, mlxsw_sp_port, ingress);
+       if (!binding)
+               return -ENOENT;
+
+       list_del(&binding->list);
+
+       if (ingress)
+               block->ingress_binding_count--;
+       else
+               block->egress_binding_count--;
+
+       if (mlxsw_sp_flow_block_ruleset_bound(block))
+               mlxsw_sp_acl_ruleset_unbind(mlxsw_sp, block, binding);
+
+       kfree(binding);
+
+       mlxsw_sp_mall_port_unbind(block, mlxsw_sp_port);
+
+       return 0;
+}
+
+static int mlxsw_sp_flow_block_mall_cb(struct mlxsw_sp_flow_block *flow_block,
+                                      struct tc_cls_matchall_offload *f)
+{
+       switch (f->command) {
+       case TC_CLSMATCHALL_REPLACE:
+               return mlxsw_sp_mall_replace(flow_block, f);
+       case TC_CLSMATCHALL_DESTROY:
+               mlxsw_sp_mall_destroy(flow_block, f);
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int mlxsw_sp_flow_block_flower_cb(struct mlxsw_sp_flow_block *flow_block,
+                                        struct flow_cls_offload *f)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_flow_block_mlxsw_sp(flow_block);
+
+       switch (f->command) {
+       case FLOW_CLS_REPLACE:
+               return mlxsw_sp_flower_replace(mlxsw_sp, flow_block, f);
+       case FLOW_CLS_DESTROY:
+               mlxsw_sp_flower_destroy(mlxsw_sp, flow_block, f);
+               return 0;
+       case FLOW_CLS_STATS:
+               return mlxsw_sp_flower_stats(mlxsw_sp, flow_block, f);
+       case FLOW_CLS_TMPLT_CREATE:
+               return mlxsw_sp_flower_tmplt_create(mlxsw_sp, flow_block, f);
+       case FLOW_CLS_TMPLT_DESTROY:
+               mlxsw_sp_flower_tmplt_destroy(mlxsw_sp, flow_block, f);
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int mlxsw_sp_flow_block_cb(enum tc_setup_type type,
+                                 void *type_data, void *cb_priv)
+{
+       struct mlxsw_sp_flow_block *flow_block = cb_priv;
+
+       if (mlxsw_sp_flow_block_disabled(flow_block))
+               return -EOPNOTSUPP;
+
+       switch (type) {
+       case TC_SETUP_CLSMATCHALL:
+               return mlxsw_sp_flow_block_mall_cb(flow_block, type_data);
+       case TC_SETUP_CLSFLOWER:
+               return mlxsw_sp_flow_block_flower_cb(flow_block, type_data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static void mlxsw_sp_tc_block_release(void *cb_priv)
+{
+       struct mlxsw_sp_flow_block *flow_block = cb_priv;
+
+       mlxsw_sp_flow_block_destroy(flow_block);
+}
+
+static LIST_HEAD(mlxsw_sp_block_cb_list);
+
+static int mlxsw_sp_setup_tc_block_bind(struct mlxsw_sp_port *mlxsw_sp_port,
+                                       struct flow_block_offload *f,
+                                       bool ingress)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       struct mlxsw_sp_flow_block *flow_block;
+       struct flow_block_cb *block_cb;
+       bool register_block = false;
+       int err;
+
+       block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_flow_block_cb,
+                                       mlxsw_sp);
+       if (!block_cb) {
+               flow_block = mlxsw_sp_flow_block_create(mlxsw_sp, f->net);
+               if (!flow_block)
+                       return -ENOMEM;
+               block_cb = flow_block_cb_alloc(mlxsw_sp_flow_block_cb,
+                                              mlxsw_sp, flow_block,
+                                              mlxsw_sp_tc_block_release);
+               if (IS_ERR(block_cb)) {
+                       mlxsw_sp_flow_block_destroy(flow_block);
+                       err = PTR_ERR(block_cb);
+                       goto err_cb_register;
+               }
+               register_block = true;
+       } else {
+               flow_block = flow_block_cb_priv(block_cb);
+       }
+       flow_block_cb_incref(block_cb);
+       err = mlxsw_sp_flow_block_bind(mlxsw_sp, flow_block,
+                                      mlxsw_sp_port, ingress, f->extack);
+       if (err)
+               goto err_block_bind;
+
+       if (ingress)
+               mlxsw_sp_port->ing_flow_block = flow_block;
+       else
+               mlxsw_sp_port->eg_flow_block = flow_block;
+
+       if (register_block) {
+               flow_block_cb_add(block_cb, f);
+               list_add_tail(&block_cb->driver_list, &mlxsw_sp_block_cb_list);
+       }
+
+       return 0;
+
+err_block_bind:
+       if (!flow_block_cb_decref(block_cb))
+               flow_block_cb_free(block_cb);
+err_cb_register:
+       return err;
+}
+
+static void mlxsw_sp_setup_tc_block_unbind(struct mlxsw_sp_port *mlxsw_sp_port,
+                                          struct flow_block_offload *f,
+                                          bool ingress)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       struct mlxsw_sp_flow_block *flow_block;
+       struct flow_block_cb *block_cb;
+       int err;
+
+       block_cb = flow_block_cb_lookup(f->block, mlxsw_sp_flow_block_cb,
+                                       mlxsw_sp);
+       if (!block_cb)
+               return;
+
+       if (ingress)
+               mlxsw_sp_port->ing_flow_block = NULL;
+       else
+               mlxsw_sp_port->eg_flow_block = NULL;
+
+       flow_block = flow_block_cb_priv(block_cb);
+       err = mlxsw_sp_flow_block_unbind(mlxsw_sp, flow_block,
+                                        mlxsw_sp_port, ingress);
+       if (!err && !flow_block_cb_decref(block_cb)) {
+               flow_block_cb_remove(block_cb, f);
+               list_del(&block_cb->driver_list);
+       }
+}
+
+int mlxsw_sp_setup_tc_block(struct mlxsw_sp_port *mlxsw_sp_port,
+                           struct flow_block_offload *f)
+{
+       bool ingress;
+
+       if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+               ingress = true;
+       else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
+               ingress = false;
+       else
+               return -EOPNOTSUPP;
+
+       f->driver_block_list = &mlxsw_sp_block_cb_list;
+
+       switch (f->command) {
+       case FLOW_BLOCK_BIND:
+               return mlxsw_sp_setup_tc_block_bind(mlxsw_sp_port, f, ingress);
+       case FLOW_BLOCK_UNBIND:
+               mlxsw_sp_setup_tc_block_unbind(mlxsw_sp_port, f, ingress);
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
index 51117a5..0897ca1 100644 (file)
@@ -15,7 +15,7 @@
 #include "core_acl_flex_keys.h"
 
 static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
-                                        struct mlxsw_sp_acl_block *block,
+                                        struct mlxsw_sp_flow_block *block,
                                         struct mlxsw_sp_acl_rule_info *rulei,
                                         struct flow_action *flow_action,
                                         struct netlink_ext_ack *extack)
@@ -36,7 +36,8 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
                err = mlxsw_sp_acl_rulei_act_count(mlxsw_sp, rulei, extack);
                if (err)
                        return err;
-       } else if (act->hw_stats != FLOW_ACTION_HW_STATS_DISABLED) {
+       } else if (act->hw_stats != FLOW_ACTION_HW_STATS_DISABLED &&
+                  act->hw_stats != FLOW_ACTION_HW_STATS_DONT_CARE) {
                NL_SET_ERR_MSG_MOD(extack, "Unsupported action HW stats type");
                return -EOPNOTSUPP;
        }
@@ -53,11 +54,11 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
                case FLOW_ACTION_DROP: {
                        bool ingress;
 
-                       if (mlxsw_sp_acl_block_is_mixed_bound(block)) {
+                       if (mlxsw_sp_flow_block_is_mixed_bound(block)) {
                                NL_SET_ERR_MSG_MOD(extack, "Drop action is not supported when block is bound to ingress and egress");
                                return -EOPNOTSUPP;
                        }
-                       ingress = mlxsw_sp_acl_block_is_ingress_bound(block);
+                       ingress = mlxsw_sp_flow_block_is_ingress_bound(block);
                        err = mlxsw_sp_acl_rulei_act_drop(rulei, ingress,
                                                          act->cookie, extack);
                        if (err) {
@@ -106,7 +107,7 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
                        struct mlxsw_sp_fid *fid;
                        u16 fid_index;
 
-                       if (mlxsw_sp_acl_block_is_egress_bound(block)) {
+                       if (mlxsw_sp_flow_block_is_egress_bound(block)) {
                                NL_SET_ERR_MSG_MOD(extack, "Redirect action is not supported on egress");
                                return -EOPNOTSUPP;
                        }
@@ -190,7 +191,7 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
 
 static int mlxsw_sp_flower_parse_meta(struct mlxsw_sp_acl_rule_info *rulei,
                                      struct flow_cls_offload *f,
-                                     struct mlxsw_sp_acl_block *block)
+                                     struct mlxsw_sp_flow_block *block)
 {
        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
        struct mlxsw_sp_port *mlxsw_sp_port;
@@ -371,7 +372,7 @@ static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp,
 }
 
 static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp,
-                                struct mlxsw_sp_acl_block *block,
+                                struct mlxsw_sp_flow_block *block,
                                 struct mlxsw_sp_acl_rule_info *rulei,
                                 struct flow_cls_offload *f)
 {
@@ -460,7 +461,7 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp,
                struct flow_match_vlan match;
 
                flow_rule_match_vlan(rule, &match);
-               if (mlxsw_sp_acl_block_is_egress_bound(block)) {
+               if (mlxsw_sp_flow_block_is_egress_bound(block)) {
                        NL_SET_ERR_MSG_MOD(f->common.extack, "vlan_id key is not supported on egress");
                        return -EOPNOTSUPP;
                }
@@ -505,7 +506,7 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp,
 }
 
 int mlxsw_sp_flower_replace(struct mlxsw_sp *mlxsw_sp,
-                           struct mlxsw_sp_acl_block *block,
+                           struct mlxsw_sp_flow_block *block,
                            struct flow_cls_offload *f)
 {
        struct mlxsw_sp_acl_rule_info *rulei;
@@ -552,7 +553,7 @@ err_rule_create:
 }
 
 void mlxsw_sp_flower_destroy(struct mlxsw_sp *mlxsw_sp,
-                            struct mlxsw_sp_acl_block *block,
+                            struct mlxsw_sp_flow_block *block,
                             struct flow_cls_offload *f)
 {
        struct mlxsw_sp_acl_ruleset *ruleset;
@@ -574,7 +575,7 @@ void mlxsw_sp_flower_destroy(struct mlxsw_sp *mlxsw_sp,
 }
 
 int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp,
-                         struct mlxsw_sp_acl_block *block,
+                         struct mlxsw_sp_flow_block *block,
                          struct flow_cls_offload *f)
 {
        enum flow_action_hw_stats used_hw_stats = FLOW_ACTION_HW_STATS_DISABLED;
@@ -611,7 +612,7 @@ err_rule_get_stats:
 }
 
 int mlxsw_sp_flower_tmplt_create(struct mlxsw_sp *mlxsw_sp,
-                                struct mlxsw_sp_acl_block *block,
+                                struct mlxsw_sp_flow_block *block,
                                 struct flow_cls_offload *f)
 {
        struct mlxsw_sp_acl_ruleset *ruleset;
@@ -632,7 +633,7 @@ int mlxsw_sp_flower_tmplt_create(struct mlxsw_sp *mlxsw_sp,
 }
 
 void mlxsw_sp_flower_tmplt_destroy(struct mlxsw_sp *mlxsw_sp,
-                                  struct mlxsw_sp_acl_block *block,
+                                  struct mlxsw_sp_flow_block *block,
                                   struct flow_cls_offload *f)
 {
        struct mlxsw_sp_acl_ruleset *ruleset;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_matchall.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_matchall.c
new file mode 100644 (file)
index 0000000..da1c05f
--- /dev/null
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/* Copyright (c) 2017-2020 Mellanox Technologies. All rights reserved */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <net/flow_offload.h>
+
+#include "spectrum.h"
+#include "spectrum_span.h"
+#include "reg.h"
+
+enum mlxsw_sp_mall_action_type {
+       MLXSW_SP_MALL_ACTION_TYPE_MIRROR,
+       MLXSW_SP_MALL_ACTION_TYPE_SAMPLE,
+};
+
+struct mlxsw_sp_mall_mirror_entry {
+       const struct net_device *to_dev;
+       int span_id;
+};
+
+struct mlxsw_sp_mall_entry {
+       struct list_head list;
+       unsigned long cookie;
+       enum mlxsw_sp_mall_action_type type;
+       bool ingress;
+       union {
+               struct mlxsw_sp_mall_mirror_entry mirror;
+               struct mlxsw_sp_port_sample sample;
+       };
+       struct rcu_head rcu;
+};
+
+static struct mlxsw_sp_mall_entry *
+mlxsw_sp_mall_entry_find(struct mlxsw_sp_flow_block *block, unsigned long cookie)
+{
+       struct mlxsw_sp_mall_entry *mall_entry;
+
+       list_for_each_entry(mall_entry, &block->mall_list, list)
+               if (mall_entry->cookie == cookie)
+                       return mall_entry;
+
+       return NULL;
+}
+
+static int
+mlxsw_sp_mall_port_mirror_add(struct mlxsw_sp_port *mlxsw_sp_port,
+                             struct mlxsw_sp_mall_entry *mall_entry)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       struct mlxsw_sp_span_trigger_parms parms;
+       enum mlxsw_sp_span_trigger trigger;
+       int err;
+
+       if (!mall_entry->mirror.to_dev) {
+               netdev_err(mlxsw_sp_port->dev, "Could not find requested device\n");
+               return -EINVAL;
+       }
+
+       err = mlxsw_sp_span_agent_get(mlxsw_sp, mall_entry->mirror.to_dev,
+                                     &mall_entry->mirror.span_id);
+       if (err)
+               return err;
+
+       err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port,
+                                             mall_entry->ingress);
+       if (err)
+               goto err_analyzed_port_get;
+
+       trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
+                                       MLXSW_SP_SPAN_TRIGGER_EGRESS;
+       parms.span_id = mall_entry->mirror.span_id;
+       err = mlxsw_sp_span_agent_bind(mlxsw_sp, trigger, mlxsw_sp_port,
+                                      &parms);
+       if (err)
+               goto err_agent_bind;
+
+       return 0;
+
+err_agent_bind:
+       mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress);
+err_analyzed_port_get:
+       mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->mirror.span_id);
+       return err;
+}
+
+static void
+mlxsw_sp_mall_port_mirror_del(struct mlxsw_sp_port *mlxsw_sp_port,
+                             struct mlxsw_sp_mall_entry *mall_entry)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       struct mlxsw_sp_span_trigger_parms parms;
+       enum mlxsw_sp_span_trigger trigger;
+
+       trigger = mall_entry->ingress ? MLXSW_SP_SPAN_TRIGGER_INGRESS :
+                                       MLXSW_SP_SPAN_TRIGGER_EGRESS;
+       parms.span_id = mall_entry->mirror.span_id;
+       mlxsw_sp_span_agent_unbind(mlxsw_sp, trigger, mlxsw_sp_port, &parms);
+       mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, mall_entry->ingress);
+       mlxsw_sp_span_agent_put(mlxsw_sp, mall_entry->mirror.span_id);
+}
+
+static int mlxsw_sp_mall_port_sample_set(struct mlxsw_sp_port *mlxsw_sp_port,
+                                        bool enable, u32 rate)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       char mpsc_pl[MLXSW_REG_MPSC_LEN];
+
+       mlxsw_reg_mpsc_pack(mpsc_pl, mlxsw_sp_port->local_port, enable, rate);
+       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpsc), mpsc_pl);
+}
+
+static int
+mlxsw_sp_mall_port_sample_add(struct mlxsw_sp_port *mlxsw_sp_port,
+                             struct mlxsw_sp_mall_entry *mall_entry)
+{
+       int err;
+
+       if (rtnl_dereference(mlxsw_sp_port->sample)) {
+               netdev_err(mlxsw_sp_port->dev, "sample already active\n");
+               return -EEXIST;
+       }
+       rcu_assign_pointer(mlxsw_sp_port->sample, &mall_entry->sample);
+
+       err = mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, true,
+                                           mall_entry->sample.rate);
+       if (err)
+               goto err_port_sample_set;
+       return 0;
+
+err_port_sample_set:
+       RCU_INIT_POINTER(mlxsw_sp_port->sample, NULL);
+       return err;
+}
+
+static void
+mlxsw_sp_mall_port_sample_del(struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       if (!mlxsw_sp_port->sample)
+               return;
+
+       mlxsw_sp_mall_port_sample_set(mlxsw_sp_port, false, 1);
+       RCU_INIT_POINTER(mlxsw_sp_port->sample, NULL);
+}
+
+static int
+mlxsw_sp_mall_port_rule_add(struct mlxsw_sp_port *mlxsw_sp_port,
+                           struct mlxsw_sp_mall_entry *mall_entry)
+{
+       switch (mall_entry->type) {
+       case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
+               return mlxsw_sp_mall_port_mirror_add(mlxsw_sp_port, mall_entry);
+       case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE:
+               return mlxsw_sp_mall_port_sample_add(mlxsw_sp_port, mall_entry);
+       default:
+               WARN_ON(1);
+               return -EINVAL;
+       }
+}
+
+static void
+mlxsw_sp_mall_port_rule_del(struct mlxsw_sp_port *mlxsw_sp_port,
+                           struct mlxsw_sp_mall_entry *mall_entry)
+{
+       switch (mall_entry->type) {
+       case MLXSW_SP_MALL_ACTION_TYPE_MIRROR:
+               mlxsw_sp_mall_port_mirror_del(mlxsw_sp_port, mall_entry);
+               break;
+       case MLXSW_SP_MALL_ACTION_TYPE_SAMPLE:
+               mlxsw_sp_mall_port_sample_del(mlxsw_sp_port);
+               break;
+       default:
+               WARN_ON(1);
+       }
+}
+
+int mlxsw_sp_mall_replace(struct mlxsw_sp_flow_block *block,
+                         struct tc_cls_matchall_offload *f)
+{
+       struct mlxsw_sp_flow_block_binding *binding;
+       struct mlxsw_sp_mall_entry *mall_entry;
+       __be16 protocol = f->common.protocol;
+       struct flow_action_entry *act;
+       int err;
+
+       if (!flow_offload_has_one_action(&f->rule->action)) {
+               NL_SET_ERR_MSG(f->common.extack, "Only singular actions are supported");
+               return -EOPNOTSUPP;
+       }
+
+       if (f->common.chain_index) {
+               NL_SET_ERR_MSG(f->common.extack, "Only chain 0 is supported");
+               return -EOPNOTSUPP;
+       }
+
+       if (mlxsw_sp_flow_block_is_mixed_bound(block)) {
+               NL_SET_ERR_MSG(f->common.extack, "Only not mixed bound blocks are supported");
+               return -EOPNOTSUPP;
+       }
+
+       mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
+       if (!mall_entry)
+               return -ENOMEM;
+       mall_entry->cookie = f->cookie;
+       mall_entry->ingress = mlxsw_sp_flow_block_is_ingress_bound(block);
+
+       act = &f->rule->action.entries[0];
+
+       if (act->id == FLOW_ACTION_MIRRED && protocol == htons(ETH_P_ALL)) {
+               mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_MIRROR;
+               mall_entry->mirror.to_dev = act->dev;
+       } else if (act->id == FLOW_ACTION_SAMPLE &&
+                  protocol == htons(ETH_P_ALL)) {
+               if (act->sample.rate > MLXSW_REG_MPSC_RATE_MAX) {
+                       NL_SET_ERR_MSG(f->common.extack, "Sample rate not supported");
+                       err = -EOPNOTSUPP;
+                       goto errout;
+               }
+               mall_entry->type = MLXSW_SP_MALL_ACTION_TYPE_SAMPLE;
+               mall_entry->sample.psample_group = act->sample.psample_group;
+               mall_entry->sample.truncate = act->sample.truncate;
+               mall_entry->sample.trunc_size = act->sample.trunc_size;
+               mall_entry->sample.rate = act->sample.rate;
+       } else {
+               err = -EOPNOTSUPP;
+               goto errout;
+       }
+
+       list_for_each_entry(binding, &block->binding_list, list) {
+               err = mlxsw_sp_mall_port_rule_add(binding->mlxsw_sp_port,
+                                                 mall_entry);
+               if (err)
+                       goto rollback;
+       }
+
+       block->rule_count++;
+       if (mall_entry->ingress)
+               block->egress_blocker_rule_count++;
+       else
+               block->ingress_blocker_rule_count++;
+       list_add_tail(&mall_entry->list, &block->mall_list);
+       return 0;
+
+rollback:
+       list_for_each_entry_continue_reverse(binding, &block->binding_list,
+                                            list)
+               mlxsw_sp_mall_port_rule_del(binding->mlxsw_sp_port, mall_entry);
+errout:
+       kfree(mall_entry);
+       return err;
+}
+
+void mlxsw_sp_mall_destroy(struct mlxsw_sp_flow_block *block,
+                          struct tc_cls_matchall_offload *f)
+{
+       struct mlxsw_sp_flow_block_binding *binding;
+       struct mlxsw_sp_mall_entry *mall_entry;
+
+       mall_entry = mlxsw_sp_mall_entry_find(block, f->cookie);
+       if (!mall_entry) {
+               NL_SET_ERR_MSG(f->common.extack, "Entry not found");
+               return;
+       }
+
+       list_del(&mall_entry->list);
+       if (mall_entry->ingress)
+               block->egress_blocker_rule_count--;
+       else
+               block->ingress_blocker_rule_count--;
+       block->rule_count--;
+       list_for_each_entry(binding, &block->binding_list, list)
+               mlxsw_sp_mall_port_rule_del(binding->mlxsw_sp_port, mall_entry);
+       kfree_rcu(mall_entry, rcu); /* sample RX packets may be in-flight */
+}
+
+int mlxsw_sp_mall_port_bind(struct mlxsw_sp_flow_block *block,
+                           struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       struct mlxsw_sp_mall_entry *mall_entry;
+       int err;
+
+       list_for_each_entry(mall_entry, &block->mall_list, list) {
+               err = mlxsw_sp_mall_port_rule_add(mlxsw_sp_port, mall_entry);
+               if (err)
+                       goto rollback;
+       }
+       return 0;
+
+rollback:
+       list_for_each_entry_continue_reverse(mall_entry, &block->mall_list,
+                                            list)
+               mlxsw_sp_mall_port_rule_del(mlxsw_sp_port, mall_entry);
+       return err;
+}
+
+void mlxsw_sp_mall_port_unbind(struct mlxsw_sp_flow_block *block,
+                              struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       struct mlxsw_sp_mall_entry *mall_entry;
+
+       list_for_each_entry(mall_entry, &block->mall_list, list)
+               mlxsw_sp_mall_port_rule_del(mlxsw_sp_port, mall_entry);
+}
index 346f4a5..221aa6a 100644 (file)
@@ -199,8 +199,8 @@ mlxsw_sp_mr_tcam_afa_block_create(struct mlxsw_sp *mlxsw_sp,
        int err;
 
        afa_block = mlxsw_afa_block_create(mlxsw_sp->afa);
-       if (!afa_block)
-               return ERR_PTR(-ENOMEM);
+       if (IS_ERR(afa_block))
+               return afa_block;
 
        err = mlxsw_afa_block_append_allocated_counter(afa_block,
                                                       counter_index);
index d5bca1b..71aee49 100644 (file)
@@ -2999,6 +2999,7 @@ static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed)
                for (i = 0; i < nh_grp->count; i++) {
                        nh = &nh_grp->nexthops[i];
                        val ^= jhash(&nh->ifindex, sizeof(nh->ifindex), seed);
+                       val ^= jhash(&nh->gw_addr, sizeof(nh->gw_addr), seed);
                }
                return jhash(&val, sizeof(val), seed);
        default:
@@ -3012,11 +3013,14 @@ mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed)
 {
        unsigned int val = fib6_entry->nrt6;
        struct mlxsw_sp_rt6 *mlxsw_sp_rt6;
-       struct net_device *dev;
 
        list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) {
-               dev = mlxsw_sp_rt6->rt->fib6_nh->fib_nh_dev;
+               struct fib6_nh *fib6_nh = mlxsw_sp_rt6->rt->fib6_nh;
+               struct net_device *dev = fib6_nh->fib_nh_dev;
+               struct in6_addr *gw = &fib6_nh->fib_nh_gw6;
+
                val ^= jhash(&dev->ifindex, sizeof(dev->ifindex), seed);
+               val ^= jhash(gw, sizeof(*gw), seed);
        }
 
        return jhash(&val, sizeof(val), seed);
index 9fb2e9d..304eb8c 100644 (file)
@@ -3,6 +3,8 @@
 
 #include <linux/if_bridge.h>
 #include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/refcount.h>
 #include <linux/rtnetlink.h>
 #include <linux/workqueue.h>
 #include <net/arp.h>
 struct mlxsw_sp_span {
        struct work_struct work;
        struct mlxsw_sp *mlxsw_sp;
+       struct list_head analyzed_ports_list;
+       struct mutex analyzed_ports_lock; /* Protects analyzed_ports_list */
+       struct list_head trigger_entries_list;
        atomic_t active_entries_count;
        int entries_count;
-       struct mlxsw_sp_span_entry entries[0];
+       struct mlxsw_sp_span_entry entries[];
+};
+
+struct mlxsw_sp_span_analyzed_port {
+       struct list_head list; /* Member of analyzed_ports_list */
+       refcount_t ref_count;
+       u8 local_port;
+       bool ingress;
+};
+
+struct mlxsw_sp_span_trigger_entry {
+       struct list_head list; /* Member of trigger_entries_list */
+       refcount_t ref_count;
+       u8 local_port;
+       enum mlxsw_sp_span_trigger trigger;
+       struct mlxsw_sp_span_trigger_parms parms;
 };
 
 static void mlxsw_sp_span_respin_work(struct work_struct *work);
@@ -48,15 +68,14 @@ int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
                return -ENOMEM;
        span->entries_count = entries_count;
        atomic_set(&span->active_entries_count, 0);
+       mutex_init(&span->analyzed_ports_lock);
+       INIT_LIST_HEAD(&span->analyzed_ports_list);
+       INIT_LIST_HEAD(&span->trigger_entries_list);
        span->mlxsw_sp = mlxsw_sp;
        mlxsw_sp->span = span;
 
-       for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
-               struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
-
-               INIT_LIST_HEAD(&curr->bound_ports_list);
-               curr->id = i;
-       }
+       for (i = 0; i < mlxsw_sp->span->entries_count; i++)
+               mlxsw_sp->span->entries[i].id = i;
 
        devlink_resource_occ_get_register(devlink, MLXSW_SP_RESOURCE_SPAN,
                                          mlxsw_sp_span_occ_get, mlxsw_sp);
@@ -68,16 +87,13 @@ int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp)
 void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp)
 {
        struct devlink *devlink = priv_to_devlink(mlxsw_sp->core);
-       int i;
 
        cancel_work_sync(&mlxsw_sp->span->work);
        devlink_resource_occ_get_unregister(devlink, MLXSW_SP_RESOURCE_SPAN);
 
-       for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
-               struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
-
-               WARN_ON_ONCE(!list_empty(&curr->bound_ports_list));
-       }
+       WARN_ON_ONCE(!list_empty(&mlxsw_sp->span->trigger_entries_list));
+       WARN_ON_ONCE(!list_empty(&mlxsw_sp->span->analyzed_ports_list));
+       mutex_destroy(&mlxsw_sp->span->analyzed_ports_lock);
        kfree(mlxsw_sp->span);
 }
 
@@ -130,7 +146,7 @@ mlxsw_sp_span_entry_phys_deconfigure(struct mlxsw_sp_span_entry *span_entry)
 static const
 struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_phys = {
        .can_handle = mlxsw_sp_port_dev_check,
-       .parms = mlxsw_sp_span_entry_phys_parms,
+       .parms_set = mlxsw_sp_span_entry_phys_parms,
        .configure = mlxsw_sp_span_entry_phys_configure,
        .deconfigure = mlxsw_sp_span_entry_phys_deconfigure,
 };
@@ -418,7 +434,7 @@ mlxsw_sp_span_entry_gretap4_deconfigure(struct mlxsw_sp_span_entry *span_entry)
 
 static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap4 = {
        .can_handle = netif_is_gretap,
-       .parms = mlxsw_sp_span_entry_gretap4_parms,
+       .parms_set = mlxsw_sp_span_entry_gretap4_parms,
        .configure = mlxsw_sp_span_entry_gretap4_configure,
        .deconfigure = mlxsw_sp_span_entry_gretap4_deconfigure,
 };
@@ -519,7 +535,7 @@ mlxsw_sp_span_entry_gretap6_deconfigure(struct mlxsw_sp_span_entry *span_entry)
 static const
 struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_gretap6 = {
        .can_handle = netif_is_ip6gretap,
-       .parms = mlxsw_sp_span_entry_gretap6_parms,
+       .parms_set = mlxsw_sp_span_entry_gretap6_parms,
        .configure = mlxsw_sp_span_entry_gretap6_configure,
        .deconfigure = mlxsw_sp_span_entry_gretap6_deconfigure,
 };
@@ -575,7 +591,7 @@ mlxsw_sp_span_entry_vlan_deconfigure(struct mlxsw_sp_span_entry *span_entry)
 static const
 struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_vlan = {
        .can_handle = mlxsw_sp_span_vlan_can_handle,
-       .parms = mlxsw_sp_span_entry_vlan_parms,
+       .parms_set = mlxsw_sp_span_entry_vlan_parms,
        .configure = mlxsw_sp_span_entry_vlan_configure,
        .deconfigure = mlxsw_sp_span_entry_vlan_deconfigure,
 };
@@ -612,7 +628,7 @@ mlxsw_sp_span_entry_nop_deconfigure(struct mlxsw_sp_span_entry *span_entry)
 }
 
 static const struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_nop = {
-       .parms = mlxsw_sp_span_entry_nop_parms,
+       .parms_set = mlxsw_sp_span_entry_nop_parms,
        .configure = mlxsw_sp_span_entry_nop_configure,
        .deconfigure = mlxsw_sp_span_entry_nop_deconfigure,
 };
@@ -622,18 +638,27 @@ mlxsw_sp_span_entry_configure(struct mlxsw_sp *mlxsw_sp,
                              struct mlxsw_sp_span_entry *span_entry,
                              struct mlxsw_sp_span_parms sparms)
 {
-       if (sparms.dest_port) {
-               if (sparms.dest_port->mlxsw_sp != mlxsw_sp) {
-                       netdev_err(span_entry->to_dev, "Cannot mirror to %s, which belongs to a different mlxsw instance",
-                                  sparms.dest_port->dev->name);
-                       sparms.dest_port = NULL;
-               } else if (span_entry->ops->configure(span_entry, sparms)) {
-                       netdev_err(span_entry->to_dev, "Failed to offload mirror to %s",
-                                  sparms.dest_port->dev->name);
-                       sparms.dest_port = NULL;
-               }
+       int err;
+
+       if (!sparms.dest_port)
+               goto set_parms;
+
+       if (sparms.dest_port->mlxsw_sp != mlxsw_sp) {
+               netdev_err(span_entry->to_dev, "Cannot mirror to %s, which belongs to a different mlxsw instance",
+                          sparms.dest_port->dev->name);
+               sparms.dest_port = NULL;
+               goto set_parms;
+       }
+
+       err = span_entry->ops->configure(span_entry, sparms);
+       if (err) {
+               netdev_err(span_entry->to_dev, "Failed to offload mirror to %s",
+                          sparms.dest_port->dev->name);
+               sparms.dest_port = NULL;
+               goto set_parms;
        }
 
+set_parms:
        span_entry->parms = sparms;
 }
 
@@ -655,7 +680,7 @@ mlxsw_sp_span_entry_create(struct mlxsw_sp *mlxsw_sp,
 
        /* find a free entry to use */
        for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
-               if (!mlxsw_sp->span->entries[i].ref_count) {
+               if (!refcount_read(&mlxsw_sp->span->entries[i].ref_count)) {
                        span_entry = &mlxsw_sp->span->entries[i];
                        break;
                }
@@ -665,7 +690,7 @@ mlxsw_sp_span_entry_create(struct mlxsw_sp *mlxsw_sp,
 
        atomic_inc(&mlxsw_sp->span->active_entries_count);
        span_entry->ops = ops;
-       span_entry->ref_count = 1;
+       refcount_set(&span_entry->ref_count, 1);
        span_entry->to_dev = to_dev;
        mlxsw_sp_span_entry_configure(mlxsw_sp, span_entry, sparms);
 
@@ -688,7 +713,7 @@ mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp,
        for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
                struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
 
-               if (curr->ref_count && curr->to_dev == to_dev)
+               if (refcount_read(&curr->ref_count) && curr->to_dev == to_dev)
                        return curr;
        }
        return NULL;
@@ -709,7 +734,7 @@ mlxsw_sp_span_entry_find_by_id(struct mlxsw_sp *mlxsw_sp, int span_id)
        for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
                struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
 
-               if (curr->ref_count && curr->id == span_id)
+               if (refcount_read(&curr->ref_count) && curr->id == span_id)
                        return curr;
        }
        return NULL;
@@ -726,7 +751,7 @@ mlxsw_sp_span_entry_get(struct mlxsw_sp *mlxsw_sp,
        span_entry = mlxsw_sp_span_entry_find_by_port(mlxsw_sp, to_dev);
        if (span_entry) {
                /* Already exists, just take a reference */
-               span_entry->ref_count++;
+               refcount_inc(&span_entry->ref_count);
                return span_entry;
        }
 
@@ -736,32 +761,13 @@ mlxsw_sp_span_entry_get(struct mlxsw_sp *mlxsw_sp,
 static int mlxsw_sp_span_entry_put(struct mlxsw_sp *mlxsw_sp,
                                   struct mlxsw_sp_span_entry *span_entry)
 {
-       WARN_ON(!span_entry->ref_count);
-       if (--span_entry->ref_count == 0)
+       if (refcount_dec_and_test(&span_entry->ref_count))
                mlxsw_sp_span_entry_destroy(mlxsw_sp, span_entry);
        return 0;
 }
 
-static bool mlxsw_sp_span_is_egress_mirror(struct mlxsw_sp_port *port)
-{
-       struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
-       struct mlxsw_sp_span_inspected_port *p;
-       int i;
-
-       for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
-               struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
-
-               list_for_each_entry(p, &curr->bound_ports_list, list)
-                       if (p->local_port == port->local_port &&
-                           p->type == MLXSW_SP_SPAN_EGRESS)
-                               return true;
-       }
-
-       return false;
-}
-
 static int
-mlxsw_sp_span_port_buffsize_update(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu)
+mlxsw_sp_span_port_buffer_update(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
        char sbib_pl[MLXSW_REG_SBIB_LEN];
@@ -780,20 +786,54 @@ mlxsw_sp_span_port_buffsize_update(struct mlxsw_sp_port *mlxsw_sp_port, u16 mtu)
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
 }
 
+static void mlxsw_sp_span_port_buffer_disable(struct mlxsw_sp *mlxsw_sp,
+                                             u8 local_port)
+{
+       char sbib_pl[MLXSW_REG_SBIB_LEN];
+
+       mlxsw_reg_sbib_pack(sbib_pl, local_port, 0);
+       mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
+}
+
+static struct mlxsw_sp_span_analyzed_port *
+mlxsw_sp_span_analyzed_port_find(struct mlxsw_sp_span *span, u8 local_port,
+                                bool ingress)
+{
+       struct mlxsw_sp_span_analyzed_port *analyzed_port;
+
+       list_for_each_entry(analyzed_port, &span->analyzed_ports_list, list) {
+               if (analyzed_port->local_port == local_port &&
+                   analyzed_port->ingress == ingress)
+                       return analyzed_port;
+       }
+
+       return NULL;
+}
+
 int mlxsw_sp_span_port_mtu_update(struct mlxsw_sp_port *port, u16 mtu)
 {
+       struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
+       int err = 0;
+
        /* If port is egress mirrored, the shared buffer size should be
         * updated according to the mtu value
         */
-       if (mlxsw_sp_span_is_egress_mirror(port))
-               return mlxsw_sp_span_port_buffsize_update(port, mtu);
-       return 0;
+       mutex_lock(&mlxsw_sp->span->analyzed_ports_lock);
+
+       if (mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span, port->local_port,
+                                            false))
+               err = mlxsw_sp_span_port_buffer_update(port, mtu);
+
+       mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock);
+
+       return err;
 }
 
 void mlxsw_sp_span_speed_update_work(struct work_struct *work)
 {
        struct delayed_work *dwork = to_delayed_work(work);
        struct mlxsw_sp_port *mlxsw_sp_port;
+       struct mlxsw_sp *mlxsw_sp;
 
        mlxsw_sp_port = container_of(dwork, struct mlxsw_sp_port,
                                     span.speed_update_dw);
@@ -801,237 +841,368 @@ void mlxsw_sp_span_speed_update_work(struct work_struct *work)
        /* If port is egress mirrored, the shared buffer size should be
         * updated according to the speed value.
         */
-       if (mlxsw_sp_span_is_egress_mirror(mlxsw_sp_port))
-               mlxsw_sp_span_port_buffsize_update(mlxsw_sp_port,
-                                                  mlxsw_sp_port->dev->mtu);
+       mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       mutex_lock(&mlxsw_sp->span->analyzed_ports_lock);
+
+       if (mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span,
+                                            mlxsw_sp_port->local_port, false))
+               mlxsw_sp_span_port_buffer_update(mlxsw_sp_port,
+                                                mlxsw_sp_port->dev->mtu);
+
+       mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock);
 }
 
-static struct mlxsw_sp_span_inspected_port *
-mlxsw_sp_span_entry_bound_port_find(struct mlxsw_sp_span_entry *span_entry,
-                                   enum mlxsw_sp_span_type type,
-                                   struct mlxsw_sp_port *port,
-                                   bool bind)
+static const struct mlxsw_sp_span_entry_ops *
+mlxsw_sp_span_entry_ops(struct mlxsw_sp *mlxsw_sp,
+                       const struct net_device *to_dev)
 {
-       struct mlxsw_sp_span_inspected_port *p;
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(mlxsw_sp_span_entry_types); ++i)
+               if (mlxsw_sp_span_entry_types[i]->can_handle(to_dev))
+                       return mlxsw_sp_span_entry_types[i];
 
-       list_for_each_entry(p, &span_entry->bound_ports_list, list)
-               if (type == p->type &&
-                   port->local_port == p->local_port &&
-                   bind == p->bound)
-                       return p;
        return NULL;
 }
 
-static int
-mlxsw_sp_span_inspected_port_bind(struct mlxsw_sp_port *port,
-                                 struct mlxsw_sp_span_entry *span_entry,
-                                 enum mlxsw_sp_span_type type,
-                                 bool bind)
+static void mlxsw_sp_span_respin_work(struct work_struct *work)
 {
-       struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
-       char mpar_pl[MLXSW_REG_MPAR_LEN];
-       int pa_id = span_entry->id;
+       struct mlxsw_sp_span *span;
+       struct mlxsw_sp *mlxsw_sp;
+       int i, err;
+
+       span = container_of(work, struct mlxsw_sp_span, work);
+       mlxsw_sp = span->mlxsw_sp;
+
+       rtnl_lock();
+       for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
+               struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
+               struct mlxsw_sp_span_parms sparms = {NULL};
+
+               if (!refcount_read(&curr->ref_count))
+                       continue;
 
-       /* bind the port to the SPAN entry */
-       mlxsw_reg_mpar_pack(mpar_pl, port->local_port,
-                           (enum mlxsw_reg_mpar_i_e)type, bind, pa_id);
-       return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mpar), mpar_pl);
+               err = curr->ops->parms_set(curr->to_dev, &sparms);
+               if (err)
+                       continue;
+
+               if (memcmp(&sparms, &curr->parms, sizeof(sparms))) {
+                       mlxsw_sp_span_entry_deconfigure(curr);
+                       mlxsw_sp_span_entry_configure(mlxsw_sp, curr, sparms);
+               }
+       }
+       rtnl_unlock();
 }
 
-static int
-mlxsw_sp_span_inspected_port_add(struct mlxsw_sp_port *port,
-                                struct mlxsw_sp_span_entry *span_entry,
-                                enum mlxsw_sp_span_type type,
-                                bool bind)
+void mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp)
 {
-       struct mlxsw_sp_span_inspected_port *inspected_port;
-       struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
-       char sbib_pl[MLXSW_REG_SBIB_LEN];
-       int i;
+       if (atomic_read(&mlxsw_sp->span->active_entries_count) == 0)
+               return;
+       mlxsw_core_schedule_work(&mlxsw_sp->span->work);
+}
+
+int mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp,
+                           const struct net_device *to_dev, int *p_span_id)
+{
+       const struct mlxsw_sp_span_entry_ops *ops;
+       struct mlxsw_sp_span_entry *span_entry;
+       struct mlxsw_sp_span_parms sparms;
        int err;
 
-       /* A given (source port, direction) can only be bound to one analyzer,
-        * so if a binding is requested, check for conflicts.
-        */
-       if (bind)
-               for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
-                       struct mlxsw_sp_span_entry *curr =
-                               &mlxsw_sp->span->entries[i];
-
-                       if (mlxsw_sp_span_entry_bound_port_find(curr, type,
-                                                               port, bind))
-                               return -EEXIST;
-               }
+       ASSERT_RTNL();
 
-       /* if it is an egress SPAN, bind a shared buffer to it */
-       if (type == MLXSW_SP_SPAN_EGRESS) {
-               err = mlxsw_sp_span_port_buffsize_update(port, port->dev->mtu);
-               if (err)
-                       return err;
+       ops = mlxsw_sp_span_entry_ops(mlxsw_sp, to_dev);
+       if (!ops) {
+               dev_err(mlxsw_sp->bus_info->dev, "Cannot mirror to requested destination\n");
+               return -EOPNOTSUPP;
        }
 
-       if (bind) {
-               err = mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
-                                                       true);
-               if (err)
-                       goto err_port_bind;
-       }
+       memset(&sparms, 0, sizeof(sparms));
+       err = ops->parms_set(to_dev, &sparms);
+       if (err)
+               return err;
 
-       inspected_port = kzalloc(sizeof(*inspected_port), GFP_KERNEL);
-       if (!inspected_port) {
-               err = -ENOMEM;
-               goto err_inspected_port_alloc;
-       }
-       inspected_port->local_port = port->local_port;
-       inspected_port->type = type;
-       inspected_port->bound = bind;
-       list_add_tail(&inspected_port->list, &span_entry->bound_ports_list);
+       span_entry = mlxsw_sp_span_entry_get(mlxsw_sp, to_dev, ops, sparms);
+       if (!span_entry)
+               return -ENOBUFS;
+
+       *p_span_id = span_entry->id;
 
        return 0;
+}
 
-err_inspected_port_alloc:
-       if (bind)
-               mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
-                                                 false);
-err_port_bind:
-       if (type == MLXSW_SP_SPAN_EGRESS) {
-               mlxsw_reg_sbib_pack(sbib_pl, port->local_port, 0);
-               mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
+void mlxsw_sp_span_agent_put(struct mlxsw_sp *mlxsw_sp, int span_id)
+{
+       struct mlxsw_sp_span_entry *span_entry;
+
+       ASSERT_RTNL();
+
+       span_entry = mlxsw_sp_span_entry_find_by_id(mlxsw_sp, span_id);
+       if (WARN_ON_ONCE(!span_entry))
+               return;
+
+       mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
+}
+
+static struct mlxsw_sp_span_analyzed_port *
+mlxsw_sp_span_analyzed_port_create(struct mlxsw_sp_span *span,
+                                  struct mlxsw_sp_port *mlxsw_sp_port,
+                                  bool ingress)
+{
+       struct mlxsw_sp_span_analyzed_port *analyzed_port;
+       int err;
+
+       analyzed_port = kzalloc(sizeof(*analyzed_port), GFP_KERNEL);
+       if (!analyzed_port)
+               return ERR_PTR(-ENOMEM);
+
+       refcount_set(&analyzed_port->ref_count, 1);
+       analyzed_port->local_port = mlxsw_sp_port->local_port;
+       analyzed_port->ingress = ingress;
+       list_add_tail(&analyzed_port->list, &span->analyzed_ports_list);
+
+       /* An egress mirror buffer should be allocated on the egress port which
+        * does the mirroring.
+        */
+       if (!ingress) {
+               u16 mtu = mlxsw_sp_port->dev->mtu;
+
+               err = mlxsw_sp_span_port_buffer_update(mlxsw_sp_port, mtu);
+               if (err)
+                       goto err_buffer_update;
        }
-       return err;
+
+       return analyzed_port;
+
+err_buffer_update:
+       list_del(&analyzed_port->list);
+       kfree(analyzed_port);
+       return ERR_PTR(err);
 }
 
 static void
-mlxsw_sp_span_inspected_port_del(struct mlxsw_sp_port *port,
-                                struct mlxsw_sp_span_entry *span_entry,
-                                enum mlxsw_sp_span_type type,
-                                bool bind)
+mlxsw_sp_span_analyzed_port_destroy(struct mlxsw_sp_span *span,
+                                   struct mlxsw_sp_span_analyzed_port *
+                                   analyzed_port)
 {
-       struct mlxsw_sp_span_inspected_port *inspected_port;
-       struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
-       char sbib_pl[MLXSW_REG_SBIB_LEN];
+       struct mlxsw_sp *mlxsw_sp = span->mlxsw_sp;
 
-       inspected_port = mlxsw_sp_span_entry_bound_port_find(span_entry, type,
-                                                            port, bind);
-       if (!inspected_port)
-               return;
+       /* Remove egress mirror buffer now that port is no longer analyzed
+        * at egress.
+        */
+       if (!analyzed_port->ingress)
+               mlxsw_sp_span_port_buffer_disable(mlxsw_sp,
+                                                 analyzed_port->local_port);
+
+       list_del(&analyzed_port->list);
+       kfree(analyzed_port);
+}
+
+int mlxsw_sp_span_analyzed_port_get(struct mlxsw_sp_port *mlxsw_sp_port,
+                                   bool ingress)
+{
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       struct mlxsw_sp_span_analyzed_port *analyzed_port;
+       u8 local_port = mlxsw_sp_port->local_port;
+       int err = 0;
+
+       mutex_lock(&mlxsw_sp->span->analyzed_ports_lock);
 
-       if (bind)
-               mlxsw_sp_span_inspected_port_bind(port, span_entry, type,
-                                                 false);
-       /* remove the SBIB buffer if it was egress SPAN */
-       if (type == MLXSW_SP_SPAN_EGRESS) {
-               mlxsw_reg_sbib_pack(sbib_pl, port->local_port, 0);
-               mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl);
+       analyzed_port = mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span,
+                                                        local_port, ingress);
+       if (analyzed_port) {
+               refcount_inc(&analyzed_port->ref_count);
+               goto out_unlock;
        }
 
-       mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
+       analyzed_port = mlxsw_sp_span_analyzed_port_create(mlxsw_sp->span,
+                                                          mlxsw_sp_port,
+                                                          ingress);
+       if (IS_ERR(analyzed_port))
+               err = PTR_ERR(analyzed_port);
 
-       list_del(&inspected_port->list);
-       kfree(inspected_port);
+out_unlock:
+       mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock);
+       return err;
 }
 
-static const struct mlxsw_sp_span_entry_ops *
-mlxsw_sp_span_entry_ops(struct mlxsw_sp *mlxsw_sp,
-                       const struct net_device *to_dev)
+void mlxsw_sp_span_analyzed_port_put(struct mlxsw_sp_port *mlxsw_sp_port,
+                                    bool ingress)
 {
-       size_t i;
+       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
+       struct mlxsw_sp_span_analyzed_port *analyzed_port;
+       u8 local_port = mlxsw_sp_port->local_port;
 
-       for (i = 0; i < ARRAY_SIZE(mlxsw_sp_span_entry_types); ++i)
-               if (mlxsw_sp_span_entry_types[i]->can_handle(to_dev))
-                       return mlxsw_sp_span_entry_types[i];
+       mutex_lock(&mlxsw_sp->span->analyzed_ports_lock);
 
-       return NULL;
+       analyzed_port = mlxsw_sp_span_analyzed_port_find(mlxsw_sp->span,
+                                                        local_port, ingress);
+       if (WARN_ON_ONCE(!analyzed_port))
+               goto out_unlock;
+
+       if (!refcount_dec_and_test(&analyzed_port->ref_count))
+               goto out_unlock;
+
+       mlxsw_sp_span_analyzed_port_destroy(mlxsw_sp->span, analyzed_port);
+
+out_unlock:
+       mutex_unlock(&mlxsw_sp->span->analyzed_ports_lock);
 }
 
-int mlxsw_sp_span_mirror_add(struct mlxsw_sp_port *from,
-                            const struct net_device *to_dev,
-                            enum mlxsw_sp_span_type type, bool bind,
-                            int *p_span_id)
+static int
+__mlxsw_sp_span_trigger_entry_bind(struct mlxsw_sp_span *span,
+                                  struct mlxsw_sp_span_trigger_entry *
+                                  trigger_entry, bool enable)
 {
-       struct mlxsw_sp *mlxsw_sp = from->mlxsw_sp;
-       const struct mlxsw_sp_span_entry_ops *ops;
-       struct mlxsw_sp_span_parms sparms = {NULL};
-       struct mlxsw_sp_span_entry *span_entry;
-       int err;
-
-       ops = mlxsw_sp_span_entry_ops(mlxsw_sp, to_dev);
-       if (!ops) {
-               netdev_err(to_dev, "Cannot mirror to %s", to_dev->name);
-               return -EOPNOTSUPP;
+       char mpar_pl[MLXSW_REG_MPAR_LEN];
+       enum mlxsw_reg_mpar_i_e i_e;
+
+       switch (trigger_entry->trigger) {
+       case MLXSW_SP_SPAN_TRIGGER_INGRESS:
+               i_e = MLXSW_REG_MPAR_TYPE_INGRESS;
+               break;
+       case MLXSW_SP_SPAN_TRIGGER_EGRESS:
+               i_e = MLXSW_REG_MPAR_TYPE_EGRESS;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return -EINVAL;
        }
 
-       err = ops->parms(to_dev, &sparms);
-       if (err)
-               return err;
+       mlxsw_reg_mpar_pack(mpar_pl, trigger_entry->local_port, i_e, enable,
+                           trigger_entry->parms.span_id);
+       return mlxsw_reg_write(span->mlxsw_sp->core, MLXSW_REG(mpar), mpar_pl);
+}
 
-       span_entry = mlxsw_sp_span_entry_get(mlxsw_sp, to_dev, ops, sparms);
-       if (!span_entry)
-               return -ENOBUFS;
+static int
+mlxsw_sp_span_trigger_entry_bind(struct mlxsw_sp_span *span,
+                                struct mlxsw_sp_span_trigger_entry *
+                                trigger_entry)
+{
+       return __mlxsw_sp_span_trigger_entry_bind(span, trigger_entry, true);
+}
 
-       netdev_dbg(from->dev, "Adding inspected port to SPAN entry %d\n",
-                  span_entry->id);
+static void
+mlxsw_sp_span_trigger_entry_unbind(struct mlxsw_sp_span *span,
+                                  struct mlxsw_sp_span_trigger_entry *
+                                  trigger_entry)
+{
+       __mlxsw_sp_span_trigger_entry_bind(span, trigger_entry, false);
+}
 
-       err = mlxsw_sp_span_inspected_port_add(from, span_entry, type, bind);
+static struct mlxsw_sp_span_trigger_entry *
+mlxsw_sp_span_trigger_entry_create(struct mlxsw_sp_span *span,
+                                  enum mlxsw_sp_span_trigger trigger,
+                                  struct mlxsw_sp_port *mlxsw_sp_port,
+                                  const struct mlxsw_sp_span_trigger_parms
+                                  *parms)
+{
+       struct mlxsw_sp_span_trigger_entry *trigger_entry;
+       int err;
+
+       trigger_entry = kzalloc(sizeof(*trigger_entry), GFP_KERNEL);
+       if (!trigger_entry)
+               return ERR_PTR(-ENOMEM);
+
+       refcount_set(&trigger_entry->ref_count, 1);
+       trigger_entry->local_port = mlxsw_sp_port->local_port;
+       trigger_entry->trigger = trigger;
+       memcpy(&trigger_entry->parms, parms, sizeof(trigger_entry->parms));
+       list_add_tail(&trigger_entry->list, &span->trigger_entries_list);
+
+       err = mlxsw_sp_span_trigger_entry_bind(span, trigger_entry);
        if (err)
-               goto err_port_bind;
+               goto err_trigger_entry_bind;
 
-       *p_span_id = span_entry->id;
-       return 0;
+       return trigger_entry;
 
-err_port_bind:
-       mlxsw_sp_span_entry_put(mlxsw_sp, span_entry);
-       return err;
+err_trigger_entry_bind:
+       list_del(&trigger_entry->list);
+       kfree(trigger_entry);
+       return ERR_PTR(err);
 }
 
-void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from, int span_id,
-                             enum mlxsw_sp_span_type type, bool bind)
+static void
+mlxsw_sp_span_trigger_entry_destroy(struct mlxsw_sp_span *span,
+                                   struct mlxsw_sp_span_trigger_entry *
+                                   trigger_entry)
 {
-       struct mlxsw_sp_span_entry *span_entry;
+       mlxsw_sp_span_trigger_entry_unbind(span, trigger_entry);
+       list_del(&trigger_entry->list);
+       kfree(trigger_entry);
+}
 
-       span_entry = mlxsw_sp_span_entry_find_by_id(from->mlxsw_sp, span_id);
-       if (!span_entry) {
-               netdev_err(from->dev, "no span entry found\n");
-               return;
+static struct mlxsw_sp_span_trigger_entry *
+mlxsw_sp_span_trigger_entry_find(struct mlxsw_sp_span *span,
+                                enum mlxsw_sp_span_trigger trigger,
+                                struct mlxsw_sp_port *mlxsw_sp_port)
+{
+       struct mlxsw_sp_span_trigger_entry *trigger_entry;
+
+       list_for_each_entry(trigger_entry, &span->trigger_entries_list, list) {
+               if (trigger_entry->trigger == trigger &&
+                   trigger_entry->local_port == mlxsw_sp_port->local_port)
+                       return trigger_entry;
        }
 
-       netdev_dbg(from->dev, "removing inspected port from SPAN entry %d\n",
-                  span_entry->id);
-       mlxsw_sp_span_inspected_port_del(from, span_entry, type, bind);
+       return NULL;
 }
 
-static void mlxsw_sp_span_respin_work(struct work_struct *work)
+int mlxsw_sp_span_agent_bind(struct mlxsw_sp *mlxsw_sp,
+                            enum mlxsw_sp_span_trigger trigger,
+                            struct mlxsw_sp_port *mlxsw_sp_port,
+                            const struct mlxsw_sp_span_trigger_parms *parms)
 {
-       struct mlxsw_sp_span *span;
-       struct mlxsw_sp *mlxsw_sp;
-       int i, err;
+       struct mlxsw_sp_span_trigger_entry *trigger_entry;
+       int err = 0;
 
-       span = container_of(work, struct mlxsw_sp_span, work);
-       mlxsw_sp = span->mlxsw_sp;
+       ASSERT_RTNL();
 
-       rtnl_lock();
-       for (i = 0; i < mlxsw_sp->span->entries_count; i++) {
-               struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span->entries[i];
-               struct mlxsw_sp_span_parms sparms = {NULL};
+       if (!mlxsw_sp_span_entry_find_by_id(mlxsw_sp, parms->span_id))
+               return -EINVAL;
 
-               if (!curr->ref_count)
-                       continue;
+       trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
+                                                        trigger,
+                                                        mlxsw_sp_port);
+       if (trigger_entry) {
+               if (trigger_entry->parms.span_id != parms->span_id)
+                       return -EINVAL;
+               refcount_inc(&trigger_entry->ref_count);
+               goto out;
+       }
 
-               err = curr->ops->parms(curr->to_dev, &sparms);
-               if (err)
-                       continue;
+       trigger_entry = mlxsw_sp_span_trigger_entry_create(mlxsw_sp->span,
+                                                          trigger,
+                                                          mlxsw_sp_port,
+                                                          parms);
+       if (IS_ERR(trigger_entry))
+               err = PTR_ERR(trigger_entry);
 
-               if (memcmp(&sparms, &curr->parms, sizeof(sparms))) {
-                       mlxsw_sp_span_entry_deconfigure(curr);
-                       mlxsw_sp_span_entry_configure(mlxsw_sp, curr, sparms);
-               }
-       }
-       rtnl_unlock();
+out:
+       return err;
 }
 
-void mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp)
+void mlxsw_sp_span_agent_unbind(struct mlxsw_sp *mlxsw_sp,
+                               enum mlxsw_sp_span_trigger trigger,
+                               struct mlxsw_sp_port *mlxsw_sp_port,
+                               const struct mlxsw_sp_span_trigger_parms *parms)
 {
-       if (atomic_read(&mlxsw_sp->span->active_entries_count) == 0)
+       struct mlxsw_sp_span_trigger_entry *trigger_entry;
+
+       ASSERT_RTNL();
+
+       if (WARN_ON_ONCE(!mlxsw_sp_span_entry_find_by_id(mlxsw_sp,
+                                                        parms->span_id)))
                return;
-       mlxsw_core_schedule_work(&mlxsw_sp->span->work);
+
+       trigger_entry = mlxsw_sp_span_trigger_entry_find(mlxsw_sp->span,
+                                                        trigger,
+                                                        mlxsw_sp_port);
+       if (WARN_ON_ONCE(!trigger_entry))
+               return;
+
+       if (!refcount_dec_and_test(&trigger_entry->ref_count))
+               return;
+
+       mlxsw_sp_span_trigger_entry_destroy(mlxsw_sp->span, trigger_entry);
 }
index 5972433..9f6dd2d 100644 (file)
@@ -6,26 +6,13 @@
 
 #include <linux/types.h>
 #include <linux/if_ether.h>
+#include <linux/refcount.h>
 
 #include "spectrum_router.h"
 
 struct mlxsw_sp;
 struct mlxsw_sp_port;
 
-enum mlxsw_sp_span_type {
-       MLXSW_SP_SPAN_EGRESS,
-       MLXSW_SP_SPAN_INGRESS
-};
-
-struct mlxsw_sp_span_inspected_port {
-       struct list_head list;
-       enum mlxsw_sp_span_type type;
-       u8 local_port;
-
-       /* Whether this is a directly bound mirror (port-to-port) or an ACL. */
-       bool bound;
-};
-
 struct mlxsw_sp_span_parms {
        struct mlxsw_sp_port *dest_port; /* NULL for unoffloaded SPAN. */
        unsigned int ttl;
@@ -36,21 +23,29 @@ struct mlxsw_sp_span_parms {
        u16 vid;
 };
 
+enum mlxsw_sp_span_trigger {
+       MLXSW_SP_SPAN_TRIGGER_INGRESS,
+       MLXSW_SP_SPAN_TRIGGER_EGRESS,
+};
+
+struct mlxsw_sp_span_trigger_parms {
+       int span_id;
+};
+
 struct mlxsw_sp_span_entry_ops;
 
 struct mlxsw_sp_span_entry {
        const struct net_device *to_dev;
        const struct mlxsw_sp_span_entry_ops *ops;
        struct mlxsw_sp_span_parms parms;
-       struct list_head bound_ports_list;
-       int ref_count;
+       refcount_t ref_count;
        int id;
 };
 
 struct mlxsw_sp_span_entry_ops {
        bool (*can_handle)(const struct net_device *to_dev);
-       int (*parms)(const struct net_device *to_dev,
-                    struct mlxsw_sp_span_parms *sparmsp);
+       int (*parms_set)(const struct net_device *to_dev,
+                        struct mlxsw_sp_span_parms *sparmsp);
        int (*configure)(struct mlxsw_sp_span_entry *span_entry,
                         struct mlxsw_sp_span_parms sparms);
        void (*deconfigure)(struct mlxsw_sp_span_entry *span_entry);
@@ -60,12 +55,6 @@ int mlxsw_sp_span_init(struct mlxsw_sp *mlxsw_sp);
 void mlxsw_sp_span_fini(struct mlxsw_sp *mlxsw_sp);
 void mlxsw_sp_span_respin(struct mlxsw_sp *mlxsw_sp);
 
-int mlxsw_sp_span_mirror_add(struct mlxsw_sp_port *from,
-                            const struct net_device *to_dev,
-                            enum mlxsw_sp_span_type type,
-                            bool bind, int *p_span_id);
-void mlxsw_sp_span_mirror_del(struct mlxsw_sp_port *from, int span_id,
-                             enum mlxsw_sp_span_type type, bool bind);
 struct mlxsw_sp_span_entry *
 mlxsw_sp_span_entry_find_by_port(struct mlxsw_sp *mlxsw_sp,
                                 const struct net_device *to_dev);
@@ -76,4 +65,21 @@ void mlxsw_sp_span_entry_invalidate(struct mlxsw_sp *mlxsw_sp,
 int mlxsw_sp_span_port_mtu_update(struct mlxsw_sp_port *port, u16 mtu);
 void mlxsw_sp_span_speed_update_work(struct work_struct *work);
 
+int mlxsw_sp_span_agent_get(struct mlxsw_sp *mlxsw_sp,
+                           const struct net_device *to_dev, int *p_span_id);
+void mlxsw_sp_span_agent_put(struct mlxsw_sp *mlxsw_sp, int span_id);
+int mlxsw_sp_span_analyzed_port_get(struct mlxsw_sp_port *mlxsw_sp_port,
+                                   bool ingress);
+void mlxsw_sp_span_analyzed_port_put(struct mlxsw_sp_port *mlxsw_sp_port,
+                                    bool ingress);
+int mlxsw_sp_span_agent_bind(struct mlxsw_sp *mlxsw_sp,
+                            enum mlxsw_sp_span_trigger trigger,
+                            struct mlxsw_sp_port *mlxsw_sp_port,
+                            const struct mlxsw_sp_span_trigger_parms *parms);
+void
+mlxsw_sp_span_agent_unbind(struct mlxsw_sp *mlxsw_sp,
+                          enum mlxsw_sp_span_trigger trigger,
+                          struct mlxsw_sp_port *mlxsw_sp_port,
+                          const struct mlxsw_sp_span_trigger_parms *parms);
+
 #endif
index 1f496fa..5bd7fb9 100644 (file)
 
 #include "encx24j600_hw.h"
 
-static inline bool is_bits_set(int value, int mask)
-{
-       return (value & mask) == mask;
-}
-
 static int encx24j600_switch_bank(struct encx24j600_context *ctx,
                                  int bank)
 {
index 39925e4..fccc480 100644 (file)
@@ -604,9 +604,8 @@ static void encx24j600_set_rxfilter_mode(struct encx24j600_priv *priv)
        }
 }
 
-static int encx24j600_hw_init(struct encx24j600_priv *priv)
+static void encx24j600_hw_init(struct encx24j600_priv *priv)
 {
-       int ret = 0;
        u16 macon2;
 
        priv->hw_enabled = false;
@@ -649,8 +648,6 @@ static int encx24j600_hw_init(struct encx24j600_priv *priv)
 
        if (netif_msg_hw(priv))
                encx24j600_dump_config(priv, "Hw is initialized");
-
-       return ret;
 }
 
 static void encx24j600_hw_enable(struct encx24j600_priv *priv)
@@ -1042,12 +1039,7 @@ static int encx24j600_spi_probe(struct spi_device *spi)
        }
 
        /* Initialize the device HW to the consistent state */
-       if (encx24j600_hw_init(priv)) {
-               netif_err(priv, probe, ndev,
-                         DRV_NAME ": HW initialization error\n");
-               ret = -EIO;
-               goto out_free;
-       }
+       encx24j600_hw_init(priv);
 
        kthread_init_worker(&priv->kworker);
        kthread_init_work(&priv->tx_work, encx24j600_tx_proc);
index e165175..49fd843 100644 (file)
@@ -331,14 +331,15 @@ static irqreturn_t moxart_mac_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t moxart_mac_start_xmit(struct sk_buff *skb,
+                                        struct net_device *ndev)
 {
        struct moxart_mac_priv_t *priv = netdev_priv(ndev);
        void *desc;
        unsigned int len;
        unsigned int tx_head;
        u32 txdes1;
-       int ret = NETDEV_TX_BUSY;
+       netdev_tx_t ret = NETDEV_TX_BUSY;
 
        spin_lock_irq(&priv->txlock);
 
@@ -564,7 +565,7 @@ static int moxart_remove(struct platform_device *pdev)
        struct net_device *ndev = platform_get_drvdata(pdev);
 
        unregister_netdev(ndev);
-       free_irq(ndev->irq, ndev);
+       devm_free_irq(&pdev->dev, ndev->irq, ndev);
        moxart_mac_free_memory(ndev);
        free_netdev(ndev);
 
index 9a36c26..91b33b5 100644 (file)
@@ -1,5 +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 ocelot_tc.o ocelot_police.o ocelot_ace.o ocelot_flower.o
+mscc_ocelot_common-y += ocelot_regs.o ocelot_tc.o ocelot_police.o ocelot_ace.o ocelot_flower.o ocelot_ptp.o
 obj-$(CONFIG_MSCC_OCELOT_SWITCH_OCELOT) += ocelot_board.o
index b4731df..c798431 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/phy.h>
-#include <linux/ptp_clock_kernel.h>
 #include <linux/skbuff.h>
 #include <linux/iopoll.h>
 #include <net/arp.h>
@@ -183,44 +182,47 @@ static void ocelot_vlan_mode(struct ocelot *ocelot, int port,
        ocelot_write(ocelot, val, ANA_VLANMASK);
 }
 
-void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
-                               bool vlan_aware)
+static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port,
+                                      u16 vid)
 {
        struct ocelot_port *ocelot_port = ocelot->ports[port];
-       u32 val;
+       u32 val = 0;
 
-       if (vlan_aware)
-               val = ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
-                     ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1);
-       else
-               val = 0;
-       ocelot_rmw_gix(ocelot, val,
-                      ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
-                      ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M,
-                      ANA_PORT_VLAN_CFG, port);
+       if (ocelot_port->vid != vid) {
+               /* Always permit deleting the native VLAN (vid = 0) */
+               if (ocelot_port->vid && vid) {
+                       dev_err(ocelot->dev,
+                               "Port already has a native VLAN: %d\n",
+                               ocelot_port->vid);
+                       return -EBUSY;
+               }
+               ocelot_port->vid = vid;
+       }
 
-       if (vlan_aware && !ocelot_port->vid)
+       ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_VID(vid),
+                      REW_PORT_VLAN_CFG_PORT_VID_M,
+                      REW_PORT_VLAN_CFG, port);
+
+       if (ocelot_port->vlan_aware && !ocelot_port->vid)
                /* If port is vlan-aware and tagged, drop untagged and priority
                 * tagged frames.
                 */
                val = ANA_PORT_DROP_CFG_DROP_UNTAGGED_ENA |
                      ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
                      ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA;
-       else
-               val = 0;
        ocelot_rmw_gix(ocelot, val,
                       ANA_PORT_DROP_CFG_DROP_UNTAGGED_ENA |
                       ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
                       ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA,
                       ANA_PORT_DROP_CFG, port);
 
-       if (vlan_aware) {
+       if (ocelot_port->vlan_aware) {
                if (ocelot_port->vid)
                        /* Tag all frames except when VID == DEFAULT_VLAN */
-                       val |= REW_TAG_CFG_TAG_CFG(1);
+                       val = REW_TAG_CFG_TAG_CFG(1);
                else
                        /* Tag all frames */
-                       val |= REW_TAG_CFG_TAG_CFG(3);
+                       val = REW_TAG_CFG_TAG_CFG(3);
        } else {
                /* Port tagging disabled. */
                val = REW_TAG_CFG_TAG_CFG(0);
@@ -228,31 +230,31 @@ void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
        ocelot_rmw_gix(ocelot, val,
                       REW_TAG_CFG_TAG_CFG_M,
                       REW_TAG_CFG, port);
+
+       return 0;
 }
-EXPORT_SYMBOL(ocelot_port_vlan_filtering);
 
-static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port,
-                                      u16 vid)
+void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
+                               bool vlan_aware)
 {
        struct ocelot_port *ocelot_port = ocelot->ports[port];
+       u32 val;
 
-       if (ocelot_port->vid != vid) {
-               /* Always permit deleting the native VLAN (vid = 0) */
-               if (ocelot_port->vid && vid) {
-                       dev_err(ocelot->dev,
-                               "Port already has a native VLAN: %d\n",
-                               ocelot_port->vid);
-                       return -EBUSY;
-               }
-               ocelot_port->vid = vid;
-       }
+       ocelot_port->vlan_aware = vlan_aware;
 
-       ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_VID(vid),
-                      REW_PORT_VLAN_CFG_PORT_VID_M,
-                      REW_PORT_VLAN_CFG, port);
+       if (vlan_aware)
+               val = ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
+                     ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1);
+       else
+               val = 0;
+       ocelot_rmw_gix(ocelot, val,
+                      ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
+                      ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M,
+                      ANA_PORT_VLAN_CFG, port);
 
-       return 0;
+       ocelot_port_set_native_vlan(ocelot, port, ocelot_port->vid);
 }
+EXPORT_SYMBOL(ocelot_port_vlan_filtering);
 
 /* Default vlan to clasify for untagged frames (may be zero) */
 static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, u16 pvid)
@@ -873,12 +875,12 @@ static void ocelot_get_stats64(struct net_device *dev,
 }
 
 int ocelot_fdb_add(struct ocelot *ocelot, int port,
-                  const unsigned char *addr, u16 vid, bool vlan_aware)
+                  const unsigned char *addr, u16 vid)
 {
        struct ocelot_port *ocelot_port = ocelot->ports[port];
 
        if (!vid) {
-               if (!vlan_aware)
+               if (!ocelot_port->vlan_aware)
                        /* If the bridge is not VLAN aware and no VID was
                         * provided, set it to pvid to ensure the MAC entry
                         * matches incoming untagged packets
@@ -905,7 +907,7 @@ static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
        struct ocelot *ocelot = priv->port.ocelot;
        int port = priv->chip_port;
 
-       return ocelot_fdb_add(ocelot, port, addr, vid, priv->vlan_aware);
+       return ocelot_fdb_add(ocelot, port, addr, vid);
 }
 
 int ocelot_fdb_del(struct ocelot *ocelot, int port,
@@ -1028,10 +1030,8 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port,
 {
        int i, j;
 
-       /* Loop through all the mac tables entries. There are 1024 rows of 4
-        * entries.
-        */
-       for (i = 0; i < 1024; i++) {
+       /* Loop through all the mac tables entries. */
+       for (i = 0; i < ocelot->num_mact_rows; i++) {
                for (j = 0; j < 4; j++) {
                        struct ocelot_mact_entry entry;
                        bool is_static;
@@ -1347,6 +1347,12 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,
 {
        info->phc_index = ocelot->ptp_clock ?
                          ptp_clock_index(ocelot->ptp_clock) : -1;
+       if (info->phc_index == -1) {
+               info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
+                                        SOF_TIMESTAMPING_RX_SOFTWARE |
+                                        SOF_TIMESTAMPING_SOFTWARE;
+               return 0;
+       }
        info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
                                 SOF_TIMESTAMPING_RX_SOFTWARE |
                                 SOF_TIMESTAMPING_SOFTWARE |
@@ -1450,8 +1456,15 @@ static void ocelot_port_attr_stp_state_set(struct ocelot *ocelot, int port,
 
 void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs)
 {
-       ocelot_write(ocelot, ANA_AUTOAGE_AGE_PERIOD(msecs / 2),
-                    ANA_AUTOAGE);
+       unsigned int age_period = ANA_AUTOAGE_AGE_PERIOD(msecs / 2000);
+
+       /* Setting AGE_PERIOD to zero effectively disables automatic aging,
+        * which is clearly not what our intention is. So avoid that.
+        */
+       if (!age_period)
+               age_period = 1;
+
+       ocelot_rmw(ocelot, age_period, ANA_AUTOAGE_AGE_PERIOD_M, ANA_AUTOAGE);
 }
 EXPORT_SYMBOL(ocelot_set_ageing_time);
 
@@ -1496,8 +1509,8 @@ static int ocelot_port_attr_set(struct net_device *dev,
                ocelot_port_attr_ageing_set(ocelot, port, attr->u.ageing_time);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
-               priv->vlan_aware = attr->u.vlan_filtering;
-               ocelot_port_vlan_filtering(ocelot, port, priv->vlan_aware);
+               ocelot_port_vlan_filtering(ocelot, port,
+                                          attr->u.vlan_filtering);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
                ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled);
@@ -1868,7 +1881,6 @@ static int ocelot_netdevice_port_event(struct net_device *dev,
                        } else {
                                err = ocelot_port_bridge_leave(ocelot, port,
                                                               info->upper_dev);
-                               priv->vlan_aware = false;
                        }
                }
                if (netif_is_lag_master(info->upper_dev)) {
@@ -1989,200 +2001,6 @@ struct notifier_block ocelot_switchdev_blocking_nb __read_mostly = {
 };
 EXPORT_SYMBOL(ocelot_switchdev_blocking_nb);
 
-int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
-{
-       struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
-       unsigned long flags;
-       time64_t s;
-       u32 val;
-       s64 ns;
-
-       spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
-
-       val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
-       val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
-       val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE);
-       ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
-
-       s = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN) & 0xffff;
-       s <<= 32;
-       s += ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
-       ns = ocelot_read_rix(ocelot, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
-
-       spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
-
-       /* Deal with negative values */
-       if (ns >= 0x3ffffff0 && ns <= 0x3fffffff) {
-               s--;
-               ns &= 0xf;
-               ns += 999999984;
-       }
-
-       set_normalized_timespec64(ts, s, ns);
-       return 0;
-}
-EXPORT_SYMBOL(ocelot_ptp_gettime64);
-
-static int ocelot_ptp_settime64(struct ptp_clock_info *ptp,
-                               const struct timespec64 *ts)
-{
-       struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
-       unsigned long flags;
-       u32 val;
-
-       spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
-
-       val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
-       val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
-       val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
-
-       ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
-
-       ocelot_write_rix(ocelot, lower_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_LSB,
-                        TOD_ACC_PIN);
-       ocelot_write_rix(ocelot, upper_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_MSB,
-                        TOD_ACC_PIN);
-       ocelot_write_rix(ocelot, ts->tv_nsec, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
-
-       val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
-       val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
-       val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_LOAD);
-
-       ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
-
-       spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
-       return 0;
-}
-
-static int ocelot_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
-{
-       if (delta > -(NSEC_PER_SEC / 2) && delta < (NSEC_PER_SEC / 2)) {
-               struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
-               unsigned long flags;
-               u32 val;
-
-               spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
-
-               val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
-               val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
-               val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
-
-               ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
-
-               ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
-               ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN);
-               ocelot_write_rix(ocelot, delta, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
-
-               val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
-               val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
-               val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_DELTA);
-
-               ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
-
-               spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
-       } else {
-               /* Fall back using ocelot_ptp_settime64 which is not exact. */
-               struct timespec64 ts;
-               u64 now;
-
-               ocelot_ptp_gettime64(ptp, &ts);
-
-               now = ktime_to_ns(timespec64_to_ktime(ts));
-               ts = ns_to_timespec64(now + delta);
-
-               ocelot_ptp_settime64(ptp, &ts);
-       }
-       return 0;
-}
-
-static int ocelot_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
-{
-       struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
-       u32 unit = 0, direction = 0;
-       unsigned long flags;
-       u64 adj = 0;
-
-       spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
-
-       if (!scaled_ppm)
-               goto disable_adj;
-
-       if (scaled_ppm < 0) {
-               direction = PTP_CFG_CLK_ADJ_CFG_DIR;
-               scaled_ppm = -scaled_ppm;
-       }
-
-       adj = PSEC_PER_SEC << 16;
-       do_div(adj, scaled_ppm);
-       do_div(adj, 1000);
-
-       /* If the adjustment value is too large, use ns instead */
-       if (adj >= (1L << 30)) {
-               unit = PTP_CFG_CLK_ADJ_FREQ_NS;
-               do_div(adj, 1000);
-       }
-
-       /* Still too big */
-       if (adj >= (1L << 30))
-               goto disable_adj;
-
-       ocelot_write(ocelot, unit | adj, PTP_CLK_CFG_ADJ_FREQ);
-       ocelot_write(ocelot, PTP_CFG_CLK_ADJ_CFG_ENA | direction,
-                    PTP_CLK_CFG_ADJ_CFG);
-
-       spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
-       return 0;
-
-disable_adj:
-       ocelot_write(ocelot, 0, PTP_CLK_CFG_ADJ_CFG);
-
-       spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
-       return 0;
-}
-
-static struct ptp_clock_info ocelot_ptp_clock_info = {
-       .owner          = THIS_MODULE,
-       .name           = "ocelot ptp",
-       .max_adj        = 0x7fffffff,
-       .n_alarm        = 0,
-       .n_ext_ts       = 0,
-       .n_per_out      = 0,
-       .n_pins         = 0,
-       .pps            = 0,
-       .gettime64      = ocelot_ptp_gettime64,
-       .settime64      = ocelot_ptp_settime64,
-       .adjtime        = ocelot_ptp_adjtime,
-       .adjfine        = ocelot_ptp_adjfine,
-};
-
-static int ocelot_init_timestamp(struct ocelot *ocelot)
-{
-       struct ptp_clock *ptp_clock;
-
-       ocelot->ptp_info = ocelot_ptp_clock_info;
-       ptp_clock = ptp_clock_register(&ocelot->ptp_info, ocelot->dev);
-       if (IS_ERR(ptp_clock))
-               return PTR_ERR(ptp_clock);
-       /* Check if PHC support is missing at the configuration level */
-       if (!ptp_clock)
-               return 0;
-
-       ocelot->ptp_clock = ptp_clock;
-
-       ocelot_write(ocelot, SYS_PTP_CFG_PTP_STAMP_WID(30), SYS_PTP_CFG);
-       ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_LOW);
-       ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_HIGH);
-
-       ocelot_write(ocelot, PTP_CFG_MISC_PTP_EN, PTP_CFG_MISC);
-
-       /* There is no device reconfiguration, PTP Rx stamping is always
-        * enabled.
-        */
-       ocelot->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
-
-       return 0;
-}
-
 /* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu.
  * The length of VLAN tags is accounted for automatically via DEV_MAC_TAGS_CFG.
  * In the special case that it's the NPI port that we're configuring, the
@@ -2528,15 +2346,6 @@ int ocelot_init(struct ocelot *ocelot)
        queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
                           OCELOT_STATS_CHECK_DELAY);
 
-       if (ocelot->ptp) {
-               ret = ocelot_init_timestamp(ocelot);
-               if (ret) {
-                       dev_err(ocelot->dev,
-                               "Timestamp initialization failed\n");
-                       return ret;
-               }
-       }
-
        return 0;
 }
 EXPORT_SYMBOL(ocelot_init);
@@ -2549,8 +2358,6 @@ void ocelot_deinit(struct ocelot *ocelot)
        cancel_delayed_work(&ocelot->stats_work);
        destroy_workqueue(ocelot->stats_queue);
        mutex_destroy(&ocelot->stats_lock);
-       if (ocelot->ptp_clock)
-               ptp_clock_unregister(ocelot->ptp_clock);
 
        for (i = 0; i < ocelot->num_phys_ports; i++) {
                port = ocelot->ports[i];
index e34ef83..f0a15aa 100644 (file)
 #include <linux/phy.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
-#include <linux/ptp_clock_kernel.h>
 #include <linux/regmap.h>
 
 #include <soc/mscc/ocelot_qsys.h>
 #include <soc/mscc/ocelot_sys.h>
 #include <soc/mscc/ocelot_dev.h>
 #include <soc/mscc/ocelot_ana.h>
+#include <soc/mscc/ocelot_ptp.h>
 #include <soc/mscc/ocelot.h>
 #include "ocelot_rew.h"
 #include "ocelot_qs.h"
 #include "ocelot_tc.h"
-#include "ocelot_ptp.h"
 
 #define OCELOT_BUFFER_CELL_SZ 60
 
@@ -56,8 +55,6 @@ struct ocelot_port_private {
        struct phy_device *phy;
        u8 chip_port;
 
-       u8 vlan_aware;
-
        struct phy *serdes;
 
        struct ocelot_port_tc tc;
index 3bd2860..dfd82a3 100644 (file)
@@ -706,13 +706,124 @@ ocelot_ace_rule_get_rule_index(struct ocelot_acl_block *block, int index)
        return NULL;
 }
 
+/* If @on=false, then SNAP, ARP, IP and OAM frames will not match on keys based
+ * on destination and source MAC addresses, but only on higher-level protocol
+ * information. The only frame types to match on keys containing MAC addresses
+ * in this case are non-SNAP, non-ARP, non-IP and non-OAM frames.
+ *
+ * If @on=true, then the above frame types (SNAP, ARP, IP and OAM) will match
+ * on MAC_ETYPE keys such as destination and source MAC on this ingress port.
+ * However the setting has the side effect of making these frames not matching
+ * on any _other_ keys than MAC_ETYPE ones.
+ */
+static void ocelot_match_all_as_mac_etype(struct ocelot *ocelot, int port,
+                                         bool on)
+{
+       u32 val = 0;
+
+       if (on)
+               val = ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS(3) |
+                     ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS(3) |
+                     ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS(3) |
+                     ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS(3) |
+                     ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS(3);
+
+       ocelot_rmw_gix(ocelot, val,
+                      ANA_PORT_VCAP_S2_CFG_S2_SNAP_DIS_M |
+                      ANA_PORT_VCAP_S2_CFG_S2_ARP_DIS_M |
+                      ANA_PORT_VCAP_S2_CFG_S2_IP_TCPUDP_DIS_M |
+                      ANA_PORT_VCAP_S2_CFG_S2_IP_OTHER_DIS_M |
+                      ANA_PORT_VCAP_S2_CFG_S2_OAM_DIS_M,
+                      ANA_PORT_VCAP_S2_CFG, port);
+}
+
+static bool ocelot_ace_is_problematic_mac_etype(struct ocelot_ace_rule *ace)
+{
+       u16 proto, mask;
+
+       if (ace->type != OCELOT_ACE_TYPE_ETYPE)
+               return false;
+
+       proto = ntohs(*(u16 *)ace->frame.etype.etype.value);
+       mask = ntohs(*(u16 *)ace->frame.etype.etype.mask);
+
+       /* ETH_P_ALL match, so all protocols below are included */
+       if (mask == 0)
+               return true;
+       if (proto == ETH_P_ARP)
+               return true;
+       if (proto == ETH_P_IP)
+               return true;
+       if (proto == ETH_P_IPV6)
+               return true;
+
+       return false;
+}
+
+static bool ocelot_ace_is_problematic_non_mac_etype(struct ocelot_ace_rule *ace)
+{
+       if (ace->type == OCELOT_ACE_TYPE_SNAP)
+               return true;
+       if (ace->type == OCELOT_ACE_TYPE_ARP)
+               return true;
+       if (ace->type == OCELOT_ACE_TYPE_IPV4)
+               return true;
+       if (ace->type == OCELOT_ACE_TYPE_IPV6)
+               return true;
+       return false;
+}
+
+static bool ocelot_exclusive_mac_etype_ace_rules(struct ocelot *ocelot,
+                                                struct ocelot_ace_rule *ace)
+{
+       struct ocelot_acl_block *block = &ocelot->acl_block;
+       struct ocelot_ace_rule *tmp;
+       unsigned long port;
+       int i;
+
+       if (ocelot_ace_is_problematic_mac_etype(ace)) {
+               /* Search for any non-MAC_ETYPE rules on the port */
+               for (i = 0; i < block->count; i++) {
+                       tmp = ocelot_ace_rule_get_rule_index(block, i);
+                       if (tmp->ingress_port_mask & ace->ingress_port_mask &&
+                           ocelot_ace_is_problematic_non_mac_etype(tmp))
+                               return false;
+               }
+
+               for_each_set_bit(port, &ace->ingress_port_mask,
+                                ocelot->num_phys_ports)
+                       ocelot_match_all_as_mac_etype(ocelot, port, true);
+       } else if (ocelot_ace_is_problematic_non_mac_etype(ace)) {
+               /* Search for any MAC_ETYPE rules on the port */
+               for (i = 0; i < block->count; i++) {
+                       tmp = ocelot_ace_rule_get_rule_index(block, i);
+                       if (tmp->ingress_port_mask & ace->ingress_port_mask &&
+                           ocelot_ace_is_problematic_mac_etype(tmp))
+                               return false;
+               }
+
+               for_each_set_bit(port, &ace->ingress_port_mask,
+                                ocelot->num_phys_ports)
+                       ocelot_match_all_as_mac_etype(ocelot, port, false);
+       }
+
+       return true;
+}
+
 int ocelot_ace_rule_offload_add(struct ocelot *ocelot,
-                               struct ocelot_ace_rule *rule)
+                               struct ocelot_ace_rule *rule,
+                               struct netlink_ext_ack *extack)
 {
        struct ocelot_acl_block *block = &ocelot->acl_block;
        struct ocelot_ace_rule *ace;
        int i, index;
 
+       if (!ocelot_exclusive_mac_etype_ace_rules(ocelot, rule)) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Cannot mix MAC_ETYPE with non-MAC_ETYPE rules");
+               return -EBUSY;
+       }
+
        /* Add rule to the linked list */
        ocelot_ace_rule_add(ocelot, block, rule);
 
index 29d22c5..099e177 100644 (file)
@@ -194,7 +194,7 @@ struct ocelot_ace_rule {
 
        enum ocelot_ace_action action;
        struct ocelot_ace_stats stats;
-       u16 ingress_port_mask;
+       unsigned long ingress_port_mask;
 
        enum ocelot_vcap_bit dmac_mc;
        enum ocelot_vcap_bit dmac_bc;
@@ -215,7 +215,8 @@ struct ocelot_ace_rule {
 };
 
 int ocelot_ace_rule_offload_add(struct ocelot *ocelot,
-                               struct ocelot_ace_rule *rule);
+                               struct ocelot_ace_rule *rule,
+                               struct netlink_ext_ack *extack);
 int ocelot_ace_rule_offload_del(struct ocelot *ocelot,
                                struct ocelot_ace_rule *rule);
 int ocelot_ace_rule_stats_update(struct ocelot *ocelot,
index 0ac9fbf..67a8d61 100644 (file)
@@ -366,6 +366,23 @@ static const struct vcap_props vsc7514_vcap_props[] = {
        },
 };
 
+static struct ptp_clock_info ocelot_ptp_clock_info = {
+       .owner          = THIS_MODULE,
+       .name           = "ocelot ptp",
+       .max_adj        = 0x7fffffff,
+       .n_alarm        = 0,
+       .n_ext_ts       = 0,
+       .n_per_out      = OCELOT_PTP_PINS_NUM,
+       .n_pins         = OCELOT_PTP_PINS_NUM,
+       .pps            = 0,
+       .gettime64      = ocelot_ptp_gettime64,
+       .settime64      = ocelot_ptp_settime64,
+       .adjtime        = ocelot_ptp_adjtime,
+       .adjfine        = ocelot_ptp_adjfine,
+       .verify         = ocelot_ptp_verify,
+       .enable         = ocelot_ptp_enable,
+};
+
 static int mscc_ocelot_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
@@ -469,6 +486,15 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
        ocelot->vcap = vsc7514_vcap_props;
 
        ocelot_init(ocelot);
+       if (ocelot->ptp) {
+               err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
+               if (err) {
+                       dev_err(ocelot->dev,
+                               "Timestamp initialization failed\n");
+                       ocelot->ptp = 0;
+               }
+       }
+
        /* No NPI port */
        ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE,
                             OCELOT_TAG_PREFIX_NONE);
@@ -574,6 +600,7 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
 {
        struct ocelot *ocelot = platform_get_drvdata(pdev);
 
+       ocelot_deinit_timestamp(ocelot);
        ocelot_deinit(ocelot);
        unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
        unregister_switchdev_notifier(&ocelot_switchdev_nb);
index 3419233..5ce172e 100644 (file)
@@ -51,6 +51,8 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
 {
        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
        struct flow_dissector *dissector = rule->match.dissector;
+       u16 proto = ntohs(f->common.protocol);
+       bool match_protocol = true;
 
        if (dissector->used_keys &
            ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
@@ -71,7 +73,6 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
 
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
                struct flow_match_eth_addrs match;
-               u16 proto = ntohs(f->common.protocol);
 
                /* The hw support mac matches only for MAC_ETYPE key,
                 * therefore if other matches(port, tcp flags, etc) are added
@@ -86,11 +87,6 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
                     BIT(FLOW_DISSECTOR_KEY_CONTROL)))
                        return -EOPNOTSUPP;
 
-               if (proto == ETH_P_IP ||
-                   proto == ETH_P_IPV6 ||
-                   proto == ETH_P_ARP)
-                       return -EOPNOTSUPP;
-
                flow_rule_match_eth_addrs(rule, &match);
                ace->type = OCELOT_ACE_TYPE_ETYPE;
                ether_addr_copy(ace->frame.etype.dmac.value,
@@ -114,6 +110,7 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
                                match.key->ip_proto;
                        ace->frame.ipv4.proto.mask[0] =
                                match.mask->ip_proto;
+                       match_protocol = false;
                }
                if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
                        ace->type = OCELOT_ACE_TYPE_IPV6;
@@ -121,11 +118,12 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
                                match.key->ip_proto;
                        ace->frame.ipv6.proto.mask[0] =
                                match.mask->ip_proto;
+                       match_protocol = false;
                }
        }
 
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&
-           ntohs(f->common.protocol) == ETH_P_IP) {
+           proto == ETH_P_IP) {
                struct flow_match_ipv4_addrs match;
                u8 *tmp;
 
@@ -141,10 +139,11 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
 
                tmp = &ace->frame.ipv4.dip.mask.addr[0];
                memcpy(tmp, &match.mask->dst, 4);
+               match_protocol = false;
        }
 
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&
-           ntohs(f->common.protocol) == ETH_P_IPV6) {
+           proto == ETH_P_IPV6) {
                return -EOPNOTSUPP;
        }
 
@@ -156,6 +155,7 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
                ace->frame.ipv4.sport.mask = ntohs(match.mask->src);
                ace->frame.ipv4.dport.value = ntohs(match.key->dst);
                ace->frame.ipv4.dport.mask = ntohs(match.mask->dst);
+               match_protocol = false;
        }
 
        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
@@ -167,9 +167,20 @@ static int ocelot_flower_parse(struct flow_cls_offload *f,
                ace->vlan.vid.mask = match.mask->vlan_id;
                ace->vlan.pcp.value[0] = match.key->vlan_priority;
                ace->vlan.pcp.mask[0] = match.mask->vlan_priority;
+               match_protocol = false;
        }
 
 finished_key_parsing:
+       if (match_protocol && proto != ETH_P_ALL) {
+               /* TODO: support SNAP, LLC etc */
+               if (proto < ETH_P_802_3_MIN)
+                       return -EOPNOTSUPP;
+               ace->type = OCELOT_ACE_TYPE_ETYPE;
+               *(u16 *)ace->frame.etype.etype.value = htons(proto);
+               *(u16 *)ace->frame.etype.etype.mask = 0xffff;
+       }
+       /* else, a rule of type OCELOT_ACE_TYPE_ANY is implicitly added */
+
        ace->prio = f->common.prio;
        ace->id = f->cookie;
        return ocelot_flower_parse_action(f, ace);
@@ -205,7 +216,7 @@ int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
                return ret;
        }
 
-       return ocelot_ace_rule_offload_add(ocelot, ace);
+       return ocelot_ace_rule_offload_add(ocelot, ace, f->common.extack);
 }
 EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
 
diff --git a/drivers/net/ethernet/mscc/ocelot_ptp.c b/drivers/net/ethernet/mscc/ocelot_ptp.c
new file mode 100644 (file)
index 0000000..a3088a1
--- /dev/null
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Microsemi Ocelot PTP clock driver
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ * Copyright 2020 NXP
+ */
+#include <soc/mscc/ocelot_ptp.h>
+#include <soc/mscc/ocelot_sys.h>
+#include <soc/mscc/ocelot.h>
+
+int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
+{
+       struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
+       unsigned long flags;
+       time64_t s;
+       u32 val;
+       s64 ns;
+
+       spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
+
+       val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
+       val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
+       val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE);
+       ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
+
+       s = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN) & 0xffff;
+       s <<= 32;
+       s += ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
+       ns = ocelot_read_rix(ocelot, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
+
+       spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
+
+       /* Deal with negative values */
+       if (ns >= 0x3ffffff0 && ns <= 0x3fffffff) {
+               s--;
+               ns &= 0xf;
+               ns += 999999984;
+       }
+
+       set_normalized_timespec64(ts, s, ns);
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_ptp_gettime64);
+
+int ocelot_ptp_settime64(struct ptp_clock_info *ptp,
+                        const struct timespec64 *ts)
+{
+       struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
+       unsigned long flags;
+       u32 val;
+
+       spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
+
+       val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
+       val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
+       val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
+
+       ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
+
+       ocelot_write_rix(ocelot, lower_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_LSB,
+                        TOD_ACC_PIN);
+       ocelot_write_rix(ocelot, upper_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_MSB,
+                        TOD_ACC_PIN);
+       ocelot_write_rix(ocelot, ts->tv_nsec, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
+
+       val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
+       val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
+       val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_LOAD);
+
+       ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
+
+       spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_ptp_settime64);
+
+int ocelot_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+       if (delta > -(NSEC_PER_SEC / 2) && delta < (NSEC_PER_SEC / 2)) {
+               struct ocelot *ocelot = container_of(ptp, struct ocelot,
+                                                    ptp_info);
+               unsigned long flags;
+               u32 val;
+
+               spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
+
+               val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
+               val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK |
+                        PTP_PIN_CFG_DOM);
+               val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
+
+               ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
+
+               ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
+               ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN);
+               ocelot_write_rix(ocelot, delta, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
+
+               val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
+               val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK |
+                        PTP_PIN_CFG_DOM);
+               val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_DELTA);
+
+               ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
+
+               spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
+       } else {
+               /* Fall back using ocelot_ptp_settime64 which is not exact. */
+               struct timespec64 ts;
+               u64 now;
+
+               ocelot_ptp_gettime64(ptp, &ts);
+
+               now = ktime_to_ns(timespec64_to_ktime(ts));
+               ts = ns_to_timespec64(now + delta);
+
+               ocelot_ptp_settime64(ptp, &ts);
+       }
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_ptp_adjtime);
+
+int ocelot_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+       struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
+       u32 unit = 0, direction = 0;
+       unsigned long flags;
+       u64 adj = 0;
+
+       spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
+
+       if (!scaled_ppm)
+               goto disable_adj;
+
+       if (scaled_ppm < 0) {
+               direction = PTP_CFG_CLK_ADJ_CFG_DIR;
+               scaled_ppm = -scaled_ppm;
+       }
+
+       adj = PSEC_PER_SEC << 16;
+       do_div(adj, scaled_ppm);
+       do_div(adj, 1000);
+
+       /* If the adjustment value is too large, use ns instead */
+       if (adj >= (1L << 30)) {
+               unit = PTP_CFG_CLK_ADJ_FREQ_NS;
+               do_div(adj, 1000);
+       }
+
+       /* Still too big */
+       if (adj >= (1L << 30))
+               goto disable_adj;
+
+       ocelot_write(ocelot, unit | adj, PTP_CLK_CFG_ADJ_FREQ);
+       ocelot_write(ocelot, PTP_CFG_CLK_ADJ_CFG_ENA | direction,
+                    PTP_CLK_CFG_ADJ_CFG);
+
+       spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
+       return 0;
+
+disable_adj:
+       ocelot_write(ocelot, 0, PTP_CLK_CFG_ADJ_CFG);
+
+       spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_ptp_adjfine);
+
+int ocelot_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
+                     enum ptp_pin_function func, unsigned int chan)
+{
+       switch (func) {
+       case PTP_PF_NONE:
+       case PTP_PF_PEROUT:
+               break;
+       case PTP_PF_EXTTS:
+       case PTP_PF_PHYSYNC:
+               return -1;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_ptp_verify);
+
+int ocelot_ptp_enable(struct ptp_clock_info *ptp,
+                     struct ptp_clock_request *rq, int on)
+{
+       struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
+       struct timespec64 ts_start, ts_period;
+       enum ocelot_ptp_pins ptp_pin;
+       unsigned long flags;
+       bool pps = false;
+       int pin = -1;
+       u32 val;
+       s64 ns;
+
+       switch (rq->type) {
+       case PTP_CLK_REQ_PEROUT:
+               /* Reject requests with unsupported flags */
+               if (rq->perout.flags)
+                       return -EOPNOTSUPP;
+
+               pin = ptp_find_pin(ocelot->ptp_clock, PTP_PF_PEROUT,
+                                  rq->perout.index);
+               if (pin == 0)
+                       ptp_pin = PTP_PIN_0;
+               else if (pin == 1)
+                       ptp_pin = PTP_PIN_1;
+               else if (pin == 2)
+                       ptp_pin = PTP_PIN_2;
+               else if (pin == 3)
+                       ptp_pin = PTP_PIN_3;
+               else
+                       return -EBUSY;
+
+               ts_start.tv_sec = rq->perout.start.sec;
+               ts_start.tv_nsec = rq->perout.start.nsec;
+               ts_period.tv_sec = rq->perout.period.sec;
+               ts_period.tv_nsec = rq->perout.period.nsec;
+
+               if (ts_period.tv_sec == 1 && ts_period.tv_nsec == 0)
+                       pps = true;
+
+               if (ts_start.tv_sec || (ts_start.tv_nsec && !pps)) {
+                       dev_warn(ocelot->dev,
+                                "Absolute start time not supported!\n");
+                       dev_warn(ocelot->dev,
+                                "Accept nsec for PPS phase adjustment, otherwise start time should be 0 0.\n");
+                       return -EINVAL;
+               }
+
+               /* Handle turning off */
+               if (!on) {
+                       spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
+                       val = PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
+                       ocelot_write_rix(ocelot, val, PTP_PIN_CFG, ptp_pin);
+                       spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
+                       break;
+               }
+
+               /* Handle PPS request */
+               if (pps) {
+                       spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
+                       /* Pulse generated perout.start.nsec after TOD has
+                        * increased seconds.
+                        * Pulse width is set to 1us.
+                        */
+                       ocelot_write_rix(ocelot, ts_start.tv_nsec,
+                                        PTP_PIN_WF_LOW_PERIOD, ptp_pin);
+                       ocelot_write_rix(ocelot, 1000,
+                                        PTP_PIN_WF_HIGH_PERIOD, ptp_pin);
+                       val = PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_CLOCK);
+                       val |= PTP_PIN_CFG_SYNC;
+                       ocelot_write_rix(ocelot, val, PTP_PIN_CFG, ptp_pin);
+                       spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
+                       break;
+               }
+
+               /* Handle periodic clock */
+               ns = timespec64_to_ns(&ts_period);
+               ns = ns >> 1;
+               if (ns > 0x3fffffff || ns <= 0x6)
+                       return -EINVAL;
+
+               spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
+               ocelot_write_rix(ocelot, ns, PTP_PIN_WF_LOW_PERIOD, ptp_pin);
+               ocelot_write_rix(ocelot, ns, PTP_PIN_WF_HIGH_PERIOD, ptp_pin);
+               val = PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_CLOCK);
+               ocelot_write_rix(ocelot, val, PTP_PIN_CFG, ptp_pin);
+               spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_ptp_enable);
+
+int ocelot_init_timestamp(struct ocelot *ocelot, struct ptp_clock_info *info)
+{
+       struct ptp_clock *ptp_clock;
+       int i;
+
+       ocelot->ptp_info = *info;
+
+       for (i = 0; i < OCELOT_PTP_PINS_NUM; i++) {
+               struct ptp_pin_desc *p = &ocelot->ptp_pins[i];
+
+               snprintf(p->name, sizeof(p->name), "switch_1588_dat%d", i);
+               p->index = i;
+               p->func = PTP_PF_NONE;
+       }
+
+       ocelot->ptp_info.pin_config = &ocelot->ptp_pins[0];
+
+       ptp_clock = ptp_clock_register(&ocelot->ptp_info, ocelot->dev);
+       if (IS_ERR(ptp_clock))
+               return PTR_ERR(ptp_clock);
+       /* Check if PHC support is missing at the configuration level */
+       if (!ptp_clock)
+               return 0;
+
+       ocelot->ptp_clock = ptp_clock;
+
+       ocelot_write(ocelot, SYS_PTP_CFG_PTP_STAMP_WID(30), SYS_PTP_CFG);
+       ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_LOW);
+       ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_HIGH);
+
+       ocelot_write(ocelot, PTP_CFG_MISC_PTP_EN, PTP_CFG_MISC);
+
+       /* There is no device reconfiguration, PTP Rx stamping is always
+        * enabled.
+        */
+       ocelot->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_init_timestamp);
+
+int ocelot_deinit_timestamp(struct ocelot *ocelot)
+{
+       if (ocelot->ptp_clock)
+               ptp_clock_unregister(ocelot->ptp_clock);
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_deinit_timestamp);
diff --git a/drivers/net/ethernet/mscc/ocelot_ptp.h b/drivers/net/ethernet/mscc/ocelot_ptp.h
deleted file mode 100644 (file)
index 9ede14a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
-/*
- * Microsemi Ocelot Switch driver
- *
- * License: Dual MIT/GPL
- * Copyright (c) 2017 Microsemi Corporation
- */
-
-#ifndef _MSCC_OCELOT_PTP_H_
-#define _MSCC_OCELOT_PTP_H_
-
-#define PTP_PIN_CFG_RSZ                        0x20
-#define PTP_PIN_TOD_SEC_MSB_RSZ                PTP_PIN_CFG_RSZ
-#define PTP_PIN_TOD_SEC_LSB_RSZ                PTP_PIN_CFG_RSZ
-#define PTP_PIN_TOD_NSEC_RSZ           PTP_PIN_CFG_RSZ
-
-#define PTP_PIN_CFG_DOM                        BIT(0)
-#define PTP_PIN_CFG_SYNC               BIT(2)
-#define PTP_PIN_CFG_ACTION(x)          ((x) << 3)
-#define PTP_PIN_CFG_ACTION_MASK                PTP_PIN_CFG_ACTION(0x7)
-
-enum {
-       PTP_PIN_ACTION_IDLE = 0,
-       PTP_PIN_ACTION_LOAD,
-       PTP_PIN_ACTION_SAVE,
-       PTP_PIN_ACTION_CLOCK,
-       PTP_PIN_ACTION_DELTA,
-       PTP_PIN_ACTION_NOSYNC,
-       PTP_PIN_ACTION_SYNC,
-};
-
-#define PTP_CFG_MISC_PTP_EN            BIT(2)
-
-#define PSEC_PER_SEC                   1000000000000LL
-
-#define PTP_CFG_CLK_ADJ_CFG_ENA                BIT(0)
-#define PTP_CFG_CLK_ADJ_CFG_DIR                BIT(1)
-
-#define PTP_CFG_CLK_ADJ_FREQ_NS                BIT(30)
-
-#endif
index b88b589..81d81ff 100644 (file)
@@ -239,6 +239,8 @@ static const u32 ocelot_ptp_regmap[] = {
        REG(PTP_PIN_TOD_SEC_MSB,           0x000004),
        REG(PTP_PIN_TOD_SEC_LSB,           0x000008),
        REG(PTP_PIN_TOD_NSEC,              0x00000c),
+       REG(PTP_PIN_WF_HIGH_PERIOD,        0x000014),
+       REG(PTP_PIN_WF_LOW_PERIOD,         0x000018),
        REG(PTP_CFG_MISC,                  0x0000a0),
        REG(PTP_CLK_CFG_ADJ_CFG,           0x0000a4),
        REG(PTP_CLK_CFG_ADJ_FREQ,          0x0000a8),
@@ -431,6 +433,7 @@ int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops)
        ocelot->stats_layout = ocelot_stats_layout;
        ocelot->num_stats = ARRAY_SIZE(ocelot_stats_layout);
        ocelot->shared_queue_sz = 224 * 1024;
+       ocelot->num_mact_rows = 1024;
        ocelot->ops = ops;
 
        ret = ocelot_regfields_init(ocelot, ocelot_regfields);
index 2616fd7..e1e1f4e 100644 (file)
@@ -1174,18 +1174,6 @@ myri10ge_submit_8rx(struct mcp_kreq_ether_recv __iomem * dst,
        mb();
 }
 
-static inline void myri10ge_vlan_ip_csum(struct sk_buff *skb, __wsum hw_csum)
-{
-       struct vlan_hdr *vh = (struct vlan_hdr *)(skb->data);
-
-       if ((skb->protocol == htons(ETH_P_8021Q)) &&
-           (vh->h_vlan_encapsulated_proto == htons(ETH_P_IP) ||
-            vh->h_vlan_encapsulated_proto == htons(ETH_P_IPV6))) {
-               skb->csum = hw_csum;
-               skb->ip_summed = CHECKSUM_COMPLETE;
-       }
-}
-
 static void
 myri10ge_alloc_rx_pages(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx,
                        int bytes, int watchdog)
index bfa0c0d..8b018ed 100644 (file)
@@ -208,11 +208,13 @@ static int jazz_sonic_probe(struct platform_device *pdev)
 
        err = register_netdev(dev);
        if (err)
-               goto out1;
+               goto undo_probe1;
 
        return 0;
 
-out1:
+undo_probe1:
+       dma_free_coherent(lp->device, SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
+                         lp->descriptors, lp->descriptors_laddr);
        release_mem_region(dev->base_addr, SONIC_MEM_SIZE);
 out:
        free_netdev(dev);
index 5e630f3..a82a370 100644 (file)
@@ -27,7 +27,7 @@ config S2IO
          on its age.
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/device_drivers/neterion/s2io.txt>.
+         <file:Documentation/networking/device_drivers/neterion/s2io.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called s2io.
@@ -42,7 +42,7 @@ config VXGE
          labeled as either one, depending on its age.
 
          More specific information on configuring the driver is in
-         <file:Documentation/networking/device_drivers/neterion/vxge.txt>.
+         <file:Documentation/networking/device_drivers/neterion/vxge.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called vxge.
index 0ec6b8e..67e6260 100644 (file)
@@ -5155,7 +5155,7 @@ static int do_s2io_delete_unicast_mc(struct s2io_nic *sp, u64 addr)
 /* read mac entries from CAM */
 static u64 do_s2io_read_unicast_mc(struct s2io_nic *sp, int offset)
 {
-       u64 tmp64 = 0xffffffffffff0000ULL, val64;
+       u64 tmp64, val64;
        struct XENA_dev_config __iomem *bar0 = sp->bar0;
 
        /* read mac addr */
index 9183b3e..354efff 100644 (file)
@@ -283,6 +283,7 @@ nfp_abm_vnic_set_mac(struct nfp_pf *pf, struct nfp_abm *abm, struct nfp_net *nn,
        if (!nfp_nsp_has_hwinfo_lookup(nsp)) {
                nfp_warn(pf->cpp, "NSP doesn't support PF MAC generation\n");
                eth_hw_addr_random(nn->dp.netdev);
+               nfp_nsp_close(nsp);
                return;
        }
 
index 4d282fc..7ff2ccb 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/mutex.h>
 #include <linux/pci.h>
 #include <linux/firmware.h>
-#include <linux/vermagic.h>
 #include <linux/vmalloc.h>
 #include <net/devlink.h>
 
@@ -31,7 +30,6 @@
 #include "nfp_net.h"
 
 static const char nfp_driver_name[] = "nfp";
-const char nfp_driver_version[] = VERMAGIC_STRING;
 
 static const struct pci_device_id nfp_pci_device_ids[] = {
        { PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP6000,
@@ -920,4 +918,3 @@ MODULE_FIRMWARE("netronome/nic_AMDA0099-0001_1x10_1x25.nffw");
 MODULE_AUTHOR("Netronome Systems <oss-drivers@netronome.com>");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("The Netronome Flow Processor (NFP) driver.");
-MODULE_VERSION(UTS_RELEASE);
index 2779f15..a5aa321 100644 (file)
@@ -203,8 +203,6 @@ nfp_get_drvinfo(struct nfp_app *app, struct pci_dev *pdev,
        char nsp_version[ETHTOOL_FWVERS_LEN] = {};
 
        strlcpy(drvinfo->driver, pdev->driver->name, sizeof(drvinfo->driver));
-       strlcpy(drvinfo->version, nfp_driver_version, sizeof(drvinfo->version));
-
        nfp_net_get_nspinfo(app, nsp_version);
        snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
                 "%s %s %s %s", vnic_version, nsp_version,
index 79d72c8..b3cabc2 100644 (file)
@@ -299,6 +299,20 @@ static void nfp_repr_clean(struct nfp_repr *repr)
        nfp_port_free(repr->port);
 }
 
+static struct lock_class_key nfp_repr_netdev_xmit_lock_key;
+
+static void nfp_repr_set_lockdep_class_one(struct net_device *dev,
+                                          struct netdev_queue *txq,
+                                          void *_unused)
+{
+       lockdep_set_class(&txq->_xmit_lock, &nfp_repr_netdev_xmit_lock_key);
+}
+
+static void nfp_repr_set_lockdep_class(struct net_device *dev)
+{
+       netdev_for_each_tx_queue(dev, nfp_repr_set_lockdep_class_one, NULL);
+}
+
 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)
@@ -308,6 +322,8 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev,
        u32 repr_cap = nn->tlv_caps.repr_cap;
        int err;
 
+       nfp_repr_set_lockdep_class(netdev);
+
        repr->port = port;
        repr->dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, GFP_KERNEL);
        if (!repr->dst)
index 2fdd075..d2708a5 100644 (file)
@@ -502,7 +502,8 @@ static int nixge_check_tx_bd_space(struct nixge_priv *priv,
        return 0;
 }
 
-static int nixge_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t nixge_start_xmit(struct sk_buff *skb,
+                                   struct net_device *ndev)
 {
        struct nixge_priv *priv = netdev_priv(ndev);
        struct nixge_hw_dma_bd *cur_p;
index d20cf03..d3cbb42 100644 (file)
@@ -823,7 +823,8 @@ static int lpc_mii_init(struct netdata_local *pldat)
        if (err)
                goto err_out_unregister_bus;
 
-       if (lpc_mii_probe(pldat->ndev) != 0)
+       err = lpc_mii_probe(pldat->ndev);
+       if (err)
                goto err_out_unregister_bus;
 
        return 0;
@@ -1029,7 +1030,8 @@ static int lpc_eth_close(struct net_device *ndev)
        return 0;
 }
 
-static int lpc_eth_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t lpc_eth_hard_start_xmit(struct sk_buff *skb,
+                                          struct net_device *ndev)
 {
        struct netdata_local *pldat = netdev_priv(ndev);
        u32 len, txidx;
index 5f8fc58..11621cc 100644 (file)
@@ -170,8 +170,7 @@ void ionic_debugfs_add_qcq(struct ionic_lif *lif, struct ionic_qcq *qcq)
        debugfs_create_x64("base_pa", 0400, cq_dentry, &cq->base_pa);
        debugfs_create_u32("num_descs", 0400, cq_dentry, &cq->num_descs);
        debugfs_create_u32("desc_size", 0400, cq_dentry, &cq->desc_size);
-       debugfs_create_u8("done_color", 0400, cq_dentry,
-                         (u8 *)&cq->done_color);
+       debugfs_create_bool("done_color", 0400, cq_dentry, &cq->done_color);
 
        debugfs_create_file("tail", 0400, cq_dentry, cq, &cq_tail_fops);
 
index 4b8a760..d5293bf 100644 (file)
@@ -2101,6 +2101,7 @@ static void ionic_lif_handle_fw_down(struct ionic_lif *lif)
                ionic_txrx_free(lif);
        }
        ionic_lifs_deinit(ionic);
+       ionic_reset(ionic);
        ionic_qcqs_free(lif);
 
        dev_info(ionic->dev, "FW Down: LIFs stopped\n");
@@ -2116,6 +2117,7 @@ static void ionic_lif_handle_fw_up(struct ionic_lif *lif)
 
        dev_info(ionic->dev, "FW Up: restarting LIFs\n");
 
+       ionic_init_devinfo(ionic);
        err = ionic_qcqs_alloc(lif);
        if (err)
                goto err_out;
@@ -2127,6 +2129,8 @@ static void ionic_lif_handle_fw_up(struct ionic_lif *lif)
        if (lif->registered)
                ionic_lif_set_netdev_info(lif);
 
+       ionic_rx_filter_replay(lif);
+
        if (netif_running(lif->netdev)) {
                err = ionic_txrx_alloc(lif);
                if (err)
@@ -2206,9 +2210,9 @@ static void ionic_lif_deinit(struct ionic_lif *lif)
        if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
                cancel_work_sync(&lif->deferred.work);
                cancel_work_sync(&lif->tx_timeout_work);
+               ionic_rx_filters_deinit(lif);
        }
 
-       ionic_rx_filters_deinit(lif);
        if (lif->netdev->features & NETIF_F_RXHASH)
                ionic_lif_rss_deinit(lif);
 
@@ -2339,24 +2343,30 @@ static int ionic_station_set(struct ionic_lif *lif)
        err = ionic_adminq_post_wait(lif, &ctx);
        if (err)
                return err;
-
+       netdev_dbg(lif->netdev, "found initial MAC addr %pM\n",
+                  ctx.comp.lif_getattr.mac);
        if (is_zero_ether_addr(ctx.comp.lif_getattr.mac))
                return 0;
 
-       memcpy(addr.sa_data, ctx.comp.lif_getattr.mac, netdev->addr_len);
-       addr.sa_family = AF_INET;
-       err = eth_prepare_mac_addr_change(netdev, &addr);
-       if (err) {
-               netdev_warn(lif->netdev, "ignoring bad MAC addr from NIC %pM - err %d\n",
-                           addr.sa_data, err);
-               return 0;
-       }
+       if (!ether_addr_equal(ctx.comp.lif_getattr.mac, netdev->dev_addr)) {
+               memcpy(addr.sa_data, ctx.comp.lif_getattr.mac, netdev->addr_len);
+               addr.sa_family = AF_INET;
+               err = eth_prepare_mac_addr_change(netdev, &addr);
+               if (err) {
+                       netdev_warn(lif->netdev, "ignoring bad MAC addr from NIC %pM - err %d\n",
+                                   addr.sa_data, err);
+                       return 0;
+               }
 
-       netdev_dbg(lif->netdev, "deleting station MAC addr %pM\n",
-                  netdev->dev_addr);
-       ionic_lif_addr(lif, netdev->dev_addr, false);
+               if (!is_zero_ether_addr(netdev->dev_addr)) {
+                       netdev_dbg(lif->netdev, "deleting station MAC addr %pM\n",
+                                  netdev->dev_addr);
+                       ionic_lif_addr(lif, netdev->dev_addr, false);
+               }
+
+               eth_commit_mac_addr_change(netdev, &addr);
+       }
 
-       eth_commit_mac_addr_change(netdev, &addr);
        netdev_dbg(lif->netdev, "adding station MAC addr %pM\n",
                   netdev->dev_addr);
        ionic_lif_addr(lif, netdev->dev_addr, true);
@@ -2421,9 +2431,11 @@ static int ionic_lif_init(struct ionic_lif *lif)
        if (err)
                goto err_out_notifyq_deinit;
 
-       err = ionic_rx_filters_init(lif);
-       if (err)
-               goto err_out_notifyq_deinit;
+       if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
+               err = ionic_rx_filters_init(lif);
+               if (err)
+                       goto err_out_notifyq_deinit;
+       }
 
        err = ionic_station_set(lif);
        if (err)
@@ -2539,8 +2551,6 @@ int ionic_lifs_register(struct ionic *ionic)
                dev_err(ionic->dev, "Cannot register net device, aborting\n");
                return err;
        }
-
-       ionic_link_status_check_request(ionic->master_lif);
        ionic->master_lif->registered = true;
 
        return 0;
index 588c62e..3ed1505 100644 (file)
@@ -6,7 +6,7 @@
 #include <linux/module.h>
 #include <linux/netdevice.h>
 #include <linux/utsname.h>
-#include <linux/vermagic.h>
+#include <generated/utsrelease.h>
 
 #include "ionic.h"
 #include "ionic_bus.h"
index 7a093f1..80eeb76 100644 (file)
@@ -2,6 +2,7 @@
 /* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
 
 #include <linux/netdevice.h>
+#include <linux/dynamic_debug.h>
 #include <linux/etherdevice.h>
 
 #include "ionic.h"
@@ -17,17 +18,49 @@ void ionic_rx_filter_free(struct ionic_lif *lif, struct ionic_rx_filter *f)
        devm_kfree(dev, f);
 }
 
-int ionic_rx_filter_del(struct ionic_lif *lif, struct ionic_rx_filter *f)
+void ionic_rx_filter_replay(struct ionic_lif *lif)
 {
-       struct ionic_admin_ctx ctx = {
-               .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
-               .cmd.rx_filter_del = {
-                       .opcode = IONIC_CMD_RX_FILTER_DEL,
-                       .filter_id = cpu_to_le32(f->filter_id),
-               },
-       };
-
-       return ionic_adminq_post_wait(lif, &ctx);
+       struct ionic_rx_filter_add_cmd *ac;
+       struct ionic_admin_ctx ctx;
+       struct ionic_rx_filter *f;
+       struct hlist_head *head;
+       struct hlist_node *tmp;
+       unsigned int i;
+       int err;
+
+       ac = &ctx.cmd.rx_filter_add;
+
+       for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
+               head = &lif->rx_filters.by_id[i];
+               hlist_for_each_entry_safe(f, tmp, head, by_id) {
+                       ctx.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work);
+                       memcpy(ac, &f->cmd, sizeof(f->cmd));
+                       dev_dbg(&lif->netdev->dev, "replay filter command:\n");
+                       dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1,
+                                        &ctx.cmd, sizeof(ctx.cmd), true);
+
+                       err = ionic_adminq_post_wait(lif, &ctx);
+                       if (err) {
+                               switch (le16_to_cpu(ac->match)) {
+                               case IONIC_RX_FILTER_MATCH_VLAN:
+                                       netdev_info(lif->netdev, "Replay failed - %d: vlan %d\n",
+                                                   err,
+                                                   le16_to_cpu(ac->vlan.vlan));
+                                       break;
+                               case IONIC_RX_FILTER_MATCH_MAC:
+                                       netdev_info(lif->netdev, "Replay failed - %d: mac %pM\n",
+                                                   err, ac->mac.addr);
+                                       break;
+                               case IONIC_RX_FILTER_MATCH_MAC_VLAN:
+                                       netdev_info(lif->netdev, "Replay failed - %d: vlan %d mac %pM\n",
+                                                   err,
+                                                   le16_to_cpu(ac->vlan.vlan),
+                                                   ac->mac.addr);
+                                       break;
+                               }
+                       }
+               }
+       }
 }
 
 int ionic_rx_filters_init(struct ionic_lif *lif)
index b6aec9c..cf8f4c0 100644 (file)
@@ -24,7 +24,7 @@ struct ionic_rx_filters {
 };
 
 void ionic_rx_filter_free(struct ionic_lif *lif, struct ionic_rx_filter *f);
-int ionic_rx_filter_del(struct ionic_lif *lif, struct ionic_rx_filter *f);
+void ionic_rx_filter_replay(struct ionic_lif *lif);
 int ionic_rx_filters_init(struct ionic_lif *lif);
 void ionic_rx_filters_deinit(struct ionic_lif *lif);
 int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
index 38a65b9..7119a18 100644 (file)
@@ -1972,7 +1972,7 @@ static int qed_init_qm_sanity(struct qed_hwfn *p_hwfn)
                return 0;
 
        if (QED_IS_ROCE_PERSONALITY(p_hwfn)) {
-               p_hwfn->hw_info.multi_tc_roce_en = 0;
+               p_hwfn->hw_info.multi_tc_roce_en = false;
                DP_NOTICE(p_hwfn,
                          "multi-tc roce was disabled to reduce requested amount of pqs\n");
                if (qed_init_qm_get_num_pqs(p_hwfn) <= RESC_NUM(p_hwfn, QED_PQ))
@@ -4392,7 +4392,7 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn,
        }
 
        if (QED_IS_ROCE_PERSONALITY(p_hwfn))
-               p_hwfn->hw_info.multi_tc_roce_en = 1;
+               p_hwfn->hw_info.multi_tc_roce_en = true;
 
        p_hwfn->hw_info.num_hw_tc = NUM_PHYS_TCS_4PORT_K2;
        p_hwfn->hw_info.num_active_tc = 1;
index 037e597..4afd857 100644 (file)
@@ -2331,7 +2331,7 @@ static void qed_ll2_register_cb_ops(struct qed_dev *cdev,
        cdev->ll2->cb_cookie = cookie;
 }
 
-struct qed_ll2_cbs ll2_cbs = {
+static struct qed_ll2_cbs ll2_cbs = {
        .rx_comp_cb = &qed_ll2b_complete_rx_packet,
        .rx_release_cb = &qed_ll2b_release_rx_packet,
        .tx_comp_cb = &qed_ll2b_complete_tx_packet,
index 96356e8..38a1d26 100644 (file)
@@ -49,6 +49,7 @@
 #include <linux/qed/qed_if.h>
 #include <linux/qed/qed_ll2_if.h>
 #include <net/devlink.h>
+#include <linux/aer.h>
 
 #include "qed.h"
 #include "qed_sriov.h"
@@ -129,6 +130,8 @@ static void qed_free_pci(struct qed_dev *cdev)
 {
        struct pci_dev *pdev = cdev->pdev;
 
+       pci_disable_pcie_error_reporting(pdev);
+
        if (cdev->doorbells && cdev->db_size)
                iounmap(cdev->doorbells);
        if (cdev->regview)
@@ -231,6 +234,12 @@ static int qed_init_pci(struct qed_dev *cdev, struct pci_dev *pdev)
                return -ENOMEM;
        }
 
+       /* AER (Advanced Error reporting) configuration */
+       rc = pci_enable_pcie_error_reporting(pdev);
+       if (rc)
+               DP_VERBOSE(cdev, NETIF_MSG_DRV,
+                          "Failed to configure PCIe AER [%d]\n", rc);
+
        return 0;
 
 err2:
index 37e7056..475b899 100644 (file)
@@ -736,9 +736,9 @@ static int qed_roce_sp_destroy_qp_responder(struct qed_hwfn *p_hwfn,
 
        p_ramrod = &p_ent->ramrod.roce_destroy_qp_resp;
 
-       p_ramrod_res = (struct roce_destroy_qp_resp_output_params *)
-           dma_alloc_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_ramrod_res),
-                              &ramrod_res_phys, GFP_KERNEL);
+       p_ramrod_res = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                                         sizeof(*p_ramrod_res),
+                                         &ramrod_res_phys, GFP_KERNEL);
 
        if (!p_ramrod_res) {
                rc = -ENOMEM;
@@ -872,10 +872,10 @@ int qed_roce_query_qp(struct qed_hwfn *p_hwfn,
        }
 
        /* Send a query responder ramrod to FW to get RQ-PSN and state */
-       p_resp_ramrod_res = (struct roce_query_qp_resp_output_params *)
-           dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
-                              sizeof(*p_resp_ramrod_res),
-                              &resp_ramrod_res_phys, GFP_KERNEL);
+       p_resp_ramrod_res =
+               dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                                  sizeof(*p_resp_ramrod_res),
+                                  &resp_ramrod_res_phys, GFP_KERNEL);
        if (!p_resp_ramrod_res) {
                DP_NOTICE(p_hwfn,
                          "qed query qp failed: cannot allocate memory (ramrod)\n");
@@ -920,8 +920,7 @@ int qed_roce_query_qp(struct qed_hwfn *p_hwfn,
        }
 
        /* Send a query requester ramrod to FW to get SQ-PSN and state */
-       p_req_ramrod_res = (struct roce_query_qp_req_output_params *)
-                          dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+       p_req_ramrod_res = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
                                              sizeof(*p_req_ramrod_res),
                                              &req_ramrod_res_phys,
                                              GFP_KERNEL);
index 234c6f3..1a708f9 100644 (file)
@@ -485,6 +485,7 @@ struct qede_fastpath {
 
 #define QEDE_SP_RECOVERY               0
 #define QEDE_SP_RX_MODE                        1
+#define QEDE_SP_AER                    7
 
 #ifdef CONFIG_RFS_ACCEL
 int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb,
index 34fa391..2565060 100644 (file)
@@ -60,6 +60,7 @@
 #include <net/ip6_checksum.h>
 #include <linux/bitops.h>
 #include <linux/vmalloc.h>
+#include <linux/aer.h>
 #include "qede.h"
 #include "qede_ptp.h"
 
@@ -124,6 +125,8 @@ static const struct pci_device_id qede_pci_tbl[] = {
 MODULE_DEVICE_TABLE(pci, qede_pci_tbl);
 
 static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id);
+static pci_ers_result_t
+qede_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state);
 
 #define TX_TIMEOUT             (5 * HZ)
 
@@ -203,6 +206,10 @@ static int qede_sriov_configure(struct pci_dev *pdev, int num_vfs_param)
 }
 #endif
 
+static const struct pci_error_handlers qede_err_handler = {
+       .error_detected = qede_io_error_detected,
+};
+
 static struct pci_driver qede_pci_driver = {
        .name = "qede",
        .id_table = qede_pci_tbl,
@@ -212,6 +219,7 @@ static struct pci_driver qede_pci_driver = {
 #ifdef CONFIG_QED_SRIOV
        .sriov_configure = qede_sriov_configure,
 #endif
+       .err_handler = &qede_err_handler,
 };
 
 static struct qed_eth_cb_ops qede_ll_ops = {
@@ -974,7 +982,8 @@ static void qede_sp_task(struct work_struct *work)
                /* SRIOV must be disabled outside the lock to avoid a deadlock.
                 * The recovery of the active VFs is currently not supported.
                 */
-               qede_sriov_configure(edev->pdev, 0);
+               if (pci_num_vf(edev->pdev))
+                       qede_sriov_configure(edev->pdev, 0);
 #endif
                qede_lock(edev);
                qede_recovery_handler(edev);
@@ -994,6 +1003,17 @@ static void qede_sp_task(struct work_struct *work)
        }
 #endif
        __qede_unlock(edev);
+
+       if (test_and_clear_bit(QEDE_SP_AER, &edev->sp_flags)) {
+#ifdef CONFIG_QED_SRIOV
+               /* SRIOV must be disabled outside the lock to avoid a deadlock.
+                * The recovery of the active VFs is currently not supported.
+                */
+               if (pci_num_vf(edev->pdev))
+                       qede_sriov_configure(edev->pdev, 0);
+#endif
+               edev->ops->common->recovery_process(edev->cdev);
+       }
 }
 
 static void qede_update_pf_params(struct qed_dev *cdev)
@@ -1694,7 +1714,7 @@ static void qede_init_fp(struct qede_dev *edev)
                                txq->ndev_txq_id = ndev_tx_id;
 
                                if (edev->dev_info.is_legacy)
-                                       txq->is_legacy = 1;
+                                       txq->is_legacy = true;
                                txq->dev = &edev->pdev->dev;
                        }
 
@@ -2579,3 +2599,49 @@ static void qede_get_eth_tlv_data(void *dev, void *data)
        etlv->num_txqs_full_set = true;
        etlv->num_rxqs_full_set = true;
 }
+
+/**
+ * qede_io_error_detected - called when PCI error is detected
+ * @pdev: Pointer to PCI device
+ * @state: The current pci connection state
+ *
+ * This function is called after a PCI bus error affecting
+ * this device has been detected.
+ */
+static pci_ers_result_t
+qede_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct qede_dev *edev = netdev_priv(dev);
+
+       if (!edev)
+               return PCI_ERS_RESULT_NONE;
+
+       DP_NOTICE(edev, "IO error detected [%d]\n", state);
+
+       __qede_lock(edev);
+       if (edev->state == QEDE_STATE_RECOVERY) {
+               DP_NOTICE(edev, "Device already in the recovery state\n");
+               __qede_unlock(edev);
+               return PCI_ERS_RESULT_NONE;
+       }
+
+       /* PF handles the recovery of its VFs */
+       if (IS_VF(edev)) {
+               DP_VERBOSE(edev, QED_MSG_IOV,
+                          "VF recovery is handled by its PF\n");
+               __qede_unlock(edev);
+               return PCI_ERS_RESULT_RECOVERED;
+       }
+
+       /* Close OS Tx */
+       netif_tx_disable(edev->ndev);
+       netif_carrier_off(edev->ndev);
+
+       set_bit(QEDE_SP_AER, &edev->sp_flags);
+       schedule_delayed_work(&edev->sp_task, 0);
+
+       __qede_unlock(edev);
+
+       return PCI_ERS_RESULT_CAN_RECOVER;
+}
index 134611a..d838774 100644 (file)
@@ -1880,12 +1880,6 @@ static inline void qlcnic_write_crb(struct qlcnic_adapter *adapter, char *buf,
        adapter->ahw->hw_ops->write_crb(adapter, buf, offset, size);
 }
 
-static inline int qlcnic_hw_write_wx_2M(struct qlcnic_adapter *adapter,
-                                       ulong off, u32 data)
-{
-       return adapter->ahw->hw_ops->write_reg(adapter, off, data);
-}
-
 static inline int qlcnic_get_mac_address(struct qlcnic_adapter *adapter,
                                         u8 *mac, u8 function)
 {
index f7c2f32..7adbb03 100644 (file)
@@ -1582,10 +1582,10 @@ void qlcnic_sriov_vf_set_multi(struct net_device *netdev)
                if (mode == VPORT_MISS_MODE_ACCEPT_ALL &&
                    !adapter->fdb_mac_learn) {
                        qlcnic_alloc_lb_filters_mem(adapter);
-                       adapter->drv_mac_learn = 1;
+                       adapter->drv_mac_learn = true;
                        adapter->rx_mac_learn = true;
                } else {
-                       adapter->drv_mac_learn = 0;
+                       adapter->drv_mac_learn = false;
                        adapter->rx_mac_learn = false;
                }
        }
index 251d4ac..117188e 100644 (file)
@@ -1431,8 +1431,9 @@ error:
 }
 
 /* Transmit the packet using specified transmit queue */
-int emac_mac_tx_buf_send(struct emac_adapter *adpt, struct emac_tx_queue *tx_q,
-                        struct sk_buff *skb)
+netdev_tx_t emac_mac_tx_buf_send(struct emac_adapter *adpt,
+                                struct emac_tx_queue *tx_q,
+                                struct sk_buff *skb)
 {
        struct emac_tpd tpd;
        u32 prod_idx;
index ae08bdd..920123e 100644 (file)
@@ -227,8 +227,9 @@ void emac_mac_stop(struct emac_adapter *adpt);
 void emac_mac_mode_config(struct emac_adapter *adpt);
 void emac_mac_rx_process(struct emac_adapter *adpt, struct emac_rx_queue *rx_q,
                         int *num_pkts, int max_pkts);
-int emac_mac_tx_buf_send(struct emac_adapter *adpt, struct emac_tx_queue *tx_q,
-                        struct sk_buff *skb);
+netdev_tx_t emac_mac_tx_buf_send(struct emac_adapter *adpt,
+                                struct emac_tx_queue *tx_q,
+                                struct sk_buff *skb);
 void emac_mac_tx_process(struct emac_adapter *adpt, struct emac_tx_queue *tx_q);
 void emac_mac_rx_tx_ring_init_all(struct platform_device *pdev,
                                  struct emac_adapter *adpt);
index 18b0c7a..20b1b43 100644 (file)
@@ -115,7 +115,8 @@ static int emac_napi_rtx(struct napi_struct *napi, int budget)
 }
 
 /* Transmit the packet */
-static int emac_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+static netdev_tx_t emac_start_xmit(struct sk_buff *skb,
+                                  struct net_device *netdev)
 {
        struct emac_adapter *adpt = netdev_priv(netdev);
 
index bf5bf05..f06dbc9 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/ethtool.h>
 #include <linux/phy.h>
 #include <linux/if_vlan.h>
-#include <linux/crc32.h>
 #include <linux/in.h>
 #include <linux/io.h>
 #include <linux/ip.h>
@@ -27,6 +26,7 @@
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
+#include <linux/bitfield.h>
 #include <linux/prefetch.h>
 #include <linux/ipv6.h>
 #include <net/ip6_checksum.h>
@@ -58,9 +58,6 @@
 #define FIRMWARE_8107E_2       "rtl_nic/rtl8107e-2.fw"
 #define FIRMWARE_8125A_3       "rtl_nic/rtl8125a-3.fw"
 
-#define R8169_MSG_DEFAULT \
-       (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN)
-
 /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
    The RTL chips use a 64 element hash table based on the Ethernet CRC. */
 #define        MC_FILTER_LIMIT 32
@@ -75,6 +72,8 @@
 #define R8169_TX_RING_BYTES    (NUM_TX_DESC * sizeof(struct TxDesc))
 #define R8169_RX_RING_BYTES    (NUM_RX_DESC * sizeof(struct RxDesc))
 
+#define OCP_STD_PHY_BASE       0xa400
+
 #define RTL_CFG_NO_GBIT        1
 
 /* write/read MMIO register */
 #define RTL_R16(tp, reg)               readw(tp->mmio_addr + (reg))
 #define RTL_R32(tp, reg)               readl(tp->mmio_addr + (reg))
 
-#define JUMBO_4K       (4*1024 - ETH_HLEN - 2)
-#define JUMBO_6K       (6*1024 - ETH_HLEN - 2)
-#define JUMBO_7K       (7*1024 - ETH_HLEN - 2)
-#define JUMBO_9K       (9*1024 - ETH_HLEN - 2)
+#define JUMBO_4K       (4 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN)
+#define JUMBO_6K       (6 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN)
+#define JUMBO_7K       (7 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN)
+#define JUMBO_9K       (9 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN)
 
 static const struct {
        const char *name;
@@ -176,10 +175,6 @@ static const struct pci_device_id rtl8169_pci_tbl[] = {
 
 MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl);
 
-static struct {
-       u32 msg_enable;
-} debug = { -1 };
-
 enum rtl_registers {
        MAC0            = 0,    /* Ethernet hardware address. */
        MAC4            = 4,
@@ -227,10 +222,13 @@ enum rtl_registers {
        CPlusCmd        = 0xe0,
        IntrMitigate    = 0xe2,
 
-#define RTL_COALESCE_MASK      0x0f
-#define RTL_COALESCE_SHIFT     4
-#define RTL_COALESCE_T_MAX     (RTL_COALESCE_MASK)
-#define RTL_COALESCE_FRAME_MAX (RTL_COALESCE_MASK << 2)
+#define RTL_COALESCE_TX_USECS  GENMASK(15, 12)
+#define RTL_COALESCE_TX_FRAMES GENMASK(11, 8)
+#define RTL_COALESCE_RX_USECS  GENMASK(7, 4)
+#define RTL_COALESCE_RX_FRAMES GENMASK(3, 0)
+
+#define RTL_COALESCE_T_MAX     0x0fU
+#define RTL_COALESCE_FRAME_MAX (RTL_COALESCE_T_MAX * 4)
 
        RxDescAddrLow   = 0xe4,
        RxDescAddrHigh  = 0xe8,
@@ -386,10 +384,12 @@ enum rtl_register_content {
        /* rx_mode_bits */
        AcceptErr       = 0x20,
        AcceptRunt      = 0x10,
+#define RX_CONFIG_ACCEPT_ERR_MASK      0x30
        AcceptBroadcast = 0x08,
        AcceptMulticast = 0x04,
        AcceptMyPhys    = 0x02,
        AcceptAllPhys   = 0x01,
+#define RX_CONFIG_ACCEPT_OK_MASK       0x0f
 #define RX_CONFIG_ACCEPT_MASK          0x3f
 
        /* TxConfigBits */
@@ -596,7 +596,6 @@ struct rtl8169_private {
        struct net_device *dev;
        struct phy_device *phydev;
        struct napi_struct napi;
-       u32 msg_enable;
        enum mac_version mac_version;
        u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
        u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
@@ -638,8 +637,6 @@ typedef void (*rtl_generic_fct)(struct rtl8169_private *tp);
 
 MODULE_AUTHOR("Realtek and the Linux r8169 crew <netdev@vger.kernel.org>");
 MODULE_DESCRIPTION("RealTek RTL-8169 Gigabit Ethernet driver");
-module_param_named(debug, debug.msg_enable, int, 0);
-MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., 16=all)");
 MODULE_SOFTDEP("pre: realtek");
 MODULE_LICENSE("GPL");
 MODULE_FIRMWARE(FIRMWARE_8168D_1);
@@ -727,53 +724,35 @@ struct rtl_cond {
        const char *msg;
 };
 
-static void rtl_udelay(unsigned int d)
-{
-       udelay(d);
-}
-
 static bool rtl_loop_wait(struct rtl8169_private *tp, const struct rtl_cond *c,
-                         void (*delay)(unsigned int), unsigned int d, int n,
-                         bool high)
+                         unsigned long usecs, int n, bool high)
 {
        int i;
 
        for (i = 0; i < n; i++) {
                if (c->check(tp) == high)
                        return true;
-               delay(d);
+               fsleep(usecs);
        }
-       netif_err(tp, drv, tp->dev, "%s == %d (loop: %d, delay: %d).\n",
-                 c->msg, !high, n, d);
-       return false;
-}
-
-static bool rtl_udelay_loop_wait_high(struct rtl8169_private *tp,
-                                     const struct rtl_cond *c,
-                                     unsigned int d, int n)
-{
-       return rtl_loop_wait(tp, c, rtl_udelay, d, n, true);
-}
 
-static bool rtl_udelay_loop_wait_low(struct rtl8169_private *tp,
-                                    const struct rtl_cond *c,
-                                    unsigned int d, int n)
-{
-       return rtl_loop_wait(tp, c, rtl_udelay, d, n, false);
+       if (net_ratelimit())
+               netdev_err(tp->dev, "%s == %d (loop: %d, delay: %lu).\n",
+                          c->msg, !high, n, usecs);
+       return false;
 }
 
-static bool rtl_msleep_loop_wait_high(struct rtl8169_private *tp,
-                                     const struct rtl_cond *c,
-                                     unsigned int d, int n)
+static bool rtl_loop_wait_high(struct rtl8169_private *tp,
+                              const struct rtl_cond *c,
+                              unsigned long d, int n)
 {
-       return rtl_loop_wait(tp, c, msleep, d, n, true);
+       return rtl_loop_wait(tp, c, d, n, true);
 }
 
-static bool rtl_msleep_loop_wait_low(struct rtl8169_private *tp,
-                                    const struct rtl_cond *c,
-                                    unsigned int d, int n)
+static bool rtl_loop_wait_low(struct rtl8169_private *tp,
+                             const struct rtl_cond *c,
+                             unsigned long d, int n)
 {
-       return rtl_loop_wait(tp, c, msleep, d, n, false);
+       return rtl_loop_wait(tp, c, d, n, false);
 }
 
 #define DECLARE_RTL_COND(name)                         \
@@ -789,7 +768,8 @@ static bool name ## _check(struct rtl8169_private *tp)
 static bool rtl_ocp_reg_failure(struct rtl8169_private *tp, u32 reg)
 {
        if (reg & 0xffff0001) {
-               netif_err(tp, drv, tp->dev, "Invalid ocp reg %x!\n", reg);
+               if (net_ratelimit())
+                       netdev_err(tp->dev, "Invalid ocp reg %x!\n", reg);
                return true;
        }
        return false;
@@ -807,7 +787,7 @@ static void r8168_phy_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
 
        RTL_W32(tp, GPHY_OCP, OCPAR_FLAG | (reg << 15) | data);
 
-       rtl_udelay_loop_wait_low(tp, &rtl_ocp_gphy_cond, 25, 10);
+       rtl_loop_wait_low(tp, &rtl_ocp_gphy_cond, 25, 10);
 }
 
 static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg)
@@ -817,7 +797,7 @@ static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg)
 
        RTL_W32(tp, GPHY_OCP, reg << 15);
 
-       return rtl_udelay_loop_wait_high(tp, &rtl_ocp_gphy_cond, 25, 10) ?
+       return rtl_loop_wait_high(tp, &rtl_ocp_gphy_cond, 25, 10) ?
                (RTL_R32(tp, GPHY_OCP) & 0xffff) : -ETIMEDOUT;
 }
 
@@ -847,8 +827,6 @@ static void r8168_mac_ocp_modify(struct rtl8169_private *tp, u32 reg, u16 mask,
        r8168_mac_ocp_write(tp, reg, (data & ~mask) | set);
 }
 
-#define OCP_STD_PHY_BASE       0xa400
-
 static void r8168g_mdio_write(struct rtl8169_private *tp, int reg, int value)
 {
        if (reg == 0x1f) {
@@ -897,7 +875,7 @@ static void r8169_mdio_write(struct rtl8169_private *tp, int reg, int value)
 {
        RTL_W32(tp, PHYAR, 0x80000000 | (reg & 0x1f) << 16 | (value & 0xffff));
 
-       rtl_udelay_loop_wait_low(tp, &rtl_phyar_cond, 25, 20);
+       rtl_loop_wait_low(tp, &rtl_phyar_cond, 25, 20);
        /*
         * According to hardware specs a 20us delay is required after write
         * complete indication, but before sending next command.
@@ -911,7 +889,7 @@ static int r8169_mdio_read(struct rtl8169_private *tp, int reg)
 
        RTL_W32(tp, PHYAR, 0x0 | (reg & 0x1f) << 16);
 
-       value = rtl_udelay_loop_wait_high(tp, &rtl_phyar_cond, 25, 20) ?
+       value = rtl_loop_wait_high(tp, &rtl_phyar_cond, 25, 20) ?
                RTL_R32(tp, PHYAR) & 0xffff : -ETIMEDOUT;
 
        /*
@@ -934,7 +912,7 @@ static void r8168dp_1_mdio_access(struct rtl8169_private *tp, int reg, u32 data)
        RTL_W32(tp, OCPAR, OCPAR_GPHY_WRITE_CMD);
        RTL_W32(tp, EPHY_RXER_NUM, 0);
 
-       rtl_udelay_loop_wait_low(tp, &rtl_ocpar_cond, 1000, 100);
+       rtl_loop_wait_low(tp, &rtl_ocpar_cond, 1000, 100);
 }
 
 static void r8168dp_1_mdio_write(struct rtl8169_private *tp, int reg, int value)
@@ -951,7 +929,7 @@ static int r8168dp_1_mdio_read(struct rtl8169_private *tp, int reg)
        RTL_W32(tp, OCPAR, OCPAR_GPHY_READ_CMD);
        RTL_W32(tp, EPHY_RXER_NUM, 0);
 
-       return rtl_udelay_loop_wait_high(tp, &rtl_ocpar_cond, 1000, 100) ?
+       return rtl_loop_wait_high(tp, &rtl_ocpar_cond, 1000, 100) ?
                RTL_R32(tp, OCPDR) & OCPDR_DATA_MASK : -ETIMEDOUT;
 }
 
@@ -1037,7 +1015,7 @@ static void rtl_ephy_write(struct rtl8169_private *tp, int reg_addr, int value)
        RTL_W32(tp, EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) |
                (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT);
 
-       rtl_udelay_loop_wait_low(tp, &rtl_ephyar_cond, 10, 100);
+       rtl_loop_wait_low(tp, &rtl_ephyar_cond, 10, 100);
 
        udelay(10);
 }
@@ -1046,7 +1024,7 @@ static u16 rtl_ephy_read(struct rtl8169_private *tp, int reg_addr)
 {
        RTL_W32(tp, EPHYAR, (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT);
 
-       return rtl_udelay_loop_wait_high(tp, &rtl_ephyar_cond, 10, 100) ?
+       return rtl_loop_wait_high(tp, &rtl_ephyar_cond, 10, 100) ?
                RTL_R32(tp, EPHYAR) & EPHYAR_DATA_MASK : ~0;
 }
 
@@ -1062,7 +1040,7 @@ static void _rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask,
        RTL_W32(tp, ERIDR, val);
        RTL_W32(tp, ERIAR, ERIAR_WRITE_CMD | type | mask | addr);
 
-       rtl_udelay_loop_wait_low(tp, &rtl_eriar_cond, 100, 100);
+       rtl_loop_wait_low(tp, &rtl_eriar_cond, 100, 100);
 }
 
 static void rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask,
@@ -1075,7 +1053,7 @@ static u32 _rtl_eri_read(struct rtl8169_private *tp, int addr, int type)
 {
        RTL_W32(tp, ERIAR, ERIAR_READ_CMD | type | ERIAR_MASK_1111 | addr);
 
-       return rtl_udelay_loop_wait_high(tp, &rtl_eriar_cond, 100, 100) ?
+       return rtl_loop_wait_high(tp, &rtl_eriar_cond, 100, 100) ?
                RTL_R32(tp, ERIDR) : ~0;
 }
 
@@ -1108,7 +1086,7 @@ static void rtl_eri_clear_bits(struct rtl8169_private *tp, int addr, u32 mask,
 static u32 r8168dp_ocp_read(struct rtl8169_private *tp, u8 mask, u16 reg)
 {
        RTL_W32(tp, OCPAR, ((u32)mask & 0x0f) << 12 | (reg & 0x0fff));
-       return rtl_udelay_loop_wait_high(tp, &rtl_ocpar_cond, 100, 20) ?
+       return rtl_loop_wait_high(tp, &rtl_ocpar_cond, 100, 20) ?
                RTL_R32(tp, OCPDR) : ~0;
 }
 
@@ -1122,7 +1100,7 @@ static void r8168dp_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg,
 {
        RTL_W32(tp, OCPDR, data);
        RTL_W32(tp, OCPAR, OCPAR_FLAG | ((u32)mask & 0x0f) << 12 | (reg & 0x0fff));
-       rtl_udelay_loop_wait_low(tp, &rtl_ocpar_cond, 100, 20);
+       rtl_loop_wait_low(tp, &rtl_ocpar_cond, 100, 20);
 }
 
 static void r8168ep_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg,
@@ -1170,7 +1148,7 @@ DECLARE_RTL_COND(rtl_ocp_tx_cond)
 static void rtl8168ep_stop_cmac(struct rtl8169_private *tp)
 {
        RTL_W8(tp, IBCR2, RTL_R8(tp, IBCR2) & ~0x01);
-       rtl_msleep_loop_wait_high(tp, &rtl_ocp_tx_cond, 50, 2000);
+       rtl_loop_wait_high(tp, &rtl_ocp_tx_cond, 50000, 2000);
        RTL_W8(tp, IBISR0, RTL_R8(tp, IBISR0) | 0x20);
        RTL_W8(tp, IBCR0, RTL_R8(tp, IBCR0) & ~0x01);
 }
@@ -1178,7 +1156,7 @@ static void rtl8168ep_stop_cmac(struct rtl8169_private *tp)
 static void rtl8168dp_driver_start(struct rtl8169_private *tp)
 {
        r8168dp_oob_notify(tp, OOB_CMD_DRIVER_START);
-       rtl_msleep_loop_wait_high(tp, &rtl_dp_ocp_read_cond, 10, 10);
+       rtl_loop_wait_high(tp, &rtl_dp_ocp_read_cond, 10000, 10);
 }
 
 static void rtl8168ep_driver_start(struct rtl8169_private *tp)
@@ -1186,7 +1164,7 @@ static void rtl8168ep_driver_start(struct rtl8169_private *tp)
        r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_START);
        r8168ep_ocp_write(tp, 0x01, 0x30,
                          r8168ep_ocp_read(tp, 0x01, 0x30) | 0x01);
-       rtl_msleep_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10, 10);
+       rtl_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10000, 10);
 }
 
 static void rtl8168_driver_start(struct rtl8169_private *tp)
@@ -1209,7 +1187,7 @@ static void rtl8168_driver_start(struct rtl8169_private *tp)
 static void rtl8168dp_driver_stop(struct rtl8169_private *tp)
 {
        r8168dp_oob_notify(tp, OOB_CMD_DRIVER_STOP);
-       rtl_msleep_loop_wait_low(tp, &rtl_dp_ocp_read_cond, 10, 10);
+       rtl_loop_wait_low(tp, &rtl_dp_ocp_read_cond, 10000, 10);
 }
 
 static void rtl8168ep_driver_stop(struct rtl8169_private *tp)
@@ -1218,7 +1196,7 @@ static void rtl8168ep_driver_stop(struct rtl8169_private *tp)
        r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_STOP);
        r8168ep_ocp_write(tp, 0x01, 0x30,
                          r8168ep_ocp_read(tp, 0x01, 0x30) | 0x01);
-       rtl_msleep_loop_wait_low(tp, &rtl_ep_ocp_read_cond, 10, 10);
+       rtl_loop_wait_low(tp, &rtl_ep_ocp_read_cond, 10000, 10);
 }
 
 static void rtl8168_driver_stop(struct rtl8169_private *tp)
@@ -1279,7 +1257,7 @@ u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr)
 {
        RTL_W32(tp, EFUSEAR, (reg_addr & EFUSEAR_REG_MASK) << EFUSEAR_REG_SHIFT);
 
-       return rtl_udelay_loop_wait_high(tp, &rtl_efusear_cond, 100, 300) ?
+       return rtl_loop_wait_high(tp, &rtl_efusear_cond, 100, 300) ?
                RTL_R32(tp, EFUSEAR) & EFUSEAR_DATA_MASK : ~0;
 }
 
@@ -1423,7 +1401,7 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
                break;
        case RTL_GIGA_MAC_VER_34:
        case RTL_GIGA_MAC_VER_37:
-       case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_52:
+       case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_61:
                options = RTL_R8(tp, Config2) & ~PME_SIGNAL;
                if (wolopts)
                        options |= PME_SIGNAL;
@@ -1497,19 +1475,15 @@ static netdev_features_t rtl8169_fix_features(struct net_device *dev,
        return features;
 }
 
-static int rtl8169_set_features(struct net_device *dev,
-                               netdev_features_t features)
+static void rtl_set_rx_config_features(struct rtl8169_private *tp,
+                                      netdev_features_t features)
 {
-       struct rtl8169_private *tp = netdev_priv(dev);
-       u32 rx_config;
-
-       rtl_lock_work(tp);
+       u32 rx_config = RTL_R32(tp, RxConfig);
 
-       rx_config = RTL_R32(tp, RxConfig);
        if (features & NETIF_F_RXALL)
-               rx_config |= (AcceptErr | AcceptRunt);
+               rx_config |= RX_CONFIG_ACCEPT_ERR_MASK;
        else
-               rx_config &= ~(AcceptErr | AcceptRunt);
+               rx_config &= ~RX_CONFIG_ACCEPT_ERR_MASK;
 
        if (rtl_is_8125(tp)) {
                if (features & NETIF_F_HW_VLAN_CTAG_RX)
@@ -1519,6 +1493,16 @@ static int rtl8169_set_features(struct net_device *dev,
        }
 
        RTL_W32(tp, RxConfig, rx_config);
+}
+
+static int rtl8169_set_features(struct net_device *dev,
+                               netdev_features_t features)
+{
+       struct rtl8169_private *tp = netdev_priv(dev);
+
+       rtl_lock_work(tp);
+
+       rtl_set_rx_config_features(tp, features);
 
        if (features & NETIF_F_RXCSUM)
                tp->cp_cmd |= RxChkSum;
@@ -1568,20 +1552,6 @@ static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs,
        rtl_unlock_work(tp);
 }
 
-static u32 rtl8169_get_msglevel(struct net_device *dev)
-{
-       struct rtl8169_private *tp = netdev_priv(dev);
-
-       return tp->msg_enable;
-}
-
-static void rtl8169_set_msglevel(struct net_device *dev, u32 value)
-{
-       struct rtl8169_private *tp = netdev_priv(dev);
-
-       tp->msg_enable = value;
-}
-
 static const char rtl8169_gstrings[][ETH_GSTRING_LEN] = {
        "tx_packets",
        "rx_packets",
@@ -1613,7 +1583,7 @@ DECLARE_RTL_COND(rtl_counters_cond)
        return RTL_R32(tp, CounterAddrLow) & (CounterReset | CounterDump);
 }
 
-static bool rtl8169_do_counters(struct rtl8169_private *tp, u32 counter_cmd)
+static void rtl8169_do_counters(struct rtl8169_private *tp, u32 counter_cmd)
 {
        dma_addr_t paddr = tp->counters_phys_addr;
        u32 cmd;
@@ -1624,22 +1594,20 @@ static bool rtl8169_do_counters(struct rtl8169_private *tp, u32 counter_cmd)
        RTL_W32(tp, CounterAddrLow, cmd);
        RTL_W32(tp, CounterAddrLow, cmd | counter_cmd);
 
-       return rtl_udelay_loop_wait_low(tp, &rtl_counters_cond, 10, 1000);
+       rtl_loop_wait_low(tp, &rtl_counters_cond, 10, 1000);
 }
 
-static bool rtl8169_reset_counters(struct rtl8169_private *tp)
+static void rtl8169_reset_counters(struct rtl8169_private *tp)
 {
        /*
         * Versions prior to RTL_GIGA_MAC_VER_19 don't support resetting the
         * tally counters.
         */
-       if (tp->mac_version < RTL_GIGA_MAC_VER_19)
-               return true;
-
-       return rtl8169_do_counters(tp, CounterReset);
+       if (tp->mac_version >= RTL_GIGA_MAC_VER_19)
+               rtl8169_do_counters(tp, CounterReset);
 }
 
-static bool rtl8169_update_counters(struct rtl8169_private *tp)
+static void rtl8169_update_counters(struct rtl8169_private *tp)
 {
        u8 val = RTL_R8(tp, ChipCmd);
 
@@ -1647,16 +1615,13 @@ static bool rtl8169_update_counters(struct rtl8169_private *tp)
         * Some chips are unable to dump tally counters when the receiver
         * is disabled. If 0xff chip may be in a PCI power-save state.
         */
-       if (!(val & CmdRxEnb) || val == 0xff)
-               return true;
-
-       return rtl8169_do_counters(tp, CounterDump);
+       if (val & CmdRxEnb && val != 0xff)
+               rtl8169_do_counters(tp, CounterDump);
 }
 
-static bool rtl8169_init_counter_offsets(struct rtl8169_private *tp)
+static void rtl8169_init_counter_offsets(struct rtl8169_private *tp)
 {
        struct rtl8169_counters *counters = tp->counters;
-       bool ret = false;
 
        /*
         * rtl8169_init_counter_offsets is called from rtl_open.  On chip
@@ -1674,22 +1639,16 @@ static bool rtl8169_init_counter_offsets(struct rtl8169_private *tp)
         */
 
        if (tp->tc_offset.inited)
-               return true;
-
-       /* If both, reset and update fail, propagate to caller. */
-       if (rtl8169_reset_counters(tp))
-               ret = true;
+               return;
 
-       if (rtl8169_update_counters(tp))
-               ret = true;
+       rtl8169_reset_counters(tp);
+       rtl8169_update_counters(tp);
 
        tp->tc_offset.tx_errors = counters->tx_errors;
        tp->tc_offset.tx_multi_collision = counters->tx_multi_collision;
        tp->tc_offset.tx_aborted = counters->tx_aborted;
        tp->tc_offset.rx_missed = counters->rx_missed;
        tp->tc_offset.inited = true;
-
-       return ret;
 }
 
 static void rtl8169_get_ethtool_stats(struct net_device *dev,
@@ -1760,46 +1719,34 @@ static void rtl8169_get_strings(struct net_device *dev, u32 stringset, u8 *data)
  * 1 1                     160us           81.92us         1.31ms
  */
 
-/* rx/tx scale factors for one particular CPlusCmd[0:1] value */
-struct rtl_coalesce_scale {
-       /* Rx / Tx */
-       u32 nsecs[2];
-};
-
 /* rx/tx scale factors for all CPlusCmd[0:1] cases */
 struct rtl_coalesce_info {
        u32 speed;
-       struct rtl_coalesce_scale scalev[4];    /* each CPlusCmd[0:1] case */
+       u32 scale_nsecs[4];
 };
 
-/* produce (r,t) pairs with each being in series of *1, *8, *8*2, *8*2*2 */
-#define rxtx_x1822(r, t) {             \
-       {{(r),          (t)}},          \
-       {{(r)*8,        (t)*8}},        \
-       {{(r)*8*2,      (t)*8*2}},      \
-       {{(r)*8*2*2,    (t)*8*2*2}},    \
-}
+/* produce array with base delay *1, *8, *8*2, *8*2*2 */
+#define COALESCE_DELAY(d) { (d), 8 * (d), 16 * (d), 32 * (d) }
+
 static const struct rtl_coalesce_info rtl_coalesce_info_8169[] = {
-       /* speed        delays:     rx00   tx00 */
-       { SPEED_10,     rxtx_x1822(40960, 40960)        },
-       { SPEED_100,    rxtx_x1822( 2560,  2560)        },
-       { SPEED_1000,   rxtx_x1822(  320,   320)        },
+       { SPEED_10,     COALESCE_DELAY(40960) },
+       { SPEED_100,    COALESCE_DELAY(2560) },
+       { SPEED_1000,   COALESCE_DELAY(320) },
        { 0 },
 };
 
 static const struct rtl_coalesce_info rtl_coalesce_info_8168_8136[] = {
-       /* speed        delays:     rx00   tx00 */
-       { SPEED_10,     rxtx_x1822(40960, 40960)        },
-       { SPEED_100,    rxtx_x1822( 2560,  2560)        },
-       { SPEED_1000,   rxtx_x1822( 5000,  5000)        },
+       { SPEED_10,     COALESCE_DELAY(40960) },
+       { SPEED_100,    COALESCE_DELAY(2560) },
+       { SPEED_1000,   COALESCE_DELAY(5000) },
        { 0 },
 };
-#undef rxtx_x1822
+#undef COALESCE_DELAY
 
 /* get rx/tx scale vector corresponding to current speed */
-static const struct rtl_coalesce_info *rtl_coalesce_info(struct net_device *dev)
+static const struct rtl_coalesce_info *
+rtl_coalesce_info(struct rtl8169_private *tp)
 {
-       struct rtl8169_private *tp = netdev_priv(dev);
        const struct rtl_coalesce_info *ci;
 
        if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
@@ -1819,16 +1766,8 @@ static int rtl_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec)
 {
        struct rtl8169_private *tp = netdev_priv(dev);
        const struct rtl_coalesce_info *ci;
-       const struct rtl_coalesce_scale *scale;
-       struct {
-               u32 *max_frames;
-               u32 *usecs;
-       } coal_settings [] = {
-               { &ec->rx_max_coalesced_frames, &ec->rx_coalesce_usecs },
-               { &ec->tx_max_coalesced_frames, &ec->tx_coalesce_usecs }
-       }, *p = coal_settings;
-       int i;
-       u16 w;
+       u32 scale, c_us, c_fr;
+       u16 intrmit;
 
        if (rtl_is_8125(tp))
                return -EOPNOTSUPP;
@@ -1836,111 +1775,102 @@ static int rtl_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec)
        memset(ec, 0, sizeof(*ec));
 
        /* get rx/tx scale corresponding to current speed and CPlusCmd[0:1] */
-       ci = rtl_coalesce_info(dev);
+       ci = rtl_coalesce_info(tp);
        if (IS_ERR(ci))
                return PTR_ERR(ci);
 
-       scale = &ci->scalev[tp->cp_cmd & INTT_MASK];
+       scale = ci->scale_nsecs[tp->cp_cmd & INTT_MASK];
 
-       /* read IntrMitigate and adjust according to scale */
-       for (w = RTL_R16(tp, IntrMitigate); w; w >>= RTL_COALESCE_SHIFT, p++) {
-               *p->max_frames = (w & RTL_COALESCE_MASK) << 2;
-               w >>= RTL_COALESCE_SHIFT;
-               *p->usecs = w & RTL_COALESCE_MASK;
-       }
+       intrmit = RTL_R16(tp, IntrMitigate);
 
-       for (i = 0; i < 2; i++) {
-               p = coal_settings + i;
-               *p->usecs = (*p->usecs * scale->nsecs[i]) / 1000;
+       c_us = FIELD_GET(RTL_COALESCE_TX_USECS, intrmit);
+       ec->tx_coalesce_usecs = DIV_ROUND_UP(c_us * scale, 1000);
 
-               /*
-                * ethtool_coalesce says it is illegal to set both usecs and
-                * max_frames to 0.
-                */
-               if (!*p->usecs && !*p->max_frames)
-                       *p->max_frames = 1;
-       }
+       c_fr = FIELD_GET(RTL_COALESCE_TX_FRAMES, intrmit);
+       /* ethtool_coalesce states usecs and max_frames must not both be 0 */
+       ec->tx_max_coalesced_frames = (c_us || c_fr) ? c_fr * 4 : 1;
+
+       c_us = FIELD_GET(RTL_COALESCE_RX_USECS, intrmit);
+       ec->rx_coalesce_usecs = DIV_ROUND_UP(c_us * scale, 1000);
+
+       c_fr = FIELD_GET(RTL_COALESCE_RX_FRAMES, intrmit);
+       ec->rx_max_coalesced_frames = (c_us || c_fr) ? c_fr * 4 : 1;
 
        return 0;
 }
 
-/* choose appropriate scale factor and CPlusCmd[0:1] for (speed, nsec) */
-static const struct rtl_coalesce_scale *rtl_coalesce_choose_scale(
-                       struct net_device *dev, u32 nsec, u16 *cp01)
+/* choose appropriate scale factor and CPlusCmd[0:1] for (speed, usec) */
+static int rtl_coalesce_choose_scale(struct rtl8169_private *tp, u32 usec,
+                                    u16 *cp01)
 {
        const struct rtl_coalesce_info *ci;
        u16 i;
 
-       ci = rtl_coalesce_info(dev);
+       ci = rtl_coalesce_info(tp);
        if (IS_ERR(ci))
-               return ERR_CAST(ci);
+               return PTR_ERR(ci);
 
        for (i = 0; i < 4; i++) {
-               u32 rxtx_maxscale = max(ci->scalev[i].nsecs[0],
-                                       ci->scalev[i].nsecs[1]);
-               if (nsec <= rxtx_maxscale * RTL_COALESCE_T_MAX) {
+               if (usec <= ci->scale_nsecs[i] * RTL_COALESCE_T_MAX / 1000U) {
                        *cp01 = i;
-                       return &ci->scalev[i];
+                       return ci->scale_nsecs[i];
                }
        }
 
-       return ERR_PTR(-EINVAL);
+       return -ERANGE;
 }
 
 static int rtl_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec)
 {
        struct rtl8169_private *tp = netdev_priv(dev);
-       const struct rtl_coalesce_scale *scale;
-       struct {
-               u32 frames;
-               u32 usecs;
-       } coal_settings [] = {
-               { ec->rx_max_coalesced_frames, ec->rx_coalesce_usecs },
-               { ec->tx_max_coalesced_frames, ec->tx_coalesce_usecs }
-       }, *p = coal_settings;
-       u16 w = 0, cp01;
-       int i;
+       u32 tx_fr = ec->tx_max_coalesced_frames;
+       u32 rx_fr = ec->rx_max_coalesced_frames;
+       u32 coal_usec_max, units;
+       u16 w = 0, cp01 = 0;
+       int scale;
 
        if (rtl_is_8125(tp))
                return -EOPNOTSUPP;
 
-       scale = rtl_coalesce_choose_scale(dev,
-                       max(p[0].usecs, p[1].usecs) * 1000, &cp01);
-       if (IS_ERR(scale))
-               return PTR_ERR(scale);
+       if (rx_fr > RTL_COALESCE_FRAME_MAX || tx_fr > RTL_COALESCE_FRAME_MAX)
+               return -ERANGE;
 
-       for (i = 0; i < 2; i++, p++) {
-               u32 units;
+       coal_usec_max = max(ec->rx_coalesce_usecs, ec->tx_coalesce_usecs);
+       scale = rtl_coalesce_choose_scale(tp, coal_usec_max, &cp01);
+       if (scale < 0)
+               return scale;
 
-               /*
-                * accept max_frames=1 we returned in rtl_get_coalesce.
-                * accept it not only when usecs=0 because of e.g. the following scenario:
-                *
-                * - both rx_usecs=0 & rx_frames=0 in hardware (no delay on RX)
-                * - rtl_get_coalesce returns rx_usecs=0, rx_frames=1
-                * - then user does `ethtool -C eth0 rx-usecs 100`
-                *
-                * since ethtool sends to kernel whole ethtool_coalesce
-                * settings, if we do not handle rx_usecs=!0, rx_frames=1
-                * we'll reject it below in `frames % 4 != 0`.
-                */
-               if (p->frames == 1) {
-                       p->frames = 0;
-               }
+       /* Accept max_frames=1 we returned in rtl_get_coalesce. Accept it
+        * not only when usecs=0 because of e.g. the following scenario:
+        *
+        * - both rx_usecs=0 & rx_frames=0 in hardware (no delay on RX)
+        * - rtl_get_coalesce returns rx_usecs=0, rx_frames=1
+        * - then user does `ethtool -C eth0 rx-usecs 100`
+        *
+        * Since ethtool sends to kernel whole ethtool_coalesce settings,
+        * if we want to ignore rx_frames then it has to be set to 0.
+        */
+       if (rx_fr == 1)
+               rx_fr = 0;
+       if (tx_fr == 1)
+               tx_fr = 0;
+
+       /* HW requires time limit to be set if frame limit is set */
+       if ((tx_fr && !ec->tx_coalesce_usecs) ||
+           (rx_fr && !ec->rx_coalesce_usecs))
+               return -EINVAL;
 
-               units = p->usecs * 1000 / scale->nsecs[i];
-               if (p->frames > RTL_COALESCE_FRAME_MAX || p->frames % 4)
-                       return -EINVAL;
+       w |= FIELD_PREP(RTL_COALESCE_TX_FRAMES, DIV_ROUND_UP(tx_fr, 4));
+       w |= FIELD_PREP(RTL_COALESCE_RX_FRAMES, DIV_ROUND_UP(rx_fr, 4));
 
-               w <<= RTL_COALESCE_SHIFT;
-               w |= units;
-               w <<= RTL_COALESCE_SHIFT;
-               w |= p->frames >> 2;
-       }
+       units = DIV_ROUND_UP(ec->tx_coalesce_usecs * 1000U, scale);
+       w |= FIELD_PREP(RTL_COALESCE_TX_USECS, units);
+       units = DIV_ROUND_UP(ec->rx_coalesce_usecs * 1000U, scale);
+       w |= FIELD_PREP(RTL_COALESCE_RX_USECS, units);
 
        rtl_lock_work(tp);
 
-       RTL_W16(tp, IntrMitigate, swab16(w));
+       RTL_W16(tp, IntrMitigate, w);
 
        tp->cp_cmd = (tp->cp_cmd & ~INTT_MASK) | cp01;
        RTL_W16(tp, CPlusCmd, tp->cp_cmd);
@@ -2013,8 +1943,6 @@ static const struct ethtool_ops rtl8169_ethtool_ops = {
        .get_link               = ethtool_op_get_link,
        .get_coalesce           = rtl_get_coalesce,
        .set_coalesce           = rtl_set_coalesce,
-       .get_msglevel           = rtl8169_get_msglevel,
-       .set_msglevel           = rtl8169_set_msglevel,
        .get_regs               = rtl8169_get_regs,
        .get_wol                = rtl8169_get_wol,
        .set_wol                = rtl8169_set_wol,
@@ -2391,8 +2319,6 @@ static void rtl_pll_power_up(struct rtl8169_private *tp)
        }
 
        phy_resume(tp->phydev);
-       /* give MAC/PHY some time to resume */
-       msleep(20);
 }
 
 static void rtl_init_rxcfg(struct rtl8169_private *tp)
@@ -2411,8 +2337,7 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)
                RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST | RX_EARLY_OFF);
                break;
        case RTL_GIGA_MAC_VER_60 ... RTL_GIGA_MAC_VER_61:
-               RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_VLAN_8125 |
-                                     RX_DMA_BURST);
+               RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST);
                break;
        default:
                RTL_W32(tp, RxConfig, RX128_INT_EN | RX_DMA_BURST);
@@ -2526,7 +2451,7 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
 {
        RTL_W8(tp, ChipCmd, CmdReset);
 
-       rtl_udelay_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
+       rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
 }
 
 static void rtl_request_firmware(struct rtl8169_private *tp)
@@ -2538,10 +2463,8 @@ static void rtl_request_firmware(struct rtl8169_private *tp)
                return;
 
        rtl_fw = kzalloc(sizeof(*rtl_fw), GFP_KERNEL);
-       if (!rtl_fw) {
-               netif_warn(tp, ifup, tp->dev, "Unable to load firmware, out of memory\n");
+       if (!rtl_fw)
                return;
-       }
 
        rtl_fw->phy_write = rtl_writephy;
        rtl_fw->phy_read = rtl_readphy;
@@ -2582,12 +2505,12 @@ static void rtl8169_hw_reset(struct rtl8169_private *tp)
        case RTL_GIGA_MAC_VER_27:
        case RTL_GIGA_MAC_VER_28:
        case RTL_GIGA_MAC_VER_31:
-               rtl_udelay_loop_wait_low(tp, &rtl_npq_cond, 20, 42*42);
+               rtl_loop_wait_low(tp, &rtl_npq_cond, 20, 2000);
                break;
        case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38:
        case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_52:
                RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
-               rtl_udelay_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666);
+               rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666);
                break;
        default:
                RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq);
@@ -2628,7 +2551,7 @@ 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 rtl8169_set_magic_reg(struct rtl8169_private *tp, unsigned mac_version)
+static void rtl8169_set_magic_reg(struct rtl8169_private *tp)
 {
        u32 val;
 
@@ -2654,8 +2577,6 @@ static void rtl_set_rx_mode(struct net_device *dev)
        u32 tmp;
 
        if (dev->flags & IFF_PROMISC) {
-               /* Unconditionally log net taps. */
-               netif_notice(tp, link, dev, "Promiscuous mode enabled\n");
                rx_mode |= AcceptAllPhys;
        } else if (netdev_mc_count(dev) > MC_FILTER_LIMIT ||
                   dev->flags & IFF_ALLMULTI ||
@@ -2668,7 +2589,7 @@ static void rtl_set_rx_mode(struct net_device *dev)
 
                mc_filter[1] = mc_filter[0] = 0;
                netdev_for_each_mc_addr(ha, dev) {
-                       u32 bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26;
+                       u32 bit_nr = eth_hw_addr_crc(ha) >> 26;
                        mc_filter[bit_nr >> 5] |= BIT(bit_nr & 31);
                }
 
@@ -2679,14 +2600,11 @@ static void rtl_set_rx_mode(struct net_device *dev)
                }
        }
 
-       if (dev->features & NETIF_F_RXALL)
-               rx_mode |= (AcceptErr | AcceptRunt);
-
        RTL_W32(tp, MAR0 + 4, mc_filter[1]);
        RTL_W32(tp, MAR0 + 0, mc_filter[0]);
 
        tmp = RTL_R32(tp, RxConfig);
-       RTL_W32(tp, RxConfig, (tmp & ~RX_CONFIG_ACCEPT_MASK) | rx_mode);
+       RTL_W32(tp, RxConfig, (tmp & ~RX_CONFIG_ACCEPT_OK_MASK) | rx_mode);
 }
 
 DECLARE_RTL_COND(rtl_csiar_cond)
@@ -2702,7 +2620,7 @@ static void rtl_csi_write(struct rtl8169_private *tp, int addr, int value)
        RTL_W32(tp, CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) |
                CSIAR_BYTE_ENABLE | func << 16);
 
-       rtl_udelay_loop_wait_low(tp, &rtl_csiar_cond, 10, 100);
+       rtl_loop_wait_low(tp, &rtl_csiar_cond, 10, 100);
 }
 
 static u32 rtl_csi_read(struct rtl8169_private *tp, int addr)
@@ -2712,7 +2630,7 @@ static u32 rtl_csi_read(struct rtl8169_private *tp, int addr)
        RTL_W32(tp, CSIAR, (addr & CSIAR_ADDR_MASK) | func << 16 |
                CSIAR_BYTE_ENABLE);
 
-       return rtl_udelay_loop_wait_high(tp, &rtl_csiar_cond, 10, 100) ?
+       return rtl_loop_wait_high(tp, &rtl_csiar_cond, 10, 100) ?
                RTL_R32(tp, CSIDR) : ~0;
 }
 
@@ -3667,7 +3585,7 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp)
 
        r8168_mac_ocp_write(tp, 0xe098, 0xc302);
 
-       rtl_udelay_loop_wait_low(tp, &rtl_mac_ocp_e00e_cond, 1000, 10);
+       rtl_loop_wait_low(tp, &rtl_mac_ocp_e00e_cond, 1000, 10);
 
        rtl8125_config_eee_mac(tp);
 
@@ -3834,7 +3752,7 @@ static void rtl_hw_start_8169(struct rtl8169_private *tp)
 
        RTL_W16(tp, CPlusCmd, tp->cp_cmd);
 
-       rtl8169_set_magic_reg(tp, tp->mac_version);
+       rtl8169_set_magic_reg(tp);
 
        /* disable interrupt coalescing */
        RTL_W16(tp, IntrMitigate, 0x0000);
@@ -3844,7 +3762,6 @@ static void rtl_hw_start(struct  rtl8169_private *tp)
 {
        rtl_unlock_config_regs(tp);
 
-       tp->cp_cmd &= CPCMD_MASK;
        RTL_W16(tp, CPlusCmd, tp->cp_cmd);
 
        if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
@@ -3866,6 +3783,7 @@ static void rtl_hw_start(struct  rtl8169_private *tp)
        RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb);
        rtl_init_rxcfg(tp);
        rtl_set_tx_config_registers(tp);
+       rtl_set_rx_config_features(tp, tp->dev->features);
        rtl_set_rx_mode(tp->dev);
        rtl_irq_enable(tp);
 }
@@ -3881,12 +3799,6 @@ static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
        return 0;
 }
 
-static inline void rtl8169_make_unusable_by_asic(struct RxDesc *desc)
-{
-       desc->addr = cpu_to_le64(0x0badbadbadbadbadull);
-       desc->opts1 &= ~cpu_to_le32(DescOwn | RsvdMask);
-}
-
 static inline void rtl8169_mark_to_asic(struct RxDesc *desc)
 {
        u32 eor = le32_to_cpu(desc->opts1) & RingEnd;
@@ -3912,8 +3824,7 @@ static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
 
        mapping = dma_map_page(d, data, 0, R8169_RX_BUF_SIZE, DMA_FROM_DEVICE);
        if (unlikely(dma_mapping_error(d, mapping))) {
-               if (net_ratelimit())
-                       netif_err(tp, drv, tp->dev, "Failed to map RX DMA!\n");
+               netdev_err(tp->dev, "Failed to map RX DMA!\n");
                __free_pages(data, get_order(R8169_RX_BUF_SIZE));
                return NULL;
        }
@@ -3934,15 +3845,11 @@ static void rtl8169_rx_clear(struct rtl8169_private *tp)
                               R8169_RX_BUF_SIZE, DMA_FROM_DEVICE);
                __free_pages(tp->Rx_databuff[i], get_order(R8169_RX_BUF_SIZE));
                tp->Rx_databuff[i] = NULL;
-               rtl8169_make_unusable_by_asic(tp->RxDescArray + i);
+               tp->RxDescArray[i].addr = 0;
+               tp->RxDescArray[i].opts1 = 0;
        }
 }
 
-static inline void rtl8169_mark_as_last_descriptor(struct RxDesc *desc)
-{
-       desc->opts1 |= cpu_to_le32(RingEnd);
-}
-
 static int rtl8169_rx_fill(struct rtl8169_private *tp)
 {
        unsigned int i;
@@ -3958,7 +3865,8 @@ static int rtl8169_rx_fill(struct rtl8169_private *tp)
                tp->Rx_databuff[i] = data;
        }
 
-       rtl8169_mark_as_last_descriptor(tp->RxDescArray + NUM_RX_DESC - 1);
+       /* mark as last descriptor in the ring */
+       tp->RxDescArray[NUM_RX_DESC - 1].opts1 |= cpu_to_le32(RingEnd);
 
        return 0;
 }
@@ -4053,7 +3961,7 @@ static int rtl8169_tx_map(struct rtl8169_private *tp, const u32 *opts, u32 len,
        ret = dma_mapping_error(d, mapping);
        if (unlikely(ret)) {
                if (net_ratelimit())
-                       netif_err(tp, drv, tp->dev, "Failed to map TX data!\n");
+                       netdev_err(tp->dev, "Failed to map TX data!\n");
                return ret;
        }
 
@@ -4124,25 +4032,20 @@ static bool rtl8169_tso_csum_v2(struct rtl8169_private *tp,
                                struct sk_buff *skb, u32 *opts)
 {
        u32 transport_offset = (u32)skb_transport_offset(skb);
-       u32 mss = skb_shinfo(skb)->gso_size;
+       struct skb_shared_info *shinfo = skb_shinfo(skb);
+       u32 mss = shinfo->gso_size;
 
        if (mss) {
-               switch (vlan_get_protocol(skb)) {
-               case htons(ETH_P_IP):
+               if (shinfo->gso_type & SKB_GSO_TCPV4) {
                        opts[0] |= TD1_GTSENV4;
-                       break;
-
-               case htons(ETH_P_IPV6):
+               } else if (shinfo->gso_type & SKB_GSO_TCPV6) {
                        if (skb_cow_head(skb, 0))
                                return false;
 
                        tcp_v6_gso_csum_prep(skb);
                        opts[0] |= TD1_GTSENV6;
-                       break;
-
-               default:
+               } else {
                        WARN_ON_ONCE(1);
-                       break;
                }
 
                opts[0] |= transport_offset << GTTCPHO_SHIFT;
@@ -4224,7 +4127,8 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
        txd_first = tp->TxDescArray + entry;
 
        if (unlikely(!rtl_tx_slots_avail(tp, frags))) {
-               netif_err(tp, drv, dev, "BUG! Tx Ring full when queue awake!\n");
+               if (net_ratelimit())
+                       netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
                goto err_stop_0;
        }
 
@@ -4262,8 +4166,8 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
 
        txd_first->opts1 |= cpu_to_le32(DescOwn | FirstFrag);
 
-       /* Force all memory writes to complete before notifying device */
-       wmb();
+       /* rtl_tx needs to see descriptor changes before updated tp->cur_tx */
+       smp_wmb();
 
        tp->cur_tx += frags + 1;
 
@@ -4308,6 +4212,37 @@ err_stop_0:
        return NETDEV_TX_BUSY;
 }
 
+static unsigned int rtl_last_frag_len(struct sk_buff *skb)
+{
+       struct skb_shared_info *info = skb_shinfo(skb);
+       unsigned int nr_frags = info->nr_frags;
+
+       if (!nr_frags)
+               return UINT_MAX;
+
+       return skb_frag_size(info->frags + nr_frags - 1);
+}
+
+/* Workaround for hw issues with TSO on RTL8168evl */
+static netdev_features_t rtl8168evl_fix_tso(struct sk_buff *skb,
+                                           netdev_features_t features)
+{
+       /* IPv4 header has options field */
+       if (vlan_get_protocol(skb) == htons(ETH_P_IP) &&
+           ip_hdrlen(skb) > sizeof(struct iphdr))
+               features &= ~NETIF_F_ALL_TSO;
+
+       /* IPv4 TCP header has options field */
+       else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4 &&
+                tcp_hdrlen(skb) > sizeof(struct tcphdr))
+               features &= ~NETIF_F_ALL_TSO;
+
+       else if (rtl_last_frag_len(skb) <= 6)
+               features &= ~NETIF_F_ALL_TSO;
+
+       return features;
+}
+
 static netdev_features_t rtl8169_features_check(struct sk_buff *skb,
                                                struct net_device *dev,
                                                netdev_features_t features)
@@ -4316,6 +4251,9 @@ static netdev_features_t rtl8169_features_check(struct sk_buff *skb,
        struct rtl8169_private *tp = netdev_priv(dev);
 
        if (skb_is_gso(skb)) {
+               if (tp->mac_version == RTL_GIGA_MAC_VER_34)
+                       features = rtl8168evl_fix_tso(skb, features);
+
                if (transport_offset > GTTCPHO_MAX &&
                    rtl_chip_supports_csum_v2(tp))
                        features &= ~NETIF_F_ALL_TSO;
@@ -4352,9 +4290,9 @@ static void rtl8169_pcierr_interrupt(struct net_device *dev)
 
        pci_status_errs = pci_status_get_and_clear_errors(pdev);
 
-       netif_err(tp, intr, dev, "PCI error (cmd = 0x%04x, status_errs = 0x%04x)\n",
-                 pci_cmd, pci_status_errs);
-
+       if (net_ratelimit())
+               netdev_err(dev, "PCI error (cmd = 0x%04x, status_errs = 0x%04x)\n",
+                          pci_cmd, pci_status_errs);
        /*
         * The recovery sequence below admits a very elaborated explanation:
         * - it seems to work;
@@ -4472,8 +4410,9 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, u32 budget
                dma_rmb();
 
                if (unlikely(status & RxRES)) {
-                       netif_info(tp, rx_err, dev, "Rx ERROR. status = %08x\n",
-                                  status);
+                       if (net_ratelimit())
+                               netdev_warn(dev, "Rx ERROR. status = %08x\n",
+                                           status);
                        dev->stats.rx_errors++;
                        if (status & (RxRWT | RxRUNT))
                                dev->stats.rx_length_errors++;
@@ -4764,8 +4703,7 @@ static int rtl_open(struct net_device *dev)
 
        rtl_hw_start(tp);
 
-       if (!rtl8169_init_counter_offsets(tp))
-               netif_warn(tp, hw, dev, "counter reset/update failed\n");
+       rtl8169_init_counter_offsets(tp);
 
        phy_start(tp->phydev);
        netif_start_queue(dev);
@@ -5164,20 +5102,19 @@ static int r8169_mdio_register(struct rtl8169_private *tp)
        new_bus->read = r8169_mdio_read_reg;
        new_bus->write = r8169_mdio_write_reg;
 
-       ret = mdiobus_register(new_bus);
+       ret = devm_mdiobus_register(new_bus);
        if (ret)
                return ret;
 
        tp->phydev = mdiobus_get_phy(new_bus, 0);
        if (!tp->phydev) {
-               mdiobus_unregister(new_bus);
                return -ENODEV;
        } else if (!tp->phydev->drv) {
                /* Most chip versions fail with the genphy driver.
                 * Therefore ensure that the dedicated PHY driver is loaded.
                 */
-               dev_err(&pdev->dev, "realtek.ko not loaded, maybe it needs to be added to initramfs?\n");
-               mdiobus_unregister(new_bus);
+               dev_err(&pdev->dev, "no dedicated PHY driver found for PHY ID 0x%08x, maybe realtek.ko needs to be added to initramfs?\n",
+                       tp->phydev->phy_id);
                return -EUNATCH;
        }
 
@@ -5189,14 +5126,12 @@ static int r8169_mdio_register(struct rtl8169_private *tp)
 
 static void rtl_hw_init_8168g(struct rtl8169_private *tp)
 {
-       tp->ocp_base = OCP_STD_PHY_BASE;
-
        RTL_W32(tp, MISC, RTL_R32(tp, MISC) | RXDV_GATED_EN);
 
-       if (!rtl_udelay_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 42))
+       if (!rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 42))
                return;
 
-       if (!rtl_udelay_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42))
+       if (!rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42))
                return;
 
        RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) & ~(CmdTxEnb | CmdRxEnb));
@@ -5205,21 +5140,19 @@ static void rtl_hw_init_8168g(struct rtl8169_private *tp)
 
        r8168_mac_ocp_modify(tp, 0xe8de, BIT(14), 0);
 
-       if (!rtl_udelay_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42))
+       if (!rtl_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42))
                return;
 
        r8168_mac_ocp_modify(tp, 0xe8de, 0, BIT(15));
 
-       rtl_udelay_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42);
+       rtl_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42);
 }
 
 static void rtl_hw_init_8125(struct rtl8169_private *tp)
 {
-       tp->ocp_base = OCP_STD_PHY_BASE;
-
        RTL_W32(tp, MISC, RTL_R32(tp, MISC) | RXDV_GATED_EN);
 
-       if (!rtl_udelay_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42))
+       if (!rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42))
                return;
 
        RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) & ~(CmdTxEnb | CmdRxEnb));
@@ -5228,14 +5161,14 @@ static void rtl_hw_init_8125(struct rtl8169_private *tp)
 
        r8168_mac_ocp_modify(tp, 0xe8de, BIT(14), 0);
 
-       if (!rtl_udelay_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42))
+       if (!rtl_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42))
                return;
 
        r8168_mac_ocp_write(tp, 0xc0aa, 0x07d0);
        r8168_mac_ocp_write(tp, 0xc0a6, 0x0150);
        r8168_mac_ocp_write(tp, 0xc01e, 0x5555);
 
-       rtl_udelay_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42);
+       rtl_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42);
 }
 
 static void rtl_hw_initialize(struct rtl8169_private *tp)
@@ -5350,9 +5283,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        tp = netdev_priv(dev);
        tp->dev = dev;
        tp->pci_dev = pdev;
-       tp->msg_enable = netif_msg_init(debug.msg_enable, R8169_MSG_DEFAULT);
        tp->supports_gmii = ent->driver_data == RTL_CFG_NO_GBIT ? 0 : 1;
        tp->eee_adv = -1;
+       tp->ocp_base = OCP_STD_PHY_BASE;
 
        /* Get the *optional* external "ether_clk" used on some boards */
        rc = rtl_get_ether_clk(tp);
@@ -5408,7 +5341,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        tp->mac_version = chipset;
 
-       tp->cp_cmd = RTL_R16(tp, CPlusCmd);
+       tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK;
 
        if (sizeof(dma_addr_t) > 4 && tp->mac_version >= RTL_GIGA_MAC_VER_18 &&
            !dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)))
@@ -5443,14 +5376,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
                           NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
-       dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
-               NETIF_F_HIGHDMA;
+       dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
        dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
 
-       tp->cp_cmd |= RxChkSum;
-       /* RTL8125 uses register RxConfig for VLAN offloading config */
-       if (!rtl_is_8125(tp))
-               tp->cp_cmd |= RxVlan;
        /*
         * Pretend we are using VLANs; This bypasses a nasty bug where
         * Interrupts stop flowing on high load on 8110SCd controllers.
@@ -5482,6 +5410,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        dev->hw_features |= NETIF_F_RXALL;
        dev->hw_features |= NETIF_F_RXFCS;
 
+       /* configure chip for default features */
+       rtl8169_set_features(dev, dev->features);
+
        jumbo_max = rtl_jumbo_max(tp);
        if (jumbo_max)
                dev->max_mtu = jumbo_max;
@@ -5507,17 +5438,16 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        rc = register_netdev(dev);
        if (rc)
-               goto err_mdio_unregister;
+               return rc;
 
-       netif_info(tp, probe, dev, "%s, %pM, XID %03x, IRQ %d\n",
-                  rtl_chip_infos[chipset].name, dev->dev_addr, xid,
-                  pci_irq_vector(pdev, 0));
+       netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
+                   rtl_chip_infos[chipset].name, dev->dev_addr, xid,
+                   pci_irq_vector(pdev, 0));
 
        if (jumbo_max)
-               netif_info(tp, probe, dev,
-                          "jumbo features [frames: %d bytes, tx checksumming: %s]\n",
-                          jumbo_max, tp->mac_version <= RTL_GIGA_MAC_VER_06 ?
-                          "ok" : "ko");
+               netdev_info(dev, "jumbo features [frames: %d bytes, tx checksumming: %s]\n",
+                           jumbo_max, tp->mac_version <= RTL_GIGA_MAC_VER_06 ?
+                           "ok" : "ko");
 
        if (r8168_check_dash(tp))
                rtl8168_driver_start(tp);
@@ -5526,10 +5456,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                pm_runtime_put_sync(&pdev->dev);
 
        return 0;
-
-err_mdio_unregister:
-       mdiobus_unregister(tp->phydev->mdio.bus);
-       return rc;
 }
 
 static struct pci_driver rtl8169_pci_driver = {
index 8ed73f4..f45331e 100644 (file)
@@ -2472,7 +2472,8 @@ static void sh_eth_tx_timeout(struct net_device *ndev, unsigned int txqueue)
 }
 
 /* Packet transmit function */
-static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t sh_eth_start_xmit(struct sk_buff *skb,
+                                    struct net_device *ndev)
 {
        struct sh_eth_private *mdp = netdev_priv(ndev);
        struct sh_eth_txdesc *txdesc;
index 5b145c6..2ba15c2 100644 (file)
@@ -1,19 +1,3 @@
-
-/*
- * snull.h -- definitions for the network module
- *
- * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
- * Copyright (C) 2001 O'Reilly & Associates
- *
- * The source code in this file can be freely used, adapted,
- * and redistributed in source or binary form, so long as an
- * acknowledgment appears in derived source files.  The citation
- * should list that the code comes from the book "Linux Device
- * Drivers" by Alessandro Rubini and Jonathan Corbet, published
- * by O'Reilly & Associates.   No warranty is attached;
- * we cannot take responsibility for errors or fitness for use.
- */
-
 /* version dependencies have been confined to a separate file */
 
 /* Tunable parameters */
index 9e1c375..4d2d91e 100644 (file)
@@ -28,7 +28,7 @@ config SMC9194
          option if you have a DELL laptop with the docking station, or
          another SMC9192/9194 based chipset.  Say Y if you want it compiled
          into the kernel, and read the file
-         <file:Documentation/networking/device_drivers/smsc/smc9.txt>.
+         <file:Documentation/networking/device_drivers/smsc/smc9.rst>.
 
          To compile this driver as a module, choose M here. The module
          will be called smc9194.
@@ -44,7 +44,7 @@ config SMC91X
          This is a driver for SMC's 91x series of Ethernet chipsets,
          including the SMC91C94 and the SMC91C111. Say Y if you want it
          compiled into the kernel, and read the file
-         <file:Documentation/networking/device_drivers/smsc/smc9.txt>.
+         <file:Documentation/networking/device_drivers/smsc/smc9.rst>.
 
          This driver is also available as a module ( = code which can be
          inserted in and removed from the running kernel whenever you want).
index 67ddf78..f263844 100644 (file)
@@ -1394,7 +1394,7 @@ static int ave_stop(struct net_device *ndev)
        return 0;
 }
 
-static int ave_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t ave_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
        struct ave_private *priv = netdev_priv(ndev);
        u32 proc_idx, done_idx, ndesc, cmdsts;
index 5a6f265..f9d024d 100644 (file)
@@ -30,6 +30,6 @@ obj-$(CONFIG_DWMAC_GENERIC)   += dwmac-generic.o
 stmmac-platform-objs:= stmmac_platform.o
 dwmac-altr-socfpga-objs := altr_tse_pcs.o dwmac-socfpga.o
 
-obj-$(CONFIG_DWMAC_INTEL) += dwmac-intel.o
-obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o
+obj-$(CONFIG_STMMAC_PCI)       += stmmac-pci.o
+obj-$(CONFIG_DWMAC_INTEL)      += dwmac-intel.o
 stmmac-pci-objs:= stmmac_pci.o
index 6208a68..127f758 100644 (file)
@@ -473,6 +473,7 @@ struct mac_device_info {
        unsigned int xlgmac;
        unsigned int num_vlan;
        u32 vlan_filter[32];
+       unsigned int promisc;
 };
 
 struct stmmac_rx_routing {
index 5419d4e..2ac9dfb 100644 (file)
@@ -5,8 +5,13 @@
 #include <linux/clk-provider.h>
 #include <linux/pci.h>
 #include <linux/dmi.h>
+#include "dwmac-intel.h"
 #include "stmmac.h"
 
+struct intel_priv_data {
+       int mdio_adhoc_addr;    /* mdio address for serdes & etc */
+};
+
 /* This struct is used to associate PCI Function of MAC controller on a board,
  * discovered via DMI, with the address of PHY connected to the MAC. The
  * negative value of the address means that MAC controller is not connected
@@ -49,6 +54,152 @@ static int stmmac_pci_find_phy_addr(struct pci_dev *pdev,
        return -ENODEV;
 }
 
+static int serdes_status_poll(struct stmmac_priv *priv, int phyaddr,
+                             int phyreg, u32 mask, u32 val)
+{
+       unsigned int retries = 10;
+       int val_rd;
+
+       do {
+               val_rd = mdiobus_read(priv->mii, phyaddr, phyreg);
+               if ((val_rd & mask) == (val & mask))
+                       return 0;
+               udelay(POLL_DELAY_US);
+       } while (--retries);
+
+       return -ETIMEDOUT;
+}
+
+static int intel_serdes_powerup(struct net_device *ndev, void *priv_data)
+{
+       struct intel_priv_data *intel_priv = priv_data;
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       int serdes_phy_addr = 0;
+       u32 data = 0;
+
+       if (!intel_priv->mdio_adhoc_addr)
+               return 0;
+
+       serdes_phy_addr = intel_priv->mdio_adhoc_addr;
+
+       /* assert clk_req */
+       data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0);
+       data |= SERDES_PLL_CLK;
+       mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
+
+       /* check for clk_ack assertion */
+       data = serdes_status_poll(priv, serdes_phy_addr,
+                                 SERDES_GSR0,
+                                 SERDES_PLL_CLK,
+                                 SERDES_PLL_CLK);
+
+       if (data) {
+               dev_err(priv->device, "Serdes PLL clk request timeout\n");
+               return data;
+       }
+
+       /* assert lane reset */
+       data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0);
+       data |= SERDES_RST;
+       mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
+
+       /* check for assert lane reset reflection */
+       data = serdes_status_poll(priv, serdes_phy_addr,
+                                 SERDES_GSR0,
+                                 SERDES_RST,
+                                 SERDES_RST);
+
+       if (data) {
+               dev_err(priv->device, "Serdes assert lane reset timeout\n");
+               return data;
+       }
+
+       /*  move power state to P0 */
+       data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0);
+
+       data &= ~SERDES_PWR_ST_MASK;
+       data |= SERDES_PWR_ST_P0 << SERDES_PWR_ST_SHIFT;
+
+       mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
+
+       /* Check for P0 state */
+       data = serdes_status_poll(priv, serdes_phy_addr,
+                                 SERDES_GSR0,
+                                 SERDES_PWR_ST_MASK,
+                                 SERDES_PWR_ST_P0 << SERDES_PWR_ST_SHIFT);
+
+       if (data) {
+               dev_err(priv->device, "Serdes power state P0 timeout.\n");
+               return data;
+       }
+
+       return 0;
+}
+
+static void intel_serdes_powerdown(struct net_device *ndev, void *intel_data)
+{
+       struct intel_priv_data *intel_priv = intel_data;
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       int serdes_phy_addr = 0;
+       u32 data = 0;
+
+       if (!intel_priv->mdio_adhoc_addr)
+               return;
+
+       serdes_phy_addr = intel_priv->mdio_adhoc_addr;
+
+       /*  move power state to P3 */
+       data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0);
+
+       data &= ~SERDES_PWR_ST_MASK;
+       data |= SERDES_PWR_ST_P3 << SERDES_PWR_ST_SHIFT;
+
+       mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
+
+       /* Check for P3 state */
+       data = serdes_status_poll(priv, serdes_phy_addr,
+                                 SERDES_GSR0,
+                                 SERDES_PWR_ST_MASK,
+                                 SERDES_PWR_ST_P3 << SERDES_PWR_ST_SHIFT);
+
+       if (data) {
+               dev_err(priv->device, "Serdes power state P3 timeout\n");
+               return;
+       }
+
+       /* de-assert clk_req */
+       data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0);
+       data &= ~SERDES_PLL_CLK;
+       mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
+
+       /* check for clk_ack de-assert */
+       data = serdes_status_poll(priv, serdes_phy_addr,
+                                 SERDES_GSR0,
+                                 SERDES_PLL_CLK,
+                                 (u32)~SERDES_PLL_CLK);
+
+       if (data) {
+               dev_err(priv->device, "Serdes PLL clk de-assert timeout\n");
+               return;
+       }
+
+       /* de-assert lane reset */
+       data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0);
+       data &= ~SERDES_RST;
+       mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
+
+       /* check for de-assert lane reset reflection */
+       data = serdes_status_poll(priv, serdes_phy_addr,
+                                 SERDES_GSR0,
+                                 SERDES_RST,
+                                 (u32)~SERDES_RST);
+
+       if (data) {
+               dev_err(priv->device, "Serdes de-assert lane reset timeout\n");
+               return;
+       }
+}
+
 static void common_default_data(struct plat_stmmacenet_data *plat)
 {
        plat->clk_csr = 2;      /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
@@ -81,6 +232,7 @@ static void common_default_data(struct plat_stmmacenet_data *plat)
 static int intel_mgbe_common_data(struct pci_dev *pdev,
                                  struct plat_stmmacenet_data *plat)
 {
+       int ret;
        int i;
 
        plat->clk_csr = 5;
@@ -153,7 +305,12 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,
                dev_warn(&pdev->dev, "Fail to register stmmac-clk\n");
                plat->stmmac_clk = NULL;
        }
-       clk_prepare_enable(plat->stmmac_clk);
+
+       ret = clk_prepare_enable(plat->stmmac_clk);
+       if (ret) {
+               clk_unregister_fixed_rate(plat->stmmac_clk);
+               return ret;
+       }
 
        /* Set default value for multicast hash bins */
        plat->multicast_filter_bins = HASH_TABLE_SIZE;
@@ -170,16 +327,11 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,
 static int ehl_common_data(struct pci_dev *pdev,
                           struct plat_stmmacenet_data *plat)
 {
-       int ret;
-
        plat->rx_queues_to_use = 8;
        plat->tx_queues_to_use = 8;
        plat->clk_ptp_rate = 200000000;
-       ret = intel_mgbe_common_data(pdev, plat);
-       if (ret)
-               return ret;
 
-       return 0;
+       return intel_mgbe_common_data(pdev, plat);
 }
 
 static int ehl_sgmii_data(struct pci_dev *pdev,
@@ -189,10 +341,13 @@ static int ehl_sgmii_data(struct pci_dev *pdev,
        plat->phy_addr = 0;
        plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
 
+       plat->serdes_powerup = intel_serdes_powerup;
+       plat->serdes_powerdown = intel_serdes_powerdown;
+
        return ehl_common_data(pdev, plat);
 }
 
-static struct stmmac_pci_info ehl_sgmii1g_pci_info = {
+static struct stmmac_pci_info ehl_sgmii1g_info = {
        .setup = ehl_sgmii_data,
 };
 
@@ -206,7 +361,7 @@ static int ehl_rgmii_data(struct pci_dev *pdev,
        return ehl_common_data(pdev, plat);
 }
 
-static struct stmmac_pci_info ehl_rgmii1g_pci_info = {
+static struct stmmac_pci_info ehl_rgmii1g_info = {
        .setup = ehl_rgmii_data,
 };
 
@@ -225,7 +380,7 @@ static int ehl_pse0_rgmii1g_data(struct pci_dev *pdev,
        return ehl_pse0_common_data(pdev, plat);
 }
 
-static struct stmmac_pci_info ehl_pse0_rgmii1g_pci_info = {
+static struct stmmac_pci_info ehl_pse0_rgmii1g_info = {
        .setup = ehl_pse0_rgmii1g_data,
 };
 
@@ -233,10 +388,12 @@ static int ehl_pse0_sgmii1g_data(struct pci_dev *pdev,
                                 struct plat_stmmacenet_data *plat)
 {
        plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
+       plat->serdes_powerup = intel_serdes_powerup;
+       plat->serdes_powerdown = intel_serdes_powerdown;
        return ehl_pse0_common_data(pdev, plat);
 }
 
-static struct stmmac_pci_info ehl_pse0_sgmii1g_pci_info = {
+static struct stmmac_pci_info ehl_pse0_sgmii1g_info = {
        .setup = ehl_pse0_sgmii1g_data,
 };
 
@@ -255,7 +412,7 @@ static int ehl_pse1_rgmii1g_data(struct pci_dev *pdev,
        return ehl_pse1_common_data(pdev, plat);
 }
 
-static struct stmmac_pci_info ehl_pse1_rgmii1g_pci_info = {
+static struct stmmac_pci_info ehl_pse1_rgmii1g_info = {
        .setup = ehl_pse1_rgmii1g_data,
 };
 
@@ -263,26 +420,23 @@ static int ehl_pse1_sgmii1g_data(struct pci_dev *pdev,
                                 struct plat_stmmacenet_data *plat)
 {
        plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
+       plat->serdes_powerup = intel_serdes_powerup;
+       plat->serdes_powerdown = intel_serdes_powerdown;
        return ehl_pse1_common_data(pdev, plat);
 }
 
-static struct stmmac_pci_info ehl_pse1_sgmii1g_pci_info = {
+static struct stmmac_pci_info ehl_pse1_sgmii1g_info = {
        .setup = ehl_pse1_sgmii1g_data,
 };
 
 static int tgl_common_data(struct pci_dev *pdev,
                           struct plat_stmmacenet_data *plat)
 {
-       int ret;
-
        plat->rx_queues_to_use = 6;
        plat->tx_queues_to_use = 4;
        plat->clk_ptp_rate = 200000000;
-       ret = intel_mgbe_common_data(pdev, plat);
-       if (ret)
-               return ret;
 
-       return 0;
+       return intel_mgbe_common_data(pdev, plat);
 }
 
 static int tgl_sgmii_data(struct pci_dev *pdev,
@@ -291,10 +445,12 @@ static int tgl_sgmii_data(struct pci_dev *pdev,
        plat->bus_id = 1;
        plat->phy_addr = 0;
        plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
+       plat->serdes_powerup = intel_serdes_powerup;
+       plat->serdes_powerdown = intel_serdes_powerdown;
        return tgl_common_data(pdev, plat);
 }
 
-static struct stmmac_pci_info tgl_sgmii1g_pci_info = {
+static struct stmmac_pci_info tgl_sgmii1g_info = {
        .setup = tgl_sgmii_data,
 };
 
@@ -397,7 +553,7 @@ static int quark_default_data(struct pci_dev *pdev,
        return 0;
 }
 
-static const struct stmmac_pci_info quark_pci_info = {
+static const struct stmmac_pci_info quark_info = {
        .setup = quark_default_data,
 };
 
@@ -417,11 +573,15 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
                               const struct pci_device_id *id)
 {
        struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data;
+       struct intel_priv_data *intel_priv;
        struct plat_stmmacenet_data *plat;
        struct stmmac_resources res;
-       int i;
        int ret;
 
+       intel_priv = devm_kzalloc(&pdev->dev, sizeof(*intel_priv), GFP_KERNEL);
+       if (!intel_priv)
+               return -ENOMEM;
+
        plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
        if (!plat)
                return -ENOMEM;
@@ -445,30 +605,36 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
                return ret;
        }
 
-       /* Get the base address of device */
-       for (i = 0; i < PCI_STD_NUM_BARS; i++) {
-               if (pci_resource_len(pdev, i) == 0)
-                       continue;
-               ret = pcim_iomap_regions(pdev, BIT(i), pci_name(pdev));
-               if (ret)
-                       return ret;
-               break;
-       }
+       ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev));
+       if (ret)
+               return ret;
 
        pci_set_master(pdev);
 
+       plat->bsp_priv = intel_priv;
+       intel_priv->mdio_adhoc_addr = 0x15;
+
        ret = info->setup(pdev, plat);
        if (ret)
                return ret;
 
-       pci_enable_msi(pdev);
+       ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+       if (ret < 0)
+               return ret;
 
        memset(&res, 0, sizeof(res));
-       res.addr = pcim_iomap_table(pdev)[i];
-       res.wol_irq = pdev->irq;
-       res.irq = pdev->irq;
+       res.addr = pcim_iomap_table(pdev)[0];
+       res.wol_irq = pci_irq_vector(pdev, 0);
+       res.irq = pci_irq_vector(pdev, 0);
+
+       ret = stmmac_dvr_probe(&pdev->dev, plat, &res);
+       if (ret) {
+               pci_free_irq_vectors(pdev);
+               clk_disable_unprepare(plat->stmmac_clk);
+               clk_unregister_fixed_rate(plat->stmmac_clk);
+       }
 
-       return stmmac_dvr_probe(&pdev->dev, plat, &res);
+       return ret;
 }
 
 /**
@@ -482,19 +648,15 @@ static void intel_eth_pci_remove(struct pci_dev *pdev)
 {
        struct net_device *ndev = dev_get_drvdata(&pdev->dev);
        struct stmmac_priv *priv = netdev_priv(ndev);
-       int i;
 
        stmmac_dvr_remove(&pdev->dev);
 
-       if (priv->plat->stmmac_clk)
-               clk_unregister_fixed_rate(priv->plat->stmmac_clk);
+       pci_free_irq_vectors(pdev);
 
-       for (i = 0; i < PCI_STD_NUM_BARS; i++) {
-               if (pci_resource_len(pdev, i) == 0)
-                       continue;
-               pcim_iounmap_regions(pdev, BIT(i));
-               break;
-       }
+       clk_disable_unprepare(priv->plat->stmmac_clk);
+       clk_unregister_fixed_rate(priv->plat->stmmac_clk);
+
+       pcim_iounmap_regions(pdev, BIT(0));
 
        pci_disable_device(pdev);
 }
@@ -553,26 +715,19 @@ static SIMPLE_DEV_PM_OPS(intel_eth_pm_ops, intel_eth_pci_suspend,
 #define PCI_DEVICE_ID_INTEL_TGL_SGMII1G_ID             0xa0ac
 
 static const struct pci_device_id intel_eth_pci_id_table[] = {
-       { PCI_DEVICE_DATA(INTEL, QUARK_ID, &quark_pci_info) },
-       { PCI_DEVICE_DATA(INTEL, EHL_RGMII1G_ID, &ehl_rgmii1g_pci_info) },
-       { PCI_DEVICE_DATA(INTEL, EHL_SGMII1G_ID, &ehl_sgmii1g_pci_info) },
-       { PCI_DEVICE_DATA(INTEL, EHL_SGMII2G5_ID, &ehl_sgmii1g_pci_info) },
-       { PCI_DEVICE_DATA(INTEL, EHL_PSE0_RGMII1G_ID,
-                         &ehl_pse0_rgmii1g_pci_info) },
-       { PCI_DEVICE_DATA(INTEL, EHL_PSE0_SGMII1G_ID,
-                         &ehl_pse0_sgmii1g_pci_info) },
-       { PCI_DEVICE_DATA(INTEL, EHL_PSE0_SGMII2G5_ID,
-                         &ehl_pse0_sgmii1g_pci_info) },
-       { PCI_DEVICE_DATA(INTEL, EHL_PSE1_RGMII1G_ID,
-                         &ehl_pse1_rgmii1g_pci_info) },
-       { PCI_DEVICE_DATA(INTEL, EHL_PSE1_SGMII1G_ID,
-                         &ehl_pse1_sgmii1g_pci_info) },
-       { PCI_DEVICE_DATA(INTEL, EHL_PSE1_SGMII2G5_ID,
-                         &ehl_pse1_sgmii1g_pci_info) },
-       { PCI_DEVICE_DATA(INTEL, TGL_SGMII1G_ID, &tgl_sgmii1g_pci_info) },
+       { PCI_DEVICE_DATA(INTEL, QUARK_ID, &quark_info) },
+       { PCI_DEVICE_DATA(INTEL, EHL_RGMII1G_ID, &ehl_rgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, EHL_SGMII1G_ID, &ehl_sgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, EHL_SGMII2G5_ID, &ehl_sgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, EHL_PSE0_RGMII1G_ID, &ehl_pse0_rgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, EHL_PSE0_SGMII1G_ID, &ehl_pse0_sgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, EHL_PSE0_SGMII2G5_ID, &ehl_pse0_sgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, EHL_PSE1_RGMII1G_ID, &ehl_pse1_rgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, EHL_PSE1_SGMII1G_ID, &ehl_pse1_sgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, EHL_PSE1_SGMII2G5_ID, &ehl_pse1_sgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, TGL_SGMII1G_ID, &tgl_sgmii1g_info) },
        {}
 };
-
 MODULE_DEVICE_TABLE(pci, intel_eth_pci_id_table);
 
 static struct pci_driver intel_eth_pci_driver = {
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.h
new file mode 100644 (file)
index 0000000..e723096
--- /dev/null
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2020, Intel Corporation
+ * DWMAC Intel header file
+ */
+
+#ifndef __DWMAC_INTEL_H__
+#define __DWMAC_INTEL_H__
+
+#define POLL_DELAY_US 8
+
+/* SERDES Register */
+#define SERDES_GSR0    0x5     /* Global Status Reg0 */
+#define SERDES_GCR0    0xb     /* Global Configuration Reg0 */
+
+/* SERDES defines */
+#define SERDES_PLL_CLK         BIT(0)          /* PLL clk valid signal */
+#define SERDES_RST             BIT(2)          /* Serdes Reset */
+#define SERDES_PWR_ST_MASK     GENMASK(6, 4)   /* Serdes Power state*/
+#define SERDES_PWR_ST_SHIFT    4
+#define SERDES_PWR_ST_P0       0x0
+#define SERDES_PWR_ST_P3       0x3
+
+#endif /* __DWMAC_INTEL_H__ */
index 0e2fa14..a3934ca 100644 (file)
@@ -119,6 +119,7 @@ static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
                { .div = 5, .val = 5, },
                { .div = 6, .val = 6, },
                { .div = 7, .val = 7, },
+               { /* end of array */ }
        };
 
        clk_configs = devm_kzalloc(dev, sizeof(*clk_configs), GFP_KERNEL);
index e0212d2..70d4178 100644 (file)
@@ -241,6 +241,8 @@ static int socfpga_set_phy_mode_common(int phymode, u32 *val)
        switch (phymode) {
        case PHY_INTERFACE_MODE_RGMII:
        case PHY_INTERFACE_MODE_RGMII_ID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
                *val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
                break;
        case PHY_INTERFACE_MODE_MII:
@@ -289,16 +291,19 @@ static int socfpga_gen5_set_phy_mode(struct socfpga_dwmac *dwmac)
            phymode == PHY_INTERFACE_MODE_MII ||
            phymode == PHY_INTERFACE_MODE_GMII ||
            phymode == PHY_INTERFACE_MODE_SGMII) {
-               ctrl |= SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2);
                regmap_read(sys_mgr_base_addr, SYSMGR_FPGAGRP_MODULE_REG,
                            &module);
                module |= (SYSMGR_FPGAGRP_MODULE_EMAC << (reg_shift / 2));
                regmap_write(sys_mgr_base_addr, SYSMGR_FPGAGRP_MODULE_REG,
                             module);
-       } else {
-               ctrl &= ~(SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2));
        }
 
+       if (dwmac->f2h_ptp_ref_clk)
+               ctrl |= SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2);
+       else
+               ctrl &= ~(SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK <<
+                         (reg_shift / 2));
+
        regmap_write(sys_mgr_base_addr, reg_offset, ctrl);
 
        /* Deassert reset for the phy configuration to be sampled by
index b2dc992..5d4df4c 100644 (file)
 #define SYSCFG_PMCR_ETH_CLK_SEL                BIT(16)
 #define SYSCFG_PMCR_ETH_REF_CLK_SEL    BIT(17)
 
+/* CLOCK feed to PHY*/
+#define ETH_CK_F_25M   25000000
+#define ETH_CK_F_50M   50000000
+#define ETH_CK_F_125M  125000000
+
 /*  Ethernet PHY interface selection in register SYSCFG Configuration
  *------------------------------------------
  * src  |BIT(23)| BIT(22)| BIT(21)|BIT(20)|
  *|         |        |      25MHz    |        50MHz       |                  |
  * ---------------------------------------------------------------------------
  *|  MII    |   -   |     eth-ck    |        n/a         |       n/a        |
- *|         |        |              |                    |                  |
+ *|         |        | st,ext-phyclk |                    |                 |
  * ---------------------------------------------------------------------------
  *|  GMII   |   -   |     eth-ck    |        n/a         |       n/a        |
- *|         |        |               |                    |                 |
+ *|         |        | st,ext-phyclk |                    |                 |
  * ---------------------------------------------------------------------------
- *| RGMII   |   -   |     eth-ck    |        n/a         |  eth-ck (no pin) |
- *|         |        |               |                    |  st,eth-clk-sel  |
+ *| RGMII   |   -   |     eth-ck    |        n/a         |      eth-ck      |
+ *|         |        | st,ext-phyclk |                    | st,eth-clk-sel or|
+ *|         |        |               |                    | st,ext-phyclk    |
  * ---------------------------------------------------------------------------
  *| RMII    |   -   |     eth-ck    |      eth-ck        |       n/a        |
- *|         |        |              | st,eth-ref-clk-sel |                  |
+ *|         |        | st,ext-phyclk | st,eth-ref-clk-sel |                 |
+ *|         |        |               | or st,ext-phyclk   |                 |
  * ---------------------------------------------------------------------------
  *
- * BIT(17) : set this bit in RMII mode when you have PHY without crystal 50MHz
- * BIT(16) : set this bit in GMII/RGMII PHY when you do not want use 125Mhz
- * from PHY
- *-----------------------------------------------------
- * src  |         BIT(17)       |       BIT(16)      |
- *-----------------------------------------------------
- * MII   |           n/a        |         n/a        |
- *-----------------------------------------------------
- * GMII  |           n/a         |   st,eth-clk-sel   |
- *-----------------------------------------------------
- * RGMII |           n/a         |   st,eth-clk-sel   |
- *-----------------------------------------------------
- * RMII  |   st,eth-ref-clk-sel         |         n/a        |
- *-----------------------------------------------------
- *
  */
 
 struct stm32_dwmac {
@@ -93,6 +85,8 @@ struct stm32_dwmac {
        struct clk *clk_eth_ck;
        struct clk *clk_ethstp;
        struct clk *syscfg_clk;
+       int ext_phyclk;
+       int enable_eth_ck;
        int eth_clk_sel_reg;
        int eth_ref_clk_sel_reg;
        int irq_pwr_wakeup;
@@ -155,14 +149,17 @@ static int stm32mp1_clk_prepare(struct stm32_dwmac *dwmac, bool prepare)
                ret = clk_prepare_enable(dwmac->syscfg_clk);
                if (ret)
                        return ret;
-               ret = clk_prepare_enable(dwmac->clk_eth_ck);
-               if (ret) {
-                       clk_disable_unprepare(dwmac->syscfg_clk);
-                       return ret;
+               if (dwmac->enable_eth_ck) {
+                       ret = clk_prepare_enable(dwmac->clk_eth_ck);
+                       if (ret) {
+                               clk_disable_unprepare(dwmac->syscfg_clk);
+                               return ret;
+                       }
                }
        } else {
                clk_disable_unprepare(dwmac->syscfg_clk);
-               clk_disable_unprepare(dwmac->clk_eth_ck);
+               if (dwmac->enable_eth_ck)
+                       clk_disable_unprepare(dwmac->clk_eth_ck);
        }
        return ret;
 }
@@ -170,24 +167,34 @@ static int stm32mp1_clk_prepare(struct stm32_dwmac *dwmac, bool prepare)
 static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat)
 {
        struct stm32_dwmac *dwmac = plat_dat->bsp_priv;
-       u32 reg = dwmac->mode_reg;
+       u32 reg = dwmac->mode_reg, clk_rate;
        int val;
 
+       clk_rate = clk_get_rate(dwmac->clk_eth_ck);
+       dwmac->enable_eth_ck = false;
        switch (plat_dat->interface) {
        case PHY_INTERFACE_MODE_MII:
+               if (clk_rate == ETH_CK_F_25M && dwmac->ext_phyclk)
+                       dwmac->enable_eth_ck = true;
                val = SYSCFG_PMCR_ETH_SEL_MII;
                pr_debug("SYSCFG init : PHY_INTERFACE_MODE_MII\n");
                break;
        case PHY_INTERFACE_MODE_GMII:
                val = SYSCFG_PMCR_ETH_SEL_GMII;
-               if (dwmac->eth_clk_sel_reg)
+               if (clk_rate == ETH_CK_F_25M &&
+                   (dwmac->eth_clk_sel_reg || dwmac->ext_phyclk)) {
+                       dwmac->enable_eth_ck = true;
                        val |= SYSCFG_PMCR_ETH_CLK_SEL;
+               }
                pr_debug("SYSCFG init : PHY_INTERFACE_MODE_GMII\n");
                break;
        case PHY_INTERFACE_MODE_RMII:
                val = SYSCFG_PMCR_ETH_SEL_RMII;
-               if (dwmac->eth_ref_clk_sel_reg)
+               if ((clk_rate == ETH_CK_F_25M || clk_rate == ETH_CK_F_50M) &&
+                   (dwmac->eth_ref_clk_sel_reg || dwmac->ext_phyclk)) {
+                       dwmac->enable_eth_ck = true;
                        val |= SYSCFG_PMCR_ETH_REF_CLK_SEL;
+               }
                pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RMII\n");
                break;
        case PHY_INTERFACE_MODE_RGMII:
@@ -195,8 +202,11 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat)
        case PHY_INTERFACE_MODE_RGMII_RXID:
        case PHY_INTERFACE_MODE_RGMII_TXID:
                val = SYSCFG_PMCR_ETH_SEL_RGMII;
-               if (dwmac->eth_clk_sel_reg)
+               if ((clk_rate == ETH_CK_F_25M || clk_rate == ETH_CK_F_125M) &&
+                   (dwmac->eth_clk_sel_reg || dwmac->ext_phyclk)) {
+                       dwmac->enable_eth_ck = true;
                        val |= SYSCFG_PMCR_ETH_CLK_SEL;
+               }
                pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RGMII\n");
                break;
        default:
@@ -294,6 +304,9 @@ static int stm32mp1_parse_data(struct stm32_dwmac *dwmac,
        struct device_node *np = dev->of_node;
        int err = 0;
 
+       /* Ethernet PHY have no crystal */
+       dwmac->ext_phyclk = of_property_read_bool(np, "st,ext-phyclk");
+
        /* Gigabit Ethernet 125MHz clock selection. */
        dwmac->eth_clk_sel_reg = of_property_read_bool(np, "st,eth-clk-sel");
 
@@ -431,7 +444,8 @@ static int stm32mp1_suspend(struct stm32_dwmac *dwmac)
 
        clk_disable_unprepare(dwmac->clk_tx);
        clk_disable_unprepare(dwmac->syscfg_clk);
-       clk_disable_unprepare(dwmac->clk_eth_ck);
+       if (dwmac->enable_eth_ck)
+               clk_disable_unprepare(dwmac->clk_eth_ck);
 
        return ret;
 }
index 7d40760..0e1ca2c 100644 (file)
@@ -150,6 +150,8 @@ static int sun7i_gmac_probe(struct platform_device *pdev)
        plat_dat->init = sun7i_gmac_init;
        plat_dat->exit = sun7i_gmac_exit;
        plat_dat->fix_mac_speed = sun7i_fix_speed;
+       plat_dat->tx_fifo_size = 4096;
+       plat_dat->rx_fifo_size = 16384;
 
        ret = sun7i_gmac_init(pdev, plat_dat->bsp_priv);
        if (ret)
index 28cac28..61f3249 100644 (file)
@@ -90,6 +90,7 @@
 #define GMAC_VLAN_CSVL                 BIT(19)
 #define GMAC_VLAN_VLC                  GENMASK(17, 16)
 #define GMAC_VLAN_VLC_SHIFT            16
+#define GMAC_VLAN_VLHT                 GENMASK(15, 0)
 
 /* MAC VLAN Tag */
 #define GMAC_VLAN_TAG_VID              GENMASK(15, 0)
index 39692d1..ecd834e 100644 (file)
@@ -450,6 +450,12 @@ static int dwmac4_add_hw_vlan_rx_fltr(struct net_device *dev,
        if (vid > 4095)
                return -EINVAL;
 
+       if (hw->promisc) {
+               netdev_err(dev,
+                          "Adding VLAN in promisc mode not supported\n");
+               return -EPERM;
+       }
+
        /* Single Rx VLAN Filter */
        if (hw->num_vlan == 1) {
                /* For single VLAN filter, VID 0 means VLAN promiscuous */
@@ -499,6 +505,12 @@ static int dwmac4_del_hw_vlan_rx_fltr(struct net_device *dev,
 {
        int i, ret = 0;
 
+       if (hw->promisc) {
+               netdev_err(dev,
+                          "Deleting VLAN in promisc mode not supported\n");
+               return -EPERM;
+       }
+
        /* Single Rx VLAN Filter */
        if (hw->num_vlan == 1) {
                if ((hw->vlan_filter[0] & GMAC_VLAN_TAG_VID) == vid) {
@@ -523,9 +535,45 @@ static int dwmac4_del_hw_vlan_rx_fltr(struct net_device *dev,
        return ret;
 }
 
+static void dwmac4_vlan_promisc_enable(struct net_device *dev,
+                                      struct mac_device_info *hw)
+{
+       void __iomem *ioaddr = hw->pcsr;
+       u32 value;
+       u32 hash;
+       u32 val;
+       int i;
+
+       /* Single Rx VLAN Filter */
+       if (hw->num_vlan == 1) {
+               dwmac4_write_single_vlan(dev, 0);
+               return;
+       }
+
+       /* Extended Rx VLAN Filter Enable */
+       for (i = 0; i < hw->num_vlan; i++) {
+               if (hw->vlan_filter[i] & GMAC_VLAN_TAG_DATA_VEN) {
+                       val = hw->vlan_filter[i] & ~GMAC_VLAN_TAG_DATA_VEN;
+                       dwmac4_write_vlan_filter(dev, hw, i, val);
+               }
+       }
+
+       hash = readl(ioaddr + GMAC_VLAN_HASH_TABLE);
+       if (hash & GMAC_VLAN_VLHT) {
+               value = readl(ioaddr + GMAC_VLAN_TAG);
+               if (value & GMAC_VLAN_VTHM) {
+                       value &= ~GMAC_VLAN_VTHM;
+                       writel(value, ioaddr + GMAC_VLAN_TAG);
+               }
+       }
+}
+
 static void dwmac4_restore_hw_vlan_rx_fltr(struct net_device *dev,
                                           struct mac_device_info *hw)
 {
+       void __iomem *ioaddr = hw->pcsr;
+       u32 value;
+       u32 hash;
        u32 val;
        int i;
 
@@ -542,6 +590,13 @@ static void dwmac4_restore_hw_vlan_rx_fltr(struct net_device *dev,
                        dwmac4_write_vlan_filter(dev, hw, i, val);
                }
        }
+
+       hash = readl(ioaddr + GMAC_VLAN_HASH_TABLE);
+       if (hash & GMAC_VLAN_VLHT) {
+               value = readl(ioaddr + GMAC_VLAN_TAG);
+               value |= GMAC_VLAN_VTHM;
+               writel(value, ioaddr + GMAC_VLAN_TAG);
+       }
 }
 
 static void dwmac4_set_filter(struct mac_device_info *hw,
@@ -624,6 +679,18 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
                value |= GMAC_PACKET_FILTER_VTFE;
 
        writel(value, ioaddr + GMAC_PACKET_FILTER);
+
+       if (dev->flags & IFF_PROMISC) {
+               if (!hw->promisc) {
+                       hw->promisc = 1;
+                       dwmac4_vlan_promisc_enable(dev, hw);
+               }
+       } else {
+               if (hw->promisc) {
+                       hw->promisc = 0;
+                       dwmac4_restore_hw_vlan_rx_fltr(dev, hw);
+               }
+       }
 }
 
 static void dwmac4_flow_ctrl(struct mac_device_info *hw, unsigned int duplex,
index 494c859..67ba67e 100644 (file)
@@ -624,7 +624,7 @@ int dwmac5_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg,
                total_offset += offset;
        }
 
-       total_ctr = cfg->ctr[0] + cfg->ctr[1] * 1000000000;
+       total_ctr = cfg->ctr[0] + cfg->ctr[1] * 1000000000ULL;
        total_ctr += total_offset;
 
        ctr_low = do_div(total_ctr, 1000000000);
index fcf0802..d291612 100644 (file)
@@ -27,12 +27,16 @@ static void config_sub_second_increment(void __iomem *ioaddr,
        unsigned long data;
        u32 reg_value;
 
-       /* For GMAC3.x, 4.x versions, convert the ptp_clock to nano second
-        *      formula = (1/ptp_clock) * 1000000000
-        * where ptp_clock is 50MHz if fine method is used to update system
+       /* For GMAC3.x, 4.x versions, in "fine adjustement mode" set sub-second
+        * increment to twice the number of nanoseconds of a clock cycle.
+        * The calculation of the default_addend value by the caller will set it
+        * to mid-range = 2^31 when the remainder of this division is zero,
+        * which will make the accumulator overflow once every 2 ptp_clock
+        * cycles, adding twice the number of nanoseconds of a clock cycle :
+        * 2000000000ULL / ptp_clock.
         */
        if (value & PTP_TCR_TSCFUPDT)
-               data = (1000000000ULL / 50000000);
+               data = (2000000000ULL / ptp_clock);
        else
                data = (1000000000ULL / ptp_clock);
 
index e6898fd..8f08393 100644 (file)
@@ -3543,15 +3543,6 @@ static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb)
        }
 }
 
-
-static inline int stmmac_rx_threshold_count(struct stmmac_rx_queue *rx_q)
-{
-       if (rx_q->rx_zeroc_thresh < STMMAC_RX_THRESH)
-               return 0;
-
-       return 1;
-}
-
 /**
  * stmmac_rx_refill - refill used skb preallocated buffers
  * @priv: driver private structure
@@ -4060,7 +4051,7 @@ static int stmmac_set_features(struct net_device *netdev,
 /**
  *  stmmac_interrupt - main ISR
  *  @irq: interrupt number.
- *  @dev_id: to pass the net device pointer.
+ *  @dev_id: to pass the net device pointer (must be valid).
  *  Description: this is the main driver interrupt service routine.
  *  It can call:
  *  o DMA service routine (to manage incoming frame reception and transmission
@@ -4084,11 +4075,6 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
        if (priv->irq_wake)
                pm_wakeup_event(priv->device, 0);
 
-       if (unlikely(!dev)) {
-               netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__);
-               return IRQ_NONE;
-       }
-
        /* Check if adapter is up */
        if (test_bit(STMMAC_DOWN, &priv->state))
                return IRQ_HANDLED;
@@ -4986,12 +4972,22 @@ int stmmac_dvr_probe(struct device *device,
                goto error_netdev_register;
        }
 
+       if (priv->plat->serdes_powerup) {
+               ret = priv->plat->serdes_powerup(ndev,
+                                                priv->plat->bsp_priv);
+
+               if (ret < 0)
+                       goto error_serdes_powerup;
+       }
+
 #ifdef CONFIG_DEBUG_FS
        stmmac_init_fs(ndev);
 #endif
 
        return ret;
 
+error_serdes_powerup:
+       unregister_netdev(ndev);
 error_netdev_register:
        phylink_destroy(priv->phylink);
 error_phy_setup:
@@ -5029,6 +5025,9 @@ int stmmac_dvr_remove(struct device *dev)
 
        stmmac_stop_all_dma(priv);
 
+       if (priv->plat->serdes_powerdown)
+               priv->plat->serdes_powerdown(ndev, priv->plat->bsp_priv);
+
        stmmac_mac_set(priv, priv->ioaddr, false);
        netif_carrier_off(ndev);
        unregister_netdev(ndev);
@@ -5081,6 +5080,9 @@ int stmmac_suspend(struct device *dev)
        /* Stop TX/RX DMA */
        stmmac_stop_all_dma(priv);
 
+       if (priv->plat->serdes_powerdown)
+               priv->plat->serdes_powerdown(ndev, priv->plat->bsp_priv);
+
        /* Enable Power down mode by programming the PMT regs */
        if (device_may_wakeup(priv->device)) {
                stmmac_pmt(priv, priv->hw, priv->wolopts);
@@ -5143,6 +5145,7 @@ int stmmac_resume(struct device *dev)
 {
        struct net_device *ndev = dev_get_drvdata(dev);
        struct stmmac_priv *priv = netdev_priv(ndev);
+       int ret;
 
        if (!netif_running(ndev))
                return 0;
@@ -5170,6 +5173,14 @@ int stmmac_resume(struct device *dev)
                        stmmac_mdio_reset(priv->mii);
        }
 
+       if (priv->plat->serdes_powerup) {
+               ret = priv->plat->serdes_powerup(ndev,
+                                                priv->plat->bsp_priv);
+
+               if (ret < 0)
+                       return ret;
+       }
+
        netif_device_attach(ndev);
 
        mutex_lock(&priv->lock);
index 3fb21f7..272cb47 100644 (file)
@@ -217,15 +217,10 @@ static int stmmac_pci_probe(struct pci_dev *pdev,
  */
 static void stmmac_pci_remove(struct pci_dev *pdev)
 {
-       struct net_device *ndev = dev_get_drvdata(&pdev->dev);
-       struct stmmac_priv *priv = netdev_priv(ndev);
        int i;
 
        stmmac_dvr_remove(&pdev->dev);
 
-       if (priv->plat->stmmac_clk)
-               clk_unregister_fixed_rate(priv->plat->stmmac_clk);
-
        for (i = 0; i < PCI_STD_NUM_BARS; i++) {
                if (pci_resource_len(pdev, i) == 0)
                        continue;
index e6d1aa8..e6e2596 100644 (file)
@@ -237,12 +237,6 @@ static inline void cas_lock_tx(struct cas *cp)
                spin_lock_nested(&cp->tx_lock[i], i);
 }
 
-static inline void cas_lock_all(struct cas *cp)
-{
-       spin_lock_irq(&cp->lock);
-       cas_lock_tx(cp);
-}
-
 /* WTZ: QA was finding deadlock problems with the previous
  * versions after long test runs with multiple cards per machine.
  * See if replacing cas_lock_all with safer versions helps. The
@@ -266,12 +260,6 @@ static inline void cas_unlock_tx(struct cas *cp)
                spin_unlock(&cp->tx_lock[i - 1]);
 }
 
-static inline void cas_unlock_all(struct cas *cp)
-{
-       cas_unlock_tx(cp);
-       spin_unlock_irq(&cp->lock);
-}
-
 #define cas_unlock_all_restore(cp, flags) \
 do { \
        struct cas *xxxcp = (cp); \
@@ -5059,7 +5047,7 @@ static int cas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (cp->cas_flags & CAS_FLAG_SATURN)
                cas_saturn_firmware_init(cp);
 
-       cp->init_block = (struct cas_init_block *)
+       cp->init_block =
                pci_alloc_consistent(pdev, sizeof(struct cas_init_block),
                                     &cp->block_dvma);
        if (!cp->init_block) {
index 40a2ce0..e287272 100644 (file)
@@ -1362,18 +1362,6 @@ static void print_rxfd(struct rxf_desc *rxfd)
  * As our benchmarks shows, it adds 1.5 Gbit/sec to NIS's throuput.
  */
 
-/*************************************************************************
- *     Tx DB                                                             *
- *************************************************************************/
-static inline int bdx_tx_db_size(struct txdb *db)
-{
-       int taken = db->wptr - db->rptr;
-       if (taken < 0)
-               taken = db->size + 1 + taken;   /* (size + 1) equals memsz */
-
-       return db->size - taken;
-}
-
 /**
  * __bdx_tx_db_ptr_next - helper function, increment read/write pointer + wrap
  * @db: tx data base
index 89cec77..988e907 100644 (file)
@@ -90,9 +90,8 @@ config TI_CPTS
 config TI_CPTS_MOD
        tristate
        depends on TI_CPTS
+       depends on PTP_1588_CLOCK
        default y if TI_CPSW=y || TI_KEYSTONE_NETCP=y || TI_CPSW_SWITCHDEV=y
-       select NET_PTP_CLASSIFY
-       imply PTP_1588_CLOCK
        default m
 
 config TI_K3_AM65_CPSW_NUSS
@@ -100,6 +99,7 @@ config TI_K3_AM65_CPSW_NUSS
        depends on ARCH_K3 && OF && TI_K3_UDMA_GLUE_LAYER
        select TI_DAVINCI_MDIO
        imply PHY_TI_GMII_SEL
+       depends on TI_K3_AM65_CPTS || !TI_K3_AM65_CPTS
        help
          This driver supports TI K3 AM654/J721E CPSW2G Ethernet SubSystem.
          The two-port Gigabit Ethernet MAC (MCU_CPSW0) subsystem provides
@@ -110,6 +110,19 @@ config TI_K3_AM65_CPSW_NUSS
          To compile this driver as a module, choose M here: the module
          will be called ti-am65-cpsw-nuss.
 
+config TI_K3_AM65_CPTS
+       tristate "TI K3 AM65x CPTS"
+       depends on ARCH_K3 && OF
+       depends on PTP_1588_CLOCK
+       help
+         Say y here to support the TI K3 AM65x CPTS with 1588 features such as
+         PTP hardware clock for each CPTS device and network packets
+         timestamping where applicable.
+         Depending on integration CPTS blocks enable compliance with
+         the IEEE 1588-2008 standard for a precision clock synchronization
+         protocol, Ethernet Enhanced Scheduled Traffic Operations (CPTS_ESTFn)
+         and PCIe Subsystem Precision Time Measurement (PTM).
+
 config TI_KEYSTONE_NETCP
        tristate "TI Keystone NETCP Core Support"
        select TI_DAVINCI_MDIO
@@ -138,7 +151,7 @@ config TLAN
 
          Devices currently supported by this driver are Compaq Netelligent,
          Compaq NetFlex and Olicom cards.  Please read the file
-         <file:Documentation/networking/device_drivers/ti/tlan.txt>
+         <file:Documentation/networking/device_drivers/ti/tlan.rst>
          for more details.
 
          To compile this driver as a module, choose M here. The module
index 5379219..bf86067 100644 (file)
@@ -26,3 +26,4 @@ keystone_netcp_ethss-y := netcp_ethss.o netcp_sgmii.o netcp_xgbepcsr.o cpsw_ale.
 
 obj-$(CONFIG_TI_K3_AM65_CPSW_NUSS) += ti-am65-cpsw-nuss.o
 ti-am65-cpsw-nuss-y := am65-cpsw-nuss.o cpsw_sl.o am65-cpsw-ethtool.o cpsw_ale.o k3-cppi-desc-pool.o
+obj-$(CONFIG_TI_K3_AM65_CPTS) += am65-cpts.o
index c3502aa..23661a6 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "am65-cpsw-nuss.h"
 #include "cpsw_ale.h"
+#include "am65-cpts.h"
 
 #define AM65_CPSW_REGDUMP_VER 0x1
 
@@ -694,6 +695,27 @@ static void am65_cpsw_get_ethtool_stats(struct net_device *ndev,
                                        hw_stats[i].offset);
 }
 
+static int am65_cpsw_get_ethtool_ts_info(struct net_device *ndev,
+                                        struct ethtool_ts_info *info)
+{
+       struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
+
+       if (!IS_ENABLED(CONFIG_TI_K3_AM65_CPTS))
+               return ethtool_op_get_ts_info(ndev, info);
+
+       info->so_timestamping =
+               SOF_TIMESTAMPING_TX_HARDWARE |
+               SOF_TIMESTAMPING_TX_SOFTWARE |
+               SOF_TIMESTAMPING_RX_HARDWARE |
+               SOF_TIMESTAMPING_RX_SOFTWARE |
+               SOF_TIMESTAMPING_SOFTWARE |
+               SOF_TIMESTAMPING_RAW_HARDWARE;
+       info->phc_index = am65_cpts_phc_index(common->cpts);
+       info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
+       info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
+       return 0;
+}
+
 static u32 am65_cpsw_get_ethtool_priv_flags(struct net_device *ndev)
 {
        struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
@@ -730,7 +752,7 @@ const struct ethtool_ops am65_cpsw_ethtool_ops_slave = {
        .get_sset_count         = am65_cpsw_get_sset_count,
        .get_strings            = am65_cpsw_get_strings,
        .get_ethtool_stats      = am65_cpsw_get_ethtool_stats,
-       .get_ts_info            = ethtool_op_get_ts_info,
+       .get_ts_info            = am65_cpsw_get_ethtool_ts_info,
        .get_priv_flags         = am65_cpsw_get_ethtool_priv_flags,
        .set_priv_flags         = am65_cpsw_set_ethtool_priv_flags,
 
index f71c15c..f8c5899 100644 (file)
@@ -30,6 +30,7 @@
 #include "cpsw_sl.h"
 #include "am65-cpsw-nuss.h"
 #include "k3-cppi-desc-pool.h"
+#include "am65-cpts.h"
 
 #define AM65_CPSW_SS_BASE      0x0
 #define AM65_CPSW_SGMII_BASE   0x100
@@ -668,6 +669,18 @@ static void am65_cpsw_nuss_rx_cleanup(void *data, dma_addr_t desc_dma)
        dev_kfree_skb_any(skb);
 }
 
+static void am65_cpsw_nuss_rx_ts(struct sk_buff *skb, u32 *psdata)
+{
+       struct skb_shared_hwtstamps *ssh;
+       u64 ns;
+
+       ns = ((u64)psdata[1] << 32) | psdata[0];
+
+       ssh = skb_hwtstamps(skb);
+       memset(ssh, 0, sizeof(*ssh));
+       ssh->hwtstamp = ns_to_ktime(ns);
+}
+
 /* RX psdata[2] word format - checksum information */
 #define AM65_CPSW_RX_PSD_CSUM_ADD      GENMASK(15, 0)
 #define AM65_CPSW_RX_PSD_CSUM_ERR      BIT(16)
@@ -745,6 +758,9 @@ static int am65_cpsw_nuss_rx_packets(struct am65_cpsw_common *common,
        skb->dev = ndev;
 
        psdata = cppi5_hdesc_get_psdata(desc_rx);
+       /* add RX timestamp */
+       if (port->rx_ts_enabled)
+               am65_cpsw_nuss_rx_ts(skb, psdata);
        csum_info = psdata[2];
        dev_dbg(dev, "%s rx csum_info:%#x\n", __func__, csum_info);
 
@@ -904,6 +920,8 @@ static int am65_cpsw_nuss_tx_compl_packets(struct am65_cpsw_common *common,
 
                ndev = skb->dev;
 
+               am65_cpts_tx_timestamp(common->cpts, skb);
+
                ndev_priv = netdev_priv(ndev);
                stats = this_cpu_ptr(ndev_priv->stats);
                u64_stats_update_begin(&stats->syncp);
@@ -995,6 +1013,10 @@ static netdev_tx_t am65_cpsw_nuss_ndo_slave_xmit(struct sk_buff *skb,
        /* padding enabled in hw */
        pkt_len = skb_headlen(skb);
 
+       /* SKB TX timestamp */
+       if (port->tx_ts_enabled)
+               am65_cpts_prep_tx_timestamp(common->cpts, skb);
+
        q_idx = skb_get_queue_mapping(skb);
        dev_dbg(dev, "%s skb_queue:%d\n", __func__, q_idx);
 
@@ -1158,6 +1180,111 @@ static int am65_cpsw_nuss_ndo_slave_set_mac_address(struct net_device *ndev,
        return 0;
 }
 
+static int am65_cpsw_nuss_hwtstamp_set(struct net_device *ndev,
+                                      struct ifreq *ifr)
+{
+       struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
+       struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
+       u32 ts_ctrl, seq_id, ts_ctrl_ltype2, ts_vlan_ltype;
+       struct hwtstamp_config cfg;
+
+       if (!IS_ENABLED(CONFIG_TI_K3_AM65_CPTS))
+               return -EOPNOTSUPP;
+
+       if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
+               return -EFAULT;
+
+       /* TX HW timestamp */
+       switch (cfg.tx_type) {
+       case HWTSTAMP_TX_OFF:
+       case HWTSTAMP_TX_ON:
+               break;
+       default:
+               return -ERANGE;
+       }
+
+       switch (cfg.rx_filter) {
+       case HWTSTAMP_FILTER_NONE:
+               port->rx_ts_enabled = false;
+               break;
+       case HWTSTAMP_FILTER_ALL:
+       case HWTSTAMP_FILTER_SOME:
+       case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+       case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+       case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+       case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+       case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+       case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+       case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+       case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+       case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+       case HWTSTAMP_FILTER_PTP_V2_EVENT:
+       case HWTSTAMP_FILTER_PTP_V2_SYNC:
+       case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+       case HWTSTAMP_FILTER_NTP_ALL:
+               port->rx_ts_enabled = true;
+               cfg.rx_filter = HWTSTAMP_FILTER_ALL;
+               break;
+       default:
+               return -ERANGE;
+       }
+
+       port->tx_ts_enabled = (cfg.tx_type == HWTSTAMP_TX_ON);
+
+       /* cfg TX timestamp */
+       seq_id = (AM65_CPSW_TS_SEQ_ID_OFFSET <<
+                 AM65_CPSW_PN_TS_SEQ_ID_OFFSET_SHIFT) | ETH_P_1588;
+
+       ts_vlan_ltype = ETH_P_8021Q;
+
+       ts_ctrl_ltype2 = ETH_P_1588 |
+                        AM65_CPSW_PN_TS_CTL_LTYPE2_TS_107 |
+                        AM65_CPSW_PN_TS_CTL_LTYPE2_TS_129 |
+                        AM65_CPSW_PN_TS_CTL_LTYPE2_TS_130 |
+                        AM65_CPSW_PN_TS_CTL_LTYPE2_TS_131 |
+                        AM65_CPSW_PN_TS_CTL_LTYPE2_TS_132 |
+                        AM65_CPSW_PN_TS_CTL_LTYPE2_TS_319 |
+                        AM65_CPSW_PN_TS_CTL_LTYPE2_TS_320 |
+                        AM65_CPSW_PN_TS_CTL_LTYPE2_TS_TTL_NONZERO;
+
+       ts_ctrl = AM65_CPSW_TS_EVENT_MSG_TYPE_BITS <<
+                 AM65_CPSW_PN_TS_CTL_MSG_TYPE_EN_SHIFT;
+
+       if (port->tx_ts_enabled)
+               ts_ctrl |= AM65_CPSW_TS_TX_ANX_ALL_EN |
+                          AM65_CPSW_PN_TS_CTL_TX_VLAN_LT1_EN;
+
+       writel(seq_id, port->port_base + AM65_CPSW_PORTN_REG_TS_SEQ_LTYPE_REG);
+       writel(ts_vlan_ltype, port->port_base +
+              AM65_CPSW_PORTN_REG_TS_VLAN_LTYPE_REG);
+       writel(ts_ctrl_ltype2, port->port_base +
+              AM65_CPSW_PORTN_REG_TS_CTL_LTYPE2);
+       writel(ts_ctrl, port->port_base + AM65_CPSW_PORTN_REG_TS_CTL);
+
+       /* en/dis RX timestamp */
+       am65_cpts_rx_enable(common->cpts, port->rx_ts_enabled);
+
+       return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+}
+
+static int am65_cpsw_nuss_hwtstamp_get(struct net_device *ndev,
+                                      struct ifreq *ifr)
+{
+       struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
+       struct hwtstamp_config cfg;
+
+       if (!IS_ENABLED(CONFIG_TI_K3_AM65_CPTS))
+               return -EOPNOTSUPP;
+
+       cfg.flags = 0;
+       cfg.tx_type = port->tx_ts_enabled ?
+                     HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+       cfg.rx_filter = port->rx_ts_enabled ?
+                       HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE;
+
+       return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+}
+
 static int am65_cpsw_nuss_ndo_slave_ioctl(struct net_device *ndev,
                                          struct ifreq *req, int cmd)
 {
@@ -1166,6 +1293,13 @@ static int am65_cpsw_nuss_ndo_slave_ioctl(struct net_device *ndev,
        if (!netif_running(ndev))
                return -EINVAL;
 
+       switch (cmd) {
+       case SIOCSHWTSTAMP:
+               return am65_cpsw_nuss_hwtstamp_set(ndev, req);
+       case SIOCGHWTSTAMP:
+               return am65_cpsw_nuss_hwtstamp_get(ndev, req);
+       }
+
        if (!port->slave.phy)
                return -EOPNOTSUPP;
 
@@ -1372,7 +1506,7 @@ static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common)
 err:
        i = devm_add_action(dev, am65_cpsw_nuss_free_tx_chns, common);
        if (i) {
-               dev_err(dev, "failed to add free_tx_chns action %d", i);
+               dev_err(dev, "Failed to add free_tx_chns action %d\n", i);
                return i;
        }
 
@@ -1481,7 +1615,7 @@ static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common)
 err:
        i = devm_add_action(dev, am65_cpsw_nuss_free_rx_chns, common);
        if (i) {
-               dev_err(dev, "failed to add free_rx_chns action %d", i);
+               dev_err(dev, "Failed to add free_rx_chns action %d\n", i);
                return i;
        }
 
@@ -1531,6 +1665,40 @@ static int am65_cpsw_am654_get_efuse_macid(struct device_node *of_node,
        return 0;
 }
 
+static int am65_cpsw_init_cpts(struct am65_cpsw_common *common)
+{
+       struct device *dev = common->dev;
+       struct device_node *node;
+       struct am65_cpts *cpts;
+       void __iomem *reg_base;
+
+       if (!IS_ENABLED(CONFIG_TI_K3_AM65_CPTS))
+               return 0;
+
+       node = of_get_child_by_name(dev->of_node, "cpts");
+       if (!node) {
+               dev_err(dev, "%s cpts not found\n", __func__);
+               return -ENOENT;
+       }
+
+       reg_base = common->cpsw_base + AM65_CPSW_NU_CPTS_BASE;
+       cpts = am65_cpts_create(dev, reg_base, node);
+       if (IS_ERR(cpts)) {
+               int ret = PTR_ERR(cpts);
+
+               if (ret == -EOPNOTSUPP) {
+                       dev_info(dev, "cpts disabled\n");
+                       return 0;
+               }
+
+               dev_err(dev, "cpts create err %d\n", ret);
+               return ret;
+       }
+       common->cpts = cpts;
+
+       return 0;
+}
+
 static int am65_cpsw_nuss_init_slave_ports(struct am65_cpsw_common *common)
 {
        struct device_node *node, *port_np;
@@ -1691,7 +1859,7 @@ static int am65_cpsw_nuss_init_ndev_2g(struct am65_cpsw_common *common)
        ret = devm_add_action_or_reset(dev, am65_cpsw_pcpu_stats_free,
                                       ndev_priv->stats);
        if (ret) {
-               dev_err(dev, "failed to add percpu stat free action %d", ret);
+               dev_err(dev, "Failed to add percpu stat free action %d\n", ret);
                return ret;
        }
 
@@ -1719,7 +1887,8 @@ static int am65_cpsw_nuss_ndev_add_napi_2g(struct am65_cpsw_common *common)
 
                ret = devm_request_irq(dev, tx_chn->irq,
                                       am65_cpsw_nuss_tx_irq,
-                                      0, tx_chn->tx_chn_name, tx_chn);
+                                      IRQF_TRIGGER_HIGH,
+                                      tx_chn->tx_chn_name, tx_chn);
                if (ret) {
                        dev_err(dev, "failure requesting tx%u irq %u, %d\n",
                                tx_chn->id, tx_chn->irq, ret);
@@ -1744,7 +1913,7 @@ static int am65_cpsw_nuss_ndev_reg_2g(struct am65_cpsw_common *common)
 
        ret = devm_request_irq(dev, common->rx_chns.irq,
                               am65_cpsw_nuss_rx_irq,
-                              0, dev_name(dev), common);
+                              IRQF_TRIGGER_HIGH, dev_name(dev), common);
        if (ret) {
                dev_err(dev, "failure requesting rx irq %u, %d\n",
                        common->rx_chns.irq, ret);
@@ -1899,6 +2068,10 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev)
                goto err_of_clear;
        }
 
+       ret = am65_cpsw_init_cpts(common);
+       if (ret)
+               goto err_of_clear;
+
        /* init ports */
        for (i = 0; i < common->port_num; i++)
                am65_cpsw_nuss_slave_disable_unused(&common->ports[i]);
index 41ae5b4..b1cddfd 100644 (file)
@@ -10,6 +10,8 @@
 #include <linux/module.h>
 #include <linux/netdevice.h>
 
+struct am65_cpts;
+
 #define HOST_PORT_NUM          0
 
 #define AM65_CPSW_MAX_TX_QUEUES        8
@@ -37,6 +39,8 @@ struct am65_cpsw_port {
        void __iomem                    *stat_base;
        bool                            disabled;
        struct am65_cpsw_slave_data     slave;
+       bool                            tx_ts_enabled;
+       bool                            rx_ts_enabled;
 };
 
 struct am65_cpsw_host {
@@ -96,8 +100,8 @@ struct am65_cpsw_common {
 
        u32                     nuss_ver;
        u32                     cpsw_ver;
-
        bool                    pf_p0_rx_ptype_rrobin;
+       struct am65_cpts        *cpts;
 };
 
 struct am65_cpsw_ndev_stats {
diff --git a/drivers/net/ethernet/ti/am65-cpts.c b/drivers/net/ethernet/ti/am65-cpts.c
new file mode 100644 (file)
index 0000000..51c94b2
--- /dev/null
@@ -0,0 +1,1038 @@
+// SPDX-License-Identifier: GPL-2.0
+/* TI K3 AM65x Common Platform Time Sync
+ *
+ * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/if_vlan.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/net_tstamp.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/ptp_classify.h>
+#include <linux/ptp_clock_kernel.h>
+
+#include "am65-cpts.h"
+
+struct am65_genf_regs {
+       u32 comp_lo;    /* Comparison Low Value 0:31 */
+       u32 comp_hi;    /* Comparison High Value 32:63 */
+       u32 control;    /* control */
+       u32 length;     /* Length */
+       u32 ppm_low;    /* PPM Load Low Value 0:31 */
+       u32 ppm_hi;     /* PPM Load High Value 32:63 */
+       u32 ts_nudge;   /* Nudge value */
+} __aligned(32) __packed;
+
+#define AM65_CPTS_GENF_MAX_NUM 9
+#define AM65_CPTS_ESTF_MAX_NUM 8
+
+struct am65_cpts_regs {
+       u32 idver;              /* Identification and version */
+       u32 control;            /* Time sync control */
+       u32 rftclk_sel;         /* Reference Clock Select Register */
+       u32 ts_push;            /* Time stamp event push */
+       u32 ts_load_val_lo;     /* Time Stamp Load Low Value 0:31 */
+       u32 ts_load_en;         /* Time stamp load enable */
+       u32 ts_comp_lo;         /* Time Stamp Comparison Low Value 0:31 */
+       u32 ts_comp_length;     /* Time Stamp Comparison Length */
+       u32 intstat_raw;        /* Time sync interrupt status raw */
+       u32 intstat_masked;     /* Time sync interrupt status masked */
+       u32 int_enable;         /* Time sync interrupt enable */
+       u32 ts_comp_nudge;      /* Time Stamp Comparison Nudge Value */
+       u32 event_pop;          /* Event interrupt pop */
+       u32 event_0;            /* Event Time Stamp lo 0:31 */
+       u32 event_1;            /* Event Type Fields */
+       u32 event_2;            /* Event Type Fields domain */
+       u32 event_3;            /* Event Time Stamp hi 32:63 */
+       u32 ts_load_val_hi;     /* Time Stamp Load High Value 32:63 */
+       u32 ts_comp_hi;         /* Time Stamp Comparison High Value 32:63 */
+       u32 ts_add_val;         /* Time Stamp Add value */
+       u32 ts_ppm_low;         /* Time Stamp PPM Load Low Value 0:31 */
+       u32 ts_ppm_hi;          /* Time Stamp PPM Load High Value 32:63 */
+       u32 ts_nudge;           /* Time Stamp Nudge value */
+       u32 reserv[33];
+       struct am65_genf_regs genf[AM65_CPTS_GENF_MAX_NUM];
+       struct am65_genf_regs estf[AM65_CPTS_ESTF_MAX_NUM];
+};
+
+/* CONTROL_REG */
+#define AM65_CPTS_CONTROL_EN                   BIT(0)
+#define AM65_CPTS_CONTROL_INT_TEST             BIT(1)
+#define AM65_CPTS_CONTROL_TS_COMP_POLARITY     BIT(2)
+#define AM65_CPTS_CONTROL_TSTAMP_EN            BIT(3)
+#define AM65_CPTS_CONTROL_SEQUENCE_EN          BIT(4)
+#define AM65_CPTS_CONTROL_64MODE               BIT(5)
+#define AM65_CPTS_CONTROL_TS_COMP_TOG          BIT(6)
+#define AM65_CPTS_CONTROL_TS_PPM_DIR           BIT(7)
+#define AM65_CPTS_CONTROL_HW1_TS_PUSH_EN       BIT(8)
+#define AM65_CPTS_CONTROL_HW2_TS_PUSH_EN       BIT(9)
+#define AM65_CPTS_CONTROL_HW3_TS_PUSH_EN       BIT(10)
+#define AM65_CPTS_CONTROL_HW4_TS_PUSH_EN       BIT(11)
+#define AM65_CPTS_CONTROL_HW5_TS_PUSH_EN       BIT(12)
+#define AM65_CPTS_CONTROL_HW6_TS_PUSH_EN       BIT(13)
+#define AM65_CPTS_CONTROL_HW7_TS_PUSH_EN       BIT(14)
+#define AM65_CPTS_CONTROL_HW8_TS_PUSH_EN       BIT(15)
+#define AM65_CPTS_CONTROL_HW1_TS_PUSH_OFFSET   (8)
+
+#define AM65_CPTS_CONTROL_TS_SYNC_SEL_MASK     (0xF)
+#define AM65_CPTS_CONTROL_TS_SYNC_SEL_SHIFT    (28)
+
+/* RFTCLK_SEL_REG */
+#define AM65_CPTS_RFTCLK_SEL_MASK              (0x1F)
+
+/* TS_PUSH_REG */
+#define AM65_CPTS_TS_PUSH                      BIT(0)
+
+/* TS_LOAD_EN_REG */
+#define AM65_CPTS_TS_LOAD_EN                   BIT(0)
+
+/* INTSTAT_RAW_REG */
+#define AM65_CPTS_INTSTAT_RAW_TS_PEND          BIT(0)
+
+/* INTSTAT_MASKED_REG */
+#define AM65_CPTS_INTSTAT_MASKED_TS_PEND       BIT(0)
+
+/* INT_ENABLE_REG */
+#define AM65_CPTS_INT_ENABLE_TS_PEND_EN                BIT(0)
+
+/* TS_COMP_NUDGE_REG */
+#define AM65_CPTS_TS_COMP_NUDGE_MASK           (0xFF)
+
+/* EVENT_POP_REG */
+#define AM65_CPTS_EVENT_POP                    BIT(0)
+
+/* EVENT_1_REG */
+#define AM65_CPTS_EVENT_1_SEQUENCE_ID_MASK     GENMASK(15, 0)
+
+#define AM65_CPTS_EVENT_1_MESSAGE_TYPE_MASK    GENMASK(19, 16)
+#define AM65_CPTS_EVENT_1_MESSAGE_TYPE_SHIFT   (16)
+
+#define AM65_CPTS_EVENT_1_EVENT_TYPE_MASK      GENMASK(23, 20)
+#define AM65_CPTS_EVENT_1_EVENT_TYPE_SHIFT     (20)
+
+#define AM65_CPTS_EVENT_1_PORT_NUMBER_MASK     GENMASK(28, 24)
+#define AM65_CPTS_EVENT_1_PORT_NUMBER_SHIFT    (24)
+
+/* EVENT_2_REG */
+#define AM65_CPTS_EVENT_2_REG_DOMAIN_MASK      (0xFF)
+#define AM65_CPTS_EVENT_2_REG_DOMAIN_SHIFT     (0)
+
+enum {
+       AM65_CPTS_EV_PUSH,      /* Time Stamp Push Event */
+       AM65_CPTS_EV_ROLL,      /* Time Stamp Rollover Event */
+       AM65_CPTS_EV_HALF,      /* Time Stamp Half Rollover Event */
+       AM65_CPTS_EV_HW,                /* Hardware Time Stamp Push Event */
+       AM65_CPTS_EV_RX,                /* Ethernet Receive Event */
+       AM65_CPTS_EV_TX,                /* Ethernet Transmit Event */
+       AM65_CPTS_EV_TS_COMP,   /* Time Stamp Compare Event */
+       AM65_CPTS_EV_HOST,      /* Host Transmit Event */
+};
+
+struct am65_cpts_event {
+       struct list_head list;
+       unsigned long tmo;
+       u32 event1;
+       u32 event2;
+       u64 timestamp;
+};
+
+#define AM65_CPTS_FIFO_DEPTH           (16)
+#define AM65_CPTS_MAX_EVENTS           (32)
+#define AM65_CPTS_EVENT_RX_TX_TIMEOUT  (20) /* ms */
+#define AM65_CPTS_SKB_TX_WORK_TIMEOUT  1 /* jiffies */
+#define AM65_CPTS_MIN_PPM              0x400
+
+struct am65_cpts {
+       struct device *dev;
+       struct am65_cpts_regs __iomem *reg;
+       struct ptp_clock_info ptp_info;
+       struct ptp_clock *ptp_clock;
+       int phc_index;
+       struct clk_hw *clk_mux_hw;
+       struct device_node *clk_mux_np;
+       struct clk *refclk;
+       u32 refclk_freq;
+       struct list_head events;
+       struct list_head pool;
+       struct am65_cpts_event pool_data[AM65_CPTS_MAX_EVENTS];
+       spinlock_t lock; /* protects events lists*/
+       u32 ext_ts_inputs;
+       u32 genf_num;
+       u32 ts_add_val;
+       int irq;
+       struct mutex ptp_clk_lock; /* PHC access sync */
+       u64 timestamp;
+       u32 genf_enable;
+       u32 hw_ts_enable;
+       struct sk_buff_head txq;
+};
+
+struct am65_cpts_skb_cb_data {
+       unsigned long tmo;
+       u32 skb_mtype_seqid;
+};
+
+#define am65_cpts_write32(c, v, r) writel(v, &(c)->reg->r)
+#define am65_cpts_read32(c, r) readl(&(c)->reg->r)
+
+static void am65_cpts_settime(struct am65_cpts *cpts, u64 start_tstamp)
+{
+       u32 val;
+
+       val = upper_32_bits(start_tstamp);
+       am65_cpts_write32(cpts, val, ts_load_val_hi);
+       val = lower_32_bits(start_tstamp);
+       am65_cpts_write32(cpts, val, ts_load_val_lo);
+
+       am65_cpts_write32(cpts, AM65_CPTS_TS_LOAD_EN, ts_load_en);
+}
+
+static void am65_cpts_set_add_val(struct am65_cpts *cpts)
+{
+       /* select coefficient according to the rate */
+       cpts->ts_add_val = (NSEC_PER_SEC / cpts->refclk_freq - 1) & 0x7;
+
+       am65_cpts_write32(cpts, cpts->ts_add_val, ts_add_val);
+}
+
+static void am65_cpts_disable(struct am65_cpts *cpts)
+{
+       am65_cpts_write32(cpts, 0, control);
+       am65_cpts_write32(cpts, 0, int_enable);
+}
+
+static int am65_cpts_event_get_port(struct am65_cpts_event *event)
+{
+       return (event->event1 & AM65_CPTS_EVENT_1_PORT_NUMBER_MASK) >>
+               AM65_CPTS_EVENT_1_PORT_NUMBER_SHIFT;
+}
+
+static int am65_cpts_event_get_type(struct am65_cpts_event *event)
+{
+       return (event->event1 & AM65_CPTS_EVENT_1_EVENT_TYPE_MASK) >>
+               AM65_CPTS_EVENT_1_EVENT_TYPE_SHIFT;
+}
+
+static int am65_cpts_cpts_purge_events(struct am65_cpts *cpts)
+{
+       struct list_head *this, *next;
+       struct am65_cpts_event *event;
+       int removed = 0;
+
+       list_for_each_safe(this, next, &cpts->events) {
+               event = list_entry(this, struct am65_cpts_event, list);
+               if (time_after(jiffies, event->tmo)) {
+                       list_del_init(&event->list);
+                       list_add(&event->list, &cpts->pool);
+                       ++removed;
+               }
+       }
+
+       if (removed)
+               dev_dbg(cpts->dev, "event pool cleaned up %d\n", removed);
+       return removed ? 0 : -1;
+}
+
+static bool am65_cpts_fifo_pop_event(struct am65_cpts *cpts,
+                                    struct am65_cpts_event *event)
+{
+       u32 r = am65_cpts_read32(cpts, intstat_raw);
+
+       if (r & AM65_CPTS_INTSTAT_RAW_TS_PEND) {
+               event->timestamp = am65_cpts_read32(cpts, event_0);
+               event->event1 = am65_cpts_read32(cpts, event_1);
+               event->event2 = am65_cpts_read32(cpts, event_2);
+               event->timestamp |= (u64)am65_cpts_read32(cpts, event_3) << 32;
+               am65_cpts_write32(cpts, AM65_CPTS_EVENT_POP, event_pop);
+               return false;
+       }
+       return true;
+}
+
+static int am65_cpts_fifo_read(struct am65_cpts *cpts)
+{
+       struct ptp_clock_event pevent;
+       struct am65_cpts_event *event;
+       bool schedule = false;
+       int i, type, ret = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&cpts->lock, flags);
+       for (i = 0; i < AM65_CPTS_FIFO_DEPTH; i++) {
+               event = list_first_entry_or_null(&cpts->pool,
+                                                struct am65_cpts_event, list);
+
+               if (!event) {
+                       if (am65_cpts_cpts_purge_events(cpts)) {
+                               dev_err(cpts->dev, "cpts: event pool empty\n");
+                               ret = -1;
+                               goto out;
+                       }
+                       continue;
+               }
+
+               if (am65_cpts_fifo_pop_event(cpts, event))
+                       break;
+
+               type = am65_cpts_event_get_type(event);
+               switch (type) {
+               case AM65_CPTS_EV_PUSH:
+                       cpts->timestamp = event->timestamp;
+                       dev_dbg(cpts->dev, "AM65_CPTS_EV_PUSH t:%llu\n",
+                               cpts->timestamp);
+                       break;
+               case AM65_CPTS_EV_RX:
+               case AM65_CPTS_EV_TX:
+                       event->tmo = jiffies +
+                               msecs_to_jiffies(AM65_CPTS_EVENT_RX_TX_TIMEOUT);
+
+                       list_del_init(&event->list);
+                       list_add_tail(&event->list, &cpts->events);
+
+                       dev_dbg(cpts->dev,
+                               "AM65_CPTS_EV_TX e1:%08x e2:%08x t:%lld\n",
+                               event->event1, event->event2,
+                               event->timestamp);
+                       schedule = true;
+                       break;
+               case AM65_CPTS_EV_HW:
+                       pevent.index = am65_cpts_event_get_port(event) - 1;
+                       pevent.timestamp = event->timestamp;
+                       pevent.type = PTP_CLOCK_EXTTS;
+                       dev_dbg(cpts->dev, "AM65_CPTS_EV_HW p:%d t:%llu\n",
+                               pevent.index, event->timestamp);
+
+                       ptp_clock_event(cpts->ptp_clock, &pevent);
+                       break;
+               case AM65_CPTS_EV_HOST:
+                       break;
+               case AM65_CPTS_EV_ROLL:
+               case AM65_CPTS_EV_HALF:
+               case AM65_CPTS_EV_TS_COMP:
+                       dev_dbg(cpts->dev,
+                               "AM65_CPTS_EVT: %d e1:%08x e2:%08x t:%lld\n",
+                               type,
+                               event->event1, event->event2,
+                               event->timestamp);
+                       break;
+               default:
+                       dev_err(cpts->dev, "cpts: unknown event type\n");
+                       ret = -1;
+                       goto out;
+               }
+       }
+
+out:
+       spin_unlock_irqrestore(&cpts->lock, flags);
+
+       if (schedule)
+               ptp_schedule_worker(cpts->ptp_clock, 0);
+
+       return ret;
+}
+
+static u64 am65_cpts_gettime(struct am65_cpts *cpts,
+                            struct ptp_system_timestamp *sts)
+{
+       unsigned long flags;
+       u64 val = 0;
+
+       /* temporarily disable cpts interrupt to avoid intentional
+        * doubled read. Interrupt can be in-flight - it's Ok.
+        */
+       am65_cpts_write32(cpts, 0, int_enable);
+
+       /* use spin_lock_irqsave() here as it has to run very fast */
+       spin_lock_irqsave(&cpts->lock, flags);
+       ptp_read_system_prets(sts);
+       am65_cpts_write32(cpts, AM65_CPTS_TS_PUSH, ts_push);
+       am65_cpts_read32(cpts, ts_push);
+       ptp_read_system_postts(sts);
+       spin_unlock_irqrestore(&cpts->lock, flags);
+
+       am65_cpts_fifo_read(cpts);
+
+       am65_cpts_write32(cpts, AM65_CPTS_INT_ENABLE_TS_PEND_EN, int_enable);
+
+       val = cpts->timestamp;
+
+       return val;
+}
+
+static irqreturn_t am65_cpts_interrupt(int irq, void *dev_id)
+{
+       struct am65_cpts *cpts = dev_id;
+
+       if (am65_cpts_fifo_read(cpts))
+               dev_dbg(cpts->dev, "cpts: unable to obtain a time stamp\n");
+
+       return IRQ_HANDLED;
+}
+
+/* PTP clock operations */
+static int am65_cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+       struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);
+       int neg_adj = 0;
+       u64 adj_period;
+       u32 val;
+
+       if (ppb < 0) {
+               neg_adj = 1;
+               ppb = -ppb;
+       }
+
+       /* base freq = 1GHz = 1 000 000 000
+        * ppb_norm = ppb * base_freq / clock_freq;
+        * ppm_norm = ppb_norm / 1000
+        * adj_period = 1 000 000 / ppm_norm
+        * adj_period = 1 000 000 000 / ppb_norm
+        * adj_period = 1 000 000 000 / (ppb * base_freq / clock_freq)
+        * adj_period = (1 000 000 000 * clock_freq) / (ppb * base_freq)
+        * adj_period = clock_freq / ppb
+        */
+       adj_period = div_u64(cpts->refclk_freq, ppb);
+
+       mutex_lock(&cpts->ptp_clk_lock);
+
+       val = am65_cpts_read32(cpts, control);
+       if (neg_adj)
+               val |= AM65_CPTS_CONTROL_TS_PPM_DIR;
+       else
+               val &= ~AM65_CPTS_CONTROL_TS_PPM_DIR;
+       am65_cpts_write32(cpts, val, control);
+
+       val = upper_32_bits(adj_period) & 0x3FF;
+       am65_cpts_write32(cpts, val, ts_ppm_hi);
+       val = lower_32_bits(adj_period);
+       am65_cpts_write32(cpts, val, ts_ppm_low);
+
+       mutex_unlock(&cpts->ptp_clk_lock);
+
+       return 0;
+}
+
+static int am65_cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+       struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);
+       s64 ns;
+
+       mutex_lock(&cpts->ptp_clk_lock);
+       ns = am65_cpts_gettime(cpts, NULL);
+       ns += delta;
+       am65_cpts_settime(cpts, ns);
+       mutex_unlock(&cpts->ptp_clk_lock);
+
+       return 0;
+}
+
+static int am65_cpts_ptp_gettimex(struct ptp_clock_info *ptp,
+                                 struct timespec64 *ts,
+                                 struct ptp_system_timestamp *sts)
+{
+       struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);
+       u64 ns;
+
+       mutex_lock(&cpts->ptp_clk_lock);
+       ns = am65_cpts_gettime(cpts, sts);
+       mutex_unlock(&cpts->ptp_clk_lock);
+       *ts = ns_to_timespec64(ns);
+
+       return 0;
+}
+
+static int am65_cpts_ptp_settime(struct ptp_clock_info *ptp,
+                                const struct timespec64 *ts)
+{
+       struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);
+       u64 ns;
+
+       ns = timespec64_to_ns(ts);
+       mutex_lock(&cpts->ptp_clk_lock);
+       am65_cpts_settime(cpts, ns);
+       mutex_unlock(&cpts->ptp_clk_lock);
+
+       return 0;
+}
+
+static void am65_cpts_extts_enable_hw(struct am65_cpts *cpts, u32 index, int on)
+{
+       u32 v;
+
+       v = am65_cpts_read32(cpts, control);
+       if (on) {
+               v |= BIT(AM65_CPTS_CONTROL_HW1_TS_PUSH_OFFSET + index);
+               cpts->hw_ts_enable |= BIT(index);
+       } else {
+               v &= ~BIT(AM65_CPTS_CONTROL_HW1_TS_PUSH_OFFSET + index);
+               cpts->hw_ts_enable &= ~BIT(index);
+       }
+       am65_cpts_write32(cpts, v, control);
+}
+
+static int am65_cpts_extts_enable(struct am65_cpts *cpts, u32 index, int on)
+{
+       if (!!(cpts->hw_ts_enable & BIT(index)) == !!on)
+               return 0;
+
+       mutex_lock(&cpts->ptp_clk_lock);
+       am65_cpts_extts_enable_hw(cpts, index, on);
+       mutex_unlock(&cpts->ptp_clk_lock);
+
+       dev_dbg(cpts->dev, "%s: ExtTS:%u %s\n",
+               __func__, index, on ? "enabled" : "disabled");
+
+       return 0;
+}
+
+static void am65_cpts_perout_enable_hw(struct am65_cpts *cpts,
+                                      struct ptp_perout_request *req, int on)
+{
+       u64 ns_period, ns_start, cycles;
+       struct timespec64 ts;
+       u32 val;
+
+       if (on) {
+               ts.tv_sec = req->period.sec;
+               ts.tv_nsec = req->period.nsec;
+               ns_period = timespec64_to_ns(&ts);
+
+               cycles = (ns_period * cpts->refclk_freq) / NSEC_PER_SEC;
+
+               ts.tv_sec = req->start.sec;
+               ts.tv_nsec = req->start.nsec;
+               ns_start = timespec64_to_ns(&ts);
+
+               val = upper_32_bits(ns_start);
+               am65_cpts_write32(cpts, val, genf[req->index].comp_hi);
+               val = lower_32_bits(ns_start);
+               am65_cpts_write32(cpts, val, genf[req->index].comp_lo);
+               val = lower_32_bits(cycles);
+               am65_cpts_write32(cpts, val, genf[req->index].length);
+
+               cpts->genf_enable |= BIT(req->index);
+       } else {
+               am65_cpts_write32(cpts, 0, genf[req->index].length);
+
+               cpts->genf_enable &= ~BIT(req->index);
+       }
+}
+
+static int am65_cpts_perout_enable(struct am65_cpts *cpts,
+                                  struct ptp_perout_request *req, int on)
+{
+       if (!!(cpts->genf_enable & BIT(req->index)) == !!on)
+               return 0;
+
+       mutex_lock(&cpts->ptp_clk_lock);
+       am65_cpts_perout_enable_hw(cpts, req, on);
+       mutex_unlock(&cpts->ptp_clk_lock);
+
+       dev_dbg(cpts->dev, "%s: GenF:%u %s\n",
+               __func__, req->index, on ? "enabled" : "disabled");
+
+       return 0;
+}
+
+static int am65_cpts_ptp_enable(struct ptp_clock_info *ptp,
+                               struct ptp_clock_request *rq, int on)
+{
+       struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);
+
+       switch (rq->type) {
+       case PTP_CLK_REQ_EXTTS:
+               return am65_cpts_extts_enable(cpts, rq->extts.index, on);
+       case PTP_CLK_REQ_PEROUT:
+               return am65_cpts_perout_enable(cpts, &rq->perout, on);
+       default:
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static long am65_cpts_ts_work(struct ptp_clock_info *ptp);
+
+static struct ptp_clock_info am65_ptp_info = {
+       .owner          = THIS_MODULE,
+       .name           = "CTPS timer",
+       .adjfreq        = am65_cpts_ptp_adjfreq,
+       .adjtime        = am65_cpts_ptp_adjtime,
+       .gettimex64     = am65_cpts_ptp_gettimex,
+       .settime64      = am65_cpts_ptp_settime,
+       .enable         = am65_cpts_ptp_enable,
+       .do_aux_work    = am65_cpts_ts_work,
+};
+
+static bool am65_cpts_match_tx_ts(struct am65_cpts *cpts,
+                                 struct am65_cpts_event *event)
+{
+       struct sk_buff_head txq_list;
+       struct sk_buff *skb, *tmp;
+       unsigned long flags;
+       bool found = false;
+       u32 mtype_seqid;
+
+       mtype_seqid = event->event1 &
+                     (AM65_CPTS_EVENT_1_MESSAGE_TYPE_MASK |
+                      AM65_CPTS_EVENT_1_EVENT_TYPE_MASK |
+                      AM65_CPTS_EVENT_1_SEQUENCE_ID_MASK);
+
+       __skb_queue_head_init(&txq_list);
+
+       spin_lock_irqsave(&cpts->txq.lock, flags);
+       skb_queue_splice_init(&cpts->txq, &txq_list);
+       spin_unlock_irqrestore(&cpts->txq.lock, flags);
+
+       /* no need to grab txq.lock as access is always done under cpts->lock */
+       skb_queue_walk_safe(&txq_list, skb, tmp) {
+               struct skb_shared_hwtstamps ssh;
+               struct am65_cpts_skb_cb_data *skb_cb =
+                                       (struct am65_cpts_skb_cb_data *)skb->cb;
+
+               if (mtype_seqid == skb_cb->skb_mtype_seqid) {
+                       u64 ns = event->timestamp;
+
+                       memset(&ssh, 0, sizeof(ssh));
+                       ssh.hwtstamp = ns_to_ktime(ns);
+                       skb_tstamp_tx(skb, &ssh);
+                       found = true;
+                       __skb_unlink(skb, &txq_list);
+                       dev_consume_skb_any(skb);
+                       dev_dbg(cpts->dev,
+                               "match tx timestamp mtype_seqid %08x\n",
+                               mtype_seqid);
+                       break;
+               }
+
+               if (time_after(jiffies, skb_cb->tmo)) {
+                       /* timeout any expired skbs over 100 ms */
+                       dev_dbg(cpts->dev,
+                               "expiring tx timestamp mtype_seqid %08x\n",
+                               mtype_seqid);
+                       __skb_unlink(skb, &txq_list);
+                       dev_consume_skb_any(skb);
+               }
+       }
+
+       spin_lock_irqsave(&cpts->txq.lock, flags);
+       skb_queue_splice(&txq_list, &cpts->txq);
+       spin_unlock_irqrestore(&cpts->txq.lock, flags);
+
+       return found;
+}
+
+static void am65_cpts_find_ts(struct am65_cpts *cpts)
+{
+       struct am65_cpts_event *event;
+       struct list_head *this, *next;
+       LIST_HEAD(events_free);
+       unsigned long flags;
+       LIST_HEAD(events);
+
+       spin_lock_irqsave(&cpts->lock, flags);
+       list_splice_init(&cpts->events, &events);
+       spin_unlock_irqrestore(&cpts->lock, flags);
+
+       list_for_each_safe(this, next, &events) {
+               event = list_entry(this, struct am65_cpts_event, list);
+               if (am65_cpts_match_tx_ts(cpts, event) ||
+                   time_after(jiffies, event->tmo)) {
+                       list_del_init(&event->list);
+                       list_add(&event->list, &events_free);
+               }
+       }
+
+       spin_lock_irqsave(&cpts->lock, flags);
+       list_splice_tail(&events, &cpts->events);
+       list_splice_tail(&events_free, &cpts->pool);
+       spin_unlock_irqrestore(&cpts->lock, flags);
+}
+
+static long am65_cpts_ts_work(struct ptp_clock_info *ptp)
+{
+       struct am65_cpts *cpts = container_of(ptp, struct am65_cpts, ptp_info);
+       unsigned long flags;
+       long delay = -1;
+
+       am65_cpts_find_ts(cpts);
+
+       spin_lock_irqsave(&cpts->txq.lock, flags);
+       if (!skb_queue_empty(&cpts->txq))
+               delay = AM65_CPTS_SKB_TX_WORK_TIMEOUT;
+       spin_unlock_irqrestore(&cpts->txq.lock, flags);
+
+       return delay;
+}
+
+/**
+ * am65_cpts_rx_enable - enable rx timestamping
+ * @cpts: cpts handle
+ * @skb: packet
+ *
+ * This functions enables rx packets timestamping. The CPTS can timestamp all
+ * rx packets.
+ */
+void am65_cpts_rx_enable(struct am65_cpts *cpts, bool en)
+{
+       u32 val;
+
+       mutex_lock(&cpts->ptp_clk_lock);
+       val = am65_cpts_read32(cpts, control);
+       if (en)
+               val |= AM65_CPTS_CONTROL_TSTAMP_EN;
+       else
+               val &= ~AM65_CPTS_CONTROL_TSTAMP_EN;
+       am65_cpts_write32(cpts, val, control);
+       mutex_unlock(&cpts->ptp_clk_lock);
+}
+EXPORT_SYMBOL_GPL(am65_cpts_rx_enable);
+
+static int am65_skb_get_mtype_seqid(struct sk_buff *skb, u32 *mtype_seqid)
+{
+       unsigned int ptp_class = ptp_classify_raw(skb);
+       u8 *msgtype, *data = skb->data;
+       unsigned int offset = 0;
+       __be16 *seqid;
+
+       if (ptp_class == PTP_CLASS_NONE)
+               return 0;
+
+       if (ptp_class & PTP_CLASS_VLAN)
+               offset += VLAN_HLEN;
+
+       switch (ptp_class & PTP_CLASS_PMASK) {
+       case PTP_CLASS_IPV4:
+               offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN;
+               break;
+       case PTP_CLASS_IPV6:
+               offset += ETH_HLEN + IP6_HLEN + UDP_HLEN;
+               break;
+       case PTP_CLASS_L2:
+               offset += ETH_HLEN;
+               break;
+       default:
+               return 0;
+       }
+
+       if (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID + sizeof(*seqid))
+               return 0;
+
+       if (unlikely(ptp_class & PTP_CLASS_V1))
+               msgtype = data + offset + OFF_PTP_CONTROL;
+       else
+               msgtype = data + offset;
+
+       seqid = (__be16 *)(data + offset + OFF_PTP_SEQUENCE_ID);
+       *mtype_seqid = (*msgtype << AM65_CPTS_EVENT_1_MESSAGE_TYPE_SHIFT) &
+                       AM65_CPTS_EVENT_1_MESSAGE_TYPE_MASK;
+       *mtype_seqid |= (ntohs(*seqid) & AM65_CPTS_EVENT_1_SEQUENCE_ID_MASK);
+
+       return 1;
+}
+
+/**
+ * am65_cpts_tx_timestamp - save tx packet for timestamping
+ * @cpts: cpts handle
+ * @skb: packet
+ *
+ * This functions saves tx packet for timestamping if packet can be timestamped.
+ * The future processing is done in from PTP auxiliary worker.
+ */
+void am65_cpts_tx_timestamp(struct am65_cpts *cpts, struct sk_buff *skb)
+{
+       struct am65_cpts_skb_cb_data *skb_cb = (void *)skb->cb;
+
+       if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))
+               return;
+
+       /* add frame to queue for processing later.
+        * The periodic FIFO check will handle this.
+        */
+       skb_get(skb);
+       /* get the timestamp for timeouts */
+       skb_cb->tmo = jiffies + msecs_to_jiffies(100);
+       skb_queue_tail(&cpts->txq, skb);
+       ptp_schedule_worker(cpts->ptp_clock, 0);
+}
+EXPORT_SYMBOL_GPL(am65_cpts_tx_timestamp);
+
+/**
+ * am65_cpts_prep_tx_timestamp - check and prepare tx packet for timestamping
+ * @cpts: cpts handle
+ * @skb: packet
+ *
+ * This functions should be called from .xmit().
+ * It checks if packet can be timestamped, fills internal cpts data
+ * in skb-cb and marks packet as SKBTX_IN_PROGRESS.
+ */
+void am65_cpts_prep_tx_timestamp(struct am65_cpts *cpts, struct sk_buff *skb)
+{
+       struct am65_cpts_skb_cb_data *skb_cb = (void *)skb->cb;
+       int ret;
+
+       if (!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
+               return;
+
+       ret = am65_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid);
+       if (!ret)
+               return;
+       skb_cb->skb_mtype_seqid |= (AM65_CPTS_EV_TX <<
+                                  AM65_CPTS_EVENT_1_EVENT_TYPE_SHIFT);
+
+       skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+}
+EXPORT_SYMBOL_GPL(am65_cpts_prep_tx_timestamp);
+
+int am65_cpts_phc_index(struct am65_cpts *cpts)
+{
+       return cpts->phc_index;
+}
+EXPORT_SYMBOL_GPL(am65_cpts_phc_index);
+
+static void cpts_free_clk_mux(void *data)
+{
+       struct am65_cpts *cpts = data;
+
+       of_clk_del_provider(cpts->clk_mux_np);
+       clk_hw_unregister_mux(cpts->clk_mux_hw);
+       of_node_put(cpts->clk_mux_np);
+}
+
+static int cpts_of_mux_clk_setup(struct am65_cpts *cpts,
+                                struct device_node *node)
+{
+       unsigned int num_parents;
+       const char **parent_names;
+       char *clk_mux_name;
+       void __iomem *reg;
+       int ret = -EINVAL;
+
+       cpts->clk_mux_np = of_get_child_by_name(node, "refclk-mux");
+       if (!cpts->clk_mux_np)
+               return 0;
+
+       num_parents = of_clk_get_parent_count(cpts->clk_mux_np);
+       if (num_parents < 1) {
+               dev_err(cpts->dev, "mux-clock %pOF must have parents\n",
+                       cpts->clk_mux_np);
+               goto mux_fail;
+       }
+
+       parent_names = devm_kcalloc(cpts->dev, sizeof(char *), num_parents,
+                                   GFP_KERNEL);
+       if (!parent_names) {
+               ret = -ENOMEM;
+               goto mux_fail;
+       }
+
+       of_clk_parent_fill(cpts->clk_mux_np, parent_names, num_parents);
+
+       clk_mux_name = devm_kasprintf(cpts->dev, GFP_KERNEL, "%s.%pOFn",
+                                     dev_name(cpts->dev), cpts->clk_mux_np);
+       if (!clk_mux_name) {
+               ret = -ENOMEM;
+               goto mux_fail;
+       }
+
+       reg = &cpts->reg->rftclk_sel;
+       /* dev must be NULL to avoid recursive incrementing
+        * of module refcnt
+        */
+       cpts->clk_mux_hw = clk_hw_register_mux(NULL, clk_mux_name,
+                                              parent_names, num_parents,
+                                              0, reg, 0, 5, 0, NULL);
+       if (IS_ERR(cpts->clk_mux_hw)) {
+               ret = PTR_ERR(cpts->clk_mux_hw);
+               goto mux_fail;
+       }
+
+       ret = of_clk_add_hw_provider(cpts->clk_mux_np, of_clk_hw_simple_get,
+                                    cpts->clk_mux_hw);
+       if (ret)
+               goto clk_hw_register;
+
+       ret = devm_add_action_or_reset(cpts->dev, cpts_free_clk_mux, cpts);
+       if (ret)
+               dev_err(cpts->dev, "failed to add clkmux reset action %d", ret);
+
+       return ret;
+
+clk_hw_register:
+       clk_hw_unregister_mux(cpts->clk_mux_hw);
+mux_fail:
+       of_node_put(cpts->clk_mux_np);
+       return ret;
+}
+
+static int am65_cpts_of_parse(struct am65_cpts *cpts, struct device_node *node)
+{
+       u32 prop[2];
+
+       if (!of_property_read_u32(node, "ti,cpts-ext-ts-inputs", &prop[0]))
+               cpts->ext_ts_inputs = prop[0];
+
+       if (!of_property_read_u32(node, "ti,cpts-periodic-outputs", &prop[0]))
+               cpts->genf_num = prop[0];
+
+       return cpts_of_mux_clk_setup(cpts, node);
+}
+
+static void am65_cpts_release(void *data)
+{
+       struct am65_cpts *cpts = data;
+
+       ptp_clock_unregister(cpts->ptp_clock);
+       am65_cpts_disable(cpts);
+       clk_disable_unprepare(cpts->refclk);
+}
+
+struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs,
+                                  struct device_node *node)
+{
+       struct am65_cpts *cpts;
+       int ret, i;
+
+       cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL);
+       if (!cpts)
+               return ERR_PTR(-ENOMEM);
+
+       cpts->dev = dev;
+       cpts->reg = (struct am65_cpts_regs __iomem *)regs;
+
+       cpts->irq = of_irq_get_byname(node, "cpts");
+       if (cpts->irq <= 0) {
+               ret = cpts->irq ?: -ENXIO;
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Failed to get IRQ number (err = %d)\n",
+                               ret);
+               return ERR_PTR(ret);
+       }
+
+       ret = am65_cpts_of_parse(cpts, node);
+       if (ret)
+               return ERR_PTR(ret);
+
+       mutex_init(&cpts->ptp_clk_lock);
+       INIT_LIST_HEAD(&cpts->events);
+       INIT_LIST_HEAD(&cpts->pool);
+       spin_lock_init(&cpts->lock);
+       skb_queue_head_init(&cpts->txq);
+
+       for (i = 0; i < AM65_CPTS_MAX_EVENTS; i++)
+               list_add(&cpts->pool_data[i].list, &cpts->pool);
+
+       cpts->refclk = devm_get_clk_from_child(dev, node, "cpts");
+       if (IS_ERR(cpts->refclk)) {
+               ret = PTR_ERR(cpts->refclk);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Failed to get refclk %d\n", ret);
+               return ERR_PTR(ret);
+       }
+
+       ret = clk_prepare_enable(cpts->refclk);
+       if (ret) {
+               dev_err(dev, "Failed to enable refclk %d\n", ret);
+               return ERR_PTR(ret);
+       }
+
+       cpts->refclk_freq = clk_get_rate(cpts->refclk);
+
+       am65_ptp_info.max_adj = cpts->refclk_freq / AM65_CPTS_MIN_PPM;
+       cpts->ptp_info = am65_ptp_info;
+
+       if (cpts->ext_ts_inputs)
+               cpts->ptp_info.n_ext_ts = cpts->ext_ts_inputs;
+       if (cpts->genf_num)
+               cpts->ptp_info.n_per_out = cpts->genf_num;
+
+       am65_cpts_set_add_val(cpts);
+
+       am65_cpts_write32(cpts, AM65_CPTS_CONTROL_EN | AM65_CPTS_CONTROL_64MODE,
+                         control);
+       am65_cpts_write32(cpts, AM65_CPTS_INT_ENABLE_TS_PEND_EN, int_enable);
+
+       /* set time to the current system time */
+       am65_cpts_settime(cpts, ktime_to_ns(ktime_get_real()));
+
+       cpts->ptp_clock = ptp_clock_register(&cpts->ptp_info, cpts->dev);
+       if (IS_ERR_OR_NULL(cpts->ptp_clock)) {
+               dev_err(dev, "Failed to register ptp clk %ld\n",
+                       PTR_ERR(cpts->ptp_clock));
+               if (!cpts->ptp_clock)
+                       ret = -ENODEV;
+               goto refclk_disable;
+       }
+       cpts->phc_index = ptp_clock_index(cpts->ptp_clock);
+
+       ret = devm_add_action_or_reset(dev, am65_cpts_release, cpts);
+       if (ret) {
+               dev_err(dev, "failed to add ptpclk reset action %d", ret);
+               return ERR_PTR(ret);
+       }
+
+       ret = devm_request_threaded_irq(dev, cpts->irq, NULL,
+                                       am65_cpts_interrupt,
+                                       IRQF_ONESHOT, dev_name(dev), cpts);
+       if (ret < 0) {
+               dev_err(cpts->dev, "error attaching irq %d\n", ret);
+               return ERR_PTR(ret);
+       }
+
+       dev_info(dev, "CPTS ver 0x%08x, freq:%u, add_val:%u\n",
+                am65_cpts_read32(cpts, idver),
+                cpts->refclk_freq, cpts->ts_add_val);
+
+       return cpts;
+
+refclk_disable:
+       clk_disable_unprepare(cpts->refclk);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(am65_cpts_create);
+
+static int am65_cpts_probe(struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct device *dev = &pdev->dev;
+       struct am65_cpts *cpts;
+       struct resource *res;
+       void __iomem *base;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cpts");
+       base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       cpts = am65_cpts_create(dev, base, node);
+       return PTR_ERR_OR_ZERO(cpts);
+}
+
+static const struct of_device_id am65_cpts_of_match[] = {
+       { .compatible = "ti,am65-cpts", },
+       { .compatible = "ti,j721e-cpts", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, am65_cpts_of_match);
+
+static struct platform_driver am65_cpts_driver = {
+       .probe          = am65_cpts_probe,
+       .driver         = {
+               .name   = "am65-cpts",
+               .of_match_table = am65_cpts_of_match,
+       },
+};
+module_platform_driver(am65_cpts_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Grygorii Strashko <grygorii.strashko@ti.com>");
+MODULE_DESCRIPTION("TI K3 AM65 CPTS driver");
diff --git a/drivers/net/ethernet/ti/am65-cpts.h b/drivers/net/ethernet/ti/am65-cpts.h
new file mode 100644 (file)
index 0000000..0b55dc1
--- /dev/null
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* TI K3 AM65 CPTS driver interface
+ *
+ * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com
+ */
+
+#ifndef K3_CPTS_H_
+#define K3_CPTS_H_
+
+#include <linux/device.h>
+#include <linux/of.h>
+
+struct am65_cpts;
+
+#if IS_ENABLED(CONFIG_TI_K3_AM65_CPTS)
+struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs,
+                                  struct device_node *node);
+int am65_cpts_phc_index(struct am65_cpts *cpts);
+void am65_cpts_tx_timestamp(struct am65_cpts *cpts, struct sk_buff *skb);
+void am65_cpts_prep_tx_timestamp(struct am65_cpts *cpts, struct sk_buff *skb);
+void am65_cpts_rx_enable(struct am65_cpts *cpts, bool en);
+#else
+static inline struct am65_cpts *am65_cpts_create(struct device *dev,
+                                                void __iomem *regs,
+                                                struct device_node *node)
+{
+       return ERR_PTR(-EOPNOTSUPP);
+}
+
+static inline int am65_cpts_phc_index(struct am65_cpts *cpts)
+{
+       return -1;
+}
+
+static inline void am65_cpts_tx_timestamp(struct am65_cpts *cpts,
+                                         struct sk_buff *skb)
+{
+}
+
+static inline void am65_cpts_prep_tx_timestamp(struct am65_cpts *cpts,
+                                              struct sk_buff *skb)
+{
+}
+
+static inline void am65_cpts_rx_enable(struct am65_cpts *cpts, bool en)
+{
+}
+#endif
+
+#endif /* K3_CPTS_H_ */
index a530afe..c207151 100644 (file)
@@ -532,7 +532,7 @@ fatal_error:
 
 }
 
-static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        int queue;
        unsigned int len;
index c2c5bf8..09f98fa 100644 (file)
@@ -1569,6 +1569,12 @@ static int cpsw_probe(struct platform_device *pdev)
                return irq;
        cpsw->irqs_table[1] = irq;
 
+       /* get misc irq*/
+       irq = platform_get_irq(pdev, 3);
+       if (irq <= 0)
+               return irq;
+       cpsw->misc_irq = irq;
+
        /*
         * This may be required here for child devices.
         */
@@ -1703,6 +1709,21 @@ static int cpsw_probe(struct platform_device *pdev)
                goto clean_unregister_netdev_ret;
        }
 
+       if (!cpsw->cpts)
+               goto skip_cpts;
+
+       ret = devm_request_irq(&pdev->dev, cpsw->misc_irq, cpsw_misc_interrupt,
+                              0, dev_name(&pdev->dev), cpsw);
+       if (ret < 0) {
+               dev_err(dev, "error attaching misc irq (%d)\n", ret);
+               goto clean_unregister_netdev_ret;
+       }
+
+       /* Enable misc CPTS evnt_pend IRQ */
+       cpts_set_irqpoll(cpsw->cpts, false);
+       writel(0x10, &cpsw->wr_regs->misc_en);
+
+skip_cpts:
        cpsw_notice(priv, probe,
                    "initialized device (regs %pa, irq %d, pool size %d)\n",
                    &ss_res->start, cpsw->irqs_table[0], descs_pool_size);
index 9209e61..dce4931 100644 (file)
@@ -1228,7 +1228,7 @@ static int cpsw_probe_dt(struct cpsw_common *cpsw)
        data->active_slave = 0;
        data->channels = CPSW_MAX_QUEUES;
        data->ale_entries = CPSW_ALE_NUM_ENTRIES;
-       data->dual_emac = 1;
+       data->dual_emac = true;
        data->bd_ram_size = CPSW_BD_RAM_SIZE;
        data->mac_control = 0;
 
@@ -1896,6 +1896,11 @@ static int cpsw_probe(struct platform_device *pdev)
                return irq;
        cpsw->irqs_table[1] = irq;
 
+       irq = platform_get_irq_byname(pdev, "misc");
+       if (irq <= 0)
+               return irq;
+       cpsw->misc_irq = irq;
+
        platform_set_drvdata(pdev, cpsw);
        /* This may be required here for child devices. */
        pm_runtime_enable(dev);
@@ -1916,7 +1921,7 @@ static int cpsw_probe(struct platform_device *pdev)
 
        soc = soc_device_match(cpsw_soc_devices);
        if (soc)
-               cpsw->quirk_irq = 1;
+               cpsw->quirk_irq = true;
 
        cpsw->rx_packet_max = rx_packet_max;
        cpsw->descs_pool_size = descs_pool_size;
@@ -1975,6 +1980,21 @@ static int cpsw_probe(struct platform_device *pdev)
                goto clean_unregister_netdev;
        }
 
+       if (!cpsw->cpts)
+               goto skip_cpts;
+
+       ret = devm_request_irq(dev, cpsw->misc_irq, cpsw_misc_interrupt,
+                              0, dev_name(&pdev->dev), cpsw);
+       if (ret < 0) {
+               dev_err(dev, "error attaching misc irq (%d)\n", ret);
+               goto clean_unregister_netdev;
+       }
+
+       /* Enable misc CPTS evnt_pend IRQ */
+       cpts_set_irqpoll(cpsw->cpts, false);
+       writel(0x10, &cpsw->wr_regs->misc_en);
+
+skip_cpts:
        ret = cpsw_register_notifiers(cpsw);
        if (ret)
                goto clean_unregister_netdev;
index 97a058c..9d098c8 100644 (file)
@@ -28,6 +28,8 @@
 #include "cpsw_sl.h"
 #include "davinci_cpdma.h"
 
+#define CPTS_N_ETX_TS 4
+
 int (*cpsw_slave_index)(struct cpsw_common *cpsw, struct cpsw_priv *priv);
 
 void cpsw_intr_enable(struct cpsw_common *cpsw)
@@ -112,6 +114,18 @@ irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+irqreturn_t cpsw_misc_interrupt(int irq, void *dev_id)
+{
+       struct cpsw_common *cpsw = dev_id;
+
+       writel(0, &cpsw->wr_regs->misc_en);
+       cpdma_ctlr_eoi(cpsw->dma, CPDMA_EOI_MISC);
+       cpts_misc_interrupt(cpsw->cpts);
+       writel(0x10, &cpsw->wr_regs->misc_en);
+
+       return IRQ_HANDLED;
+}
+
 int cpsw_tx_mq_poll(struct napi_struct *napi_tx, int budget)
 {
        struct cpsw_common      *cpsw = napi_to_cpsw(napi_tx);
@@ -522,7 +536,8 @@ int cpsw_init_common(struct cpsw_common *cpsw, void __iomem *ss_regs,
        if (!cpts_node)
                cpts_node = cpsw->dev->of_node;
 
-       cpsw->cpts = cpts_create(cpsw->dev, cpts_regs, cpts_node);
+       cpsw->cpts = cpts_create(cpsw->dev, cpts_regs, cpts_node,
+                                CPTS_N_ETX_TS);
        if (IS_ERR(cpsw->cpts)) {
                ret = PTR_ERR(cpsw->cpts);
                cpdma_ctlr_destroy(cpsw->dma);
index b8d7b92..bf4e179 100644 (file)
@@ -350,6 +350,7 @@ struct cpsw_common {
        bool                            rx_irq_disabled;
        bool                            tx_irq_disabled;
        u32 irqs_table[IRQ_NUM];
+       int misc_irq;
        struct cpts                     *cpts;
        struct devlink *devlink;
        int                             rx_ch_num, tx_ch_num;
@@ -442,6 +443,7 @@ int cpsw_run_xdp(struct cpsw_priv *priv, int ch, struct xdp_buff *xdp,
                 struct page *page, int port);
 irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id);
 irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id);
+irqreturn_t cpsw_misc_interrupt(int irq, void *dev_id);
 int cpsw_tx_mq_poll(struct napi_struct *napi_tx, int budget);
 int cpsw_tx_poll(struct napi_struct *napi_tx, int budget);
 int cpsw_rx_mq_poll(struct napi_struct *napi_rx, int budget);
index 729ce09..7c55d39 100644 (file)
 #include "cpts.h"
 
 #define CPTS_SKB_TX_WORK_TIMEOUT 1 /* jiffies */
+#define CPTS_SKB_RX_TX_TMO 100 /*ms */
+#define CPTS_EVENT_RX_TX_TIMEOUT (100) /* ms */
 
 struct cpts_skb_cb_data {
+       u32 skb_mtype_seqid;
        unsigned long tmo;
 };
 
 #define cpts_read32(c, r)      readl_relaxed(&c->reg->r)
 #define cpts_write32(c, v, r)  writel_relaxed(v, &c->reg->r)
 
-static int cpts_match(struct sk_buff *skb, unsigned int ptp_class,
-                     u16 ts_seqid, u8 ts_msgtype);
+static int cpts_event_port(struct cpts_event *event)
+{
+       return (event->high >> PORT_NUMBER_SHIFT) & PORT_NUMBER_MASK;
+}
 
 static int event_expired(struct cpts_event *event)
 {
@@ -71,7 +76,7 @@ static int cpts_purge_events(struct cpts *cpts)
        }
 
        if (removed)
-               pr_debug("cpts: event pool cleaned up %d\n", removed);
+               dev_dbg(cpts->dev, "cpts: event pool cleaned up %d\n", removed);
        return removed ? 0 : -1;
 }
 
@@ -94,132 +99,126 @@ static void cpts_purge_txq(struct cpts *cpts)
                dev_dbg(cpts->dev, "txq cleaned up %d\n", removed);
 }
 
-static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event)
-{
-       struct sk_buff *skb, *tmp;
-       u16 seqid;
-       u8 mtype;
-       bool found = false;
-
-       mtype = (event->high >> MESSAGE_TYPE_SHIFT) & MESSAGE_TYPE_MASK;
-       seqid = (event->high >> SEQUENCE_ID_SHIFT) & SEQUENCE_ID_MASK;
-
-       /* no need to grab txq.lock as access is always done under cpts->lock */
-       skb_queue_walk_safe(&cpts->txq, skb, tmp) {
-               struct skb_shared_hwtstamps ssh;
-               unsigned int class = ptp_classify_raw(skb);
-               struct cpts_skb_cb_data *skb_cb =
-                                       (struct cpts_skb_cb_data *)skb->cb;
-
-               if (cpts_match(skb, class, seqid, mtype)) {
-                       u64 ns = timecounter_cyc2time(&cpts->tc, event->low);
-
-                       memset(&ssh, 0, sizeof(ssh));
-                       ssh.hwtstamp = ns_to_ktime(ns);
-                       skb_tstamp_tx(skb, &ssh);
-                       found = true;
-                       __skb_unlink(skb, &cpts->txq);
-                       dev_consume_skb_any(skb);
-                       dev_dbg(cpts->dev, "match tx timestamp mtype %u seqid %04x\n",
-                               mtype, seqid);
-                       break;
-               }
-
-               if (time_after(jiffies, skb_cb->tmo)) {
-                       /* timeout any expired skbs over 1s */
-                       dev_dbg(cpts->dev, "expiring tx timestamp from txq\n");
-                       __skb_unlink(skb, &cpts->txq);
-                       dev_consume_skb_any(skb);
-               }
-       }
-
-       return found;
-}
-
 /*
  * Returns zero if matching event type was found.
  */
 static int cpts_fifo_read(struct cpts *cpts, int match)
 {
+       struct ptp_clock_event pevent;
+       bool need_schedule = false;
+       struct cpts_event *event;
+       unsigned long flags;
        int i, type = -1;
        u32 hi, lo;
-       struct cpts_event *event;
+
+       spin_lock_irqsave(&cpts->lock, flags);
 
        for (i = 0; i < CPTS_FIFO_DEPTH; i++) {
                if (cpts_fifo_pop(cpts, &hi, &lo))
                        break;
 
                if (list_empty(&cpts->pool) && cpts_purge_events(cpts)) {
-                       pr_err("cpts: event pool empty\n");
-                       return -1;
+                       dev_warn(cpts->dev, "cpts: event pool empty\n");
+                       break;
                }
 
                event = list_first_entry(&cpts->pool, struct cpts_event, list);
-               event->tmo = jiffies + 2;
                event->high = hi;
                event->low = lo;
+               event->timestamp = timecounter_cyc2time(&cpts->tc, event->low);
                type = event_type(event);
+
+               dev_dbg(cpts->dev, "CPTS_EV: %d high:%08X low:%08x\n",
+                       type, event->high, event->low);
                switch (type) {
-               case CPTS_EV_TX:
-                       if (cpts_match_tx_ts(cpts, event)) {
-                               /* if the new event matches an existing skb,
-                                * then don't queue it
-                                */
-                               break;
-                       }
-                       /* fall through */
                case CPTS_EV_PUSH:
+                       WRITE_ONCE(cpts->cur_timestamp, lo);
+                       timecounter_read(&cpts->tc);
+                       if (cpts->mult_new) {
+                               cpts->cc.mult = cpts->mult_new;
+                               cpts->mult_new = 0;
+                       }
+                       if (!cpts->irq_poll)
+                               complete(&cpts->ts_push_complete);
+                       break;
+               case CPTS_EV_TX:
                case CPTS_EV_RX:
+                       event->tmo = jiffies +
+                               msecs_to_jiffies(CPTS_EVENT_RX_TX_TIMEOUT);
+
                        list_del_init(&event->list);
                        list_add_tail(&event->list, &cpts->events);
+                       need_schedule = true;
                        break;
                case CPTS_EV_ROLL:
                case CPTS_EV_HALF:
+                       break;
                case CPTS_EV_HW:
+                       pevent.timestamp = event->timestamp;
+                       pevent.type = PTP_CLOCK_EXTTS;
+                       pevent.index = cpts_event_port(event) - 1;
+                       ptp_clock_event(cpts->clock, &pevent);
                        break;
                default:
-                       pr_err("cpts: unknown event type\n");
+                       dev_err(cpts->dev, "cpts: unknown event type\n");
                        break;
                }
                if (type == match)
                        break;
        }
+
+       spin_unlock_irqrestore(&cpts->lock, flags);
+
+       if (!cpts->irq_poll && need_schedule)
+               ptp_schedule_worker(cpts->clock, 0);
+
        return type == match ? 0 : -1;
 }
 
+void cpts_misc_interrupt(struct cpts *cpts)
+{
+       cpts_fifo_read(cpts, -1);
+}
+EXPORT_SYMBOL_GPL(cpts_misc_interrupt);
+
 static u64 cpts_systim_read(const struct cyclecounter *cc)
 {
-       u64 val = 0;
-       struct cpts_event *event;
-       struct list_head *this, *next;
        struct cpts *cpts = container_of(cc, struct cpts, cc);
 
+       return READ_ONCE(cpts->cur_timestamp);
+}
+
+static void cpts_update_cur_time(struct cpts *cpts, int match,
+                                struct ptp_system_timestamp *sts)
+{
+       unsigned long flags;
+
+       reinit_completion(&cpts->ts_push_complete);
+
+       /* use spin_lock_irqsave() here as it has to run very fast */
+       spin_lock_irqsave(&cpts->lock, flags);
+       ptp_read_system_prets(sts);
        cpts_write32(cpts, TS_PUSH, ts_push);
-       if (cpts_fifo_read(cpts, CPTS_EV_PUSH))
-               pr_err("cpts: unable to obtain a time stamp\n");
+       cpts_read32(cpts, ts_push);
+       ptp_read_system_postts(sts);
+       spin_unlock_irqrestore(&cpts->lock, flags);
 
-       list_for_each_safe(this, next, &cpts->events) {
-               event = list_entry(this, struct cpts_event, list);
-               if (event_type(event) == CPTS_EV_PUSH) {
-                       list_del_init(&event->list);
-                       list_add(&event->list, &cpts->pool);
-                       val = event->low;
-                       break;
-               }
-       }
+       if (cpts->irq_poll && cpts_fifo_read(cpts, match) && match != -1)
+               dev_err(cpts->dev, "cpts: unable to obtain a time stamp\n");
 
-       return val;
+       if (!cpts->irq_poll &&
+           !wait_for_completion_timeout(&cpts->ts_push_complete, HZ))
+               dev_err(cpts->dev, "cpts: obtain a time stamp timeout\n");
 }
 
 /* PTP clock operations */
 
 static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
 {
-       u64 adj;
-       u32 diff, mult;
-       int neg_adj = 0;
-       unsigned long flags;
        struct cpts *cpts = container_of(ptp, struct cpts, info);
+       int neg_adj = 0;
+       u32 diff, mult;
+       u64 adj;
 
        if (ppb < 0) {
                neg_adj = 1;
@@ -230,38 +229,40 @@ static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
        adj *= ppb;
        diff = div_u64(adj, 1000000000ULL);
 
-       spin_lock_irqsave(&cpts->lock, flags);
+       mutex_lock(&cpts->ptp_clk_mutex);
 
-       timecounter_read(&cpts->tc);
+       cpts->mult_new = neg_adj ? mult - diff : mult + diff;
 
-       cpts->cc.mult = neg_adj ? mult - diff : mult + diff;
-
-       spin_unlock_irqrestore(&cpts->lock, flags);
+       cpts_update_cur_time(cpts, CPTS_EV_PUSH, NULL);
 
+       mutex_unlock(&cpts->ptp_clk_mutex);
        return 0;
 }
 
 static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
 {
-       unsigned long flags;
        struct cpts *cpts = container_of(ptp, struct cpts, info);
 
-       spin_lock_irqsave(&cpts->lock, flags);
+       mutex_lock(&cpts->ptp_clk_mutex);
        timecounter_adjtime(&cpts->tc, delta);
-       spin_unlock_irqrestore(&cpts->lock, flags);
+       mutex_unlock(&cpts->ptp_clk_mutex);
 
        return 0;
 }
 
-static int cpts_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
+static int cpts_ptp_gettimeex(struct ptp_clock_info *ptp,
+                             struct timespec64 *ts,
+                             struct ptp_system_timestamp *sts)
 {
-       u64 ns;
-       unsigned long flags;
        struct cpts *cpts = container_of(ptp, struct cpts, info);
+       u64 ns;
+
+       mutex_lock(&cpts->ptp_clk_mutex);
+
+       cpts_update_cur_time(cpts, CPTS_EV_PUSH, sts);
 
-       spin_lock_irqsave(&cpts->lock, flags);
        ns = timecounter_read(&cpts->tc);
-       spin_unlock_irqrestore(&cpts->lock, flags);
+       mutex_unlock(&cpts->ptp_clk_mutex);
 
        *ts = ns_to_timespec64(ns);
 
@@ -271,15 +272,38 @@ static int cpts_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
 static int cpts_ptp_settime(struct ptp_clock_info *ptp,
                            const struct timespec64 *ts)
 {
-       u64 ns;
-       unsigned long flags;
        struct cpts *cpts = container_of(ptp, struct cpts, info);
+       u64 ns;
 
        ns = timespec64_to_ns(ts);
 
-       spin_lock_irqsave(&cpts->lock, flags);
+       mutex_lock(&cpts->ptp_clk_mutex);
        timecounter_init(&cpts->tc, &cpts->cc, ns);
-       spin_unlock_irqrestore(&cpts->lock, flags);
+       mutex_unlock(&cpts->ptp_clk_mutex);
+
+       return 0;
+}
+
+static int cpts_extts_enable(struct cpts *cpts, u32 index, int on)
+{
+       u32 v;
+
+       if (((cpts->hw_ts_enable & BIT(index)) >> index) == on)
+               return 0;
+
+       mutex_lock(&cpts->ptp_clk_mutex);
+
+       v = cpts_read32(cpts, control);
+       if (on) {
+               v |= BIT(8 + index);
+               cpts->hw_ts_enable |= BIT(index);
+       } else {
+               v &= ~BIT(8 + index);
+               cpts->hw_ts_enable &= ~BIT(index);
+       }
+       cpts_write32(cpts, v, control);
+
+       mutex_unlock(&cpts->ptp_clk_mutex);
 
        return 0;
 }
@@ -287,28 +311,120 @@ static int cpts_ptp_settime(struct ptp_clock_info *ptp,
 static int cpts_ptp_enable(struct ptp_clock_info *ptp,
                           struct ptp_clock_request *rq, int on)
 {
+       struct cpts *cpts = container_of(ptp, struct cpts, info);
+
+       switch (rq->type) {
+       case PTP_CLK_REQ_EXTTS:
+               return cpts_extts_enable(cpts, rq->extts.index, on);
+       default:
+               break;
+       }
+
        return -EOPNOTSUPP;
 }
 
+static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event)
+{
+       struct sk_buff_head txq_list;
+       struct sk_buff *skb, *tmp;
+       unsigned long flags;
+       bool found = false;
+       u32 mtype_seqid;
+
+       mtype_seqid = event->high &
+                     ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) |
+                      (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) |
+                      (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT));
+
+       __skb_queue_head_init(&txq_list);
+
+       spin_lock_irqsave(&cpts->txq.lock, flags);
+       skb_queue_splice_init(&cpts->txq, &txq_list);
+       spin_unlock_irqrestore(&cpts->txq.lock, flags);
+
+       skb_queue_walk_safe(&txq_list, skb, tmp) {
+               struct skb_shared_hwtstamps ssh;
+               struct cpts_skb_cb_data *skb_cb =
+                                       (struct cpts_skb_cb_data *)skb->cb;
+
+               if (mtype_seqid == skb_cb->skb_mtype_seqid) {
+                       memset(&ssh, 0, sizeof(ssh));
+                       ssh.hwtstamp = ns_to_ktime(event->timestamp);
+                       skb_tstamp_tx(skb, &ssh);
+                       found = true;
+                       __skb_unlink(skb, &txq_list);
+                       dev_consume_skb_any(skb);
+                       dev_dbg(cpts->dev, "match tx timestamp mtype_seqid %08x\n",
+                               mtype_seqid);
+                       break;
+               }
+
+               if (time_after(jiffies, skb_cb->tmo)) {
+                       /* timeout any expired skbs over 1s */
+                       dev_dbg(cpts->dev, "expiring tx timestamp from txq\n");
+                       __skb_unlink(skb, &txq_list);
+                       dev_consume_skb_any(skb);
+               }
+       }
+
+       spin_lock_irqsave(&cpts->txq.lock, flags);
+       skb_queue_splice(&txq_list, &cpts->txq);
+       spin_unlock_irqrestore(&cpts->txq.lock, flags);
+
+       return found;
+}
+
+static void cpts_process_events(struct cpts *cpts)
+{
+       struct list_head *this, *next;
+       struct cpts_event *event;
+       LIST_HEAD(events_free);
+       unsigned long flags;
+       LIST_HEAD(events);
+
+       spin_lock_irqsave(&cpts->lock, flags);
+       list_splice_init(&cpts->events, &events);
+       spin_unlock_irqrestore(&cpts->lock, flags);
+
+       list_for_each_safe(this, next, &events) {
+               event = list_entry(this, struct cpts_event, list);
+               if (cpts_match_tx_ts(cpts, event) ||
+                   time_after(jiffies, event->tmo)) {
+                       list_del_init(&event->list);
+                       list_add(&event->list, &events_free);
+               }
+       }
+
+       spin_lock_irqsave(&cpts->lock, flags);
+       list_splice_tail(&events, &cpts->events);
+       list_splice_tail(&events_free, &cpts->pool);
+       spin_unlock_irqrestore(&cpts->lock, flags);
+}
+
 static long cpts_overflow_check(struct ptp_clock_info *ptp)
 {
        struct cpts *cpts = container_of(ptp, struct cpts, info);
        unsigned long delay = cpts->ov_check_period;
-       struct timespec64 ts;
        unsigned long flags;
+       u64 ns;
 
-       spin_lock_irqsave(&cpts->lock, flags);
-       ts = ns_to_timespec64(timecounter_read(&cpts->tc));
+       mutex_lock(&cpts->ptp_clk_mutex);
+
+       cpts_update_cur_time(cpts, -1, NULL);
+       ns = timecounter_read(&cpts->tc);
+
+       cpts_process_events(cpts);
 
+       spin_lock_irqsave(&cpts->txq.lock, flags);
        if (!skb_queue_empty(&cpts->txq)) {
                cpts_purge_txq(cpts);
                if (!skb_queue_empty(&cpts->txq))
                        delay = CPTS_SKB_TX_WORK_TIMEOUT;
        }
-       spin_unlock_irqrestore(&cpts->lock, flags);
+       spin_unlock_irqrestore(&cpts->txq.lock, flags);
 
-       pr_debug("cpts overflow check at %lld.%09ld\n",
-                (long long)ts.tv_sec, ts.tv_nsec);
+       dev_dbg(cpts->dev, "cpts overflow check at %lld\n", ns);
+       mutex_unlock(&cpts->ptp_clk_mutex);
        return (long)delay;
 }
 
@@ -321,18 +437,21 @@ static const struct ptp_clock_info cpts_info = {
        .pps            = 0,
        .adjfreq        = cpts_ptp_adjfreq,
        .adjtime        = cpts_ptp_adjtime,
-       .gettime64      = cpts_ptp_gettime,
+       .gettimex64     = cpts_ptp_gettimeex,
        .settime64      = cpts_ptp_settime,
        .enable         = cpts_ptp_enable,
        .do_aux_work    = cpts_overflow_check,
 };
 
-static int cpts_match(struct sk_buff *skb, unsigned int ptp_class,
-                     u16 ts_seqid, u8 ts_msgtype)
+static int cpts_skb_get_mtype_seqid(struct sk_buff *skb, u32 *mtype_seqid)
 {
-       u16 *seqid;
-       unsigned int offset = 0;
+       unsigned int ptp_class = ptp_classify_raw(skb);
        u8 *msgtype, *data = skb->data;
+       unsigned int offset = 0;
+       u16 *seqid;
+
+       if (ptp_class == PTP_CLASS_NONE)
+               return 0;
 
        if (ptp_class & PTP_CLASS_VLAN)
                offset += VLAN_HLEN;
@@ -360,25 +479,23 @@ static int cpts_match(struct sk_buff *skb, unsigned int ptp_class,
                msgtype = data + offset;
 
        seqid = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID);
+       *mtype_seqid = (*msgtype & MESSAGE_TYPE_MASK) << MESSAGE_TYPE_SHIFT;
+       *mtype_seqid |= (ntohs(*seqid) & SEQUENCE_ID_MASK) << SEQUENCE_ID_SHIFT;
 
-       return (ts_msgtype == (*msgtype & 0xf) && ts_seqid == ntohs(*seqid));
+       return 1;
 }
 
-static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type)
+static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb,
+                       int ev_type, u32 skb_mtype_seqid)
 {
-       u64 ns = 0;
-       struct cpts_event *event;
        struct list_head *this, *next;
-       unsigned int class = ptp_classify_raw(skb);
+       struct cpts_event *event;
        unsigned long flags;
-       u16 seqid;
-       u8 mtype;
-
-       if (class == PTP_CLASS_NONE)
-               return 0;
+       u32 mtype_seqid;
+       u64 ns = 0;
 
-       spin_lock_irqsave(&cpts->lock, flags);
        cpts_fifo_read(cpts, -1);
+       spin_lock_irqsave(&cpts->lock, flags);
        list_for_each_safe(this, next, &cpts->events) {
                event = list_entry(this, struct cpts_event, list);
                if (event_expired(event)) {
@@ -386,29 +503,19 @@ static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type)
                        list_add(&event->list, &cpts->pool);
                        continue;
                }
-               mtype = (event->high >> MESSAGE_TYPE_SHIFT) & MESSAGE_TYPE_MASK;
-               seqid = (event->high >> SEQUENCE_ID_SHIFT) & SEQUENCE_ID_MASK;
-               if (ev_type == event_type(event) &&
-                   cpts_match(skb, class, seqid, mtype)) {
-                       ns = timecounter_cyc2time(&cpts->tc, event->low);
+
+               mtype_seqid = event->high &
+                             ((MESSAGE_TYPE_MASK << MESSAGE_TYPE_SHIFT) |
+                              (SEQUENCE_ID_MASK << SEQUENCE_ID_SHIFT) |
+                              (EVENT_TYPE_MASK << EVENT_TYPE_SHIFT));
+
+               if (mtype_seqid == skb_mtype_seqid) {
+                       ns = event->timestamp;
                        list_del_init(&event->list);
                        list_add(&event->list, &cpts->pool);
                        break;
                }
        }
-
-       if (ev_type == CPTS_EV_TX && !ns) {
-               struct cpts_skb_cb_data *skb_cb =
-                               (struct cpts_skb_cb_data *)skb->cb;
-               /* Not found, add frame to queue for processing later.
-                * The periodic FIFO check will handle this.
-                */
-               skb_get(skb);
-               /* get the timestamp for timeouts */
-               skb_cb->tmo = jiffies + msecs_to_jiffies(100);
-               __skb_queue_tail(&cpts->txq, skb);
-               ptp_schedule_worker(cpts->clock, 0);
-       }
        spin_unlock_irqrestore(&cpts->lock, flags);
 
        return ns;
@@ -416,10 +523,21 @@ static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type)
 
 void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb)
 {
-       u64 ns;
+       struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb;
        struct skb_shared_hwtstamps *ssh;
+       int ret;
+       u64 ns;
 
-       ns = cpts_find_ts(cpts, skb, CPTS_EV_RX);
+       ret = cpts_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid);
+       if (!ret)
+               return;
+
+       skb_cb->skb_mtype_seqid |= (CPTS_EV_RX << EVENT_TYPE_SHIFT);
+
+       dev_dbg(cpts->dev, "%s mtype seqid %08x\n",
+               __func__, skb_cb->skb_mtype_seqid);
+
+       ns = cpts_find_ts(cpts, skb, CPTS_EV_RX, skb_cb->skb_mtype_seqid);
        if (!ns)
                return;
        ssh = skb_hwtstamps(skb);
@@ -430,17 +548,27 @@ EXPORT_SYMBOL_GPL(cpts_rx_timestamp);
 
 void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
 {
-       u64 ns;
-       struct skb_shared_hwtstamps ssh;
+       struct cpts_skb_cb_data *skb_cb = (struct cpts_skb_cb_data *)skb->cb;
+       int ret;
 
        if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))
                return;
-       ns = cpts_find_ts(cpts, skb, CPTS_EV_TX);
-       if (!ns)
+
+       ret = cpts_skb_get_mtype_seqid(skb, &skb_cb->skb_mtype_seqid);
+       if (!ret)
                return;
-       memset(&ssh, 0, sizeof(ssh));
-       ssh.hwtstamp = ns_to_ktime(ns);
-       skb_tstamp_tx(skb, &ssh);
+
+       skb_cb->skb_mtype_seqid |= (CPTS_EV_TX << EVENT_TYPE_SHIFT);
+
+       dev_dbg(cpts->dev, "%s mtype seqid %08x\n",
+               __func__, skb_cb->skb_mtype_seqid);
+
+       /* Always defer TX TS processing to PTP worker */
+       skb_get(skb);
+       /* get the timestamp for timeouts */
+       skb_cb->tmo = jiffies + msecs_to_jiffies(CPTS_SKB_RX_TX_TMO);
+       skb_queue_tail(&cpts->txq, skb);
+       ptp_schedule_worker(cpts->clock, 0);
 }
 EXPORT_SYMBOL_GPL(cpts_tx_timestamp);
 
@@ -632,7 +760,7 @@ of_error:
 }
 
 struct cpts *cpts_create(struct device *dev, void __iomem *regs,
-                        struct device_node *node)
+                        struct device_node *node, u32 n_ext_ts)
 {
        struct cpts *cpts;
        int ret;
@@ -643,7 +771,10 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
 
        cpts->dev = dev;
        cpts->reg = (struct cpsw_cpts __iomem *)regs;
+       cpts->irq_poll = true;
        spin_lock_init(&cpts->lock);
+       mutex_init(&cpts->ptp_clk_mutex);
+       init_completion(&cpts->ts_push_complete);
 
        ret = cpts_of_parse(cpts, node);
        if (ret)
@@ -668,6 +799,9 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
        cpts->cc.mask = CLOCKSOURCE_MASK(32);
        cpts->info = cpts_info;
 
+       if (n_ext_ts)
+               cpts->info.n_ext_ts = n_ext_ts;
+
        cpts_calc_mult_shift(cpts);
        /* save cc.mult original value as it can be modified
         * by cpts_ptp_adjfreq().
index bb997c1..07222f6 100644 (file)
@@ -94,6 +94,7 @@ struct cpts_event {
        unsigned long tmo;
        u32 high;
        u32 low;
+       u64 timestamp;
 };
 
 struct cpts {
@@ -103,7 +104,7 @@ struct cpts {
        int rx_enable;
        struct ptp_clock_info info;
        struct ptp_clock *clock;
-       spinlock_t lock; /* protects time registers */
+       spinlock_t lock; /* protects fifo/events */
        u32 cc_mult; /* for the nominal frequency */
        struct cyclecounter cc;
        struct timecounter tc;
@@ -114,6 +115,12 @@ struct cpts {
        struct cpts_event pool_data[CPTS_MAX_EVENTS];
        unsigned long ov_check_period;
        struct sk_buff_head txq;
+       u64 cur_timestamp;
+       u32 mult_new;
+       struct mutex ptp_clk_mutex; /* sync PTP interface and worker */
+       bool irq_poll;
+       struct completion       ts_push_complete;
+       u32 hw_ts_enable;
 };
 
 void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb);
@@ -121,8 +128,9 @@ void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb);
 int cpts_register(struct cpts *cpts);
 void cpts_unregister(struct cpts *cpts);
 struct cpts *cpts_create(struct device *dev, void __iomem *regs,
-                        struct device_node *node);
+                        struct device_node *node, u32 n_ext_ts);
 void cpts_release(struct cpts *cpts);
+void cpts_misc_interrupt(struct cpts *cpts);
 
 static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb)
 {
@@ -134,6 +142,11 @@ static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb)
        return true;
 }
 
+static inline void cpts_set_irqpoll(struct cpts *cpts, bool en)
+{
+       cpts->irq_poll = en;
+}
+
 #else
 struct cpts;
 
@@ -146,7 +159,7 @@ static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb)
 
 static inline
 struct cpts *cpts_create(struct device *dev, void __iomem *regs,
-                        struct device_node *node)
+                        struct device_node *node, u32 n_ext_ts)
 {
        return NULL;
 }
@@ -169,6 +182,14 @@ static inline bool cpts_can_timestamp(struct cpts *cpts, struct sk_buff *skb)
 {
        return false;
 }
+
+static inline void cpts_misc_interrupt(struct cpts *cpts)
+{
+}
+
+static inline void cpts_set_irqpoll(struct cpts *cpts, bool en)
+{
+}
 #endif
 
 
index 38b7f6d..702fdc3 100644 (file)
@@ -397,6 +397,8 @@ static int davinci_mdio_probe(struct platform_device *pdev)
        data->dev = dev;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -EINVAL;
        data->regs = devm_ioremap(dev, res->start, resource_size(res));
        if (!data->regs)
                return -ENOMEM;
index ad7cfc1..38cc12f 100644 (file)
@@ -64,8 +64,8 @@ k3_cppi_desc_pool_create_name(struct device *dev, size_t size,
                return ERR_PTR(-ENOMEM);
 
        pool->gen_pool = gen_pool_create(ilog2(pool->desc_size), -1);
-       if (IS_ERR(pool->gen_pool)) {
-               ret = PTR_ERR(pool->gen_pool);
+       if (!pool->gen_pool) {
+               ret = -ENOMEM;
                dev_err(pool->dev, "pool create failed %d\n", ret);
                kfree_const(pool_name);
                goto gen_pool_create_fail;
index fb36115..9d6e27f 100644 (file)
@@ -3716,7 +3716,8 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
        if (!cpts_node)
                cpts_node = of_node_get(node);
 
-       gbe_dev->cpts = cpts_create(gbe_dev->dev, gbe_dev->cpts_reg, cpts_node);
+       gbe_dev->cpts = cpts_create(gbe_dev->dev, gbe_dev->cpts_reg,
+                                   cpts_node, 0);
        of_node_put(cpts_node);
        if (IS_ENABLED(CONFIG_TI_CPTS) && IS_ERR(gbe_dev->cpts)) {
                ret = PTR_ERR(gbe_dev->cpts);
index ad46520..8577098 100644 (file)
@@ -70,7 +70,7 @@ MODULE_DESCRIPTION("Driver for TI ThunderLAN based ethernet PCI adapters");
 MODULE_LICENSE("GPL");
 
 /* Turn on debugging.
- * See Documentation/networking/device_drivers/ti/tlan.txt for details
+ * See Documentation/networking/device_drivers/ti/tlan.rst for details
  */
 static  int            debug;
 module_param(debug, int, 0);
index 070dd6f..310e683 100644 (file)
@@ -1150,7 +1150,7 @@ static irqreturn_t gelic_card_interrupt(int irq, void *ptr)
  * gelic_net_poll_controller - artificial interrupt for netconsole etc.
  * @netdev: interface device structure
  *
- * see Documentation/networking/netconsole.txt
+ * see Documentation/networking/netconsole.rst
  */
 void gelic_net_poll_controller(struct net_device *netdev)
 {
index 6576271..3902b3a 100644 (file)
@@ -1615,7 +1615,7 @@ spider_net_interrupt(int irq, void *ptr)
  * spider_net_poll_controller - artificial interrupt for netconsole etc.
  * @netdev: interface device structure
  *
- * see Documentation/networking/netconsole.txt
+ * see Documentation/networking/netconsole.rst
  */
 static void
 spider_net_poll_controller(struct net_device *netdev)
index b50c3ec..6bcda20 100644 (file)
@@ -643,7 +643,7 @@ static int tc_mii_probe(struct net_device *dev)
                linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, mask);
                linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mask);
        }
-       linkmode_and(phydev->supported, phydev->supported, mask);
+       linkmode_andnot(phydev->supported, phydev->supported, mask);
        linkmode_copy(phydev->advertising, phydev->supported);
 
        lp->link = 0;
index a962097..6cff5f7 100644 (file)
@@ -19,6 +19,7 @@ if NET_VENDOR_VIA
 config VIA_RHINE
        tristate "VIA Rhine support"
        depends on PCI || (OF_IRQ && GENERIC_PCI_IOMAP)
+       depends on PCI || ARCH_VT8500 || COMPILE_TEST
        depends on HAS_DMA
        select CRC32
        select MII
index 3e313e7..9292440 100644 (file)
@@ -1410,9 +1410,9 @@ static int temac_probe(struct platform_device *pdev)
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        lp->regs = devm_ioremap(&pdev->dev, res->start,
                                        resource_size(res));
-       if (IS_ERR(lp->regs)) {
+       if (!lp->regs) {
                dev_err(&pdev->dev, "could not map TEMAC registers\n");
-               return PTR_ERR(lp->regs);
+               return -ENOMEM;
        }
 
        /* Select register access functions with the specified
@@ -1505,10 +1505,10 @@ static int temac_probe(struct platform_device *pdev)
                res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
                lp->sdma_regs = devm_ioremap(&pdev->dev, res->start,
                                                     resource_size(res));
-               if (IS_ERR(lp->sdma_regs)) {
+               if (!lp->sdma_regs) {
                        dev_err(&pdev->dev,
                                "could not map DMA registers\n");
-                       return PTR_ERR(lp->sdma_regs);
+                       return -ENOMEM;
                }
                if (pdata->dma_little_endian) {
                        lp->dma_in = temac_dma_in32_le;
index 269596c..2e52029 100644 (file)
@@ -1387,6 +1387,8 @@ static int ixp4xx_eth_probe(struct platform_device *pdev)
                return -ENODEV;
        regs_phys = res->start;
        port->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(port->regs))
+               return PTR_ERR(port->regs);
 
        switch (port->id) {
        case IXP4XX_ETH_NPEA:
index 3b412a5..da4f58e 100644 (file)
@@ -77,7 +77,7 @@ config SKFP
          - Netelligent 100 FDDI SAS UTP
          - Netelligent 100 FDDI SAS Fibre MIC
 
-         Read <file:Documentation/networking/skfp.txt> for information about
+         Read <file:Documentation/networking/skfp.rst> for information about
          the driver.
 
          Questions concerning this driver can be addressed to:
index 09f279c..6b461be 100644 (file)
@@ -1207,7 +1207,7 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[],
                enum ifla_geneve_df df = nla_get_u8(data[IFLA_GENEVE_DF]);
 
                if (df < 0 || df > GENEVE_DF_MAX) {
-                       NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_GENEVE_DF],
+                       NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_DF],
                                            "Invalid DF attribute");
                        return -EINVAL;
                }
index 672cd2c..21640a0 100644 (file)
@@ -1169,11 +1169,11 @@ out_unlock:
 static struct genl_family gtp_genl_family;
 
 static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,
-                             u32 type, struct pdp_ctx *pctx)
+                             int flags, u32 type, struct pdp_ctx *pctx)
 {
        void *genlh;
 
-       genlh = genlmsg_put(skb, snd_portid, snd_seq, &gtp_genl_family, 0,
+       genlh = genlmsg_put(skb, snd_portid, snd_seq, &gtp_genl_family, flags,
                            type);
        if (genlh == NULL)
                goto nlmsg_failure;
@@ -1227,8 +1227,8 @@ static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info)
                goto err_unlock;
        }
 
-       err = gtp_genl_fill_info(skb2, NETLINK_CB(skb).portid,
-                                info->snd_seq, info->nlhdr->nlmsg_type, pctx);
+       err = gtp_genl_fill_info(skb2, NETLINK_CB(skb).portid, info->snd_seq,
+                                0, info->nlhdr->nlmsg_type, pctx);
        if (err < 0)
                goto err_unlock_free;
 
@@ -1271,6 +1271,7 @@ static int gtp_genl_dump_pdp(struct sk_buff *skb,
                                    gtp_genl_fill_info(skb,
                                            NETLINK_CB(cb->skb).portid,
                                            cb->nlh->nlmsg_seq,
+                                           NLM_F_MULTI,
                                            cb->nlh->nlmsg_type, pctx)) {
                                        cb->args[0] = i;
                                        cb->args[1] = j;
index 8e05b5c..f4500f0 100644 (file)
@@ -30,7 +30,7 @@ config 6PACK
 
          Note that this driver is still experimental and might cause
          problems. For details about the features and the usage of the
-         driver, read <file:Documentation/networking/6pack.txt>.
+         driver, read <file:Documentation/networking/6pack.rst>.
 
          To compile this driver as a module, choose M here: the module
          will be called 6pack.
@@ -84,7 +84,7 @@ config SCC
        ---help---
          These cards are used to connect your Linux box to an amateur radio
          in order to communicate with other computers. If you want to use
-         this, read <file:Documentation/networking/z8530drv.txt> and the
+         this, read <file:Documentation/networking/z8530drv.rst> and the
          AX25-HOWTO, available from
          <http://www.tldp.org/docs.html#howto>. Also make sure to say Y
          to "Amateur Radio AX.25 Level 2" support.
@@ -98,7 +98,7 @@ config SCC_DELAY
        help
          Say Y here if you experience problems with the SCC driver not
          working properly; please read
-         <file:Documentation/networking/z8530drv.txt> for details.
+         <file:Documentation/networking/z8530drv.rst> for details.
 
          If unsure, say N.
 
@@ -127,7 +127,7 @@ config BAYCOM_SER_FDX
          your serial interface chip. To configure the driver, use the sethdlc
          utility available in the standard ax25 utilities package. For
          information on the modems, see <http://www.baycom.de/> and
-         <file:Documentation/networking/baycom.txt>.
+         <file:Documentation/networking/baycom.rst>.
 
          To compile this driver as a module, choose M here: the module
          will be called baycom_ser_fdx.  This is recommended.
@@ -145,7 +145,7 @@ config BAYCOM_SER_HDX
          the driver, use the sethdlc utility available in the standard ax25
          utilities package. For information on the modems, see
          <http://www.baycom.de/> and
-         <file:Documentation/networking/baycom.txt>.
+         <file:Documentation/networking/baycom.rst>.
 
          To compile this driver as a module, choose M here: the module
          will be called baycom_ser_hdx.  This is recommended.
@@ -160,7 +160,7 @@ config BAYCOM_PAR
          par96 designs. To configure the driver, use the sethdlc utility
          available in the standard ax25 utilities package. For information on
          the modems, see <http://www.baycom.de/> and the file
-         <file:Documentation/networking/baycom.txt>.
+         <file:Documentation/networking/baycom.rst>.
 
          To compile this driver as a module, choose M here: the module
          will be called baycom_par.  This is recommended.
@@ -175,7 +175,7 @@ config BAYCOM_EPP
          designs. To configure the driver, use the sethdlc utility available
          in the standard ax25 utilities package. For information on the
          modems, see <http://www.baycom.de/> and the file
-         <file:Documentation/networking/baycom.txt>.
+         <file:Documentation/networking/baycom.rst>.
 
          To compile this driver as a module, choose M here: the module
          will be called baycom_epp.  This is recommended.
index fbea6f2..2066881 100644 (file)
@@ -107,6 +107,25 @@ struct bpqdev {
 
 static LIST_HEAD(bpq_devices);
 
+/*
+ * bpqether network devices are paired with ethernet devices below them, so
+ * form a special "super class" of normal ethernet devices; split their locks
+ * off into a separate class since they always nest.
+ */
+static struct lock_class_key bpq_netdev_xmit_lock_key;
+
+static void bpq_set_lockdep_class_one(struct net_device *dev,
+                                     struct netdev_queue *txq,
+                                     void *_unused)
+{
+       lockdep_set_class(&txq->_xmit_lock, &bpq_netdev_xmit_lock_key);
+}
+
+static void bpq_set_lockdep_class(struct net_device *dev)
+{
+       netdev_for_each_tx_queue(dev, bpq_set_lockdep_class_one, NULL);
+}
+
 /* ------------------------------------------------------------------------ */
 
 
@@ -477,6 +496,7 @@ static int bpq_new_device(struct net_device *edev)
        err = register_netdevice(ndev);
        if (err)
                goto error;
+       bpq_set_lockdep_class(ndev);
 
        /* List protected by RTNL */
        list_add_rcu(&bpq->bpq_list, &bpq_devices);
index 6c03932..33fdd55 100644 (file)
@@ -7,7 +7,7 @@
  *            ------------------
  *
  * You can find a subset of the documentation in 
- * Documentation/networking/z8530drv.txt.
+ * Documentation/networking/z8530drv.rst.
  */
 
 /*
index d8e86bd..5de57fc 100644 (file)
@@ -707,7 +707,8 @@ no_memory:
        goto drop;
 }
 
-static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t netvsc_start_xmit(struct sk_buff *skb,
+                                    struct net_device *ndev)
 {
        return netvsc_xmit(skb, ndev, false);
 }
@@ -2456,6 +2457,8 @@ static int netvsc_probe(struct hv_device *dev,
                NETIF_F_HW_VLAN_CTAG_RX;
        net->vlan_features = net->features;
 
+       netdev_lockdep_set_classes(net);
+
        /* MTU range: 68 - 1500 or 65521 */
        net->min_mtu = NETVSC_MTU_MIN;
        if (nvdev->nvsp_version >= NVSP_PROTOCOL_VERSION_2)
index 845478a..6657060 100644 (file)
@@ -415,13 +415,14 @@ static void gsi_evt_ring_de_alloc_command(struct gsi *gsi, u32 evt_ring_id)
                        evt_ring->state);
 }
 
-/* Return the hardware's notion of the current state of a channel */
-static enum gsi_channel_state
-gsi_channel_state(struct gsi *gsi, u32 channel_id)
+/* Fetch the current state of a channel from hardware */
+static enum gsi_channel_state gsi_channel_state(struct gsi_channel *channel)
 {
+       u32 channel_id = gsi_channel_id(channel);
+       void *virt = channel->gsi->virt;
        u32 val;
 
-       val = ioread32(gsi->virt + GSI_CH_C_CNTXT_0_OFFSET(channel_id));
+       val = ioread32(virt + GSI_CH_C_CNTXT_0_OFFSET(channel_id));
 
        return u32_get_bits(val, CHSTATE_FMASK);
 }
@@ -432,16 +433,18 @@ gsi_channel_command(struct gsi_channel *channel, enum gsi_ch_cmd_opcode opcode)
 {
        struct completion *completion = &channel->completion;
        u32 channel_id = gsi_channel_id(channel);
+       struct gsi *gsi = channel->gsi;
        u32 val;
 
        val = u32_encode_bits(channel_id, CH_CHID_FMASK);
        val |= u32_encode_bits(opcode, CH_OPCODE_FMASK);
 
-       if (gsi_command(channel->gsi, GSI_CH_CMD_OFFSET, val, completion))
+       if (gsi_command(gsi, GSI_CH_CMD_OFFSET, val, completion))
                return 0;       /* Success! */
 
-       dev_err(channel->gsi->dev, "GSI command %u to channel %u timed out "
-               "(state is %u)\n", opcode, channel_id, channel->state);
+       dev_err(gsi->dev,
+               "GSI command %u to channel %u timed out (state is %u)\n",
+               opcode, channel_id, gsi_channel_state(channel));
 
        return -ETIMEDOUT;
 }
@@ -450,18 +453,21 @@ gsi_channel_command(struct gsi_channel *channel, enum gsi_ch_cmd_opcode opcode)
 static int gsi_channel_alloc_command(struct gsi *gsi, u32 channel_id)
 {
        struct gsi_channel *channel = &gsi->channel[channel_id];
+       enum gsi_channel_state state;
        int ret;
 
        /* Get initial channel state */
-       channel->state = gsi_channel_state(gsi, channel_id);
-
-       if (channel->state != GSI_CHANNEL_STATE_NOT_ALLOCATED)
+       state = gsi_channel_state(channel);
+       if (state != GSI_CHANNEL_STATE_NOT_ALLOCATED)
                return -EINVAL;
 
        ret = gsi_channel_command(channel, GSI_CH_ALLOCATE);
-       if (!ret && channel->state != GSI_CHANNEL_STATE_ALLOCATED) {
+
+       /* Channel state will normally have been updated */
+       state = gsi_channel_state(channel);
+       if (!ret && state != GSI_CHANNEL_STATE_ALLOCATED) {
                dev_err(gsi->dev, "bad channel state (%u) after alloc\n",
-                       channel->state);
+                       state);
                ret = -EIO;
        }
 
@@ -471,18 +477,21 @@ static int gsi_channel_alloc_command(struct gsi *gsi, u32 channel_id)
 /* Start an ALLOCATED channel */
 static int gsi_channel_start_command(struct gsi_channel *channel)
 {
-       enum gsi_channel_state state = channel->state;
+       enum gsi_channel_state state;
        int ret;
 
+       state = gsi_channel_state(channel);
        if (state != GSI_CHANNEL_STATE_ALLOCATED &&
            state != GSI_CHANNEL_STATE_STOPPED)
                return -EINVAL;
 
        ret = gsi_channel_command(channel, GSI_CH_START);
-       if (!ret && channel->state != GSI_CHANNEL_STATE_STARTED) {
+
+       /* Channel state will normally have been updated */
+       state = gsi_channel_state(channel);
+       if (!ret && state != GSI_CHANNEL_STATE_STARTED) {
                dev_err(channel->gsi->dev,
-                       "bad channel state (%u) after start\n",
-                       channel->state);
+                       "bad channel state (%u) after start\n", state);
                ret = -EIO;
        }
 
@@ -492,23 +501,27 @@ static int gsi_channel_start_command(struct gsi_channel *channel)
 /* Stop a GSI channel in STARTED state */
 static int gsi_channel_stop_command(struct gsi_channel *channel)
 {
-       enum gsi_channel_state state = channel->state;
+       enum gsi_channel_state state;
        int ret;
 
+       state = gsi_channel_state(channel);
        if (state != GSI_CHANNEL_STATE_STARTED &&
            state != GSI_CHANNEL_STATE_STOP_IN_PROC)
                return -EINVAL;
 
        ret = gsi_channel_command(channel, GSI_CH_STOP);
-       if (ret || channel->state == GSI_CHANNEL_STATE_STOPPED)
+
+       /* Channel state will normally have been updated */
+       state = gsi_channel_state(channel);
+       if (ret || state == GSI_CHANNEL_STATE_STOPPED)
                return ret;
 
        /* We may have to try again if stop is in progress */
-       if (channel->state == GSI_CHANNEL_STATE_STOP_IN_PROC)
+       if (state == GSI_CHANNEL_STATE_STOP_IN_PROC)
                return -EAGAIN;
 
-       dev_err(channel->gsi->dev, "bad channel state (%u) after stop\n",
-               channel->state);
+       dev_err(channel->gsi->dev,
+               "bad channel state (%u) after stop\n", state);
 
        return -EIO;
 }
@@ -516,41 +529,49 @@ static int gsi_channel_stop_command(struct gsi_channel *channel)
 /* Reset a GSI channel in ALLOCATED or ERROR state. */
 static void gsi_channel_reset_command(struct gsi_channel *channel)
 {
+       enum gsi_channel_state state;
        int ret;
 
        msleep(1);      /* A short delay is required before a RESET command */
 
-       if (channel->state != GSI_CHANNEL_STATE_STOPPED &&
-           channel->state != GSI_CHANNEL_STATE_ERROR) {
+       state = gsi_channel_state(channel);
+       if (state != GSI_CHANNEL_STATE_STOPPED &&
+           state != GSI_CHANNEL_STATE_ERROR) {
                dev_err(channel->gsi->dev,
-                       "bad channel state (%u) before reset\n",
-                       channel->state);
+                       "bad channel state (%u) before reset\n", state);
                return;
        }
 
        ret = gsi_channel_command(channel, GSI_CH_RESET);
-       if (!ret && channel->state != GSI_CHANNEL_STATE_ALLOCATED)
+
+       /* Channel state will normally have been updated */
+       state = gsi_channel_state(channel);
+       if (!ret && state != GSI_CHANNEL_STATE_ALLOCATED)
                dev_err(channel->gsi->dev,
-                       "bad channel state (%u) after reset\n",
-                       channel->state);
+                       "bad channel state (%u) after reset\n", state);
 }
 
 /* Deallocate an ALLOCATED GSI channel */
 static void gsi_channel_de_alloc_command(struct gsi *gsi, u32 channel_id)
 {
        struct gsi_channel *channel = &gsi->channel[channel_id];
+       enum gsi_channel_state state;
        int ret;
 
-       if (channel->state != GSI_CHANNEL_STATE_ALLOCATED) {
-               dev_err(gsi->dev, "bad channel state (%u) before dealloc\n",
-                       channel->state);
+       state = gsi_channel_state(channel);
+       if (state != GSI_CHANNEL_STATE_ALLOCATED) {
+               dev_err(gsi->dev,
+                       "bad channel state (%u) before dealloc\n", state);
                return;
        }
 
        ret = gsi_channel_command(channel, GSI_CH_DE_ALLOC);
-       if (!ret && channel->state != GSI_CHANNEL_STATE_NOT_ALLOCATED)
-               dev_err(gsi->dev, "bad channel state (%u) after dealloc\n",
-                       channel->state);
+
+       /* Channel state will normally have been updated */
+       state = gsi_channel_state(channel);
+       if (!ret && state != GSI_CHANNEL_STATE_NOT_ALLOCATED)
+               dev_err(gsi->dev,
+                       "bad channel state (%u) after dealloc\n", state);
 }
 
 /* Ring an event ring doorbell, reporting the last entry processed by the AP.
@@ -777,6 +798,7 @@ int gsi_channel_start(struct gsi *gsi, u32 channel_id)
 int gsi_channel_stop(struct gsi *gsi, u32 channel_id)
 {
        struct gsi_channel *channel = &gsi->channel[channel_id];
+       enum gsi_channel_state state;
        u32 retries;
        int ret;
 
@@ -786,7 +808,8 @@ int gsi_channel_stop(struct gsi *gsi, u32 channel_id)
         * STOP command timed out.  We won't stop a channel if stopping it
         * was successful previously (so we still want the freeze above).
         */
-       if (channel->state == GSI_CHANNEL_STATE_STOPPED)
+       state = gsi_channel_state(channel);
+       if (state == GSI_CHANNEL_STATE_STOPPED)
                return 0;
 
        /* RX channels might require a little time to enter STOPPED state */
@@ -811,18 +834,18 @@ int gsi_channel_stop(struct gsi *gsi, u32 channel_id)
 }
 
 /* Reset and reconfigure a channel (possibly leaving doorbell disabled) */
-void gsi_channel_reset(struct gsi *gsi, u32 channel_id, bool db_enable)
+void gsi_channel_reset(struct gsi *gsi, u32 channel_id, bool legacy)
 {
        struct gsi_channel *channel = &gsi->channel[channel_id];
 
        mutex_lock(&gsi->mutex);
 
-       /* Due to a hardware quirk we need to reset RX channels twice. */
        gsi_channel_reset_command(channel);
-       if (!channel->toward_ipa)
+       /* Due to a hardware quirk we may need to reset RX channels twice. */
+       if (legacy && !channel->toward_ipa)
                gsi_channel_reset_command(channel);
 
-       gsi_channel_program(channel, db_enable);
+       gsi_channel_program(channel, legacy);
        gsi_channel_trans_cancel_pending(channel);
 
        mutex_unlock(&gsi->mutex);
@@ -940,7 +963,6 @@ static void gsi_isr_chan_ctrl(struct gsi *gsi)
                channel_mask ^= BIT(channel_id);
 
                channel = &gsi->channel[channel_id];
-               channel->state = gsi_channel_state(gsi, channel_id);
 
                complete(&channel->completion);
        }
@@ -1041,6 +1063,7 @@ static void gsi_isr_gp_int1(struct gsi *gsi)
 
        complete(&gsi->completion);
 }
+
 /* Inter-EE interrupt handler */
 static void gsi_isr_glob_ee(struct gsi *gsi)
 {
@@ -1433,7 +1456,7 @@ static void gsi_evt_ring_teardown(struct gsi *gsi)
 
 /* Setup function for a single channel */
 static int gsi_channel_setup_one(struct gsi *gsi, u32 channel_id,
-                                bool db_enable)
+                                bool legacy)
 {
        struct gsi_channel *channel = &gsi->channel[channel_id];
        u32 evt_ring_id = channel->evt_ring_id;
@@ -1452,7 +1475,7 @@ static int gsi_channel_setup_one(struct gsi *gsi, u32 channel_id,
        if (ret)
                goto err_evt_ring_de_alloc;
 
-       gsi_channel_program(channel, db_enable);
+       gsi_channel_program(channel, legacy);
 
        if (channel->toward_ipa)
                netif_tx_napi_add(&gsi->dummy_dev, &channel->napi,
@@ -1493,6 +1516,12 @@ static int gsi_generic_command(struct gsi *gsi, u32 channel_id,
        struct completion *completion = &gsi->completion;
        u32 val;
 
+       /* First zero the result code field */
+       val = ioread32(gsi->virt + GSI_CNTXT_SCRATCH_0_OFFSET);
+       val &= ~GENERIC_EE_RESULT_FMASK;
+       iowrite32(val, gsi->virt + GSI_CNTXT_SCRATCH_0_OFFSET);
+
+       /* Now issue the command */
        val = u32_encode_bits(opcode, GENERIC_OPCODE_FMASK);
        val |= u32_encode_bits(channel_id, GENERIC_CHID_FMASK);
        val |= u32_encode_bits(GSI_EE_MODEM, GENERIC_EE_FMASK);
@@ -1523,7 +1552,7 @@ static void gsi_modem_channel_halt(struct gsi *gsi, u32 channel_id)
 }
 
 /* Setup function for channels */
-static int gsi_channel_setup(struct gsi *gsi, bool db_enable)
+static int gsi_channel_setup(struct gsi *gsi, bool legacy)
 {
        u32 channel_id = 0;
        u32 mask;
@@ -1535,7 +1564,7 @@ static int gsi_channel_setup(struct gsi *gsi, bool db_enable)
        mutex_lock(&gsi->mutex);
 
        do {
-               ret = gsi_channel_setup_one(gsi, channel_id, db_enable);
+               ret = gsi_channel_setup_one(gsi, channel_id, legacy);
                if (ret)
                        goto err_unwind;
        } while (++channel_id < gsi->channel_count);
@@ -1621,7 +1650,7 @@ static void gsi_channel_teardown(struct gsi *gsi)
 }
 
 /* Setup function for GSI.  GSI firmware must be loaded and initialized */
-int gsi_setup(struct gsi *gsi, bool db_enable)
+int gsi_setup(struct gsi *gsi, bool legacy)
 {
        u32 val;
 
@@ -1664,7 +1693,7 @@ int gsi_setup(struct gsi *gsi, bool db_enable)
        /* Writing 1 indicates IRQ interrupts; 0 would be MSI */
        iowrite32(1, gsi->virt + GSI_CNTXT_INTSET_OFFSET);
 
-       return gsi_channel_setup(gsi, db_enable);
+       return gsi_channel_setup(gsi, legacy);
 }
 
 /* Inverse of gsi_setup() */
@@ -1798,9 +1827,9 @@ static int gsi_channel_init_one(struct gsi *gsi,
 
        /* Worst case we need an event for every outstanding TRE */
        if (data->channel.tre_count > data->channel.event_count) {
-               dev_warn(gsi->dev, "channel %u limited to %u TREs\n",
-                       data->channel_id, data->channel.tre_count);
                tre_count = data->channel.event_count;
+               dev_warn(gsi->dev, "channel %u limited to %u TREs\n",
+                        data->channel_id, tre_count);
        } else {
                tre_count = data->channel.tre_count;
        }
index 0698ff1..90a0219 100644 (file)
@@ -113,8 +113,7 @@ struct gsi_channel {
        u16 tre_count;
        u16 event_count;
 
-       struct completion completion;   /* signals channel state changes */
-       enum gsi_channel_state state;
+       struct completion completion;   /* signals channel command completion */
 
        struct gsi_ring tre_ring;
        u32 evt_ring_id;
@@ -166,14 +165,14 @@ struct gsi {
 /**
  * gsi_setup() - Set up the GSI subsystem
  * @gsi:       Address of GSI structure embedded in an IPA structure
- * @db_enable: Whether to use the GSI doorbell engine
+ * @legacy:    Set up for legacy hardware
  *
  * @Return:    0 if successful, or a negative error code
  *
  * Performs initialization that must wait until the GSI hardware is
  * ready (including firmware loaded).
  */
-int gsi_setup(struct gsi *gsi, bool db_enable);
+int gsi_setup(struct gsi *gsi, bool legacy);
 
 /**
  * gsi_teardown() - Tear down GSI subsystem
@@ -221,15 +220,15 @@ int gsi_channel_stop(struct gsi *gsi, u32 channel_id);
  * gsi_channel_reset() - Reset an allocated GSI channel
  * @gsi:       GSI pointer
  * @channel_id:        Channel to be reset
- * @db_enable: Whether doorbell engine should be enabled
+ * @legacy:    Legacy behavior
  *
- * Reset a channel and reconfigure it.  The @db_enable flag indicates
- * whether the doorbell engine will be enabled following reconfiguration.
+ * Reset a channel and reconfigure it.  The @legacy flag indicates
+ * that some steps should be done differently for legacy hardware.
  *
  * GSI hardware relinquishes ownership of all pending receive buffer
  * transactions and they will complete with their cancelled flag set.
  */
-void gsi_channel_reset(struct gsi *gsi, u32 channel_id, bool db_enable);
+void gsi_channel_reset(struct gsi *gsi, u32 channel_id, bool legacy);
 
 int gsi_channel_suspend(struct gsi *gsi, u32 channel_id, bool stop);
 int gsi_channel_resume(struct gsi *gsi, u32 channel_id, bool start);
index 7613b9c..acc9e74 100644 (file)
 #define INTER_EE_RESULT_FMASK          GENMASK(2, 0)
 #define GENERIC_EE_RESULT_FMASK                GENMASK(7, 5)
 #define GENERIC_EE_SUCCESS_FVAL                        1
+#define GENERIC_EE_INCORRECT_DIRECTION_FVAL    3
+#define GENERIC_EE_INCORRECT_CHANNEL_FVAL      5
 #define GENERIC_EE_NO_RESOURCES_FVAL           7
 #define USB_MAX_PACKET_FMASK           GENMASK(15, 15) /* 0: HS; 1: SS */
 #define MHI_BASE_CHANNEL_FMASK         GENMASK(31, 24)
index 23fb298..b10a853 100644 (file)
@@ -47,6 +47,10 @@ struct ipa_interrupt;
  * @mem_offset:                Offset from @mem_virt used for access to IPA memory
  * @mem_size:          Total size (bytes) of memory at @mem_virt
  * @mem:               Array of IPA-local memory region descriptors
+ * @imem_iova:         I/O virtual address of IPA region in IMEM
+ * @imem_size;         Size of IMEM region
+ * @smem_iova:         I/O virtual address of IPA region in SMEM
+ * @smem_size;         Size of SMEM region
  * @zero_addr:         DMA address of preallocated zero-filled memory
  * @zero_virt:         Virtual address of preallocated zero-filled memory
  * @zero_size:         Size (bytes) of preallocated zero-filled memory
@@ -88,6 +92,12 @@ struct ipa {
        u32 mem_size;
        const struct ipa_mem *mem;
 
+       unsigned long imem_iova;
+       size_t imem_size;
+
+       unsigned long smem_iova;
+       size_t smem_size;
+
        dma_addr_t zero_addr;
        void *zero_virt;
        size_t zero_size;
index d226b85..394f8a6 100644 (file)
@@ -103,28 +103,6 @@ struct ipa_cmd_ip_packet_init {
 /* Field masks for ipa_cmd_ip_packet_init dest_endpoint field */
 #define IPA_PACKET_INIT_DEST_ENDPOINT_FMASK            GENMASK(4, 0)
 
-/* IPA_CMD_DMA_TASK_32B_ADDR */
-
-/* This opcode gets modified with a DMA operation count */
-
-#define DMA_TASK_32B_ADDR_OPCODE_COUNT_FMASK           GENMASK(15, 8)
-
-struct ipa_cmd_hw_dma_task_32b_addr {
-       __le16 flags;
-       __le16 size;
-       __le32 addr;
-       __le16 packet_size;
-       u8 reserved[6];
-};
-
-/* Field masks for ipa_cmd_hw_dma_task_32b_addr flags field */
-#define DMA_TASK_32B_ADDR_FLAGS_SW_RSVD_FMASK          GENMASK(10, 0)
-#define DMA_TASK_32B_ADDR_FLAGS_CMPLT_FMASK            GENMASK(11, 11)
-#define DMA_TASK_32B_ADDR_FLAGS_EOF_FMASK              GENMASK(12, 12)
-#define DMA_TASK_32B_ADDR_FLAGS_FLSH_FMASK             GENMASK(13, 13)
-#define DMA_TASK_32B_ADDR_FLAGS_LOCK_FMASK             GENMASK(14, 14)
-#define DMA_TASK_32B_ADDR_FLAGS_UNLOCK_FMASK           GENMASK(15, 15)
-
 /* IPA_CMD_DMA_SHARED_MEM */
 
 /* For IPA v4.0+, this opcode gets modified with pipeline clear options */
@@ -163,7 +141,6 @@ union ipa_cmd_payload {
        struct ipa_cmd_hw_hdr_init_local hdr_init_local;
        struct ipa_cmd_register_write register_write;
        struct ipa_cmd_ip_packet_init ip_packet_init;
-       struct ipa_cmd_hw_dma_task_32b_addr dma_task_32b_addr;
        struct ipa_cmd_hw_dma_mem_mem dma_shared_mem;
        struct ipa_cmd_ip_packet_tag_status ip_packet_tag_status;
 };
@@ -508,42 +485,6 @@ static void ipa_cmd_ip_packet_init_add(struct gsi_trans *trans, u8 endpoint_id)
                          direction, opcode);
 }
 
-/* Use a 32-bit DMA command to zero a block of memory */
-void ipa_cmd_dma_task_32b_addr_add(struct gsi_trans *trans, u16 size,
-                                  dma_addr_t addr, bool toward_ipa)
-{
-       struct ipa *ipa = container_of(trans->gsi, struct ipa, gsi);
-       enum ipa_cmd_opcode opcode = IPA_CMD_DMA_TASK_32B_ADDR;
-       struct ipa_cmd_hw_dma_task_32b_addr *payload;
-       union ipa_cmd_payload *cmd_payload;
-       enum dma_data_direction direction;
-       dma_addr_t payload_addr;
-       u16 flags;
-
-       /* assert(addr <= U32_MAX); */
-       addr &= GENMASK_ULL(31, 0);
-
-       /* The opcode encodes the number of DMA operations in the high byte */
-       opcode |= u16_encode_bits(1, DMA_TASK_32B_ADDR_OPCODE_COUNT_FMASK);
-
-       direction = toward_ipa ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
-
-       /* complete: 0 = don't interrupt; eof: 0 = don't assert eot */
-       flags = DMA_TASK_32B_ADDR_FLAGS_FLSH_FMASK;
-       /* lock: 0 = don't lock endpoint; unlock: 0 = don't unlock */
-
-       cmd_payload = ipa_cmd_payload_alloc(ipa, &payload_addr);
-       payload = &cmd_payload->dma_task_32b_addr;
-
-       payload->flags = cpu_to_le16(flags);
-       payload->size = cpu_to_le16(size);
-       payload->addr = cpu_to_le32((u32)addr);
-       payload->packet_size = cpu_to_le16(size);
-
-       gsi_trans_cmd_add(trans, payload, sizeof(*payload), payload_addr,
-                         direction, opcode);
-}
-
 /* Use a DMA command to read or write a block of IPA-resident memory */
 void ipa_cmd_dma_shared_mem_add(struct gsi_trans *trans, u32 offset, u16 size,
                                dma_addr_t addr, bool toward_ipa)
index 4917525..e440aa6 100644 (file)
@@ -35,7 +35,6 @@ enum ipa_cmd_opcode {
        IPA_CMD_HDR_INIT_LOCAL          = 9,
        IPA_CMD_REGISTER_WRITE          = 12,
        IPA_CMD_IP_PACKET_INIT          = 16,
-       IPA_CMD_DMA_TASK_32B_ADDR       = 17,
        IPA_CMD_DMA_SHARED_MEM          = 19,
        IPA_CMD_IP_PACKET_TAG_STATUS    = 20,
 };
@@ -147,16 +146,6 @@ void ipa_cmd_hdr_init_local_add(struct gsi_trans *trans, u32 offset, u16 size,
 void ipa_cmd_register_write_add(struct gsi_trans *trans, u32 offset, u32 value,
                                u32 mask, bool clear_full);
 
-/**
- * ipa_cmd_dma_task_32b_addr_add() - Add a 32-bit DMA command to a transaction
- * @trans:     GSi transaction
- * @size:      Number of bytes to be memory to be transferred
- * @addr:      DMA address of buffer to be read into or written from
- * @toward_ipa:        true means write to IPA memory; false means read
- */
-void ipa_cmd_dma_task_32b_addr_add(struct gsi_trans *trans, u16 size,
-                                  dma_addr_t addr, bool toward_ipa);
-
 /**
  * ipa_cmd_dma_shared_mem_add() - Add a DMA memory command to a transaction
  * @trans:     GSI transaction
index 042b5fc..43faa35 100644 (file)
@@ -193,7 +193,7 @@ static const struct ipa_resource_data ipa_resource_data = {
 };
 
 /* IPA-resident memory region configuration for the SC7180 SoC. */
-static const struct ipa_mem ipa_mem_data[] = {
+static const struct ipa_mem ipa_mem_local_data[] = {
        [IPA_MEM_UC_SHARED] = {
                .offset         = 0x0000,
                .size           = 0x0080,
@@ -296,12 +296,20 @@ static const struct ipa_mem ipa_mem_data[] = {
        },
 };
 
+static struct ipa_mem_data ipa_mem_data = {
+       .local_count    = ARRAY_SIZE(ipa_mem_local_data),
+       .local          = ipa_mem_local_data,
+       .imem_addr      = 0x146a8000,
+       .imem_size      = 0x00002000,
+       .smem_id        = 497,
+       .smem_size      = 0x00002000,
+};
+
 /* Configuration data for the SC7180 SoC. */
 const struct ipa_data ipa_data_sc7180 = {
        .version        = IPA_VERSION_4_2,
        .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data),
        .endpoint_data  = ipa_gsi_endpoint_data,
        .resource_data  = &ipa_resource_data,
-       .mem_count      = ARRAY_SIZE(ipa_mem_data),
-       .mem_data       = ipa_mem_data,
+       .mem_data       = &ipa_mem_data,
 };
index 0d9c36e..52d4b84 100644 (file)
@@ -74,7 +74,6 @@ static const struct ipa_gsi_endpoint_data ipa_gsi_endpoint_data[] = {
                                .tx = {
                                        .status_endpoint =
                                                IPA_ENDPOINT_MODEM_AP_RX,
-                                       .delay  = true,
                                },
                        },
                },
@@ -235,7 +234,7 @@ static const struct ipa_resource_data ipa_resource_data = {
 };
 
 /* IPA-resident memory region configuration for the SDM845 SoC. */
-static const struct ipa_mem ipa_mem_data[] = {
+static const struct ipa_mem ipa_mem_local_data[] = {
        [IPA_MEM_UC_SHARED] = {
                .offset         = 0x0000,
                .size           = 0x0080,
@@ -318,12 +317,20 @@ static const struct ipa_mem ipa_mem_data[] = {
        },
 };
 
+static struct ipa_mem_data ipa_mem_data = {
+       .local_count    = ARRAY_SIZE(ipa_mem_local_data),
+       .local          = ipa_mem_local_data,
+       .imem_addr      = 0x146bd000,
+       .imem_size      = 0x00002000,
+       .smem_id        = 497,
+       .smem_size      = 0x00002000,
+};
+
 /* Configuration data for the SDM845 SoC. */
 const struct ipa_data ipa_data_sdm845 = {
        .version        = IPA_VERSION_3_5_1,
        .endpoint_count = ARRAY_SIZE(ipa_gsi_endpoint_data),
        .endpoint_data  = ipa_gsi_endpoint_data,
        .resource_data  = &ipa_resource_data,
-       .mem_count      = ARRAY_SIZE(ipa_mem_data),
-       .mem_data       = ipa_mem_data,
+       .mem_data       = &ipa_mem_data,
 };
index 7110de2..7fc1058 100644 (file)
@@ -80,18 +80,12 @@ struct gsi_channel_data {
 /**
  * struct ipa_endpoint_tx_data - configuration data for TX endpoints
  * @status_endpoint:   endpoint to which status elements are sent
- * @delay:             whether endpoint starts in delay mode
- *
- * Delay mode prevents a TX endpoint from transmitting anything, even if
- * commands have been presented to the hardware.  Once the endpoint exits
- * delay mode, queued transfer commands are sent.
  *
  * The @status_endpoint is only valid if the endpoint's @status_enable
  * flag is set.
  */
 struct ipa_endpoint_tx_data {
        enum ipa_endpoint_name status_endpoint;
-       bool delay;
 };
 
 /**
@@ -245,15 +239,21 @@ struct ipa_resource_data {
 };
 
 /**
- * struct ipa_mem - IPA-local memory region description
- * @offset:            offset in IPA memory space to base of the region
- * @size:              size in bytes base of the region
- * @canary_count:      number of 32-bit "canary" values that precede region
+ * struct ipa_mem - description of IPA memory regions
+ * @local_count:       number of regions defined in the local[] array
+ * @local:             array of IPA-local memory region descriptors
+ * @imem_addr:         physical address of IPA region within IMEM
+ * @imem_size:         size in bytes of IPA IMEM region
+ * @smem_id:           item identifier for IPA region within SMEM memory
+ * @imem_size:         size in bytes of the IPA SMEM region
  */
 struct ipa_mem_data {
-       u32 offset;
-       u16 size;
-       u16 canary_count;
+       u32 local_count;
+       const struct ipa_mem *local;
+       u32 imem_addr;
+       u32 imem_size;
+       u32 smem_id;
+       u32 smem_size;
 };
 
 /**
@@ -270,8 +270,7 @@ struct ipa_data {
        u32 endpoint_count;     /* # entries in endpoint_data[] */
        const struct ipa_gsi_endpoint_data *endpoint_data;
        const struct ipa_resource_data *resource_data;
-       u32 mem_count;          /* # entries in mem_data[] */
-       const struct ipa_mem *mem_data;
+       const struct ipa_mem_data *mem_data;
 };
 
 extern const struct ipa_data ipa_data_sdm845;
index 6de03be..5fec30e 100644 (file)
 /* The amount of RX buffer space consumed by standard skb overhead */
 #define IPA_RX_BUFFER_OVERHEAD (PAGE_SIZE - SKB_MAX_ORDER(NET_SKB_PAD, 0))
 
-#define IPA_ENDPOINT_STOP_RX_RETRIES           10
-#define IPA_ENDPOINT_STOP_RX_SIZE              1       /* bytes */
-
 #define IPA_ENDPOINT_RESET_AGGR_RETRY_MAX      3
 #define IPA_AGGR_TIME_LIMIT_DEFAULT            1000    /* microseconds */
 
-#define ENDPOINT_STOP_DMA_TIMEOUT              15      /* milliseconds */
-
 /** enum ipa_status_opcode - status element opcode hardware values */
 enum ipa_status_opcode {
        IPA_STATUS_OPCODE_PACKET                = 0x01,
@@ -284,25 +279,52 @@ static struct gsi_trans *ipa_endpoint_trans_alloc(struct ipa_endpoint *endpoint,
 /* suspend_delay represents suspend for RX, delay for TX endpoints.
  * Note that suspend is not supported starting with IPA v4.0.
  */
-static int
+static bool
 ipa_endpoint_init_ctrl(struct ipa_endpoint *endpoint, bool suspend_delay)
 {
        u32 offset = IPA_REG_ENDP_INIT_CTRL_N_OFFSET(endpoint->endpoint_id);
        struct ipa *ipa = endpoint->ipa;
+       bool state;
        u32 mask;
        u32 val;
 
-       /* assert(ipa->version == IPA_VERSION_3_5_1 */
+       /* Suspend is not supported for IPA v4.0+.  Delay doesn't work
+        * correctly on IPA v4.2.
+        *
+        * if (endpoint->toward_ipa)
+        *      assert(ipa->version != IPA_VERSION_4.2);
+        * else
+        *      assert(ipa->version == IPA_VERSION_3_5_1);
+        */
        mask = endpoint->toward_ipa ? ENDP_DELAY_FMASK : ENDP_SUSPEND_FMASK;
 
        val = ioread32(ipa->reg_virt + offset);
-       if (suspend_delay == !!(val & mask))
-               return -EALREADY;       /* Already set to desired state */
+       /* Don't bother if it's already in the requested state */
+       state = !!(val & mask);
+       if (suspend_delay != state) {
+               val ^= mask;
+               iowrite32(val, ipa->reg_virt + offset);
+       }
 
-       val ^= mask;
-       iowrite32(val, ipa->reg_virt + offset);
+       return state;
+}
 
-       return 0;
+/* We currently don't care what the previous state was for delay mode */
+static void
+ipa_endpoint_program_delay(struct ipa_endpoint *endpoint, bool enable)
+{
+       /* assert(endpoint->toward_ipa); */
+
+       (void)ipa_endpoint_init_ctrl(endpoint, enable);
+}
+
+/* Returns previous suspend state (true means it was enabled) */
+static bool
+ipa_endpoint_program_suspend(struct ipa_endpoint *endpoint, bool enable)
+{
+       /* assert(!endpoint->toward_ipa); */
+
+       return ipa_endpoint_init_ctrl(endpoint, enable);
 }
 
 /* Enable or disable delay or suspend mode on all modem endpoints */
@@ -311,7 +333,7 @@ void ipa_endpoint_modem_pause_all(struct ipa *ipa, bool enable)
        bool support_suspend;
        u32 endpoint_id;
 
-       /* DELAY mode doesn't work right on IPA v4.2 */
+       /* DELAY mode doesn't work correctly on IPA v4.2 */
        if (ipa->version == IPA_VERSION_4_2)
                return;
 
@@ -325,8 +347,10 @@ void ipa_endpoint_modem_pause_all(struct ipa *ipa, bool enable)
                        continue;
 
                /* Set TX delay mode, or for IPA v3.5.1 RX suspend mode */
-               if (endpoint->toward_ipa || support_suspend)
-                       (void)ipa_endpoint_init_ctrl(endpoint, enable);
+               if (endpoint->toward_ipa)
+                       ipa_endpoint_program_delay(endpoint, enable);
+               else if (support_suspend)
+                       (void)ipa_endpoint_program_suspend(endpoint, enable);
        }
 }
 
@@ -1133,10 +1157,10 @@ static int ipa_endpoint_reset_rx_aggr(struct ipa_endpoint *endpoint)
 {
        struct device *dev = &endpoint->ipa->pdev->dev;
        struct ipa *ipa = endpoint->ipa;
-       bool endpoint_suspended = false;
        struct gsi *gsi = &ipa->gsi;
+       bool suspended = false;
        dma_addr_t addr;
-       bool db_enable;
+       bool legacy;
        u32 retries;
        u32 len = 1;
        void *virt;
@@ -1164,8 +1188,7 @@ static int ipa_endpoint_reset_rx_aggr(struct ipa_endpoint *endpoint)
 
        /* Make sure the channel isn't suspended */
        if (endpoint->ipa->version == IPA_VERSION_3_5_1)
-               if (!ipa_endpoint_init_ctrl(endpoint, false))
-                       endpoint_suspended = true;
+               suspended = ipa_endpoint_program_suspend(endpoint, false);
 
        /* Start channel and do a 1 byte read */
        ret = gsi_channel_start(gsi, endpoint->channel_id);
@@ -1191,7 +1214,7 @@ static int ipa_endpoint_reset_rx_aggr(struct ipa_endpoint *endpoint)
 
        gsi_trans_read_byte_done(gsi, endpoint->channel_id);
 
-       ret = ipa_endpoint_stop(endpoint);
+       ret = gsi_channel_stop(gsi, endpoint->channel_id);
        if (ret)
                goto out_suspend_again;
 
@@ -1200,18 +1223,18 @@ static int ipa_endpoint_reset_rx_aggr(struct ipa_endpoint *endpoint)
         * complete the channel reset sequence.  Finish by suspending the
         * channel again (if necessary).
         */
-       db_enable = ipa->version == IPA_VERSION_3_5_1;
-       gsi_channel_reset(gsi, endpoint->channel_id, db_enable);
+       legacy = ipa->version == IPA_VERSION_3_5_1;
+       gsi_channel_reset(gsi, endpoint->channel_id, legacy);
 
        msleep(1);
 
        goto out_suspend_again;
 
 err_endpoint_stop:
-       ipa_endpoint_stop(endpoint);
+       (void)gsi_channel_stop(gsi, endpoint->channel_id);
 out_suspend_again:
-       if (endpoint_suspended)
-               (void)ipa_endpoint_init_ctrl(endpoint, true);
+       if (suspended)
+               (void)ipa_endpoint_program_suspend(endpoint, true);
        dma_unmap_single(dev, addr, len, DMA_FROM_DEVICE);
 out_kfree:
        kfree(virt);
@@ -1223,8 +1246,8 @@ static void ipa_endpoint_reset(struct ipa_endpoint *endpoint)
 {
        u32 channel_id = endpoint->channel_id;
        struct ipa *ipa = endpoint->ipa;
-       bool db_enable;
        bool special;
+       bool legacy;
        int ret = 0;
 
        /* On IPA v3.5.1, if an RX endpoint is reset while aggregation
@@ -1233,12 +1256,12 @@ static void ipa_endpoint_reset(struct ipa_endpoint *endpoint)
         *
         * IPA v3.5.1 enables the doorbell engine.  Newer versions do not.
         */
-       db_enable = ipa->version == IPA_VERSION_3_5_1;
+       legacy = ipa->version == IPA_VERSION_3_5_1;
        special = !endpoint->toward_ipa && endpoint->data->aggregation;
        if (special && ipa_endpoint_aggr_active(endpoint))
                ret = ipa_endpoint_reset_rx_aggr(endpoint);
        else
-               gsi_channel_reset(&ipa->gsi, channel_id, db_enable);
+               gsi_channel_reset(&ipa->gsi, channel_id, legacy);
 
        if (ret)
                dev_err(&ipa->pdev->dev,
@@ -1283,7 +1306,7 @@ static int ipa_endpoint_stop_rx_dma(struct ipa *ipa)
  */
 int ipa_endpoint_stop(struct ipa_endpoint *endpoint)
 {
-       u32 retries = endpoint->toward_ipa ? 0 : IPA_ENDPOINT_STOP_RX_RETRIES;
+       u32 retries = IPA_ENDPOINT_STOP_RX_RETRIES;
        int ret;
 
        do {
@@ -1291,12 +1314,9 @@ int ipa_endpoint_stop(struct ipa_endpoint *endpoint)
                struct gsi *gsi = &ipa->gsi;
 
                ret = gsi_channel_stop(gsi, endpoint->channel_id);
-               if (ret != -EAGAIN)
+               if (ret != -EAGAIN || endpoint->toward_ipa)
                        break;
 
-               if (endpoint->toward_ipa)
-                       continue;
-
                /* For IPA v3.5.1, send a DMA read task and check again */
                if (ipa->version == IPA_VERSION_3_5_1) {
                        ret = ipa_endpoint_stop_rx_dma(ipa);
@@ -1312,31 +1332,16 @@ int ipa_endpoint_stop(struct ipa_endpoint *endpoint)
 
 static void ipa_endpoint_program(struct ipa_endpoint *endpoint)
 {
-       struct device *dev = &endpoint->ipa->pdev->dev;
-       int ret;
-
        if (endpoint->toward_ipa) {
-               bool delay_mode = endpoint->data->tx.delay;
-
-               ret = ipa_endpoint_init_ctrl(endpoint, delay_mode);
-               /* Endpoint is expected to not be in delay mode */
-               if (!ret != delay_mode) {
-                       dev_warn(dev,
-                               "TX endpoint %u was %sin delay mode\n",
-                               endpoint->endpoint_id,
-                               delay_mode ? "already " : "");
-               }
+               if (endpoint->ipa->version != IPA_VERSION_4_2)
+                       ipa_endpoint_program_delay(endpoint, false);
                ipa_endpoint_init_hdr_ext(endpoint);
                ipa_endpoint_init_aggr(endpoint);
                ipa_endpoint_init_deaggr(endpoint);
                ipa_endpoint_init_seq(endpoint);
        } else {
-               if (endpoint->ipa->version == IPA_VERSION_3_5_1) {
-                       if (!ipa_endpoint_init_ctrl(endpoint, false))
-                               dev_warn(dev,
-                                       "RX endpoint %u was suspended\n",
-                                       endpoint->endpoint_id);
-               }
+               if (endpoint->ipa->version == IPA_VERSION_3_5_1)
+                       (void)ipa_endpoint_program_suspend(endpoint, false);
                ipa_endpoint_init_hdr_ext(endpoint);
                ipa_endpoint_init_aggr(endpoint);
        }
@@ -1377,12 +1382,13 @@ void ipa_endpoint_disable_one(struct ipa_endpoint *endpoint)
 {
        u32 mask = BIT(endpoint->endpoint_id);
        struct ipa *ipa = endpoint->ipa;
+       struct gsi *gsi = &ipa->gsi;
        int ret;
 
-       if (!(endpoint->ipa->enabled & mask))
+       if (!(ipa->enabled & mask))
                return;
 
-       endpoint->ipa->enabled ^= mask;
+       ipa->enabled ^= mask;
 
        if (!endpoint->toward_ipa) {
                ipa_endpoint_replenish_disable(endpoint);
@@ -1391,7 +1397,7 @@ void ipa_endpoint_disable_one(struct ipa_endpoint *endpoint)
        }
 
        /* Note that if stop fails, the channel's state is not well-defined */
-       ret = ipa_endpoint_stop(endpoint);
+       ret = gsi_channel_stop(gsi, endpoint->channel_id);
        if (ret)
                dev_err(&ipa->pdev->dev,
                        "error %d attempting to stop endpoint %u\n", ret,
@@ -1448,7 +1454,7 @@ void ipa_endpoint_suspend_one(struct ipa_endpoint *endpoint)
                 * aggregation frame, then simulating the arrival of such
                 * an interrupt.
                 */
-               WARN_ON(ipa_endpoint_init_ctrl(endpoint, true));
+               (void)ipa_endpoint_program_suspend(endpoint, true);
                ipa_endpoint_suspend_aggr(endpoint);
        }
 
@@ -1471,7 +1477,7 @@ void ipa_endpoint_resume_one(struct ipa_endpoint *endpoint)
        /* IPA v3.5.1 doesn't use channel start for resume */
        start_channel = endpoint->ipa->version != IPA_VERSION_3_5_1;
        if (!endpoint->toward_ipa && !start_channel)
-               WARN_ON(ipa_endpoint_init_ctrl(endpoint, false));
+               (void)ipa_endpoint_program_suspend(endpoint, false);
 
        ret = gsi_channel_resume(gsi, endpoint->channel_id, start_channel);
        if (ret)
index 4b336a1..3b297d6 100644 (file)
@@ -76,8 +76,6 @@ int ipa_endpoint_modem_exception_reset_all(struct ipa *ipa);
 
 int ipa_endpoint_skb_tx(struct ipa_endpoint *endpoint, struct sk_buff *skb);
 
-int ipa_endpoint_stop(struct ipa_endpoint *endpoint);
-
 void ipa_endpoint_exit_one(struct ipa_endpoint *endpoint);
 
 int ipa_endpoint_enable_one(struct ipa_endpoint *endpoint);
index 28998dc..e0b1fe3 100644 (file)
@@ -108,7 +108,7 @@ int ipa_setup(struct ipa *ipa)
        struct ipa_endpoint *command_endpoint;
        int ret;
 
-       /* IPA v4.0 and above don't use the doorbell engine. */
+       /* Setup for IPA v3.5.1 has some slight differences */
        ret = gsi_setup(&ipa->gsi, ipa->version == IPA_VERSION_3_5_1);
        if (ret)
                return ret;
@@ -778,7 +778,7 @@ static int ipa_probe(struct platform_device *pdev)
        if (ret)
                goto err_kfree_ipa;
 
-       ret = ipa_mem_init(ipa, data->mem_count, data->mem_data);
+       ret = ipa_mem_init(ipa, data->mem_data);
        if (ret)
                goto err_reg_exit;
 
index 42d2c29..3ef8141 100644 (file)
@@ -8,19 +8,24 @@
 #include <linux/bitfield.h>
 #include <linux/bug.h>
 #include <linux/dma-mapping.h>
+#include <linux/iommu.h>
 #include <linux/io.h>
+#include <linux/soc/qcom/smem.h>
 
 #include "ipa.h"
 #include "ipa_reg.h"
+#include "ipa_data.h"
 #include "ipa_cmd.h"
 #include "ipa_mem.h"
-#include "ipa_data.h"
 #include "ipa_table.h"
 #include "gsi_trans.h"
 
 /* "Canary" value placed between memory regions to detect overflow */
 #define IPA_MEM_CANARY_VAL             cpu_to_le32(0xdeadbeef)
 
+/* SMEM host id representing the modem. */
+#define QCOM_SMEM_HOST_MODEM   1
+
 /* Add an immediate command to a transaction that zeroes a memory region */
 static void
 ipa_mem_zero_region_add(struct gsi_trans *trans, const struct ipa_mem *mem)
@@ -265,16 +270,194 @@ int ipa_mem_zero_modem(struct ipa *ipa)
        return 0;
 }
 
+/**
+ * ipa_imem_init() - Initialize IMEM memory used by the IPA
+ * @ipa:       IPA pointer
+ * @addr:      Physical address of the IPA region in IMEM
+ * @size:      Size (bytes) of the IPA region in IMEM
+ *
+ * IMEM is a block of shared memory separate from system DRAM, and
+ * a portion of this memory is available for the IPA to use.  The
+ * modem accesses this memory directly, but the IPA accesses it
+ * via the IOMMU, using the AP's credentials.
+ *
+ * If this region exists (size > 0) we map it for read/write access
+ * through the IOMMU using the IPA device.
+ *
+ * Note: @addr and @size are not guaranteed to be page-aligned.
+ */
+static int ipa_imem_init(struct ipa *ipa, unsigned long addr, size_t size)
+{
+       struct device *dev = &ipa->pdev->dev;
+       struct iommu_domain *domain;
+       unsigned long iova;
+       phys_addr_t phys;
+       int ret;
+
+       if (!size)
+               return 0;       /* IMEM memory not used */
+
+       domain = iommu_get_domain_for_dev(dev);
+       if (!domain) {
+               dev_err(dev, "no IOMMU domain found for IMEM\n");
+               return -EINVAL;
+       }
+
+       /* Align the address down and the size up to page boundaries */
+       phys = addr & PAGE_MASK;
+       size = PAGE_ALIGN(size + addr - phys);
+       iova = phys;    /* We just want a direct mapping */
+
+       ret = iommu_map(domain, iova, phys, size, IOMMU_READ | IOMMU_WRITE);
+       if (ret)
+               return ret;
+
+       ipa->imem_iova = iova;
+       ipa->imem_size = size;
+
+       return 0;
+}
+
+static void ipa_imem_exit(struct ipa *ipa)
+{
+       struct iommu_domain *domain;
+       struct device *dev;
+
+       if (!ipa->imem_size)
+               return;
+
+       dev = &ipa->pdev->dev;
+       domain = iommu_get_domain_for_dev(dev);
+       if (domain) {
+               size_t size;
+
+               size = iommu_unmap(domain, ipa->imem_iova, ipa->imem_size);
+               if (size != ipa->imem_size)
+                       dev_warn(dev, "unmapped %zu IMEM bytes, expected %lu\n",
+                                size, ipa->imem_size);
+       } else {
+               dev_err(dev, "couldn't get IPA IOMMU domain for IMEM\n");
+       }
+
+       ipa->imem_size = 0;
+       ipa->imem_iova = 0;
+}
+
+/**
+ * ipa_smem_init() - Initialize SMEM memory used by the IPA
+ * @ipa:       IPA pointer
+ * @item:      Item ID of SMEM memory
+ * @size:      Size (bytes) of SMEM memory region
+ *
+ * SMEM is a managed block of shared DRAM, from which numbered "items"
+ * can be allocated.  One item is designated for use by the IPA.
+ *
+ * The modem accesses SMEM memory directly, but the IPA accesses it
+ * via the IOMMU, using the AP's credentials.
+ *
+ * If size provided is non-zero, we allocate it and map it for
+ * access through the IOMMU.
+ *
+ * Note: @size and the item address are is not guaranteed to be page-aligned.
+ */
+static int ipa_smem_init(struct ipa *ipa, u32 item, size_t size)
+{
+       struct device *dev = &ipa->pdev->dev;
+       struct iommu_domain *domain;
+       unsigned long iova;
+       phys_addr_t phys;
+       phys_addr_t addr;
+       size_t actual;
+       void *virt;
+       int ret;
+
+       if (!size)
+               return 0;       /* SMEM memory not used */
+
+       /* SMEM is memory shared between the AP and another system entity
+        * (in this case, the modem).  An allocation from SMEM is persistent
+        * until the AP reboots; there is no way to free an allocated SMEM
+        * region.  Allocation only reserves the space; to use it you need
+        * to "get" a pointer it (this implies no reference counting).
+        * The item might have already been allocated, in which case we
+        * use it unless the size isn't what we expect.
+        */
+       ret = qcom_smem_alloc(QCOM_SMEM_HOST_MODEM, item, size);
+       if (ret && ret != -EEXIST) {
+               dev_err(dev, "error %d allocating size %zu SMEM item %u\n",
+                       ret, size, item);
+               return ret;
+       }
+
+       /* Now get the address of the SMEM memory region */
+       virt = qcom_smem_get(QCOM_SMEM_HOST_MODEM, item, &actual);
+       if (IS_ERR(virt)) {
+               ret = PTR_ERR(virt);
+               dev_err(dev, "error %d getting SMEM item %u\n", ret, item);
+               return ret;
+       }
+
+       /* In case the region was already allocated, verify the size */
+       if (ret && actual != size) {
+               dev_err(dev, "SMEM item %u has size %zu, expected %zu\n",
+                       item, actual, size);
+               return -EINVAL;
+       }
+
+       domain = iommu_get_domain_for_dev(dev);
+       if (!domain) {
+               dev_err(dev, "no IOMMU domain found for SMEM\n");
+               return -EINVAL;
+       }
+
+       /* Align the address down and the size up to a page boundary */
+       addr = qcom_smem_virt_to_phys(virt) & PAGE_MASK;
+       phys = addr & PAGE_MASK;
+       size = PAGE_ALIGN(size + addr - phys);
+       iova = phys;    /* We just want a direct mapping */
+
+       ret = iommu_map(domain, iova, phys, size, IOMMU_READ | IOMMU_WRITE);
+       if (ret)
+               return ret;
+
+       ipa->smem_iova = iova;
+       ipa->smem_size = size;
+
+       return 0;
+}
+
+static void ipa_smem_exit(struct ipa *ipa)
+{
+       struct device *dev = &ipa->pdev->dev;
+       struct iommu_domain *domain;
+
+       domain = iommu_get_domain_for_dev(dev);
+       if (domain) {
+               size_t size;
+
+               size = iommu_unmap(domain, ipa->smem_iova, ipa->smem_size);
+               if (size != ipa->smem_size)
+                       dev_warn(dev, "unmapped %zu SMEM bytes, expected %lu\n",
+                                size, ipa->smem_size);
+
+       } else {
+               dev_err(dev, "couldn't get IPA IOMMU domain for SMEM\n");
+       }
+
+       ipa->smem_size = 0;
+       ipa->smem_iova = 0;
+}
+
 /* Perform memory region-related initialization */
-int ipa_mem_init(struct ipa *ipa, u32 count, const struct ipa_mem *mem)
+int ipa_mem_init(struct ipa *ipa, const struct ipa_mem_data *mem_data)
 {
        struct device *dev = &ipa->pdev->dev;
        struct resource *res;
        int ret;
 
-       if (count > IPA_MEM_COUNT) {
+       if (mem_data->local_count > IPA_MEM_COUNT) {
                dev_err(dev, "to many memory regions (%u > %u)\n",
-                       count, IPA_MEM_COUNT);
+                       mem_data->local_count, IPA_MEM_COUNT);
                return -EINVAL;
        }
 
@@ -302,13 +485,30 @@ int ipa_mem_init(struct ipa *ipa, u32 count, const struct ipa_mem *mem)
        ipa->mem_size = resource_size(res);
 
        /* The ipa->mem[] array is indexed by enum ipa_mem_id values */
-       ipa->mem = mem;
+       ipa->mem = mem_data->local;
+
+       ret = ipa_imem_init(ipa, mem_data->imem_addr, mem_data->imem_size);
+       if (ret)
+               goto err_unmap;
+
+       ret = ipa_smem_init(ipa, mem_data->smem_id, mem_data->smem_size);
+       if (ret)
+               goto err_imem_exit;
 
        return 0;
+
+err_imem_exit:
+       ipa_imem_exit(ipa);
+err_unmap:
+       memunmap(ipa->mem_virt);
+
+       return ret;
 }
 
 /* Inverse of ipa_mem_init() */
 void ipa_mem_exit(struct ipa *ipa)
 {
+       ipa_smem_exit(ipa);
+       ipa_imem_exit(ipa);
        memunmap(ipa->mem_virt);
 }
index 065cb49..f99180f 100644 (file)
@@ -7,6 +7,7 @@
 #define _IPA_MEM_H_
 
 struct ipa;
+struct ipa_mem_data;
 
 /**
  * DOC: IPA Local Memory
@@ -84,7 +85,7 @@ void ipa_mem_teardown(struct ipa *ipa);
 
 int ipa_mem_zero_modem(struct ipa *ipa);
 
-int ipa_mem_init(struct ipa *ipa, u32 count, const struct ipa_mem *mem);
+int ipa_mem_init(struct ipa *ipa, const struct ipa_mem_data *mem_data);
 void ipa_mem_exit(struct ipa *ipa);
 
 #endif /* _IPA_MEM_H_ */
index 55c9329..ed10818 100644 (file)
@@ -297,14 +297,13 @@ static void ipa_modem_crashed(struct ipa *ipa)
 
        ret = ipa_endpoint_modem_exception_reset_all(ipa);
        if (ret)
-               dev_err(dev, "error %d resetting exception endpoint",
-                       ret);
+               dev_err(dev, "error %d resetting exception endpoint\n", ret);
 
        ipa_endpoint_modem_pause_all(ipa, false);
 
        ret = ipa_modem_stop(ipa);
        if (ret)
-               dev_err(dev, "error %d stopping modem", ret);
+               dev_err(dev, "error %d stopping modem\n", ret);
 
        /* Now prepare for the next modem boot */
        ret = ipa_mem_zero_modem(ipa);
index f195f27..15e87c0 100644 (file)
@@ -131,6 +131,8 @@ static int ipvlan_init(struct net_device *dev)
        dev->gso_max_segs = phy_dev->gso_max_segs;
        dev->hard_header_len = phy_dev->hard_header_len;
 
+       netdev_lockdep_set_classes(dev);
+
        ipvlan->pcpu_stats = netdev_alloc_pcpu_stats(struct ipvl_pcpu_stats);
        if (!ipvlan->pcpu_stats)
                return -ENOMEM;
index 0d580d8..20b53e2 100644 (file)
@@ -1305,7 +1305,8 @@ static struct crypto_aead *macsec_alloc_tfm(char *key, int key_len, int icv_len)
        struct crypto_aead *tfm;
        int ret;
 
-       tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
+       /* Pick a sync gcm(aes) cipher to ensure order is preserved. */
+       tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
 
        if (IS_ERR(tfm))
                return tfm;
@@ -2640,11 +2641,12 @@ static int macsec_upd_offload(struct sk_buff *skb, struct genl_info *info)
        if (ret)
                goto rollback;
 
-       rtnl_unlock();
        /* Force features update, since they are different for SW MACSec and
         * HW offloading cases.
         */
        netdev_update_features(dev);
+
+       rtnl_unlock();
        return 0;
 
 rollback:
@@ -3809,7 +3811,7 @@ static int macsec_changelink(struct net_device *dev, struct nlattr *tb[],
                             struct netlink_ext_ack *extack)
 {
        struct macsec_dev *macsec = macsec_priv(dev);
-       struct macsec_tx_sa tx_sc;
+       struct macsec_tx_sc tx_sc;
        struct macsec_secy secy;
        int ret;
 
@@ -4002,11 +4004,11 @@ static int macsec_newlink(struct net *net, struct net_device *dev,
                          struct netlink_ext_ack *extack)
 {
        struct macsec_dev *macsec = macsec_priv(dev);
+       rx_handler_func_t *rx_handler;
+       u8 icv_len = DEFAULT_ICV_LEN;
        struct net_device *real_dev;
-       int err;
+       int err, mtu;
        sci_t sci;
-       u8 icv_len = DEFAULT_ICV_LEN;
-       rx_handler_func_t *rx_handler;
 
        if (!tb[IFLA_LINK])
                return -EINVAL;
@@ -4033,7 +4035,11 @@ static int macsec_newlink(struct net *net, struct net_device *dev,
 
        if (data && data[IFLA_MACSEC_ICV_LEN])
                icv_len = nla_get_u8(data[IFLA_MACSEC_ICV_LEN]);
-       dev->mtu = real_dev->mtu - icv_len - macsec_extra_len(true);
+       mtu = real_dev->mtu - icv_len - macsec_extra_len(true);
+       if (mtu < 0)
+               dev->mtu = 0;
+       else
+               dev->mtu = mtu;
 
        rx_handler = rtnl_dereference(real_dev->rx_handler);
        if (rx_handler && rx_handler != macsec_handle_frame)
@@ -4043,6 +4049,8 @@ static int macsec_newlink(struct net *net, struct net_device *dev,
        if (err < 0)
                return err;
 
+       netdev_lockdep_set_classes(dev);
+
        err = netdev_upper_dev_link(real_dev, dev, extack);
        if (err < 0)
                goto unregister;
index e7289d6..34eb073 100644 (file)
@@ -123,7 +123,8 @@ static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port,
        struct macvlan_dev *vlan;
        u32 idx = macvlan_eth_hash(addr);
 
-       hlist_for_each_entry_rcu(vlan, &port->vlan_hash[idx], hlist) {
+       hlist_for_each_entry_rcu(vlan, &port->vlan_hash[idx], hlist,
+                                lockdep_rtnl_is_held()) {
                if (ether_addr_equal_64bits(vlan->dev->dev_addr, addr))
                        return vlan;
        }
@@ -889,6 +890,8 @@ static int macvlan_init(struct net_device *dev)
        dev->gso_max_segs       = lowerdev->gso_max_segs;
        dev->hard_header_len    = lowerdev->hard_header_len;
 
+       netdev_lockdep_set_classes(dev);
+
        vlan->pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats);
        if (!vlan->pcpu_stats)
                return -ENOMEM;
@@ -1704,7 +1707,7 @@ static int macvlan_device_event(struct notifier_block *unused,
                                                struct macvlan_dev,
                                                list);
 
-               if (macvlan_sync_address(vlan->dev, dev->dev_addr))
+               if (vlan && macvlan_sync_address(vlan->dev, dev->dev_addr))
                        return NOTIFY_BAD;
 
                break;
index 3fa33d2..2a32f26 100644 (file)
@@ -157,6 +157,13 @@ config MDIO_I2C
 
          This is library mode.
 
+config MDIO_IPQ4019
+       tristate "Qualcomm IPQ4019 MDIO interface support"
+       depends on HAS_IOMEM && OF_MDIO
+       help
+         This driver supports the MDIO interface found in Qualcomm
+         IPQ40xx series Soc-s.
+
 config MDIO_IPQ8064
        tristate "Qualcomm IPQ8064 MDIO interface support"
        depends on HAS_IOMEM && OF_MDIO
@@ -346,6 +353,17 @@ config BROADCOM_PHY
          Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464,
          BCM5481, BCM54810 and BCM5482 PHYs.
 
+config BCM54140_PHY
+       tristate "Broadcom BCM54140 PHY"
+       depends on PHYLIB
+       depends on HWMON || HWMON=n
+       select BCM_NET_PHYLIB
+       help
+         Support the Broadcom BCM54140 Quad SGMII/QSGMII PHY.
+
+         This driver also supports the hardware monitoring of this PHY and
+         exposes voltage and temperature sensors.
+
 config BCM84881_PHY
        tristate "Broadcom BCM84881 PHY"
        depends on PHYLIB
index 2f5c709..dc9e53b 100644 (file)
@@ -37,6 +37,7 @@ obj-$(CONFIG_MDIO_CAVIUM)     += mdio-cavium.o
 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_IPQ4019)     += mdio-ipq4019.o
 obj-$(CONFIG_MDIO_IPQ8064)     += mdio-ipq8064.o
 obj-$(CONFIG_MDIO_MOXART)      += mdio-moxart.o
 obj-$(CONFIG_MDIO_MSCC_MIIM)   += mdio-mscc-miim.o
@@ -68,6 +69,7 @@ obj-$(CONFIG_BCM87XX_PHY)     += bcm87xx.o
 obj-$(CONFIG_BCM_CYGNUS_PHY)   += bcm-cygnus.o
 obj-$(CONFIG_BCM_NET_PHYLIB)   += bcm-phy-lib.o
 obj-$(CONFIG_BROADCOM_PHY)     += broadcom.o
+obj-$(CONFIG_BCM54140_PHY)     += bcm54140.o
 obj-$(CONFIG_BCM84881_PHY)     += bcm84881.o
 obj-$(CONFIG_CICADA_PHY)       += cicada.o
 obj-$(CONFIG_CORTINA_PHY)      += cortina.o
index 31f731e..f4fec5f 100644 (file)
@@ -43,6 +43,9 @@
 #define AT803X_INTR_STATUS                     0x13
 
 #define AT803X_SMART_SPEED                     0x14
+#define AT803X_SMART_SPEED_ENABLE              BIT(5)
+#define AT803X_SMART_SPEED_RETRY_LIMIT_MASK    GENMASK(4, 2)
+#define AT803X_SMART_SPEED_BYPASS_TIMER                BIT(1)
 #define AT803X_LED_CONTROL                     0x18
 
 #define AT803X_DEVICE_ADDR                     0x03
 #define AT803X_CLK_OUT_STRENGTH_HALF           1
 #define AT803X_CLK_OUT_STRENGTH_QUARTER                2
 
+#define AT803X_DEFAULT_DOWNSHIFT 5
+#define AT803X_MIN_DOWNSHIFT 2
+#define AT803X_MAX_DOWNSHIFT 9
+
 #define ATH9331_PHY_ID 0x004dd041
 #define ATH8030_PHY_ID 0x004dd076
 #define ATH8031_PHY_ID 0x004dd074
+#define ATH8032_PHY_ID 0x004dd023
 #define ATH8035_PHY_ID 0x004dd072
 #define AT803X_PHY_ID_MASK                     0xffffffef
 
@@ -712,6 +720,80 @@ static int at803x_read_status(struct phy_device *phydev)
        return 0;
 }
 
+static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
+{
+       int val;
+
+       val = phy_read(phydev, AT803X_SMART_SPEED);
+       if (val < 0)
+               return val;
+
+       if (val & AT803X_SMART_SPEED_ENABLE)
+               *d = FIELD_GET(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, val) + 2;
+       else
+               *d = DOWNSHIFT_DEV_DISABLE;
+
+       return 0;
+}
+
+static int at803x_set_downshift(struct phy_device *phydev, u8 cnt)
+{
+       u16 mask, set;
+       int ret;
+
+       switch (cnt) {
+       case DOWNSHIFT_DEV_DEFAULT_COUNT:
+               cnt = AT803X_DEFAULT_DOWNSHIFT;
+               fallthrough;
+       case AT803X_MIN_DOWNSHIFT ... AT803X_MAX_DOWNSHIFT:
+               set = AT803X_SMART_SPEED_ENABLE |
+                     AT803X_SMART_SPEED_BYPASS_TIMER |
+                     FIELD_PREP(AT803X_SMART_SPEED_RETRY_LIMIT_MASK, cnt - 2);
+               mask = AT803X_SMART_SPEED_RETRY_LIMIT_MASK;
+               break;
+       case DOWNSHIFT_DEV_DISABLE:
+               set = 0;
+               mask = AT803X_SMART_SPEED_ENABLE |
+                      AT803X_SMART_SPEED_BYPASS_TIMER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = phy_modify_changed(phydev, AT803X_SMART_SPEED, mask, set);
+
+       /* After changing the smart speed settings, we need to perform a
+        * software reset, use phy_init_hw() to make sure we set the
+        * reapply any values which might got lost during software reset.
+        */
+       if (ret == 1)
+               ret = phy_init_hw(phydev);
+
+       return ret;
+}
+
+static int at803x_get_tunable(struct phy_device *phydev,
+                             struct ethtool_tunable *tuna, void *data)
+{
+       switch (tuna->id) {
+       case ETHTOOL_PHY_DOWNSHIFT:
+               return at803x_get_downshift(phydev, data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int at803x_set_tunable(struct phy_device *phydev,
+                             struct ethtool_tunable *tuna, const void *data)
+{
+       switch (tuna->id) {
+       case ETHTOOL_PHY_DOWNSHIFT:
+               return at803x_set_downshift(phydev, *(const u8 *)data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 static struct phy_driver at803x_driver[] = {
 {
        /* Qualcomm Atheros AR8035 */
@@ -721,6 +803,7 @@ static struct phy_driver at803x_driver[] = {
        .probe                  = at803x_probe,
        .remove                 = at803x_remove,
        .config_init            = at803x_config_init,
+       .soft_reset             = genphy_soft_reset,
        .set_wol                = at803x_set_wol,
        .get_wol                = at803x_get_wol,
        .suspend                = at803x_suspend,
@@ -729,6 +812,8 @@ static struct phy_driver at803x_driver[] = {
        .read_status            = at803x_read_status,
        .ack_interrupt          = at803x_ack_interrupt,
        .config_intr            = at803x_config_intr,
+       .get_tunable            = at803x_get_tunable,
+       .set_tunable            = at803x_set_tunable,
 }, {
        /* Qualcomm Atheros AR8030 */
        .phy_id                 = ATH8030_PHY_ID,
@@ -753,6 +838,7 @@ static struct phy_driver at803x_driver[] = {
        .probe                  = at803x_probe,
        .remove                 = at803x_remove,
        .config_init            = at803x_config_init,
+       .soft_reset             = genphy_soft_reset,
        .set_wol                = at803x_set_wol,
        .get_wol                = at803x_get_wol,
        .suspend                = at803x_suspend,
@@ -762,6 +848,23 @@ static struct phy_driver at803x_driver[] = {
        .aneg_done              = at803x_aneg_done,
        .ack_interrupt          = &at803x_ack_interrupt,
        .config_intr            = &at803x_config_intr,
+       .get_tunable            = at803x_get_tunable,
+       .set_tunable            = at803x_set_tunable,
+}, {
+       /* Qualcomm Atheros AR8032 */
+       PHY_ID_MATCH_EXACT(ATH8032_PHY_ID),
+       .name                   = "Qualcomm Atheros AR8032",
+       .probe                  = at803x_probe,
+       .remove                 = at803x_remove,
+       .config_init            = at803x_config_init,
+       .link_change_notify     = at803x_link_change_notify,
+       .set_wol                = at803x_set_wol,
+       .get_wol                = at803x_get_wol,
+       .suspend                = at803x_suspend,
+       .resume                 = at803x_resume,
+       /* PHY_BASIC_FEATURES */
+       .ack_interrupt          = at803x_ack_interrupt,
+       .config_intr            = at803x_config_intr,
 }, {
        /* ATHEROS AR9331 */
        PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
@@ -778,6 +881,7 @@ module_phy_driver(at803x_driver);
 static struct mdio_device_id __maybe_unused atheros_tbl[] = {
        { ATH8030_PHY_ID, AT803X_PHY_ID_MASK },
        { ATH8031_PHY_ID, AT803X_PHY_ID_MASK },
+       { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) },
        { ATH8035_PHY_ID, AT803X_PHY_ID_MASK },
        { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
        { }
index e77b274..d5f9a27 100644 (file)
@@ -155,6 +155,86 @@ int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,
 }
 EXPORT_SYMBOL_GPL(bcm_phy_write_shadow);
 
+int __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)
+{
+       int val;
+
+       val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
+       if (val < 0)
+               return val;
+
+       return __phy_read(phydev, MII_BCM54XX_RDB_DATA);
+}
+EXPORT_SYMBOL_GPL(__bcm_phy_read_rdb);
+
+int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)
+{
+       int ret;
+
+       phy_lock_mdio_bus(phydev);
+       ret = __bcm_phy_read_rdb(phydev, rdb);
+       phy_unlock_mdio_bus(phydev);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(bcm_phy_read_rdb);
+
+int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)
+{
+       int ret;
+
+       ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
+       if (ret < 0)
+               return ret;
+
+       return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val);
+}
+EXPORT_SYMBOL_GPL(__bcm_phy_write_rdb);
+
+int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)
+{
+       int ret;
+
+       phy_lock_mdio_bus(phydev);
+       ret = __bcm_phy_write_rdb(phydev, rdb, val);
+       phy_unlock_mdio_bus(phydev);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(bcm_phy_write_rdb);
+
+int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)
+{
+       int new, ret;
+
+       ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
+       if (ret < 0)
+               return ret;
+
+       ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA);
+       if (ret < 0)
+               return ret;
+
+       new = (ret & ~mask) | set;
+       if (new == ret)
+               return 0;
+
+       return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new);
+}
+EXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb);
+
+int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)
+{
+       int ret;
+
+       phy_lock_mdio_bus(phydev);
+       ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set);
+       phy_unlock_mdio_bus(phydev);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(bcm_phy_modify_rdb);
+
 int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)
 {
        int val;
index 129df81..4d3de91 100644 (file)
@@ -48,6 +48,15 @@ int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,
                         u16 val);
 int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow);
 
+int __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val);
+int bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val);
+int __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb);
+int bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb);
+int __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask,
+                        u16 set);
+int bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask,
+                      u16 set);
+
 int bcm_phy_ack_intr(struct phy_device *phydev);
 int bcm_phy_config_intr(struct phy_device *phydev);
 
diff --git a/drivers/net/phy/bcm54140.c b/drivers/net/phy/bcm54140.c
new file mode 100644 (file)
index 0000000..9ef37a3
--- /dev/null
@@ -0,0 +1,857 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Broadcom BCM54140 Quad SGMII/QSGMII Copper/Fiber Gigabit PHY
+ *
+ * Copyright (c) 2020 Michael Walle <michael@walle.cc>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/brcmphy.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+
+#include "bcm-phy-lib.h"
+
+/* RDB per-port registers
+ */
+#define BCM54140_RDB_ISR               0x00a   /* interrupt status */
+#define BCM54140_RDB_IMR               0x00b   /* interrupt mask */
+#define  BCM54140_RDB_INT_LINK         BIT(1)  /* link status changed */
+#define  BCM54140_RDB_INT_SPEED                BIT(2)  /* link speed change */
+#define  BCM54140_RDB_INT_DUPLEX       BIT(3)  /* duplex mode changed */
+#define BCM54140_RDB_SPARE1            0x012   /* spare control 1 */
+#define  BCM54140_RDB_SPARE1_LSLM      BIT(2)  /* link speed LED mode */
+#define BCM54140_RDB_SPARE2            0x014   /* spare control 2 */
+#define  BCM54140_RDB_SPARE2_WS_RTRY_DIS BIT(8) /* wirespeed retry disable */
+#define  BCM54140_RDB_SPARE2_WS_RTRY_LIMIT GENMASK(4, 2) /* retry limit */
+#define BCM54140_RDB_SPARE3            0x015   /* spare control 3 */
+#define  BCM54140_RDB_SPARE3_BIT0      BIT(0)
+#define BCM54140_RDB_LED_CTRL          0x019   /* LED control */
+#define  BCM54140_RDB_LED_CTRL_ACTLINK0        BIT(4)
+#define  BCM54140_RDB_LED_CTRL_ACTLINK1        BIT(8)
+#define BCM54140_RDB_C_APWR            0x01a   /* auto power down control */
+#define  BCM54140_RDB_C_APWR_SINGLE_PULSE      BIT(8)  /* single pulse */
+#define  BCM54140_RDB_C_APWR_APD_MODE_DIS      0 /* ADP disable */
+#define  BCM54140_RDB_C_APWR_APD_MODE_EN       1 /* ADP enable */
+#define  BCM54140_RDB_C_APWR_APD_MODE_DIS2     2 /* ADP disable */
+#define  BCM54140_RDB_C_APWR_APD_MODE_EN_ANEG  3 /* ADP enable w/ aneg */
+#define  BCM54140_RDB_C_APWR_APD_MODE_MASK     GENMASK(6, 5)
+#define  BCM54140_RDB_C_APWR_SLP_TIM_MASK BIT(4)/* sleep timer */
+#define  BCM54140_RDB_C_APWR_SLP_TIM_2_7 0     /* 2.7s */
+#define  BCM54140_RDB_C_APWR_SLP_TIM_5_4 1     /* 5.4s */
+#define BCM54140_RDB_C_PWR             0x02a   /* copper power control */
+#define  BCM54140_RDB_C_PWR_ISOLATE    BIT(5)  /* super isolate mode */
+#define BCM54140_RDB_C_MISC_CTRL       0x02f   /* misc copper control */
+#define  BCM54140_RDB_C_MISC_CTRL_WS_EN BIT(4) /* wirespeed enable */
+
+/* RDB global registers
+ */
+#define BCM54140_RDB_TOP_IMR           0x82d   /* interrupt mask */
+#define  BCM54140_RDB_TOP_IMR_PORT0    BIT(4)
+#define  BCM54140_RDB_TOP_IMR_PORT1    BIT(5)
+#define  BCM54140_RDB_TOP_IMR_PORT2    BIT(6)
+#define  BCM54140_RDB_TOP_IMR_PORT3    BIT(7)
+#define BCM54140_RDB_MON_CTRL          0x831   /* monitor control */
+#define  BCM54140_RDB_MON_CTRL_V_MODE  BIT(3)  /* voltage mode */
+#define  BCM54140_RDB_MON_CTRL_SEL_MASK        GENMASK(2, 1)
+#define  BCM54140_RDB_MON_CTRL_SEL_TEMP        0       /* meassure temperature */
+#define  BCM54140_RDB_MON_CTRL_SEL_1V0 1       /* meassure AVDDL 1.0V */
+#define  BCM54140_RDB_MON_CTRL_SEL_3V3 2       /* meassure AVDDH 3.3V */
+#define  BCM54140_RDB_MON_CTRL_SEL_RR  3       /* meassure all round-robin */
+#define  BCM54140_RDB_MON_CTRL_PWR_DOWN        BIT(0)  /* power-down monitor */
+#define BCM54140_RDB_MON_TEMP_VAL      0x832   /* temperature value */
+#define BCM54140_RDB_MON_TEMP_MAX      0x833   /* temperature high thresh */
+#define BCM54140_RDB_MON_TEMP_MIN      0x834   /* temperature low thresh */
+#define  BCM54140_RDB_MON_TEMP_DATA_MASK GENMASK(9, 0)
+#define BCM54140_RDB_MON_1V0_VAL       0x835   /* AVDDL 1.0V value */
+#define BCM54140_RDB_MON_1V0_MAX       0x836   /* AVDDL 1.0V high thresh */
+#define BCM54140_RDB_MON_1V0_MIN       0x837   /* AVDDL 1.0V low thresh */
+#define  BCM54140_RDB_MON_1V0_DATA_MASK        GENMASK(10, 0)
+#define BCM54140_RDB_MON_3V3_VAL       0x838   /* AVDDH 3.3V value */
+#define BCM54140_RDB_MON_3V3_MAX       0x839   /* AVDDH 3.3V high thresh */
+#define BCM54140_RDB_MON_3V3_MIN       0x83a   /* AVDDH 3.3V low thresh */
+#define  BCM54140_RDB_MON_3V3_DATA_MASK        GENMASK(11, 0)
+#define BCM54140_RDB_MON_ISR           0x83b   /* interrupt status */
+#define  BCM54140_RDB_MON_ISR_3V3      BIT(2)  /* AVDDH 3.3V alarm */
+#define  BCM54140_RDB_MON_ISR_1V0      BIT(1)  /* AVDDL 1.0V alarm */
+#define  BCM54140_RDB_MON_ISR_TEMP     BIT(0)  /* temperature alarm */
+
+/* According to the datasheet the formula is:
+ *   T = 413.35 - (0.49055 * bits[9:0])
+ */
+#define BCM54140_HWMON_TO_TEMP(v) (413350L - (v) * 491)
+#define BCM54140_HWMON_FROM_TEMP(v) DIV_ROUND_CLOSEST_ULL(413350L - (v), 491)
+
+/* According to the datasheet the formula is:
+ *   U = bits[11:0] / 1024 * 220 / 0.2
+ *
+ * Normalized:
+ *   U = bits[11:0] / 4096 * 2514
+ */
+#define BCM54140_HWMON_TO_IN_1V0(v) ((v) * 2514 >> 11)
+#define BCM54140_HWMON_FROM_IN_1V0(v) DIV_ROUND_CLOSEST_ULL(((v) << 11), 2514)
+
+/* According to the datasheet the formula is:
+ *   U = bits[10:0] / 1024 * 880 / 0.7
+ *
+ * Normalized:
+ *   U = bits[10:0] / 2048 * 4400
+ */
+#define BCM54140_HWMON_TO_IN_3V3(v) ((v) * 4400 >> 12)
+#define BCM54140_HWMON_FROM_IN_3V3(v) DIV_ROUND_CLOSEST_ULL(((v) << 12), 4400)
+
+#define BCM54140_HWMON_TO_IN(ch, v) ((ch) ? BCM54140_HWMON_TO_IN_3V3(v) \
+                                         : BCM54140_HWMON_TO_IN_1V0(v))
+#define BCM54140_HWMON_FROM_IN(ch, v) ((ch) ? BCM54140_HWMON_FROM_IN_3V3(v) \
+                                           : BCM54140_HWMON_FROM_IN_1V0(v))
+#define BCM54140_HWMON_IN_MASK(ch) ((ch) ? BCM54140_RDB_MON_3V3_DATA_MASK \
+                                        : BCM54140_RDB_MON_1V0_DATA_MASK)
+#define BCM54140_HWMON_IN_VAL_REG(ch) ((ch) ? BCM54140_RDB_MON_3V3_VAL \
+                                           : BCM54140_RDB_MON_1V0_VAL)
+#define BCM54140_HWMON_IN_MIN_REG(ch) ((ch) ? BCM54140_RDB_MON_3V3_MIN \
+                                           : BCM54140_RDB_MON_1V0_MIN)
+#define BCM54140_HWMON_IN_MAX_REG(ch) ((ch) ? BCM54140_RDB_MON_3V3_MAX \
+                                           : BCM54140_RDB_MON_1V0_MAX)
+#define BCM54140_HWMON_IN_ALARM_BIT(ch) ((ch) ? BCM54140_RDB_MON_ISR_3V3 \
+                                             : BCM54140_RDB_MON_ISR_1V0)
+
+/* This PHY has two different PHY IDs depening on its MODE_SEL pin. This
+ * pin choses between 4x SGMII and QSGMII mode:
+ *   AE02_5009 4x SGMII
+ *   AE02_5019 QSGMII
+ */
+#define BCM54140_PHY_ID_MASK   0xffffffe8
+
+#define BCM54140_PHY_ID_REV(phy_id)    ((phy_id) & 0x7)
+#define BCM54140_REV_B0                        1
+
+#define BCM54140_DEFAULT_DOWNSHIFT 5
+#define BCM54140_MAX_DOWNSHIFT 9
+
+struct bcm54140_priv {
+       int port;
+       int base_addr;
+#if IS_ENABLED(CONFIG_HWMON)
+       /* protect the alarm bits */
+       struct mutex alarm_lock;
+       u16 alarm;
+#endif
+};
+
+#if IS_ENABLED(CONFIG_HWMON)
+static umode_t bcm54140_hwmon_is_visible(const void *data,
+                                        enum hwmon_sensor_types type,
+                                        u32 attr, int channel)
+{
+       switch (type) {
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_min:
+               case hwmon_in_max:
+                       return 0644;
+               case hwmon_in_label:
+               case hwmon_in_input:
+               case hwmon_in_alarm:
+                       return 0444;
+               default:
+                       return 0;
+               }
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp_min:
+               case hwmon_temp_max:
+                       return 0644;
+               case hwmon_temp_input:
+               case hwmon_temp_alarm:
+                       return 0444;
+               default:
+                       return 0;
+               }
+       default:
+               return 0;
+       }
+}
+
+static int bcm54140_hwmon_read_alarm(struct device *dev, unsigned int bit,
+                                    long *val)
+{
+       struct phy_device *phydev = dev_get_drvdata(dev);
+       struct bcm54140_priv *priv = phydev->priv;
+       int tmp, ret = 0;
+
+       mutex_lock(&priv->alarm_lock);
+
+       /* latch any alarm bits */
+       tmp = bcm_phy_read_rdb(phydev, BCM54140_RDB_MON_ISR);
+       if (tmp < 0) {
+               ret = tmp;
+               goto out;
+       }
+       priv->alarm |= tmp;
+
+       *val = !!(priv->alarm & bit);
+       priv->alarm &= ~bit;
+
+out:
+       mutex_unlock(&priv->alarm_lock);
+       return ret;
+}
+
+static int bcm54140_hwmon_read_temp(struct device *dev, u32 attr, long *val)
+{
+       struct phy_device *phydev = dev_get_drvdata(dev);
+       u16 reg;
+       int tmp;
+
+       switch (attr) {
+       case hwmon_temp_input:
+               reg = BCM54140_RDB_MON_TEMP_VAL;
+               break;
+       case hwmon_temp_min:
+               reg = BCM54140_RDB_MON_TEMP_MIN;
+               break;
+       case hwmon_temp_max:
+               reg = BCM54140_RDB_MON_TEMP_MAX;
+               break;
+       case hwmon_temp_alarm:
+               return bcm54140_hwmon_read_alarm(dev,
+                                                BCM54140_RDB_MON_ISR_TEMP,
+                                                val);
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       tmp = bcm_phy_read_rdb(phydev, reg);
+       if (tmp < 0)
+               return tmp;
+
+       *val = BCM54140_HWMON_TO_TEMP(tmp & BCM54140_RDB_MON_TEMP_DATA_MASK);
+
+       return 0;
+}
+
+static int bcm54140_hwmon_read_in(struct device *dev, u32 attr,
+                                 int channel, long *val)
+{
+       struct phy_device *phydev = dev_get_drvdata(dev);
+       u16 bit, reg;
+       int tmp;
+
+       switch (attr) {
+       case hwmon_in_input:
+               reg = BCM54140_HWMON_IN_VAL_REG(channel);
+               break;
+       case hwmon_in_min:
+               reg = BCM54140_HWMON_IN_MIN_REG(channel);
+               break;
+       case hwmon_in_max:
+               reg = BCM54140_HWMON_IN_MAX_REG(channel);
+               break;
+       case hwmon_in_alarm:
+               bit = BCM54140_HWMON_IN_ALARM_BIT(channel);
+               return bcm54140_hwmon_read_alarm(dev, bit, val);
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       tmp = bcm_phy_read_rdb(phydev, reg);
+       if (tmp < 0)
+               return tmp;
+
+       tmp &= BCM54140_HWMON_IN_MASK(channel);
+       *val = BCM54140_HWMON_TO_IN(channel, tmp);
+
+       return 0;
+}
+
+static int bcm54140_hwmon_read(struct device *dev,
+                              enum hwmon_sensor_types type, u32 attr,
+                              int channel, long *val)
+{
+       switch (type) {
+       case hwmon_temp:
+               return bcm54140_hwmon_read_temp(dev, attr, val);
+       case hwmon_in:
+               return bcm54140_hwmon_read_in(dev, attr, channel, val);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static const char *const bcm54140_hwmon_in_labels[] = {
+       "AVDDL",
+       "AVDDH",
+};
+
+static int bcm54140_hwmon_read_string(struct device *dev,
+                                     enum hwmon_sensor_types type, u32 attr,
+                                     int channel, const char **str)
+{
+       switch (type) {
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_label:
+                       *str = bcm54140_hwmon_in_labels[channel];
+                       return 0;
+               default:
+                       return -EOPNOTSUPP;
+               }
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int bcm54140_hwmon_write_temp(struct device *dev, u32 attr,
+                                    int channel, long val)
+{
+       struct phy_device *phydev = dev_get_drvdata(dev);
+       u16 mask = BCM54140_RDB_MON_TEMP_DATA_MASK;
+       u16 reg;
+
+       val = clamp_val(val, BCM54140_HWMON_TO_TEMP(mask),
+                       BCM54140_HWMON_TO_TEMP(0));
+
+       switch (attr) {
+       case hwmon_temp_min:
+               reg = BCM54140_RDB_MON_TEMP_MIN;
+               break;
+       case hwmon_temp_max:
+               reg = BCM54140_RDB_MON_TEMP_MAX;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return bcm_phy_modify_rdb(phydev, reg, mask,
+                                 BCM54140_HWMON_FROM_TEMP(val));
+}
+
+static int bcm54140_hwmon_write_in(struct device *dev, u32 attr,
+                                  int channel, long val)
+{
+       struct phy_device *phydev = dev_get_drvdata(dev);
+       u16 mask = BCM54140_HWMON_IN_MASK(channel);
+       u16 reg;
+
+       val = clamp_val(val, 0, BCM54140_HWMON_TO_IN(channel, mask));
+
+       switch (attr) {
+       case hwmon_in_min:
+               reg = BCM54140_HWMON_IN_MIN_REG(channel);
+               break;
+       case hwmon_in_max:
+               reg = BCM54140_HWMON_IN_MAX_REG(channel);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return bcm_phy_modify_rdb(phydev, reg, mask,
+                                 BCM54140_HWMON_FROM_IN(channel, val));
+}
+
+static int bcm54140_hwmon_write(struct device *dev,
+                               enum hwmon_sensor_types type, u32 attr,
+                               int channel, long val)
+{
+       switch (type) {
+       case hwmon_temp:
+               return bcm54140_hwmon_write_temp(dev, attr, channel, val);
+       case hwmon_in:
+               return bcm54140_hwmon_write_in(dev, attr, channel, val);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static const struct hwmon_channel_info *bcm54140_hwmon_info[] = {
+       HWMON_CHANNEL_INFO(temp,
+                          HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+                          HWMON_T_ALARM),
+       HWMON_CHANNEL_INFO(in,
+                          HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+                          HWMON_I_ALARM | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX |
+                          HWMON_I_ALARM | HWMON_I_LABEL),
+       NULL
+};
+
+static const struct hwmon_ops bcm54140_hwmon_ops = {
+       .is_visible = bcm54140_hwmon_is_visible,
+       .read = bcm54140_hwmon_read,
+       .read_string = bcm54140_hwmon_read_string,
+       .write = bcm54140_hwmon_write,
+};
+
+static const struct hwmon_chip_info bcm54140_chip_info = {
+       .ops = &bcm54140_hwmon_ops,
+       .info = bcm54140_hwmon_info,
+};
+
+static int bcm54140_enable_monitoring(struct phy_device *phydev)
+{
+       u16 mask, set;
+
+       /* 3.3V voltage mode */
+       set = BCM54140_RDB_MON_CTRL_V_MODE;
+
+       /* select round-robin */
+       mask = BCM54140_RDB_MON_CTRL_SEL_MASK;
+       set |= FIELD_PREP(BCM54140_RDB_MON_CTRL_SEL_MASK,
+                         BCM54140_RDB_MON_CTRL_SEL_RR);
+
+       /* remove power-down bit */
+       mask |= BCM54140_RDB_MON_CTRL_PWR_DOWN;
+
+       return bcm_phy_modify_rdb(phydev, BCM54140_RDB_MON_CTRL, mask, set);
+}
+
+static int bcm54140_probe_once(struct phy_device *phydev)
+{
+       struct device *hwmon;
+       int ret;
+
+       /* enable hardware monitoring */
+       ret = bcm54140_enable_monitoring(phydev);
+       if (ret)
+               return ret;
+
+       hwmon = devm_hwmon_device_register_with_info(&phydev->mdio.dev,
+                                                    "BCM54140", phydev,
+                                                    &bcm54140_chip_info,
+                                                    NULL);
+       return PTR_ERR_OR_ZERO(hwmon);
+}
+#endif
+
+static int bcm54140_base_read_rdb(struct phy_device *phydev, u16 rdb)
+{
+       int ret;
+
+       phy_lock_mdio_bus(phydev);
+       ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
+       if (ret < 0)
+               goto out;
+
+       ret = __phy_package_read(phydev, MII_BCM54XX_RDB_DATA);
+
+out:
+       phy_unlock_mdio_bus(phydev);
+       return ret;
+}
+
+static int bcm54140_base_write_rdb(struct phy_device *phydev,
+                                  u16 rdb, u16 val)
+{
+       int ret;
+
+       phy_lock_mdio_bus(phydev);
+       ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
+       if (ret < 0)
+               goto out;
+
+       ret = __phy_package_write(phydev, MII_BCM54XX_RDB_DATA, val);
+
+out:
+       phy_unlock_mdio_bus(phydev);
+       return ret;
+}
+
+/* Under some circumstances a core PLL may not lock, this will then prevent
+ * a successful link establishment. Restart the PLL after the voltages are
+ * stable to workaround this issue.
+ */
+static int bcm54140_b0_workaround(struct phy_device *phydev)
+{
+       int spare3;
+       int ret;
+
+       spare3 = bcm_phy_read_rdb(phydev, BCM54140_RDB_SPARE3);
+       if (spare3 < 0)
+               return spare3;
+
+       spare3 &= ~BCM54140_RDB_SPARE3_BIT0;
+
+       ret = bcm_phy_write_rdb(phydev, BCM54140_RDB_SPARE3, spare3);
+       if (ret)
+               return ret;
+
+       ret = phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN);
+       if (ret)
+               return ret;
+
+       ret = phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
+       if (ret)
+               return ret;
+
+       spare3 |= BCM54140_RDB_SPARE3_BIT0;
+
+       return bcm_phy_write_rdb(phydev, BCM54140_RDB_SPARE3, spare3);
+}
+
+/* The BCM54140 is a quad PHY where only the first port has access to the
+ * global register. Thus we need to find out its PHY address.
+ *
+ */
+static int bcm54140_get_base_addr_and_port(struct phy_device *phydev)
+{
+       struct bcm54140_priv *priv = phydev->priv;
+       struct mii_bus *bus = phydev->mdio.bus;
+       int addr, min_addr, max_addr;
+       int step = 1;
+       u32 phy_id;
+       int tmp;
+
+       min_addr = phydev->mdio.addr;
+       max_addr = phydev->mdio.addr;
+       addr = phydev->mdio.addr;
+
+       /* We scan forward and backwards and look for PHYs which have the
+        * same phy_id like we do. Step 1 will scan forward, step 2
+        * backwards. Once we are finished, we have a min_addr and
+        * max_addr which resembles the range of PHY addresses of the same
+        * type of PHY. There is one caveat; there may be many PHYs of
+        * the same type, but we know that each PHY takes exactly 4
+        * consecutive addresses. Therefore we can deduce our offset
+        * to the base address of this quad PHY.
+        */
+
+       while (1) {
+               if (step == 3) {
+                       break;
+               } else if (step == 1) {
+                       max_addr = addr;
+                       addr++;
+               } else {
+                       min_addr = addr;
+                       addr--;
+               }
+
+               if (addr < 0 || addr >= PHY_MAX_ADDR) {
+                       addr = phydev->mdio.addr;
+                       step++;
+                       continue;
+               }
+
+               /* read the PHY id */
+               tmp = mdiobus_read(bus, addr, MII_PHYSID1);
+               if (tmp < 0)
+                       return tmp;
+               phy_id = tmp << 16;
+               tmp = mdiobus_read(bus, addr, MII_PHYSID2);
+               if (tmp < 0)
+                       return tmp;
+               phy_id |= tmp;
+
+               /* see if it is still the same PHY */
+               if ((phy_id & phydev->drv->phy_id_mask) !=
+                   (phydev->drv->phy_id & phydev->drv->phy_id_mask)) {
+                       addr = phydev->mdio.addr;
+                       step++;
+               }
+       }
+
+       /* The range we get should be a multiple of four. Please note that both
+        * the min_addr and max_addr are inclusive. So we have to add one if we
+        * subtract them.
+        */
+       if ((max_addr - min_addr + 1) % 4) {
+               dev_err(&phydev->mdio.dev,
+                       "Detected Quad PHY IDs %d..%d doesn't make sense.\n",
+                       min_addr, max_addr);
+               return -EINVAL;
+       }
+
+       priv->port = (phydev->mdio.addr - min_addr) % 4;
+       priv->base_addr = phydev->mdio.addr - priv->port;
+
+       return 0;
+}
+
+static int bcm54140_probe(struct phy_device *phydev)
+{
+       struct bcm54140_priv *priv;
+       int ret;
+
+       priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       phydev->priv = priv;
+
+       ret = bcm54140_get_base_addr_and_port(phydev);
+       if (ret)
+               return ret;
+
+       devm_phy_package_join(&phydev->mdio.dev, phydev, priv->base_addr, 0);
+
+#if IS_ENABLED(CONFIG_HWMON)
+       mutex_init(&priv->alarm_lock);
+
+       if (phy_package_init_once(phydev)) {
+               ret = bcm54140_probe_once(phydev);
+               if (ret)
+                       return ret;
+       }
+#endif
+
+       phydev_dbg(phydev, "probed (port %d, base PHY address %d)\n",
+                  priv->port, priv->base_addr);
+
+       return 0;
+}
+
+static int bcm54140_config_init(struct phy_device *phydev)
+{
+       u16 reg = 0xffff;
+       int ret;
+
+       /* Apply hardware errata */
+       if (BCM54140_PHY_ID_REV(phydev->phy_id) == BCM54140_REV_B0) {
+               ret = bcm54140_b0_workaround(phydev);
+               if (ret)
+                       return ret;
+       }
+
+       /* Unmask events we are interested in. */
+       reg &= ~(BCM54140_RDB_INT_DUPLEX |
+                BCM54140_RDB_INT_SPEED |
+                BCM54140_RDB_INT_LINK);
+       ret = bcm_phy_write_rdb(phydev, BCM54140_RDB_IMR, reg);
+       if (ret)
+               return ret;
+
+       /* LED1=LINKSPD[1], LED2=LINKSPD[2], LED3=LINK/ACTIVITY */
+       ret = bcm_phy_modify_rdb(phydev, BCM54140_RDB_SPARE1,
+                                0, BCM54140_RDB_SPARE1_LSLM);
+       if (ret)
+               return ret;
+
+       ret = bcm_phy_modify_rdb(phydev, BCM54140_RDB_LED_CTRL,
+                                0, BCM54140_RDB_LED_CTRL_ACTLINK0);
+       if (ret)
+               return ret;
+
+       /* disable super isolate mode */
+       return bcm_phy_modify_rdb(phydev, BCM54140_RDB_C_PWR,
+                                 BCM54140_RDB_C_PWR_ISOLATE, 0);
+}
+
+static int bcm54140_did_interrupt(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = bcm_phy_read_rdb(phydev, BCM54140_RDB_ISR);
+
+       return (ret < 0) ? 0 : ret;
+}
+
+static int bcm54140_ack_intr(struct phy_device *phydev)
+{
+       int reg;
+
+       /* clear pending interrupts */
+       reg = bcm_phy_read_rdb(phydev, BCM54140_RDB_ISR);
+       if (reg < 0)
+               return reg;
+
+       return 0;
+}
+
+static int bcm54140_config_intr(struct phy_device *phydev)
+{
+       struct bcm54140_priv *priv = phydev->priv;
+       static const u16 port_to_imr_bit[] = {
+               BCM54140_RDB_TOP_IMR_PORT0, BCM54140_RDB_TOP_IMR_PORT1,
+               BCM54140_RDB_TOP_IMR_PORT2, BCM54140_RDB_TOP_IMR_PORT3,
+       };
+       int reg;
+
+       if (priv->port >= ARRAY_SIZE(port_to_imr_bit))
+               return -EINVAL;
+
+       reg = bcm54140_base_read_rdb(phydev, BCM54140_RDB_TOP_IMR);
+       if (reg < 0)
+               return reg;
+
+       if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+               reg &= ~port_to_imr_bit[priv->port];
+       else
+               reg |= port_to_imr_bit[priv->port];
+
+       return bcm54140_base_write_rdb(phydev, BCM54140_RDB_TOP_IMR, reg);
+}
+
+static int bcm54140_get_downshift(struct phy_device *phydev, u8 *data)
+{
+       int val;
+
+       val = bcm_phy_read_rdb(phydev, BCM54140_RDB_C_MISC_CTRL);
+       if (val < 0)
+               return val;
+
+       if (!(val & BCM54140_RDB_C_MISC_CTRL_WS_EN)) {
+               *data = DOWNSHIFT_DEV_DISABLE;
+               return 0;
+       }
+
+       val = bcm_phy_read_rdb(phydev, BCM54140_RDB_SPARE2);
+       if (val < 0)
+               return val;
+
+       if (val & BCM54140_RDB_SPARE2_WS_RTRY_DIS)
+               *data = 1;
+       else
+               *data = FIELD_GET(BCM54140_RDB_SPARE2_WS_RTRY_LIMIT, val) + 2;
+
+       return 0;
+}
+
+static int bcm54140_set_downshift(struct phy_device *phydev, u8 cnt)
+{
+       u16 mask, set;
+       int ret;
+
+       if (cnt > BCM54140_MAX_DOWNSHIFT && cnt != DOWNSHIFT_DEV_DEFAULT_COUNT)
+               return -EINVAL;
+
+       if (!cnt)
+               return bcm_phy_modify_rdb(phydev, BCM54140_RDB_C_MISC_CTRL,
+                                         BCM54140_RDB_C_MISC_CTRL_WS_EN, 0);
+
+       if (cnt == DOWNSHIFT_DEV_DEFAULT_COUNT)
+               cnt = BCM54140_DEFAULT_DOWNSHIFT;
+
+       if (cnt == 1) {
+               mask = 0;
+               set = BCM54140_RDB_SPARE2_WS_RTRY_DIS;
+       } else {
+               mask = BCM54140_RDB_SPARE2_WS_RTRY_DIS;
+               mask |= BCM54140_RDB_SPARE2_WS_RTRY_LIMIT;
+               set = FIELD_PREP(BCM54140_RDB_SPARE2_WS_RTRY_LIMIT, cnt - 2);
+       }
+       ret = bcm_phy_modify_rdb(phydev, BCM54140_RDB_SPARE2,
+                                mask, set);
+       if (ret)
+               return ret;
+
+       return bcm_phy_modify_rdb(phydev, BCM54140_RDB_C_MISC_CTRL,
+                                 0, BCM54140_RDB_C_MISC_CTRL_WS_EN);
+}
+
+static int bcm54140_get_edpd(struct phy_device *phydev, u16 *tx_interval)
+{
+       int val;
+
+       val = bcm_phy_read_rdb(phydev, BCM54140_RDB_C_APWR);
+       if (val < 0)
+               return val;
+
+       switch (FIELD_GET(BCM54140_RDB_C_APWR_APD_MODE_MASK, val)) {
+       case BCM54140_RDB_C_APWR_APD_MODE_DIS:
+       case BCM54140_RDB_C_APWR_APD_MODE_DIS2:
+               *tx_interval = ETHTOOL_PHY_EDPD_DISABLE;
+               break;
+       case BCM54140_RDB_C_APWR_APD_MODE_EN:
+       case BCM54140_RDB_C_APWR_APD_MODE_EN_ANEG:
+               switch (FIELD_GET(BCM54140_RDB_C_APWR_SLP_TIM_MASK, val)) {
+               case BCM54140_RDB_C_APWR_SLP_TIM_2_7:
+                       *tx_interval = 2700;
+                       break;
+               case BCM54140_RDB_C_APWR_SLP_TIM_5_4:
+                       *tx_interval = 5400;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int bcm54140_set_edpd(struct phy_device *phydev, u16 tx_interval)
+{
+       u16 mask, set;
+
+       mask = BCM54140_RDB_C_APWR_APD_MODE_MASK;
+       if (tx_interval == ETHTOOL_PHY_EDPD_DISABLE)
+               set = FIELD_PREP(BCM54140_RDB_C_APWR_APD_MODE_MASK,
+                                BCM54140_RDB_C_APWR_APD_MODE_DIS);
+       else
+               set = FIELD_PREP(BCM54140_RDB_C_APWR_APD_MODE_MASK,
+                                BCM54140_RDB_C_APWR_APD_MODE_EN_ANEG);
+
+       /* enable single pulse mode */
+       set |= BCM54140_RDB_C_APWR_SINGLE_PULSE;
+
+       /* set sleep timer */
+       mask |= BCM54140_RDB_C_APWR_SLP_TIM_MASK;
+       switch (tx_interval) {
+       case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS:
+       case ETHTOOL_PHY_EDPD_DISABLE:
+       case 2700:
+               set |= BCM54140_RDB_C_APWR_SLP_TIM_2_7;
+               break;
+       case 5400:
+               set |= BCM54140_RDB_C_APWR_SLP_TIM_5_4;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return bcm_phy_modify_rdb(phydev, BCM54140_RDB_C_APWR, mask, set);
+}
+
+static int bcm54140_get_tunable(struct phy_device *phydev,
+                               struct ethtool_tunable *tuna, void *data)
+{
+       switch (tuna->id) {
+       case ETHTOOL_PHY_DOWNSHIFT:
+               return bcm54140_get_downshift(phydev, data);
+       case ETHTOOL_PHY_EDPD:
+               return bcm54140_get_edpd(phydev, data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int bcm54140_set_tunable(struct phy_device *phydev,
+                               struct ethtool_tunable *tuna, const void *data)
+{
+       switch (tuna->id) {
+       case ETHTOOL_PHY_DOWNSHIFT:
+               return bcm54140_set_downshift(phydev, *(const u8 *)data);
+       case ETHTOOL_PHY_EDPD:
+               return bcm54140_set_edpd(phydev, *(const u16 *)data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static struct phy_driver bcm54140_drivers[] = {
+       {
+               .phy_id         = PHY_ID_BCM54140,
+               .phy_id_mask    = BCM54140_PHY_ID_MASK,
+               .name           = "Broadcom BCM54140",
+               .features       = PHY_GBIT_FEATURES,
+               .config_init    = bcm54140_config_init,
+               .did_interrupt  = bcm54140_did_interrupt,
+               .ack_interrupt  = bcm54140_ack_intr,
+               .config_intr    = bcm54140_config_intr,
+               .probe          = bcm54140_probe,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
+               .soft_reset     = genphy_soft_reset,
+               .get_tunable    = bcm54140_get_tunable,
+               .set_tunable    = bcm54140_set_tunable,
+       },
+};
+module_phy_driver(bcm54140_drivers);
+
+static struct mdio_device_id __maybe_unused bcm54140_tbl[] = {
+       { PHY_ID_BCM54140, BCM54140_PHY_ID_MASK },
+       { }
+};
+
+MODULE_AUTHOR("Michael Walle");
+MODULE_DESCRIPTION("Broadcom BCM54140 PHY driver");
+MODULE_DEVICE_TABLE(mdio, bcm54140_tbl);
+MODULE_LICENSE("GPL");
index 3840d2a..9717a16 100644 (file)
@@ -155,9 +155,6 @@ static int bcm84881_read_status(struct phy_device *phydev)
        if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete)
                phydev->link = false;
 
-       if (!phydev->link)
-               return 0;
-
        linkmode_zero(phydev->lp_advertising);
        phydev->speed = SPEED_UNKNOWN;
        phydev->duplex = DUPLEX_UNKNOWN;
@@ -165,6 +162,9 @@ static int bcm84881_read_status(struct phy_device *phydev)
        phydev->asym_pause = 0;
        phydev->mdix = 0;
 
+       if (!phydev->link)
+               return 0;
+
        if (phydev->autoneg_complete) {
                val = genphy_c45_read_lpa(phydev);
                if (val < 0)
index ae4873f..97201d5 100644 (file)
@@ -781,6 +781,19 @@ static struct phy_driver broadcom_drivers[] = {
        .get_strings    = bcm_phy_get_strings,
        .get_stats      = bcm53xx_phy_get_stats,
        .probe          = bcm53xx_phy_probe,
+}, {
+       .phy_id         = PHY_ID_BCM53125,
+       .phy_id_mask    = 0xfffffff0,
+       .name           = "Broadcom BCM53125",
+       .flags          = PHY_IS_INTERNAL,
+       /* PHY_GBIT_FEATURES */
+       .get_sset_count = bcm_phy_get_sset_count,
+       .get_strings    = bcm_phy_get_strings,
+       .get_stats      = bcm53xx_phy_get_stats,
+       .probe          = bcm53xx_phy_probe,
+       .config_init    = bcm54xx_config_init,
+       .ack_interrupt  = bcm_phy_ack_intr,
+       .config_intr    = bcm_phy_config_intr,
 }, {
        .phy_id         = PHY_ID_BCM89610,
        .phy_id_mask    = 0xfffffff0,
@@ -810,6 +823,7 @@ static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
        { PHY_ID_BCMAC131, 0xfffffff0 },
        { PHY_ID_BCM5241, 0xfffffff0 },
        { PHY_ID_BCM5395, 0xfffffff0 },
+       { PHY_ID_BCM53125, 0xfffffff0 },
        { PHY_ID_BCM89610, 0xfffffff0 },
        { }
 };
index 856cdc3..aac5136 100644 (file)
@@ -82,7 +82,6 @@ static struct phy_driver cortina_driver[] = {
        .features       = PHY_10GBIT_FEATURES,
        .config_aneg    = gen10g_config_aneg,
        .read_status    = cortina_read_status,
-       .soft_reset     = genphy_no_soft_reset,
        .probe          = cortina_probe,
 },
 };
index 415c273..ecbd5e0 100644 (file)
@@ -1120,7 +1120,7 @@ static struct dp83640_clock *dp83640_clock_get_bus(struct mii_bus *bus)
                goto out;
        }
        dp83640_clock_init(clock, bus);
-       list_add_tail(&phyter_clocks, &clock->list);
+       list_add_tail(&clock->list, &phyter_clocks);
 out:
        mutex_unlock(&phyter_clocks_lock);
 
index fe9aa3a..1dd19d0 100644 (file)
@@ -137,19 +137,18 @@ static int dp83822_set_wol(struct phy_device *phydev,
                        value &= ~DP83822_WOL_SECURE_ON;
                }
 
-               value |= (DP83822_WOL_EN | DP83822_WOL_INDICATION_SEL |
-                         DP83822_WOL_CLR_INDICATION);
-               phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
-                             value);
+               /* Clear any pending WoL interrupt */
+               phy_read(phydev, MII_DP83822_MISR2);
+
+               value |= DP83822_WOL_EN | DP83822_WOL_INDICATION_SEL |
+                        DP83822_WOL_CLR_INDICATION;
+
+               return phy_write_mmd(phydev, DP83822_DEVADDR,
+                                    MII_DP83822_WOL_CFG, value);
        } else {
-               value = phy_read_mmd(phydev, DP83822_DEVADDR,
-                                    MII_DP83822_WOL_CFG);
-               value &= ~DP83822_WOL_EN;
-               phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
-                             value);
+               return phy_clear_bits_mmd(phydev, DP83822_DEVADDR,
+                                         MII_DP83822_WOL_CFG, DP83822_WOL_EN);
        }
-
-       return 0;
 }
 
 static void dp83822_get_wol(struct phy_device *phydev,
@@ -258,12 +257,11 @@ static int dp83822_config_intr(struct phy_device *phydev)
 
 static int dp83822_config_init(struct phy_device *phydev)
 {
-       int value;
-
-       value = DP83822_WOL_MAGIC_EN | DP83822_WOL_SECURE_ON | DP83822_WOL_EN;
+       int value = DP83822_WOL_EN | DP83822_WOL_MAGIC_EN |
+                   DP83822_WOL_SECURE_ON;
 
-       return phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
-             value);
+       return phy_clear_bits_mmd(phydev, DP83822_DEVADDR,
+                                 MII_DP83822_WOL_CFG, value);
 }
 
 static int dp83822_phy_reset(struct phy_device *phydev)
index b55e3c0..4017ae1 100644 (file)
@@ -365,7 +365,7 @@ static int dp83867_get_downshift(struct phy_device *phydev, u8 *data)
                break;
        default:
                return -EINVAL;
-       };
+       }
 
        *data = enable ? count : DOWNSHIFT_DEV_DISABLE;
 
@@ -400,7 +400,7 @@ static int dp83867_set_downshift(struct phy_device *phydev, u8 cnt)
                        phydev_err(phydev,
                                   "Downshift count must be 1, 2, 4 or 8\n");
                        return -EINVAL;
-       };
+       }
 
        val = DP83867_DOWNSHIFT_EN;
        val |= FIELD_PREP(DP83867_DOWNSHIFT_ATTEMPT_MASK, count);
index 06f0883..d737253 100644 (file)
@@ -139,16 +139,19 @@ static int dp83811_set_wol(struct phy_device *phydev,
                        value &= ~DP83811_WOL_SECURE_ON;
                }
 
-               value |= (DP83811_WOL_EN | DP83811_WOL_INDICATION_SEL |
-                         DP83811_WOL_CLR_INDICATION);
-               phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
-                             value);
+               /* Clear any pending WoL interrupt */
+               phy_read(phydev, MII_DP83811_INT_STAT1);
+
+               value |= DP83811_WOL_EN | DP83811_WOL_INDICATION_SEL |
+                        DP83811_WOL_CLR_INDICATION;
+
+               return phy_write_mmd(phydev, DP83811_DEVADDR,
+                                    MII_DP83811_WOL_CFG, value);
        } else {
-               phy_clear_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
-                                  DP83811_WOL_EN);
+               return phy_clear_bits_mmd(phydev, DP83811_DEVADDR,
+                                         MII_DP83811_WOL_CFG, DP83811_WOL_EN);
        }
 
-       return 0;
 }
 
 static void dp83811_get_wol(struct phy_device *phydev,
@@ -292,8 +295,8 @@ static int dp83811_config_init(struct phy_device *phydev)
 
        value = DP83811_WOL_MAGIC_EN | DP83811_WOL_SECURE_ON | DP83811_WOL_EN;
 
-       return phy_write_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
-             value);
+       return phy_clear_bits_mmd(phydev, DP83811_DEVADDR, MII_DP83811_WOL_CFG,
+                                 value);
 }
 
 static int dp83811_phy_reset(struct phy_device *phydev)
index 4714ca0..7fc8e10 100644 (file)
@@ -1263,6 +1263,30 @@ static int marvell_read_status_page_an(struct phy_device *phydev,
        int lpa;
        int err;
 
+       if (!(status & MII_M1011_PHY_STATUS_RESOLVED)) {
+               phydev->link = 0;
+               return 0;
+       }
+
+       if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
+               phydev->duplex = DUPLEX_FULL;
+       else
+               phydev->duplex = DUPLEX_HALF;
+
+       switch (status & MII_M1011_PHY_STATUS_SPD_MASK) {
+       case MII_M1011_PHY_STATUS_1000:
+               phydev->speed = SPEED_1000;
+               break;
+
+       case MII_M1011_PHY_STATUS_100:
+               phydev->speed = SPEED_100;
+               break;
+
+       default:
+               phydev->speed = SPEED_10;
+               break;
+       }
+
        if (!fiber) {
                err = genphy_read_lpa(phydev);
                if (err < 0)
@@ -1291,28 +1315,6 @@ static int marvell_read_status_page_an(struct phy_device *phydev,
                }
        }
 
-       if (!(status & MII_M1011_PHY_STATUS_RESOLVED))
-               return 0;
-
-       if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
-               phydev->duplex = DUPLEX_FULL;
-       else
-               phydev->duplex = DUPLEX_HALF;
-
-       switch (status & MII_M1011_PHY_STATUS_SPD_MASK) {
-       case MII_M1011_PHY_STATUS_1000:
-               phydev->speed = SPEED_1000;
-               break;
-
-       case MII_M1011_PHY_STATUS_100:
-               phydev->speed = SPEED_100;
-               break;
-
-       default:
-               phydev->speed = SPEED_10;
-               break;
-       }
-
        return 0;
 }
 
index 7621bad..d4c2e62 100644 (file)
@@ -33,6 +33,8 @@
 #define MV_PHY_ALASKA_NBT_QUIRK_REV    (MARVELL_PHY_ID_88X3310 | 0xa)
 
 enum {
+       MV_PMA_FW_VER0          = 0xc011,
+       MV_PMA_FW_VER1          = 0xc012,
        MV_PMA_BOOT             = 0xc050,
        MV_PMA_BOOT_FATAL       = BIT(0),
 
@@ -64,6 +66,9 @@ enum {
        MV_PCS_CSSR1_SPD2_2500  = 0x0004,
        MV_PCS_CSSR1_SPD2_10000 = 0x0000,
 
+       /* Temperature read register (88E2110 only) */
+       MV_PCS_TEMP             = 0x8042,
+
        /* These registers appear at 0x800X and 0xa00X - the 0xa00X control
         * registers appear to set themselves to the 0x800X when AN is
         * restarted, but status registers appear readable from either.
@@ -73,7 +78,9 @@ enum {
 
        /* Vendor2 MMD registers */
        MV_V2_PORT_CTRL         = 0xf001,
-       MV_V2_PORT_CTRL_PWRDOWN = 0x0800,
+       MV_V2_PORT_CTRL_SWRST   = BIT(15),
+       MV_V2_PORT_CTRL_PWRDOWN = BIT(11),
+       /* Temperature control/read registers (88X3310 only) */
        MV_V2_TEMP_CTRL         = 0xf08a,
        MV_V2_TEMP_CTRL_MASK    = 0xc000,
        MV_V2_TEMP_CTRL_SAMPLE  = 0x0000,
@@ -83,6 +90,8 @@ enum {
 };
 
 struct mv3310_priv {
+       u32 firmware_ver;
+
        struct device *hwmon_dev;
        char *hwmon_name;
 };
@@ -99,6 +108,24 @@ static umode_t mv3310_hwmon_is_visible(const void *data,
        return 0;
 }
 
+static int mv3310_hwmon_read_temp_reg(struct phy_device *phydev)
+{
+       return phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP);
+}
+
+static int mv2110_hwmon_read_temp_reg(struct phy_device *phydev)
+{
+       return phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_TEMP);
+}
+
+static int mv10g_hwmon_read_temp_reg(struct phy_device *phydev)
+{
+       if (phydev->drv->phy_id == MARVELL_PHY_ID_88X3310)
+               return mv3310_hwmon_read_temp_reg(phydev);
+       else /* MARVELL_PHY_ID_88E2110 */
+               return mv2110_hwmon_read_temp_reg(phydev);
+}
+
 static int mv3310_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
                             u32 attr, int channel, long *value)
 {
@@ -111,7 +138,7 @@ static int mv3310_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
        }
 
        if (type == hwmon_temp && attr == hwmon_temp_input) {
-               temp = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP);
+               temp = mv10g_hwmon_read_temp_reg(phydev);
                if (temp < 0)
                        return temp;
 
@@ -164,6 +191,9 @@ static int mv3310_hwmon_config(struct phy_device *phydev, bool enable)
        u16 val;
        int ret;
 
+       if (phydev->drv->phy_id != MARVELL_PHY_ID_88X3310)
+               return 0;
+
        ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, MV_V2_TEMP,
                            MV_V2_TEMP_UNKNOWN);
        if (ret < 0)
@@ -235,8 +265,18 @@ static int mv3310_power_down(struct phy_device *phydev)
 
 static int mv3310_power_up(struct phy_device *phydev)
 {
-       return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL,
-                                 MV_V2_PORT_CTRL_PWRDOWN);
+       struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+       int ret;
+
+       ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL,
+                                MV_V2_PORT_CTRL_PWRDOWN);
+
+       if (phydev->drv->phy_id != MARVELL_PHY_ID_88X3310 ||
+           priv->firmware_ver < 0x00030000)
+               return ret;
+
+       return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL,
+                               MV_V2_PORT_CTRL_SWRST);
 }
 
 static int mv3310_reset(struct phy_device *phydev, u32 unit)
@@ -355,6 +395,22 @@ static int mv3310_probe(struct phy_device *phydev)
 
        dev_set_drvdata(&phydev->mdio.dev, priv);
 
+       ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0);
+       if (ret < 0)
+               return ret;
+
+       priv->firmware_ver = ret << 16;
+
+       ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER1);
+       if (ret < 0)
+               return ret;
+
+       priv->firmware_ver |= ret;
+
+       phydev_info(phydev, "Firmware version %u.%u.%u.%u\n",
+                   priv->firmware_ver >> 24, (priv->firmware_ver >> 16) & 255,
+                   (priv->firmware_ver >> 8) & 255, priv->firmware_ver & 255);
+
        /* Powering down the port when not in use saves about 600mW */
        ret = mv3310_power_down(phydev);
        if (ret)
@@ -697,7 +753,6 @@ static struct phy_driver mv3310_drivers[] = {
                .phy_id_mask    = MARVELL_PHY_ID_MASK,
                .name           = "mv88x3310",
                .get_features   = mv3310_get_features,
-               .soft_reset     = genphy_no_soft_reset,
                .config_init    = mv3310_config_init,
                .probe          = mv3310_probe,
                .suspend        = mv3310_suspend,
@@ -715,7 +770,6 @@ static struct phy_driver mv3310_drivers[] = {
                .probe          = mv3310_probe,
                .suspend        = mv3310_suspend,
                .resume         = mv3310_resume,
-               .soft_reset     = genphy_no_soft_reset,
                .config_init    = mv3310_config_init,
                .config_aneg    = mv3310_config_aneg,
                .aneg_done      = mv3310_aneg_done,
index f1ded03..77fc970 100644 (file)
@@ -159,7 +159,7 @@ static int iproc_mdio_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, priv);
 
-       dev_info(&pdev->dev, "Broadcom iProc MDIO bus at 0x%p\n", priv->base);
+       dev_info(&pdev->dev, "Broadcom iProc MDIO bus registered\n");
 
        return 0;
 
@@ -179,7 +179,7 @@ static int iproc_mdio_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM_SLEEP
-int iproc_mdio_resume(struct device *dev)
+static int iproc_mdio_resume(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct iproc_mdio_priv *priv = platform_get_drvdata(pdev);
diff --git a/drivers/net/phy/mdio-ipq4019.c b/drivers/net/phy/mdio-ipq4019.c
new file mode 100644 (file)
index 0000000..1ce81ff
--- /dev/null
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2020 Sartura Ltd. */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+
+#define MDIO_ADDR_REG                          0x44
+#define MDIO_DATA_WRITE_REG                    0x48
+#define MDIO_DATA_READ_REG                     0x4c
+#define MDIO_CMD_REG                           0x50
+#define MDIO_CMD_ACCESS_BUSY           BIT(16)
+#define MDIO_CMD_ACCESS_START          BIT(8)
+#define MDIO_CMD_ACCESS_CODE_READ      0
+#define MDIO_CMD_ACCESS_CODE_WRITE     1
+
+#define ipq4019_MDIO_TIMEOUT   10000
+#define ipq4019_MDIO_SLEEP             10
+
+struct ipq4019_mdio_data {
+       void __iomem    *membase;
+};
+
+static int ipq4019_mdio_wait_busy(struct mii_bus *bus)
+{
+       struct ipq4019_mdio_data *priv = bus->priv;
+       unsigned int busy;
+
+       return readl_poll_timeout(priv->membase + MDIO_CMD_REG, busy,
+                                 (busy & MDIO_CMD_ACCESS_BUSY) == 0,
+                                 ipq4019_MDIO_SLEEP, ipq4019_MDIO_TIMEOUT);
+}
+
+static int ipq4019_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+       struct ipq4019_mdio_data *priv = bus->priv;
+       unsigned int cmd;
+
+       /* Reject clause 45 */
+       if (regnum & MII_ADDR_C45)
+               return -EOPNOTSUPP;
+
+       if (ipq4019_mdio_wait_busy(bus))
+               return -ETIMEDOUT;
+
+       /* issue the phy address and reg */
+       writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);
+
+       cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ;
+
+       /* issue read command */
+       writel(cmd, priv->membase + MDIO_CMD_REG);
+
+       /* Wait read complete */
+       if (ipq4019_mdio_wait_busy(bus))
+               return -ETIMEDOUT;
+
+       /* Read and return data */
+       return readl(priv->membase + MDIO_DATA_READ_REG);
+}
+
+static int ipq4019_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
+                                                        u16 value)
+{
+       struct ipq4019_mdio_data *priv = bus->priv;
+       unsigned int cmd;
+
+       /* Reject clause 45 */
+       if (regnum & MII_ADDR_C45)
+               return -EOPNOTSUPP;
+
+       if (ipq4019_mdio_wait_busy(bus))
+               return -ETIMEDOUT;
+
+       /* issue the phy address and reg */
+       writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);
+
+       /* issue write data */
+       writel(value, priv->membase + MDIO_DATA_WRITE_REG);
+
+       cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE;
+       /* issue write command */
+       writel(cmd, priv->membase + MDIO_CMD_REG);
+
+       /* Wait write complete */
+       if (ipq4019_mdio_wait_busy(bus))
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int ipq4019_mdio_probe(struct platform_device *pdev)
+{
+       struct ipq4019_mdio_data *priv;
+       struct mii_bus *bus;
+       int ret;
+
+       bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*priv));
+       if (!bus)
+               return -ENOMEM;
+
+       priv = bus->priv;
+
+       priv->membase = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(priv->membase))
+               return PTR_ERR(priv->membase);
+
+       bus->name = "ipq4019_mdio";
+       bus->read = ipq4019_mdio_read;
+       bus->write = ipq4019_mdio_write;
+       bus->parent = &pdev->dev;
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%s%d", pdev->name, pdev->id);
+
+       ret = of_mdiobus_register(bus, pdev->dev.of_node);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, bus);
+
+       return 0;
+}
+
+static int ipq4019_mdio_remove(struct platform_device *pdev)
+{
+       struct mii_bus *bus = platform_get_drvdata(pdev);
+
+       mdiobus_unregister(bus);
+
+       return 0;
+}
+
+static const struct of_device_id ipq4019_mdio_dt_ids[] = {
+       { .compatible = "qcom,ipq4019-mdio" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ipq4019_mdio_dt_ids);
+
+static struct platform_driver ipq4019_mdio_driver = {
+       .probe = ipq4019_mdio_probe,
+       .remove = ipq4019_mdio_remove,
+       .driver = {
+               .name = "ipq4019-mdio",
+               .of_match_table = ipq4019_mdio_dt_ids,
+       },
+};
+
+module_platform_driver(ipq4019_mdio_driver);
+
+MODULE_DESCRIPTION("ipq4019 MDIO interface driver");
+MODULE_AUTHOR("Qualcomm Atheros");
+MODULE_LICENSE("Dual BSD/GPL");
index 522760c..255fdfc 100644 (file)
 
 static int mdiobus_register_gpiod(struct mdio_device *mdiodev)
 {
-       int error;
-
        /* Deassert the optional reset signal */
        mdiodev->reset_gpio = gpiod_get_optional(&mdiodev->dev,
                                                 "reset", GPIOD_OUT_LOW);
-       error = PTR_ERR_OR_ZERO(mdiodev->reset_gpio);
-       if (error)
-               return error;
+       if (IS_ERR(mdiodev->reset_gpio))
+               return PTR_ERR(mdiodev->reset_gpio);
 
        if (mdiodev->reset_gpio)
                gpiod_set_consumer_name(mdiodev->reset_gpio, "PHY reset");
@@ -170,7 +167,12 @@ EXPORT_SYMBOL(mdiobus_alloc_size);
 
 static void _devm_mdiobus_free(struct device *dev, void *res)
 {
-       mdiobus_free(*(struct mii_bus **)res);
+       struct mii_bus *bus = *(struct mii_bus **)res;
+
+       if (bus->is_managed_registered && bus->state == MDIOBUS_REGISTERED)
+               mdiobus_unregister(bus);
+
+       mdiobus_free(bus);
 }
 
 static int devm_mdiobus_match(struct device *dev, void *res, void *data)
@@ -210,6 +212,7 @@ struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv)
        if (bus) {
                *ptr = bus;
                devres_add(dev, ptr);
+               bus->is_managed = 1;
        } else {
                devres_free(ptr);
        }
@@ -464,7 +467,7 @@ static struct class mdio_bus_class = {
 
 /**
  * mdio_find_bus - Given the name of a mdiobus, find the mii_bus.
- * @mdio_bus_np: Pointer to the mii_bus.
+ * @mdio_name: The name of a mdiobus.
  *
  * Returns a reference to the mii_bus, or NULL if none found.  The
  * embedded struct device will have its reference count incremented,
@@ -611,6 +614,7 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
        }
 
        mutex_init(&bus->mdio_lock);
+       mutex_init(&bus->shared_lock);
 
        /* de-assert bus level PHY GPIO reset */
        gpiod = devm_gpiod_get_optional(&bus->dev, "reset", GPIOD_OUT_LOW);
@@ -627,8 +631,11 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
                gpiod_set_value_cansleep(gpiod, 0);
        }
 
-       if (bus->reset)
-               bus->reset(bus);
+       if (bus->reset) {
+               err = bus->reset(bus);
+               if (err)
+                       goto error_reset_gpiod;
+       }
 
        for (i = 0; i < PHY_MAX_ADDR; i++) {
                if ((bus->phy_mask & (1 << i)) == 0) {
@@ -657,7 +664,7 @@ error:
                mdiodev->device_remove(mdiodev);
                mdiodev->device_free(mdiodev);
        }
-
+error_reset_gpiod:
        /* Put PHYs in RESET to save power */
        if (bus->reset_gpiod)
                gpiod_set_value_cansleep(bus->reset_gpiod, 1);
index 05d2034..3fe5526 100644 (file)
@@ -19,6 +19,7 @@
  *                      ksz9477
  */
 
+#include <linux/bitfield.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/phy.h>
@@ -490,9 +491,50 @@ static int ksz9021_config_init(struct phy_device *phydev)
 
 /* MMD Address 0x2 */
 #define MII_KSZ9031RN_CONTROL_PAD_SKEW 4
+#define MII_KSZ9031RN_RX_CTL_M         GENMASK(7, 4)
+#define MII_KSZ9031RN_TX_CTL_M         GENMASK(3, 0)
+
 #define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5
+#define MII_KSZ9031RN_RXD3             GENMASK(15, 12)
+#define MII_KSZ9031RN_RXD2             GENMASK(11, 8)
+#define MII_KSZ9031RN_RXD1             GENMASK(7, 4)
+#define MII_KSZ9031RN_RXD0             GENMASK(3, 0)
+
 #define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6
+#define MII_KSZ9031RN_TXD3             GENMASK(15, 12)
+#define MII_KSZ9031RN_TXD2             GENMASK(11, 8)
+#define MII_KSZ9031RN_TXD1             GENMASK(7, 4)
+#define MII_KSZ9031RN_TXD0             GENMASK(3, 0)
+
 #define MII_KSZ9031RN_CLK_PAD_SKEW     8
+#define MII_KSZ9031RN_GTX_CLK          GENMASK(9, 5)
+#define MII_KSZ9031RN_RX_CLK           GENMASK(4, 0)
+
+/* KSZ9031 has internal RGMII_IDRX = 1.2ns and RGMII_IDTX = 0ns. To
+ * provide different RGMII options we need to configure delay offset
+ * for each pad relative to build in delay.
+ */
+/* keep rx as "No delay adjustment" and set rx_clk to +0.60ns to get delays of
+ * 1.80ns
+ */
+#define RX_ID                          0x7
+#define RX_CLK_ID                      0x19
+
+/* set rx to +0.30ns and rx_clk to -0.90ns to compensate the
+ * internal 1.2ns delay.
+ */
+#define RX_ND                          0xc
+#define RX_CLK_ND                      0x0
+
+/* set tx to -0.42ns and tx_clk to +0.96ns to get 1.38ns delay */
+#define TX_ID                          0x0
+#define TX_CLK_ID                      0x1f
+
+/* set tx and tx_clk to "No delay adjustment" to keep 0ns
+ * dealy
+ */
+#define TX_ND                          0x7
+#define TX_CLK_ND                      0xf
 
 /* MMD Address 0x1C */
 #define MII_KSZ9031RN_EDPD             0x23
@@ -501,7 +543,8 @@ static int ksz9021_config_init(struct phy_device *phydev)
 static int ksz9031_of_load_skew_values(struct phy_device *phydev,
                                       const struct device_node *of_node,
                                       u16 reg, size_t field_sz,
-                                      const char *field[], u8 numfields)
+                                      const char *field[], u8 numfields,
+                                      bool *update)
 {
        int val[4] = {-1, -2, -3, -4};
        int matches = 0;
@@ -517,6 +560,8 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev,
        if (!matches)
                return 0;
 
+       *update |= true;
+
        if (matches < numfields)
                newval = phy_read_mmd(phydev, 2, reg);
        else
@@ -565,6 +610,67 @@ static int ksz9031_enable_edpd(struct phy_device *phydev)
                             reg | MII_KSZ9031RN_EDPD_ENABLE);
 }
 
+static int ksz9031_config_rgmii_delay(struct phy_device *phydev)
+{
+       u16 rx, tx, rx_clk, tx_clk;
+       int ret;
+
+       switch (phydev->interface) {
+       case PHY_INTERFACE_MODE_RGMII:
+               tx = TX_ND;
+               tx_clk = TX_CLK_ND;
+               rx = RX_ND;
+               rx_clk = RX_CLK_ND;
+               break;
+       case PHY_INTERFACE_MODE_RGMII_ID:
+               tx = TX_ID;
+               tx_clk = TX_CLK_ID;
+               rx = RX_ID;
+               rx_clk = RX_CLK_ID;
+               break;
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+               tx = TX_ND;
+               tx_clk = TX_CLK_ND;
+               rx = RX_ID;
+               rx_clk = RX_CLK_ID;
+               break;
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+               tx = TX_ID;
+               tx_clk = TX_CLK_ID;
+               rx = RX_ND;
+               rx_clk = RX_CLK_ND;
+               break;
+       default:
+               return 0;
+       }
+
+       ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_CONTROL_PAD_SKEW,
+                           FIELD_PREP(MII_KSZ9031RN_RX_CTL_M, rx) |
+                           FIELD_PREP(MII_KSZ9031RN_TX_CTL_M, tx));
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_RX_DATA_PAD_SKEW,
+                           FIELD_PREP(MII_KSZ9031RN_RXD3, rx) |
+                           FIELD_PREP(MII_KSZ9031RN_RXD2, rx) |
+                           FIELD_PREP(MII_KSZ9031RN_RXD1, rx) |
+                           FIELD_PREP(MII_KSZ9031RN_RXD0, rx));
+       if (ret < 0)
+               return ret;
+
+       ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_TX_DATA_PAD_SKEW,
+                           FIELD_PREP(MII_KSZ9031RN_TXD3, tx) |
+                           FIELD_PREP(MII_KSZ9031RN_TXD2, tx) |
+                           FIELD_PREP(MII_KSZ9031RN_TXD1, tx) |
+                           FIELD_PREP(MII_KSZ9031RN_TXD0, tx));
+       if (ret < 0)
+               return ret;
+
+       return phy_write_mmd(phydev, 2, MII_KSZ9031RN_CLK_PAD_SKEW,
+                            FIELD_PREP(MII_KSZ9031RN_GTX_CLK, tx_clk) |
+                            FIELD_PREP(MII_KSZ9031RN_RX_CLK, rx_clk));
+}
+
 static int ksz9031_config_init(struct phy_device *phydev)
 {
        const struct device *dev = &phydev->mdio.dev;
@@ -597,21 +703,33 @@ static int ksz9031_config_init(struct phy_device *phydev)
        } while (!of_node && dev_walker);
 
        if (of_node) {
+               bool update = false;
+
+               if (phy_interface_is_rgmii(phydev)) {
+                       result = ksz9031_config_rgmii_delay(phydev);
+                       if (result < 0)
+                               return result;
+               }
+
                ksz9031_of_load_skew_values(phydev, of_node,
                                MII_KSZ9031RN_CLK_PAD_SKEW, 5,
-                               clk_skews, 2);
+                               clk_skews, 2, &update);
 
                ksz9031_of_load_skew_values(phydev, of_node,
                                MII_KSZ9031RN_CONTROL_PAD_SKEW, 4,
-                               control_skews, 2);
+                               control_skews, 2, &update);
 
                ksz9031_of_load_skew_values(phydev, of_node,
                                MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4,
-                               rx_data_skews, 4);
+                               rx_data_skews, 4, &update);
 
                ksz9031_of_load_skew_values(phydev, of_node,
                                MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4,
-                               tx_data_skews, 4);
+                               tx_data_skews, 4, &update);
+
+               if (update && phydev->interface != PHY_INTERFACE_MODE_RGMII)
+                       phydev_warn(phydev,
+                                   "*-skew-ps values should be used only with phy-mode = \"rgmii\"\n");
 
                /* Silicon Errata Sheet (DS80000691D or DS80000692D):
                 * When the device links in the 1000BASE-T slave mode only,
@@ -1204,7 +1322,7 @@ static struct phy_driver ksphy_driver[] = {
        .driver_data    = &ksz9021_type,
        .probe          = kszphy_probe,
        .config_init    = ksz9131_config_init,
-       .read_status    = ksz9031_read_status,
+       .read_status    = genphy_read_status,
        .ack_interrupt  = kszphy_ack_interrupt,
        .config_intr    = kszphy_config_intr,
        .get_sset_count = kszphy_get_sset_count,
index 001def4..fed3e39 100644 (file)
@@ -3,9 +3,21 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/delay.h>
 #include <linux/mii.h>
 #include <linux/phy.h>
 
+/* External Register Control Register */
+#define LAN87XX_EXT_REG_CTL                     (0x14)
+#define LAN87XX_EXT_REG_CTL_RD_CTL              (0x1000)
+#define LAN87XX_EXT_REG_CTL_WR_CTL              (0x0800)
+
+/* External Register Read Data Register */
+#define LAN87XX_EXT_REG_RD_DATA                 (0x15)
+
+/* External Register Write Data Register */
+#define LAN87XX_EXT_REG_WR_DATA                 (0x16)
+
 /* Interrupt Source Register */
 #define LAN87XX_INTERRUPT_SOURCE                (0x18)
 
 #define LAN87XX_MASK_LINK_UP                    (0x0004)
 #define LAN87XX_MASK_LINK_DOWN                  (0x0002)
 
+/* phyaccess nested types */
+#define        PHYACC_ATTR_MODE_READ           0
+#define        PHYACC_ATTR_MODE_WRITE          1
+#define        PHYACC_ATTR_MODE_MODIFY         2
+
+#define        PHYACC_ATTR_BANK_SMI            0
+#define        PHYACC_ATTR_BANK_MISC           1
+#define        PHYACC_ATTR_BANK_PCS            2
+#define        PHYACC_ATTR_BANK_AFE            3
+#define        PHYACC_ATTR_BANK_MAX            7
+
 #define DRIVER_AUTHOR  "Nisar Sayed <nisar.sayed@microchip.com>"
 #define DRIVER_DESC    "Microchip LAN87XX T1 PHY driver"
 
+struct access_ereg_val {
+       u8  mode;
+       u8  bank;
+       u8  offset;
+       u16 val;
+       u16 mask;
+};
+
+static int access_ereg(struct phy_device *phydev, u8 mode, u8 bank,
+                      u8 offset, u16 val)
+{
+       u16 ereg = 0;
+       int rc = 0;
+
+       if (mode > PHYACC_ATTR_MODE_WRITE || bank > PHYACC_ATTR_BANK_MAX)
+               return -EINVAL;
+
+       if (bank == PHYACC_ATTR_BANK_SMI) {
+               if (mode == PHYACC_ATTR_MODE_WRITE)
+                       rc = phy_write(phydev, offset, val);
+               else
+                       rc = phy_read(phydev, offset);
+               return rc;
+       }
+
+       if (mode == PHYACC_ATTR_MODE_WRITE) {
+               ereg = LAN87XX_EXT_REG_CTL_WR_CTL;
+               rc = phy_write(phydev, LAN87XX_EXT_REG_WR_DATA, val);
+               if (rc < 0)
+                       return rc;
+       } else {
+               ereg = LAN87XX_EXT_REG_CTL_RD_CTL;
+       }
+
+       ereg |= (bank << 8) | offset;
+
+       rc = phy_write(phydev, LAN87XX_EXT_REG_CTL, ereg);
+       if (rc < 0)
+               return rc;
+
+       if (mode == PHYACC_ATTR_MODE_READ)
+               rc = phy_read(phydev, LAN87XX_EXT_REG_RD_DATA);
+
+       return rc;
+}
+
+static int access_ereg_modify_changed(struct phy_device *phydev,
+                                     u8 bank, u8 offset, u16 val, u16 mask)
+{
+       int new = 0, rc = 0;
+
+       if (bank > PHYACC_ATTR_BANK_MAX)
+               return -EINVAL;
+
+       rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, bank, offset, val);
+       if (rc < 0)
+               return rc;
+
+       new = val | (rc & (mask ^ 0xFFFF));
+       rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, bank, offset, new);
+
+       return rc;
+}
+
+static int lan87xx_phy_init(struct phy_device *phydev)
+{
+       static const struct access_ereg_val init[] = {
+               /* TX Amplitude = 5 */
+               {PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_AFE, 0x0B,
+                0x000A, 0x001E},
+               /* Clear SMI interrupts */
+               {PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI, 0x18,
+                0, 0},
+               /* Clear MISC interrupts */
+               {PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_MISC, 0x08,
+                0, 0},
+               /* Turn on TC10 Ring Oscillator (ROSC) */
+               {PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_MISC, 0x20,
+                0x0020, 0x0020},
+               /* WUR Detect Length to 1.2uS, LPC Detect Length to 1.09uS */
+               {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_PCS, 0x20,
+                0x283C, 0},
+               /* Wake_In Debounce Length to 39uS, Wake_Out Length to 79uS */
+               {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x21,
+                0x274F, 0},
+               /* Enable Auto Wake Forward to Wake_Out, ROSC on, Sleep,
+                * and Wake_In to wake PHY
+                */
+               {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x20,
+                0x80A7, 0},
+               /* Enable WUP Auto Fwd, Enable Wake on MDI, Wakeup Debouncer
+                * to 128 uS
+                */
+               {PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_MISC, 0x24,
+                0xF110, 0},
+               /* Enable HW Init */
+               {PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_SMI, 0x1A,
+                0x0100, 0x0100},
+       };
+       int rc, i;
+
+       /* Start manual initialization procedures in Managed Mode */
+       rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI,
+                                       0x1a, 0x0000, 0x0100);
+       if (rc < 0)
+               return rc;
+
+       /* Soft Reset the SMI block */
+       rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI,
+                                       0x00, 0x8000, 0x8000);
+       if (rc < 0)
+               return rc;
+
+       /* Check to see if the self-clearing bit is cleared */
+       usleep_range(1000, 2000);
+       rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
+                        PHYACC_ATTR_BANK_SMI, 0x00, 0);
+       if (rc < 0)
+               return rc;
+       if ((rc & 0x8000) != 0)
+               return -ETIMEDOUT;
+
+       /* PHY Initialization */
+       for (i = 0; i < ARRAY_SIZE(init); i++) {
+               if (init[i].mode == PHYACC_ATTR_MODE_MODIFY) {
+                       rc = access_ereg_modify_changed(phydev, init[i].bank,
+                                                       init[i].offset,
+                                                       init[i].val,
+                                                       init[i].mask);
+               } else {
+                       rc = access_ereg(phydev, init[i].mode, init[i].bank,
+                                        init[i].offset, init[i].val);
+               }
+               if (rc < 0)
+                       return rc;
+       }
+
+       return 0;
+}
+
 static int lan87xx_phy_config_intr(struct phy_device *phydev)
 {
        int rc, val = 0;
@@ -40,6 +203,13 @@ static int lan87xx_phy_ack_interrupt(struct phy_device *phydev)
        return rc < 0 ? rc : 0;
 }
 
+static int lan87xx_config_init(struct phy_device *phydev)
+{
+       int rc = lan87xx_phy_init(phydev);
+
+       return rc < 0 ? rc : 0;
+}
+
 static struct phy_driver microchip_t1_phy_driver[] = {
        {
                .phy_id         = 0x0007c150,
@@ -48,6 +218,7 @@ static struct phy_driver microchip_t1_phy_driver[] = {
 
                .features       = PHY_BASIC_T1_FEATURES,
 
+               .config_init    = lan87xx_config_init,
                .config_aneg    = genphy_config_aneg,
 
                .ack_interrupt  = lan87xx_phy_ack_interrupt,
index 030bf8b..acdd8ee 100644 (file)
@@ -353,7 +353,6 @@ struct vsc8531_private {
        const struct vsc85xx_hw_stat *hw_stats;
        u64 *stats;
        int nstats;
-       bool pkg_init;
        /* For multiple port PHYs; the MDIO address of the base PHY in the
         * package.
         */
index acddef7..6508d65 100644 (file)
@@ -691,27 +691,23 @@ out_unlock:
 /* phydev->bus->mdio_lock should be locked when using this function */
 static int phy_base_write(struct phy_device *phydev, u32 regnum, u16 val)
 {
-       struct vsc8531_private *priv = phydev->priv;
-
        if (unlikely(!mutex_is_locked(&phydev->mdio.bus->mdio_lock))) {
                dev_err(&phydev->mdio.dev, "MDIO bus lock not held!\n");
                dump_stack();
        }
 
-       return __mdiobus_write(phydev->mdio.bus, priv->base_addr, regnum, val);
+       return __phy_package_write(phydev, regnum, val);
 }
 
 /* phydev->bus->mdio_lock should be locked when using this function */
 static int phy_base_read(struct phy_device *phydev, u32 regnum)
 {
-       struct vsc8531_private *priv = phydev->priv;
-
        if (unlikely(!mutex_is_locked(&phydev->mdio.bus->mdio_lock))) {
                dev_err(&phydev->mdio.dev, "MDIO bus lock not held!\n");
                dump_stack();
        }
 
-       return __mdiobus_read(phydev->mdio.bus, priv->base_addr, regnum);
+       return __phy_package_read(phydev, regnum);
 }
 
 /* bus->mdio_lock should be locked when using this function */
@@ -1287,66 +1283,38 @@ out:
        return ret;
 }
 
-/* Check if one PHY has already done the init of the parts common to all PHYs
- * in the Quad PHY package.
- */
-static bool vsc8584_is_pkg_init(struct phy_device *phydev, bool reversed)
+static void vsc8584_get_base_addr(struct phy_device *phydev)
 {
-       struct mdio_device **map = phydev->mdio.bus->mdio_map;
-       struct vsc8531_private *vsc8531;
-       struct phy_device *phy;
-       int i, addr;
-
-       /* VSC8584 is a Quad PHY */
-       for (i = 0; i < 4; i++) {
-               vsc8531 = phydev->priv;
-
-               if (reversed)
-                       addr = vsc8531->base_addr - i;
-               else
-                       addr = vsc8531->base_addr + i;
-
-               if (!map[addr])
-                       continue;
+       struct vsc8531_private *vsc8531 = phydev->priv;
+       u16 val, addr;
 
-               phy = container_of(map[addr], struct phy_device, mdio);
+       mutex_lock(&phydev->mdio.bus->mdio_lock);
+       __phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED);
 
-               if ((phy->phy_id & phydev->drv->phy_id_mask) !=
-                   (phydev->drv->phy_id & phydev->drv->phy_id_mask))
-                       continue;
+       addr = __phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_4);
+       addr >>= PHY_CNTL_4_ADDR_POS;
 
-               vsc8531 = phy->priv;
+       val = __phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL);
 
-               if (vsc8531 && vsc8531->pkg_init)
-                       return true;
-       }
+       __phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD);
+       mutex_unlock(&phydev->mdio.bus->mdio_lock);
 
-       return false;
+       if (val & PHY_ADDR_REVERSED)
+               vsc8531->base_addr = phydev->mdio.addr + addr;
+       else
+               vsc8531->base_addr = phydev->mdio.addr - addr;
 }
 
 static int vsc8584_config_init(struct phy_device *phydev)
 {
        struct vsc8531_private *vsc8531 = phydev->priv;
-       u16 addr, val;
        int ret, i;
+       u16 val;
 
        phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
 
        mutex_lock(&phydev->mdio.bus->mdio_lock);
 
-       __mdiobus_write(phydev->mdio.bus, phydev->mdio.addr,
-                       MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED);
-       addr = __mdiobus_read(phydev->mdio.bus, phydev->mdio.addr,
-                             MSCC_PHY_EXT_PHY_CNTL_4);
-       addr >>= PHY_CNTL_4_ADDR_POS;
-
-       val = __mdiobus_read(phydev->mdio.bus, phydev->mdio.addr,
-                            MSCC_PHY_ACTIPHY_CNTL);
-       if (val & PHY_ADDR_REVERSED)
-               vsc8531->base_addr = phydev->mdio.addr + addr;
-       else
-               vsc8531->base_addr = phydev->mdio.addr - addr;
-
        /* Some parts of the init sequence are identical for every PHY in the
         * package. Some parts are modifying the GPIO register bank which is a
         * set of registers that are affecting all PHYs, a few resetting the
@@ -1360,7 +1328,7 @@ static int vsc8584_config_init(struct phy_device *phydev)
         * do the correct init sequence for all PHYs that are package-critical
         * in this pre-init function.
         */
-       if (!vsc8584_is_pkg_init(phydev, val & PHY_ADDR_REVERSED ? 1 : 0)) {
+       if (phy_package_init_once(phydev)) {
                /* The following switch statement assumes that the lowest
                 * nibble of the phy_id_mask is always 0. This works because
                 * the lowest nibble of the PHY_ID's below are also 0.
@@ -1389,8 +1357,6 @@ static int vsc8584_config_init(struct phy_device *phydev)
                        goto err;
        }
 
-       vsc8531->pkg_init = true;
-
        phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
                       MSCC_PHY_PAGE_EXTENDED_GPIO);
 
@@ -1428,7 +1394,8 @@ static int vsc8584_config_init(struct phy_device *phydev)
 
        /* Disable SerDes for 100Base-FX */
        ret = vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF |
-                         PROC_CMD_FIBER_PORT(addr) | PROC_CMD_FIBER_DISABLE |
+                         PROC_CMD_FIBER_PORT(vsc8531->base_addr) |
+                         PROC_CMD_FIBER_DISABLE |
                          PROC_CMD_READ_MOD_WRITE_PORT |
                          PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_100BASE_FX);
        if (ret)
@@ -1436,7 +1403,8 @@ static int vsc8584_config_init(struct phy_device *phydev)
 
        /* Disable SerDes for 1000Base-X */
        ret = vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF |
-                         PROC_CMD_FIBER_PORT(addr) | PROC_CMD_FIBER_DISABLE |
+                         PROC_CMD_FIBER_PORT(vsc8531->base_addr) |
+                         PROC_CMD_FIBER_DISABLE |
                          PROC_CMD_READ_MOD_WRITE_PORT |
                          PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_1000BASE_X);
        if (ret)
@@ -1751,26 +1719,14 @@ static int vsc8514_config_init(struct phy_device *phydev)
 {
        struct vsc8531_private *vsc8531 = phydev->priv;
        unsigned long deadline;
-       u16 val, addr;
        int ret, i;
+       u16 val;
        u32 reg;
 
        phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
 
        mutex_lock(&phydev->mdio.bus->mdio_lock);
 
-       __phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED);
-
-       addr = __phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_4);
-       addr >>= PHY_CNTL_4_ADDR_POS;
-
-       val = __phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL);
-
-       if (val & PHY_ADDR_REVERSED)
-               vsc8531->base_addr = phydev->mdio.addr + addr;
-       else
-               vsc8531->base_addr = phydev->mdio.addr - addr;
-
        /* Some parts of the init sequence are identical for every PHY in the
         * package. Some parts are modifying the GPIO register bank which is a
         * set of registers that are affecting all PHYs, a few resetting the
@@ -1782,11 +1738,9 @@ static int vsc8514_config_init(struct phy_device *phydev)
         * do the correct init sequence for all PHYs that are package-critical
         * in this pre-init function.
         */
-       if (!vsc8584_is_pkg_init(phydev, val & PHY_ADDR_REVERSED ? 1 : 0))
+       if (phy_package_init_once(phydev))
                vsc8514_config_pre_init(phydev);
 
-       vsc8531->pkg_init = true;
-
        phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
                       MSCC_PHY_PAGE_EXTENDED_GPIO);
 
@@ -1992,6 +1946,10 @@ static int vsc8514_probe(struct phy_device *phydev)
 
        phydev->priv = vsc8531;
 
+       vsc8584_get_base_addr(phydev);
+       devm_phy_package_join(&phydev->mdio.dev, phydev,
+                             vsc8531->base_addr, 0);
+
        vsc8531->nleds = 4;
        vsc8531->supp_led_modes = VSC85XX_SUPP_LED_MODES;
        vsc8531->hw_stats = vsc85xx_hw_stats;
@@ -2047,6 +2005,10 @@ static int vsc8584_probe(struct phy_device *phydev)
 
        phydev->priv = vsc8531;
 
+       vsc8584_get_base_addr(phydev);
+       devm_phy_package_join(&phydev->mdio.dev, phydev,
+                             vsc8531->base_addr, 0);
+
        vsc8531->nleds = 4;
        vsc8531->supp_led_modes = VSC8584_SUPP_LED_MODES;
        vsc8531->hw_stats = vsc8584_hw_stats;
index 47caae7..ca5f9d4 100644 (file)
@@ -6,15 +6,19 @@
 #include <linux/delay.h>
 #include <linux/ethtool.h>
 #include <linux/kernel.h>
+#include <linux/mdio.h>
 #include <linux/mii.h>
 #include <linux/module.h>
 #include <linux/phy.h>
 #include <linux/hwmon.h>
 #include <linux/bitfield.h>
+#include <linux/of_mdio.h>
+#include <linux/of_irq.h>
 
 #define PHY_ID_MASK                    0xfffffff0
 #define PHY_ID_TJA1100                 0x0180dc40
 #define PHY_ID_TJA1101                 0x0180dd00
+#define PHY_ID_TJA1102                 0x0180dc80
 
 #define MII_ECTRL                      17
 #define MII_ECTRL_LINK_CONTROL         BIT(15)
@@ -26,6 +30,7 @@
 #define MII_ECTRL_WAKE_REQUEST         BIT(0)
 
 #define MII_CFG1                       18
+#define MII_CFG1_MASTER_SLAVE          BIT(15)
 #define MII_CFG1_AUTO_OP               BIT(14)
 #define MII_CFG1_SLEEP_CONFIRM         BIT(6)
 #define MII_CFG1_LED_MODE_MASK         GENMASK(5, 4)
 #define MII_INTSRC_TEMP_ERR            BIT(1)
 #define MII_INTSRC_UV_ERR              BIT(3)
 
+#define MII_INTEN                      22
+#define MII_INTEN_LINK_FAIL            BIT(10)
+#define MII_INTEN_LINK_UP              BIT(9)
+
 #define MII_COMMSTAT                   23
 #define MII_COMMSTAT_LINK_UP           BIT(15)
 
@@ -52,6 +61,8 @@
 struct tja11xx_priv {
        char            *hwmon_name;
        struct device   *hwmon_dev;
+       struct phy_device *phydev;
+       struct work_struct phy_register_work;
 };
 
 struct tja11xx_phy_stats {
@@ -157,6 +168,32 @@ static int tja11xx_soft_reset(struct phy_device *phydev)
        return genphy_soft_reset(phydev);
 }
 
+static int tja11xx_config_aneg(struct phy_device *phydev)
+{
+       u16 ctl = 0;
+       int ret;
+
+       switch (phydev->master_slave_set) {
+       case MASTER_SLAVE_CFG_MASTER_FORCE:
+               ctl |= MII_CFG1_MASTER_SLAVE;
+               break;
+       case MASTER_SLAVE_CFG_SLAVE_FORCE:
+               break;
+       case MASTER_SLAVE_CFG_UNKNOWN:
+       case MASTER_SLAVE_CFG_UNSUPPORTED:
+               return 0;
+       default:
+               phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+               return -ENOTSUPP;
+       }
+
+       ret = phy_modify_changed(phydev, MII_CFG1, MII_CFG1_MASTER_SLAVE, ctl);
+       if (ret < 0)
+               return ret;
+
+       return __genphy_config_aneg(phydev, ret);
+}
+
 static int tja11xx_config_init(struct phy_device *phydev)
 {
        int ret;
@@ -180,6 +217,7 @@ static int tja11xx_config_init(struct phy_device *phydev)
                        return ret;
                break;
        case PHY_ID_TJA1101:
+       case PHY_ID_TJA1102:
                ret = phy_set_bits(phydev, MII_COMMCFG, MII_COMMCFG_AUTO_OP);
                if (ret)
                        return ret;
@@ -213,10 +251,22 @@ static int tja11xx_read_status(struct phy_device *phydev)
 {
        int ret;
 
+       phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
+       phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
+
        ret = genphy_update_link(phydev);
        if (ret)
                return ret;
 
+       ret = phy_read(phydev, MII_CFG1);
+       if (ret < 0)
+               return ret;
+
+       if (ret & MII_CFG1_MASTER_SLAVE)
+               phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
+       else
+               phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
+
        if (phydev->link) {
                ret = phy_read(phydev, MII_COMMSTAT);
                if (ret < 0)
@@ -317,16 +367,12 @@ static const struct hwmon_chip_info tja11xx_hwmon_chip_info = {
        .info           = tja11xx_hwmon_info,
 };
 
-static int tja11xx_probe(struct phy_device *phydev)
+static int tja11xx_hwmon_register(struct phy_device *phydev,
+                                 struct tja11xx_priv *priv)
 {
        struct device *dev = &phydev->mdio.dev;
-       struct tja11xx_priv *priv;
        int i;
 
-       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
        priv->hwmon_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
        if (!priv->hwmon_name)
                return -ENOMEM;
@@ -344,6 +390,152 @@ static int tja11xx_probe(struct phy_device *phydev)
        return PTR_ERR_OR_ZERO(priv->hwmon_dev);
 }
 
+static int tja11xx_probe(struct phy_device *phydev)
+{
+       struct device *dev = &phydev->mdio.dev;
+       struct tja11xx_priv *priv;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->phydev = phydev;
+
+       return tja11xx_hwmon_register(phydev, priv);
+}
+
+static void tja1102_p1_register(struct work_struct *work)
+{
+       struct tja11xx_priv *priv = container_of(work, struct tja11xx_priv,
+                                                phy_register_work);
+       struct phy_device *phydev_phy0 = priv->phydev;
+       struct mii_bus *bus = phydev_phy0->mdio.bus;
+       struct device *dev = &phydev_phy0->mdio.dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *child;
+       int ret;
+
+       for_each_available_child_of_node(np, child) {
+               struct phy_device *phy;
+               int addr;
+
+               addr = of_mdio_parse_addr(dev, child);
+               if (addr < 0) {
+                       dev_err(dev, "Can't parse addr\n");
+                       continue;
+               } else if (addr != phydev_phy0->mdio.addr + 1) {
+                       /* Currently we care only about double PHY chip TJA1102.
+                        * If some day NXP will decide to bring chips with more
+                        * PHYs, this logic should be reworked.
+                        */
+                       dev_err(dev, "Unexpected address. Should be: %i\n",
+                               phydev_phy0->mdio.addr + 1);
+                       continue;
+               }
+
+               if (mdiobus_is_registered_device(bus, addr)) {
+                       dev_err(dev, "device is already registered\n");
+                       continue;
+               }
+
+               /* Real PHY ID of Port 1 is 0 */
+               phy = phy_device_create(bus, addr, PHY_ID_TJA1102, false, NULL);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "Can't create PHY device for Port 1: %i\n",
+                               addr);
+                       continue;
+               }
+
+               /* Overwrite parent device. phy_device_create() set parent to
+                * the mii_bus->dev, which is not correct in case.
+                */
+               phy->mdio.dev.parent = dev;
+
+               ret = of_mdiobus_phy_device_register(bus, phy, child, addr);
+               if (ret) {
+                       /* All resources needed for Port 1 should be already
+                        * available for Port 0. Both ports use the same
+                        * interrupt line, so -EPROBE_DEFER would make no sense
+                        * here.
+                        */
+                       dev_err(dev, "Can't register Port 1. Unexpected error: %i\n",
+                               ret);
+                       phy_device_free(phy);
+               }
+       }
+}
+
+static int tja1102_p0_probe(struct phy_device *phydev)
+{
+       struct device *dev = &phydev->mdio.dev;
+       struct tja11xx_priv *priv;
+       int ret;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->phydev = phydev;
+       INIT_WORK(&priv->phy_register_work, tja1102_p1_register);
+
+       ret = tja11xx_hwmon_register(phydev, priv);
+       if (ret)
+               return ret;
+
+       schedule_work(&priv->phy_register_work);
+
+       return 0;
+}
+
+static int tja1102_match_phy_device(struct phy_device *phydev, bool port0)
+{
+       int ret;
+
+       if ((phydev->phy_id & PHY_ID_MASK) != PHY_ID_TJA1102)
+               return 0;
+
+       ret = phy_read(phydev, MII_PHYSID2);
+       if (ret < 0)
+               return ret;
+
+       /* TJA1102 Port 1 has phyid 0 and doesn't support temperature
+        * and undervoltage alarms.
+        */
+       if (port0)
+               return ret ? 1 : 0;
+
+       return !ret;
+}
+
+static int tja1102_p0_match_phy_device(struct phy_device *phydev)
+{
+       return tja1102_match_phy_device(phydev, true);
+}
+
+static int tja1102_p1_match_phy_device(struct phy_device *phydev)
+{
+       return tja1102_match_phy_device(phydev, false);
+}
+
+static int tja11xx_ack_interrupt(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = phy_read(phydev, MII_INTSRC);
+
+       return (ret < 0) ? ret : 0;
+}
+
+static int tja11xx_config_intr(struct phy_device *phydev)
+{
+       int value = 0;
+
+       if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+               value = MII_INTEN_LINK_FAIL | MII_INTEN_LINK_UP;
+
+       return phy_write(phydev, MII_INTEN, value);
+}
+
 static struct phy_driver tja11xx_driver[] = {
        {
                PHY_ID_MATCH_MODEL(PHY_ID_TJA1100),
@@ -351,6 +543,7 @@ static struct phy_driver tja11xx_driver[] = {
                .features       = PHY_BASIC_T1_FEATURES,
                .probe          = tja11xx_probe,
                .soft_reset     = tja11xx_soft_reset,
+               .config_aneg    = tja11xx_config_aneg,
                .config_init    = tja11xx_config_init,
                .read_status    = tja11xx_read_status,
                .suspend        = genphy_suspend,
@@ -366,8 +559,44 @@ static struct phy_driver tja11xx_driver[] = {
                .features       = PHY_BASIC_T1_FEATURES,
                .probe          = tja11xx_probe,
                .soft_reset     = tja11xx_soft_reset,
+               .config_aneg    = tja11xx_config_aneg,
+               .config_init    = tja11xx_config_init,
+               .read_status    = tja11xx_read_status,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
+               .set_loopback   = genphy_loopback,
+               /* Statistics */
+               .get_sset_count = tja11xx_get_sset_count,
+               .get_strings    = tja11xx_get_strings,
+               .get_stats      = tja11xx_get_stats,
+       }, {
+               .name           = "NXP TJA1102 Port 0",
+               .features       = PHY_BASIC_T1_FEATURES,
+               .probe          = tja1102_p0_probe,
+               .soft_reset     = tja11xx_soft_reset,
+               .config_aneg    = tja11xx_config_aneg,
+               .config_init    = tja11xx_config_init,
+               .read_status    = tja11xx_read_status,
+               .match_phy_device = tja1102_p0_match_phy_device,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
+               .set_loopback   = genphy_loopback,
+               /* Statistics */
+               .get_sset_count = tja11xx_get_sset_count,
+               .get_strings    = tja11xx_get_strings,
+               .get_stats      = tja11xx_get_stats,
+               .ack_interrupt  = tja11xx_ack_interrupt,
+               .config_intr    = tja11xx_config_intr,
+
+       }, {
+               .name           = "NXP TJA1102 Port 1",
+               .features       = PHY_BASIC_T1_FEATURES,
+               /* currently no probe for Port 1 is need */
+               .soft_reset     = tja11xx_soft_reset,
+               .config_aneg    = tja11xx_config_aneg,
                .config_init    = tja11xx_config_init,
                .read_status    = tja11xx_read_status,
+               .match_phy_device = tja1102_p1_match_phy_device,
                .suspend        = genphy_suspend,
                .resume         = genphy_resume,
                .set_loopback   = genphy_loopback,
@@ -375,6 +604,8 @@ static struct phy_driver tja11xx_driver[] = {
                .get_sset_count = tja11xx_get_sset_count,
                .get_strings    = tja11xx_get_strings,
                .get_stats      = tja11xx_get_stats,
+               .ack_interrupt  = tja11xx_ack_interrupt,
+               .config_intr    = tja11xx_config_intr,
        }
 };
 
@@ -383,6 +614,7 @@ module_phy_driver(tja11xx_driver);
 static struct mdio_device_id __maybe_unused tja11xx_tbl[] = {
        { PHY_ID_MATCH_MODEL(PHY_ID_TJA1100) },
        { PHY_ID_MATCH_MODEL(PHY_ID_TJA1101) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_TJA1102) },
        { }
 };
 
index 67ba47a..defe09d 100644 (file)
@@ -564,6 +564,5 @@ struct phy_driver genphy_c45_driver = {
        .phy_id         = 0xffffffff,
        .phy_id_mask    = 0xffffffff,
        .name           = "Generic Clause 45 PHY",
-       .soft_reset     = genphy_no_soft_reset,
        .read_status    = genphy_c45_read_status,
 };
index 72c69a9..8c22d02 100644 (file)
@@ -295,7 +295,7 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev,
                         phydev->advertising, autoneg == AUTONEG_ENABLE);
 
        phydev->duplex = duplex;
-
+       phydev->master_slave_set = cmd->base.master_slave_cfg;
        phydev->mdix_ctrl = cmd->base.eth_tp_mdix_ctrl;
 
        /* Restart the PHY */
@@ -314,6 +314,8 @@ void phy_ethtool_ksettings_get(struct phy_device *phydev,
 
        cmd->base.speed = phydev->speed;
        cmd->base.duplex = phydev->duplex;
+       cmd->base.master_slave_cfg = phydev->master_slave_get;
+       cmd->base.master_slave_state = phydev->master_slave_state;
        if (phydev->interface == PHY_INTERFACE_MODE_MOCA)
                cmd->base.port = PORT_BNC;
        else
index ac27841..83fc8e1 100644 (file)
@@ -1082,8 +1082,12 @@ int phy_init_hw(struct phy_device *phydev)
        if (!phydev->drv)
                return 0;
 
-       if (phydev->drv->soft_reset)
+       if (phydev->drv->soft_reset) {
                ret = phydev->drv->soft_reset(phydev);
+               /* see comment in genphy_soft_reset for an explanation */
+               if (!ret)
+                       phydev->suspended = 0;
+       }
 
        if (ret < 0)
                return ret;
@@ -1457,6 +1461,144 @@ bool phy_driver_is_genphy_10g(struct phy_device *phydev)
 }
 EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g);
 
+/**
+ * phy_package_join - join a common PHY group
+ * @phydev: target phy_device struct
+ * @addr: cookie and PHY address for global register access
+ * @priv_size: if non-zero allocate this amount of bytes for private data
+ *
+ * This joins a PHY group and provides a shared storage for all phydevs in
+ * this group. This is intended to be used for packages which contain
+ * more than one PHY, for example a quad PHY transceiver.
+ *
+ * The addr parameter serves as a cookie which has to have the same value
+ * for all members of one group and as a PHY address to access generic
+ * registers of a PHY package. Usually, one of the PHY addresses of the
+ * different PHYs in the package provides access to these global registers.
+ * The address which is given here, will be used in the phy_package_read()
+ * and phy_package_write() convenience functions. If your PHY doesn't have
+ * global registers you can just pick any of the PHY addresses.
+ *
+ * This will set the shared pointer of the phydev to the shared storage.
+ * If this is the first call for a this cookie the shared storage will be
+ * allocated. If priv_size is non-zero, the given amount of bytes are
+ * allocated for the priv member.
+ *
+ * Returns < 1 on error, 0 on success. Esp. calling phy_package_join()
+ * with the same cookie but a different priv_size is an error.
+ */
+int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+       struct phy_package_shared *shared;
+       int ret;
+
+       if (addr < 0 || addr >= PHY_MAX_ADDR)
+               return -EINVAL;
+
+       mutex_lock(&bus->shared_lock);
+       shared = bus->shared[addr];
+       if (!shared) {
+               ret = -ENOMEM;
+               shared = kzalloc(sizeof(*shared), GFP_KERNEL);
+               if (!shared)
+                       goto err_unlock;
+               if (priv_size) {
+                       shared->priv = kzalloc(priv_size, GFP_KERNEL);
+                       if (!shared->priv)
+                               goto err_free;
+                       shared->priv_size = priv_size;
+               }
+               shared->addr = addr;
+               refcount_set(&shared->refcnt, 1);
+               bus->shared[addr] = shared;
+       } else {
+               ret = -EINVAL;
+               if (priv_size && priv_size != shared->priv_size)
+                       goto err_unlock;
+               refcount_inc(&shared->refcnt);
+       }
+       mutex_unlock(&bus->shared_lock);
+
+       phydev->shared = shared;
+
+       return 0;
+
+err_free:
+       kfree(shared);
+err_unlock:
+       mutex_unlock(&bus->shared_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_package_join);
+
+/**
+ * phy_package_leave - leave a common PHY group
+ * @phydev: target phy_device struct
+ *
+ * This leaves a PHY group created by phy_package_join(). If this phydev
+ * was the last user of the shared data between the group, this data is
+ * freed. Resets the phydev->shared pointer to NULL.
+ */
+void phy_package_leave(struct phy_device *phydev)
+{
+       struct phy_package_shared *shared = phydev->shared;
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       if (!shared)
+               return;
+
+       if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
+               bus->shared[shared->addr] = NULL;
+               mutex_unlock(&bus->shared_lock);
+               kfree(shared->priv);
+               kfree(shared);
+       }
+
+       phydev->shared = NULL;
+}
+EXPORT_SYMBOL_GPL(phy_package_leave);
+
+static void devm_phy_package_leave(struct device *dev, void *res)
+{
+       phy_package_leave(*(struct phy_device **)res);
+}
+
+/**
+ * devm_phy_package_join - resource managed phy_package_join()
+ * @dev: device that is registering this PHY package
+ * @phydev: target phy_device struct
+ * @addr: cookie and PHY address for global register access
+ * @priv_size: if non-zero allocate this amount of bytes for private data
+ *
+ * Managed phy_package_join(). Shared storage fetched by this function,
+ * phy_package_leave() is automatically called on driver detach. See
+ * phy_package_join() for more information.
+ */
+int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
+                         int addr, size_t priv_size)
+{
+       struct phy_device **ptr;
+       int ret;
+
+       ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
+                          GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+
+       ret = phy_package_join(phydev, addr, priv_size);
+
+       if (!ret) {
+               *ptr = phydev;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(devm_phy_package_join);
+
 /**
  * phy_detach - detach a PHY device from its network device
  * @phydev: target phy_device struct
@@ -1524,6 +1666,9 @@ int phy_suspend(struct phy_device *phydev)
        struct phy_driver *phydrv = phydev->drv;
        int ret;
 
+       if (phydev->suspended)
+               return 0;
+
        /* If the device has WOL enabled, we cannot suspend the PHY */
        phy_ethtool_get_wol(phydev, &wol);
        if (wol.wolopts || (netdev && netdev->wol_enabled))
@@ -1768,6 +1913,90 @@ int genphy_setup_forced(struct phy_device *phydev)
 }
 EXPORT_SYMBOL(genphy_setup_forced);
 
+static int genphy_setup_master_slave(struct phy_device *phydev)
+{
+       u16 ctl = 0;
+
+       if (!phydev->is_gigabit_capable)
+               return 0;
+
+       switch (phydev->master_slave_set) {
+       case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+               ctl |= CTL1000_PREFER_MASTER;
+               break;
+       case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+               break;
+       case MASTER_SLAVE_CFG_MASTER_FORCE:
+               ctl |= CTL1000_AS_MASTER;
+               /* fallthrough */
+       case MASTER_SLAVE_CFG_SLAVE_FORCE:
+               ctl |= CTL1000_ENABLE_MASTER;
+               break;
+       case MASTER_SLAVE_CFG_UNKNOWN:
+       case MASTER_SLAVE_CFG_UNSUPPORTED:
+               return 0;
+       default:
+               phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+               return -EOPNOTSUPP;
+       }
+
+       return phy_modify_changed(phydev, MII_CTRL1000,
+                                 (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER |
+                                  CTL1000_PREFER_MASTER), ctl);
+}
+
+static int genphy_read_master_slave(struct phy_device *phydev)
+{
+       int cfg, state;
+       u16 val;
+
+       if (!phydev->is_gigabit_capable) {
+               phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED;
+               phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
+               return 0;
+       }
+
+       phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
+       phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+
+       val = phy_read(phydev, MII_CTRL1000);
+       if (val < 0)
+               return val;
+
+       if (val & CTL1000_ENABLE_MASTER) {
+               if (val & CTL1000_AS_MASTER)
+                       cfg = MASTER_SLAVE_CFG_MASTER_FORCE;
+               else
+                       cfg = MASTER_SLAVE_CFG_SLAVE_FORCE;
+       } else {
+               if (val & CTL1000_PREFER_MASTER)
+                       cfg = MASTER_SLAVE_CFG_MASTER_PREFERRED;
+               else
+                       cfg = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
+       }
+
+       val = phy_read(phydev, MII_STAT1000);
+       if (val < 0)
+               return val;
+
+       if (val & LPA_1000MSFAIL) {
+               state = MASTER_SLAVE_STATE_ERR;
+       } else if (phydev->link) {
+               /* this bits are valid only for active link */
+               if (val & LPA_1000MSRES)
+                       state = MASTER_SLAVE_STATE_MASTER;
+               else
+                       state = MASTER_SLAVE_STATE_SLAVE;
+       } else {
+               state = MASTER_SLAVE_STATE_UNKNOWN;
+       }
+
+       phydev->master_slave_get = cfg;
+       phydev->master_slave_state = state;
+
+       return 0;
+}
+
 /**
  * genphy_restart_aneg - Enable and Restart Autonegotiation
  * @phydev: target phy_device struct
@@ -1826,6 +2055,12 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed)
        if (genphy_config_eee_advert(phydev))
                changed = true;
 
+       err = genphy_setup_master_slave(phydev);
+       if (err < 0)
+               return err;
+       else if (err)
+               changed = true;
+
        if (AUTONEG_ENABLE != phydev->autoneg)
                return genphy_setup_forced(phydev);
 
@@ -2060,6 +2295,10 @@ int genphy_read_status(struct phy_device *phydev)
        phydev->pause = 0;
        phydev->asym_pause = 0;
 
+       err = genphy_read_master_slave(phydev);
+       if (err < 0)
+               return err;
+
        err = genphy_read_lpa(phydev);
        if (err < 0)
                return err;
@@ -2154,6 +2393,12 @@ int genphy_soft_reset(struct phy_device *phydev)
        if (ret < 0)
                return ret;
 
+       /* Clause 22 states that setting bit BMCR_RESET sets control registers
+        * to their default value. Therefore the POWER DOWN bit is supposed to
+        * be cleared after soft reset.
+        */
+       phydev->suspended = 0;
+
        ret = phy_poll_reset(phydev);
        if (ret)
                return ret;
@@ -2627,7 +2872,6 @@ static struct phy_driver genphy_driver = {
        .phy_id         = 0xffffffff,
        .phy_id_mask    = 0xffffffff,
        .name           = "Generic PHY",
-       .soft_reset     = genphy_no_soft_reset,
        .get_features   = genphy_read_abilities,
        .suspend        = genphy_suspend,
        .resume         = genphy_resume,
index 34ca12a..0f23bec 100644 (file)
@@ -480,8 +480,8 @@ static void phylink_get_fixed_state(struct phylink *pl,
                                    struct phylink_link_state *state)
 {
        *state = pl->link_config;
-       if (pl->get_fixed_state)
-               pl->get_fixed_state(pl->netdev, state);
+       if (pl->config->get_fixed_state)
+               pl->config->get_fixed_state(pl->config, state);
        else if (pl->link_gpio)
                state->link = !!gpiod_get_value_cansleep(pl->link_gpio);
 
@@ -1044,32 +1044,6 @@ void phylink_disconnect_phy(struct phylink *pl)
 }
 EXPORT_SYMBOL_GPL(phylink_disconnect_phy);
 
-/**
- * phylink_fixed_state_cb() - allow setting a fixed link callback
- * @pl: a pointer to a &struct phylink returned from phylink_create()
- * @cb: callback to execute to determine the fixed link state.
- *
- * The MAC driver should call this driver when the state of its link
- * can be determined through e.g: an out of band MMIO register.
- */
-int phylink_fixed_state_cb(struct phylink *pl,
-                          void (*cb)(struct net_device *dev,
-                                     struct phylink_link_state *state))
-{
-       /* It does not make sense to let the link be overriden unless we use
-        * MLO_AN_FIXED
-        */
-       if (pl->cfg_link_an_mode != MLO_AN_FIXED)
-               return -EINVAL;
-
-       mutex_lock(&pl->state_mutex);
-       pl->get_fixed_state = cb;
-       mutex_unlock(&pl->state_mutex);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(phylink_fixed_state_cb);
-
 /**
  * phylink_mac_change() - notify phylink of a change in MAC state
  * @pl: a pointer to a &struct phylink returned from phylink_create()
@@ -1106,6 +1080,8 @@ static irqreturn_t phylink_link_handler(int irq, void *data)
  */
 void phylink_start(struct phylink *pl)
 {
+       bool poll = false;
+
        ASSERT_RTNL();
 
        phylink_info(pl, "configuring for %s/%s link mode\n",
@@ -1142,10 +1118,18 @@ void phylink_start(struct phylink *pl)
                                irq = 0;
                }
                if (irq <= 0)
-                       mod_timer(&pl->link_poll, jiffies + HZ);
+                       poll = true;
+       }
+
+       switch (pl->cfg_link_an_mode) {
+       case MLO_AN_FIXED:
+               poll |= pl->config->poll_fixed_state;
+               break;
+       case MLO_AN_INBAND:
+               poll |= pl->config->pcs_poll;
+               break;
        }
-       if ((pl->cfg_link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) ||
-           pl->config->pcs_poll)
+       if (poll)
                mod_timer(&pl->link_poll, jiffies + HZ);
        if (pl->phydev)
                phy_start(pl->phydev);
index 2d99e9d..c7229d0 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/bitops.h>
 #include <linux/phy.h>
 #include <linux/module.h>
+#include <linux/delay.h>
 
 #define RTL821x_PHYSR                          0x11
 #define RTL821x_PHYSR_DUPLEX                   BIT(13)
@@ -526,6 +527,16 @@ static int rtl8125_match_phy_device(struct phy_device *phydev)
               rtlgen_supports_2_5gbps(phydev);
 }
 
+static int rtlgen_resume(struct phy_device *phydev)
+{
+       int ret = genphy_resume(phydev);
+
+       /* Internal PHY's from RTL8168h up may not be instantly ready */
+       msleep(20);
+
+       return ret;
+}
+
 static struct phy_driver realtek_drvs[] = {
        {
                PHY_ID_MATCH_EXACT(0x00008201),
@@ -609,7 +620,7 @@ static struct phy_driver realtek_drvs[] = {
                .match_phy_device = rtlgen_match_phy_device,
                .read_status    = rtlgen_read_status,
                .suspend        = genphy_suspend,
-               .resume         = genphy_resume,
+               .resume         = rtlgen_resume,
                .read_page      = rtl821x_read_page,
                .write_page     = rtl821x_write_page,
                .read_mmd       = rtlgen_read_mmd,
@@ -621,7 +632,7 @@ static struct phy_driver realtek_drvs[] = {
                .config_aneg    = rtl8125_config_aneg,
                .read_status    = rtl8125_read_status,
                .suspend        = genphy_suspend,
-               .resume         = genphy_resume,
+               .resume         = rtlgen_resume,
                .read_page      = rtl821x_read_page,
                .write_page     = rtl821x_write_page,
                .read_mmd       = rtl8125_read_mmd,
index beb054b..8057ea8 100644 (file)
@@ -78,7 +78,6 @@ static struct phy_driver teranetics_driver[] = {
        .phy_id_mask    = 0xffffffff,
        .name           = "Teranetics TN2020",
        .features       = PHY_10GBIT_FEATURES,
-       .soft_reset     = genphy_no_soft_reset,
        .aneg_done      = teranetics_aneg_done,
        .config_aneg    = gen10g_config_aneg,
        .read_status    = teranetics_read_status,
index b41035b..e03556d 100644 (file)
@@ -21,7 +21,7 @@ config PLIP
          bits at a time (mode 0) or with special PLIP cables, to be used on
          bidirectional parallel ports only, which can transmit 8 bits at a
          time (mode 1); you can find the wiring of these cables in
-         <file:Documentation/networking/PLIP.txt>.  The cables can be up to
+         <file:Documentation/networking/plip.rst>.  The cables can be up to
          15m long.  Mode 0 works also if one of the machines runs DOS/Windows
          and has some PLIP software installed, e.g. the Crynwr PLIP packet
          driver (<http://oak.oakland.edu/simtel.net/msdos/pktdrvr-pre.html>)
index 22cc2cb..7d00589 100644 (file)
@@ -1410,6 +1410,8 @@ static int ppp_dev_init(struct net_device *dev)
 {
        struct ppp *ppp;
 
+       netdev_lockdep_set_classes(dev);
+
        ppp = netdev_priv(dev);
        /* Let the netdevice take a reference on the ppp file. This ensures
         * that ppp_destroy_interface() won't run before the device gets
index 8eeb38c..2056d6a 100644 (file)
@@ -166,7 +166,8 @@ static int rionet_queue_tx_msg(struct sk_buff *skb, struct net_device *ndev,
        return 0;
 }
 
-static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+static netdev_tx_t rionet_start_xmit(struct sk_buff *skb,
+                                    struct net_device *ndev)
 {
        int i;
        struct rionet_private *rnet = netdev_priv(ndev);
index 4004f98..8c1e027 100644 (file)
@@ -468,6 +468,9 @@ static const struct team_mode *team_mode_get(const char *kind)
        struct team_mode_item *mitem;
        const struct team_mode *mode = NULL;
 
+       if (!try_module_get(THIS_MODULE))
+               return NULL;
+
        spin_lock(&mode_list_lock);
        mitem = __find_mode(kind);
        if (!mitem) {
@@ -483,6 +486,7 @@ static const struct team_mode *team_mode_get(const char *kind)
        }
 
        spin_unlock(&mode_list_lock);
+       module_put(THIS_MODULE);
        return mode;
 }
 
@@ -1643,6 +1647,7 @@ static int team_init(struct net_device *dev)
 
        lockdep_register_key(&team->team_lock_key);
        __mutex_init(&team->lock, "team->team_lock_key", &team->team_lock_key);
+       netdev_lockdep_set_classes(dev);
 
        return 0;
 
index 07476c6..44889eb 100644 (file)
@@ -1888,6 +1888,7 @@ drop:
 
        skb_reset_network_header(skb);
        skb_probe_transport_header(skb);
+       skb_record_rx_queue(skb, tfile->queue_index);
 
        if (skb_xdp) {
                struct bpf_prog *xdp_prog;
@@ -2459,6 +2460,7 @@ build:
        skb->protocol = eth_type_trans(skb, tun->dev);
        skb_reset_network_header(skb);
        skb_probe_transport_header(skb);
+       skb_record_rx_queue(skb, tfile->queue_index);
 
        if (skb_xdp) {
                err = do_xdp_generic(xdp_prog, skb);
@@ -2470,7 +2472,6 @@ build:
            !tfile->detached)
                rxhash = __skb_get_hash_symmetric(skb);
 
-       skb_record_rx_queue(skb, tfile->queue_index);
        netif_receive_skb(skb);
 
        /* No need for get_cpu_ptr() here since this function is
index 93044cf..b05bb11 100644 (file)
@@ -31,6 +31,7 @@
 #define AX_ACCESS_PHY                          0x02
 #define AX_ACCESS_EEPROM                       0x04
 #define AX_ACCESS_EFUS                         0x05
+#define AX_RELOAD_EEPROM_EFUSE                 0x06
 #define AX_PAUSE_WATERLVL_HIGH                 0x54
 #define AX_PAUSE_WATERLVL_LOW                  0x55
 
@@ -611,6 +612,81 @@ ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
        return 0;
 }
 
+static int
+ax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
+                  u8 *data)
+{
+       struct usbnet *dev = netdev_priv(net);
+       u16 *eeprom_buff;
+       int first_word;
+       int last_word;
+       int ret;
+       int i;
+
+       netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n",
+                  eeprom->len, eeprom->offset, eeprom->magic);
+
+       if (eeprom->len == 0)
+               return -EINVAL;
+
+       if (eeprom->magic != AX88179_EEPROM_MAGIC)
+               return -EINVAL;
+
+       first_word = eeprom->offset >> 1;
+       last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+
+       eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
+                                   GFP_KERNEL);
+       if (!eeprom_buff)
+               return -ENOMEM;
+
+       /* align data to 16 bit boundaries, read the missing data from
+          the EEPROM */
+       if (eeprom->offset & 1) {
+               ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, first_word, 1, 2,
+                                      &eeprom_buff[0]);
+               if (ret < 0) {
+                       netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word);
+                       goto free;
+               }
+       }
+
+       if ((eeprom->offset + eeprom->len) & 1) {
+               ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, last_word, 1, 2,
+                                      &eeprom_buff[last_word - first_word]);
+               if (ret < 0) {
+                       netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word);
+                       goto free;
+               }
+       }
+
+       memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len);
+
+       for (i = first_word; i <= last_word; i++) {
+               netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n",
+                          i, eeprom_buff[i - first_word]);
+               ret = ax88179_write_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
+                                       &eeprom_buff[i - first_word]);
+               if (ret < 0) {
+                       netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", i);
+                       goto free;
+               }
+               msleep(20);
+       }
+
+       /* reload EEPROM data */
+       ret = ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0x0000, 0, 0, NULL);
+       if (ret < 0) {
+               netdev_err(net, "Failed to reload EEPROM data\n");
+               goto free;
+       }
+
+       ret = 0;
+free:
+       kfree(eeprom_buff);
+       return ret;
+}
+
 static int ax88179_get_link_ksettings(struct net_device *net,
                                      struct ethtool_link_ksettings *cmd)
 {
@@ -822,6 +898,7 @@ static const struct ethtool_ops ax88179_ethtool_ops = {
        .set_wol                = ax88179_set_wol,
        .get_eeprom_len         = ax88179_get_eeprom_len,
        .get_eeprom             = ax88179_get_eeprom,
+       .set_eeprom             = ax88179_set_eeprom,
        .get_eee                = ax88179_get_eee,
        .set_eee                = ax88179_set_eee,
        .nway_reset             = usbnet_nway_reset,
index 6c738a2..4bb8552 100644 (file)
@@ -1359,6 +1359,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x413c, 0x81b3, 8)},    /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */
        {QMI_FIXED_INTF(0x413c, 0x81b6, 8)},    /* Dell Wireless 5811e */
        {QMI_FIXED_INTF(0x413c, 0x81b6, 10)},   /* Dell Wireless 5811e */
+       {QMI_FIXED_INTF(0x413c, 0x81cc, 8)},    /* Dell Wireless 5816e */
        {QMI_FIXED_INTF(0x413c, 0x81d7, 0)},    /* Dell Wireless 5821e */
        {QMI_FIXED_INTF(0x413c, 0x81d7, 1)},    /* Dell Wireless 5821e preproduction config */
        {QMI_FIXED_INTF(0x413c, 0x81e0, 0)},    /* Dell Wireless 5821e with eSIM support*/
index 389d19d..0abd257 100644 (file)
@@ -354,11 +354,6 @@ static void sierra_net_set_ctx_index(struct sierra_net_data *priv, u8 ctx_ix)
                cpu_to_be16(SIERRA_NET_HIP_EXT_IP_OUT_ID);
 }
 
-static inline int sierra_net_is_valid_addrlen(u8 len)
-{
-       return len == sizeof(struct in_addr);
-}
-
 static int sierra_net_parse_lsi(struct usbnet *dev, char *data, int datalen)
 {
        struct lsi_umts *lsi = (struct lsi_umts *)data;
index 66e00dd..43928a1 100644 (file)
@@ -188,8 +188,8 @@ static netdev_tx_t vrf_process_v6_outbound(struct sk_buff *skb,
        fl6.flowi6_proto = iph->nexthdr;
        fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF;
 
-       dst = ip6_route_output(net, NULL, &fl6);
-       if (dst == dst_null)
+       dst = ip6_dst_lookup_flow(net, NULL, &fl6, NULL);
+       if (IS_ERR(dst) || dst == dst_null)
                goto err;
 
        skb_dst_drop(skb);
@@ -474,7 +474,8 @@ static struct sk_buff *vrf_ip6_out(struct net_device *vrf_dev,
        if (rt6_need_strict(&ipv6_hdr(skb)->daddr))
                return skb;
 
-       if (qdisc_tx_is_default(vrf_dev))
+       if (qdisc_tx_is_default(vrf_dev) ||
+           IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED)
                return vrf_ip6_out_direct(vrf_dev, sk, skb);
 
        return vrf_ip6_out_redirect(vrf_dev, skb);
@@ -686,7 +687,8 @@ static struct sk_buff *vrf_ip_out(struct net_device *vrf_dev,
            ipv4_is_lbcast(ip_hdr(skb)->daddr))
                return skb;
 
-       if (qdisc_tx_is_default(vrf_dev))
+       if (qdisc_tx_is_default(vrf_dev) ||
+           IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED)
                return vrf_ip_out_direct(vrf_dev, sk, skb);
 
        return vrf_ip_out_redirect(vrf_dev, skb);
@@ -865,6 +867,7 @@ static int vrf_dev_init(struct net_device *dev)
 
        /* similarly, oper state is irrelevant; set to up to avoid confusion */
        dev->operstate = IF_OPER_UP;
+       netdev_lockdep_set_classes(dev);
        return 0;
 
 out_rth:
index 45308b3..a5b415f 100644 (file)
@@ -3144,7 +3144,7 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[],
                u32 id = nla_get_u32(data[IFLA_VXLAN_ID]);
 
                if (id >= VXLAN_N_VID) {
-                       NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_VXLAN_ID],
+                       NL_SET_ERR_MSG_ATTR(extack, data[IFLA_VXLAN_ID],
                                            "VXLAN ID must be lower than 16777216");
                        return -ERANGE;
                }
@@ -3155,7 +3155,7 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[],
                        = nla_data(data[IFLA_VXLAN_PORT_RANGE]);
 
                if (ntohs(p->high) < ntohs(p->low)) {
-                       NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_VXLAN_PORT_RANGE],
+                       NL_SET_ERR_MSG_ATTR(extack, data[IFLA_VXLAN_PORT_RANGE],
                                            "Invalid source port range");
                        return -EINVAL;
                }
@@ -3165,7 +3165,7 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[],
                enum ifla_vxlan_df df = nla_get_u8(data[IFLA_VXLAN_DF]);
 
                if (df < 0 || df > VXLAN_DF_MAX) {
-                       NL_SET_ERR_MSG_ATTR(extack, tb[IFLA_VXLAN_DF],
+                       NL_SET_ERR_MSG_ATTR(extack, data[IFLA_VXLAN_DF],
                                            "Invalid DF attribute");
                        return -EINVAL;
                }
index dbc0e3f..3e21726 100644 (file)
@@ -336,7 +336,7 @@ config DLCI
 
          To use frame relay, you need supporting hardware (called FRAD) and
          certain programs from the net-tools package as explained in
-         <file:Documentation/networking/framerelay.txt>.
+         <file:Documentation/networking/framerelay.rst>.
 
          To compile this driver as a module, choose M here: the
          module will be called dlci.
@@ -361,7 +361,7 @@ config SDLA
 
          These are multi-protocol cards, but only Frame Relay is supported
          by the driver at this time. Please read
-         <file:Documentation/networking/framerelay.txt>.
+         <file:Documentation/networking/framerelay.rst>.
 
          To compile this driver as a module, choose M here: the
          module will be called sdla.
index 529ebca..1f7709d 100644 (file)
@@ -354,6 +354,7 @@ out:
                usb_autopm_put_interface(i2400mu->usb_iface);
        d_fnend(8, dev, "(i2400m %p ack %p size %zu) = %ld\n",
                i2400m, ack, ack_size, (long) result);
+       usb_put_urb(&notif_urb);
        return result;
 
 error_exceeded:
index 5c964fc..71b8e80 100644 (file)
@@ -35,8 +35,10 @@ int wg_packet_queue_init(struct crypt_queue *queue, work_func_t function,
                if (multicore) {
                        queue->worker = wg_packet_percpu_multicore_worker_alloc(
                                function, queue);
-                       if (!queue->worker)
+                       if (!queue->worker) {
+                               ptr_ring_cleanup(&queue->ring, NULL);
                                return -ENOMEM;
+                       }
                } else {
                        INIT_WORK(&queue->work, function);
                }
index da3b782..3bb5b9a 100644 (file)
@@ -226,21 +226,20 @@ void wg_packet_handshake_receive_worker(struct work_struct *work)
 static void keep_key_fresh(struct wg_peer *peer)
 {
        struct noise_keypair *keypair;
-       bool send = false;
+       bool send;
 
        if (peer->sent_lastminute_handshake)
                return;
 
        rcu_read_lock_bh();
        keypair = rcu_dereference_bh(peer->keypairs.current_keypair);
-       if (likely(keypair && READ_ONCE(keypair->sending.is_valid)) &&
-           keypair->i_am_the_initiator &&
-           unlikely(wg_birthdate_has_expired(keypair->sending.birthdate,
-                       REJECT_AFTER_TIME - KEEPALIVE_TIMEOUT - REKEY_TIMEOUT)))
-               send = true;
+       send = keypair && READ_ONCE(keypair->sending.is_valid) &&
+              keypair->i_am_the_initiator &&
+              wg_birthdate_has_expired(keypair->sending.birthdate,
+                       REJECT_AFTER_TIME - KEEPALIVE_TIMEOUT - REKEY_TIMEOUT);
        rcu_read_unlock_bh();
 
-       if (send) {
+       if (unlikely(send)) {
                peer->sent_lastminute_handshake = true;
                wg_packet_send_queued_handshake_initiation(peer, false);
        }
@@ -393,13 +392,11 @@ static void wg_packet_consume_data_done(struct wg_peer *peer,
                len = ntohs(ip_hdr(skb)->tot_len);
                if (unlikely(len < sizeof(struct iphdr)))
                        goto dishonest_packet_size;
-               if (INET_ECN_is_ce(PACKET_CB(skb)->ds))
-                       IP_ECN_set_ce(ip_hdr(skb));
+               INET_ECN_decapsulate(skb, PACKET_CB(skb)->ds, ip_hdr(skb)->tos);
        } else if (skb->protocol == htons(ETH_P_IPV6)) {
                len = ntohs(ipv6_hdr(skb)->payload_len) +
                      sizeof(struct ipv6hdr);
-               if (INET_ECN_is_ce(PACKET_CB(skb)->ds))
-                       IP6_ECN_set_ce(skb, ipv6_hdr(skb));
+               INET_ECN_decapsulate(skb, PACKET_CB(skb)->ds, ipv6_get_dsfield(ipv6_hdr(skb)));
        } else {
                goto dishonest_packet_type;
        }
@@ -518,6 +515,8 @@ void wg_packet_decrypt_worker(struct work_struct *work)
                                &PACKET_CB(skb)->keypair->receiving)) ?
                                PACKET_STATE_CRYPTED : PACKET_STATE_DEAD;
                wg_queue_enqueue_per_peer_napi(skb, state);
+               if (need_resched())
+                       cond_resched();
        }
 }
 
index bcd6462..007cd44 100644 (file)
@@ -120,9 +120,9 @@ bool __init wg_ratelimiter_selftest(void)
        enum { TRIALS_BEFORE_GIVING_UP = 5000 };
        bool success = false;
        int test = 0, trials;
-       struct sk_buff *skb4, *skb6;
+       struct sk_buff *skb4, *skb6 = NULL;
        struct iphdr *hdr4;
-       struct ipv6hdr *hdr6;
+       struct ipv6hdr *hdr6 = NULL;
 
        if (IS_ENABLED(CONFIG_KASAN) || IS_ENABLED(CONFIG_UBSAN))
                return true;
index 7348c10..6687db6 100644 (file)
@@ -124,20 +124,17 @@ void wg_packet_send_handshake_cookie(struct wg_device *wg,
 static void keep_key_fresh(struct wg_peer *peer)
 {
        struct noise_keypair *keypair;
-       bool send = false;
+       bool send;
 
        rcu_read_lock_bh();
        keypair = rcu_dereference_bh(peer->keypairs.current_keypair);
-       if (likely(keypair && READ_ONCE(keypair->sending.is_valid)) &&
-           (unlikely(atomic64_read(&keypair->sending.counter.counter) >
-                     REKEY_AFTER_MESSAGES) ||
-            (keypair->i_am_the_initiator &&
-             unlikely(wg_birthdate_has_expired(keypair->sending.birthdate,
-                                               REKEY_AFTER_TIME)))))
-               send = true;
+       send = keypair && READ_ONCE(keypair->sending.is_valid) &&
+              (atomic64_read(&keypair->sending.counter.counter) > REKEY_AFTER_MESSAGES ||
+               (keypair->i_am_the_initiator &&
+                wg_birthdate_has_expired(keypair->sending.birthdate, REKEY_AFTER_TIME)));
        rcu_read_unlock_bh();
 
-       if (send)
+       if (unlikely(send))
                wg_packet_send_queued_handshake_initiation(peer, false);
 }
 
@@ -281,6 +278,8 @@ void wg_packet_tx_worker(struct work_struct *work)
 
                wg_noise_keypair_put(keypair, false);
                wg_peer_put(peer);
+               if (need_resched())
+                       cond_resched();
        }
 }
 
@@ -304,7 +303,8 @@ void wg_packet_encrypt_worker(struct work_struct *work)
                }
                wg_queue_enqueue_per_peer(&PACKET_PEER(first)->tx_queue, first,
                                          state);
-
+               if (need_resched())
+                       cond_resched();
        }
 }
 
index b0d6541..f901802 100644 (file)
@@ -76,12 +76,6 @@ static int send4(struct wg_device *wg, struct sk_buff *skb,
                        net_dbg_ratelimited("%s: No route to %pISpfsc, error %d\n",
                                            wg->dev->name, &endpoint->addr, ret);
                        goto err;
-               } else if (unlikely(rt->dst.dev == skb->dev)) {
-                       ip_rt_put(rt);
-                       ret = -ELOOP;
-                       net_dbg_ratelimited("%s: Avoiding routing loop to %pISpfsc\n",
-                                           wg->dev->name, &endpoint->addr);
-                       goto err;
                }
                if (cache)
                        dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
@@ -149,12 +143,6 @@ static int send6(struct wg_device *wg, struct sk_buff *skb,
                        net_dbg_ratelimited("%s: No route to %pISpfsc, error %d\n",
                                            wg->dev->name, &endpoint->addr, ret);
                        goto err;
-               } else if (unlikely(dst->dev == skb->dev)) {
-                       dst_release(dst);
-                       ret = -ELOOP;
-                       net_dbg_ratelimited("%s: Avoiding routing loop to %pISpfsc\n",
-                                           wg->dev->name, &endpoint->addr);
-                       goto err;
                }
                if (cache)
                        dst_cache_set_ip6(cache, dst, &fl.saddr);
index 1c98d78..15b0ad1 100644 (file)
@@ -57,7 +57,7 @@ config PCMCIA_RAYCS
        ---help---
          Say Y here if you intend to attach an Aviator/Raytheon PCMCIA
          (PC-card) wireless Ethernet networking card to your computer.
-         Please read the file <file:Documentation/networking/ray_cs.txt> for
+         Please read the file <file:Documentation/networking/ray_cs.rst> for
          details.
 
          To compile this driver as a module, choose M here: the module will be
index 459b8d4..f9af55f 100644 (file)
@@ -36,12 +36,13 @@ static inline int ath11k_thermal_register(struct ath11k_base *sc)
        return 0;
 }
 
-static inline void ath11k_thermal_unregister(struct ath11k *ar)
+static inline void ath11k_thermal_unregister(struct ath11k_base *sc)
 {
 }
 
 static inline int ath11k_thermal_set_throttling(struct ath11k *ar, u32 throttle_state)
 {
+       return 0;
 }
 
 static inline void ath11k_thermal_event_temperature(struct ath11k *ar,
index 0587452..55babc6 100644 (file)
@@ -629,19 +629,6 @@ static s8 b43_rssi_postprocess(struct b43_wldev *dev,
        return (s8) tmp;
 }
 
-//TODO
-#if 0
-static s8 b43_rssinoise_postprocess(struct b43_wldev *dev, u8 in_rssi)
-{
-       struct b43_phy *phy = &dev->phy;
-       s8 ret;
-
-       ret = b43_rssi_postprocess(dev, in_rssi, 0, 1, 1);
-
-       return ret;
-}
-#endif
-
 void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
 {
        struct ieee80211_rx_status status;
index 10584ee..c88655a 100644 (file)
@@ -726,9 +726,18 @@ static int brcmf_net_mon_stop(struct net_device *ndev)
        return err;
 }
 
+static netdev_tx_t brcmf_net_mon_start_xmit(struct sk_buff *skb,
+                                           struct net_device *ndev)
+{
+       dev_kfree_skb_any(skb);
+
+       return NETDEV_TX_OK;
+}
+
 static const struct net_device_ops brcmf_netdev_ops_mon = {
        .ndo_open = brcmf_net_mon_open,
        .ndo_stop = brcmf_net_mon_stop,
+       .ndo_start_xmit = brcmf_net_mon_start_xmit,
 };
 
 int brcmf_net_mon_attach(struct brcmf_if *ifp)
index ab17903..f42b3cd 100644 (file)
@@ -16,7 +16,7 @@ config IPW2100
          A driver for the Intel PRO/Wireless 2100 Network
          Connection 802.11b wireless network adapter.
 
-         See <file:Documentation/networking/device_drivers/intel/ipw2100.txt>
+         See <file:Documentation/networking/device_drivers/intel/ipw2100.rst>
          for information on the capabilities currently enabled in this driver
          and for tips for debugging issues and problems.
 
@@ -78,7 +78,7 @@ config IPW2200
          A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network
          Connection adapters.
 
-         See <file:Documentation/networking/device_drivers/intel/ipw2200.txt>
+         See <file:Documentation/networking/device_drivers/intel/ipw2200.rst>
          for information on the capabilities currently enabled in this
          driver and for tips for debugging issues and problems.
 
index 97ea6e2..624fe72 100644 (file)
@@ -8352,7 +8352,7 @@ static int ipw2100_mod_firmware_load(struct ipw2100_fw *fw)
        if (IPW2100_FW_MAJOR(h->version) != IPW2100_FW_MAJOR_VERSION) {
                printk(KERN_WARNING DRV_NAME ": Firmware image not compatible "
                       "(detected version id of %u). "
-                      "See Documentation/networking/device_drivers/intel/ipw2100.txt\n",
+                      "See Documentation/networking/device_drivers/intel/ipw2100.rst\n",
                       h->version);
                return 1;
        }
index 6209f85..0af9e99 100644 (file)
@@ -374,7 +374,7 @@ out:
 }
 
 static void *
-il3945_rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
+il3945_rs_alloc(struct ieee80211_hw *hw)
 {
        return hw->priv;
 }
index 7c6e2c8..0a02d8a 100644 (file)
@@ -2474,7 +2474,7 @@ il4965_rs_fill_link_cmd(struct il_priv *il, struct il_lq_sta *lq_sta,
 }
 
 static void *
-il4965_rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
+il4965_rs_alloc(struct ieee80211_hw *hw)
 {
        return hw->priv;
 }
index 226165d..dac809d 100644 (file)
@@ -3019,7 +3019,7 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
                        cpu_to_le16(priv->lib->bt_params->agg_time_limit);
 }
 
-static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
+static void *rs_alloc(struct ieee80211_hw *hw)
 {
        return hw->priv;
 }
index 344eba8..e2184ba 100644 (file)
@@ -372,9 +372,14 @@ int iwl_sar_select_profile(struct iwl_fw_runtime *fwrt,
                if (!prof->enabled) {
                        IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n",
                                        profs[i]);
-                       /* if one of the profiles is disabled, we fail all */
-                       return -ENOENT;
+                       /*
+                        * if one of the profiles is disabled, we
+                        * ignore all of them and return 1 to
+                        * differentiate disabled from other failures.
+                        */
+                       return 1;
                }
+
                IWL_DEBUG_INFO(fwrt,
                               "SAR EWRD: chain %d profile index %d\n",
                               i, profs[i]);
index 73196cb..75d958b 100644 (file)
@@ -8,7 +8,7 @@
  * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
- * Copyright(c) 2019 Intel Corporation
+ * Copyright(c) 2019 - 2020 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,7 +31,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) 2019 Intel Corporation
+ * Copyright(c) 2019 - 2020 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -99,7 +99,7 @@ enum iwl_mvm_dqa_txq {
        IWL_MVM_DQA_MAX_MGMT_QUEUE = 8,
        IWL_MVM_DQA_AP_PROBE_RESP_QUEUE = 9,
        IWL_MVM_DQA_MIN_DATA_QUEUE = 10,
-       IWL_MVM_DQA_MAX_DATA_QUEUE = 31,
+       IWL_MVM_DQA_MAX_DATA_QUEUE = 30,
 };
 
 enum iwl_mvm_tx_fifo {
index 9ff12c2..a483d38 100644 (file)
@@ -1467,7 +1467,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
                                kmemdup(pieces->dbg_conf_tlv[i],
                                        pieces->dbg_conf_tlv_len[i],
                                        GFP_KERNEL);
-                       if (!pieces->dbg_conf_tlv[i])
+                       if (!drv->fw.dbg.conf_tlv[i])
                                goto out_free_fw;
                }
        }
index 6047b98..d91a8e8 100644 (file)
@@ -532,8 +532,7 @@ static struct ieee80211_sband_iftype_data iwl_he_capa[] = {
                                        IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
                                        IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
                                .mac_cap_info[2] =
-                                       IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP |
-                                       IEEE80211_HE_MAC_CAP2_ACK_EN,
+                                       IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP,
                                .mac_cap_info[3] =
                                        IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
                                        IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2,
@@ -617,8 +616,7 @@ static struct ieee80211_sband_iftype_data iwl_he_capa[] = {
                                        IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
                                        IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
                                .mac_cap_info[2] =
-                                       IEEE80211_HE_MAC_CAP2_BSR |
-                                       IEEE80211_HE_MAC_CAP2_ACK_EN,
+                                       IEEE80211_HE_MAC_CAP2_BSR,
                                .mac_cap_info[3] =
                                        IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
                                        IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2,
index d659833..164fc9e 100644 (file)
@@ -789,6 +789,7 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b)
                struct iwl_dev_tx_power_cmd_v4 v4;
        } cmd;
 
+       int ret;
        u16 len = 0;
 
        cmd.v5.v3.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS);
@@ -803,9 +804,14 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b)
                len = sizeof(cmd.v4.v3);
 
 
-       if (iwl_sar_select_profile(&mvm->fwrt, cmd.v5.v3.per_chain_restriction,
-                                  prof_a, prof_b))
-               return -ENOENT;
+       ret = iwl_sar_select_profile(&mvm->fwrt,
+                                    cmd.v5.v3.per_chain_restriction,
+                                    prof_a, prof_b);
+
+       /* return on error or if the profile is disabled (positive number) */
+       if (ret)
+               return ret;
+
        IWL_DEBUG_RADIO(mvm, "Sending REDUCE_TX_POWER_CMD per chain\n");
        return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd);
 }
@@ -1134,16 +1140,7 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm)
                                "EWRD SAR BIOS table invalid or unavailable. (%d)\n",
                                ret);
 
-       ret = iwl_mvm_sar_select_profile(mvm, 1, 1);
-       /*
-        * If we don't have profile 0 from BIOS, just skip it.  This
-        * means that SAR Geo will not be enabled either, even if we
-        * have other valid profiles.
-        */
-       if (ret == -ENOENT)
-               return 1;
-
-       return ret;
+       return iwl_mvm_sar_select_profile(mvm, 1, 1);
 }
 
 static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm)
@@ -1372,7 +1369,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        ret = iwl_mvm_sar_init(mvm);
        if (ret == 0) {
                ret = iwl_mvm_sar_geo_init(mvm);
-       } else if (ret > 0 && !iwl_sar_get_wgds_table(&mvm->fwrt)) {
+       } else if (ret == -ENOENT && !iwl_sar_get_wgds_table(&mvm->fwrt)) {
                /*
                 * If basic SAR is not available, we check for WGDS,
                 * which should *not* be available either.  If it is
index 1b6cbcf..2462659 100644 (file)
@@ -3665,7 +3665,7 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
                        cpu_to_le16(iwl_mvm_coex_agg_time_limit(mvm, sta));
 }
 
-static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
+static void *rs_alloc(struct ieee80211_hw *hw)
 {
        return hw->priv;
 }
index 5ee33c8..77b8def 100644 (file)
@@ -8,7 +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 - 2019 Intel Corporation
+ * Copyright(c) 2018 - 2020 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,7 +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 - 2019 Intel Corporation
+ * Copyright(c) 2018 - 2020 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -566,6 +566,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
 
 struct iwl_mvm_stat_data {
        struct iwl_mvm *mvm;
+       __le32 flags;
        __le32 mac_id;
        u8 beacon_filter_average_energy;
        void *general;
@@ -606,6 +607,13 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
                        -general->beacon_average_energy[vif_id];
        }
 
+       /* make sure that beacon statistics don't go backwards with TCM
+        * request to clear statistics
+        */
+       if (le32_to_cpu(data->flags) & IWL_STATISTICS_REPLY_FLG_CLEAR)
+               mvmvif->beacon_stats.accu_num_beacons +=
+                       mvmvif->beacon_stats.num_beacons;
+
        if (mvmvif->id != id)
                return;
 
@@ -763,6 +771,7 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
 
                flags = stats->flag;
        }
+       data.flags = flags;
 
        iwl_mvm_rx_stats_check_trigger(mvm, pkt);
 
index 64ef3f3..56ae72d 100644 (file)
@@ -722,6 +722,11 @@ static int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id,
 
        lockdep_assert_held(&mvm->mutex);
 
+       if (WARN(maxq >= mvm->trans->trans_cfg->base_params->num_of_queues,
+                "max queue %d >= num_of_queues (%d)", maxq,
+                mvm->trans->trans_cfg->base_params->num_of_queues))
+               maxq = mvm->trans->trans_cfg->base_params->num_of_queues - 1;
+
        /* This should not be hit with new TX path */
        if (WARN_ON(iwl_mvm_has_new_tx_api(mvm)))
                return -ENOSPC;
@@ -1164,9 +1169,9 @@ static int iwl_mvm_inactivity_check(struct iwl_mvm *mvm, u8 alloc_for_sta)
                                                   inactive_tid_bitmap,
                                                   &unshare_queues,
                                                   &changetid_queues);
-               if (ret >= 0 && free_queue < 0) {
+               if (ret && free_queue < 0) {
                        queue_owner = sta;
-                       free_queue = ret;
+                       free_queue = i;
                }
                /* only unlock sta lock - we still need the queue info lock */
                spin_unlock_bh(&mvmsta->lock);
index 27e94e6..b6a5921 100644 (file)
@@ -132,6 +132,18 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
        int cmdq_size = max_t(u32, IWL_CMD_QUEUE_SIZE,
                              trans->cfg->min_txq_size);
 
+       switch (trans_pcie->rx_buf_size) {
+       case IWL_AMSDU_DEF:
+               return -EINVAL;
+       case IWL_AMSDU_2K:
+               break;
+       case IWL_AMSDU_4K:
+       case IWL_AMSDU_8K:
+       case IWL_AMSDU_12K:
+               control_flags |= IWL_PRPH_SCRATCH_RB_SIZE_4K;
+               break;
+       }
+
        /* Allocate prph scratch */
        prph_scratch = dma_alloc_coherent(trans->dev, sizeof(*prph_scratch),
                                          &trans_pcie->prph_scratch_dma_addr,
@@ -146,10 +158,8 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
                cpu_to_le16((u16)iwl_read32(trans, CSR_HW_REV));
        prph_sc_ctrl->version.size = cpu_to_le16(sizeof(*prph_scratch) / 4);
 
-       control_flags = IWL_PRPH_SCRATCH_RB_SIZE_4K |
-                       IWL_PRPH_SCRATCH_MTR_MODE |
-                       (IWL_PRPH_MTR_FORMAT_256B &
-                        IWL_PRPH_SCRATCH_MTR_FORMAT);
+       control_flags |= IWL_PRPH_SCRATCH_MTR_MODE;
+       control_flags |= IWL_PRPH_MTR_FORMAT_256B & IWL_PRPH_SCRATCH_MTR_FORMAT;
 
        /* initialize RX default queue */
        prph_sc_ctrl->rbd_cfg.free_rbd_addr =
index a30f6b0..53747ac 100644 (file)
@@ -1420,6 +1420,9 @@ void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue)
 
        iwl_pcie_gen2_txq_unmap(trans, queue);
 
+       iwl_pcie_gen2_txq_free_memory(trans, trans_pcie->txq[queue]);
+       trans_pcie->txq[queue] = NULL;
+
        IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", queue);
 }
 
index 58212c5..aadf3de 100644 (file)
@@ -3041,6 +3041,27 @@ static void prism2_clear_set_tim_queue(local_info_t *local)
        }
 }
 
+
+/*
+ * HostAP uses two layers of net devices, where the inner
+ * layer gets called all the time from the outer layer.
+ * This is a natural nesting, which needs a split lock type.
+ */
+static struct lock_class_key hostap_netdev_xmit_lock_key;
+
+static void prism2_set_lockdep_class_one(struct net_device *dev,
+                                        struct netdev_queue *txq,
+                                        void *_unused)
+{
+       lockdep_set_class(&txq->_xmit_lock,
+                         &hostap_netdev_xmit_lock_key);
+}
+
+static void prism2_set_lockdep_class(struct net_device *dev)
+{
+       netdev_for_each_tx_queue(dev, prism2_set_lockdep_class_one, NULL);
+}
+
 static struct net_device *
 prism2_init_local_data(struct prism2_helper_functions *funcs, int card_idx,
                       struct device *sdev)
@@ -3199,6 +3220,7 @@ while (0)
        if (ret >= 0)
                ret = register_netdevice(dev);
 
+       prism2_set_lockdep_class(dev);
        rtnl_unlock();
        if (ret < 0) {
                printk(KERN_WARNING "%s: register netdevice failed!\n",
index 7fe8207..0528d4c 100644 (file)
@@ -3669,9 +3669,9 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
        }
 
        if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
-               hwname = kasprintf(GFP_KERNEL, "%.*s",
-                                  nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
-                                  (char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]));
+               hwname = kstrndup((char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]),
+                                 nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
+                                 GFP_KERNEL);
                if (!hwname)
                        return -ENOMEM;
                param.hwname = hwname;
@@ -3691,9 +3691,9 @@ static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
        if (info->attrs[HWSIM_ATTR_RADIO_ID]) {
                idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
        } else if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
-               hwname = kasprintf(GFP_KERNEL, "%.*s",
-                                  nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
-                                  (char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]));
+               hwname = kstrndup((char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]),
+                                 nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
+                                 GFP_KERNEL);
                if (!hwname)
                        return -ENOMEM;
        } else
@@ -4068,7 +4068,7 @@ static void hwsim_virtio_rx_work(struct work_struct *work)
        }
        vq = hwsim_vqs[HWSIM_VQ_RX];
        sg_init_one(sg, skb->head, skb_end_offset(skb));
-       err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL);
+       err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_ATOMIC);
        if (WARN(err, "virtqueue_add_inbuf returned %d\n", err))
                nlmsg_free(skb);
        else
index 0c7d749..4b5ea0e 100644 (file)
@@ -261,7 +261,7 @@ static void rtl_rate_update(void *ppriv,
 {
 }
 
-static void *rtl_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
+static void *rtl_rate_alloc(struct ieee80211_hw *hw)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        return rtlpriv;
index b9a5868..a9752c3 100644 (file)
@@ -1350,22 +1350,17 @@ static void rtw_pci_phy_cfg(struct rtw_dev *rtwdev)
        rtw_pci_link_cfg(rtwdev);
 }
 
-#ifdef CONFIG_PM
-static int rtw_pci_suspend(struct device *dev)
+static int __maybe_unused rtw_pci_suspend(struct device *dev)
 {
        return 0;
 }
 
-static int rtw_pci_resume(struct device *dev)
+static int __maybe_unused rtw_pci_resume(struct device *dev)
 {
        return 0;
 }
 
 static SIMPLE_DEV_PM_OPS(rtw_pm_ops, rtw_pci_suspend, rtw_pci_resume);
-#define RTW_PM_OPS (&rtw_pm_ops)
-#else
-#define RTW_PM_OPS NULL
-#endif
 
 static int rtw_pci_claim(struct rtw_dev *rtwdev, struct pci_dev *pdev)
 {
@@ -1597,7 +1592,7 @@ static struct pci_driver rtw_pci_driver = {
        .id_table = rtw_pci_id_table,
        .probe = rtw_pci_probe,
        .remove = rtw_pci_remove,
-       .driver.pm = RTW_PM_OPS,
+       .driver.pm = &rtw_pm_ops,
 };
 module_pci_driver(rtw_pci_driver);
 
index 91c1bd6..f2adea9 100644 (file)
@@ -3642,6 +3642,8 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
 
        return;
  out_put_disk:
+       /* prevent double queue cleanup */
+       ns->disk->queue = NULL;
        put_disk(ns->disk);
  out_unlink_ns:
        mutex_lock(&ctrl->subsys->lock);
index 9f982c0..a04afe7 100644 (file)
@@ -60,39 +60,15 @@ static struct mii_timestamper *of_find_mii_timestamper(struct device_node *node)
        return register_mii_timestamper(arg.np, arg.args[0]);
 }
 
-static int of_mdiobus_register_phy(struct mii_bus *mdio,
-                                   struct device_node *child, u32 addr)
+int of_mdiobus_phy_device_register(struct mii_bus *mdio, struct phy_device *phy,
+                             struct device_node *child, u32 addr)
 {
-       struct mii_timestamper *mii_ts;
-       struct phy_device *phy;
-       bool is_c45;
        int rc;
-       u32 phy_id;
-
-       mii_ts = of_find_mii_timestamper(child);
-       if (IS_ERR(mii_ts))
-               return PTR_ERR(mii_ts);
-
-       is_c45 = of_device_is_compatible(child,
-                                        "ethernet-phy-ieee802.3-c45");
-
-       if (!is_c45 && !of_get_phy_id(child, &phy_id))
-               phy = phy_device_create(mdio, addr, phy_id, 0, NULL);
-       else
-               phy = get_phy_device(mdio, addr, is_c45);
-       if (IS_ERR(phy)) {
-               if (mii_ts)
-                       unregister_mii_timestamper(mii_ts);
-               return PTR_ERR(phy);
-       }
 
        rc = of_irq_get(child, 0);
-       if (rc == -EPROBE_DEFER) {
-               if (mii_ts)
-                       unregister_mii_timestamper(mii_ts);
-               phy_device_free(phy);
+       if (rc == -EPROBE_DEFER)
                return rc;
-       }
+
        if (rc > 0) {
                phy->irq = rc;
                mdio->irq[addr] = rc;
@@ -117,11 +93,48 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio,
        /* All data is now stored in the phy struct;
         * register it */
        rc = phy_device_register(phy);
+       if (rc) {
+               of_node_put(child);
+               return rc;
+       }
+
+       dev_dbg(&mdio->dev, "registered phy %pOFn at address %i\n",
+               child, addr);
+       return 0;
+}
+EXPORT_SYMBOL(of_mdiobus_phy_device_register);
+
+static int of_mdiobus_register_phy(struct mii_bus *mdio,
+                                   struct device_node *child, u32 addr)
+{
+       struct mii_timestamper *mii_ts;
+       struct phy_device *phy;
+       bool is_c45;
+       int rc;
+       u32 phy_id;
+
+       mii_ts = of_find_mii_timestamper(child);
+       if (IS_ERR(mii_ts))
+               return PTR_ERR(mii_ts);
+
+       is_c45 = of_device_is_compatible(child,
+                                        "ethernet-phy-ieee802.3-c45");
+
+       if (!is_c45 && !of_get_phy_id(child, &phy_id))
+               phy = phy_device_create(mdio, addr, phy_id, 0, NULL);
+       else
+               phy = get_phy_device(mdio, addr, is_c45);
+       if (IS_ERR(phy)) {
+               if (mii_ts)
+                       unregister_mii_timestamper(mii_ts);
+               return PTR_ERR(phy);
+       }
+
+       rc = of_mdiobus_phy_device_register(mdio, phy, child, addr);
        if (rc) {
                if (mii_ts)
                        unregister_mii_timestamper(mii_ts);
                phy_device_free(phy);
-               of_node_put(child);
                return rc;
        }
 
@@ -132,8 +145,6 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio,
        if (mii_ts)
                phy->mii_ts = mii_ts;
 
-       dev_dbg(&mdio->dev, "registered phy %pOFn at address %i\n",
-               child, addr);
        return 0;
 }
 
index c9219fd..50bbe0e 100644 (file)
@@ -261,6 +261,8 @@ static struct property *dup_and_fixup_symbol_prop(
 
        of_property_set_flag(new_prop, OF_DYNAMIC);
 
+       kfree(target_path);
+
        return new_prop;
 
 err_free_new_prop:
index c190da5..6327d1f 100644 (file)
@@ -3,22 +3,37 @@
 /plugin/;
 
 /*
- * &electric_1/motor-1 and &spin_ctrl_1 are the same node:
- *   /testcase-data-2/substation@100/motor-1
+ * &electric_1/motor-1/electric and &spin_ctrl_1/electric are the same node:
+ *   /testcase-data-2/substation@100/motor-1/electric
  *
  * Thus the property "rpm_avail" in each fragment will
  * result in an attempt to update the same property twice.
  * This will result in an error and the overlay apply
  * will fail.
+ *
+ * The previous version of this test did not include the extra
+ * level of node 'electric'.  That resulted in the 'rpm_avail'
+ * property being located in the pre-existing node 'motor-1'.
+ * Modifying a property results in a WARNING that a memory leak
+ * will occur if the overlay is removed.  Since the overlay apply
+ * fails, the memory leak does actually occur, and kmemleak will
+ * further report the memory leak if CONFIG_DEBUG_KMEMLEAK is
+ * enabled.  Adding the overlay node 'electric' avoids the
+ * memory leak and thus people who use kmemleak will not
+ * have to debug this non-problem again.
  */
 
 &electric_1 {
 
        motor-1 {
-               rpm_avail = < 100 >;
+               electric {
+                       rpm_avail = < 100 >;
+               };
        };
 };
 
 &spin_ctrl_1 {
-               rpm_avail = < 100 200 >;
+               electric {
+                       rpm_avail = < 100 200 >;
+               };
 };
index 7e27670..398de04 100644 (file)
@@ -861,6 +861,10 @@ static void __init of_unittest_changeset(void)
        unittest(!of_changeset_revert(&chgset), "revert failed\n");
 
        of_changeset_destroy(&chgset);
+
+       of_node_put(n1);
+       of_node_put(n2);
+       of_node_put(n21);
 #endif
 }
 
@@ -1243,10 +1247,13 @@ static void __init of_unittest_platform_populate(void)
 
        of_platform_populate(np, match, NULL, &test_bus->dev);
        for_each_child_of_node(np, child) {
-               for_each_child_of_node(child, grandchild)
-                       unittest(of_find_device_by_node(grandchild),
+               for_each_child_of_node(child, grandchild) {
+                       pdev = of_find_device_by_node(grandchild);
+                       unittest(pdev,
                                 "Could not create device for node '%pOFn'\n",
                                 grandchild);
+                       of_dev_put(pdev);
+               }
        }
 
        of_platform_depopulate(&test_bus->dev);
@@ -3087,8 +3094,11 @@ static __init void of_unittest_overlay_high_level(void)
                                goto err_unlock;
                        }
                        if (__of_add_property(of_symbols, new_prop)) {
+                               kfree(new_prop->name);
+                               kfree(new_prop->value);
+                               kfree(new_prop);
                                /* "name" auto-generated by unflatten */
-                               if (!strcmp(new_prop->name, "name"))
+                               if (!strcmp(prop->name, "name"))
                                        continue;
                                unittest(0, "duplicate property '%s' in overlay_base node __symbols__",
                                         prop->name);
@@ -3171,21 +3181,21 @@ static __init void of_unittest_overlay_high_level(void)
                   "OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/controller");
 
        EXPECT_BEGIN(KERN_ERR,
-                    "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data-2/substation@100/motor-1/rpm_avail");
+                    "OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/electric");
        EXPECT_BEGIN(KERN_ERR,
-                    "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data-2/substation@100/motor-1/rpm_avail");
+                    "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/rpm_avail");
        EXPECT_BEGIN(KERN_ERR,
-                    "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/rpm_avail");
+                    "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/name");
 
        unittest(overlay_data_apply("overlay_bad_add_dup_prop", NULL),
                 "Adding overlay 'overlay_bad_add_dup_prop' failed\n");
 
        EXPECT_END(KERN_ERR,
-                  "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/rpm_avail");
+                    "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/name");
        EXPECT_END(KERN_ERR,
-                  "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data-2/substation@100/motor-1/rpm_avail");
+                    "OF: overlay: ERROR: multiple fragments add, update, and/or delete property /testcase-data-2/substation@100/motor-1/electric/rpm_avail");
        EXPECT_END(KERN_ERR,
-                  "OF: overlay: WARNING: memory leak will occur if overlay removed, property: /testcase-data-2/substation@100/motor-1/rpm_avail");
+                    "OF: overlay: ERROR: multiple fragments add and/or delete node /testcase-data-2/substation@100/motor-1/electric");
 
        unittest(overlay_data_apply("overlay_bad_phandle", NULL),
                 "Adding overlay 'overlay_bad_phandle' failed\n");
index ba43e6a..e4f01e7 100644 (file)
@@ -819,6 +819,8 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
        if (unlikely(!target_freq)) {
                if (opp_table->required_opp_tables) {
                        ret = _set_required_opps(dev, opp_table, NULL);
+               } else if (!_get_opp_count(opp_table)) {
+                       return 0;
                } else {
                        dev_err(dev, "target frequency can't be 0\n");
                        ret = -EINVAL;
@@ -849,6 +851,18 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
                goto put_opp_table;
        }
 
+       /*
+        * For IO devices which require an OPP on some platforms/SoCs
+        * while just needing to scale the clock on some others
+        * we look for empty OPP tables with just a clock handle and
+        * scale only the clk. This makes dev_pm_opp_set_rate()
+        * equivalent to a clk_set_rate()
+        */
+       if (!_get_opp_count(opp_table)) {
+               ret = _generic_set_opp_clk_only(dev, clk, freq);
+               goto put_opp_table;
+       }
+
        temp_freq = old_freq;
        old_opp = _find_freq_ceil(opp_table, &temp_freq);
        if (IS_ERR(old_opp)) {
index 4880404..ee7b5da 100644 (file)
@@ -34,7 +34,7 @@
 #define PARPORT_MAX_SPINTIME_VALUE 1000
 
 static int do_active_device(struct ctl_table *table, int write,
-                     void __user *result, size_t *lenp, loff_t *ppos)
+                     void *result, size_t *lenp, loff_t *ppos)
 {
        struct parport *port = (struct parport *)table->extra1;
        char buffer[256];
@@ -65,13 +65,13 @@ static int do_active_device(struct ctl_table *table, int write,
                *lenp = len;
 
        *ppos += len;
-
-       return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+       memcpy(result, buffer, len);
+       return 0;
 }
 
 #ifdef CONFIG_PARPORT_1284
 static int do_autoprobe(struct ctl_table *table, int write,
-                       void __user *result, size_t *lenp, loff_t *ppos)
+                       void *result, size_t *lenp, loff_t *ppos)
 {
        struct parport_device_info *info = table->extra2;
        const char *str;
@@ -108,13 +108,13 @@ static int do_autoprobe(struct ctl_table *table, int write,
 
        *ppos += len;
 
-       return copy_to_user (result, buffer, len) ? -EFAULT : 0;
+       memcpy(result, buffer, len);
+       return 0;
 }
 #endif /* IEEE1284.3 support. */
 
 static int do_hardware_base_addr(struct ctl_table *table, int write,
-                                void __user *result,
-                                size_t *lenp, loff_t *ppos)
+                                void *result, size_t *lenp, loff_t *ppos)
 {
        struct parport *port = (struct parport *)table->extra1;
        char buffer[20];
@@ -136,13 +136,12 @@ static int do_hardware_base_addr(struct ctl_table *table, int write,
                *lenp = len;
 
        *ppos += len;
-
-       return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+       memcpy(result, buffer, len);
+       return 0;
 }
 
 static int do_hardware_irq(struct ctl_table *table, int write,
-                          void __user *result,
-                          size_t *lenp, loff_t *ppos)
+                          void *result, size_t *lenp, loff_t *ppos)
 {
        struct parport *port = (struct parport *)table->extra1;
        char buffer[20];
@@ -164,13 +163,12 @@ static int do_hardware_irq(struct ctl_table *table, int write,
                *lenp = len;
 
        *ppos += len;
-
-       return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+       memcpy(result, buffer, len);
+       return 0;
 }
 
 static int do_hardware_dma(struct ctl_table *table, int write,
-                          void __user *result,
-                          size_t *lenp, loff_t *ppos)
+                          void *result, size_t *lenp, loff_t *ppos)
 {
        struct parport *port = (struct parport *)table->extra1;
        char buffer[20];
@@ -192,13 +190,12 @@ static int do_hardware_dma(struct ctl_table *table, int write,
                *lenp = len;
 
        *ppos += len;
-
-       return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+       memcpy(result, buffer, len);
+       return 0;
 }
 
 static int do_hardware_modes(struct ctl_table *table, int write,
-                            void __user *result,
-                            size_t *lenp, loff_t *ppos)
+                            void *result, size_t *lenp, loff_t *ppos)
 {
        struct parport *port = (struct parport *)table->extra1;
        char buffer[40];
@@ -231,8 +228,8 @@ static int do_hardware_modes(struct ctl_table *table, int write,
                *lenp = len;
 
        *ppos += len;
-
-       return copy_to_user(result, buffer, len) ? -EFAULT : 0;
+       memcpy(result, buffer, len);
+       return 0;
 }
 
 #define PARPORT_PORT_DIR(CHILD) { .procname = NULL, .mode = 0555, .child = CHILD }
index 28c9a24..ca9ed57 100644 (file)
@@ -5567,3 +5567,10 @@ static void pci_fixup_no_d0_pme(struct pci_dev *dev)
        dev->pme_support &= ~(PCI_PM_CAP_PME_D0 >> PCI_PM_CAP_PME_SHIFT);
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ASMEDIA, 0x2142, pci_fixup_no_d0_pme);
+
+static void apex_pci_fixup_class(struct pci_dev *pdev)
+{
+       pdev->class = (PCI_CLASS_SYSTEM_OTHER << 8) | pdev->class;
+}
+DECLARE_PCI_FIXUP_CLASS_HEADER(0x1ac1, 0x089a,
+                              PCI_CLASS_NOT_DEFINED, 8, apex_pci_fixup_class);
index a208aca..c591c95 100644 (file)
@@ -1,7 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config PHY_TEGRA_XUSB
        tristate "NVIDIA Tegra XUSB pad controller driver"
-       depends on ARCH_TEGRA
+       depends on ARCH_TEGRA && USB_SUPPORT
+       select USB_COMMON
        select USB_CONN_GPIO
        select USB_PHY
        help
index b7f2c00..9c4af76 100644 (file)
@@ -52,28 +52,15 @@ static int cros_ec_sensorhub_register(struct device *dev,
        int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 };
        struct cros_ec_command *msg = sensorhub->msg;
        struct cros_ec_dev *ec = sensorhub->ec;
-       int ret, i, sensor_num;
+       int ret, i;
        char *name;
 
-       sensor_num = cros_ec_get_sensor_count(ec);
-       if (sensor_num < 0) {
-               dev_err(dev,
-                       "Unable to retrieve sensor information (err:%d)\n",
-                       sensor_num);
-               return sensor_num;
-       }
-
-       sensorhub->sensor_num = sensor_num;
-       if (sensor_num == 0) {
-               dev_err(dev, "Zero sensors reported.\n");
-               return -EINVAL;
-       }
 
        msg->version = 1;
        msg->insize = sizeof(struct ec_response_motion_sense);
        msg->outsize = sizeof(struct ec_params_motion_sense);
 
-       for (i = 0; i < sensor_num; i++) {
+       for (i = 0; i < sensorhub->sensor_num; i++) {
                sensorhub->params->cmd = MOTIONSENSE_CMD_INFO;
                sensorhub->params->info.sensor_num = i;
 
@@ -140,8 +127,7 @@ static int cros_ec_sensorhub_probe(struct platform_device *pdev)
        struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
        struct cros_ec_sensorhub *data;
        struct cros_ec_command *msg;
-       int ret;
-       int i;
+       int ret, i, sensor_num;
 
        msg = devm_kzalloc(dev, sizeof(struct cros_ec_command) +
                           max((u16)sizeof(struct ec_params_motion_sense),
@@ -166,10 +152,52 @@ static int cros_ec_sensorhub_probe(struct platform_device *pdev)
        dev_set_drvdata(dev, data);
 
        /* Check whether this EC is a sensor hub. */
-       if (cros_ec_check_features(data->ec, EC_FEATURE_MOTION_SENSE)) {
+       if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) {
+               sensor_num = cros_ec_get_sensor_count(ec);
+               if (sensor_num < 0) {
+                       dev_err(dev,
+                               "Unable to retrieve sensor information (err:%d)\n",
+                               sensor_num);
+                       return sensor_num;
+               }
+               if (sensor_num == 0) {
+                       dev_err(dev, "Zero sensors reported.\n");
+                       return -EINVAL;
+               }
+               data->sensor_num = sensor_num;
+
+               /*
+                * Prepare the ring handler before enumering the
+                * sensors.
+                */
+               if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
+                       ret = cros_ec_sensorhub_ring_allocate(data);
+                       if (ret)
+                               return ret;
+               }
+
+               /* Enumerate the sensors.*/
                ret = cros_ec_sensorhub_register(dev, data);
                if (ret)
                        return ret;
+
+               /*
+                * When the EC does not have a FIFO, the sensors will query
+                * their data themselves via sysfs or a software trigger.
+                */
+               if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
+                       ret = cros_ec_sensorhub_ring_add(data);
+                       if (ret)
+                               return ret;
+                       /*
+                        * The msg and its data is not under the control of the
+                        * ring handler.
+                        */
+                       return devm_add_action_or_reset(dev,
+                                       cros_ec_sensorhub_ring_remove,
+                                       data);
+               }
+
        } else {
                /*
                 * If the device has sensors but does not claim to
@@ -184,22 +212,6 @@ static int cros_ec_sensorhub_probe(struct platform_device *pdev)
                }
        }
 
-       /*
-        * If the EC does not have a FIFO, the sensors will query their data
-        * themselves via sysfs or a software trigger.
-        */
-       if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
-               ret = cros_ec_sensorhub_ring_add(data);
-               if (ret)
-                       return ret;
-               /*
-                * The msg and its data is not under the control of the ring
-                * handler.
-                */
-               return devm_add_action_or_reset(dev,
-                                               cros_ec_sensorhub_ring_remove,
-                                               data);
-       }
 
        return 0;
 }
index 230e6cf..24e48d9 100644 (file)
@@ -40,7 +40,7 @@ cros_sensorhub_send_sample(struct cros_ec_sensorhub *sensorhub,
        int id = sample->sensor_id;
        struct iio_dev *indio_dev;
 
-       if (id > sensorhub->sensor_num)
+       if (id >= sensorhub->sensor_num)
                return -EINVAL;
 
        cb = sensorhub->push_data[id].push_data_cb;
@@ -820,7 +820,7 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
        if (fifo_info->count > sensorhub->fifo_size ||
            fifo_info->size != sensorhub->fifo_size) {
                dev_warn(sensorhub->dev,
-                        "Mismatch EC data: count %d, size %d - expected %d",
+                        "Mismatch EC data: count %d, size %d - expected %d\n",
                         fifo_info->count, fifo_info->size,
                         sensorhub->fifo_size);
                goto error;
@@ -851,14 +851,14 @@ static void cros_ec_sensorhub_ring_handler(struct cros_ec_sensorhub *sensorhub)
                }
                if (number_data > fifo_info->count - i) {
                        dev_warn(sensorhub->dev,
-                                "Invalid EC data: too many entry received: %d, expected %d",
+                                "Invalid EC data: too many entry received: %d, expected %d\n",
                                 number_data, fifo_info->count - i);
                        break;
                }
                if (out + number_data >
                    sensorhub->ring + fifo_info->count) {
                        dev_warn(sensorhub->dev,
-                                "Too many samples: %d (%zd data) to %d entries for expected %d entries",
+                                "Too many samples: %d (%zd data) to %d entries for expected %d entries\n",
                                 i, out - sensorhub->ring, i + number_data,
                                 fifo_info->count);
                        break;
@@ -957,17 +957,15 @@ static int cros_ec_sensorhub_event(struct notifier_block *nb,
 }
 
 /**
- * cros_ec_sensorhub_ring_add() - Add the FIFO functionality if the EC
- *                               supports it.
+ * cros_ec_sensorhub_ring_allocate() - Prepare the FIFO functionality if the EC
+ *                                    supports it.
  *
  * @sensorhub : Sensor Hub object.
  *
  * Return: 0 on success.
  */
-int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub)
+int cros_ec_sensorhub_ring_allocate(struct cros_ec_sensorhub *sensorhub)
 {
-       struct cros_ec_dev *ec = sensorhub->ec;
-       int ret;
        int fifo_info_length =
                sizeof(struct ec_response_motion_sense_fifo_info) +
                sizeof(u16) * sensorhub->sensor_num;
@@ -978,6 +976,49 @@ int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub)
        if (!sensorhub->fifo_info)
                return -ENOMEM;
 
+       /*
+        * Allocate the callback area based on the number of sensors.
+        * Add one for the sensor ring.
+        */
+       sensorhub->push_data = devm_kcalloc(sensorhub->dev,
+                       sensorhub->sensor_num,
+                       sizeof(*sensorhub->push_data),
+                       GFP_KERNEL);
+       if (!sensorhub->push_data)
+               return -ENOMEM;
+
+       sensorhub->tight_timestamps = cros_ec_check_features(
+                       sensorhub->ec,
+                       EC_FEATURE_MOTION_SENSE_TIGHT_TIMESTAMPS);
+
+       if (sensorhub->tight_timestamps) {
+               sensorhub->batch_state = devm_kcalloc(sensorhub->dev,
+                               sensorhub->sensor_num,
+                               sizeof(*sensorhub->batch_state),
+                               GFP_KERNEL);
+               if (!sensorhub->batch_state)
+                       return -ENOMEM;
+       }
+
+       return 0;
+}
+
+/**
+ * cros_ec_sensorhub_ring_add() - Add the FIFO functionality if the EC
+ *                               supports it.
+ *
+ * @sensorhub : Sensor Hub object.
+ *
+ * Return: 0 on success.
+ */
+int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub)
+{
+       struct cros_ec_dev *ec = sensorhub->ec;
+       int ret;
+       int fifo_info_length =
+               sizeof(struct ec_response_motion_sense_fifo_info) +
+               sizeof(u16) * sensorhub->sensor_num;
+
        /* Retrieve FIFO information */
        sensorhub->msg->version = 2;
        sensorhub->params->cmd = MOTIONSENSE_CMD_FIFO_INFO;
@@ -998,31 +1039,9 @@ int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub)
        if (!sensorhub->ring)
                return -ENOMEM;
 
-       /*
-        * Allocate the callback area based on the number of sensors.
-        */
-       sensorhub->push_data = devm_kcalloc(
-                       sensorhub->dev, sensorhub->sensor_num,
-                       sizeof(*sensorhub->push_data),
-                       GFP_KERNEL);
-       if (!sensorhub->push_data)
-               return -ENOMEM;
-
        sensorhub->fifo_timestamp[CROS_EC_SENSOR_LAST_TS] =
                cros_ec_get_time_ns();
 
-       sensorhub->tight_timestamps = cros_ec_check_features(
-                       ec, EC_FEATURE_MOTION_SENSE_TIGHT_TIMESTAMPS);
-
-       if (sensorhub->tight_timestamps) {
-               sensorhub->batch_state = devm_kcalloc(sensorhub->dev,
-                               sensorhub->sensor_num,
-                               sizeof(*sensorhub->batch_state),
-                               GFP_KERNEL);
-               if (!sensorhub->batch_state)
-                       return -ENOMEM;
-       }
-
        /* Register the notifier that will act as a top half interrupt. */
        sensorhub->notifier.notifier_call = cros_ec_sensorhub_event;
        ret = blocking_notifier_chain_register(&ec->ec_dev->event_notifier,
index 6f12747..c4404d9 100644 (file)
@@ -515,9 +515,33 @@ static struct asus_wmi_driver asus_nb_wmi_driver = {
        .detect_quirks = asus_nb_wmi_quirks,
 };
 
+static const struct dmi_system_id asus_nb_wmi_blacklist[] __initconst = {
+       {
+               /*
+                * asus-nb-wm adds no functionality. The T100TA has a detachable
+                * USB kbd, so no hotkeys and it has no WMI rfkill; and loading
+                * asus-nb-wm causes the camera LED to turn and _stay_ on.
+                */
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
+               },
+       },
+       {
+               /* The Asus T200TA has the same issue as the T100TA */
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T200TA"),
+               },
+       },
+       {} /* Terminating entry */
+};
 
 static int __init asus_nb_wmi_init(void)
 {
+       if (dmi_check_system(asus_nb_wmi_blacklist))
+               return -ENODEV;
+
        return asus_wmi_register_driver(&asus_nb_wmi_driver);
 }
 
index b96d172..12d5ab7 100644 (file)
@@ -53,7 +53,7 @@ static int uncore_max_entries __read_mostly;
 /* Storage for uncore data for all instances */
 static struct uncore_data *uncore_instances;
 /* Root of the all uncore sysfs kobjs */
-struct kobject *uncore_root_kobj;
+static struct kobject *uncore_root_kobj;
 /* Stores the CPU mask of the target CPUs to use during uncore read/write */
 static cpumask_t uncore_cpu_mask;
 /* CPU online callback register instance */
index d2a5d4c..7c8bdab 100644 (file)
@@ -255,7 +255,7 @@ static const struct pmc_bit_map *ext_cnp_pfear_map[] = {
 };
 
 static const struct pmc_bit_map icl_pfear_map[] = {
-       /* Ice Lake generation onwards only */
+       /* Ice Lake and Jasper Lake generation onwards only */
        {"RES_65",              BIT(0)},
        {"RES_66",              BIT(1)},
        {"RES_67",              BIT(2)},
@@ -274,7 +274,7 @@ static const struct pmc_bit_map *ext_icl_pfear_map[] = {
 };
 
 static const struct pmc_bit_map tgl_pfear_map[] = {
-       /* Tiger Lake, Elkhart Lake and Jasper Lake generation onwards only */
+       /* Tiger Lake and Elkhart Lake generation onwards only */
        {"PSF9",                BIT(0)},
        {"RES_66",              BIT(1)},
        {"RES_67",              BIT(2)},
@@ -692,7 +692,6 @@ static void pmc_core_lpm_display(struct pmc_dev *pmcdev, struct device *dev,
        kfree(lpm_regs);
 }
 
-#if IS_ENABLED(CONFIG_DEBUG_FS)
 static bool slps0_dbg_latch;
 
 static inline u8 pmc_core_reg_read_byte(struct pmc_dev *pmcdev, int offset)
@@ -1133,15 +1132,6 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
                                    &pmc_core_substate_l_sts_regs_fops);
        }
 }
-#else
-static inline void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
-{
-}
-
-static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
-{
-}
-#endif /* CONFIG_DEBUG_FS */
 
 static const struct x86_cpu_id intel_pmc_core_ids[] = {
        X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L,           &spt_reg_map),
@@ -1156,7 +1146,7 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = {
        X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L,         &tgl_reg_map),
        X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE,           &tgl_reg_map),
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT,        &tgl_reg_map),
-       X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L,      &tgl_reg_map),
+       X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L,      &icl_reg_map),
        {}
 };
 
@@ -1260,13 +1250,11 @@ static int pmc_core_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-
 static bool warn_on_s0ix_failures;
 module_param(warn_on_s0ix_failures, bool, 0644);
 MODULE_PARM_DESC(warn_on_s0ix_failures, "Check and warn for S0ix failures");
 
-static int pmc_core_suspend(struct device *dev)
+static __maybe_unused int pmc_core_suspend(struct device *dev)
 {
        struct pmc_dev *pmcdev = dev_get_drvdata(dev);
 
@@ -1318,7 +1306,7 @@ static inline bool pmc_core_is_s0ix_failed(struct pmc_dev *pmcdev)
        return false;
 }
 
-static int pmc_core_resume(struct device *dev)
+static __maybe_unused int pmc_core_resume(struct device *dev)
 {
        struct pmc_dev *pmcdev = dev_get_drvdata(dev);
        const struct pmc_bit_map **maps = pmcdev->map->lpm_sts;
@@ -1348,8 +1336,6 @@ static int pmc_core_resume(struct device *dev)
        return 0;
 }
 
-#endif
-
 static const struct dev_pm_ops pmc_core_pm_ops = {
        SET_LATE_SYSTEM_SLEEP_PM_OPS(pmc_core_suspend, pmc_core_resume)
 };
index 0d50b24..5eae55d 100644 (file)
@@ -282,9 +282,7 @@ struct pmc_dev {
        u32 base_addr;
        void __iomem *regbase;
        const struct pmc_reg_map *map;
-#if IS_ENABLED(CONFIG_DEBUG_FS)
        struct dentry *dbgfs_dir;
-#endif /* CONFIG_DEBUG_FS */
        int pmc_xram_read_bit;
        struct mutex lock; /* generic mutex lock for PMC Core */
 
index 946ac2d..cc4f9cb 100644 (file)
@@ -522,8 +522,8 @@ static int mshw0011_probe(struct i2c_client *client)
        strlcpy(board_info.type, "MSHW0011-bat0", I2C_NAME_SIZE);
 
        bat0 = i2c_acpi_new_device(dev, 1, &board_info);
-       if (!bat0)
-               return -ENOMEM;
+       if (IS_ERR(bat0))
+               return PTR_ERR(bat0);
 
        data->bat0 = bat0;
        i2c_set_clientdata(bat0, data);
index 8eaadba..0f70448 100644 (file)
@@ -9548,7 +9548,7 @@ static ssize_t tpacpi_battery_store(int what,
                if (!battery_info.batteries[battery].start_support)
                        return -ENODEV;
                /* valid values are [0, 99] */
-               if (value < 0 || value > 99)
+               if (value > 99)
                        return -EINVAL;
                if (value > battery_info.batteries[battery].charge_stop)
                        return -EINVAL;
index 601cbb2..54a2546 100644 (file)
@@ -23,7 +23,7 @@ struct xiaomi_wmi {
        unsigned int key_code;
 };
 
-int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context)
+static int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context)
 {
        struct xiaomi_wmi *data;
 
@@ -48,7 +48,7 @@ int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context)
        return input_register_device(data->input_dev);
 }
 
-void xiaomi_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy)
+static void xiaomi_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy)
 {
        struct xiaomi_wmi *data;
 
index 65c23ef..b3c05ff 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/power_supply.h>
 #include <linux/errno.h>
 #include <linux/delay.h>
-#include <linux/vermagic.h>
+#include <generated/utsrelease.h>
 
 enum test_power_id {
        TEST_AC,
index 93d574f..375cd6e 100644 (file)
@@ -136,6 +136,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
                caps.pps = ptp->info->pps;
                caps.n_pins = ptp->info->n_pins;
                caps.cross_timestamping = ptp->info->getcrosststamp != NULL;
+               caps.adjust_phase = ptp->info->adjphase != NULL;
                if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
                        err = -EFAULT;
                break;
index acabbe7..fc984a8 100644 (file)
@@ -146,6 +146,9 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
                else
                        err = ops->adjfreq(ops, ppb);
                ptp->dialed_frequency = tx->freq;
+       } else if (tx->modes & ADJ_OFFSET) {
+               if (ops->adjphase)
+                       err = ops->adjphase(ops, tx->offset);
        } else if (tx->modes == 0) {
                tx->freq = ptp->dialed_frequency;
                err = 0;
index 032e112..ceb6bc5 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/ptp_clock_kernel.h>
 #include <linux/delay.h>
+#include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/timekeeping.h>
 
@@ -24,6 +25,16 @@ MODULE_LICENSE("GPL");
 
 #define SETTIME_CORRECTION (0)
 
+static long set_write_phase_ready(struct ptp_clock_info *ptp)
+{
+       struct idtcm_channel *channel =
+               container_of(ptp, struct idtcm_channel, caps);
+
+       channel->write_phase_ready = 1;
+
+       return 0;
+}
+
 static int char_array_to_timespec(u8 *buf,
                                  u8 count,
                                  struct timespec64 *ts)
@@ -780,7 +791,7 @@ static int idtcm_load_firmware(struct idtcm *idtcm,
 
                        /* Page size 128, last 4 bytes of page skipped */
                        if (((loaddr > 0x7b) && (loaddr <= 0x7f))
-                            || ((loaddr > 0xfb) && (loaddr <= 0xff)))
+                            || loaddr > 0xfb)
                                continue;
 
                        err = idtcm_write(idtcm, regaddr, 0, &val, sizeof(val));
@@ -871,6 +882,64 @@ static int idtcm_set_pll_mode(struct idtcm_channel *channel,
 
 /* PTP Hardware Clock interface */
 
+/**
+ * @brief Maximum absolute value for write phase offset in picoseconds
+ *
+ * Destination signed register is 32-bit register in resolution of 50ps
+ *
+ * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350
+ */
+static int _idtcm_adjphase(struct idtcm_channel *channel, s32 delta_ns)
+{
+       struct idtcm *idtcm = channel->idtcm;
+
+       int err;
+       u8 i;
+       u8 buf[4] = {0};
+       s32 phase_50ps;
+       s64 offset_ps;
+
+       if (channel->pll_mode != PLL_MODE_WRITE_PHASE) {
+
+               err = idtcm_set_pll_mode(channel, PLL_MODE_WRITE_PHASE);
+
+               if (err)
+                       return err;
+
+               channel->write_phase_ready = 0;
+
+               ptp_schedule_worker(channel->ptp_clock,
+                                   msecs_to_jiffies(WR_PHASE_SETUP_MS));
+       }
+
+       if (!channel->write_phase_ready)
+               delta_ns = 0;
+
+       offset_ps = (s64)delta_ns * 1000;
+
+       /*
+        * Check for 32-bit signed max * 50:
+        *
+        * 0x7fffffff * 50 =  2147483647 * 50 = 107374182350
+        */
+       if (offset_ps > MAX_ABS_WRITE_PHASE_PICOSECONDS)
+               offset_ps = MAX_ABS_WRITE_PHASE_PICOSECONDS;
+       else if (offset_ps < -MAX_ABS_WRITE_PHASE_PICOSECONDS)
+               offset_ps = -MAX_ABS_WRITE_PHASE_PICOSECONDS;
+
+       phase_50ps = DIV_ROUND_CLOSEST(div64_s64(offset_ps, 50), 1);
+
+       for (i = 0; i < 4; i++) {
+               buf[i] = phase_50ps & 0xff;
+               phase_50ps >>= 8;
+       }
+
+       err = idtcm_write(idtcm, channel->dpll_phase, DPLL_WR_PHASE,
+                         buf, sizeof(buf));
+
+       return err;
+}
+
 static int idtcm_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
 {
        struct idtcm_channel *channel =
@@ -977,6 +1046,24 @@ static int idtcm_adjtime(struct ptp_clock_info *ptp, s64 delta)
        return err;
 }
 
+static int idtcm_adjphase(struct ptp_clock_info *ptp, s32 delta)
+{
+       struct idtcm_channel *channel =
+               container_of(ptp, struct idtcm_channel, caps);
+
+       struct idtcm *idtcm = channel->idtcm;
+
+       int err;
+
+       mutex_lock(&idtcm->reg_lock);
+
+       err = _idtcm_adjphase(channel, delta);
+
+       mutex_unlock(&idtcm->reg_lock);
+
+       return err;
+}
+
 static int idtcm_enable(struct ptp_clock_info *ptp,
                        struct ptp_clock_request *rq, int on)
 {
@@ -1055,13 +1142,16 @@ static const struct ptp_clock_info idtcm_caps = {
        .owner          = THIS_MODULE,
        .max_adj        = 244000,
        .n_per_out      = 1,
+       .adjphase       = &idtcm_adjphase,
        .adjfreq        = &idtcm_adjfreq,
        .adjtime        = &idtcm_adjtime,
        .gettime64      = &idtcm_gettime,
        .settime64      = &idtcm_settime,
        .enable         = &idtcm_enable,
+       .do_aux_work    = &set_write_phase_ready,
 };
 
+
 static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
 {
        struct idtcm_channel *channel;
@@ -1146,6 +1236,8 @@ static int idtcm_enable_channel(struct idtcm *idtcm, u32 index)
        if (!channel->ptp_clock)
                return -ENOTSUPP;
 
+       channel->write_phase_ready = 0;
+
        dev_info(&idtcm->client->dev, "PLL%d registered as ptp%d\n",
                 index, channel->ptp_clock->index);
 
index 6c1f93a..3de0eb7 100644 (file)
@@ -15,6 +15,8 @@
 #define FW_FILENAME    "idtcm.bin"
 #define MAX_PHC_PLL    4
 
+#define MAX_ABS_WRITE_PHASE_PICOSECONDS (107374182350LL)
+
 #define PLL_MASK_ADDR          (0xFFA5)
 #define DEFAULT_PLL_MASK       (0x04)
 
@@ -33,8 +35,9 @@
 
 #define POST_SM_RESET_DELAY_MS         (3000)
 #define PHASE_PULL_IN_THRESHOLD_NS     (150000)
-#define TOD_WRITE_OVERHEAD_COUNT_MAX    (5)
-#define TOD_BYTE_COUNT                  (11)
+#define TOD_WRITE_OVERHEAD_COUNT_MAX   (5)
+#define TOD_BYTE_COUNT                 (11)
+#define WR_PHASE_SETUP_MS              (5000)
 
 /* Values of DPLL_N.DPLL_MODE.PLL_MODE */
 enum pll_mode {
@@ -77,6 +80,7 @@ struct idtcm_channel {
        u16                     hw_dpll_n;
        enum pll_mode           pll_mode;
        u16                     output_mask;
+       int                     write_phase_ready;
 };
 
 struct idtcm {
index b63ac24..179f6c4 100644 (file)
@@ -23,12 +23,12 @@ MODULE_VERSION("1.0");
 MODULE_LICENSE("GPL");
 
 /* Module Parameters */
-u32 sync_tod_timeout = SYNC_TOD_TIMEOUT_SEC;
+static u32 sync_tod_timeout = SYNC_TOD_TIMEOUT_SEC;
 module_param(sync_tod_timeout, uint, 0);
 MODULE_PARM_DESC(sync_tod_timeout,
 "duration in second to keep SYNC_TOD on (set to 0 to keep it always on)");
 
-u32 phase_snap_threshold = SNAP_THRESHOLD_NS;
+static u32 phase_snap_threshold = SNAP_THRESHOLD_NS;
 module_param(phase_snap_threshold, uint, 0);
 MODULE_PARM_DESC(phase_snap_threshold,
 "threshold (150000ns by default) below which adjtime would ignore");
@@ -881,7 +881,7 @@ static int idt82p33_load_firmware(struct idt82p33 *idt82p33)
 
                        /* Page size 128, last 4 bytes of page skipped */
                        if (((loaddr > 0x7b) && (loaddr <= 0x7f))
-                            || ((loaddr > 0xfb) && (loaddr <= 0xff)))
+                            || loaddr > 0xfb)
                                continue;
 
                        err = idt82p33_write(idt82p33, _ADDR(page, loaddr),
index dfda54c..7711651 100644 (file)
@@ -400,8 +400,8 @@ static int ines_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr)
        ines_write32(port, ts_stat_rx, ts_stat_rx);
        ines_write32(port, ts_stat_tx, ts_stat_tx);
 
-       port->rxts_enabled = ts_stat_rx == TS_ENABLE ? true : false;
-       port->txts_enabled = ts_stat_tx == TS_ENABLE ? true : false;
+       port->rxts_enabled = ts_stat_rx == TS_ENABLE;
+       port->txts_enabled = ts_stat_tx == TS_ENABLE;
 
        spin_unlock_irqrestore(&port->lock, flags);
 
@@ -783,16 +783,10 @@ static struct mii_timestamping_ctrl ines_ctrl = {
 static int ines_ptp_ctrl_probe(struct platform_device *pld)
 {
        struct ines_clock *clock;
-       struct resource *res;
        void __iomem *addr;
        int err = 0;
 
-       res = platform_get_resource(pld, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(&pld->dev, "missing memory resource\n");
-               return -EINVAL;
-       }
-       addr = devm_ioremap_resource(&pld->dev, res);
+       addr = devm_platform_ioremap_resource(pld, 0);
        if (IS_ERR(addr)) {
                err = PTR_ERR(addr);
                goto out;
index fc7d0b7..658d33f 100644 (file)
@@ -22,7 +22,7 @@ struct kvm_ptp_clock {
        struct ptp_clock_info caps;
 };
 
-DEFINE_SPINLOCK(kvm_ptp_lock);
+static DEFINE_SPINLOCK(kvm_ptp_lock);
 
 static struct pvclock_vsyscall_time_info *hv_clock;
 
index deb2009..0066c83 100644 (file)
@@ -68,7 +68,7 @@ struct mtk_scp {
        wait_queue_head_t ack_wq;
 
        void __iomem *cpu_addr;
-       phys_addr_t phys_addr;
+       dma_addr_t dma_addr;
        size_t dram_size;
 
        struct rproc_subdev *rpmsg_subdev;
index ea3743e..2bead57 100644 (file)
@@ -330,7 +330,7 @@ static void *scp_da_to_va(struct rproc *rproc, u64 da, size_t len)
                if (offset >= 0 && (offset + len) < scp->sram_size)
                        return (void __force *)scp->sram_base + offset;
        } else {
-               offset = da - scp->phys_addr;
+               offset = da - scp->dma_addr;
                if (offset >= 0 && (offset + len) < scp->dram_size)
                        return (void __force *)scp->cpu_addr + offset;
        }
@@ -451,7 +451,7 @@ static int scp_map_memory_region(struct mtk_scp *scp)
        /* Reserved SCP code size */
        scp->dram_size = MAX_CODE_SIZE;
        scp->cpu_addr = dma_alloc_coherent(scp->dev, scp->dram_size,
-                                          &scp->phys_addr, GFP_KERNEL);
+                                          &scp->dma_addr, GFP_KERNEL);
        if (!scp->cpu_addr)
                return -ENOMEM;
 
@@ -461,7 +461,7 @@ static int scp_map_memory_region(struct mtk_scp *scp)
 static void scp_unmap_memory_region(struct mtk_scp *scp)
 {
        dma_free_coherent(scp->dev, scp->dram_size, scp->cpu_addr,
-                         scp->phys_addr);
+                         scp->dma_addr);
        of_reserved_mem_device_release(scp->dev);
 }
 
index ce49c32..5475d4f 100644 (file)
@@ -367,7 +367,7 @@ unroll_pd_votes:
        }
 
        return ret;
-};
+}
 
 static void q6v5_pds_disable(struct q6v5 *qproc, struct device **pds,
                             size_t pd_count)
@@ -1527,7 +1527,7 @@ unroll_attach:
                dev_pm_domain_detach(devs[i], false);
 
        return ret;
-};
+}
 
 static void q6v5_pds_detach(struct q6v5 *qproc, struct device **pds,
                            size_t pd_count)
@@ -1675,7 +1675,7 @@ static int q6v5_probe(struct platform_device *pdev)
        ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name",
                                            1, &qproc->hexagon_mdt_image);
        if (ret < 0 && ret != -EINVAL)
-               return ret;
+               goto free_rproc;
 
        platform_set_drvdata(pdev, qproc);
 
@@ -1766,17 +1766,23 @@ static int q6v5_probe(struct platform_device *pdev)
        qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12);
        if (IS_ERR(qproc->sysmon)) {
                ret = PTR_ERR(qproc->sysmon);
-               goto detach_proxy_pds;
+               goto remove_subdevs;
        }
 
        ret = rproc_add(rproc);
        if (ret)
-               goto detach_proxy_pds;
+               goto remove_sysmon_subdev;
 
        return 0;
 
-detach_proxy_pds:
+remove_sysmon_subdev:
+       qcom_remove_sysmon_subdev(qproc->sysmon);
+remove_subdevs:
        qcom_remove_ipa_notify_subdev(qproc->rproc, &qproc->ipa_notify_subdev);
+       qcom_remove_ssr_subdev(rproc, &qproc->ssr_subdev);
+       qcom_remove_smd_subdev(rproc, &qproc->smd_subdev);
+       qcom_remove_glink_subdev(rproc, &qproc->glink_subdev);
+detach_proxy_pds:
        q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
 detach_active_pds:
        q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count);
@@ -1789,19 +1795,20 @@ free_rproc:
 static int q6v5_remove(struct platform_device *pdev)
 {
        struct q6v5 *qproc = platform_get_drvdata(pdev);
+       struct rproc *rproc = qproc->rproc;
 
-       rproc_del(qproc->rproc);
+       rproc_del(rproc);
 
        qcom_remove_sysmon_subdev(qproc->sysmon);
-       qcom_remove_ipa_notify_subdev(qproc->rproc, &qproc->ipa_notify_subdev);
-       qcom_remove_glink_subdev(qproc->rproc, &qproc->glink_subdev);
-       qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
-       qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev);
+       qcom_remove_ipa_notify_subdev(rproc, &qproc->ipa_notify_subdev);
+       qcom_remove_ssr_subdev(rproc, &qproc->ssr_subdev);
+       qcom_remove_smd_subdev(rproc, &qproc->smd_subdev);
+       qcom_remove_glink_subdev(rproc, &qproc->glink_subdev);
 
-       q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count);
        q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count);
+       q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count);
 
-       rproc_free(qproc->rproc);
+       rproc_free(rproc);
 
        return 0;
 }
index 7f8536b..52b8713 100644 (file)
@@ -4,6 +4,7 @@
  */
 
 #include <linux/remoteproc.h>
+#include <linux/slab.h>
 
 #include "remoteproc_internal.h"
 
index 6a66dbf..0bdd56f 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/regmap.h>
 #include <linux/remoteproc.h>
 #include <linux/reset.h>
+#include <linux/slab.h>
 #include <linux/workqueue.h>
 
 #include "remoteproc_internal.h"
index 232aa4e..83f2b88 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/platform_device.h>
 #include <linux/remoteproc.h>
 #include <linux/rpmsg/mtk_rpmsg.h>
+#include <linux/slab.h>
 #include <linux/workqueue.h>
 
 #include "rpmsg_internal.h"
index a8682f6..376f1ef 100644 (file)
@@ -26,7 +26,6 @@ config DASD
        def_tristate y
        prompt "Support for DASD devices"
        depends on CCW && BLOCK
-       select IOSCHED_DEADLINE
        help
          Enable this option if you want to access DASDs directly utilizing
          S/390s channel subsystem commands. This is necessary for running
index 3850a0f..53120e6 100644 (file)
@@ -63,12 +63,9 @@ config QETH
        prompt "Gigabit Ethernet device support"
        depends on CCW && NETDEVICES && IP_MULTICAST && QDIO && ETHERNET
        help
-         This driver supports the IBM System z OSA Express adapters
-         in QDIO mode (all media types), HiperSockets interfaces and z/VM
-         virtual NICs for Guest LAN and VSWITCH.
-       
-         For details please refer to the documentation provided by IBM at
-         <http://www.ibm.com/developerworks/linux/linux390>
+         This driver supports IBM's OSA Express network adapters in QDIO mode,
+         HiperSockets interfaces and z/VM virtual NICs for Guest LAN and
+         VSWITCH.
 
          To compile this driver as a module, choose M.
          The module name is qeth.
index e0b2631..51ea56b 100644 (file)
@@ -11,6 +11,7 @@
 #define __QETH_CORE_H__
 
 #include <linux/completion.h>
+#include <linux/debugfs.h>
 #include <linux/if.h>
 #include <linux/if_arp.h>
 #include <linux/etherdevice.h>
 #include <linux/seq_file.h>
 #include <linux/hashtable.h>
 #include <linux/ip.h>
+#include <linux/rcupdate.h>
 #include <linux/refcount.h>
 #include <linux/timer.h>
+#include <linux/types.h>
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 
@@ -31,6 +34,7 @@
 #include <net/ipv6.h>
 #include <net/if_inet6.h>
 #include <net/addrconf.h>
+#include <net/route.h>
 #include <net/sch_generic.h>
 #include <net/tcp.h>
 
@@ -231,11 +235,7 @@ struct qeth_hdr_layer3 {
        __u16 frame_offset;
        union {
                /* TX: */
-               struct in6_addr ipv6_addr;
-               struct ipv4 {
-                       u8 res[12];
-                       u32 addr;
-               } ipv4;
+               struct in6_addr addr;
                /* RX: */
                struct rx {
                        u8 res1[2];
@@ -352,10 +352,15 @@ static inline bool qeth_l3_same_next_hop(struct qeth_hdr_layer3 *h1,
                                         struct qeth_hdr_layer3 *h2)
 {
        return !((h1->flags ^ h2->flags) & QETH_HDR_IPV6) &&
-              ipv6_addr_equal(&h1->next_hop.ipv6_addr,
-                              &h2->next_hop.ipv6_addr);
+              ipv6_addr_equal(&h1->next_hop.addr, &h2->next_hop.addr);
 }
 
+struct qeth_local_addr {
+       struct hlist_node hnode;
+       struct rcu_head rcu;
+       struct in6_addr addr;
+};
+
 enum qeth_qdio_info_states {
        QETH_QDIO_UNINITIALIZED,
        QETH_QDIO_ALLOCATED,
@@ -688,6 +693,9 @@ struct qeth_card_info {
        u8 promisc_mode:1;
        u8 use_v1_blkt:1;
        u8 is_vm_nic:1;
+       /* no bitfield, we take a pointer on these two: */
+       u8 has_lp2lp_cso_v6;
+       u8 has_lp2lp_cso_v4;
        enum qeth_card_types type;
        enum qeth_link_types link_type;
        int broadcast_capable;
@@ -786,6 +794,7 @@ struct qeth_card {
        struct qeth_channel data;
 
        struct net_device *dev;
+       struct dentry *debugfs;
        struct qeth_card_stats stats;
        struct qeth_card_info info;
        struct qeth_token token;
@@ -797,6 +806,10 @@ struct qeth_card {
        wait_queue_head_t wait_q;
        DECLARE_HASHTABLE(mac_htable, 4);
        DECLARE_HASHTABLE(ip_htable, 4);
+       DECLARE_HASHTABLE(local_addrs4, 4);
+       DECLARE_HASHTABLE(local_addrs6, 4);
+       spinlock_t local_addrs4_lock;
+       spinlock_t local_addrs6_lock;
        struct mutex ip_lock;
        DECLARE_HASHTABLE(ip_mc_htable, 4);
        struct work_struct rx_mode_work;
@@ -928,6 +941,25 @@ static inline struct dst_entry *qeth_dst_check_rcu(struct sk_buff *skb, int ipv)
        return dst;
 }
 
+static inline __be32 qeth_next_hop_v4_rcu(struct sk_buff *skb,
+                                         struct dst_entry *dst)
+{
+       struct rtable *rt = (struct rtable *) dst;
+
+       return (rt) ? rt_nexthop(rt, ip_hdr(skb)->daddr) : ip_hdr(skb)->daddr;
+}
+
+static inline struct in6_addr *qeth_next_hop_v6_rcu(struct sk_buff *skb,
+                                                   struct dst_entry *dst)
+{
+       struct rt6_info *rt = (struct rt6_info *) dst;
+
+       if (rt && !ipv6_addr_any(&rt->rt6i_gateway))
+               return &rt->rt6i_gateway;
+       else
+               return &ipv6_hdr(skb)->daddr;
+}
+
 static inline void qeth_tx_csum(struct sk_buff *skb, u8 *flags, int ipv)
 {
        *flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ;
@@ -1021,7 +1053,8 @@ struct qeth_cmd_buffer *qeth_get_diag_cmd(struct qeth_card *card,
 void qeth_notify_cmd(struct qeth_cmd_buffer *iob, int reason);
 void qeth_put_cmd(struct qeth_cmd_buffer *iob);
 
-void qeth_schedule_recovery(struct qeth_card *);
+int qeth_schedule_recovery(struct qeth_card *card);
+void qeth_flush_local_addrs(struct qeth_card *card);
 int qeth_poll(struct napi_struct *napi, int budget);
 void qeth_clear_ipacmd_list(struct qeth_card *);
 int qeth_qdio_clear_card(struct qeth_card *, int);
index f768946..db8e069 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/if_vlan.h>
 #include <linux/netdevice.h>
 #include <linux/netdev_features.h>
+#include <linux/rcutree.h>
 #include <linux/skbuff.h>
 #include <linux/vmalloc.h>
 
@@ -60,6 +61,7 @@ EXPORT_SYMBOL_GPL(qeth_core_header_cache);
 static struct kmem_cache *qeth_qdio_outbuf_cache;
 
 static struct device *qeth_core_root_dev;
+static struct dentry *qeth_debugfs_root;
 static struct lock_class_key qdio_out_skb_queue_key;
 
 static void qeth_issue_next_read_cb(struct qeth_card *card,
@@ -623,6 +625,257 @@ void qeth_notify_cmd(struct qeth_cmd_buffer *iob, int reason)
 }
 EXPORT_SYMBOL_GPL(qeth_notify_cmd);
 
+static void qeth_flush_local_addrs4(struct qeth_card *card)
+{
+       struct qeth_local_addr *addr;
+       struct hlist_node *tmp;
+       unsigned int i;
+
+       spin_lock_irq(&card->local_addrs4_lock);
+       hash_for_each_safe(card->local_addrs4, i, tmp, addr, hnode) {
+               hash_del_rcu(&addr->hnode);
+               kfree_rcu(addr, rcu);
+       }
+       spin_unlock_irq(&card->local_addrs4_lock);
+}
+
+static void qeth_flush_local_addrs6(struct qeth_card *card)
+{
+       struct qeth_local_addr *addr;
+       struct hlist_node *tmp;
+       unsigned int i;
+
+       spin_lock_irq(&card->local_addrs6_lock);
+       hash_for_each_safe(card->local_addrs6, i, tmp, addr, hnode) {
+               hash_del_rcu(&addr->hnode);
+               kfree_rcu(addr, rcu);
+       }
+       spin_unlock_irq(&card->local_addrs6_lock);
+}
+
+void qeth_flush_local_addrs(struct qeth_card *card)
+{
+       qeth_flush_local_addrs4(card);
+       qeth_flush_local_addrs6(card);
+}
+EXPORT_SYMBOL_GPL(qeth_flush_local_addrs);
+
+static void qeth_add_local_addrs4(struct qeth_card *card,
+                                 struct qeth_ipacmd_local_addrs4 *cmd)
+{
+       unsigned int i;
+
+       if (cmd->addr_length !=
+           sizeof_field(struct qeth_ipacmd_local_addr4, addr)) {
+               dev_err_ratelimited(&card->gdev->dev,
+                                   "Dropped IPv4 ADD LOCAL ADDR event with bad length %u\n",
+                                   cmd->addr_length);
+               return;
+       }
+
+       spin_lock(&card->local_addrs4_lock);
+       for (i = 0; i < cmd->count; i++) {
+               unsigned int key = ipv4_addr_hash(cmd->addrs[i].addr);
+               struct qeth_local_addr *addr;
+               bool duplicate = false;
+
+               hash_for_each_possible(card->local_addrs4, addr, hnode, key) {
+                       if (addr->addr.s6_addr32[3] == cmd->addrs[i].addr) {
+                               duplicate = true;
+                               break;
+                       }
+               }
+
+               if (duplicate)
+                       continue;
+
+               addr = kmalloc(sizeof(*addr), GFP_ATOMIC);
+               if (!addr) {
+                       dev_err(&card->gdev->dev,
+                               "Failed to allocate local addr object. Traffic to %pI4 might suffer.\n",
+                               &cmd->addrs[i].addr);
+                       continue;
+               }
+
+               ipv6_addr_set(&addr->addr, 0, 0, 0, cmd->addrs[i].addr);
+               hash_add_rcu(card->local_addrs4, &addr->hnode, key);
+       }
+       spin_unlock(&card->local_addrs4_lock);
+}
+
+static void qeth_add_local_addrs6(struct qeth_card *card,
+                                 struct qeth_ipacmd_local_addrs6 *cmd)
+{
+       unsigned int i;
+
+       if (cmd->addr_length !=
+           sizeof_field(struct qeth_ipacmd_local_addr6, addr)) {
+               dev_err_ratelimited(&card->gdev->dev,
+                                   "Dropped IPv6 ADD LOCAL ADDR event with bad length %u\n",
+                                   cmd->addr_length);
+               return;
+       }
+
+       spin_lock(&card->local_addrs6_lock);
+       for (i = 0; i < cmd->count; i++) {
+               u32 key = ipv6_addr_hash(&cmd->addrs[i].addr);
+               struct qeth_local_addr *addr;
+               bool duplicate = false;
+
+               hash_for_each_possible(card->local_addrs6, addr, hnode, key) {
+                       if (ipv6_addr_equal(&addr->addr, &cmd->addrs[i].addr)) {
+                               duplicate = true;
+                               break;
+                       }
+               }
+
+               if (duplicate)
+                       continue;
+
+               addr = kmalloc(sizeof(*addr), GFP_ATOMIC);
+               if (!addr) {
+                       dev_err(&card->gdev->dev,
+                               "Failed to allocate local addr object. Traffic to %pI6c might suffer.\n",
+                               &cmd->addrs[i].addr);
+                       continue;
+               }
+
+               addr->addr = cmd->addrs[i].addr;
+               hash_add_rcu(card->local_addrs6, &addr->hnode, key);
+       }
+       spin_unlock(&card->local_addrs6_lock);
+}
+
+static void qeth_del_local_addrs4(struct qeth_card *card,
+                                 struct qeth_ipacmd_local_addrs4 *cmd)
+{
+       unsigned int i;
+
+       if (cmd->addr_length !=
+           sizeof_field(struct qeth_ipacmd_local_addr4, addr)) {
+               dev_err_ratelimited(&card->gdev->dev,
+                                   "Dropped IPv4 DEL LOCAL ADDR event with bad length %u\n",
+                                   cmd->addr_length);
+               return;
+       }
+
+       spin_lock(&card->local_addrs4_lock);
+       for (i = 0; i < cmd->count; i++) {
+               struct qeth_ipacmd_local_addr4 *addr = &cmd->addrs[i];
+               unsigned int key = ipv4_addr_hash(addr->addr);
+               struct qeth_local_addr *tmp;
+
+               hash_for_each_possible(card->local_addrs4, tmp, hnode, key) {
+                       if (tmp->addr.s6_addr32[3] == addr->addr) {
+                               hash_del_rcu(&tmp->hnode);
+                               kfree_rcu(tmp, rcu);
+                               break;
+                       }
+               }
+       }
+       spin_unlock(&card->local_addrs4_lock);
+}
+
+static void qeth_del_local_addrs6(struct qeth_card *card,
+                                 struct qeth_ipacmd_local_addrs6 *cmd)
+{
+       unsigned int i;
+
+       if (cmd->addr_length !=
+           sizeof_field(struct qeth_ipacmd_local_addr6, addr)) {
+               dev_err_ratelimited(&card->gdev->dev,
+                                   "Dropped IPv6 DEL LOCAL ADDR event with bad length %u\n",
+                                   cmd->addr_length);
+               return;
+       }
+
+       spin_lock(&card->local_addrs6_lock);
+       for (i = 0; i < cmd->count; i++) {
+               struct qeth_ipacmd_local_addr6 *addr = &cmd->addrs[i];
+               u32 key = ipv6_addr_hash(&addr->addr);
+               struct qeth_local_addr *tmp;
+
+               hash_for_each_possible(card->local_addrs6, tmp, hnode, key) {
+                       if (ipv6_addr_equal(&tmp->addr, &addr->addr)) {
+                               hash_del_rcu(&tmp->hnode);
+                               kfree_rcu(tmp, rcu);
+                               break;
+                       }
+               }
+       }
+       spin_unlock(&card->local_addrs6_lock);
+}
+
+static bool qeth_next_hop_is_local_v4(struct qeth_card *card,
+                                     struct sk_buff *skb)
+{
+       struct qeth_local_addr *tmp;
+       bool is_local = false;
+       unsigned int key;
+       __be32 next_hop;
+
+       if (hash_empty(card->local_addrs4))
+               return false;
+
+       rcu_read_lock();
+       next_hop = qeth_next_hop_v4_rcu(skb, qeth_dst_check_rcu(skb, 4));
+       key = ipv4_addr_hash(next_hop);
+
+       hash_for_each_possible_rcu(card->local_addrs4, tmp, hnode, key) {
+               if (tmp->addr.s6_addr32[3] == next_hop) {
+                       is_local = true;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+
+       return is_local;
+}
+
+static bool qeth_next_hop_is_local_v6(struct qeth_card *card,
+                                     struct sk_buff *skb)
+{
+       struct qeth_local_addr *tmp;
+       struct in6_addr *next_hop;
+       bool is_local = false;
+       u32 key;
+
+       if (hash_empty(card->local_addrs6))
+               return false;
+
+       rcu_read_lock();
+       next_hop = qeth_next_hop_v6_rcu(skb, qeth_dst_check_rcu(skb, 6));
+       key = ipv6_addr_hash(next_hop);
+
+       hash_for_each_possible_rcu(card->local_addrs6, tmp, hnode, key) {
+               if (ipv6_addr_equal(&tmp->addr, next_hop)) {
+                       is_local = true;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+
+       return is_local;
+}
+
+static int qeth_debugfs_local_addr_show(struct seq_file *m, void *v)
+{
+       struct qeth_card *card = m->private;
+       struct qeth_local_addr *tmp;
+       unsigned int i;
+
+       rcu_read_lock();
+       hash_for_each_rcu(card->local_addrs4, i, tmp, hnode)
+               seq_printf(m, "%pI4\n", &tmp->addr.s6_addr32[3]);
+       hash_for_each_rcu(card->local_addrs6, i, tmp, hnode)
+               seq_printf(m, "%pI6c\n", &tmp->addr);
+       rcu_read_unlock();
+
+       return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(qeth_debugfs_local_addr);
+
 static void qeth_issue_ipa_msg(struct qeth_ipa_cmd *cmd, int rc,
                struct qeth_card *card)
 {
@@ -686,9 +939,19 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
        case IPA_CMD_MODCCID:
                return cmd;
        case IPA_CMD_REGISTER_LOCAL_ADDR:
+               if (cmd->hdr.prot_version == QETH_PROT_IPV4)
+                       qeth_add_local_addrs4(card, &cmd->data.local_addrs4);
+               else if (cmd->hdr.prot_version == QETH_PROT_IPV6)
+                       qeth_add_local_addrs6(card, &cmd->data.local_addrs6);
+
                QETH_CARD_TEXT(card, 3, "irla");
                return NULL;
        case IPA_CMD_UNREGISTER_LOCAL_ADDR:
+               if (cmd->hdr.prot_version == QETH_PROT_IPV4)
+                       qeth_del_local_addrs4(card, &cmd->data.local_addrs4);
+               else if (cmd->hdr.prot_version == QETH_PROT_IPV6)
+                       qeth_del_local_addrs6(card, &cmd->data.local_addrs6);
+
                QETH_CARD_TEXT(card, 3, "urla");
                return NULL;
        default:
@@ -868,16 +1131,18 @@ static int qeth_set_thread_start_bit(struct qeth_card *card,
                unsigned long thread)
 {
        unsigned long flags;
+       int rc = 0;
 
        spin_lock_irqsave(&card->thread_mask_lock, flags);
-       if (!(card->thread_allowed_mask & thread) ||
-             (card->thread_start_mask & thread)) {
-               spin_unlock_irqrestore(&card->thread_mask_lock, flags);
-               return -EPERM;
-       }
-       card->thread_start_mask |= thread;
+       if (!(card->thread_allowed_mask & thread))
+               rc = -EPERM;
+       else if (card->thread_start_mask & thread)
+               rc = -EBUSY;
+       else
+               card->thread_start_mask |= thread;
        spin_unlock_irqrestore(&card->thread_mask_lock, flags);
-       return 0;
+
+       return rc;
 }
 
 static void qeth_clear_thread_start_bit(struct qeth_card *card,
@@ -930,11 +1195,17 @@ static int qeth_do_run_thread(struct qeth_card *card, unsigned long thread)
        return rc;
 }
 
-void qeth_schedule_recovery(struct qeth_card *card)
+int qeth_schedule_recovery(struct qeth_card *card)
 {
+       int rc;
+
        QETH_CARD_TEXT(card, 2, "startrec");
-       if (qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD) == 0)
+
+       rc = qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD);
+       if (!rc)
                schedule_work(&card->kernel_thread_starter);
+
+       return rc;
 }
 
 static int qeth_get_problem(struct qeth_card *card, struct ccw_device *cdev,
@@ -1376,6 +1647,10 @@ static void qeth_setup_card(struct qeth_card *card)
        qeth_init_qdio_info(card);
        INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work);
        INIT_WORK(&card->close_dev_work, qeth_close_dev_handler);
+       hash_init(card->local_addrs4);
+       hash_init(card->local_addrs6);
+       spin_lock_init(&card->local_addrs4_lock);
+       spin_lock_init(&card->local_addrs6_lock);
 }
 
 static void qeth_core_sl_print(struct seq_file *m, struct service_level *slr)
@@ -1412,6 +1687,11 @@ static struct qeth_card *qeth_alloc_card(struct ccwgroup_device *gdev)
        if (!card->read_cmd)
                goto out_read_cmd;
 
+       card->debugfs = debugfs_create_dir(dev_name(&gdev->dev),
+                                          qeth_debugfs_root);
+       debugfs_create_file("local_addrs", 0400, card->debugfs, card,
+                           &qeth_debugfs_local_addr_fops);
+
        card->qeth_service_level.seq_print = qeth_core_sl_print;
        register_service_level(&card->qeth_service_level);
        return card;
@@ -3345,11 +3625,11 @@ static int qeth_switch_to_nonpacking_if_needed(struct qeth_qdio_out_q *queue)
 static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
                               int count)
 {
+       struct qeth_qdio_out_buffer *buf = queue->bufs[index];
+       unsigned int qdio_flags = QDIO_FLAG_SYNC_OUTPUT;
        struct qeth_card *card = queue->card;
-       struct qeth_qdio_out_buffer *buf;
        int rc;
        int i;
-       unsigned int qdio_flags;
 
        for (i = index; i < index + count; ++i) {
                unsigned int bidx = QDIO_BUFNR(i);
@@ -3366,9 +3646,10 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
                if (IS_IQD(card)) {
                        skb_queue_walk(&buf->skb_list, skb)
                                skb_tx_timestamp(skb);
-                       continue;
                }
+       }
 
+       if (!IS_IQD(card)) {
                if (!queue->do_pack) {
                        if ((atomic_read(&queue->used_buffers) >=
                                (QETH_HIGH_WATERMARK_PACK -
@@ -3393,12 +3674,12 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
                                buf->buffer->element[0].sflags |= SBAL_SFLAGS0_PCI_REQ;
                        }
                }
+
+               if (atomic_read(&queue->set_pci_flags_count))
+                       qdio_flags |= QDIO_FLAG_PCI_OUT;
        }
 
        QETH_TXQ_STAT_INC(queue, doorbell);
-       qdio_flags = QDIO_FLAG_SYNC_OUTPUT;
-       if (atomic_read(&queue->set_pci_flags_count))
-               qdio_flags |= QDIO_FLAG_PCI_OUT;
        rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags,
                     queue->queue_no, index, count);
 
@@ -3809,15 +4090,47 @@ static bool qeth_iqd_may_bulk(struct qeth_qdio_out_q *queue,
               qeth_l3_iqd_same_vlan(&prev_hdr->hdr.l3, &curr_hdr->hdr.l3);
 }
 
-static unsigned int __qeth_fill_buffer(struct sk_buff *skb,
-                                      struct qeth_qdio_out_buffer *buf,
-                                      bool is_first_elem, unsigned int offset)
+/**
+ * qeth_fill_buffer() - map skb into an output buffer
+ * @buf:       buffer to transport the skb
+ * @skb:       skb to map into the buffer
+ * @hdr:       qeth_hdr for this skb. Either at skb->data, or allocated
+ *             from qeth_core_header_cache.
+ * @offset:    when mapping the skb, start at skb->data + offset
+ * @hd_len:    if > 0, build a dedicated header element of this size
+ */
+static unsigned int qeth_fill_buffer(struct qeth_qdio_out_buffer *buf,
+                                    struct sk_buff *skb, struct qeth_hdr *hdr,
+                                    unsigned int offset, unsigned int hd_len)
 {
        struct qdio_buffer *buffer = buf->buffer;
        int element = buf->next_element_to_fill;
        int length = skb_headlen(skb) - offset;
        char *data = skb->data + offset;
        unsigned int elem_length, cnt;
+       bool is_first_elem = true;
+
+       __skb_queue_tail(&buf->skb_list, skb);
+
+       /* build dedicated element for HW Header */
+       if (hd_len) {
+               is_first_elem = false;
+
+               buffer->element[element].addr = virt_to_phys(hdr);
+               buffer->element[element].length = hd_len;
+               buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG;
+
+               /* HW header is allocated from cache: */
+               if ((void *)hdr != skb->data)
+                       buf->is_header[element] = 1;
+               /* HW header was pushed and is contiguous with linear part: */
+               else if (length > 0 && !PAGE_ALIGNED(data) &&
+                        (data == (char *)hdr + hd_len))
+                       buffer->element[element].eflags |=
+                               SBAL_EFLAGS_CONTIGUOUS;
+
+               element++;
+       }
 
        /* map linear part into buffer element(s) */
        while (length > 0) {
@@ -3871,40 +4184,6 @@ static unsigned int __qeth_fill_buffer(struct sk_buff *skb,
        return element;
 }
 
-/**
- * qeth_fill_buffer() - map skb into an output buffer
- * @buf:       buffer to transport the skb
- * @skb:       skb to map into the buffer
- * @hdr:       qeth_hdr for this skb. Either at skb->data, or allocated
- *             from qeth_core_header_cache.
- * @offset:    when mapping the skb, start at skb->data + offset
- * @hd_len:    if > 0, build a dedicated header element of this size
- */
-static unsigned int qeth_fill_buffer(struct qeth_qdio_out_buffer *buf,
-                                    struct sk_buff *skb, struct qeth_hdr *hdr,
-                                    unsigned int offset, unsigned int hd_len)
-{
-       struct qdio_buffer *buffer = buf->buffer;
-       bool is_first_elem = true;
-
-       __skb_queue_tail(&buf->skb_list, skb);
-
-       /* build dedicated header element */
-       if (hd_len) {
-               int element = buf->next_element_to_fill;
-               is_first_elem = false;
-
-               buffer->element[element].addr = virt_to_phys(hdr);
-               buffer->element[element].length = hd_len;
-               buffer->element[element].eflags = SBAL_EFLAGS_FIRST_FRAG;
-               /* remember to free cache-allocated qeth_hdr: */
-               buf->is_header[element] = ((void *)hdr != skb->data);
-               buf->next_element_to_fill++;
-       }
-
-       return __qeth_fill_buffer(skb, buf, is_first_elem, offset);
-}
-
 static int __qeth_xmit(struct qeth_card *card, struct qeth_qdio_out_q *queue,
                       struct sk_buff *skb, unsigned int elements,
                       struct qeth_hdr *hdr, unsigned int offset,
@@ -4889,9 +5168,11 @@ out_free_nothing:
 static void qeth_core_free_card(struct qeth_card *card)
 {
        QETH_CARD_TEXT(card, 2, "freecrd");
+
+       unregister_service_level(&card->qeth_service_level);
+       debugfs_remove_recursive(card->debugfs);
        qeth_put_cmd(card->read_cmd);
        destroy_workqueue(card->event_wq);
-       unregister_service_level(&card->qeth_service_level);
        dev_set_drvdata(&card->gdev->dev, NULL);
        kfree(card);
 }
@@ -6300,7 +6581,7 @@ static int qeth_set_csum_off(struct qeth_card *card, enum qeth_ipa_funcs cstype,
 }
 
 static int qeth_set_csum_on(struct qeth_card *card, enum qeth_ipa_funcs cstype,
-                           enum qeth_prot_versions prot)
+                           enum qeth_prot_versions prot, u8 *lp2lp)
 {
        u32 required_features = QETH_IPA_CHECKSUM_UDP | QETH_IPA_CHECKSUM_TCP;
        struct qeth_cmd_buffer *iob;
@@ -6352,18 +6633,17 @@ static int qeth_set_csum_on(struct qeth_card *card, enum qeth_ipa_funcs cstype,
 
        dev_info(&card->gdev->dev, "HW Checksumming (%sbound IPv%d) enabled\n",
                 cstype == IPA_INBOUND_CHECKSUM ? "in" : "out", prot);
-       if (!qeth_ipa_caps_enabled(&caps, QETH_IPA_CHECKSUM_LP2LP) &&
-           cstype == IPA_OUTBOUND_CHECKSUM)
-               dev_warn(&card->gdev->dev,
-                        "Hardware checksumming is performed only if %s and its peer use different OSA Express 3 ports\n",
-                        QETH_CARD_IFNAME(card));
+
+       if (lp2lp)
+               *lp2lp = qeth_ipa_caps_enabled(&caps, QETH_IPA_CHECKSUM_LP2LP);
+
        return 0;
 }
 
 static int qeth_set_ipa_csum(struct qeth_card *card, bool on, int cstype,
-                            enum qeth_prot_versions prot)
+                            enum qeth_prot_versions prot, u8 *lp2lp)
 {
-       return on ? qeth_set_csum_on(card, cstype, prot) :
+       return on ? qeth_set_csum_on(card, cstype, prot, lp2lp) :
                    qeth_set_csum_off(card, cstype, prot);
 }
 
@@ -6451,13 +6731,13 @@ static int qeth_set_ipa_rx_csum(struct qeth_card *card, bool on)
 
        if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
                rc_ipv4 = qeth_set_ipa_csum(card, on, IPA_INBOUND_CHECKSUM,
-                                           QETH_PROT_IPV4);
+                                           QETH_PROT_IPV4, NULL);
        if (!qeth_is_supported6(card, IPA_INBOUND_CHECKSUM_V6))
                /* no/one Offload Assist available, so the rc is trivial */
                return rc_ipv4;
 
        rc_ipv6 = qeth_set_ipa_csum(card, on, IPA_INBOUND_CHECKSUM,
-                                   QETH_PROT_IPV6);
+                                   QETH_PROT_IPV6, NULL);
 
        if (on)
                /* enable: success if any Assist is active */
@@ -6493,6 +6773,24 @@ void qeth_enable_hw_features(struct net_device *dev)
 }
 EXPORT_SYMBOL_GPL(qeth_enable_hw_features);
 
+static void qeth_check_restricted_features(struct qeth_card *card,
+                                          netdev_features_t changed,
+                                          netdev_features_t actual)
+{
+       netdev_features_t ipv6_features = NETIF_F_TSO6;
+       netdev_features_t ipv4_features = NETIF_F_TSO;
+
+       if (!card->info.has_lp2lp_cso_v6)
+               ipv6_features |= NETIF_F_IPV6_CSUM;
+       if (!card->info.has_lp2lp_cso_v4)
+               ipv4_features |= NETIF_F_IP_CSUM;
+
+       if ((changed & ipv6_features) && !(actual & ipv6_features))
+               qeth_flush_local_addrs6(card);
+       if ((changed & ipv4_features) && !(actual & ipv4_features))
+               qeth_flush_local_addrs4(card);
+}
+
 int qeth_set_features(struct net_device *dev, netdev_features_t features)
 {
        struct qeth_card *card = dev->ml_priv;
@@ -6504,13 +6802,15 @@ int qeth_set_features(struct net_device *dev, netdev_features_t features)
 
        if ((changed & NETIF_F_IP_CSUM)) {
                rc = qeth_set_ipa_csum(card, features & NETIF_F_IP_CSUM,
-                                      IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV4);
+                                      IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV4,
+                                      &card->info.has_lp2lp_cso_v4);
                if (rc)
                        changed ^= NETIF_F_IP_CSUM;
        }
        if (changed & NETIF_F_IPV6_CSUM) {
                rc = qeth_set_ipa_csum(card, features & NETIF_F_IPV6_CSUM,
-                                      IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV6);
+                                      IPA_OUTBOUND_CHECKSUM, QETH_PROT_IPV6,
+                                      &card->info.has_lp2lp_cso_v6);
                if (rc)
                        changed ^= NETIF_F_IPV6_CSUM;
        }
@@ -6532,6 +6832,9 @@ int qeth_set_features(struct net_device *dev, netdev_features_t features)
                        changed ^= NETIF_F_TSO6;
        }
 
+       qeth_check_restricted_features(card, dev->features ^ features,
+                                      dev->features ^ changed);
+
        /* everything changed successfully? */
        if ((dev->features ^ features) == changed)
                return 0;
@@ -6568,6 +6871,34 @@ netdev_features_t qeth_features_check(struct sk_buff *skb,
                                      struct net_device *dev,
                                      netdev_features_t features)
 {
+       /* Traffic with local next-hop is not eligible for some offloads: */
+       if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               struct qeth_card *card = dev->ml_priv;
+               netdev_features_t restricted = 0;
+
+               if (skb_is_gso(skb) && !netif_needs_gso(skb, features))
+                       restricted |= NETIF_F_ALL_TSO;
+
+               switch (vlan_get_protocol(skb)) {
+               case htons(ETH_P_IP):
+                       if (!card->info.has_lp2lp_cso_v4)
+                               restricted |= NETIF_F_IP_CSUM;
+
+                       if (restricted && qeth_next_hop_is_local_v4(card, skb))
+                               features &= ~restricted;
+                       break;
+               case htons(ETH_P_IPV6):
+                       if (!card->info.has_lp2lp_cso_v6)
+                               restricted |= NETIF_F_IPV6_CSUM;
+
+                       if (restricted && qeth_next_hop_is_local_v6(card, skb))
+                               features &= ~restricted;
+                       break;
+               default:
+                       break;
+               }
+       }
+
        /* GSO segmentation builds skbs with
         *      a (small) linear part for the headers, and
         *      page frags for the data.
@@ -6717,17 +7048,17 @@ int qeth_stop(struct net_device *dev)
                unsigned int i;
 
                /* Quiesce the NAPI instances: */
-               qeth_for_each_output_queue(card, queue, i) {
+               qeth_for_each_output_queue(card, queue, i)
                        napi_disable(&queue->napi);
-                       del_timer_sync(&queue->timer);
-               }
 
                /* Stop .ndo_start_xmit, might still access queue->napi. */
                netif_tx_disable(dev);
 
-               /* Queues may get re-allocated, so remove the NAPIs here. */
-               qeth_for_each_output_queue(card, queue, i)
+               qeth_for_each_output_queue(card, queue, i) {
+                       del_timer_sync(&queue->timer);
+                       /* Queues may get re-allocated, so remove the NAPIs. */
                        netif_napi_del(&queue->napi);
+               }
        } else {
                netif_tx_disable(dev);
        }
@@ -6745,6 +7076,8 @@ static int __init qeth_core_init(void)
 
        pr_info("loading core functions\n");
 
+       qeth_debugfs_root = debugfs_create_dir("qeth", NULL);
+
        rc = qeth_register_dbf_views();
        if (rc)
                goto dbf_err;
@@ -6786,6 +7119,7 @@ slab_err:
 register_err:
        qeth_unregister_dbf_views();
 dbf_err:
+       debugfs_remove_recursive(qeth_debugfs_root);
        pr_err("Initializing the qeth device driver failed\n");
        return rc;
 }
@@ -6799,6 +7133,7 @@ static void __exit qeth_core_exit(void)
        kmem_cache_destroy(qeth_core_header_cache);
        root_device_unregister(qeth_core_root_dev);
        qeth_unregister_dbf_views();
+       debugfs_remove_recursive(qeth_debugfs_root);
        pr_info("core functions removed\n");
 }
 
index d89a04b..9d6f39d 100644 (file)
@@ -772,6 +772,29 @@ struct qeth_ipacmd_addr_change {
        struct qeth_ipacmd_addr_change_entry entry[];
 } __packed;
 
+/* [UN]REGISTER_LOCAL_ADDRESS notifications */
+struct qeth_ipacmd_local_addr4 {
+       __be32 addr;
+       u32 flags;
+};
+
+struct qeth_ipacmd_local_addrs4 {
+       u32 count;
+       u32 addr_length;
+       struct qeth_ipacmd_local_addr4 addrs[];
+};
+
+struct qeth_ipacmd_local_addr6 {
+       struct in6_addr addr;
+       u32 flags;
+};
+
+struct qeth_ipacmd_local_addrs6 {
+       u32 count;
+       u32 addr_length;
+       struct qeth_ipacmd_local_addr6 addrs[];
+};
+
 /* Header for each IPA command */
 struct qeth_ipacmd_hdr {
        __u8   command;
@@ -803,6 +826,8 @@ struct qeth_ipa_cmd {
                struct qeth_ipacmd_setbridgeport        sbp;
                struct qeth_ipacmd_addr_change          addrchange;
                struct qeth_ipacmd_vnicc                vnicc;
+               struct qeth_ipacmd_local_addrs4         local_addrs4;
+               struct qeth_ipacmd_local_addrs6         local_addrs6;
        } data;
 } __attribute__ ((packed));
 
index d7e429f..c901c94 100644 (file)
@@ -275,17 +275,20 @@ static ssize_t qeth_dev_recover_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t count)
 {
        struct qeth_card *card = dev_get_drvdata(dev);
-       char *tmp;
-       int i;
+       bool reset;
+       int rc;
+
+       rc = kstrtobool(buf, &reset);
+       if (rc)
+               return rc;
 
        if (!qeth_card_hw_is_reachable(card))
                return -EPERM;
 
-       i = simple_strtoul(buf, &tmp, 16);
-       if (i == 1)
-               qeth_schedule_recovery(card);
+       if (reset)
+               rc = qeth_schedule_recovery(card);
 
-       return count;
+       return rc ? rc : count;
 }
 
 static DEVICE_ATTR(recover, 0200, NULL, qeth_dev_recover_store);
index 0bd5b09..da47e42 100644 (file)
@@ -291,6 +291,7 @@ static void qeth_l2_stop_card(struct qeth_card *card)
        qeth_qdio_clear_card(card, 0);
        qeth_clear_working_pool_list(card);
        flush_workqueue(card->event_wq);
+       qeth_flush_local_addrs(card);
        card->info.promisc_mode = 0;
 }
 
@@ -709,6 +710,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
 
        if (card->dev->hw_features & (NETIF_F_TSO | NETIF_F_TSO6)) {
                card->dev->needed_headroom = sizeof(struct qeth_hdr_tso);
+               netif_keep_dst(card->dev);
                netif_set_gso_max_size(card->dev,
                                       PAGE_SIZE * (QDIO_MAX_ELEMENTS_PER_BUFFER - 1));
        }
index 0742a74..1e50aa0 100644 (file)
@@ -1176,6 +1176,7 @@ static void qeth_l3_stop_card(struct qeth_card *card)
        qeth_qdio_clear_card(card, 0);
        qeth_clear_working_pool_list(card);
        flush_workqueue(card->event_wq);
+       qeth_flush_local_addrs(card);
        card->info.promisc_mode = 0;
 }
 
@@ -1693,8 +1694,8 @@ static void qeth_l3_fill_header(struct qeth_qdio_out_q *queue,
 
                if (skb->protocol == htons(ETH_P_AF_IUCV)) {
                        l3_hdr->flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST;
-                       l3_hdr->next_hop.ipv6_addr.s6_addr16[0] = htons(0xfe80);
-                       memcpy(&l3_hdr->next_hop.ipv6_addr.s6_addr32[2],
+                       l3_hdr->next_hop.addr.s6_addr16[0] = htons(0xfe80);
+                       memcpy(&l3_hdr->next_hop.addr.s6_addr32[2],
                               iucv_trans_hdr(skb)->destUserID, 8);
                        return;
                }
@@ -1728,18 +1729,10 @@ static void qeth_l3_fill_header(struct qeth_qdio_out_q *queue,
        l3_hdr->flags |= qeth_l3_cast_type_to_flag(cast_type);
 
        if (ipv == 4) {
-               struct rtable *rt = (struct rtable *) dst;
-
-               *((__be32 *) &hdr->hdr.l3.next_hop.ipv4.addr) = (rt) ?
-                               rt_nexthop(rt, ip_hdr(skb)->daddr) :
-                               ip_hdr(skb)->daddr;
+               l3_hdr->next_hop.addr.s6_addr32[3] =
+                                       qeth_next_hop_v4_rcu(skb, dst);
        } else if (ipv == 6) {
-               struct rt6_info *rt = (struct rt6_info *) dst;
-
-               if (rt && !ipv6_addr_any(&rt->rt6i_gateway))
-                       l3_hdr->next_hop.ipv6_addr = rt->rt6i_gateway;
-               else
-                       l3_hdr->next_hop.ipv6_addr = ipv6_hdr(skb)->daddr;
+               l3_hdr->next_hop.addr = *qeth_next_hop_v6_rcu(skb, dst);
 
                hdr->hdr.l3.flags |= QETH_HDR_IPV6;
                if (!IS_IQD(card))
index 17feff1..2017c43 100644 (file)
@@ -127,7 +127,7 @@ config CHR_DEV_SG
 
          For scanners, look at SANE (<http://www.sane-project.org/>). For CD
          writer software look at Cdrtools
-         (<http://cdrecord.berlios.de/private/cdrecord.html>)
+         (<http://cdrtools.sourceforge.net/>)
          and for burning a "disk at once": CDRDAO
          (<http://cdrdao.sourceforge.net/>). Cdparanoia is a high
          quality digital reader of audio CDs (<http://www.xiph.org/paranoia/>).
index 90a1745..13ed907 100644 (file)
@@ -6,6 +6,7 @@ config SCSI_HISI_SAS
        select SCSI_SAS_LIBSAS
        select BLK_DEV_INTEGRITY
        depends on ATA
+       select SATA_HOST
        help
                This driver supports HiSilicon's SAS HBA, including support based
                on platform device
index f301a80..bf1e98f 100644 (file)
@@ -2539,7 +2539,6 @@ ql_dbg(uint level, scsi_qla_host_t *vha, uint id, const char *fmt, ...)
 {
        va_list va;
        struct va_format vaf;
-       char pbuf[64];
 
        va_start(va, fmt);
 
@@ -2547,6 +2546,8 @@ ql_dbg(uint level, scsi_qla_host_t *vha, uint id, const char *fmt, ...)
        vaf.va = &va;
 
        if (!ql_mask_match(level)) {
+               char pbuf[64];
+
                if (vha != NULL) {
                        const struct pci_dev *pdev = vha->hw->pdev;
                        /* <module-name> <msg-id>:<host> Message */
index 5b2deaa..caa6b84 100644 (file)
@@ -3611,8 +3611,6 @@ qla24xx_detect_sfp(scsi_qla_host_t *vha)
                        ha->lr_distance = LR_DISTANCE_5K;
        }
 
-       if (!vha->flags.init_done)
-               rc = QLA_SUCCESS;
 out:
        ql_dbg(ql_dbg_async, vha, 0x507b,
            "SFP detect: %s-Range SFP %s (nvr=%x ll=%x lr=%x lrd=%x).\n",
index 8d7a905..8a78d39 100644 (file)
@@ -87,7 +87,6 @@ qla24xx_process_abts(struct scsi_qla_host *vha, void *pkt)
        }
 
        /* terminate exchange */
-       memset(rsp_els, 0, sizeof(*rsp_els));
        rsp_els->entry_type = ELS_IOCB_TYPE;
        rsp_els->entry_count = 1;
        rsp_els->nport_handle = ~0;
index 9fd83d1..4ed9043 100644 (file)
@@ -4894,8 +4894,6 @@ qla25xx_set_els_cmds_supported(scsi_qla_host_t *vha)
                return QLA_MEMORY_ALLOC_FAILED;
        }
 
-       memset(els_cmd_map, 0, ELS_CMD_MAP_SIZE);
-
        els_cmd_map[index] |= 1 << bit;
 
        mcp->mb[0] = MBC_SET_RNID_PARAMS;
index d190db5..1d9a486 100644 (file)
@@ -3732,6 +3732,13 @@ qla2x00_remove_one(struct pci_dev *pdev)
        }
        qla2x00_wait_for_hba_ready(base_vha);
 
+       /*
+        * if UNLOADING flag is already set, then continue unload,
+        * where it was set first.
+        */
+       if (test_and_set_bit(UNLOADING, &base_vha->dpc_flags))
+               return;
+
        if (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) ||
            IS_QLA28XX(ha)) {
                if (ha->flags.fw_started)
@@ -3750,15 +3757,6 @@ qla2x00_remove_one(struct pci_dev *pdev)
 
        qla2x00_wait_for_sess_deletion(base_vha);
 
-       /*
-        * if UNLOAD flag is already set, then continue unload,
-        * where it was set first.
-        */
-       if (test_bit(UNLOADING, &base_vha->dpc_flags))
-               return;
-
-       set_bit(UNLOADING, &base_vha->dpc_flags);
-
        qla_nvme_delete(base_vha);
 
        dma_free_coherent(&ha->pdev->dev,
@@ -4864,6 +4862,9 @@ qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type)
        struct qla_work_evt *e;
        uint8_t bail;
 
+       if (test_bit(UNLOADING, &vha->dpc_flags))
+               return NULL;
+
        QLA_VHA_MARK_BUSY(vha, bail);
        if (bail)
                return NULL;
@@ -6628,13 +6629,6 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
        struct pci_dev *pdev = ha->pdev;
        scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
 
-       /*
-        * if UNLOAD flag is already set, then continue unload,
-        * where it was set first.
-        */
-       if (test_bit(UNLOADING, &base_vha->dpc_flags))
-               return;
-
        ql_log(ql_log_warn, base_vha, 0x015b,
            "Disabling adapter.\n");
 
@@ -6645,9 +6639,14 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work)
                return;
        }
 
-       qla2x00_wait_for_sess_deletion(base_vha);
+       /*
+        * if UNLOADING flag is already set, then continue unload,
+        * where it was set first.
+        */
+       if (test_and_set_bit(UNLOADING, &base_vha->dpc_flags))
+               return;
 
-       set_bit(UNLOADING, &base_vha->dpc_flags);
+       qla2x00_wait_for_sess_deletion(base_vha);
 
        qla2x00_delete_all_vps(ha, base_vha);
 
index 47835c4..06c260f 100644 (file)
@@ -2284,6 +2284,7 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
                switch (oldstate) {
                case SDEV_RUNNING:
                case SDEV_CREATED_BLOCK:
+               case SDEV_QUIESCE:
                case SDEV_OFFLINE:
                        break;
                default:
index 4e6af59..20472aa 100644 (file)
@@ -685,8 +685,10 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
        hp->flags = input_size; /* structure abuse ... */
        hp->pack_id = old_hdr.pack_id;
        hp->usr_ptr = NULL;
-       if (copy_from_user(cmnd, buf, cmd_size))
+       if (copy_from_user(cmnd, buf, cmd_size)) {
+               sg_remove_request(sfp, srp);
                return -EFAULT;
+       }
        /*
         * SG_DXFER_TO_FROM_DEV is functionally equivalent to SG_DXFER_FROM_DEV,
         * but is is possible that the app intended SG_DXFER_TO_DEV, because there
@@ -793,8 +795,10 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp,
                        "sg_common_write:  scsi opcode=0x%02x, cmd_size=%d\n",
                        (int) cmnd[0], (int) hp->cmd_len));
 
-       if (hp->dxfer_len >= SZ_256M)
+       if (hp->dxfer_len >= SZ_256M) {
+               sg_remove_request(sfp, srp);
                return -EINVAL;
+       }
 
        k = sg_start_req(srp, cmnd);
        if (k) {
index cd4f641..bcdcd3e 100644 (file)
@@ -478,12 +478,18 @@ int dpaa2_io_service_enqueue_multiple_desc_fq(struct dpaa2_io *d,
                                const struct dpaa2_fd *fd,
                                int nb)
 {
-       int i;
-       struct qbman_eq_desc ed[32];
+       struct qbman_eq_desc *ed;
+       int i, ret;
+
+       ed = kcalloc(sizeof(struct qbman_eq_desc), 32, GFP_KERNEL);
+       if (!ed)
+               return -ENOMEM;
 
        d = service_select(d);
-       if (!d)
-               return -ENODEV;
+       if (!d) {
+               ret = -ENODEV;
+               goto out;
+       }
 
        for (i = 0; i < nb; i++) {
                qbman_eq_desc_clear(&ed[i]);
@@ -491,7 +497,10 @@ int dpaa2_io_service_enqueue_multiple_desc_fq(struct dpaa2_io *d,
                qbman_eq_desc_set_fq(&ed[i], fqid[i]);
        }
 
-       return qbman_swp_enqueue_multiple_desc(d->swp, &ed[0], fd, nb);
+       ret = qbman_swp_enqueue_multiple_desc(d->swp, &ed[0], fd, nb);
+out:
+       kfree(ed);
+       return ret;
 }
 EXPORT_SYMBOL(dpaa2_io_service_enqueue_multiple_desc_fq);
 
index d1f49ca..804b8ba 100644 (file)
@@ -753,7 +753,7 @@ int qbman_swp_enqueue_multiple_mem_back(struct qbman_swp *s,
        if (!s->eqcr.available) {
                eqcr_ci = s->eqcr.ci;
                p = s->addr_cena + QBMAN_CENA_SWP_EQCR_CI_MEMBACK;
-               s->eqcr.ci = __raw_readl(p) & full_mask;
+               s->eqcr.ci = *p & full_mask;
                s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size,
                                        eqcr_ci, s->eqcr.ci);
                if (!s->eqcr.available) {
@@ -823,7 +823,6 @@ int qbman_swp_enqueue_multiple_desc_direct(struct qbman_swp *s,
        const uint32_t *cl;
        uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask;
        int i, num_enqueued = 0;
-       uint64_t addr_cena;
 
        half_mask = (s->eqcr.pi_ci_mask>>1);
        full_mask = s->eqcr.pi_ci_mask;
@@ -867,7 +866,6 @@ int qbman_swp_enqueue_multiple_desc_direct(struct qbman_swp *s,
 
        /* Flush all the cacheline without load/store in between */
        eqcr_pi = s->eqcr.pi;
-       addr_cena = (uint64_t)s->addr_cena;
        for (i = 0; i < num_enqueued; i++)
                eqcr_pi++;
        s->eqcr.pi = eqcr_pi & full_mask;
@@ -901,7 +899,7 @@ int qbman_swp_enqueue_multiple_desc_mem_back(struct qbman_swp *s,
        if (!s->eqcr.available) {
                eqcr_ci = s->eqcr.ci;
                p = s->addr_cena + QBMAN_CENA_SWP_EQCR_CI_MEMBACK;
-               s->eqcr.ci = __raw_readl(p) & full_mask;
+               s->eqcr.ci = *p & full_mask;
                s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size,
                                        eqcr_ci, s->eqcr.ci);
                if (!s->eqcr.available)
index 67aa94b..d515d2c 100644 (file)
@@ -21,6 +21,7 @@ config SOC_IMX8M
        bool "i.MX8M SoC family support"
        depends on ARCH_MXC || COMPILE_TEST
        default ARCH_MXC && ARM64
+       select SOC_BUS
        help
          If you say yes here you get support for the NXP i.MX8M family
          support, it will provide the SoC info like SoC family,
index 223f1f9..646512d 100644 (file)
@@ -19,7 +19,7 @@ config XILINX_VCU
 
 config ZYNQMP_POWER
        bool "Enable Xilinx Zynq MPSoC Power Management driver"
-       depends on PM && ARCH_ZYNQMP
+       depends on PM && ZYNQMP_FIRMWARE
        default y
        select MAILBOX
        select ZYNQMP_IPI_MBOX
@@ -35,7 +35,7 @@ config ZYNQMP_POWER
 config ZYNQMP_PM_DOMAINS
        bool "Enable Zynq MPSoC generic PM domains"
        default y
-       depends on PM && ARCH_ZYNQMP && ZYNQMP_FIRMWARE
+       depends on PM && ZYNQMP_FIRMWARE
        select PM_GENERIC_DOMAINS
        help
          Say yes to enable device power management through PM domains
index 08d1bbb..e84b4fb 100644 (file)
@@ -2725,8 +2725,10 @@ static int comedi_open(struct inode *inode, struct file *file)
        }
 
        cfp = kzalloc(sizeof(*cfp), GFP_KERNEL);
-       if (!cfp)
+       if (!cfp) {
+               comedi_dev_put(dev);
                return -ENOMEM;
+       }
 
        cfp->dev = dev;
 
index 83026ba..78a7c1b 100644 (file)
@@ -92,6 +92,7 @@ static int dt2815_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
        int ret;
 
        for (i = 0; i < insn->n; i++) {
+               /* FIXME: lo bit 0 chooses voltage output or current output */
                lo = ((data[i] & 0x0f) << 4) | (chan << 1) | 0x01;
                hi = (data[i] & 0xff0) >> 4;
 
@@ -105,6 +106,8 @@ static int dt2815_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
                if (ret)
                        return ret;
 
+               outb(hi, dev->iobase + DT2815_DATA);
+
                devpriv->ao_readback[chan] = data[i];
        }
        return i;
index f6fc07f..b48dcbf 100644 (file)
@@ -79,7 +79,7 @@ The DPSW can have ports connected to DPNIs or to PHYs via DPMACs.
 
 For a more detailed description of the Ethernet switch device driver model
 see:
-       Documentation/networking/switchdev.txt
+       Documentation/networking/switchdev.rst
 
 Creating an Ethernet Switch
 ===========================
index 46199c8..f12f81c 100644 (file)
@@ -570,13 +570,6 @@ static const struct pci_device_id apex_pci_ids[] = {
        { PCI_DEVICE(APEX_PCI_VENDOR_ID, APEX_PCI_DEVICE_ID) }, { 0 }
 };
 
-static void apex_pci_fixup_class(struct pci_dev *pdev)
-{
-       pdev->class = (PCI_CLASS_SYSTEM_OTHER << 8) | pdev->class;
-}
-DECLARE_PCI_FIXUP_CLASS_HEADER(APEX_PCI_VENDOR_ID, APEX_PCI_DEVICE_ID,
-                              PCI_CLASS_NOT_DEFINED, 8, apex_pci_fixup_class);
-
 static int apex_pci_probe(struct pci_dev *pci_dev,
                          const struct pci_device_id *id)
 {
index a2d67c2..5f0e089 100644 (file)
@@ -228,8 +228,7 @@ int gasket_sysfs_create_entries(struct device *device,
        }
 
        mutex_lock(&mapping->mutex);
-       for (i = 0; strcmp(attrs[i].attr.attr.name, GASKET_ARRAY_END_MARKER);
-               i++) {
+       for (i = 0; attrs[i].attr.attr.name != NULL; i++) {
                if (mapping->attribute_count == GASKET_SYSFS_MAX_NODES) {
                        dev_err(device,
                                "Maximum number of sysfs nodes reached for device\n");
index 1d0eed6..ab5aa35 100644 (file)
  */
 #define GASKET_SYSFS_MAX_NODES 196
 
-/* End markers for sysfs struct arrays. */
-#define GASKET_ARRAY_END_TOKEN GASKET_RESERVED_ARRAY_END
-#define GASKET_ARRAY_END_MARKER __stringify(GASKET_ARRAY_END_TOKEN)
-
 /*
  * Terminator struct for a gasket_sysfs_attr array. Must be at the end of
  * all gasket_sysfs_attribute arrays.
index 41b73f9..ac3b188 100644 (file)
@@ -83,9 +83,6 @@ static int vnt_set_keymode(struct ieee80211_hw *hw, u8 *mac_addr,
        case  VNT_KEY_PAIRWISE:
                key_mode |= mode;
                key_inx = 4;
-               /* Don't save entry for pairwise key for station mode */
-               if (priv->op_mode == NL80211_IFTYPE_STATION)
-                       clear_bit(entry, &priv->key_entry_inuse);
                break;
        default:
                return -EINVAL;
@@ -109,7 +106,6 @@ static int vnt_set_keymode(struct ieee80211_hw *hw, u8 *mac_addr,
 int vnt_set_keys(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
                 struct ieee80211_vif *vif, struct ieee80211_key_conf *key)
 {
-       struct ieee80211_bss_conf *conf = &vif->bss_conf;
        struct vnt_private *priv = hw->priv;
        u8 *mac_addr = NULL;
        u8 key_dec_mode = 0;
@@ -154,16 +150,12 @@ int vnt_set_keys(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
                return -EOPNOTSUPP;
        }
 
-       if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+       if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
                vnt_set_keymode(hw, mac_addr, key, VNT_KEY_PAIRWISE,
                                key_dec_mode, true);
-       } else {
-               vnt_set_keymode(hw, mac_addr, key, VNT_KEY_DEFAULTKEY,
+       else
+               vnt_set_keymode(hw, mac_addr, key, VNT_KEY_GROUP_ADDRESS,
                                key_dec_mode, true);
 
-               vnt_set_keymode(hw, (u8 *)conf->bssid, key,
-                               VNT_KEY_GROUP_ADDRESS, key_dec_mode, true);
-       }
-
        return 0;
 }
index 8e7269c..5f78cad 100644 (file)
@@ -625,8 +625,6 @@ static int vnt_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 
        priv->op_mode = vif->type;
 
-       vnt_set_bss_mode(priv);
-
        /* LED blink on TX */
        vnt_mac_set_led(priv, LEDSTS_STS, LEDSTS_INTER);
 
@@ -713,7 +711,6 @@ static void vnt_bss_info_changed(struct ieee80211_hw *hw,
                priv->basic_rates = conf->basic_rates;
 
                vnt_update_top_rates(priv);
-               vnt_set_bss_mode(priv);
 
                dev_dbg(&priv->usb->dev, "basic rates %x\n", conf->basic_rates);
        }
@@ -742,11 +739,14 @@ static void vnt_bss_info_changed(struct ieee80211_hw *hw,
                        priv->short_slot_time = false;
 
                vnt_set_short_slot_time(priv);
-               vnt_update_ifs(priv);
                vnt_set_vga_gain_offset(priv, priv->bb_vga[0]);
                vnt_update_pre_ed_threshold(priv, false);
        }
 
+       if (changed & (BSS_CHANGED_BASIC_RATES | BSS_CHANGED_ERP_PREAMBLE |
+                      BSS_CHANGED_ERP_SLOT))
+               vnt_set_bss_mode(priv);
+
        if (changed & BSS_CHANGED_TXPOWER)
                vnt_rf_setpower(priv, priv->current_rate,
                                conf->chandef.chan->hw_value);
@@ -770,12 +770,15 @@ static void vnt_bss_info_changed(struct ieee80211_hw *hw,
                        vnt_mac_reg_bits_on(priv, MAC_REG_TFTCTL,
                                            TFTCTL_TSFCNTREN);
 
-                       vnt_adjust_tsf(priv, conf->beacon_rate->hw_value,
-                                      conf->sync_tsf, priv->current_tsf);
-
                        vnt_mac_set_beacon_interval(priv, conf->beacon_int);
 
                        vnt_reset_next_tbtt(priv, conf->beacon_int);
+
+                       vnt_adjust_tsf(priv, conf->beacon_rate->hw_value,
+                                      conf->sync_tsf, priv->current_tsf);
+
+                       vnt_update_next_tbtt(priv,
+                                            conf->sync_tsf, conf->beacon_int);
                } else {
                        vnt_clear_current_tsf(priv);
 
@@ -809,15 +812,11 @@ static void vnt_configure(struct ieee80211_hw *hw,
 {
        struct vnt_private *priv = hw->priv;
        u8 rx_mode = 0;
-       int rc;
 
        *total_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC;
 
-       rc = vnt_control_in(priv, MESSAGE_TYPE_READ, MAC_REG_RCR,
-                           MESSAGE_REQUEST_MACREG, sizeof(u8), &rx_mode);
-
-       if (!rc)
-               rx_mode = RCR_MULTICAST | RCR_BROADCAST;
+       vnt_control_in(priv, MESSAGE_TYPE_READ, MAC_REG_RCR,
+                      MESSAGE_REQUEST_MACREG, sizeof(u8), &rx_mode);
 
        dev_dbg(&priv->usb->dev, "rx mode in = %x\n", rx_mode);
 
@@ -856,8 +855,12 @@ static int vnt_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
        case SET_KEY:
                return vnt_set_keys(hw, sta, vif, key);
        case DISABLE_KEY:
-               if (test_bit(key->hw_key_idx, &priv->key_entry_inuse))
+               if (test_bit(key->hw_key_idx, &priv->key_entry_inuse)) {
                        clear_bit(key->hw_key_idx, &priv->key_entry_inuse);
+
+                       vnt_mac_disable_keyentry(priv, key->hw_key_idx);
+               }
+
        default:
                break;
        }
index eae211e..91b62c3 100644 (file)
@@ -207,7 +207,8 @@ static void vnt_int_process_data(struct vnt_private *priv)
                                priv->wake_up_count =
                                        priv->hw->conf.listen_interval;
 
-                       --priv->wake_up_count;
+                       if (priv->wake_up_count)
+                               --priv->wake_up_count;
 
                        /* Turn on wake up to listen next beacon */
                        if (priv->wake_up_count == 1)
index 6b4b354..1e031d8 100644 (file)
@@ -63,7 +63,7 @@ static int fc_get_pr_transport_id(
         * encoded TransportID.
         */
        ptr = &se_nacl->initiatorname[0];
-       for (i = 0; i < 24; ) {
+       for (i = 0; i < 23; ) {
                if (!strncmp(&ptr[i], ":", 1)) {
                        i++;
                        continue;
@@ -341,7 +341,8 @@ static char *iscsi_parse_pr_out_transport_id(
                        *p = tolower(*p);
                        p++;
                }
-       }
+       } else
+               *port_nexus_ptr = NULL;
 
        return &buf[4];
 }
index 51ffd5c..1c181d3 100644 (file)
@@ -432,7 +432,7 @@ iblock_execute_zero_out(struct block_device *bdev, struct se_cmd *cmd)
                                target_to_linux_sector(dev, cmd->t_task_lba),
                                target_to_linux_sector(dev,
                                        sbc_get_write_same_sectors(cmd)),
-                               GFP_KERNEL, false);
+                               GFP_KERNEL, BLKDEV_ZERO_NOUNMAP);
        if (ret)
                return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
 
index 0b9dfa6..f769bb1 100644 (file)
@@ -2073,6 +2073,7 @@ static void tcmu_reset_ring(struct tcmu_dev *udev, u8 err_level)
        mb->cmd_tail = 0;
        mb->cmd_head = 0;
        tcmu_flush_dcache_range(mb, sizeof(*mb));
+       clear_bit(TCMU_DEV_BIT_BROKEN, &udev->flags);
 
        del_timer(&udev->cmd_timer);
 
index 31b7e1b..d1b27b0 100644 (file)
@@ -88,7 +88,7 @@ config HVC_DCC
 
 config HVC_RISCV_SBI
        bool "RISC-V SBI console support"
-       depends on RISCV_SBI
+       depends on RISCV_SBI_V01
        select HVC_DRIVER
        help
          This enables support for console output via RISC-V SBI calls, which
index 27284a2..436cc51 100644 (file)
@@ -302,10 +302,6 @@ int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops)
        vtermnos[index] = vtermno;
        cons_ops[index] = ops;
 
-       /* reserve all indices up to and including this index */
-       if (last_hvc < index)
-               last_hvc = index;
-
        /* check if we need to re-register the kernel console */
        hvc_check_console(index);
 
@@ -960,13 +956,22 @@ struct hvc_struct *hvc_alloc(uint32_t vtermno, int data,
                    cons_ops[i] == hp->ops)
                        break;
 
-       /* no matching slot, just use a counter */
-       if (i >= MAX_NR_HVC_CONSOLES)
-               i = ++last_hvc;
+       if (i >= MAX_NR_HVC_CONSOLES) {
+
+               /* find 'empty' slot for console */
+               for (i = 0; i < MAX_NR_HVC_CONSOLES && vtermnos[i] != -1; i++) {
+               }
+
+               /* no matching slot, just use a counter */
+               if (i == MAX_NR_HVC_CONSOLES)
+                       i = ++last_hvc + MAX_NR_HVC_CONSOLES;
+       }
 
        hp->index = i;
-       cons_ops[i] = ops;
-       vtermnos[i] = vtermno;
+       if (i < MAX_NR_HVC_CONSOLES) {
+               cons_ops[i] = ops;
+               vtermnos[i] = vtermno;
+       }
 
        list_add_tail(&(hp->next), &hvc_structs);
        mutex_unlock(&hvc_structs_mutex);
index fbaa4ec..e2138e7 100644 (file)
@@ -632,18 +632,21 @@ init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev)
        tty_port_init(&info->port);
        info->port.ops = &rocket_port_ops;
        info->flags &= ~ROCKET_MODE_MASK;
-       switch (pc104[board][line]) {
-       case 422:
-               info->flags |= ROCKET_MODE_RS422;
-               break;
-       case 485:
-               info->flags |= ROCKET_MODE_RS485;
-               break;
-       case 232:
-       default:
+       if (board < ARRAY_SIZE(pc104) && line < ARRAY_SIZE(pc104_1))
+               switch (pc104[board][line]) {
+               case 422:
+                       info->flags |= ROCKET_MODE_RS422;
+                       break;
+               case 485:
+                       info->flags |= ROCKET_MODE_RS485;
+                       break;
+               case 232:
+               default:
+                       info->flags |= ROCKET_MODE_RS232;
+                       break;
+               }
+       else
                info->flags |= ROCKET_MODE_RS232;
-               break;
-       }
 
        info->intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR;
        if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) {
index 0aea76c..adf9e80 100644 (file)
@@ -86,7 +86,7 @@ config SERIAL_EARLYCON_ARM_SEMIHOST
 
 config SERIAL_EARLYCON_RISCV_SBI
        bool "Early console using RISC-V SBI"
-       depends on RISCV_SBI
+       depends on RISCV_SBI_V01
        select SERIAL_CORE
        select SERIAL_CORE_CONSOLE
        select SERIAL_EARLYCON
index 5674da2..ed0aa5c 100644 (file)
@@ -843,8 +843,10 @@ static int bcm_uart_probe(struct platform_device *pdev)
        if (IS_ERR(clk) && pdev->dev.of_node)
                clk = of_clk_get(pdev->dev.of_node, 0);
 
-       if (IS_ERR(clk))
+       if (IS_ERR(clk)) {
+               clk_put(clk);
                return -ENODEV;
+       }
 
        port->iotype = UPIO_MEM;
        port->irq = res_irq->start;
index 42c8cc9..c149f8c 100644 (file)
@@ -680,6 +680,12 @@ static int owl_uart_probe(struct platform_device *pdev)
                return PTR_ERR(owl_port->clk);
        }
 
+       ret = clk_prepare_enable(owl_port->clk);
+       if (ret) {
+               dev_err(&pdev->dev, "could not enable clk\n");
+               return ret;
+       }
+
        owl_port->port.dev = &pdev->dev;
        owl_port->port.line = pdev->id;
        owl_port->port.type = PORT_OWL;
@@ -712,6 +718,7 @@ static int owl_uart_remove(struct platform_device *pdev)
 
        uart_remove_one_port(&owl_uart_driver, &owl_port->port);
        owl_uart_ports[pdev->id] = NULL;
+       clk_disable_unprepare(owl_port->clk);
 
        return 0;
 }
index c073aa7..e1179e7 100644 (file)
@@ -870,9 +870,16 @@ static void sci_receive_chars(struct uart_port *port)
                                tty_insert_flip_char(tport, c, TTY_NORMAL);
                } else {
                        for (i = 0; i < count; i++) {
-                               char c = serial_port_in(port, SCxRDR);
-
-                               status = serial_port_in(port, SCxSR);
+                               char c;
+
+                               if (port->type == PORT_SCIF ||
+                                   port->type == PORT_HSCIF) {
+                                       status = serial_port_in(port, SCxSR);
+                                       c = serial_port_in(port, SCxRDR);
+                               } else {
+                                       c = serial_port_in(port, SCxRDR);
+                                       status = serial_port_in(port, SCxSR);
+                               }
                                if (uart_handle_sysrq_char(port, c)) {
                                        count--; i--;
                                        continue;
index eafada8..e35073e 100644 (file)
@@ -567,6 +567,9 @@ static int hv_probe(struct platform_device *op)
        sunserial_console_match(&sunhv_console, op->dev.of_node,
                                &sunhv_reg, port->line, false);
 
+       /* We need to initialize lock even for non-registered console */
+       spin_lock_init(&port->lock);
+
        err = uart_add_one_port(&sunhv_reg, port);
        if (err)
                goto out_unregister_driver;
index 6b26f76..ac137b6 100644 (file)
 
 #define CDNS_UART_TTY_NAME     "ttyPS"
 #define CDNS_UART_NAME         "xuartps"
+#define CDNS_UART_MAJOR                0       /* use dynamic node allocation */
+#define CDNS_UART_MINOR                0       /* works best with devtmpfs */
+#define CDNS_UART_NR_PORTS     16
 #define CDNS_UART_FIFO_SIZE    64      /* FIFO size */
 #define CDNS_UART_REGISTER_SPACE       0x1000
 #define TX_TIMEOUT             500000
 
 /* Rx Trigger level */
 static int rx_trigger_level = 56;
-static int uartps_major;
 module_param(rx_trigger_level, uint, 0444);
 MODULE_PARM_DESC(rx_trigger_level, "Rx trigger level, 1-63 bytes");
 
@@ -188,7 +190,6 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
  * @pclk:              APB clock
  * @cdns_uart_driver:  Pointer to UART driver
  * @baud:              Current baud rate
- * @id:                        Port ID
  * @clk_rate_change_nb:        Notifier block for clock changes
  * @quirks:            Flags for RXBS support.
  */
@@ -198,7 +199,6 @@ struct cdns_uart {
        struct clk              *pclk;
        struct uart_driver      *cdns_uart_driver;
        unsigned int            baud;
-       int                     id;
        struct notifier_block   clk_rate_change_nb;
        u32                     quirks;
        bool cts_override;
@@ -1133,6 +1133,8 @@ static const struct uart_ops cdns_uart_ops = {
 #endif
 };
 
+static struct uart_driver cdns_uart_uart_driver;
+
 #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
 /**
  * cdns_uart_console_putchar - write the character to the FIFO buffer
@@ -1272,6 +1274,16 @@ static int cdns_uart_console_setup(struct console *co, char *options)
 
        return uart_set_options(port, co, baud, parity, bits, flow);
 }
+
+static struct console cdns_uart_console = {
+       .name   = CDNS_UART_TTY_NAME,
+       .write  = cdns_uart_console_write,
+       .device = uart_console_device,
+       .setup  = cdns_uart_console_setup,
+       .flags  = CON_PRINTBUFFER,
+       .index  = -1, /* Specified on the cmdline (e.g. console=ttyPS ) */
+       .data   = &cdns_uart_uart_driver,
+};
 #endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */
 
 #ifdef CONFIG_PM_SLEEP
@@ -1403,89 +1415,8 @@ static const struct of_device_id cdns_uart_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, cdns_uart_of_match);
 
-/*
- * Maximum number of instances without alias IDs but if there is alias
- * which target "< MAX_UART_INSTANCES" range this ID can't be used.
- */
-#define MAX_UART_INSTANCES     32
-
-/* Stores static aliases list */
-static DECLARE_BITMAP(alias_bitmap, MAX_UART_INSTANCES);
-static int alias_bitmap_initialized;
-
-/* Stores actual bitmap of allocated IDs with alias IDs together */
-static DECLARE_BITMAP(bitmap, MAX_UART_INSTANCES);
-/* Protect bitmap operations to have unique IDs */
-static DEFINE_MUTEX(bitmap_lock);
-
-static int cdns_get_id(struct platform_device *pdev)
-{
-       int id, ret;
-
-       mutex_lock(&bitmap_lock);
-
-       /* Alias list is stable that's why get alias bitmap only once */
-       if (!alias_bitmap_initialized) {
-               ret = of_alias_get_alias_list(cdns_uart_of_match, "serial",
-                                             alias_bitmap, MAX_UART_INSTANCES);
-               if (ret && ret != -EOVERFLOW) {
-                       mutex_unlock(&bitmap_lock);
-                       return ret;
-               }
-
-               alias_bitmap_initialized++;
-       }
-
-       /* Make sure that alias ID is not taken by instance without alias */
-       bitmap_or(bitmap, bitmap, alias_bitmap, MAX_UART_INSTANCES);
-
-       dev_dbg(&pdev->dev, "Alias bitmap: %*pb\n",
-               MAX_UART_INSTANCES, bitmap);
-
-       /* Look for a serialN alias */
-       id = of_alias_get_id(pdev->dev.of_node, "serial");
-       if (id < 0) {
-               dev_warn(&pdev->dev,
-                        "No serial alias passed. Using the first free id\n");
-
-               /*
-                * Start with id 0 and check if there is no serial0 alias
-                * which points to device which is compatible with this driver.
-                * If alias exists then try next free position.
-                */
-               id = 0;
-
-               for (;;) {
-                       dev_info(&pdev->dev, "Checking id %d\n", id);
-                       id = find_next_zero_bit(bitmap, MAX_UART_INSTANCES, id);
-
-                       /* No free empty instance */
-                       if (id == MAX_UART_INSTANCES) {
-                               dev_err(&pdev->dev, "No free ID\n");
-                               mutex_unlock(&bitmap_lock);
-                               return -EINVAL;
-                       }
-
-                       dev_dbg(&pdev->dev, "The empty id is %d\n", id);
-                       /* Check if ID is empty */
-                       if (!test_and_set_bit(id, bitmap)) {
-                               /* Break the loop if bit is taken */
-                               dev_dbg(&pdev->dev,
-                                       "Selected ID %d allocation passed\n",
-                                       id);
-                               break;
-                       }
-                       dev_dbg(&pdev->dev,
-                               "Selected ID %d allocation failed\n", id);
-                       /* if taking bit fails then try next one */
-                       id++;
-               }
-       }
-
-       mutex_unlock(&bitmap_lock);
-
-       return id;
-}
+/* Temporary variable for storing number of instances */
+static int instances;
 
 /**
  * cdns_uart_probe - Platform driver probe
@@ -1495,16 +1426,11 @@ static int cdns_get_id(struct platform_device *pdev)
  */
 static int cdns_uart_probe(struct platform_device *pdev)
 {
-       int rc, irq;
+       int rc, id, irq;
        struct uart_port *port;
        struct resource *res;
        struct cdns_uart *cdns_uart_data;
        const struct of_device_id *match;
-       struct uart_driver *cdns_uart_uart_driver;
-       char *driver_name;
-#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
-       struct console *cdns_uart_console;
-#endif
 
        cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data),
                        GFP_KERNEL);
@@ -1514,64 +1440,35 @@ static int cdns_uart_probe(struct platform_device *pdev)
        if (!port)
                return -ENOMEM;
 
-       cdns_uart_uart_driver = devm_kzalloc(&pdev->dev,
-                                            sizeof(*cdns_uart_uart_driver),
-                                            GFP_KERNEL);
-       if (!cdns_uart_uart_driver)
-               return -ENOMEM;
-
-       cdns_uart_data->id = cdns_get_id(pdev);
-       if (cdns_uart_data->id < 0)
-               return cdns_uart_data->id;
+       /* Look for a serialN alias */
+       id = of_alias_get_id(pdev->dev.of_node, "serial");
+       if (id < 0)
+               id = 0;
 
-       /* There is a need to use unique driver name */
-       driver_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%d",
-                                    CDNS_UART_NAME, cdns_uart_data->id);
-       if (!driver_name) {
-               rc = -ENOMEM;
-               goto err_out_id;
+       if (id >= CDNS_UART_NR_PORTS) {
+               dev_err(&pdev->dev, "Cannot get uart_port structure\n");
+               return -ENODEV;
        }
 
-       cdns_uart_uart_driver->owner = THIS_MODULE;
-       cdns_uart_uart_driver->driver_name = driver_name;
-       cdns_uart_uart_driver->dev_name = CDNS_UART_TTY_NAME;
-       cdns_uart_uart_driver->major = uartps_major;
-       cdns_uart_uart_driver->minor = cdns_uart_data->id;
-       cdns_uart_uart_driver->nr = 1;
-
+       if (!cdns_uart_uart_driver.state) {
+               cdns_uart_uart_driver.owner = THIS_MODULE;
+               cdns_uart_uart_driver.driver_name = CDNS_UART_NAME;
+               cdns_uart_uart_driver.dev_name = CDNS_UART_TTY_NAME;
+               cdns_uart_uart_driver.major = CDNS_UART_MAJOR;
+               cdns_uart_uart_driver.minor = CDNS_UART_MINOR;
+               cdns_uart_uart_driver.nr = CDNS_UART_NR_PORTS;
 #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
-       cdns_uart_console = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_console),
-                                        GFP_KERNEL);
-       if (!cdns_uart_console) {
-               rc = -ENOMEM;
-               goto err_out_id;
-       }
-
-       strncpy(cdns_uart_console->name, CDNS_UART_TTY_NAME,
-               sizeof(cdns_uart_console->name));
-       cdns_uart_console->index = cdns_uart_data->id;
-       cdns_uart_console->write = cdns_uart_console_write;
-       cdns_uart_console->device = uart_console_device;
-       cdns_uart_console->setup = cdns_uart_console_setup;
-       cdns_uart_console->flags = CON_PRINTBUFFER;
-       cdns_uart_console->data = cdns_uart_uart_driver;
-       cdns_uart_uart_driver->cons = cdns_uart_console;
+               cdns_uart_uart_driver.cons = &cdns_uart_console;
 #endif
 
-       rc = uart_register_driver(cdns_uart_uart_driver);
-       if (rc < 0) {
-               dev_err(&pdev->dev, "Failed to register driver\n");
-               goto err_out_id;
+               rc = uart_register_driver(&cdns_uart_uart_driver);
+               if (rc < 0) {
+                       dev_err(&pdev->dev, "Failed to register driver\n");
+                       return rc;
+               }
        }
 
-       cdns_uart_data->cdns_uart_driver = cdns_uart_uart_driver;
-
-       /*
-        * Setting up proper name_base needs to be done after uart
-        * registration because tty_driver structure is not filled.
-        * name_base is 0 by default.
-        */
-       cdns_uart_uart_driver->tty_driver->name_base = cdns_uart_data->id;
+       cdns_uart_data->cdns_uart_driver = &cdns_uart_uart_driver;
 
        match = of_match_node(cdns_uart_of_match, pdev->dev.of_node);
        if (match && match->data) {
@@ -1649,6 +1546,7 @@ static int cdns_uart_probe(struct platform_device *pdev)
        port->ops       = &cdns_uart_ops;
        port->fifosize  = CDNS_UART_FIFO_SIZE;
        port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_XILINX_PS_UART_CONSOLE);
+       port->line      = id;
 
        /*
         * Register the port.
@@ -1680,7 +1578,7 @@ static int cdns_uart_probe(struct platform_device *pdev)
                console_port = port;
 #endif
 
-       rc = uart_add_one_port(cdns_uart_uart_driver, port);
+       rc = uart_add_one_port(&cdns_uart_uart_driver, port);
        if (rc) {
                dev_err(&pdev->dev,
                        "uart_add_one_port() failed; err=%i\n", rc);
@@ -1690,13 +1588,15 @@ static int cdns_uart_probe(struct platform_device *pdev)
 #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
        /* This is not port which is used for console that's why clean it up */
        if (console_port == port &&
-           !(cdns_uart_uart_driver->cons->flags & CON_ENABLED))
+           !(cdns_uart_uart_driver.cons->flags & CON_ENABLED))
                console_port = NULL;
 #endif
 
-       uartps_major = cdns_uart_uart_driver->tty_driver->major;
        cdns_uart_data->cts_override = of_property_read_bool(pdev->dev.of_node,
                                                             "cts-override");
+
+       instances++;
+
        return 0;
 
 err_out_pm_disable:
@@ -1712,12 +1612,8 @@ err_out_clk_disable:
 err_out_clk_dis_pclk:
        clk_disable_unprepare(cdns_uart_data->pclk);
 err_out_unregister_driver:
-       uart_unregister_driver(cdns_uart_data->cdns_uart_driver);
-err_out_id:
-       mutex_lock(&bitmap_lock);
-       if (cdns_uart_data->id < MAX_UART_INSTANCES)
-               clear_bit(cdns_uart_data->id, bitmap);
-       mutex_unlock(&bitmap_lock);
+       if (!instances)
+               uart_unregister_driver(cdns_uart_data->cdns_uart_driver);
        return rc;
 }
 
@@ -1740,10 +1636,6 @@ static int cdns_uart_remove(struct platform_device *pdev)
 #endif
        rc = uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port);
        port->mapbase = 0;
-       mutex_lock(&bitmap_lock);
-       if (cdns_uart_data->id < MAX_UART_INSTANCES)
-               clear_bit(cdns_uart_data->id, bitmap);
-       mutex_unlock(&bitmap_lock);
        clk_disable_unprepare(cdns_uart_data->uartclk);
        clk_disable_unprepare(cdns_uart_data->pclk);
        pm_runtime_disable(&pdev->dev);
@@ -1756,13 +1648,8 @@ static int cdns_uart_remove(struct platform_device *pdev)
                console_port = NULL;
 #endif
 
-       /* If this is last instance major number should be initialized */
-       mutex_lock(&bitmap_lock);
-       if (bitmap_empty(bitmap, MAX_UART_INSTANCES))
-               uartps_major = 0;
-       mutex_unlock(&bitmap_lock);
-
-       uart_unregister_driver(cdns_uart_data->cdns_uart_driver);
+       if (!--instances)
+               uart_unregister_driver(cdns_uart_data->cdns_uart_driver);
        return rc;
 }
 
index 5e0d081..0dc3878 100644 (file)
@@ -74,6 +74,7 @@ int sysrq_mask(void)
                return 1;
        return sysrq_enabled;
 }
+EXPORT_SYMBOL_GPL(sysrq_mask);
 
 /*
  * A value of 1 means 'all', other nonzero values are an op mask:
@@ -1058,6 +1059,7 @@ int sysrq_toggle_support(int enable_mask)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(sysrq_toggle_support);
 
 static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p,
                                 struct sysrq_key_op *remove_op_p)
index 309a391..e5ffed7 100644 (file)
@@ -81,6 +81,7 @@
 #include <linux/errno.h>
 #include <linux/kd.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
 #include <linux/major.h>
 #include <linux/mm.h>
 #include <linux/console.h>
@@ -350,7 +351,7 @@ static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows)
        /* allocate everything in one go */
        memsize = cols * rows * sizeof(char32_t);
        memsize += rows * sizeof(char32_t *);
-       p = kmalloc(memsize, GFP_KERNEL);
+       p = vmalloc(memsize);
        if (!p)
                return NULL;
 
@@ -366,7 +367,7 @@ static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows)
 
 static void vc_uniscr_set(struct vc_data *vc, struct uni_screen *new_uniscr)
 {
-       kfree(vc->vc_uni_screen);
+       vfree(vc->vc_uni_screen);
        vc->vc_uni_screen = new_uniscr;
 }
 
@@ -1206,7 +1207,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
        if (new_cols == vc->vc_cols && new_rows == vc->vc_rows)
                return 0;
 
-       if (new_screen_size > (4 << 20))
+       if (new_screen_size > KMALLOC_MAX_SIZE)
                return -EINVAL;
        newscreen = kzalloc(new_screen_size, GFP_USER);
        if (!newscreen)
index 84d6f7d..ded8d93 100644 (file)
@@ -412,9 +412,12 @@ static void acm_ctrl_irq(struct urb *urb)
 
 exit:
        retval = usb_submit_urb(urb, GFP_ATOMIC);
-       if (retval && retval != -EPERM)
+       if (retval && retval != -EPERM && retval != -ENODEV)
                dev_err(&acm->control->dev,
                        "%s - usb_submit_urb failed: %d\n", __func__, retval);
+       else
+               dev_vdbg(&acm->control->dev,
+                       "control resubmission terminated %d\n", retval);
 }
 
 static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
@@ -430,6 +433,8 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
                        dev_err(&acm->data->dev,
                                "urb %d failed submission with %d\n",
                                index, res);
+               } else {
+                       dev_vdbg(&acm->data->dev, "intended failure %d\n", res);
                }
                set_bit(index, &acm->read_urbs_free);
                return res;
@@ -471,6 +476,7 @@ static void acm_read_bulk_callback(struct urb *urb)
        int status = urb->status;
        bool stopped = false;
        bool stalled = false;
+       bool cooldown = false;
 
        dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n",
                rb->index, urb->actual_length, status);
@@ -497,6 +503,14 @@ static void acm_read_bulk_callback(struct urb *urb)
                        __func__, status);
                stopped = true;
                break;
+       case -EOVERFLOW:
+       case -EPROTO:
+               dev_dbg(&acm->data->dev,
+                       "%s - cooling babbling device\n", __func__);
+               usb_mark_last_busy(acm->dev);
+               set_bit(rb->index, &acm->urbs_in_error_delay);
+               cooldown = true;
+               break;
        default:
                dev_dbg(&acm->data->dev,
                        "%s - nonzero urb status received: %d\n",
@@ -518,9 +532,11 @@ static void acm_read_bulk_callback(struct urb *urb)
         */
        smp_mb__after_atomic();
 
-       if (stopped || stalled) {
+       if (stopped || stalled || cooldown) {
                if (stalled)
                        schedule_work(&acm->work);
+               else if (cooldown)
+                       schedule_delayed_work(&acm->dwork, HZ / 2);
                return;
        }
 
@@ -557,14 +573,20 @@ static void acm_softint(struct work_struct *work)
        struct acm *acm = container_of(work, struct acm, work);
 
        if (test_bit(EVENT_RX_STALL, &acm->flags)) {
-               if (!(usb_autopm_get_interface(acm->data))) {
+               smp_mb(); /* against acm_suspend() */
+               if (!acm->susp_count) {
                        for (i = 0; i < acm->rx_buflimit; i++)
                                usb_kill_urb(acm->read_urbs[i]);
                        usb_clear_halt(acm->dev, acm->in);
                        acm_submit_read_urbs(acm, GFP_KERNEL);
-                       usb_autopm_put_interface(acm->data);
+                       clear_bit(EVENT_RX_STALL, &acm->flags);
                }
-               clear_bit(EVENT_RX_STALL, &acm->flags);
+       }
+
+       if (test_and_clear_bit(ACM_ERROR_DELAY, &acm->flags)) {
+               for (i = 0; i < ACM_NR; i++) 
+                       if (test_and_clear_bit(i, &acm->urbs_in_error_delay))
+                                       acm_submit_read_urb(acm, i, GFP_NOIO);
        }
 
        if (test_and_clear_bit(EVENT_TTY_WAKEUP, &acm->flags))
@@ -1333,6 +1355,7 @@ made_compressed_probe:
        acm->readsize = readsize;
        acm->rx_buflimit = num_rx_buf;
        INIT_WORK(&acm->work, acm_softint);
+       INIT_DELAYED_WORK(&acm->dwork, acm_softint);
        init_waitqueue_head(&acm->wioctl);
        spin_lock_init(&acm->write_lock);
        spin_lock_init(&acm->read_lock);
@@ -1542,6 +1565,7 @@ static void acm_disconnect(struct usb_interface *intf)
 
        acm_kill_urbs(acm);
        cancel_work_sync(&acm->work);
+       cancel_delayed_work_sync(&acm->dwork);
 
        tty_unregister_device(acm_tty_driver, acm->minor);
 
@@ -1584,6 +1608,8 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
 
        acm_kill_urbs(acm);
        cancel_work_sync(&acm->work);
+       cancel_delayed_work_sync(&acm->dwork);
+       acm->urbs_in_error_delay = 0;
 
        return 0;
 }
index ca1c026..cd5e9d8 100644 (file)
@@ -109,8 +109,11 @@ struct acm {
 #              define EVENT_TTY_WAKEUP 0
 #              define EVENT_RX_STALL   1
 #              define ACM_THROTTLED    2
+#              define ACM_ERROR_DELAY  3
+       unsigned long urbs_in_error_delay;              /* these need to be restarted after a delay */
        struct usb_cdc_line_coding line;                /* bits, stop, parity */
-       struct work_struct work;                        /* work queue entry for line discipline waking up */
+       struct work_struct work;                        /* work queue entry for various purposes*/
+       struct delayed_work dwork;                      /* for cool downs needed in error recovery */
        unsigned int ctrlin;                            /* input control lines (DCD, DSR, RI, break, overruns) */
        unsigned int ctrlout;                           /* output control lines (DTR, RTS) */
        struct async_icount iocount;                    /* counters for control line changes */
index 54cd8ef..2b6565c 100644 (file)
@@ -1223,6 +1223,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 #ifdef CONFIG_PM
                        udev->reset_resume = 1;
 #endif
+                       /* Don't set the change_bits when the device
+                        * was powered off.
+                        */
+                       if (test_bit(port1, hub->power_bits))
+                               set_bit(port1, hub->change_bits);
 
                } else {
                        /* The power session is gone; tell hub_wq */
@@ -2723,13 +2728,11 @@ static bool use_new_scheme(struct usb_device *udev, int retry,
 {
        int old_scheme_first_port =
                port_dev->quirks & USB_PORT_QUIRK_OLD_SCHEME;
-       int quick_enumeration = (udev->speed == USB_SPEED_HIGH);
 
        if (udev->speed >= USB_SPEED_SUPER)
                return false;
 
-       return USE_NEW_SCHEME(retry, old_scheme_first_port || old_scheme_first
-                             || quick_enumeration);
+       return USE_NEW_SCHEME(retry, old_scheme_first_port || old_scheme_first);
 }
 
 /* Is a USB 3.0 port in the Inactive or Compliance Mode state?
@@ -3088,6 +3091,15 @@ static int check_port_resume_type(struct usb_device *udev,
                if (portchange & USB_PORT_STAT_C_ENABLE)
                        usb_clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_ENABLE);
+
+               /*
+                * Whatever made this reset-resume necessary may have
+                * turned on the port1 bit in hub->change_bits.  But after
+                * a successful reset-resume we want the bit to be clear;
+                * if it was on it would indicate that something happened
+                * following the reset-resume.
+                */
+               clear_bit(port1, hub->change_bits);
        }
 
        return status;
index d5f834f..a48678a 100644 (file)
@@ -589,12 +589,13 @@ void usb_sg_cancel(struct usb_sg_request *io)
        int i, retval;
 
        spin_lock_irqsave(&io->lock, flags);
-       if (io->status) {
+       if (io->status || io->count == 0) {
                spin_unlock_irqrestore(&io->lock, flags);
                return;
        }
        /* shut everything down */
        io->status = -ECONNRESET;
+       io->count++;            /* Keep the request alive until we're done */
        spin_unlock_irqrestore(&io->lock, flags);
 
        for (i = io->entries - 1; i >= 0; --i) {
@@ -608,6 +609,12 @@ void usb_sg_cancel(struct usb_sg_request *io)
                        dev_warn(&io->dev->dev, "%s, unlink --> %d\n",
                                 __func__, retval);
        }
+
+       spin_lock_irqsave(&io->lock, flags);
+       io->count--;
+       if (!io->count)
+               complete(&io->complete);
+       spin_unlock_irqrestore(&io->lock, flags);
 }
 EXPORT_SYMBOL_GPL(usb_sg_cancel);
 
index da30b56..3e8efe7 100644 (file)
@@ -430,6 +430,10 @@ static const struct usb_device_id usb_quirk_list[] = {
        /* Corsair K70 LUX */
        { USB_DEVICE(0x1b1c, 0x1b36), .driver_info = USB_QUIRK_DELAY_INIT },
 
+       /* Corsair K70 RGB RAPDIFIRE */
+       { USB_DEVICE(0x1b1c, 0x1b38), .driver_info = USB_QUIRK_DELAY_INIT |
+         USB_QUIRK_DELAY_CTRL_MSG },
+
        /* MIDI keyboard WORLDE MINI */
        { USB_DEVICE(0x1c75, 0x0204), .driver_info =
                        USB_QUIRK_CONFIG_INTF_STRINGS },
index 6846eb0..4c171a8 100644 (file)
 
 /* Global TX Fifo Size Register */
 #define DWC31_GTXFIFOSIZ_TXFRAMNUM     BIT(15)         /* DWC_usb31 only */
-#define DWC31_GTXFIFOSIZ_TXFDEF(n)     ((n) & 0x7fff)  /* DWC_usb31 only */
-#define DWC3_GTXFIFOSIZ_TXFDEF(n)      ((n) & 0xffff)
+#define DWC31_GTXFIFOSIZ_TXFDEP(n)     ((n) & 0x7fff)  /* DWC_usb31 only */
+#define DWC3_GTXFIFOSIZ_TXFDEP(n)      ((n) & 0xffff)
 #define DWC3_GTXFIFOSIZ_TXFSTADDR(n)   ((n) & 0xffff0000)
 
+/* Global RX Fifo Size Register */
+#define DWC31_GRXFIFOSIZ_RXFDEP(n)     ((n) & 0x7fff)  /* DWC_usb31 only */
+#define DWC3_GRXFIFOSIZ_RXFDEP(n)      ((n) & 0xffff)
+
 /* Global Event Size Registers */
 #define DWC3_GEVNTSIZ_INTMASK          BIT(31)
 #define DWC3_GEVNTSIZ_SIZE(n)          ((n) & 0xffff)
index 4d3c79d..00746c2 100644 (file)
@@ -1728,7 +1728,6 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
        u32                     reg;
 
        u8                      link_state;
-       u8                      speed;
 
        /*
         * According to the Databook Remote wakeup request should
@@ -1738,16 +1737,13 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
         */
        reg = dwc3_readl(dwc->regs, DWC3_DSTS);
 
-       speed = reg & DWC3_DSTS_CONNECTSPD;
-       if ((speed == DWC3_DSTS_SUPERSPEED) ||
-           (speed == DWC3_DSTS_SUPERSPEED_PLUS))
-               return 0;
-
        link_state = DWC3_DSTS_USBLNKST(reg);
 
        switch (link_state) {
+       case DWC3_LINK_STATE_RESET:
        case DWC3_LINK_STATE_RX_DET:    /* in HS, means Early Suspend */
        case DWC3_LINK_STATE_U3:        /* in HS, means SUSPEND */
+       case DWC3_LINK_STATE_RESUME:
                break;
        default:
                return -EINVAL;
@@ -2227,7 +2223,6 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
 {
        struct dwc3 *dwc = dep->dwc;
        int mdwidth;
-       int kbytes;
        int size;
 
        mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
@@ -2236,24 +2231,24 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
 
        size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
        if (dwc3_is_usb31(dwc))
-               size = DWC31_GTXFIFOSIZ_TXFDEF(size);
+               size = DWC31_GTXFIFOSIZ_TXFDEP(size);
        else
-               size = DWC3_GTXFIFOSIZ_TXFDEF(size);
+               size = DWC3_GTXFIFOSIZ_TXFDEP(size);
 
        /* FIFO Depth is in MDWDITH bytes. Multiply */
        size *= mdwidth;
 
-       kbytes = size / 1024;
-       if (kbytes == 0)
-               kbytes = 1;
-
        /*
-        * FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for
-        * internal overhead. We don't really know how these are used,
-        * but documentation say it exists.
+        * To meet performance requirement, a minimum TxFIFO size of 3x
+        * MaxPacketSize is recommended for endpoints that support burst and a
+        * minimum TxFIFO size of 2x MaxPacketSize for endpoints that don't
+        * support burst. Use those numbers and we can calculate the max packet
+        * limit as below.
         */
-       size -= mdwidth * (kbytes + 1);
-       size /= kbytes;
+       if (dwc->maximum_speed >= USB_SPEED_SUPER)
+               size /= 3;
+       else
+               size /= 2;
 
        usb_ep_set_maxpacket_limit(&dep->endpoint, size);
 
@@ -2271,8 +2266,39 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
 static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
 {
        struct dwc3 *dwc = dep->dwc;
+       int mdwidth;
+       int size;
+
+       mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
 
-       usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
+       /* MDWIDTH is represented in bits, convert to bytes */
+       mdwidth /= 8;
+
+       /* All OUT endpoints share a single RxFIFO space */
+       size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0));
+       if (dwc3_is_usb31(dwc))
+               size = DWC31_GRXFIFOSIZ_RXFDEP(size);
+       else
+               size = DWC3_GRXFIFOSIZ_RXFDEP(size);
+
+       /* FIFO depth is in MDWDITH bytes */
+       size *= mdwidth;
+
+       /*
+        * To meet performance requirement, a minimum recommended RxFIFO size
+        * is defined as follow:
+        * RxFIFO size >= (3 x MaxPacketSize) +
+        * (3 x 8 bytes setup packets size) + (16 bytes clock crossing margin)
+        *
+        * Then calculate the max packet limit as below.
+        */
+       size -= (3 * 8) + 16;
+       if (size < 0)
+               size = 0;
+       else
+               size /= 3;
+
+       usb_ep_set_maxpacket_limit(&dep->endpoint, size);
        dep->endpoint.max_streams = 15;
        dep->endpoint.ops = &dwc3_gadget_ep_ops;
        list_add_tail(&dep->endpoint.ep_list,
@@ -2484,14 +2510,7 @@ static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
 
 static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req)
 {
-       /*
-        * For OUT direction, host may send less than the setup
-        * length. Return true for all OUT requests.
-        */
-       if (!req->direction)
-               return true;
-
-       return req->request.actual == req->request.length;
+       return req->num_pending_sgs == 0;
 }
 
 static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
@@ -2515,8 +2534,7 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
 
        req->request.actual = req->request.length - req->remaining;
 
-       if (!dwc3_gadget_ep_request_completed(req) ||
-                       req->num_pending_sgs) {
+       if (!dwc3_gadget_ep_request_completed(req)) {
                __dwc3_gadget_kick_transfer(dep);
                goto out;
        }
index 971c6b9..171280c 100644 (file)
@@ -728,19 +728,19 @@ static void xdbc_handle_tx_event(struct xdbc_trb *evt_trb)
        case COMP_USB_TRANSACTION_ERROR:
        case COMP_STALL_ERROR:
        default:
-               if (ep_id == XDBC_EPID_OUT)
+               if (ep_id == XDBC_EPID_OUT || ep_id == XDBC_EPID_OUT_INTEL)
                        xdbc.flags |= XDBC_FLAGS_OUT_STALL;
-               if (ep_id == XDBC_EPID_IN)
+               if (ep_id == XDBC_EPID_IN || ep_id == XDBC_EPID_IN_INTEL)
                        xdbc.flags |= XDBC_FLAGS_IN_STALL;
 
                xdbc_trace("endpoint %d stalled\n", ep_id);
                break;
        }
 
-       if (ep_id == XDBC_EPID_IN) {
+       if (ep_id == XDBC_EPID_IN || ep_id == XDBC_EPID_IN_INTEL) {
                xdbc.flags &= ~XDBC_FLAGS_IN_PROCESS;
                xdbc_bulk_transfer(NULL, XDBC_MAX_PACKET, true);
-       } else if (ep_id == XDBC_EPID_OUT) {
+       } else if (ep_id == XDBC_EPID_OUT || ep_id == XDBC_EPID_OUT_INTEL) {
                xdbc.flags &= ~XDBC_FLAGS_OUT_PROCESS;
        } else {
                xdbc_trace("invalid endpoint id %d\n", ep_id);
index 673686e..6e2b726 100644 (file)
@@ -120,8 +120,22 @@ struct xdbc_ring {
        u32                     cycle_state;
 };
 
-#define XDBC_EPID_OUT          2
-#define XDBC_EPID_IN           3
+/*
+ * These are the "Endpoint ID" (also known as "Context Index") values for the
+ * OUT Transfer Ring and the IN Transfer Ring of a Debug Capability Context data
+ * structure.
+ * According to the "eXtensible Host Controller Interface for Universal Serial
+ * Bus (xHCI)" specification, section "7.6.3.2 Endpoint Contexts and Transfer
+ * Rings", these should be 0 and 1, and those are the values AMD machines give
+ * you; but Intel machines seem to use the formula from section "4.5.1 Device
+ * Context Index", which is supposed to be used for the Device Context only.
+ * Luckily the values from Intel don't overlap with those from AMD, so we can
+ * just test for both.
+ */
+#define XDBC_EPID_OUT          0
+#define XDBC_EPID_IN           1
+#define XDBC_EPID_OUT_INTEL    2
+#define XDBC_EPID_IN_INTEL     3
 
 struct xdbc_state {
        u16                     vendor;
index c81023b..10f01f9 100644 (file)
@@ -1813,6 +1813,10 @@ static void ffs_data_reset(struct ffs_data *ffs)
        ffs->state = FFS_READ_DESCRIPTORS;
        ffs->setup_state = FFS_NO_SETUP;
        ffs->flags = 0;
+
+       ffs->ms_os_descs_ext_prop_count = 0;
+       ffs->ms_os_descs_ext_prop_name_len = 0;
+       ffs->ms_os_descs_ext_prop_data_len = 0;
 }
 
 
index 7640634..ca7d95b 100644 (file)
@@ -81,6 +81,7 @@ static int raw_event_queue_add(struct raw_event_queue *queue,
 static struct usb_raw_event *raw_event_queue_fetch(
                                struct raw_event_queue *queue)
 {
+       int ret;
        unsigned long flags;
        struct usb_raw_event *event;
 
@@ -89,11 +90,18 @@ static struct usb_raw_event *raw_event_queue_fetch(
         * there's at least one event queued by decrementing the semaphore,
         * and then take the lock to protect queue struct fields.
         */
-       if (down_interruptible(&queue->sema))
-               return NULL;
+       ret = down_interruptible(&queue->sema);
+       if (ret)
+               return ERR_PTR(ret);
        spin_lock_irqsave(&queue->lock, flags);
-       if (WARN_ON(!queue->size))
-               return NULL;
+       /*
+        * queue->size must have the same value as queue->sema counter (before
+        * the down_interruptible() call above), so this check is a fail-safe.
+        */
+       if (WARN_ON(!queue->size)) {
+               spin_unlock_irqrestore(&queue->lock, flags);
+               return ERR_PTR(-ENODEV);
+       }
        event = queue->events[0];
        queue->size--;
        memmove(&queue->events[0], &queue->events[1],
@@ -392,9 +400,8 @@ static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
        char *udc_device_name;
        unsigned long flags;
 
-       ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
-       if (ret)
-               return ret;
+       if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
+               return -EFAULT;
 
        switch (arg.speed) {
        case USB_SPEED_UNKNOWN:
@@ -501,15 +508,13 @@ out_unlock:
 
 static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
 {
-       int ret = 0;
        struct usb_raw_event arg;
        unsigned long flags;
        struct usb_raw_event *event;
        uint32_t length;
 
-       ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
-       if (ret)
-               return ret;
+       if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
+               return -EFAULT;
 
        spin_lock_irqsave(&dev->lock, flags);
        if (dev->state != STATE_DEV_RUNNING) {
@@ -525,25 +530,31 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
        spin_unlock_irqrestore(&dev->lock, flags);
 
        event = raw_event_queue_fetch(&dev->queue);
-       if (!event) {
+       if (PTR_ERR(event) == -EINTR) {
                dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
                return -EINTR;
        }
+       if (IS_ERR(event)) {
+               dev_err(&dev->gadget->dev, "failed to fetch event\n");
+               spin_lock_irqsave(&dev->lock, flags);
+               dev->state = STATE_DEV_FAILED;
+               spin_unlock_irqrestore(&dev->lock, flags);
+               return -ENODEV;
+       }
        length = min(arg.length, event->length);
-       ret = copy_to_user((void __user *)value, event,
-                               sizeof(*event) + length);
-       return ret;
+       if (copy_to_user((void __user *)value, event, sizeof(*event) + length))
+               return -EFAULT;
+
+       return 0;
 }
 
 static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
                                bool get_from_user)
 {
-       int ret;
        void *data;
 
-       ret = copy_from_user(io, ptr, sizeof(*io));
-       if (ret)
-               return ERR_PTR(ret);
+       if (copy_from_user(io, ptr, sizeof(*io)))
+               return ERR_PTR(-EFAULT);
        if (io->ep >= USB_RAW_MAX_ENDPOINTS)
                return ERR_PTR(-EINVAL);
        if (!usb_raw_io_flags_valid(io->flags))
@@ -658,12 +669,13 @@ static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value)
        if (IS_ERR(data))
                return PTR_ERR(data);
        ret = raw_process_ep0_io(dev, &io, data, false);
-       if (ret < 0) {
-               kfree(data);
-               return ret;
-       }
+       if (ret)
+               goto free;
+
        length = min(io.length, (unsigned int)ret);
-       ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);
+       if (copy_to_user((void __user *)(value + sizeof(io)), data, length))
+               ret = -EFAULT;
+free:
        kfree(data);
        return ret;
 }
@@ -952,12 +964,13 @@ static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value)
        if (IS_ERR(data))
                return PTR_ERR(data);
        ret = raw_process_ep_io(dev, &io, data, false);
-       if (ret < 0) {
-               kfree(data);
-               return ret;
-       }
+       if (ret)
+               goto free;
+
        length = min(io.length, (unsigned int)ret);
-       ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);
+       if (copy_to_user((void __user *)(value + sizeof(io)), data, length))
+               ret = -EFAULT;
+free:
        kfree(data);
        return ret;
 }
index 6e04321..2220034 100644 (file)
@@ -1951,10 +1951,10 @@ static irqreturn_t usba_vbus_irq_thread(int irq, void *devid)
                        usba_start(udc);
                } else {
                        udc->suspended = false;
-                       usba_stop(udc);
-
                        if (udc->driver->disconnect)
                                udc->driver->disconnect(&udc->gadget);
+
+                       usba_stop(udc);
                }
                udc->vbus_prev = vbus;
        }
index a4d9b5e..d49c6dc 100644 (file)
@@ -540,7 +540,7 @@ static void bdc_req_complete(struct bdc_ep *ep, struct bdc_req *req,
 {
        struct bdc *bdc = ep->bdc;
 
-       if (req == NULL  || &req->queue == NULL || &req->usb_req == NULL)
+       if (req == NULL)
                return;
 
        dev_dbg(bdc->dev, "%s ep:%s status:%d\n", __func__, ep->name, status);
index 9eca1fe..f37316d 100644 (file)
@@ -1571,6 +1571,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
                }
                if ((temp & PORT_RC))
                        reset_change = true;
+               if (temp & PORT_OC)
+                       status = 1;
        }
        if (!status && !reset_change) {
                xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
@@ -1636,6 +1638,13 @@ retry:
                                 port_index);
                        goto retry;
                }
+               /* bail out if port detected a over-current condition */
+               if (t1 & PORT_OC) {
+                       bus_state->bus_suspended = 0;
+                       spin_unlock_irqrestore(&xhci->lock, flags);
+                       xhci_dbg(xhci, "Bus suspend bailout, port over-current detected\n");
+                       return -EBUSY;
+               }
                /* suspend ports in U0, or bail out for new connect changes */
                if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) {
                        if ((t1 & PORT_CSC) && wake_enabled) {
index a78787b..0fda0c0 100644 (file)
@@ -547,6 +547,23 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
                                stream_id);
                return;
        }
+       /*
+        * A cancelled TD can complete with a stall if HW cached the trb.
+        * In this case driver can't find cur_td, but if the ring is empty we
+        * can move the dequeue pointer to the current enqueue position.
+        */
+       if (!cur_td) {
+               if (list_empty(&ep_ring->td_list)) {
+                       state->new_deq_seg = ep_ring->enq_seg;
+                       state->new_deq_ptr = ep_ring->enqueue;
+                       state->new_cycle_state = ep_ring->cycle_state;
+                       goto done;
+               } else {
+                       xhci_warn(xhci, "Can't find new dequeue state, missing cur_td\n");
+                       return;
+               }
+       }
+
        /* Dig out the cycle state saved by the xHC during the stop ep cmd */
        xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
                        "Finding endpoint context");
@@ -592,6 +609,7 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
        state->new_deq_seg = new_seg;
        state->new_deq_ptr = new_deq;
 
+done:
        /* Don't update the ring cycle state for the producer (us). */
        xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
                        "Cycle state = 0x%x", state->new_cycle_state);
@@ -1856,8 +1874,8 @@ static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
 
        if (reset_type == EP_HARD_RESET) {
                ep->ep_state |= EP_HARD_CLEAR_TOGGLE;
-               xhci_cleanup_stalled_ring(xhci, ep_index, stream_id, td);
-               xhci_clear_hub_tt_buffer(xhci, td, ep);
+               xhci_cleanup_stalled_ring(xhci, slot_id, ep_index, stream_id,
+                                         td);
        }
        xhci_ring_cmd_db(xhci);
 }
@@ -1978,11 +1996,18 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
        if (trb_comp_code == COMP_STALL_ERROR ||
                xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
                                                trb_comp_code)) {
-               /* Issue a reset endpoint command to clear the host side
-                * halt, followed by a set dequeue command to move the
-                * dequeue pointer past the TD.
-                * The class driver clears the device side halt later.
+               /*
+                * xhci internal endpoint state will go to a "halt" state for
+                * any stall, including default control pipe protocol stall.
+                * To clear the host side halt we need to issue a reset endpoint
+                * command, followed by a set dequeue command to move past the
+                * TD.
+                * Class drivers clear the device side halt from a functional
+                * stall later. Hub TT buffer should only be cleared for FS/LS
+                * devices behind HS hubs for functional stalls.
                 */
+               if ((ep_index != 0) || (trb_comp_code != COMP_STALL_ERROR))
+                       xhci_clear_hub_tt_buffer(xhci, td, ep);
                xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
                                        ep_ring->stream_id, td, EP_HARD_RESET);
        } else {
@@ -2539,6 +2564,15 @@ static int handle_tx_event(struct xhci_hcd *xhci,
                                xhci_dbg(xhci, "td_list is empty while skip flag set. Clear skip flag for slot %u ep %u.\n",
                                         slot_id, ep_index);
                        }
+                       if (trb_comp_code == COMP_STALL_ERROR ||
+                           xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
+                                                             trb_comp_code)) {
+                               xhci_cleanup_halted_endpoint(xhci, slot_id,
+                                                            ep_index,
+                                                            ep_ring->stream_id,
+                                                            NULL,
+                                                            EP_HARD_RESET);
+                       }
                        goto cleanup;
                }
 
index fe38275..bee5dec 100644 (file)
@@ -3031,19 +3031,19 @@ static void xhci_setup_input_ctx_for_quirk(struct xhci_hcd *xhci,
                        added_ctxs, added_ctxs);
 }
 
-void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index,
-                              unsigned int stream_id, struct xhci_td *td)
+void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int slot_id,
+                              unsigned int ep_index, unsigned int stream_id,
+                              struct xhci_td *td)
 {
        struct xhci_dequeue_state deq_state;
-       struct usb_device *udev = td->urb->dev;
 
        xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
                        "Cleaning up stalled endpoint ring");
        /* We need to move the HW's dequeue pointer past this TD,
         * or it will attempt to resend it on the next doorbell ring.
         */
-       xhci_find_new_dequeue_state(xhci, udev->slot_id,
-                       ep_index, stream_id, td, &deq_state);
+       xhci_find_new_dequeue_state(xhci, slot_id, ep_index, stream_id, td,
+                                   &deq_state);
 
        if (!deq_state.new_deq_ptr || !deq_state.new_deq_seg)
                return;
@@ -3054,7 +3054,7 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index,
        if (!(xhci->quirks & XHCI_RESET_EP_QUIRK)) {
                xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
                                "Queueing new dequeue state");
-               xhci_queue_new_dequeue_state(xhci, udev->slot_id,
+               xhci_queue_new_dequeue_state(xhci, slot_id,
                                ep_index, &deq_state);
        } else {
                /* Better hope no one uses the input context between now and the
@@ -3065,7 +3065,7 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index,
                xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
                                "Setting up input context for "
                                "configure endpoint command");
-               xhci_setup_input_ctx_for_quirk(xhci, udev->slot_id,
+               xhci_setup_input_ctx_for_quirk(xhci, slot_id,
                                ep_index, &deq_state);
        }
 }
index 3289bb5..86cfefd 100644 (file)
@@ -2116,8 +2116,9 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
 void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
                unsigned int slot_id, unsigned int ep_index,
                struct xhci_dequeue_state *deq_state);
-void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index,
-               unsigned int stream_id, struct xhci_td *td);
+void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int slot_id,
+                              unsigned int ep_index, unsigned int stream_id,
+                              struct xhci_td *td);
 void xhci_stop_endpoint_command_watchdog(struct timer_list *t);
 void xhci_handle_command_timeout(struct work_struct *work);
 
index 2ab9600..fc8a5da 100644 (file)
@@ -1199,18 +1199,18 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
 /* High level: Gfx (indexed) register access */
 
 #ifdef CONFIG_USB_SISUSBVGA_CON
-int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data)
+int sisusb_setreg(struct sisusb_usb_data *sisusb, u32 port, u8 data)
 {
        return sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
 }
 
-int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data)
+int sisusb_getreg(struct sisusb_usb_data *sisusb, u32 port, u8 *data)
 {
        return sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
 }
 #endif
 
-int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port,
+int sisusb_setidxreg(struct sisusb_usb_data *sisusb, u32 port,
                u8 index, u8 data)
 {
        int ret;
@@ -1220,7 +1220,7 @@ int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port,
        return ret;
 }
 
-int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port,
+int sisusb_getidxreg(struct sisusb_usb_data *sisusb, u32 port,
                u8 index, u8 *data)
 {
        int ret;
@@ -1230,7 +1230,7 @@ int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port,
        return ret;
 }
 
-int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
+int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, u32 port, u8 idx,
                u8 myand, u8 myor)
 {
        int ret;
@@ -1245,7 +1245,7 @@ int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
 }
 
 static int sisusb_setidxregmask(struct sisusb_usb_data *sisusb,
-               int port, u8 idx, u8 data, u8 mask)
+               u32 port, u8 idx, u8 data, u8 mask)
 {
        int ret;
        u8 tmp;
@@ -1258,13 +1258,13 @@ static int sisusb_setidxregmask(struct sisusb_usb_data *sisusb,
        return ret;
 }
 
-int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port,
+int sisusb_setidxregor(struct sisusb_usb_data *sisusb, u32 port,
                u8 index, u8 myor)
 {
        return sisusb_setidxregandor(sisusb, port, index, 0xff, myor);
 }
 
-int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port,
+int sisusb_setidxregand(struct sisusb_usb_data *sisusb, u32 port,
                u8 idx, u8 myand)
 {
        return sisusb_setidxregandor(sisusb, port, idx, myand, 0x00);
@@ -2785,8 +2785,8 @@ static loff_t sisusb_lseek(struct file *file, loff_t offset, int orig)
 static int sisusb_handle_command(struct sisusb_usb_data *sisusb,
                struct sisusb_command *y, unsigned long arg)
 {
-       int     retval, port, length;
-       u32     address;
+       int     retval, length;
+       u32     port, address;
 
        /* All our commands require the device
         * to be initialized.
index 1782c75..ace0998 100644 (file)
@@ -812,17 +812,17 @@ static const struct SiS_VCLKData SiSUSB_VCLKData[] = {
 int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
 int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);
 
-extern int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data);
-extern int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 * data);
-extern int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port,
+extern int sisusb_setreg(struct sisusb_usb_data *sisusb, u32 port, u8 data);
+extern int sisusb_getreg(struct sisusb_usb_data *sisusb, u32 port, u8 * data);
+extern int sisusb_setidxreg(struct sisusb_usb_data *sisusb, u32 port,
                            u8 index, u8 data);
-extern int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port,
+extern int sisusb_getidxreg(struct sisusb_usb_data *sisusb, u32 port,
                            u8 index, u8 * data);
-extern int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port,
+extern int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, u32 port,
                                 u8 idx, u8 myand, u8 myor);
-extern int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port,
+extern int sisusb_setidxregor(struct sisusb_usb_data *sisusb, u32 port,
                              u8 index, u8 myor);
-extern int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port,
+extern int sisusb_setidxregand(struct sisusb_usb_data *sisusb, u32 port,
                               u8 idx, u8 myand);
 
 void sisusb_delete(struct kref *kref);
index 3670fda..d592071 100644 (file)
@@ -81,6 +81,19 @@ static void uas_free_streams(struct uas_dev_info *devinfo);
 static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *prefix,
                                int status);
 
+/*
+ * This driver needs its own workqueue, as we need to control memory allocation.
+ *
+ * In the course of error handling and power management uas_wait_for_pending_cmnds()
+ * needs to flush pending work items. In these contexts we cannot allocate memory
+ * by doing block IO as we would deadlock. For the same reason we cannot wait
+ * for anything allocating memory not heeding these constraints.
+ *
+ * So we have to control all work items that can be on the workqueue we flush.
+ * Hence we cannot share a queue and need our own.
+ */
+static struct workqueue_struct *workqueue;
+
 static void uas_do_work(struct work_struct *work)
 {
        struct uas_dev_info *devinfo =
@@ -109,7 +122,7 @@ static void uas_do_work(struct work_struct *work)
                if (!err)
                        cmdinfo->state &= ~IS_IN_WORK_LIST;
                else
-                       schedule_work(&devinfo->work);
+                       queue_work(workqueue, &devinfo->work);
        }
 out:
        spin_unlock_irqrestore(&devinfo->lock, flags);
@@ -134,7 +147,7 @@ static void uas_add_work(struct uas_cmd_info *cmdinfo)
 
        lockdep_assert_held(&devinfo->lock);
        cmdinfo->state |= IS_IN_WORK_LIST;
-       schedule_work(&devinfo->work);
+       queue_work(workqueue, &devinfo->work);
 }
 
 static void uas_zap_pending(struct uas_dev_info *devinfo, int result)
@@ -190,6 +203,9 @@ static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *prefix,
        struct uas_cmd_info *ci = (void *)&cmnd->SCp;
        struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
 
+       if (status == -ENODEV) /* too late */
+               return;
+
        scmd_printk(KERN_INFO, cmnd,
                    "%s %d uas-tag %d inflight:%s%s%s%s%s%s%s%s%s%s%s%s ",
                    prefix, status, cmdinfo->uas_tag,
@@ -1226,7 +1242,31 @@ static struct usb_driver uas_driver = {
        .id_table = uas_usb_ids,
 };
 
-module_usb_driver(uas_driver);
+static int __init uas_init(void)
+{
+       int rv;
+
+       workqueue = alloc_workqueue("uas", WQ_MEM_RECLAIM, 0);
+       if (!workqueue)
+               return -ENOMEM;
+
+       rv = usb_register(&uas_driver);
+       if (rv) {
+               destroy_workqueue(workqueue);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void __exit uas_exit(void)
+{
+       usb_deregister(&uas_driver);
+       destroy_workqueue(workqueue);
+}
+
+module_init(uas_init);
+module_exit(uas_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_IMPORT_NS(USB_STORAGE);
index 1880f3e..f6c3681 100644 (file)
@@ -2323,6 +2323,13 @@ UNUSUAL_DEV(  0x3340, 0xffff, 0x0000, 0x0000,
                USB_SC_DEVICE,USB_PR_DEVICE,NULL,
                US_FL_MAX_SECTORS_64 ),
 
+/* Reported by Cyril Roelandt <tipecaml@gmail.com> */
+UNUSUAL_DEV(  0x357d, 0x7788, 0x0114, 0x0114,
+               "JMicron",
+               "USB to ATA/ATAPI Bridge",
+               USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+               US_FL_BROKEN_FUA ),
+
 /* Reported by Andrey Rahmatullin <wrar@altlinux.org> */
 UNUSUAL_DEV(  0x4102, 0x1020, 0x0100,  0x0100,
                "iRiver",
index c823122..e8ddb81 100644 (file)
@@ -198,7 +198,10 @@ EXPORT_SYMBOL_GPL(typec_altmode_vdm);
 const struct typec_altmode *
 typec_altmode_get_partner(struct typec_altmode *adev)
 {
-       return adev ? &to_altmode(adev)->partner->adev : NULL;
+       if (!adev || !to_altmode(adev)->partner)
+               return NULL;
+
+       return &to_altmode(adev)->partner->adev;
 }
 EXPORT_SYMBOL_GPL(typec_altmode_get_partner);
 
index 46457c1..7afe275 100644 (file)
@@ -114,8 +114,8 @@ pi3usb30532_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
 static int pi3usb30532_probe(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
-       struct typec_switch_desc sw_desc;
-       struct typec_mux_desc mux_desc;
+       struct typec_switch_desc sw_desc = { };
+       struct typec_mux_desc mux_desc = { };
        struct pi3usb30532 *pi;
        int ret;
 
index de3576e..82b19eb 100644 (file)
@@ -3794,6 +3794,14 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
                 */
                break;
 
+       case PORT_RESET:
+       case PORT_RESET_WAIT_OFF:
+               /*
+                * State set back to default mode once the timer completes.
+                * Ignore CC changes here.
+                */
+               break;
+
        default:
                if (tcpm_port_is_disconnected(port))
                        tcpm_set_state(port, unattached_state(port), 0);
@@ -3855,6 +3863,15 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port)
        case SRC_TRY_DEBOUNCE:
                /* Do nothing, waiting for sink detection */
                break;
+
+       case PORT_RESET:
+       case PORT_RESET_WAIT_OFF:
+               /*
+                * State set back to default mode once the timer completes.
+                * Ignore vbus changes here.
+                */
+               break;
+
        default:
                break;
        }
@@ -3908,10 +3925,19 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port)
        case PORT_RESET_WAIT_OFF:
                tcpm_set_state(port, tcpm_default_state(port), 0);
                break;
+
        case SRC_TRY_WAIT:
        case SRC_TRY_DEBOUNCE:
                /* Do nothing, waiting for sink detection */
                break;
+
+       case PORT_RESET:
+               /*
+                * State set back to default mode once the timer completes.
+                * Ignore vbus changes here.
+                */
+               break;
+
        default:
                if (port->pwr_role == TYPEC_SINK &&
                    port->attached)
index 7db1460..e814006 100644 (file)
@@ -1,21 +1,16 @@
 # SPDX-License-Identifier: GPL-2.0-only
-config VDPA
-       tristate
+menuconfig VDPA
+       tristate "vDPA drivers"
        help
          Enable this module to support vDPA device that uses a
          datapath which complies with virtio specifications with
          vendor specific control path.
 
-menuconfig VDPA_MENU
-       bool "VDPA drivers"
-       default n
-
-if VDPA_MENU
+if VDPA
 
 config VDPA_SIM
        tristate "vDPA device simulator"
-       depends on RUNTIME_TESTING_MENU
-       select VDPA
+       depends on RUNTIME_TESTING_MENU && HAS_DMA && VHOST_DPN
        select VHOST_RING
        default n
        help
@@ -24,9 +19,8 @@ config VDPA_SIM
          development of vDPA.
 
 config IFCVF
-       tristate "Intel IFC VF VDPA driver"
+       tristate "Intel IFC VF vDPA driver"
        depends on PCI_MSI
-       select VDPA
        default n
        help
          This kernel module can drive Intel IFC VF NIC to offload
@@ -34,4 +28,4 @@ config IFCVF
          To compile this driver as a module, choose M here: the module will
          be called ifcvf.
 
-endif # VDPA_MENU
+endif # VDPA
index b61b06e..e24371d 100644 (file)
@@ -301,12 +301,10 @@ int ifcvf_set_vq_state(struct ifcvf_hw *hw, u16 qid, u64 num)
 
 static int ifcvf_hw_enable(struct ifcvf_hw *hw)
 {
-       struct ifcvf_lm_cfg __iomem *ifcvf_lm;
        struct virtio_pci_common_cfg __iomem *cfg;
        struct ifcvf_adapter *ifcvf;
        u32 i;
 
-       ifcvf_lm = (struct ifcvf_lm_cfg __iomem *)hw->lm_cfg;
        ifcvf = vf_to_adapter(hw);
        cfg = hw->common_cfg;
        ifc_iowrite16(IFCVF_MSI_CONFIG_OFF, &cfg->msix_config);
index 8d54dc5..abf6a06 100644 (file)
@@ -31,11 +31,9 @@ static irqreturn_t ifcvf_intr_handler(int irq, void *arg)
 static int ifcvf_start_datapath(void *private)
 {
        struct ifcvf_hw *vf = ifcvf_private_to_vf(private);
-       struct ifcvf_adapter *ifcvf;
        u8 status;
        int ret;
 
-       ifcvf = vf_to_adapter(vf);
        vf->nr_vring = IFCVF_MAX_QUEUE_PAIRS * 2;
        ret = ifcvf_start_hw(vf);
        if (ret < 0) {
@@ -228,7 +226,7 @@ static u32 ifcvf_vdpa_get_vendor_id(struct vdpa_device *vdpa_dev)
        return IFCVF_SUBSYS_VENDOR_ID;
 }
 
-static u16 ifcvf_vdpa_get_vq_align(struct vdpa_device *vdpa_dev)
+static u32 ifcvf_vdpa_get_vq_align(struct vdpa_device *vdpa_dev)
 {
        return IFCVF_QUEUE_ALIGNMENT;
 }
index e9ed6a2..ff6562f 100644 (file)
@@ -116,7 +116,7 @@ EXPORT_SYMBOL_GPL(__vdpa_alloc_device);
 
 /**
  * vdpa_register_device - register a vDPA device
- * Callers must have a succeed call of vdpa_init_device() before.
+ * Callers must have a succeed call of vdpa_alloc_device() before.
  * @vdev: the vdpa device to be registered to vDPA bus
  *
  * Returns an error when fail to add to vDPA bus
index 6e8a0cf..7957d2d 100644 (file)
@@ -435,7 +435,7 @@ static u64 vdpasim_get_vq_state(struct vdpa_device *vdpa, u16 idx)
        return vrh->last_avail_idx;
 }
 
-static u16 vdpasim_get_vq_align(struct vdpa_device *vdpa)
+static u32 vdpasim_get_vq_align(struct vdpa_device *vdpa)
 {
        return VDPASIM_QUEUE_ALIGN;
 }
@@ -488,7 +488,7 @@ static u8 vdpasim_get_status(struct vdpa_device *vdpa)
        status = vdpasim->status;
        spin_unlock(&vdpasim->lock);
 
-       return vdpasim->status;
+       return status;
 }
 
 static void vdpasim_set_status(struct vdpa_device *vdpa, u8 status)
index 85b32c3..cc1d647 100644 (file)
@@ -342,8 +342,8 @@ static int vaddr_get_pfn(struct mm_struct *mm, unsigned long vaddr,
        vma = find_vma_intersection(mm, vaddr, vaddr + 1);
 
        if (vma && vma->vm_flags & VM_PFNMAP) {
-               *pfn = ((vaddr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
-               if (is_invalid_reserved_pfn(*pfn))
+               if (!follow_pfn(vma, vaddr, pfn) &&
+                   is_invalid_reserved_pfn(*pfn))
                        ret = 0;
        }
 done:
@@ -555,7 +555,7 @@ static int vfio_iommu_type1_pin_pages(void *iommu_data,
                        continue;
                }
 
-               remote_vaddr = dma->vaddr + iova - dma->iova;
+               remote_vaddr = dma->vaddr + (iova - dma->iova);
                ret = vfio_pin_page_external(dma, remote_vaddr, &phys_pfn[i],
                                             do_accounting);
                if (ret)
@@ -2345,10 +2345,10 @@ static int vfio_iommu_type1_dma_rw_chunk(struct vfio_iommu *iommu,
        vaddr = dma->vaddr + offset;
 
        if (write)
-               *copied = __copy_to_user((void __user *)vaddr, data,
+               *copied = copy_to_user((void __user *)vaddr, data,
                                         count) ? 0 : count;
        else
-               *copied = __copy_from_user(data, (void __user *)vaddr,
+               *copied = copy_from_user(data, (void __user *)vaddr,
                                           count) ? 0 : count;
        if (kthread)
                unuse_mm(mm);
index 362b832..c4f2737 100644 (file)
@@ -3,6 +3,8 @@ config VHOST_IOTLB
        tristate
        help
          Generic IOTLB implementation for vhost and vringh.
+         This option is selected by any driver which needs to support
+         an IOMMU in software.
 
 config VHOST_RING
        tristate
@@ -11,6 +13,15 @@ config VHOST_RING
          This option is selected by any driver which needs to access
          the host side of a virtio ring.
 
+config VHOST_DPN
+       bool
+       depends on !ARM || AEABI
+       default y
+       help
+         Anything selecting VHOST or VHOST_RING must depend on VHOST_DPN.
+         This excludes the deprecated ARM ABI since that forces a 4 byte
+         alignment on all structs - incompatible with virtio spec requirements.
+
 config VHOST
        tristate
        select VHOST_IOTLB
@@ -26,7 +37,7 @@ if VHOST_MENU
 
 config VHOST_NET
        tristate "Host kernel accelerator for virtio net"
-       depends on NET && EVENTFD && (TUN || !TUN) && (TAP || !TAP)
+       depends on NET && EVENTFD && (TUN || !TUN) && (TAP || !TAP) && VHOST_DPN
        select VHOST
        ---help---
          This kernel module can be loaded in host kernel to accelerate
@@ -38,7 +49,7 @@ config VHOST_NET
 
 config VHOST_SCSI
        tristate "VHOST_SCSI TCM fabric driver"
-       depends on TARGET_CORE && EVENTFD
+       depends on TARGET_CORE && EVENTFD && VHOST_DPN
        select VHOST
        default n
        ---help---
@@ -47,7 +58,7 @@ config VHOST_SCSI
 
 config VHOST_VSOCK
        tristate "vhost virtio-vsock driver"
-       depends on VSOCKETS && EVENTFD
+       depends on VSOCKETS && EVENTFD && VHOST_DPN
        select VHOST
        select VIRTIO_VSOCKETS_COMMON
        default n
@@ -61,9 +72,9 @@ config VHOST_VSOCK
 
 config VHOST_VDPA
        tristate "Vhost driver for vDPA-based backend"
-       depends on EVENTFD
+       depends on EVENTFD && VHOST_DPN
        select VHOST
-       select VDPA
+       depends on VDPA
        help
          This kernel module can be loaded in host kernel to accelerate
          guest virtio devices with the vDPA-based backends.
index 87469d6..2927f02 100644 (file)
@@ -424,7 +424,7 @@ static void vhost_net_disable_vq(struct vhost_net *n,
        struct vhost_net_virtqueue *nvq =
                container_of(vq, struct vhost_net_virtqueue, vq);
        struct vhost_poll *poll = n->poll + (nvq - n->vqs);
-       if (!vq->private_data)
+       if (!vhost_vq_get_backend(vq))
                return;
        vhost_poll_stop(poll);
 }
@@ -437,7 +437,7 @@ static int vhost_net_enable_vq(struct vhost_net *n,
        struct vhost_poll *poll = n->poll + (nvq - n->vqs);
        struct socket *sock;
 
-       sock = vq->private_data;
+       sock = vhost_vq_get_backend(vq);
        if (!sock)
                return 0;
 
@@ -524,7 +524,7 @@ static void vhost_net_busy_poll(struct vhost_net *net,
                return;
 
        vhost_disable_notify(&net->dev, vq);
-       sock = rvq->private_data;
+       sock = vhost_vq_get_backend(rvq);
 
        busyloop_timeout = poll_rx ? rvq->busyloop_timeout:
                                     tvq->busyloop_timeout;
@@ -570,8 +570,10 @@ static int vhost_net_tx_get_vq_desc(struct vhost_net *net,
 
        if (r == tvq->num && tvq->busyloop_timeout) {
                /* Flush batched packets first */
-               if (!vhost_sock_zcopy(tvq->private_data))
-                       vhost_tx_batch(net, tnvq, tvq->private_data, msghdr);
+               if (!vhost_sock_zcopy(vhost_vq_get_backend(tvq)))
+                       vhost_tx_batch(net, tnvq,
+                                      vhost_vq_get_backend(tvq),
+                                      msghdr);
 
                vhost_net_busy_poll(net, rvq, tvq, busyloop_intr, false);
 
@@ -685,7 +687,7 @@ static int vhost_net_build_xdp(struct vhost_net_virtqueue *nvq,
        struct vhost_virtqueue *vq = &nvq->vq;
        struct vhost_net *net = container_of(vq->dev, struct vhost_net,
                                             dev);
-       struct socket *sock = vq->private_data;
+       struct socket *sock = vhost_vq_get_backend(vq);
        struct page_frag *alloc_frag = &net->page_frag;
        struct virtio_net_hdr *gso;
        struct xdp_buff *xdp = &nvq->xdp[nvq->batched_xdp];
@@ -952,7 +954,7 @@ static void handle_tx(struct vhost_net *net)
        struct socket *sock;
 
        mutex_lock_nested(&vq->mutex, VHOST_NET_VQ_TX);
-       sock = vq->private_data;
+       sock = vhost_vq_get_backend(vq);
        if (!sock)
                goto out;
 
@@ -1121,7 +1123,7 @@ static void handle_rx(struct vhost_net *net)
        int recv_pkts = 0;
 
        mutex_lock_nested(&vq->mutex, VHOST_NET_VQ_RX);
-       sock = vq->private_data;
+       sock = vhost_vq_get_backend(vq);
        if (!sock)
                goto out;
 
@@ -1345,9 +1347,9 @@ static struct socket *vhost_net_stop_vq(struct vhost_net *n,
                container_of(vq, struct vhost_net_virtqueue, vq);
 
        mutex_lock(&vq->mutex);
-       sock = vq->private_data;
+       sock = vhost_vq_get_backend(vq);
        vhost_net_disable_vq(n, vq);
-       vq->private_data = NULL;
+       vhost_vq_set_backend(vq, NULL);
        vhost_net_buf_unproduce(nvq);
        nvq->rx_ring = NULL;
        mutex_unlock(&vq->mutex);
@@ -1521,7 +1523,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
        }
 
        /* start polling new socket */
-       oldsock = vq->private_data;
+       oldsock = vhost_vq_get_backend(vq);
        if (sock != oldsock) {
                ubufs = vhost_net_ubuf_alloc(vq,
                                             sock && vhost_sock_zcopy(sock));
@@ -1531,7 +1533,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
                }
 
                vhost_net_disable_vq(n, vq);
-               vq->private_data = sock;
+               vhost_vq_set_backend(vq, sock);
                vhost_net_buf_unproduce(nvq);
                r = vhost_vq_init_access(vq);
                if (r)
@@ -1568,7 +1570,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
        return 0;
 
 err_used:
-       vq->private_data = oldsock;
+       vhost_vq_set_backend(vq, oldsock);
        vhost_net_enable_vq(n, vq);
        if (ubufs)
                vhost_net_ubuf_put_wait_and_free(ubufs);
index 7653667..c399522 100644 (file)
@@ -452,7 +452,7 @@ vhost_scsi_do_evt_work(struct vhost_scsi *vs, struct vhost_scsi_evt *evt)
        unsigned out, in;
        int head, ret;
 
-       if (!vq->private_data) {
+       if (!vhost_vq_get_backend(vq)) {
                vs->vs_events_missed = true;
                return;
        }
@@ -892,7 +892,7 @@ vhost_scsi_get_req(struct vhost_virtqueue *vq, struct vhost_scsi_ctx *vc,
        } else {
                struct vhost_scsi_tpg **vs_tpg, *tpg;
 
-               vs_tpg = vq->private_data;      /* validated at handler entry */
+               vs_tpg = vhost_vq_get_backend(vq);      /* validated at handler entry */
 
                tpg = READ_ONCE(vs_tpg[*vc->target]);
                if (unlikely(!tpg)) {
@@ -929,7 +929,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
         * We can handle the vq only after the endpoint is setup by calling the
         * VHOST_SCSI_SET_ENDPOINT ioctl.
         */
-       vs_tpg = vq->private_data;
+       vs_tpg = vhost_vq_get_backend(vq);
        if (!vs_tpg)
                goto out;
 
@@ -1184,7 +1184,7 @@ vhost_scsi_ctl_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
         * We can handle the vq only after the endpoint is setup by calling the
         * VHOST_SCSI_SET_ENDPOINT ioctl.
         */
-       if (!vq->private_data)
+       if (!vhost_vq_get_backend(vq))
                goto out;
 
        memset(&vc, 0, sizeof(vc));
@@ -1322,7 +1322,7 @@ static void vhost_scsi_evt_handle_kick(struct vhost_work *work)
        struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev);
 
        mutex_lock(&vq->mutex);
-       if (!vq->private_data)
+       if (!vhost_vq_get_backend(vq))
                goto out;
 
        if (vs->vs_events_missed)
@@ -1460,7 +1460,7 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs,
                for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) {
                        vq = &vs->vqs[i].vq;
                        mutex_lock(&vq->mutex);
-                       vq->private_data = vs_tpg;
+                       vhost_vq_set_backend(vq, vs_tpg);
                        vhost_vq_init_access(vq);
                        mutex_unlock(&vq->mutex);
                }
@@ -1547,7 +1547,7 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs,
                for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) {
                        vq = &vs->vqs[i].vq;
                        mutex_lock(&vq->mutex);
-                       vq->private_data = NULL;
+                       vhost_vq_set_backend(vq, NULL);
                        mutex_unlock(&vq->mutex);
                }
        }
index e37c92d..9a3a090 100644 (file)
@@ -49,7 +49,7 @@ static void handle_vq(struct vhost_test *n)
        void *private;
 
        mutex_lock(&vq->mutex);
-       private = vq->private_data;
+       private = vhost_vq_get_backend(vq);
        if (!private) {
                mutex_unlock(&vq->mutex);
                return;
@@ -120,7 +120,7 @@ static int vhost_test_open(struct inode *inode, struct file *f)
        vqs[VHOST_TEST_VQ] = &n->vqs[VHOST_TEST_VQ];
        n->vqs[VHOST_TEST_VQ].handle_kick = handle_vq_kick;
        vhost_dev_init(dev, vqs, VHOST_TEST_VQ_MAX, UIO_MAXIOV,
-                      VHOST_TEST_PKT_WEIGHT, VHOST_TEST_WEIGHT);
+                      VHOST_TEST_PKT_WEIGHT, VHOST_TEST_WEIGHT, NULL);
 
        f->private_data = n;
 
@@ -133,8 +133,8 @@ static void *vhost_test_stop_vq(struct vhost_test *n,
        void *private;
 
        mutex_lock(&vq->mutex);
-       private = vq->private_data;
-       vq->private_data = NULL;
+       private = vhost_vq_get_backend(vq);
+       vhost_vq_set_backend(vq, NULL);
        mutex_unlock(&vq->mutex);
        return private;
 }
@@ -198,8 +198,8 @@ static long vhost_test_run(struct vhost_test *n, int test)
                priv = test ? n : NULL;
 
                /* start polling new socket */
-               oldpriv = vq->private_data;
-               vq->private_data = priv;
+               oldpriv = vhost_vq_get_backend(vq);
+               vhost_vq_set_backend(vq, priv);
 
                r = vhost_vq_init_access(&n->vqs[index]);
 
@@ -225,7 +225,7 @@ static long vhost_test_reset_owner(struct vhost_test *n)
 {
        void *priv = NULL;
        long err;
-       struct vhost_umem *umem;
+       struct vhost_iotlb *umem;
 
        mutex_lock(&n->dev.mutex);
        err = vhost_dev_check_owner(&n->dev);
index 421f02a..0968361 100644 (file)
@@ -296,7 +296,6 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
        struct vdpa_callback cb;
        struct vhost_virtqueue *vq;
        struct vhost_vring_state s;
-       u8 status;
        u32 idx;
        long r;
 
@@ -310,8 +309,6 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
        idx = array_index_nospec(idx, v->nvqs);
        vq = &v->vqs[idx];
 
-       status = ops->get_status(vdpa);
-
        if (cmd == VHOST_VDPA_SET_VRING_ENABLE) {
                if (copy_from_user(&s, argp, sizeof(s)))
                        return -EFAULT;
@@ -678,8 +675,6 @@ static int vhost_vdpa_open(struct inode *inode, struct file *filep)
        int nvqs, i, r, opened;
 
        v = container_of(inode->i_cdev, struct vhost_vdpa, cdev);
-       if (!v)
-               return -ENODEV;
 
        opened = atomic_cmpxchg(&v->opened, 0, 1);
        if (opened)
index 1813821..f8403bd 100644 (file)
@@ -231,6 +231,33 @@ enum {
                         (1ULL << VIRTIO_F_VERSION_1)
 };
 
+/**
+ * vhost_vq_set_backend - Set backend.
+ *
+ * @vq            Virtqueue.
+ * @private_data  The private data.
+ *
+ * Context: Need to call with vq->mutex acquired.
+ */
+static inline void vhost_vq_set_backend(struct vhost_virtqueue *vq,
+                                       void *private_data)
+{
+       vq->private_data = private_data;
+}
+
+/**
+ * vhost_vq_get_backend - Get backend.
+ *
+ * @vq            Virtqueue.
+ *
+ * Context: Need to call with vq->mutex acquired.
+ * Return: Private data previously set with vhost_vq_set_backend.
+ */
+static inline void *vhost_vq_get_backend(struct vhost_virtqueue *vq)
+{
+       return vq->private_data;
+}
+
 static inline bool vhost_has_feature(struct vhost_virtqueue *vq, int bit)
 {
        return vq->acked_features & (1ULL << bit);
index ee0491f..ba8e0d6 100644 (file)
 #include <linux/uaccess.h>
 #include <linux/slab.h>
 #include <linux/export.h>
+#if IS_REACHABLE(CONFIG_VHOST_IOTLB)
 #include <linux/bvec.h>
 #include <linux/highmem.h>
 #include <linux/vhost_iotlb.h>
+#endif
 #include <uapi/linux/virtio_config.h>
 
 static __printf(1,2) __cold void vringh_bad(const char *fmt, ...)
@@ -1059,6 +1061,8 @@ int vringh_need_notify_kern(struct vringh *vrh)
 }
 EXPORT_SYMBOL(vringh_need_notify_kern);
 
+#if IS_REACHABLE(CONFIG_VHOST_IOTLB)
+
 static int iotlb_translate(const struct vringh *vrh,
                           u64 addr, u64 len, struct bio_vec iov[],
                           int iov_size, u32 perm)
@@ -1416,5 +1420,6 @@ int vringh_need_notify_iotlb(struct vringh *vrh)
 }
 EXPORT_SYMBOL(vringh_need_notify_iotlb);
 
+#endif
 
 MODULE_LICENSE("GPL");
index 9766948..fb4e944 100644 (file)
@@ -91,7 +91,7 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
 
        mutex_lock(&vq->mutex);
 
-       if (!vq->private_data)
+       if (!vhost_vq_get_backend(vq))
                goto out;
 
        /* Avoid further vmexits, we're already processing the virtqueue */
@@ -181,14 +181,14 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
                        break;
                }
 
-               vhost_add_used(vq, head, sizeof(pkt->hdr) + payload_len);
-               added = true;
-
-               /* Deliver to monitoring devices all correctly transmitted
-                * packets.
+               /* Deliver to monitoring devices all packets that we
+                * will transmit.
                 */
                virtio_transport_deliver_tap_pkt(pkt);
 
+               vhost_add_used(vq, head, sizeof(pkt->hdr) + payload_len);
+               added = true;
+
                pkt->off += payload_len;
                total_len += payload_len;
 
@@ -196,6 +196,12 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
                 * to send it with the next available buffer.
                 */
                if (pkt->off < pkt->len) {
+                       /* We are queueing the same virtio_vsock_pkt to handle
+                        * the remaining bytes, and we want to deliver it
+                        * to monitoring devices in the next iteration.
+                        */
+                       pkt->tap_delivered = false;
+
                        spin_lock_bh(&vsock->send_pkt_list_lock);
                        list_add(&pkt->list, &vsock->send_pkt_list);
                        spin_unlock_bh(&vsock->send_pkt_list_lock);
@@ -440,7 +446,7 @@ static void vhost_vsock_handle_tx_kick(struct vhost_work *work)
 
        mutex_lock(&vq->mutex);
 
-       if (!vq->private_data)
+       if (!vhost_vq_get_backend(vq))
                goto out;
 
        vhost_disable_notify(&vsock->dev, vq);
@@ -533,8 +539,8 @@ static int vhost_vsock_start(struct vhost_vsock *vsock)
                        goto err_vq;
                }
 
-               if (!vq->private_data) {
-                       vq->private_data = vsock;
+               if (!vhost_vq_get_backend(vq)) {
+                       vhost_vq_set_backend(vq, vsock);
                        ret = vhost_vq_init_access(vq);
                        if (ret)
                                goto err_vq;
@@ -543,18 +549,23 @@ static int vhost_vsock_start(struct vhost_vsock *vsock)
                mutex_unlock(&vq->mutex);
        }
 
+       /* Some packets may have been queued before the device was started,
+        * let's kick the send worker to send them.
+        */
+       vhost_work_queue(&vsock->dev, &vsock->send_pkt_work);
+
        mutex_unlock(&vsock->dev.mutex);
        return 0;
 
 err_vq:
-       vq->private_data = NULL;
+       vhost_vq_set_backend(vq, NULL);
        mutex_unlock(&vq->mutex);
 
        for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) {
                vq = &vsock->vqs[i];
 
                mutex_lock(&vq->mutex);
-               vq->private_data = NULL;
+               vhost_vq_set_backend(vq, NULL);
                mutex_unlock(&vq->mutex);
        }
 err:
@@ -577,7 +588,7 @@ static int vhost_vsock_stop(struct vhost_vsock *vsock)
                struct vhost_virtqueue *vq = &vsock->vqs[i];
 
                mutex_lock(&vq->mutex);
-               vq->private_data = NULL;
+               vhost_vq_set_backend(vq, NULL);
                mutex_unlock(&vq->mutex);
        }
 
index 2bbf94b..69a32df 100644 (file)
@@ -45,7 +45,7 @@ config VIRTIO_PCI_LEGACY
 
 config VIRTIO_VDPA
        tristate "vDPA driver for virtio devices"
-       select VDPA
+       depends on VDPA
        select VIRTIO
        help
          This driver provides support for virtio based paravirtual
index 0ef1656..51086a5 100644 (file)
@@ -165,7 +165,7 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq)
 
 }
 
-int virtballoon_free_page_report(struct page_reporting_dev_info *pr_dev_info,
+static int virtballoon_free_page_report(struct page_reporting_dev_info *pr_dev_info,
                                   struct scatterlist *sg, unsigned int nents)
 {
        struct virtio_balloon *vb =
@@ -580,7 +580,7 @@ static u32 virtio_balloon_cmd_id_received(struct virtio_balloon *vb)
        if (test_and_clear_bit(VIRTIO_BALLOON_CONFIG_READ_CMD_ID,
                               &vb->config_read_bitmap))
                virtio_cread(vb->vdev, struct virtio_balloon_config,
-                            free_page_report_cmd_id,
+                            free_page_hint_cmd_id,
                             &vb->cmd_id_received_cache);
 
        return vb->cmd_id_received_cache;
index 5ae5296..efaf65b 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/virtio.h>
 #include <linux/virtio_config.h>
 #include <linux/input.h>
+#include <linux/slab.h>
 
 #include <uapi/linux/virtio_ids.h>
 #include <uapi/linux/virtio_input.h>
index 53e0492..190d26e 100644 (file)
@@ -137,10 +137,14 @@ wdt_restart(struct watchdog_device *wdd, unsigned long mode, void *cmd)
 {
        struct sp805_wdt *wdt = watchdog_get_drvdata(wdd);
 
+       writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
        writel_relaxed(0, wdt->base + WDTCONTROL);
        writel_relaxed(0, wdt->base + WDTLOAD);
        writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
 
+       /* Flush posted writes. */
+       readl_relaxed(wdt->base + WDTLOCK);
+
        return 0;
 }
 
index 3858432..040d2a4 100644 (file)
@@ -448,7 +448,14 @@ EXPORT_SYMBOL_GPL(xenbus_free_evtchn);
 int xenbus_map_ring_valloc(struct xenbus_device *dev, grant_ref_t *gnt_refs,
                           unsigned int nr_grefs, void **vaddr)
 {
-       return ring_ops->map(dev, gnt_refs, nr_grefs, vaddr);
+       int err;
+
+       err = ring_ops->map(dev, gnt_refs, nr_grefs, vaddr);
+       /* Some hypervisors are buggy and can return 1. */
+       if (err > 0)
+               err = GNTST_general_error;
+
+       return err;
 }
 EXPORT_SYMBOL_GPL(xenbus_map_ring_valloc);
 
index 6765949..380ad5a 100644 (file)
@@ -169,7 +169,7 @@ static int afs_record_cm_probe(struct afs_call *call, struct afs_server *server)
 
        spin_lock(&server->probe_lock);
 
-       if (!test_bit(AFS_SERVER_FL_HAVE_EPOCH, &server->flags)) {
+       if (!test_and_set_bit(AFS_SERVER_FL_HAVE_EPOCH, &server->flags)) {
                server->cm_epoch = call->epoch;
                server->probe.cm_epoch = call->epoch;
                goto out;
index 5c794f4..d1e1caa 100644 (file)
@@ -1032,7 +1032,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
        struct dentry *parent;
        struct inode *inode;
        struct key *key;
-       afs_dataversion_t dir_version;
+       afs_dataversion_t dir_version, invalid_before;
        long de_version;
        int ret;
 
@@ -1084,8 +1084,8 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
        if (de_version == (long)dir_version)
                goto out_valid_noupdate;
 
-       dir_version = dir->invalid_before;
-       if (de_version - (long)dir_version >= 0)
+       invalid_before = dir->invalid_before;
+       if (de_version - (long)invalid_before >= 0)
                goto out_valid;
 
        _debug("dir modified");
@@ -1275,6 +1275,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
        struct afs_fs_cursor fc;
        struct afs_vnode *dvnode = AFS_FS_I(dir);
        struct key *key;
+       afs_dataversion_t data_version;
        int ret;
 
        mode |= S_IFDIR;
@@ -1295,7 +1296,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
-               afs_dataversion_t data_version = dvnode->status.data_version + 1;
+               data_version = dvnode->status.data_version + 1;
 
                while (afs_select_fileserver(&fc)) {
                        fc.cb_break = afs_calc_vnode_cb_break(dvnode);
@@ -1316,10 +1317,14 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
                goto error_key;
        }
 
-       if (ret == 0 &&
-           test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
-               afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid,
-                                afs_edit_dir_for_create);
+       if (ret == 0) {
+               down_write(&dvnode->validate_lock);
+               if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
+                   dvnode->status.data_version == data_version)
+                       afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid,
+                                        afs_edit_dir_for_create);
+               up_write(&dvnode->validate_lock);
+       }
 
        key_put(key);
        kfree(scb);
@@ -1360,6 +1365,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
        struct afs_fs_cursor fc;
        struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL;
        struct key *key;
+       afs_dataversion_t data_version;
        int ret;
 
        _enter("{%llx:%llu},{%pd}",
@@ -1391,7 +1397,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
 
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
-               afs_dataversion_t data_version = dvnode->status.data_version + 1;
+               data_version = dvnode->status.data_version + 1;
 
                while (afs_select_fileserver(&fc)) {
                        fc.cb_break = afs_calc_vnode_cb_break(dvnode);
@@ -1404,9 +1410,12 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
                ret = afs_end_vnode_operation(&fc);
                if (ret == 0) {
                        afs_dir_remove_subdir(dentry);
-                       if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
+                       down_write(&dvnode->validate_lock);
+                       if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
+                           dvnode->status.data_version == data_version)
                                afs_edit_dir_remove(dvnode, &dentry->d_name,
                                                    afs_edit_dir_for_rmdir);
+                       up_write(&dvnode->validate_lock);
                }
        }
 
@@ -1544,10 +1553,15 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
                ret = afs_end_vnode_operation(&fc);
                if (ret == 0 && !(scb[1].have_status || scb[1].have_error))
                        ret = afs_dir_remove_link(dvnode, dentry, key);
-               if (ret == 0 &&
-                   test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
-                       afs_edit_dir_remove(dvnode, &dentry->d_name,
-                                           afs_edit_dir_for_unlink);
+
+               if (ret == 0) {
+                       down_write(&dvnode->validate_lock);
+                       if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
+                           dvnode->status.data_version == data_version)
+                               afs_edit_dir_remove(dvnode, &dentry->d_name,
+                                                   afs_edit_dir_for_unlink);
+                       up_write(&dvnode->validate_lock);
+               }
        }
 
        if (need_rehash && ret < 0 && ret != -ENOENT)
@@ -1573,6 +1587,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
        struct afs_status_cb *scb;
        struct afs_vnode *dvnode = AFS_FS_I(dir);
        struct key *key;
+       afs_dataversion_t data_version;
        int ret;
 
        mode |= S_IFREG;
@@ -1597,7 +1612,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
 
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
-               afs_dataversion_t data_version = dvnode->status.data_version + 1;
+               data_version = dvnode->status.data_version + 1;
 
                while (afs_select_fileserver(&fc)) {
                        fc.cb_break = afs_calc_vnode_cb_break(dvnode);
@@ -1618,9 +1633,12 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
                goto error_key;
        }
 
-       if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
+       down_write(&dvnode->validate_lock);
+       if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
+           dvnode->status.data_version == data_version)
                afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid,
                                 afs_edit_dir_for_create);
+       up_write(&dvnode->validate_lock);
 
        kfree(scb);
        key_put(key);
@@ -1648,6 +1666,7 @@ static int afs_link(struct dentry *from, struct inode *dir,
        struct afs_vnode *dvnode = AFS_FS_I(dir);
        struct afs_vnode *vnode = AFS_FS_I(d_inode(from));
        struct key *key;
+       afs_dataversion_t data_version;
        int ret;
 
        _enter("{%llx:%llu},{%llx:%llu},{%pd}",
@@ -1672,7 +1691,7 @@ static int afs_link(struct dentry *from, struct inode *dir,
 
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
-               afs_dataversion_t data_version = dvnode->status.data_version + 1;
+               data_version = dvnode->status.data_version + 1;
 
                if (mutex_lock_interruptible_nested(&vnode->io_lock, 1) < 0) {
                        afs_end_vnode_operation(&fc);
@@ -1702,9 +1721,12 @@ static int afs_link(struct dentry *from, struct inode *dir,
                goto error_key;
        }
 
-       if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
+       down_write(&dvnode->validate_lock);
+       if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
+           dvnode->status.data_version == data_version)
                afs_edit_dir_add(dvnode, &dentry->d_name, &vnode->fid,
                                 afs_edit_dir_for_link);
+       up_write(&dvnode->validate_lock);
 
        key_put(key);
        kfree(scb);
@@ -1732,6 +1754,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
        struct afs_status_cb *scb;
        struct afs_vnode *dvnode = AFS_FS_I(dir);
        struct key *key;
+       afs_dataversion_t data_version;
        int ret;
 
        _enter("{%llx:%llu},{%pd},%s",
@@ -1759,7 +1782,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
 
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
-               afs_dataversion_t data_version = dvnode->status.data_version + 1;
+               data_version = dvnode->status.data_version + 1;
 
                while (afs_select_fileserver(&fc)) {
                        fc.cb_break = afs_calc_vnode_cb_break(dvnode);
@@ -1780,9 +1803,12 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
                goto error_key;
        }
 
-       if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
+       down_write(&dvnode->validate_lock);
+       if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
+           dvnode->status.data_version == data_version)
                afs_edit_dir_add(dvnode, &dentry->d_name, &iget_data.fid,
                                 afs_edit_dir_for_symlink);
+       up_write(&dvnode->validate_lock);
 
        key_put(key);
        kfree(scb);
@@ -1812,6 +1838,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct dentry *tmp = NULL, *rehash = NULL;
        struct inode *new_inode;
        struct key *key;
+       afs_dataversion_t orig_data_version;
+       afs_dataversion_t new_data_version;
        bool new_negative = d_is_negative(new_dentry);
        int ret;
 
@@ -1890,10 +1918,6 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
 
        ret = -ERESTARTSYS;
        if (afs_begin_vnode_operation(&fc, orig_dvnode, key, true)) {
-               afs_dataversion_t orig_data_version;
-               afs_dataversion_t new_data_version;
-               struct afs_status_cb *new_scb = &scb[1];
-
                orig_data_version = orig_dvnode->status.data_version + 1;
 
                if (orig_dvnode != new_dvnode) {
@@ -1904,7 +1928,6 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        new_data_version = new_dvnode->status.data_version + 1;
                } else {
                        new_data_version = orig_data_version;
-                       new_scb = &scb[0];
                }
 
                while (afs_select_fileserver(&fc)) {
@@ -1912,7 +1935,7 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        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,
-                                     &scb[0], new_scb);
+                                     &scb[0], &scb[1]);
                }
 
                afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break,
@@ -1930,18 +1953,25 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
        if (ret == 0) {
                if (rehash)
                        d_rehash(rehash);
-               if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags))
-                   afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name,
-                                       afs_edit_dir_for_rename_0);
+               down_write(&orig_dvnode->validate_lock);
+               if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags) &&
+                   orig_dvnode->status.data_version == orig_data_version)
+                       afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name,
+                                           afs_edit_dir_for_rename_0);
+               if (orig_dvnode != new_dvnode) {
+                       up_write(&orig_dvnode->validate_lock);
 
-               if (!new_negative &&
-                   test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags))
-                       afs_edit_dir_remove(new_dvnode, &new_dentry->d_name,
-                                           afs_edit_dir_for_rename_1);
+                       down_write(&new_dvnode->validate_lock);
+               }
+               if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags) &&
+                   orig_dvnode->status.data_version == new_data_version) {
+                       if (!new_negative)
+                               afs_edit_dir_remove(new_dvnode, &new_dentry->d_name,
+                                                   afs_edit_dir_for_rename_1);
 
-               if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags))
                        afs_edit_dir_add(new_dvnode, &new_dentry->d_name,
                                         &vnode->fid, afs_edit_dir_for_rename_2);
+               }
 
                new_inode = d_inode(new_dentry);
                if (new_inode) {
@@ -1957,14 +1987,10 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
                 * Note that if we ever implement RENAME_EXCHANGE, we'll have
                 * to update both dentries with opposing dir versions.
                 */
-               if (new_dvnode != orig_dvnode) {
-                       afs_update_dentry_version(&fc, old_dentry, &scb[1]);
-                       afs_update_dentry_version(&fc, new_dentry, &scb[1]);
-               } else {
-                       afs_update_dentry_version(&fc, old_dentry, &scb[0]);
-                       afs_update_dentry_version(&fc, new_dentry, &scb[0]);
-               }
+               afs_update_dentry_version(&fc, old_dentry, &scb[1]);
+               afs_update_dentry_version(&fc, new_dentry, &scb[1]);
                d_move(old_dentry, new_dentry);
+               up_write(&new_dvnode->validate_lock);
                goto error_tmp;
        }
 
index 361088a..d94e2b7 100644 (file)
@@ -21,6 +21,7 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
 {
        struct afs_fs_cursor fc;
        struct afs_status_cb *scb;
+       afs_dataversion_t dir_data_version;
        int ret = -ERESTARTSYS;
 
        _enter("%pd,%pd", old, new);
@@ -31,7 +32,7 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
 
        trace_afs_silly_rename(vnode, false);
        if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
-               afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
+               dir_data_version = dvnode->status.data_version + 1;
 
                while (afs_select_fileserver(&fc)) {
                        fc.cb_break = afs_calc_vnode_cb_break(dvnode);
@@ -54,12 +55,15 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
                        dvnode->silly_key = key_get(key);
                }
 
-               if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
+               down_write(&dvnode->validate_lock);
+               if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
+                   dvnode->status.data_version == dir_data_version) {
                        afs_edit_dir_remove(dvnode, &old->d_name,
                                            afs_edit_dir_for_silly_0);
-               if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
                        afs_edit_dir_add(dvnode, &new->d_name,
                                         &vnode->fid, afs_edit_dir_for_silly_1);
+               }
+               up_write(&dvnode->validate_lock);
        }
 
        kfree(scb);
@@ -181,10 +185,14 @@ static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode
                                clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
                        }
                }
-               if (ret == 0 &&
-                   test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
-                       afs_edit_dir_remove(dvnode, &dentry->d_name,
-                                           afs_edit_dir_for_unlink);
+               if (ret == 0) {
+                       down_write(&dvnode->validate_lock);
+                       if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
+                           dvnode->status.data_version == dir_data_version)
+                               afs_edit_dir_remove(dvnode, &dentry->d_name,
+                                                   afs_edit_dir_for_unlink);
+                       up_write(&dvnode->validate_lock);
+               }
        }
 
        kfree(scb);
index e1b9ed6..a587767 100644 (file)
@@ -117,11 +117,8 @@ out:
               (unsigned int)rtt, ret);
 
        have_result |= afs_fs_probe_done(server);
-       if (have_result) {
-               server->probe.have_result = true;
-               wake_up_var(&server->probe.have_result);
+       if (have_result)
                wake_up_all(&server->probe_wq);
-       }
 }
 
 /*
index 1f9c5d8..68fc466 100644 (file)
@@ -65,6 +65,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp,
        bool inline_error = (call->operation_ID == afs_FS_InlineBulkStatus);
        u64 data_version, size;
        u32 type, abort_code;
+       int ret;
 
        abort_code = ntohl(xdr->abort_code);
 
@@ -78,7 +79,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp,
                         */
                        status->abort_code = abort_code;
                        scb->have_error = true;
-                       return 0;
+                       goto good;
                }
 
                pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version));
@@ -87,7 +88,8 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp,
 
        if (abort_code != 0 && inline_error) {
                status->abort_code = abort_code;
-               return 0;
+               scb->have_error = true;
+               goto good;
        }
 
        type = ntohl(xdr->type);
@@ -123,13 +125,16 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp,
        data_version |= (u64)ntohl(xdr->data_version_hi) << 32;
        status->data_version = data_version;
        scb->have_status = true;
-
+good:
+       ret = 0;
+advance:
        *_bp = (const void *)*_bp + sizeof(*xdr);
-       return 0;
+       return ret;
 
 bad:
        xdr_dump_bad(*_bp);
-       return afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status);
+       ret = afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status);
+       goto advance;
 }
 
 static time64_t xdr_decode_expiry(struct afs_call *call, u32 expiry)
@@ -981,16 +986,16 @@ static int afs_deliver_fs_rename(struct afs_call *call)
        if (ret < 0)
                return ret;
 
-       /* unmarshall the reply once we've received all of it */
+       /* If the two dirs are the same, we have two copies of the same status
+        * report, so we just decode it twice.
+        */
        bp = call->buffer;
        ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_dir_scb);
        if (ret < 0)
                return ret;
-       if (call->out_dir_scb != call->out_scb) {
-               ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb);
-               if (ret < 0)
-                       return ret;
-       }
+       ret = xdr_decode_AFSFetchStatus(&bp, call, call->out_scb);
+       if (ret < 0)
+               return ret;
        xdr_decode_AFSVolSync(&bp, call->out_volsync);
 
        _leave(" = 0 [done]");
index ef732dd..8025551 100644 (file)
@@ -533,12 +533,10 @@ struct afs_server {
                u32             abort_code;
                u32             cm_epoch;
                short           error;
-               bool            have_result;
                bool            responded:1;
                bool            is_yfs:1;
                bool            not_yfs:1;
                bool            local_failure:1;
-               bool            no_epoch:1;
                bool            cm_probed:1;
                bool            said_rebooted:1;
                bool            said_inconsistent:1;
@@ -1335,7 +1333,7 @@ extern struct afs_volume *afs_create_volume(struct afs_fs_context *);
 extern void afs_activate_volume(struct afs_volume *);
 extern void afs_deactivate_volume(struct afs_volume *);
 extern void afs_put_volume(struct afs_cell *, struct afs_volume *);
-extern int afs_check_volume_status(struct afs_volume *, struct key *);
+extern int afs_check_volume_status(struct afs_volume *, struct afs_fs_cursor *);
 
 /*
  * write.c
index 172ba56..2a3305e 100644 (file)
@@ -192,7 +192,7 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
                        write_unlock(&vnode->volume->servers_lock);
 
                        set_bit(AFS_VOLUME_NEEDS_UPDATE, &vnode->volume->flags);
-                       error = afs_check_volume_status(vnode->volume, fc->key);
+                       error = afs_check_volume_status(vnode->volume, fc);
                        if (error < 0)
                                goto failed_set_error;
 
@@ -281,7 +281,7 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
 
                        set_bit(AFS_VOLUME_WAIT, &vnode->volume->flags);
                        set_bit(AFS_VOLUME_NEEDS_UPDATE, &vnode->volume->flags);
-                       error = afs_check_volume_status(vnode->volume, fc->key);
+                       error = afs_check_volume_status(vnode->volume, fc);
                        if (error < 0)
                                goto failed_set_error;
 
@@ -341,7 +341,7 @@ start:
        /* See if we need to do an update of the volume record.  Note that the
         * volume may have moved or even have been deleted.
         */
-       error = afs_check_volume_status(vnode->volume, fc->key);
+       error = afs_check_volume_status(vnode->volume, fc);
        if (error < 0)
                goto failed_set_error;
 
index b7f3cb2..11b90ac 100644 (file)
@@ -594,12 +594,9 @@ retry:
        }
 
        ret = wait_on_bit(&server->flags, AFS_SERVER_FL_UPDATING,
-                         TASK_INTERRUPTIBLE);
+                         (fc->flags & AFS_FS_CURSOR_INTR) ?
+                         TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
        if (ret == -ERESTARTSYS) {
-               if (!(fc->flags & AFS_FS_CURSOR_INTR) && server->addresses) {
-                       _leave(" = t [intr]");
-                       return true;
-               }
                fc->error = ret;
                _leave(" = f [intr]");
                return false;
index 9a5ce96..72eacc1 100644 (file)
@@ -302,8 +302,8 @@ static void afs_vl_dump_edestaddrreq(const struct afs_vl_cursor *vc)
                                pr_notice("VC:  - nr=%u/%u/%u pf=%u\n",
                                          a->nr_ipv4, a->nr_addrs, a->max_addrs,
                                          a->preferred);
-                               pr_notice("VC:  - pr=%lx R=%lx F=%lx\n",
-                                         a->probed, a->responded, a->failed);
+                               pr_notice("VC:  - R=%lx F=%lx\n",
+                                         a->responded, a->failed);
                                if (a == vc->ac.alist)
                                        pr_notice("VC:  - current\n");
                        }
index 92ca5e2..4310336 100644 (file)
@@ -281,7 +281,7 @@ error:
 /*
  * Make sure the volume record is up to date.
  */
-int afs_check_volume_status(struct afs_volume *volume, struct key *key)
+int afs_check_volume_status(struct afs_volume *volume, struct afs_fs_cursor *fc)
 {
        time64_t now = ktime_get_real_seconds();
        int ret, retries = 0;
@@ -299,7 +299,7 @@ retry:
        }
 
        if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
-               ret = afs_update_volume_status(volume, key);
+               ret = afs_update_volume_status(volume, fc->key);
                clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags);
                clear_bit_unlock(AFS_VOLUME_UPDATING, &volume->flags);
                wake_up_bit(&volume->flags, AFS_VOLUME_WAIT);
@@ -312,7 +312,9 @@ retry:
                return 0;
        }
 
-       ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT, TASK_INTERRUPTIBLE);
+       ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT,
+                         (fc->flags & AFS_FS_CURSOR_INTR) ?
+                         TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
        if (ret == -ERESTARTSYS) {
                _leave(" = %d", ret);
                return ret;
index a26126a..b5b45c5 100644 (file)
@@ -165,15 +165,15 @@ static void xdr_dump_bad(const __be32 *bp)
        int i;
 
        pr_notice("YFS XDR: Bad status record\n");
-       for (i = 0; i < 5 * 4 * 4; i += 16) {
+       for (i = 0; i < 6 * 4 * 4; i += 16) {
                memcpy(x, bp, 16);
                bp += 4;
                pr_notice("%03x: %08x %08x %08x %08x\n",
                          i, ntohl(x[0]), ntohl(x[1]), ntohl(x[2]), ntohl(x[3]));
        }
 
-       memcpy(x, bp, 4);
-       pr_notice("0x50: %08x\n", ntohl(x[0]));
+       memcpy(x, bp, 8);
+       pr_notice("0x60: %08x %08x\n", ntohl(x[0]), ntohl(x[1]));
 }
 
 /*
@@ -186,13 +186,14 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp,
        const struct yfs_xdr_YFSFetchStatus *xdr = (const void *)*_bp;
        struct afs_file_status *status = &scb->status;
        u32 type;
+       int ret;
 
        status->abort_code = ntohl(xdr->abort_code);
        if (status->abort_code != 0) {
                if (status->abort_code == VNOVNODE)
                        status->nlink = 0;
                scb->have_error = true;
-               return 0;
+               goto good;
        }
 
        type = ntohl(xdr->type);
@@ -220,13 +221,16 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp,
        status->size            = xdr_to_u64(xdr->size);
        status->data_version    = xdr_to_u64(xdr->data_version);
        scb->have_status        = true;
-
+good:
+       ret = 0;
+advance:
        *_bp += xdr_size(xdr);
-       return 0;
+       return ret;
 
 bad:
        xdr_dump_bad(*_bp);
-       return afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status);
+       ret = afs_protocol_error(call, -EBADMSG, afs_eproto_bad_status);
+       goto advance;
 }
 
 /*
@@ -1153,11 +1157,9 @@ static int yfs_deliver_fs_rename(struct afs_call *call)
        ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb);
        if (ret < 0)
                return ret;
-       if (call->out_dir_scb != call->out_scb) {
-               ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb);
-               if (ret < 0)
-                       return ret;
-       }
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb);
+       if (ret < 0)
+               return ret;
 
        xdr_decode_YFSVolSync(&bp, call->out_volsync);
        _leave(" = 0 [done]");
index 52b6f64..93672c3 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/module.h>
 #include <linux/blkpg.h>
 #include <linux/magic.h>
-#include <linux/dax.h>
 #include <linux/buffer_head.h>
 #include <linux/swap.h>
 #include <linux/pagevec.h>
@@ -1893,6 +1892,16 @@ static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part)
        struct gendisk *disk = bdev->bd_disk;
        struct block_device *victim = NULL;
 
+       /*
+        * Sync early if it looks like we're the last one.  If someone else
+        * opens the block device between now and the decrement of bd_openers
+        * then we did a sync that we didn't need to, but that's not the end
+        * of the world and we want to avoid long (could be several minute)
+        * syncs while holding the mutex.
+        */
+       if (bdev->bd_openers == 1)
+               sync_blockdev(bdev);
+
        mutex_lock_nested(&bdev->bd_mutex, for_part);
        if (for_part)
                bdev->bd_part_count--;
index 9c380e7..0cc0257 100644 (file)
@@ -391,7 +391,7 @@ static int is_shared_data_backref(struct preftrees *preftrees, u64 bytenr)
        struct rb_node **p = &preftrees->direct.root.rb_root.rb_node;
        struct rb_node *parent = NULL;
        struct prelim_ref *ref = NULL;
-       struct prelim_ref target = {0};
+       struct prelim_ref target = {};
        int result;
 
        target.parent = bytenr;
index 786849f..696f471 100644 (file)
@@ -916,7 +916,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
        path = btrfs_alloc_path();
        if (!path) {
                ret = -ENOMEM;
-               goto out;
+               goto out_put_group;
        }
 
        /*
@@ -954,7 +954,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
                ret = btrfs_orphan_add(trans, BTRFS_I(inode));
                if (ret) {
                        btrfs_add_delayed_iput(inode);
-                       goto out;
+                       goto out_put_group;
                }
                clear_nlink(inode);
                /* One for the block groups ref */
@@ -977,13 +977,13 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
 
        ret = btrfs_search_slot(trans, tree_root, &key, path, -1, 1);
        if (ret < 0)
-               goto out;
+               goto out_put_group;
        if (ret > 0)
                btrfs_release_path(path);
        if (ret == 0) {
                ret = btrfs_del_item(trans, tree_root, path);
                if (ret)
-                       goto out;
+                       goto out_put_group;
                btrfs_release_path(path);
        }
 
@@ -1102,9 +1102,9 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
 
        ret = remove_block_group_free_space(trans, block_group);
        if (ret)
-               goto out;
+               goto out_put_group;
 
-       btrfs_put_block_group(block_group);
+       /* Once for the block groups rbtree */
        btrfs_put_block_group(block_group);
 
        ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
@@ -1127,6 +1127,10 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
                /* once for the tree */
                free_extent_map(em);
        }
+
+out_put_group:
+       /* Once for the lookup reference */
+       btrfs_put_block_group(block_group);
 out:
        if (remove_rsv)
                btrfs_delayed_refs_rsv_release(fs_info, 1);
@@ -1288,11 +1292,15 @@ static bool clean_pinned_extents(struct btrfs_trans_handle *trans,
        if (ret)
                goto err;
        mutex_unlock(&fs_info->unused_bg_unpin_mutex);
+       if (prev_trans)
+               btrfs_put_transaction(prev_trans);
 
        return true;
 
 err:
        mutex_unlock(&fs_info->unused_bg_unpin_mutex);
+       if (prev_trans)
+               btrfs_put_transaction(prev_trans);
        btrfs_dec_block_group_ro(bg);
        return false;
 }
@@ -3370,6 +3378,7 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
                            space_info->bytes_reserved > 0 ||
                            space_info->bytes_may_use > 0))
                        btrfs_dump_space_info(info, space_info, 0, 0);
+               WARN_ON(space_info->reclaim_size > 0);
                list_del(&space_info->list);
                btrfs_sysfs_remove_space_info(space_info);
        }
index 21a1577..353228d 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 
 #ifndef BTRFS_DISCARD_H
 #define BTRFS_DISCARD_H
index a6cb5cb..d10c7be 100644 (file)
@@ -2036,9 +2036,6 @@ void btrfs_free_fs_roots(struct btrfs_fs_info *fs_info)
                for (i = 0; i < ret; i++)
                        btrfs_drop_and_free_fs_root(fs_info, gang[i]);
        }
-
-       if (test_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state))
-               btrfs_free_log_root_tree(NULL, fs_info);
 }
 
 static void btrfs_init_scrub(struct btrfs_fs_info *fs_info)
@@ -3888,7 +3885,7 @@ void btrfs_drop_and_free_fs_root(struct btrfs_fs_info *fs_info,
        spin_unlock(&fs_info->fs_roots_radix_lock);
 
        if (test_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state)) {
-               btrfs_free_log(NULL, root);
+               ASSERT(root->log_root == NULL);
                if (root->reloc_root) {
                        btrfs_put_root(root->reloc_root);
                        root->reloc_root = NULL;
@@ -4211,6 +4208,36 @@ static void btrfs_error_commit_super(struct btrfs_fs_info *fs_info)
        up_write(&fs_info->cleanup_work_sem);
 }
 
+static void btrfs_drop_all_logs(struct btrfs_fs_info *fs_info)
+{
+       struct btrfs_root *gang[8];
+       u64 root_objectid = 0;
+       int ret;
+
+       spin_lock(&fs_info->fs_roots_radix_lock);
+       while ((ret = radix_tree_gang_lookup(&fs_info->fs_roots_radix,
+                                            (void **)gang, root_objectid,
+                                            ARRAY_SIZE(gang))) != 0) {
+               int i;
+
+               for (i = 0; i < ret; i++)
+                       gang[i] = btrfs_grab_root(gang[i]);
+               spin_unlock(&fs_info->fs_roots_radix_lock);
+
+               for (i = 0; i < ret; i++) {
+                       if (!gang[i])
+                               continue;
+                       root_objectid = gang[i]->root_key.objectid;
+                       btrfs_free_log(NULL, gang[i]);
+                       btrfs_put_root(gang[i]);
+               }
+               root_objectid++;
+               spin_lock(&fs_info->fs_roots_radix_lock);
+       }
+       spin_unlock(&fs_info->fs_roots_radix_lock);
+       btrfs_free_log_root_tree(NULL, fs_info);
+}
+
 static void btrfs_destroy_ordered_extents(struct btrfs_root *root)
 {
        struct btrfs_ordered_extent *ordered;
@@ -4603,6 +4630,7 @@ static int btrfs_cleanup_transaction(struct btrfs_fs_info *fs_info)
        btrfs_destroy_delayed_inodes(fs_info);
        btrfs_assert_delayed_root_empty(fs_info);
        btrfs_destroy_all_delalloc_inodes(fs_info);
+       btrfs_drop_all_logs(fs_info);
        mutex_unlock(&fs_info->transaction_kthread_mutex);
 
        return 0;
index 8a144f9..719e68a 100644 (file)
@@ -2097,6 +2097,21 @@ int btrfs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
 
        atomic_inc(&root->log_batch);
 
+       /*
+        * If the inode needs a full sync, make sure we use a full range to
+        * avoid log tree corruption, due to hole detection racing with ordered
+        * extent completion for adjacent ranges and races between logging and
+        * completion of ordered extents for adjancent ranges - both races
+        * could lead to file extent items in the log with overlapping ranges.
+        * Do this while holding the inode lock, to avoid races with other
+        * tasks.
+        */
+       if (test_bit(BTRFS_INODE_NEEDS_FULL_SYNC,
+                    &BTRFS_I(inode)->runtime_flags)) {
+               start = 0;
+               end = LLONG_MAX;
+       }
+
        /*
         * Before we acquired the inode's lock, someone may have dirtied more
         * pages in the target range. We need to make sure that writeback for
index d197314..040009d 100644 (file)
@@ -264,6 +264,7 @@ copy_inline_extent:
                            size);
        inode_add_bytes(dst, datal);
        set_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &BTRFS_I(dst)->runtime_flags);
+       ret = btrfs_inode_set_file_extent_range(BTRFS_I(dst), 0, aligned_end);
 out:
        if (!ret && !trans) {
                /*
index f655956..03bc713 100644 (file)
@@ -611,8 +611,8 @@ static int should_ignore_root(struct btrfs_root *root)
        if (!reloc_root)
                return 0;
 
-       if (btrfs_root_last_snapshot(&reloc_root->root_item) ==
-           root->fs_info->running_transaction->transid - 1)
+       if (btrfs_header_generation(reloc_root->commit_root) ==
+           root->fs_info->running_transaction->transid)
                return 0;
        /*
         * if there is reloc tree and it was created in previous
@@ -1527,8 +1527,7 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
        int clear_rsv = 0;
        int ret;
 
-       if (!rc || !rc->create_reloc_tree ||
-           root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
+       if (!rc)
                return 0;
 
        /*
@@ -1538,12 +1537,28 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle *trans,
        if (reloc_root_is_dead(root))
                return 0;
 
+       /*
+        * This is subtle but important.  We do not do
+        * record_root_in_transaction for reloc roots, instead we record their
+        * corresponding fs root, and then here we update the last trans for the
+        * reloc root.  This means that we have to do this for the entire life
+        * of the reloc root, regardless of which stage of the relocation we are
+        * in.
+        */
        if (root->reloc_root) {
                reloc_root = root->reloc_root;
                reloc_root->last_trans = trans->transid;
                return 0;
        }
 
+       /*
+        * We are merging reloc roots, we do not need new reloc trees.  Also
+        * reloc trees never need their own reloc tree.
+        */
+       if (!rc->create_reloc_tree ||
+           root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
+               return 0;
+
        if (!trans->reloc_reserved) {
                rsv = trans->block_rsv;
                trans->block_rsv = rc->block_rsv;
@@ -4544,6 +4559,7 @@ int btrfs_recover_relocation(struct btrfs_root *root)
                if (IS_ERR(fs_root)) {
                        err = PTR_ERR(fs_root);
                        list_add_tail(&reloc_root->root_list, &reloc_roots);
+                       btrfs_end_transaction(trans);
                        goto out_unset;
                }
 
index 8b0fe05..ff17a44 100644 (file)
@@ -361,6 +361,16 @@ int btrfs_can_overcommit(struct btrfs_fs_info *fs_info,
        return 0;
 }
 
+static void remove_ticket(struct btrfs_space_info *space_info,
+                         struct reserve_ticket *ticket)
+{
+       if (!list_empty(&ticket->list)) {
+               list_del_init(&ticket->list);
+               ASSERT(space_info->reclaim_size >= ticket->bytes);
+               space_info->reclaim_size -= ticket->bytes;
+       }
+}
+
 /*
  * This is for space we already have accounted in space_info->bytes_may_use, so
  * basically when we're returning space from block_rsv's.
@@ -388,9 +398,7 @@ again:
                        btrfs_space_info_update_bytes_may_use(fs_info,
                                                              space_info,
                                                              ticket->bytes);
-                       list_del_init(&ticket->list);
-                       ASSERT(space_info->reclaim_size >= ticket->bytes);
-                       space_info->reclaim_size -= ticket->bytes;
+                       remove_ticket(space_info, ticket);
                        ticket->bytes = 0;
                        space_info->tickets_id++;
                        wake_up(&ticket->wait);
@@ -899,7 +907,7 @@ static bool maybe_fail_all_tickets(struct btrfs_fs_info *fs_info,
                        btrfs_info(fs_info, "failing ticket with %llu bytes",
                                   ticket->bytes);
 
-               list_del_init(&ticket->list);
+               remove_ticket(space_info, ticket);
                ticket->error = -ENOSPC;
                wake_up(&ticket->wait);
 
@@ -1063,7 +1071,7 @@ static void wait_reserve_ticket(struct btrfs_fs_info *fs_info,
                         * despite getting an error, resulting in a space leak
                         * (bytes_may_use counter of our space_info).
                         */
-                       list_del_init(&ticket->list);
+                       remove_ticket(space_info, ticket);
                        ticket->error = -EINTR;
                        break;
                }
@@ -1121,7 +1129,7 @@ static int handle_reserve_ticket(struct btrfs_fs_info *fs_info,
                 * either the async reclaim job deletes the ticket from the list
                 * or we delete it ourselves at wait_reserve_ticket().
                 */
-               list_del_init(&ticket->list);
+               remove_ticket(space_info, ticket);
                if (!ret)
                        ret = -ENOSPC;
        }
index 8cede6e..2d54981 100644 (file)
@@ -662,10 +662,19 @@ again:
        }
 
 got_it:
-       btrfs_record_root_in_trans(h, root);
-
        if (!current->journal_info)
                current->journal_info = h;
+
+       /*
+        * btrfs_record_root_in_trans() needs to alloc new extents, and may
+        * call btrfs_join_transaction() while we're also starting a
+        * transaction.
+        *
+        * Thus it need to be called after current->journal_info initialized,
+        * or we can deadlock.
+        */
+       btrfs_record_root_in_trans(h, root);
+
        return h;
 
 join_fail:
index 58c1114..02ebdd9 100644 (file)
@@ -96,8 +96,8 @@ enum {
 static int btrfs_log_inode(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root, struct btrfs_inode *inode,
                           int inode_only,
-                          u64 start,
-                          u64 end,
+                          const loff_t start,
+                          const loff_t end,
                           struct btrfs_log_ctx *ctx);
 static int link_to_fixup_dir(struct btrfs_trans_handle *trans,
                             struct btrfs_root *root,
@@ -4226,6 +4226,9 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans,
        const u64 ino = btrfs_ino(inode);
        struct btrfs_path *dst_path = NULL;
        bool dropped_extents = false;
+       u64 truncate_offset = i_size;
+       struct extent_buffer *leaf;
+       int slot;
        int ins_nr = 0;
        int start_slot;
        int ret;
@@ -4240,9 +4243,43 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans,
        if (ret < 0)
                goto out;
 
+       /*
+        * We must check if there is a prealloc extent that starts before the
+        * i_size and crosses the i_size boundary. This is to ensure later we
+        * truncate down to the end of that extent and not to the i_size, as
+        * otherwise we end up losing part of the prealloc extent after a log
+        * replay and with an implicit hole if there is another prealloc extent
+        * that starts at an offset beyond i_size.
+        */
+       ret = btrfs_previous_item(root, path, ino, BTRFS_EXTENT_DATA_KEY);
+       if (ret < 0)
+               goto out;
+
+       if (ret == 0) {
+               struct btrfs_file_extent_item *ei;
+
+               leaf = path->nodes[0];
+               slot = path->slots[0];
+               ei = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
+
+               if (btrfs_file_extent_type(leaf, ei) ==
+                   BTRFS_FILE_EXTENT_PREALLOC) {
+                       u64 extent_end;
+
+                       btrfs_item_key_to_cpu(leaf, &key, slot);
+                       extent_end = key.offset +
+                               btrfs_file_extent_num_bytes(leaf, ei);
+
+                       if (extent_end > i_size)
+                               truncate_offset = extent_end;
+               }
+       } else {
+               ret = 0;
+       }
+
        while (true) {
-               struct extent_buffer *leaf = path->nodes[0];
-               int slot = path->slots[0];
+               leaf = path->nodes[0];
+               slot = path->slots[0];
 
                if (slot >= btrfs_header_nritems(leaf)) {
                        if (ins_nr > 0) {
@@ -4280,7 +4317,7 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans,
                                ret = btrfs_truncate_inode_items(trans,
                                                         root->log_root,
                                                         &inode->vfs_inode,
-                                                        i_size,
+                                                        truncate_offset,
                                                         BTRFS_EXTENT_DATA_KEY);
                        } while (ret == -EAGAIN);
                        if (ret)
@@ -4533,15 +4570,13 @@ static int btrfs_log_all_xattrs(struct btrfs_trans_handle *trans,
 static int btrfs_log_holes(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root,
                           struct btrfs_inode *inode,
-                          struct btrfs_path *path,
-                          const u64 start,
-                          const u64 end)
+                          struct btrfs_path *path)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
        struct btrfs_key key;
        const u64 ino = btrfs_ino(inode);
        const u64 i_size = i_size_read(&inode->vfs_inode);
-       u64 prev_extent_end = start;
+       u64 prev_extent_end = 0;
        int ret;
 
        if (!btrfs_fs_incompat(fs_info, NO_HOLES) || i_size == 0)
@@ -4549,21 +4584,14 @@ static int btrfs_log_holes(struct btrfs_trans_handle *trans,
 
        key.objectid = ino;
        key.type = BTRFS_EXTENT_DATA_KEY;
-       key.offset = start;
+       key.offset = 0;
 
        ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
        if (ret < 0)
                return ret;
 
-       if (ret > 0 && path->slots[0] > 0) {
-               btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0] - 1);
-               if (key.objectid == ino && key.type == BTRFS_EXTENT_DATA_KEY)
-                       path->slots[0]--;
-       }
-
        while (true) {
                struct extent_buffer *leaf = path->nodes[0];
-               u64 extent_end;
 
                if (path->slots[0] >= btrfs_header_nritems(path->nodes[0])) {
                        ret = btrfs_next_leaf(root, path);
@@ -4580,18 +4608,9 @@ static int btrfs_log_holes(struct btrfs_trans_handle *trans,
                if (key.objectid != ino || key.type != BTRFS_EXTENT_DATA_KEY)
                        break;
 
-               extent_end = btrfs_file_extent_end(path);
-               if (extent_end <= start)
-                       goto next_slot;
-
                /* We have a hole, log it. */
                if (prev_extent_end < key.offset) {
-                       u64 hole_len;
-
-                       if (key.offset >= end)
-                               hole_len = end - prev_extent_end;
-                       else
-                               hole_len = key.offset - prev_extent_end;
+                       const u64 hole_len = key.offset - prev_extent_end;
 
                        /*
                         * Release the path to avoid deadlocks with other code
@@ -4621,20 +4640,16 @@ static int btrfs_log_holes(struct btrfs_trans_handle *trans,
                        leaf = path->nodes[0];
                }
 
-               prev_extent_end = min(extent_end, end);
-               if (extent_end >= end)
-                       break;
-next_slot:
+               prev_extent_end = btrfs_file_extent_end(path);
                path->slots[0]++;
                cond_resched();
        }
 
-       if (prev_extent_end < end && prev_extent_end < i_size) {
+       if (prev_extent_end < i_size) {
                u64 hole_len;
 
                btrfs_release_path(path);
-               hole_len = min(ALIGN(i_size, fs_info->sectorsize), end);
-               hole_len -= prev_extent_end;
+               hole_len = ALIGN(i_size - prev_extent_end, fs_info->sectorsize);
                ret = btrfs_insert_file_extent(trans, root->log_root,
                                               ino, prev_extent_end, 0, 0,
                                               hole_len, 0, hole_len,
@@ -4971,8 +4986,6 @@ static int copy_inode_items_to_log(struct btrfs_trans_handle *trans,
                                   const u64 logged_isize,
                                   const bool recursive_logging,
                                   const int inode_only,
-                                  const u64 start,
-                                  const u64 end,
                                   struct btrfs_log_ctx *ctx,
                                   bool *need_log_inode_item)
 {
@@ -4981,21 +4994,6 @@ static int copy_inode_items_to_log(struct btrfs_trans_handle *trans,
        int ins_nr = 0;
        int ret;
 
-       /*
-        * We must make sure we don't copy extent items that are entirely out of
-        * the range [start, end - 1]. This is not just an optimization to avoid
-        * copying but also needed to avoid a corruption where we end up with
-        * file extent items in the log tree that have overlapping ranges - this
-        * can happen if we race with ordered extent completion for ranges that
-        * are outside our target range. For example we copy an extent item and
-        * when we move to the next leaf, that extent was trimmed and a new one
-        * covering a subrange of it, but with a higher key, was inserted - we
-        * would then copy this other extent too, resulting in a log tree with
-        * 2 extent items that represent overlapping ranges.
-        *
-        * We can copy the entire extents at the range bondaries however, even
-        * if they cover an area outside the target range. That's ok.
-        */
        while (1) {
                ret = btrfs_search_forward(root, min_key, path, trans->transid);
                if (ret < 0)
@@ -5063,29 +5061,6 @@ again:
                        goto next_slot;
                }
 
-               if (min_key->type == BTRFS_EXTENT_DATA_KEY) {
-                       const u64 extent_end = btrfs_file_extent_end(path);
-
-                       if (extent_end <= start) {
-                               if (ins_nr > 0) {
-                                       ret = copy_items(trans, inode, dst_path,
-                                                        path, ins_start_slot,
-                                                        ins_nr, inode_only,
-                                                        logged_isize);
-                                       if (ret < 0)
-                                               return ret;
-                                       ins_nr = 0;
-                               }
-                               goto next_slot;
-                       }
-                       if (extent_end >= end) {
-                               ins_nr++;
-                               if (ins_nr == 1)
-                                       ins_start_slot = path->slots[0];
-                               break;
-                       }
-               }
-
                if (ins_nr && ins_start_slot + ins_nr == path->slots[0]) {
                        ins_nr++;
                        goto next_slot;
@@ -5151,8 +5126,8 @@ next_key:
 static int btrfs_log_inode(struct btrfs_trans_handle *trans,
                           struct btrfs_root *root, struct btrfs_inode *inode,
                           int inode_only,
-                          u64 start,
-                          u64 end,
+                          const loff_t start,
+                          const loff_t end,
                           struct btrfs_log_ctx *ctx)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
@@ -5180,9 +5155,6 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
                return -ENOMEM;
        }
 
-       start = ALIGN_DOWN(start, fs_info->sectorsize);
-       end = ALIGN(end, fs_info->sectorsize);
-
        min_key.objectid = ino;
        min_key.type = BTRFS_INODE_ITEM_KEY;
        min_key.offset = 0;
@@ -5298,8 +5270,8 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
 
        err = copy_inode_items_to_log(trans, inode, &min_key, &max_key,
                                      path, dst_path, logged_isize,
-                                     recursive_logging, inode_only,
-                                     start, end, ctx, &need_log_inode_item);
+                                     recursive_logging, inode_only, ctx,
+                                     &need_log_inode_item);
        if (err)
                goto out_unlock;
 
@@ -5312,7 +5284,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
        if (max_key.type >= BTRFS_EXTENT_DATA_KEY && !fast_search) {
                btrfs_release_path(path);
                btrfs_release_path(dst_path);
-               err = btrfs_log_holes(trans, root, inode, path, start, end);
+               err = btrfs_log_holes(trans, root, inode, path);
                if (err)
                        goto out_unlock;
        }
index f73276d..a60f603 100644 (file)
@@ -967,7 +967,7 @@ grow_dev_page(struct block_device *bdev, sector_t block,
        struct page *page;
        struct buffer_head *bh;
        sector_t end_block;
-       int ret = 0;            /* Will call free_more_memory() */
+       int ret = 0;
        gfp_t gfp_mask;
 
        gfp_mask = mapping_gfp_constraint(inode->i_mapping, ~__GFP_FS) | gfp;
@@ -1371,6 +1371,17 @@ void __breadahead(struct block_device *bdev, sector_t block, unsigned size)
 }
 EXPORT_SYMBOL(__breadahead);
 
+void __breadahead_gfp(struct block_device *bdev, sector_t block, unsigned size,
+                     gfp_t gfp)
+{
+       struct buffer_head *bh = __getblk_gfp(bdev, block, size, gfp);
+       if (likely(bh)) {
+               ll_rw_block(REQ_OP_READ, REQ_RAHEAD, 1, &bh);
+               brelse(bh);
+       }
+}
+EXPORT_SYMBOL(__breadahead_gfp);
+
 /**
  *  __bread_gfp() - reads a specified block and returns the bh
  *  @bdev: the block_device to read from
index d594c26..4c4202c 100644 (file)
@@ -1051,8 +1051,8 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc,
 
        /* If op failed, mark everyone involved for errors */
        if (result) {
-               int pathlen;
-               u64 base;
+               int pathlen = 0;
+               u64 base = 0;
                char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen,
                                                  &base, 0);
 
index 4a5ccbb..afdfca9 100644 (file)
@@ -527,8 +527,8 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc,
 
        if (result) {
                struct dentry *dentry = req->r_dentry;
-               int pathlen;
-               u64 base;
+               int pathlen = 0;
+               u64 base = 0;
                char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen,
                                                  &base, 0);
 
index 4e5be79..903d9ed 100644 (file)
@@ -521,7 +521,7 @@ extern void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc);
 
 static inline void ceph_mdsc_free_path(char *path, int len)
 {
-       if (path)
+       if (!IS_ERR_OR_NULL(path))
                __putname(path - (PATH_MAX - 1 - len));
 }
 
index 05dd3de..39b708d 100644 (file)
@@ -1891,7 +1891,8 @@ GLOBAL_EXTERN struct list_head            cifs_tcp_ses_list;
 /*
  * This lock protects the cifs_tcp_ses_list, the list of smb sessions per
  * tcp session, and the list of tcon's per smb session. It also protects
- * the reference counters for the server, smb session, and tcon. Finally,
+ * the reference counters for the server, smb session, and tcon. It also
+ * protects some fields in the TCP_Server_Info struct such as dstaddr. Finally,
  * changes to the tcon->tidStatus should be done while holding this lock.
  * generally the locks should be taken in order tcp_ses_lock before
  * tcon->open_file_lock and that before file->file_info_lock since the
index 140efc1..182b864 100644 (file)
@@ -594,6 +594,8 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
                               cifs_max_pending);
        set_credits(server, server->maxReq);
        server->maxBuf = le16_to_cpu(rsp->MaxBufSize);
+       /* set up max_read for readpages check */
+       server->max_read = server->maxBuf;
        /* even though we do not use raw we might as well set this
        accurately, in case we ever find a need for it */
        if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
@@ -755,6 +757,8 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
        set_credits(server, server->maxReq);
        /* probably no need to store and check maxvcs */
        server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize);
+       /* set up max_read for readpages check */
+       server->max_read = server->maxBuf;
        server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
        cifs_dbg(NOISY, "Max buf = %d\n", ses->server->maxBuf);
        server->capabilities = le32_to_cpu(pSMBr->Capabilities);
index 95b3ab0..28268ed 100644 (file)
@@ -375,8 +375,10 @@ static int reconn_set_ipaddr(struct TCP_Server_Info *server)
                return rc;
        }
 
+       spin_lock(&cifs_tcp_ses_lock);
        rc = cifs_convert_address((struct sockaddr *)&server->dstaddr, ipaddr,
                                  strlen(ipaddr));
+       spin_unlock(&cifs_tcp_ses_lock);
        kfree(ipaddr);
 
        return !rc ? -1 : 0;
@@ -3373,6 +3375,10 @@ cifs_find_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
        spin_lock(&cifs_tcp_ses_lock);
        list_for_each(tmp, &ses->tcon_list) {
                tcon = list_entry(tmp, struct cifs_tcon, tcon_list);
+#ifdef CONFIG_CIFS_DFS_UPCALL
+               if (tcon->dfs_path)
+                       continue;
+#endif
                if (!match_tcon(tcon, volume_info))
                        continue;
                ++tcon->tc_count;
index 8fbbdcd..390d2b1 100644 (file)
@@ -61,7 +61,7 @@ static void cifs_set_ops(struct inode *inode)
                }
 
                /* check if server can support readpages */
-               if (cifs_sb_master_tcon(cifs_sb)->ses->server->maxBuf <
+               if (cifs_sb_master_tcon(cifs_sb)->ses->server->max_read <
                                PAGE_SIZE + MAX_CIFS_HDR_SIZE)
                        inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
                else
index a456feb..550ce90 100644 (file)
@@ -1025,51 +1025,99 @@ int copy_path_name(char *dst, const char *src)
 }
 
 struct super_cb_data {
-       struct TCP_Server_Info *server;
+       void *data;
        struct super_block *sb;
 };
 
-static void super_cb(struct super_block *sb, void *arg)
+static void tcp_super_cb(struct super_block *sb, void *arg)
 {
-       struct super_cb_data *d = arg;
+       struct super_cb_data *sd = arg;
+       struct TCP_Server_Info *server = sd->data;
        struct cifs_sb_info *cifs_sb;
        struct cifs_tcon *tcon;
 
-       if (d->sb)
+       if (sd->sb)
                return;
 
        cifs_sb = CIFS_SB(sb);
        tcon = cifs_sb_master_tcon(cifs_sb);
-       if (tcon->ses->server == d->server)
-               d->sb = sb;
+       if (tcon->ses->server == server)
+               sd->sb = sb;
 }
 
-struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server)
+static struct super_block *__cifs_get_super(void (*f)(struct super_block *, void *),
+                                           void *data)
 {
-       struct super_cb_data d = {
-               .server = server,
+       struct super_cb_data sd = {
+               .data = data,
                .sb = NULL,
        };
 
-       iterate_supers_type(&cifs_fs_type, super_cb, &d);
+       iterate_supers_type(&cifs_fs_type, f, &sd);
 
-       if (unlikely(!d.sb))
-               return ERR_PTR(-ENOENT);
+       if (!sd.sb)
+               return ERR_PTR(-EINVAL);
        /*
         * Grab an active reference in order to prevent automounts (DFS links)
         * of expiring and then freeing up our cifs superblock pointer while
         * we're doing failover.
         */
-       cifs_sb_active(d.sb);
-       return d.sb;
+       cifs_sb_active(sd.sb);
+       return sd.sb;
 }
 
-void cifs_put_tcp_super(struct super_block *sb)
+static void __cifs_put_super(struct super_block *sb)
 {
        if (!IS_ERR_OR_NULL(sb))
                cifs_sb_deactive(sb);
 }
 
+struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server)
+{
+       return __cifs_get_super(tcp_super_cb, server);
+}
+
+void cifs_put_tcp_super(struct super_block *sb)
+{
+       __cifs_put_super(sb);
+}
+
+#ifdef CONFIG_CIFS_DFS_UPCALL
+static void tcon_super_cb(struct super_block *sb, void *arg)
+{
+       struct super_cb_data *sd = arg;
+       struct cifs_tcon *tcon = sd->data;
+       struct cifs_sb_info *cifs_sb;
+
+       if (sd->sb)
+               return;
+
+       cifs_sb = CIFS_SB(sb);
+       if (tcon->dfs_path && cifs_sb->origin_fullpath &&
+           !strcasecmp(tcon->dfs_path, cifs_sb->origin_fullpath))
+               sd->sb = sb;
+}
+
+static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon)
+{
+       return __cifs_get_super(tcon_super_cb, tcon);
+}
+
+static inline void cifs_put_tcon_super(struct super_block *sb)
+{
+       __cifs_put_super(sb);
+}
+#else
+static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon)
+{
+       return ERR_PTR(-EOPNOTSUPP);
+}
+
+static inline void cifs_put_tcon_super(struct super_block *sb)
+{
+}
+#endif
+
 int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
                         size_t prefix_len)
 {
@@ -1077,7 +1125,7 @@ int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
        struct cifs_sb_info *cifs_sb;
        int rc = 0;
 
-       sb = cifs_get_tcp_super(tcon->ses->server);
+       sb = cifs_get_tcon_super(tcon);
        if (IS_ERR(sb))
                return PTR_ERR(sb);
 
@@ -1099,6 +1147,6 @@ int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
        cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
 
 out:
-       cifs_put_tcp_super(sb);
+       cifs_put_tcon_super(sb);
        return rc;
 }
index b36c46f..f829f41 100644 (file)
@@ -687,6 +687,11 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon,
        if (smb3_encryption_required(tcon))
                flags |= CIFS_TRANSFORM_REQ;
 
+       if (!server->ops->new_lease_key)
+               return -EIO;
+
+       server->ops->new_lease_key(pfid);
+
        memset(rqst, 0, sizeof(rqst));
        resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
        memset(rsp_iov, 0, sizeof(rsp_iov));
index 47d3e38..b30aa3c 100644 (file)
@@ -1552,6 +1552,21 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
        }
 
        rc = SMB2_sess_establish_session(sess_data);
+#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS
+       if (ses->server->dialect < SMB30_PROT_ID) {
+               cifs_dbg(VFS, "%s: dumping generated SMB2 session keys\n", __func__);
+               /*
+                * The session id is opaque in terms of endianness, so we can't
+                * print it as a long long. we dump it as we got it on the wire
+                */
+               cifs_dbg(VFS, "Session Id    %*ph\n", (int)sizeof(ses->Suid),
+                        &ses->Suid);
+               cifs_dbg(VFS, "Session Key   %*ph\n",
+                        SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response);
+               cifs_dbg(VFS, "Signing Key   %*ph\n",
+                        SMB3_SIGN_KEY_SIZE, ses->auth_key.response);
+       }
+#endif
 out:
        kfree(ntlmssp_blob);
        SMB2_sess_free_buffer(sess_data);
index 1a6c227..c0348e3 100644 (file)
@@ -660,8 +660,8 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
                return rc;
 
        if (memcmp(server_response_sig, shdr->Signature, SMB2_SIGNATURE_SIZE)) {
-               dump_stack();
-               cifs_dbg(VFS, "sign fail cmd 0x%x message id 0x%llx\n", shdr->Command, shdr->MessageId);
+               cifs_dbg(VFS, "sign fail cmd 0x%x message id 0x%llx\n",
+                       shdr->Command, shdr->MessageId);
                return -EACCES;
        } else
                return 0;
index f8296a8..408418e 100644 (file)
@@ -211,6 +211,8 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm,
                        return -ENOMEM;
                (*argv)[(*argc)++] = 0;
                ++pat_ptr;
+               if (!(*pat_ptr))
+                       return -ENOMEM;
        }
 
        /* Repeat as long as we have more pattern to process and more output
index b280e07..8dd4d8d 100644 (file)
@@ -165,7 +165,7 @@ static long get_nr_dentry_negative(void)
        return sum < 0 ? 0 : sum;
 }
 
-int proc_nr_dentry(struct ctl_table *table, int write, void __user *buffer,
+int proc_nr_dentry(struct ctl_table *table, int write, void *buffer,
                   size_t *lenp, loff_t *ppos)
 {
        dentry_stat.nr_dentry = get_nr_dentry();
index 2d35768..ae49a55 100644 (file)
@@ -506,20 +506,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_u32_wo, NULL, debugfs_u32_set, "%llu\n");
  * This function creates a file in debugfs with the given name that
  * contains the value of the variable @value.  If the @mode variable is so
  * set, it can be read from, and written to.
- *
- * This function will return a pointer to a dentry if it succeeds.  This
- * pointer must be passed to the debugfs_remove() function when the file is
- * to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here.)  If an error occurs, ERR_PTR(-ERROR) will be
- * returned.
- *
- * If debugfs is not enabled in the kernel, the value ERR_PTR(-ENODEV) will
- * be returned.
  */
-struct dentry *debugfs_create_u32(const char *name, umode_t mode,
-                                struct dentry *parent, u32 *value)
+void debugfs_create_u32(const char *name, umode_t mode, struct dentry *parent,
+                       u32 *value)
 {
-       return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u32,
+       debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u32,
                                   &fops_u32_ro, &fops_u32_wo);
 }
 EXPORT_SYMBOL_GPL(debugfs_create_u32);
index dc1a1d5..f00fcc4 100644 (file)
@@ -47,7 +47,7 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused)
 }
 
 int drop_caches_sysctl_handler(struct ctl_table *table, int write,
-       void __user *buffer, size_t *length, loff_t *ppos)
+               void *buffer, size_t *length, loff_t *ppos)
 {
        int ret;
 
index 6a04cc0..6774a5a 100644 (file)
@@ -91,7 +91,6 @@ static int exfat_allocate_bitmap(struct super_block *sb,
                }
        }
 
-       sbi->pbr_bh = NULL;
        return 0;
 }
 
@@ -137,8 +136,6 @@ void exfat_free_bitmap(struct exfat_sb_info *sbi)
 {
        int i;
 
-       brelse(sbi->pbr_bh);
-
        for (i = 0; i < sbi->map_sectors; i++)
                __brelse(sbi->vol_amap[i]);
 
index 67d4e46..d67fb8a 100644 (file)
@@ -507,6 +507,7 @@ void exfat_msg(struct super_block *sb, const char *lv, const char *fmt, ...)
                __printf(3, 4) __cold;
 void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
                u8 tz, __le16 time, __le16 date, u8 time_ms);
+void exfat_truncate_atime(struct timespec64 *ts);
 void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
                u8 *tz, __le16 *time, __le16 *date, u8 *time_ms);
 unsigned short exfat_calc_chksum_2byte(void *data, int len,
index 483f683..4f76764 100644 (file)
@@ -273,6 +273,7 @@ int exfat_getattr(const struct path *path, struct kstat *stat,
        struct exfat_inode_info *ei = EXFAT_I(inode);
 
        generic_fillattr(inode, stat);
+       exfat_truncate_atime(&stat->atime);
        stat->result_mask |= STATX_BTIME;
        stat->btime.tv_sec = ei->i_crtime.tv_sec;
        stat->btime.tv_nsec = ei->i_crtime.tv_nsec;
@@ -339,6 +340,7 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
        }
 
        setattr_copy(inode, attr);
+       exfat_truncate_atime(&inode->i_atime);
        mark_inode_dirty(inode);
 
 out:
index 14a3300..ebd2cbe 100644 (file)
@@ -88,7 +88,8 @@ void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
        if (time_ms) {
                ts->tv_sec += time_ms / 100;
                ts->tv_nsec = (time_ms % 100) * 10 * NSEC_PER_MSEC;
-       }
+       } else
+               ts->tv_nsec = 0;
 
        if (tz & EXFAT_TZ_VALID)
                /* Adjust timezone to UTC0. */
@@ -124,6 +125,17 @@ void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
        *tz = EXFAT_TZ_VALID;
 }
 
+/*
+ * The timestamp for access_time has double seconds granularity.
+ * (There is no 10msIncrement field for access_time unlike create/modify_time)
+ * atime also has only a 2-second resolution.
+ */
+void exfat_truncate_atime(struct timespec64 *ts)
+{
+       ts->tv_sec = round_down(ts->tv_sec, 2);
+       ts->tv_nsec = 0;
+}
+
 unsigned short exfat_calc_chksum_2byte(void *data, int len,
                unsigned short chksum, int type)
 {
index a8681d9..b72d782 100644 (file)
@@ -595,6 +595,7 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
        inode_inc_iversion(inode);
        inode->i_mtime = inode->i_atime = inode->i_ctime =
                EXFAT_I(inode)->i_crtime = current_time(inode);
+       exfat_truncate_atime(&inode->i_atime);
        /* timestamp is already written, so mark_inode_dirty() is unneeded. */
 
        d_instantiate(dentry, inode);
@@ -854,6 +855,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
 
        inode_inc_iversion(dir);
        dir->i_mtime = dir->i_atime = current_time(dir);
+       exfat_truncate_atime(&dir->i_atime);
        if (IS_DIRSYNC(dir))
                exfat_sync_inode(dir);
        else
@@ -861,6 +863,7 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
 
        clear_nlink(inode);
        inode->i_mtime = inode->i_atime = current_time(inode);
+       exfat_truncate_atime(&inode->i_atime);
        exfat_unhash_inode(inode);
        exfat_d_version_set(dentry, inode_query_iversion(dir));
 unlock:
@@ -903,6 +906,7 @@ static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
        inode_inc_iversion(inode);
        inode->i_mtime = inode->i_atime = inode->i_ctime =
                EXFAT_I(inode)->i_crtime = current_time(inode);
+       exfat_truncate_atime(&inode->i_atime);
        /* timestamp is already written, so mark_inode_dirty() is unneeded. */
 
        d_instantiate(dentry, inode);
@@ -1019,6 +1023,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
 
        inode_inc_iversion(dir);
        dir->i_mtime = dir->i_atime = current_time(dir);
+       exfat_truncate_atime(&dir->i_atime);
        if (IS_DIRSYNC(dir))
                exfat_sync_inode(dir);
        else
@@ -1027,6 +1032,7 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
 
        clear_nlink(inode);
        inode->i_mtime = inode->i_atime = current_time(inode);
+       exfat_truncate_atime(&inode->i_atime);
        exfat_unhash_inode(inode);
        exfat_d_version_set(dentry, inode_query_iversion(dir));
 unlock:
@@ -1387,6 +1393,7 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
        inode_inc_iversion(new_dir);
        new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
                EXFAT_I(new_dir)->i_crtime = current_time(new_dir);
+       exfat_truncate_atime(&new_dir->i_atime);
        if (IS_DIRSYNC(new_dir))
                exfat_sync_inode(new_dir);
        else
index 16ed202..0565d55 100644 (file)
@@ -49,6 +49,7 @@ static void exfat_put_super(struct super_block *sb)
                sync_blockdev(sb->s_bdev);
        exfat_set_vol_flags(sb, VOL_CLEAN);
        exfat_free_bitmap(sbi);
+       brelse(sbi->pbr_bh);
        mutex_unlock(&sbi->s_lock);
 
        call_rcu(&sbi->rcu, exfat_delayed_free);
@@ -100,7 +101,7 @@ static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf)
 int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
 {
        struct exfat_sb_info *sbi = EXFAT_SB(sb);
-       struct pbr64 *bpb;
+       struct pbr64 *bpb = (struct pbr64 *)sbi->pbr_bh->b_data;
        bool sync = 0;
 
        /* flags are not changed */
@@ -115,15 +116,6 @@ int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
        if (sb_rdonly(sb))
                return 0;
 
-       if (!sbi->pbr_bh) {
-               sbi->pbr_bh = sb_bread(sb, 0);
-               if (!sbi->pbr_bh) {
-                       exfat_msg(sb, KERN_ERR, "failed to read boot sector");
-                       return -ENOMEM;
-               }
-       }
-
-       bpb = (struct pbr64 *)sbi->pbr_bh->b_data;
        bpb->bsx.vol_flags = cpu_to_le16(new_flag);
 
        if (new_flag == VOL_DIRTY && !buffer_dirty(sbi->pbr_bh))
@@ -159,7 +151,6 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
                seq_puts(m, ",iocharset=utf8");
        else if (sbi->nls_io)
                seq_printf(m, ",iocharset=%s", sbi->nls_io->charset);
-       seq_printf(m, ",bps=%ld", sb->s_blocksize);
        if (opts->errors == EXFAT_ERRORS_CONT)
                seq_puts(m, ",errors=continue");
        else if (opts->errors == EXFAT_ERRORS_PANIC)
@@ -351,14 +342,15 @@ static int exfat_read_root(struct inode *inode)
        exfat_save_attr(inode, ATTR_SUBDIR);
        inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
                current_time(inode);
+       exfat_truncate_atime(&inode->i_atime);
        exfat_cache_init_inode(inode);
        return 0;
 }
 
-static struct pbr *exfat_read_pbr_with_logical_sector(struct super_block *sb,
-               struct buffer_head **prev_bh)
+static struct pbr *exfat_read_pbr_with_logical_sector(struct super_block *sb)
 {
-       struct pbr *p_pbr = (struct pbr *) (*prev_bh)->b_data;
+       struct exfat_sb_info *sbi = EXFAT_SB(sb);
+       struct pbr *p_pbr = (struct pbr *) (sbi->pbr_bh)->b_data;
        unsigned short logical_sect = 0;
 
        logical_sect = 1 << p_pbr->bsx.f64.sect_size_bits;
@@ -378,26 +370,23 @@ static struct pbr *exfat_read_pbr_with_logical_sector(struct super_block *sb,
        }
 
        if (logical_sect > sb->s_blocksize) {
-               struct buffer_head *bh = NULL;
-
-               __brelse(*prev_bh);
-               *prev_bh = NULL;
+               brelse(sbi->pbr_bh);
+               sbi->pbr_bh = NULL;
 
                if (!sb_set_blocksize(sb, logical_sect)) {
                        exfat_msg(sb, KERN_ERR,
                                "unable to set blocksize %u", logical_sect);
                        return NULL;
                }
-               bh = sb_bread(sb, 0);
-               if (!bh) {
+               sbi->pbr_bh = sb_bread(sb, 0);
+               if (!sbi->pbr_bh) {
                        exfat_msg(sb, KERN_ERR,
                                "unable to read boot sector (logical sector size = %lu)",
                                sb->s_blocksize);
                        return NULL;
                }
 
-               *prev_bh = bh;
-               p_pbr = (struct pbr *) bh->b_data;
+               p_pbr = (struct pbr *)sbi->pbr_bh->b_data;
        }
        return p_pbr;
 }
@@ -408,21 +397,20 @@ static int __exfat_fill_super(struct super_block *sb)
        int ret;
        struct pbr *p_pbr;
        struct pbr64 *p_bpb;
-       struct buffer_head *bh;
        struct exfat_sb_info *sbi = EXFAT_SB(sb);
 
        /* set block size to read super block */
        sb_min_blocksize(sb, 512);
 
        /* read boot sector */
-       bh = sb_bread(sb, 0);
-       if (!bh) {
+       sbi->pbr_bh = sb_bread(sb, 0);
+       if (!sbi->pbr_bh) {
                exfat_msg(sb, KERN_ERR, "unable to read boot sector");
                return -EIO;
        }
 
        /* PRB is read */
-       p_pbr = (struct pbr *)bh->b_data;
+       p_pbr = (struct pbr *)sbi->pbr_bh->b_data;
 
        /* check the validity of PBR */
        if (le16_to_cpu((p_pbr->signature)) != PBR_SIGNATURE) {
@@ -433,7 +421,7 @@ static int __exfat_fill_super(struct super_block *sb)
 
 
        /* check logical sector size */
-       p_pbr = exfat_read_pbr_with_logical_sector(sb, &bh);
+       p_pbr = exfat_read_pbr_with_logical_sector(sb);
        if (!p_pbr) {
                ret = -EIO;
                goto free_bh;
@@ -514,7 +502,7 @@ free_alloc_bitmap:
 free_upcase_table:
        exfat_free_upcase_table(sbi);
 free_bh:
-       brelse(bh);
+       brelse(sbi->pbr_bh);
        return ret;
 }
 
@@ -531,17 +519,18 @@ static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
        if (opts->discard) {
                struct request_queue *q = bdev_get_queue(sb->s_bdev);
 
-               if (!blk_queue_discard(q))
+               if (!blk_queue_discard(q)) {
                        exfat_msg(sb, KERN_WARNING,
                                "mounting with \"discard\" option, but the device does not support discard");
-               opts->discard = 0;
+                       opts->discard = 0;
+               }
        }
 
        sb->s_flags |= SB_NODIRATIME;
        sb->s_magic = EXFAT_SUPER_MAGIC;
        sb->s_op = &exfat_sops;
 
-       sb->s_time_gran = 1;
+       sb->s_time_gran = 10 * NSEC_PER_MSEC;
        sb->s_time_min = EXFAT_MIN_TIMESTAMP_SECS;
        sb->s_time_max = EXFAT_MAX_TIMESTAMP_SECS;
 
@@ -605,6 +594,7 @@ put_inode:
 free_table:
        exfat_free_upcase_table(sbi);
        exfat_free_bitmap(sbi);
+       brelse(sbi->pbr_bh);
 
 check_nls_io:
        unload_nls(sbi->nls_io);
@@ -717,6 +707,7 @@ static void __exit exit_exfat_fs(void)
 module_init(init_exfat_fs);
 module_exit(exit_exfat_fs);
 
+MODULE_ALIAS_FS("exfat");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("exFAT filesystem support");
 MODULE_AUTHOR("Samsung Electronics Co., Ltd.");
index 0e0a4d6..a32e5f7 100644 (file)
@@ -410,7 +410,7 @@ verified:
  * Read the bitmap for a given block_group,and validate the
  * bits for block/inode/inode tables are set in the bitmaps
  *
- * Return buffer_head on success or NULL in case of failure.
+ * Return buffer_head on success or an ERR_PTR in case of failure.
  */
 struct buffer_head *
 ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group)
@@ -502,7 +502,7 @@ out:
        return ERR_PTR(err);
 }
 
-/* Returns 0 on success, 1 on error */
+/* Returns 0 on success, -errno on error */
 int ext4_wait_block_bitmap(struct super_block *sb, ext4_group_t block_group,
                           struct buffer_head *bh)
 {
index 7f16e1a..0c76cdd 100644 (file)
@@ -338,9 +338,6 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
                if (inode && inode_needs_sync(inode)) {
                        sync_dirty_buffer(bh);
                        if (buffer_req(bh) && !buffer_uptodate(bh)) {
-                               struct ext4_super_block *es;
-
-                               es = EXT4_SB(inode->i_sb)->s_es;
                                ext4_error_inode_err(inode, where, line,
                                                     bh->b_blocknr, EIO,
                                        "IO error syncing itable block");
index 031752c..f2b577b 100644 (file)
@@ -3374,8 +3374,8 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
                (unsigned long long)map->m_lblk, map_len);
 
        sbi = EXT4_SB(inode->i_sb);
-       eof_block = (inode->i_size + inode->i_sb->s_blocksize - 1) >>
-               inode->i_sb->s_blocksize_bits;
+       eof_block = (EXT4_I(inode)->i_disksize + inode->i_sb->s_blocksize - 1)
+                       >> inode->i_sb->s_blocksize_bits;
        if (eof_block < map->m_lblk + map_len)
                eof_block = map->m_lblk + map_len;
 
@@ -3627,8 +3627,8 @@ static int ext4_split_convert_extents(handle_t *handle,
                  __func__, inode->i_ino,
                  (unsigned long long)map->m_lblk, map->m_len);
 
-       eof_block = (inode->i_size + inode->i_sb->s_blocksize - 1) >>
-               inode->i_sb->s_blocksize_bits;
+       eof_block = (EXT4_I(inode)->i_disksize + inode->i_sb->s_blocksize - 1)
+                       >> inode->i_sb->s_blocksize_bits;
        if (eof_block < map->m_lblk + map->m_len)
                eof_block = map->m_lblk + map->m_len;
        /*
index b420c9d..4b8c9a9 100644 (file)
@@ -113,7 +113,7 @@ verified:
  * Read the inode allocation bitmap for a given block_group, reading
  * into the specified slot in the superblock's bitmap cache.
  *
- * Return buffer_head of bitmap on success or NULL.
+ * Return buffer_head of bitmap on success, or an ERR_PTR on error.
  */
 static struct buffer_head *
 ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
@@ -662,7 +662,7 @@ static int find_group_other(struct super_block *sb, struct inode *parent,
  * block has been written back to disk.  (Yes, these values are
  * somewhat arbitrary...)
  */
-#define RECENTCY_MIN   5
+#define RECENTCY_MIN   60
 #define RECENTCY_DIRTY 300
 
 static int recently_deleted(struct super_block *sb, ext4_group_t group, int ino)
index e416096..2a4aae6 100644 (file)
@@ -1973,7 +1973,7 @@ static int ext4_writepage(struct page *page,
        bool keep_towrite = false;
 
        if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) {
-               ext4_invalidatepage(page, 0, PAGE_SIZE);
+               inode->i_mapping->a_ops->invalidatepage(page, 0, PAGE_SIZE);
                unlock_page(page);
                return -EIO;
        }
@@ -4364,7 +4364,7 @@ make_io:
                        if (end > table)
                                end = table;
                        while (b <= end)
-                               sb_breadahead(sb, b++);
+                               sb_breadahead_unmovable(sb, b++);
                }
 
                /*
index 87c85be..30d5d97 100644 (file)
@@ -1943,7 +1943,8 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac,
        int free;
 
        free = e4b->bd_info->bb_free;
-       BUG_ON(free <= 0);
+       if (WARN_ON(free <= 0))
+               return;
 
        i = e4b->bd_info->bb_first_free;
 
@@ -1966,7 +1967,8 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac,
                }
 
                mb_find_extent(e4b, i, ac->ac_g_ex.fe_len, &ex);
-               BUG_ON(ex.fe_len <= 0);
+               if (WARN_ON(ex.fe_len <= 0))
+                       break;
                if (free < ex.fe_len) {
                        ext4_grp_locked_error(sb, e4b->bd_group, 0, 0,
                                        "%d free clusters as per "
index 9728e7b..bf5fcb4 100644 (file)
@@ -596,7 +596,6 @@ void __ext4_error_file(struct file *file, const char *function,
 {
        va_list args;
        struct va_format vaf;
-       struct ext4_super_block *es;
        struct inode *inode = file_inode(file);
        char pathname[80], *path;
 
@@ -604,7 +603,6 @@ void __ext4_error_file(struct file *file, const char *function,
                return;
 
        trace_ext4_error(inode->i_sb, function, line);
-       es = EXT4_SB(inode->i_sb)->s_es;
        if (ext4_error_ratelimit(inode->i_sb)) {
                path = file_path(file, pathname, sizeof(pathname));
                if (IS_ERR(path))
@@ -4340,7 +4338,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        /* Pre-read the descriptors into the buffer cache */
        for (i = 0; i < db_count; i++) {
                block = descriptor_loc(sb, logical_sb_block, i);
-               sb_breadahead(sb, block);
+               sb_breadahead_unmovable(sb, block);
        }
 
        for (i = 0; i < db_count; i++) {
index 30d55c9..3b61253 100644 (file)
@@ -80,14 +80,14 @@ EXPORT_SYMBOL_GPL(get_max_files);
  */
 #if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
 int proc_nr_files(struct ctl_table *table, int write,
-                     void __user *buffer, size_t *lenp, loff_t *ppos)
+                     void *buffer, size_t *lenp, loff_t *ppos)
 {
        files_stat.nr_files = get_nr_files();
        return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
 }
 #else
 int proc_nr_files(struct ctl_table *table, int write,
-                     void __user *buffer, size_t *lenp, loff_t *ppos)
+                     void *buffer, size_t *lenp, loff_t *ppos)
 {
        return -ENOSYS;
 }
index 59c2494..c1e6cc9 100644 (file)
@@ -51,8 +51,7 @@ static unsigned fscache_op_max_active = 2;
 static struct ctl_table_header *fscache_sysctl_header;
 
 static int fscache_max_active_sysctl(struct ctl_table *table, int write,
-                                    void __user *buffer,
-                                    size_t *lenp, loff_t *ppos)
+                                    void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct workqueue_struct **wqp = table->extra1;
        unsigned int *datap = table->data;
index 93d9252..cc6e701 100644 (file)
@@ -108,7 +108,7 @@ long get_nr_dirty_inodes(void)
  */
 #ifdef CONFIG_SYSCTL
 int proc_nr_inodes(struct ctl_table *table, int write,
-                  void __user *buffer, size_t *lenp, loff_t *ppos)
+                  void *buffer, size_t *lenp, loff_t *ppos)
 {
        inodes_stat.nr_inodes = get_nr_inodes();
        inodes_stat.nr_unused = get_nr_inodes_unused();
index 5190bfb..0b91b06 100644 (file)
@@ -357,7 +357,6 @@ struct io_timeout_data {
        struct hrtimer                  timer;
        struct timespec64               ts;
        enum hrtimer_mode               mode;
-       u32                             seq_offset;
 };
 
 struct io_accept {
@@ -385,7 +384,7 @@ struct io_timeout {
        struct file                     *file;
        u64                             addr;
        int                             flags;
-       unsigned                        count;
+       u32                             count;
 };
 
 struct io_rw {
@@ -508,6 +507,7 @@ enum {
        REQ_F_FORCE_ASYNC_BIT   = IOSQE_ASYNC_BIT,
        REQ_F_BUFFER_SELECT_BIT = IOSQE_BUFFER_SELECT_BIT,
 
+       REQ_F_LINK_HEAD_BIT,
        REQ_F_LINK_NEXT_BIT,
        REQ_F_FAIL_LINK_BIT,
        REQ_F_INFLIGHT_BIT,
@@ -524,6 +524,7 @@ enum {
        REQ_F_OVERFLOW_BIT,
        REQ_F_POLLED_BIT,
        REQ_F_BUFFER_SELECTED_BIT,
+       REQ_F_NO_FILE_TABLE_BIT,
 
        /* not a real bit, just to check we're not overflowing the space */
        __REQ_F_LAST_BIT,
@@ -543,6 +544,8 @@ enum {
        /* IOSQE_BUFFER_SELECT */
        REQ_F_BUFFER_SELECT     = BIT(REQ_F_BUFFER_SELECT_BIT),
 
+       /* head of a link */
+       REQ_F_LINK_HEAD         = BIT(REQ_F_LINK_HEAD_BIT),
        /* already grabbed next link */
        REQ_F_LINK_NEXT         = BIT(REQ_F_LINK_NEXT_BIT),
        /* fail rest of links */
@@ -575,6 +578,8 @@ enum {
        REQ_F_POLLED            = BIT(REQ_F_POLLED_BIT),
        /* buffer already selected */
        REQ_F_BUFFER_SELECTED   = BIT(REQ_F_BUFFER_SELECTED_BIT),
+       /* doesn't need file table for this request */
+       REQ_F_NO_FILE_TABLE     = BIT(REQ_F_NO_FILE_TABLE_BIT),
 };
 
 struct async_poll {
@@ -797,6 +802,7 @@ static const struct io_op_def io_op_defs[] = {
                .needs_file             = 1,
                .fd_non_neg             = 1,
                .needs_fs               = 1,
+               .file_table             = 1,
        },
        [IORING_OP_READ] = {
                .needs_mm               = 1,
@@ -955,8 +961,8 @@ static inline bool __req_need_defer(struct io_kiocb *req)
 {
        struct io_ring_ctx *ctx = req->ctx;
 
-       return req->sequence != ctx->cached_cq_tail + ctx->cached_sq_dropped
-                                       + atomic_read(&ctx->cached_cq_overflow);
+       return req->sequence != ctx->cached_cq_tail
+                               + atomic_read(&ctx->cached_cq_overflow);
 }
 
 static inline bool req_need_defer(struct io_kiocb *req)
@@ -1289,7 +1295,7 @@ static struct io_kiocb *io_get_fallback_req(struct io_ring_ctx *ctx)
        struct io_kiocb *req;
 
        req = ctx->fallback_req;
-       if (!test_and_set_bit_lock(0, (unsigned long *) ctx->fallback_req))
+       if (!test_and_set_bit_lock(0, (unsigned long *) &ctx->fallback_req))
                return req;
 
        return NULL;
@@ -1376,7 +1382,7 @@ static void __io_free_req(struct io_kiocb *req)
        if (likely(!io_is_fallback_req(req)))
                kmem_cache_free(req_cachep, req);
        else
-               clear_bit_unlock(0, (unsigned long *) req->ctx->fallback_req);
+               clear_bit_unlock(0, (unsigned long *) &req->ctx->fallback_req);
 }
 
 struct req_batch {
@@ -1437,7 +1443,7 @@ static bool io_link_cancel_timeout(struct io_kiocb *req)
        if (ret != -1) {
                io_cqring_fill_event(req, -ECANCELED);
                io_commit_cqring(ctx);
-               req->flags &= ~REQ_F_LINK;
+               req->flags &= ~REQ_F_LINK_HEAD;
                io_put_req(req);
                return true;
        }
@@ -1473,7 +1479,7 @@ static void io_req_link_next(struct io_kiocb *req, struct io_kiocb **nxtptr)
 
                list_del_init(&req->link_list);
                if (!list_empty(&nxt->link_list))
-                       nxt->flags |= REQ_F_LINK;
+                       nxt->flags |= REQ_F_LINK_HEAD;
                *nxtptr = nxt;
                break;
        }
@@ -1484,7 +1490,7 @@ static void io_req_link_next(struct io_kiocb *req, struct io_kiocb **nxtptr)
 }
 
 /*
- * Called if REQ_F_LINK is set, and we fail the head request
+ * Called if REQ_F_LINK_HEAD is set, and we fail the head request
  */
 static void io_fail_links(struct io_kiocb *req)
 {
@@ -1517,7 +1523,7 @@ static void io_fail_links(struct io_kiocb *req)
 
 static void io_req_find_next(struct io_kiocb *req, struct io_kiocb **nxt)
 {
-       if (likely(!(req->flags & REQ_F_LINK)))
+       if (likely(!(req->flags & REQ_F_LINK_HEAD)))
                return;
 
        /*
@@ -1669,7 +1675,7 @@ static inline unsigned int io_sqring_entries(struct io_ring_ctx *ctx)
 
 static inline bool io_req_multi_free(struct req_batch *rb, struct io_kiocb *req)
 {
-       if ((req->flags & REQ_F_LINK) || io_is_fallback_req(req))
+       if ((req->flags & REQ_F_LINK_HEAD) || io_is_fallback_req(req))
                return false;
 
        if (!(req->flags & REQ_F_FIXED_FILE) || req->io)
@@ -2032,7 +2038,7 @@ static struct file *__io_file_get(struct io_submit_state *state, int fd)
  * any file. For now, just ensure that anything potentially problematic is done
  * inline.
  */
-static bool io_file_supports_async(struct file *file)
+static bool io_file_supports_async(struct file *file, int rw)
 {
        umode_t mode = file_inode(file)->i_mode;
 
@@ -2041,7 +2047,13 @@ static bool io_file_supports_async(struct file *file)
        if (S_ISREG(mode) && file->f_op != &io_uring_fops)
                return true;
 
-       return false;
+       if (!(file->f_mode & FMODE_NOWAIT))
+               return false;
+
+       if (rw == READ)
+               return file->f_op->read_iter != NULL;
+
+       return file->f_op->write_iter != NULL;
 }
 
 static int io_prep_rw(struct io_kiocb *req, const struct io_uring_sqe *sqe,
@@ -2562,14 +2574,14 @@ static int io_read(struct io_kiocb *req, bool force_nonblock)
 
        req->result = 0;
        io_size = ret;
-       if (req->flags & REQ_F_LINK)
+       if (req->flags & REQ_F_LINK_HEAD)
                req->result = io_size;
 
        /*
         * If the file doesn't support async, mark it as REQ_F_MUST_PUNT so
         * we know to async punt it even if it was opened O_NONBLOCK
         */
-       if (force_nonblock && !io_file_supports_async(req->file))
+       if (force_nonblock && !io_file_supports_async(req->file, READ))
                goto copy_iov;
 
        iov_count = iov_iter_count(&iter);
@@ -2592,7 +2604,8 @@ copy_iov:
                        if (ret)
                                goto out_free;
                        /* any defer here is final, must blocking retry */
-                       if (!(req->flags & REQ_F_NOWAIT))
+                       if (!(req->flags & REQ_F_NOWAIT) &&
+                           !file_can_poll(req->file))
                                req->flags |= REQ_F_MUST_PUNT;
                        return -EAGAIN;
                }
@@ -2653,14 +2666,14 @@ static int io_write(struct io_kiocb *req, bool force_nonblock)
 
        req->result = 0;
        io_size = ret;
-       if (req->flags & REQ_F_LINK)
+       if (req->flags & REQ_F_LINK_HEAD)
                req->result = io_size;
 
        /*
         * If the file doesn't support async, mark it as REQ_F_MUST_PUNT so
         * we know to async punt it even if it was opened O_NONBLOCK
         */
-       if (force_nonblock && !io_file_supports_async(req->file))
+       if (force_nonblock && !io_file_supports_async(req->file, WRITE))
                goto copy_iov;
 
        /* file path doesn't support NOWAIT for non-direct_IO */
@@ -2714,7 +2727,8 @@ copy_iov:
                        if (ret)
                                goto out_free;
                        /* any defer here is final, must blocking retry */
-                       req->flags |= REQ_F_MUST_PUNT;
+                       if (!file_can_poll(req->file))
+                               req->flags |= REQ_F_MUST_PUNT;
                        return -EAGAIN;
                }
        }
@@ -2754,15 +2768,6 @@ static int io_splice_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
        return 0;
 }
 
-static bool io_splice_punt(struct file *file)
-{
-       if (get_pipe_info(file))
-               return false;
-       if (!io_file_supports_async(file))
-               return true;
-       return !(file->f_mode & O_NONBLOCK);
-}
-
 static int io_splice(struct io_kiocb *req, bool force_nonblock)
 {
        struct io_splice *sp = &req->splice;
@@ -2772,11 +2777,8 @@ static int io_splice(struct io_kiocb *req, bool force_nonblock)
        loff_t *poff_in, *poff_out;
        long ret;
 
-       if (force_nonblock) {
-               if (io_splice_punt(in) || io_splice_punt(out))
-                       return -EAGAIN;
-               flags |= SPLICE_F_NONBLOCK;
-       }
+       if (force_nonblock)
+               return -EAGAIN;
 
        poff_in = (sp->off_in == -1) ? NULL : &sp->off_in;
        poff_out = (sp->off_out == -1) ? NULL : &sp->off_out;
@@ -3353,8 +3355,12 @@ static int io_statx(struct io_kiocb *req, bool force_nonblock)
        struct kstat stat;
        int ret;
 
-       if (force_nonblock)
+       if (force_nonblock) {
+               /* only need file table for an actual valid fd */
+               if (ctx->dfd == -1 || ctx->dfd == AT_FDCWD)
+                       req->flags |= REQ_F_NO_FILE_TABLE;
                return -EAGAIN;
+       }
 
        if (vfs_stat_set_lookup_flags(&lookup_flags, ctx->how.flags))
                return -EINVAL;
@@ -3500,7 +3506,7 @@ static void io_sync_file_range_finish(struct io_wq_work **workptr)
        if (io_req_cancelled(req))
                return;
        __io_sync_file_range(req);
-       io_put_req(req); /* put submission ref */
+       io_steal_work(req, workptr);
 }
 
 static int io_sync_file_range(struct io_kiocb *req, bool force_nonblock)
@@ -4153,25 +4159,62 @@ static int __io_async_wake(struct io_kiocb *req, struct io_poll_iocb *poll,
        return 1;
 }
 
+static bool io_poll_rewait(struct io_kiocb *req, struct io_poll_iocb *poll)
+       __acquires(&req->ctx->completion_lock)
+{
+       struct io_ring_ctx *ctx = req->ctx;
+
+       if (!req->result && !READ_ONCE(poll->canceled)) {
+               struct poll_table_struct pt = { ._key = poll->events };
+
+               req->result = vfs_poll(req->file, &pt) & poll->events;
+       }
+
+       spin_lock_irq(&ctx->completion_lock);
+       if (!req->result && !READ_ONCE(poll->canceled)) {
+               add_wait_queue(poll->head, &poll->wait);
+               return true;
+       }
+
+       return false;
+}
+
 static void io_async_task_func(struct callback_head *cb)
 {
        struct io_kiocb *req = container_of(cb, struct io_kiocb, task_work);
        struct async_poll *apoll = req->apoll;
        struct io_ring_ctx *ctx = req->ctx;
+       bool canceled;
 
        trace_io_uring_task_run(req->ctx, req->opcode, req->user_data);
 
-       WARN_ON_ONCE(!list_empty(&req->apoll->poll.wait.entry));
+       if (io_poll_rewait(req, &apoll->poll)) {
+               spin_unlock_irq(&ctx->completion_lock);
+               return;
+       }
 
-       if (hash_hashed(&req->hash_node)) {
-               spin_lock_irq(&ctx->completion_lock);
+       if (hash_hashed(&req->hash_node))
                hash_del(&req->hash_node);
-               spin_unlock_irq(&ctx->completion_lock);
+
+       canceled = READ_ONCE(apoll->poll.canceled);
+       if (canceled) {
+               io_cqring_fill_event(req, -ECANCELED);
+               io_commit_cqring(ctx);
        }
 
+       spin_unlock_irq(&ctx->completion_lock);
+
        /* restore ->work in case we need to retry again */
        memcpy(&req->work, &apoll->work, sizeof(req->work));
 
+       if (canceled) {
+               kfree(apoll);
+               io_cqring_ev_posted(ctx);
+               req_set_fail_links(req);
+               io_double_put_req(req);
+               return;
+       }
+
        __set_current_state(TASK_RUNNING);
        mutex_lock(&ctx->uring_lock);
        __io_queue_sqe(req, NULL);
@@ -4315,11 +4358,13 @@ static bool __io_poll_remove_one(struct io_kiocb *req,
 
 static bool io_poll_remove_one(struct io_kiocb *req)
 {
+       struct async_poll *apoll = NULL;
        bool do_complete;
 
        if (req->opcode == IORING_OP_POLL_ADD) {
                do_complete = __io_poll_remove_one(req, &req->poll);
        } else {
+               apoll = req->apoll;
                /* non-poll requests have submit ref still */
                do_complete = __io_poll_remove_one(req, &req->apoll->poll);
                if (do_complete)
@@ -4328,6 +4373,14 @@ static bool io_poll_remove_one(struct io_kiocb *req)
 
        hash_del(&req->hash_node);
 
+       if (do_complete && apoll) {
+               /*
+                * restore ->work because we need to call io_req_work_drop_env.
+                */
+               memcpy(&req->work, &apoll->work, sizeof(req->work));
+               kfree(apoll);
+       }
+
        if (do_complete) {
                io_cqring_fill_event(req, -ECANCELED);
                io_commit_cqring(req->ctx);
@@ -4342,7 +4395,7 @@ static void io_poll_remove_all(struct io_ring_ctx *ctx)
 {
        struct hlist_node *tmp;
        struct io_kiocb *req;
-       int i;
+       int posted = 0, i;
 
        spin_lock_irq(&ctx->completion_lock);
        for (i = 0; i < (1U << ctx->cancel_hash_bits); i++) {
@@ -4350,11 +4403,12 @@ static void io_poll_remove_all(struct io_ring_ctx *ctx)
 
                list = &ctx->cancel_hash[i];
                hlist_for_each_entry_safe(req, tmp, list, hash_node)
-                       io_poll_remove_one(req);
+                       posted += io_poll_remove_one(req);
        }
        spin_unlock_irq(&ctx->completion_lock);
 
-       io_cqring_ev_posted(ctx);
+       if (posted)
+               io_cqring_ev_posted(ctx);
 }
 
 static int io_poll_cancel(struct io_ring_ctx *ctx, __u64 sqe_addr)
@@ -4423,18 +4477,11 @@ static void io_poll_task_handler(struct io_kiocb *req, struct io_kiocb **nxt)
        struct io_ring_ctx *ctx = req->ctx;
        struct io_poll_iocb *poll = &req->poll;
 
-       if (!req->result && !READ_ONCE(poll->canceled)) {
-               struct poll_table_struct pt = { ._key = poll->events };
-
-               req->result = vfs_poll(req->file, &pt) & poll->events;
-       }
-
-       spin_lock_irq(&ctx->completion_lock);
-       if (!req->result && !READ_ONCE(poll->canceled)) {
-               add_wait_queue(poll->head, &poll->wait);
+       if (io_poll_rewait(req, poll)) {
                spin_unlock_irq(&ctx->completion_lock);
                return;
        }
+
        hash_del(&req->hash_node);
        io_poll_complete(req, req->result, 0);
        req->flags |= REQ_F_COMP_LOCKED;
@@ -4665,11 +4712,12 @@ static int io_timeout_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe,
 
 static int io_timeout(struct io_kiocb *req)
 {
-       unsigned count;
        struct io_ring_ctx *ctx = req->ctx;
        struct io_timeout_data *data;
        struct list_head *entry;
        unsigned span = 0;
+       u32 count = req->timeout.count;
+       u32 seq = req->sequence;
 
        data = &req->io->timeout;
 
@@ -4678,7 +4726,6 @@ static int io_timeout(struct io_kiocb *req)
         * timeout event to be satisfied. If it isn't set, then this is
         * a pure timeout request, sequence isn't used.
         */
-       count = req->timeout.count;
        if (!count) {
                req->flags |= REQ_F_TIMEOUT_NOSEQ;
                spin_lock_irq(&ctx->completion_lock);
@@ -4686,8 +4733,7 @@ static int io_timeout(struct io_kiocb *req)
                goto add;
        }
 
-       req->sequence = ctx->cached_sq_head + count - 1;
-       data->seq_offset = count;
+       req->sequence = seq + count;
 
        /*
         * Insertion sort, ensuring the first entry in the list is always
@@ -4696,26 +4742,26 @@ static int io_timeout(struct io_kiocb *req)
        spin_lock_irq(&ctx->completion_lock);
        list_for_each_prev(entry, &ctx->timeout_list) {
                struct io_kiocb *nxt = list_entry(entry, struct io_kiocb, list);
-               unsigned nxt_sq_head;
+               unsigned nxt_seq;
                long long tmp, tmp_nxt;
-               u32 nxt_offset = nxt->io->timeout.seq_offset;
+               u32 nxt_offset = nxt->timeout.count;
 
                if (nxt->flags & REQ_F_TIMEOUT_NOSEQ)
                        continue;
 
                /*
-                * Since cached_sq_head + count - 1 can overflow, use type long
+                * Since seq + count can overflow, use type long
                 * long to store it.
                 */
-               tmp = (long long)ctx->cached_sq_head + count - 1;
-               nxt_sq_head = nxt->sequence - nxt_offset + 1;
-               tmp_nxt = (long long)nxt_sq_head + nxt_offset - 1;
+               tmp = (long long)seq + count;
+               nxt_seq = nxt->sequence - nxt_offset;
+               tmp_nxt = (long long)nxt_seq + nxt_offset;
 
                /*
                 * cached_sq_head may overflow, and it will never overflow twice
                 * once there is some timeout req still be valid.
                 */
-               if (ctx->cached_sq_head < nxt_sq_head)
+               if (seq < nxt_seq)
                        tmp += UINT_MAX;
 
                if (tmp > tmp_nxt)
@@ -4973,7 +5019,7 @@ static int io_req_defer(struct io_kiocb *req, const struct io_uring_sqe *sqe)
        int ret;
 
        /* Still need defer if there is pending req in defer list. */
-       if (!req_need_defer(req) && list_empty(&ctx->defer_list))
+       if (!req_need_defer(req) && list_empty_careful(&ctx->defer_list))
                return 0;
 
        if (!req->io && io_alloc_async_ctx(req))
@@ -5387,7 +5433,7 @@ static int io_grab_files(struct io_kiocb *req)
        int ret = -EBADF;
        struct io_ring_ctx *ctx = req->ctx;
 
-       if (req->work.files)
+       if (req->work.files || (req->flags & REQ_F_NO_FILE_TABLE))
                return 0;
        if (!ctx->ring_file)
                return -EBADF;
@@ -5476,7 +5522,7 @@ static struct io_kiocb *io_prep_linked_timeout(struct io_kiocb *req)
 {
        struct io_kiocb *nxt;
 
-       if (!(req->flags & REQ_F_LINK))
+       if (!(req->flags & REQ_F_LINK_HEAD))
                return NULL;
        /* for polled retry, if flag is set, we already went through here */
        if (req->flags & REQ_F_POLLED)
@@ -5604,54 +5650,11 @@ static inline void io_queue_link_head(struct io_kiocb *req)
                io_queue_sqe(req, NULL);
 }
 
-#define SQE_VALID_FLAGS        (IOSQE_FIXED_FILE|IOSQE_IO_DRAIN|IOSQE_IO_LINK| \
-                               IOSQE_IO_HARDLINK | IOSQE_ASYNC | \
-                               IOSQE_BUFFER_SELECT)
-
-static bool io_submit_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe,
+static int io_submit_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe,
                          struct io_submit_state *state, struct io_kiocb **link)
 {
        struct io_ring_ctx *ctx = req->ctx;
-       unsigned int sqe_flags;
-       int ret, id, fd;
-
-       sqe_flags = READ_ONCE(sqe->flags);
-
-       /* enforce forwards compatibility on users */
-       if (unlikely(sqe_flags & ~SQE_VALID_FLAGS)) {
-               ret = -EINVAL;
-               goto err_req;
-       }
-
-       if ((sqe_flags & IOSQE_BUFFER_SELECT) &&
-           !io_op_defs[req->opcode].buffer_select) {
-               ret = -EOPNOTSUPP;
-               goto err_req;
-       }
-
-       id = READ_ONCE(sqe->personality);
-       if (id) {
-               req->work.creds = idr_find(&ctx->personality_idr, id);
-               if (unlikely(!req->work.creds)) {
-                       ret = -EINVAL;
-                       goto err_req;
-               }
-               get_cred(req->work.creds);
-       }
-
-       /* same numerical values with corresponding REQ_F_*, safe to copy */
-       req->flags |= sqe_flags & (IOSQE_IO_DRAIN | IOSQE_IO_HARDLINK |
-                                       IOSQE_ASYNC | IOSQE_FIXED_FILE |
-                                       IOSQE_BUFFER_SELECT);
-
-       fd = READ_ONCE(sqe->fd);
-       ret = io_req_set_file(state, req, fd, sqe_flags);
-       if (unlikely(ret)) {
-err_req:
-               io_cqring_add_event(req, ret);
-               io_double_put_req(req);
-               return false;
-       }
+       int ret;
 
        /*
         * If we already have a head request, queue this one for async
@@ -5670,42 +5673,39 @@ err_req:
                 * next after the link request. The last one is done via
                 * drain_next flag to persist the effect across calls.
                 */
-               if (sqe_flags & IOSQE_IO_DRAIN) {
+               if (req->flags & REQ_F_IO_DRAIN) {
                        head->flags |= REQ_F_IO_DRAIN;
                        ctx->drain_next = 1;
                }
-               if (io_alloc_async_ctx(req)) {
-                       ret = -EAGAIN;
-                       goto err_req;
-               }
+               if (io_alloc_async_ctx(req))
+                       return -EAGAIN;
 
                ret = io_req_defer_prep(req, sqe);
                if (ret) {
                        /* fail even hard links since we don't submit */
                        head->flags |= REQ_F_FAIL_LINK;
-                       goto err_req;
+                       return ret;
                }
                trace_io_uring_link(ctx, req, head);
                list_add_tail(&req->link_list, &head->link_list);
 
                /* last request of a link, enqueue the link */
-               if (!(sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK))) {
+               if (!(req->flags & (REQ_F_LINK | REQ_F_HARDLINK))) {
                        io_queue_link_head(head);
                        *link = NULL;
                }
        } else {
                if (unlikely(ctx->drain_next)) {
                        req->flags |= REQ_F_IO_DRAIN;
-                       req->ctx->drain_next = 0;
+                       ctx->drain_next = 0;
                }
-               if (sqe_flags & (IOSQE_IO_LINK|IOSQE_IO_HARDLINK)) {
-                       req->flags |= REQ_F_LINK;
+               if (req->flags & (REQ_F_LINK | REQ_F_HARDLINK)) {
+                       req->flags |= REQ_F_LINK_HEAD;
                        INIT_LIST_HEAD(&req->link_list);
 
-                       if (io_alloc_async_ctx(req)) {
-                               ret = -EAGAIN;
-                               goto err_req;
-                       }
+                       if (io_alloc_async_ctx(req))
+                               return -EAGAIN;
+
                        ret = io_req_defer_prep(req, sqe);
                        if (ret)
                                req->flags |= REQ_F_FAIL_LINK;
@@ -5715,7 +5715,7 @@ err_req:
                }
        }
 
-       return true;
+       return 0;
 }
 
 /*
@@ -5789,15 +5789,23 @@ static inline void io_consume_sqe(struct io_ring_ctx *ctx)
        ctx->cached_sq_head++;
 }
 
-static void io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req,
-                       const struct io_uring_sqe *sqe)
+#define SQE_VALID_FLAGS        (IOSQE_FIXED_FILE|IOSQE_IO_DRAIN|IOSQE_IO_LINK| \
+                               IOSQE_IO_HARDLINK | IOSQE_ASYNC | \
+                               IOSQE_BUFFER_SELECT)
+
+static int io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req,
+                      const struct io_uring_sqe *sqe,
+                      struct io_submit_state *state, bool async)
 {
+       unsigned int sqe_flags;
+       int id, fd;
+
        /*
         * All io need record the previous position, if LINK vs DARIN,
         * it can be used to mark the position of the first IO in the
         * link list.
         */
-       req->sequence = ctx->cached_sq_head;
+       req->sequence = ctx->cached_sq_head - ctx->cached_sq_dropped;
        req->opcode = READ_ONCE(sqe->opcode);
        req->user_data = READ_ONCE(sqe->user_data);
        req->io = NULL;
@@ -5808,17 +5816,50 @@ static void io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req,
        refcount_set(&req->refs, 2);
        req->task = NULL;
        req->result = 0;
+       req->needs_fixed_file = async;
        INIT_IO_WORK(&req->work, io_wq_submit_work);
+
+       if (unlikely(req->opcode >= IORING_OP_LAST))
+               return -EINVAL;
+
+       if (io_op_defs[req->opcode].needs_mm && !current->mm) {
+               if (unlikely(!mmget_not_zero(ctx->sqo_mm)))
+                       return -EFAULT;
+               use_mm(ctx->sqo_mm);
+       }
+
+       sqe_flags = READ_ONCE(sqe->flags);
+       /* enforce forwards compatibility on users */
+       if (unlikely(sqe_flags & ~SQE_VALID_FLAGS))
+               return -EINVAL;
+
+       if ((sqe_flags & IOSQE_BUFFER_SELECT) &&
+           !io_op_defs[req->opcode].buffer_select)
+               return -EOPNOTSUPP;
+
+       id = READ_ONCE(sqe->personality);
+       if (id) {
+               req->work.creds = idr_find(&ctx->personality_idr, id);
+               if (unlikely(!req->work.creds))
+                       return -EINVAL;
+               get_cred(req->work.creds);
+       }
+
+       /* same numerical values with corresponding REQ_F_*, safe to copy */
+       req->flags |= sqe_flags & (IOSQE_IO_DRAIN | IOSQE_IO_HARDLINK |
+                                       IOSQE_ASYNC | IOSQE_FIXED_FILE |
+                                       IOSQE_BUFFER_SELECT | IOSQE_IO_LINK);
+
+       fd = READ_ONCE(sqe->fd);
+       return io_req_set_file(state, req, fd, sqe_flags);
 }
 
 static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
-                         struct file *ring_file, int ring_fd,
-                         struct mm_struct **mm, bool async)
+                         struct file *ring_file, int ring_fd, bool async)
 {
        struct io_submit_state state, *statep = NULL;
        struct io_kiocb *link = NULL;
        int i, submitted = 0;
-       bool mm_fault = false;
 
        /* if we have a backlog and couldn't flush it all, return BUSY */
        if (test_bit(0, &ctx->sq_check_overflow)) {
@@ -5858,34 +5899,23 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr,
                        break;
                }
 
-               io_init_req(ctx, req, sqe);
+               err = io_init_req(ctx, req, sqe, statep, async);
                io_consume_sqe(ctx);
                /* will complete beyond this point, count as submitted */
                submitted++;
 
-               if (unlikely(req->opcode >= IORING_OP_LAST)) {
-                       err = -EINVAL;
+               if (unlikely(err)) {
 fail_req:
                        io_cqring_add_event(req, err);
                        io_double_put_req(req);
                        break;
                }
 
-               if (io_op_defs[req->opcode].needs_mm && !*mm) {
-                       mm_fault = mm_fault || !mmget_not_zero(ctx->sqo_mm);
-                       if (unlikely(mm_fault)) {
-                               err = -EFAULT;
-                               goto fail_req;
-                       }
-                       use_mm(ctx->sqo_mm);
-                       *mm = ctx->sqo_mm;
-               }
-
-               req->needs_fixed_file = async;
                trace_io_uring_submit_sqe(ctx, req->opcode, req->user_data,
                                                true, async);
-               if (!io_submit_sqe(req, sqe, statep, &link))
-                       break;
+               err = io_submit_sqe(req, sqe, statep, &link);
+               if (err)
+                       goto fail_req;
        }
 
        if (unlikely(submitted != nr)) {
@@ -5904,10 +5934,19 @@ fail_req:
        return submitted;
 }
 
+static inline void io_sq_thread_drop_mm(struct io_ring_ctx *ctx)
+{
+       struct mm_struct *mm = current->mm;
+
+       if (mm) {
+               unuse_mm(mm);
+               mmput(mm);
+       }
+}
+
 static int io_sq_thread(void *data)
 {
        struct io_ring_ctx *ctx = data;
-       struct mm_struct *cur_mm = NULL;
        const struct cred *old_cred;
        mm_segment_t old_fs;
        DEFINE_WAIT(wait);
@@ -5948,11 +5987,7 @@ static int io_sq_thread(void *data)
                         * adding ourselves to the waitqueue, as the unuse/drop
                         * may sleep.
                         */
-                       if (cur_mm) {
-                               unuse_mm(cur_mm);
-                               mmput(cur_mm);
-                               cur_mm = NULL;
-                       }
+                       io_sq_thread_drop_mm(ctx);
 
                        /*
                         * We're polling. If we're within the defined idle
@@ -6016,7 +6051,7 @@ static int io_sq_thread(void *data)
                }
 
                mutex_lock(&ctx->uring_lock);
-               ret = io_submit_sqes(ctx, to_submit, NULL, -1, &cur_mm, true);
+               ret = io_submit_sqes(ctx, to_submit, NULL, -1, true);
                mutex_unlock(&ctx->uring_lock);
                timeout = jiffies + ctx->sq_thread_idle;
        }
@@ -6025,10 +6060,7 @@ static int io_sq_thread(void *data)
                task_work_run();
 
        set_fs(old_fs);
-       if (cur_mm) {
-               unuse_mm(cur_mm);
-               mmput(cur_mm);
-       }
+       io_sq_thread_drop_mm(ctx);
        revert_creds(old_cred);
 
        kthread_parkme();
@@ -7299,7 +7331,7 @@ static void io_ring_ctx_wait_and_kill(struct io_ring_ctx *ctx)
         * it could cause shutdown to hang.
         */
        while (ctx->sqo_thread && !wq_has_sleeper(&ctx->sqo_wait))
-               cpu_relax();
+               cond_resched();
 
        io_kill_timeouts(ctx);
        io_poll_remove_all(ctx);
@@ -7509,13 +7541,8 @@ SYSCALL_DEFINE6(io_uring_enter, unsigned int, fd, u32, to_submit,
                        wake_up(&ctx->sqo_wait);
                submitted = to_submit;
        } else if (to_submit) {
-               struct mm_struct *cur_mm;
-
                mutex_lock(&ctx->uring_lock);
-               /* already have mm, so io_submit_sqes() won't try to grab it */
-               cur_mm = ctx->sqo_mm;
-               submitted = io_submit_sqes(ctx, to_submit, f.file, fd,
-                                          &cur_mm, false);
+               submitted = io_submit_sqes(ctx, to_submit, f.file, fd, false);
                mutex_unlock(&ctx->uring_lock);
 
                if (submitted != to_submit)
index 282d45b..5e80b40 100644 (file)
@@ -55,6 +55,7 @@ EXPORT_SYMBOL(vfs_ioctl);
 static int ioctl_fibmap(struct file *filp, int __user *p)
 {
        struct inode *inode = file_inode(filp);
+       struct super_block *sb = inode->i_sb;
        int error, ur_block;
        sector_t block;
 
@@ -71,6 +72,13 @@ static int ioctl_fibmap(struct file *filp, int __user *p)
        block = ur_block;
        error = bmap(inode, &block);
 
+       if (block > INT_MAX) {
+               error = -ERANGE;
+               pr_warn_ratelimited("[%s/%d] FS: %s File: %pD4 would truncate fibmap result\n",
+                                   current->comm, task_pid_nr(current),
+                                   sb->s_id, filp);
+       }
+
        if (error)
                ur_block = 0;
        else
index bccf305..d55e8f4 100644 (file)
@@ -117,10 +117,7 @@ iomap_bmap_actor(struct inode *inode, loff_t pos, loff_t length,
 
        if (iomap->type == IOMAP_MAPPED) {
                addr = (pos - iomap->offset + iomap->addr) >> inode->i_blkbits;
-               if (addr > INT_MAX)
-                       WARN(1, "would truncate bmap result\n");
-               else
-                       *bno = addr;
+               *bno = addr;
        }
        return 0;
 }
index c5c3fc6..26c94b3 100644 (file)
@@ -253,37 +253,45 @@ int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
 
 int nfs3_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 {
-       struct posix_acl *alloc = NULL, *dfacl = NULL;
+       struct posix_acl *orig = acl, *dfacl = NULL, *alloc;
        int status;
 
        if (S_ISDIR(inode->i_mode)) {
                switch(type) {
                case ACL_TYPE_ACCESS:
-                       alloc = dfacl = get_acl(inode, ACL_TYPE_DEFAULT);
+                       alloc = get_acl(inode, ACL_TYPE_DEFAULT);
                        if (IS_ERR(alloc))
                                goto fail;
+                       dfacl = alloc;
                        break;
 
                case ACL_TYPE_DEFAULT:
-                       dfacl = acl;
-                       alloc = acl = get_acl(inode, ACL_TYPE_ACCESS);
+                       alloc = get_acl(inode, ACL_TYPE_ACCESS);
                        if (IS_ERR(alloc))
                                goto fail;
+                       dfacl = acl;
+                       acl = alloc;
                        break;
                }
        }
 
        if (acl == NULL) {
-               alloc = acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
+               alloc = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
                if (IS_ERR(alloc))
                        goto fail;
+               acl = alloc;
        }
        status = __nfs3_proc_setacls(inode, acl, dfacl);
-       posix_acl_release(alloc);
+out:
+       if (acl != orig)
+               posix_acl_release(acl);
+       if (dfacl != orig)
+               posix_acl_release(dfacl);
        return status;
 
 fail:
-       return PTR_ERR(alloc);
+       status = PTR_ERR(alloc);
+       goto out;
 }
 
 const struct xattr_handler *nfs3_xattr_handlers[] = {
index 512afb1..a0c1e65 100644 (file)
@@ -7891,6 +7891,7 @@ static void
 nfs4_bind_one_conn_to_session_done(struct rpc_task *task, void *calldata)
 {
        struct nfs41_bind_conn_to_session_args *args = task->tk_msg.rpc_argp;
+       struct nfs41_bind_conn_to_session_res *res = task->tk_msg.rpc_resp;
        struct nfs_client *clp = args->client;
 
        switch (task->tk_status) {
@@ -7899,6 +7900,12 @@ nfs4_bind_one_conn_to_session_done(struct rpc_task *task, void *calldata)
                nfs4_schedule_session_recovery(clp->cl_session,
                                task->tk_status);
        }
+       if (args->dir == NFS4_CDFC4_FORE_OR_BOTH &&
+                       res->dir != NFS4_CDFS4_BOTH) {
+               rpc_task_close_connection(task);
+               if (args->retries++ < MAX_BIND_CONN_TO_SESSION_RETRIES)
+                       rpc_restart_call(task);
+       }
 }
 
 static const struct rpc_call_ops nfs4_bind_one_conn_to_session_ops = {
@@ -7921,6 +7928,7 @@ int nfs4_proc_bind_one_conn_to_session(struct rpc_clnt *clnt,
        struct nfs41_bind_conn_to_session_args args = {
                .client = clp,
                .dir = NFS4_CDFC4_FORE_OR_BOTH,
+               .retries = 0,
        };
        struct nfs41_bind_conn_to_session_res res;
        struct rpc_message msg = {
@@ -9191,8 +9199,7 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout)
        nfs4_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0, 0);
 
        task = rpc_run_task(&task_setup_data);
-       if (IS_ERR(task))
-               return ERR_CAST(task);
+
        status = rpc_wait_for_completion_task(task);
        if (status != 0)
                goto out;
index f2dc35c..dd2e14f 100644 (file)
@@ -1332,13 +1332,15 @@ _pnfs_return_layout(struct inode *ino)
                        !valid_layout) {
                spin_unlock(&ino->i_lock);
                dprintk("NFS: %s no layout segments to return\n", __func__);
-               goto out_put_layout_hdr;
+               goto out_wait_layoutreturn;
        }
 
        send = pnfs_prepare_layoutreturn(lo, &stateid, &cred, NULL);
        spin_unlock(&ino->i_lock);
        if (send)
                status = pnfs_send_layoutreturn(lo, &stateid, &cred, IOMODE_ANY, true);
+out_wait_layoutreturn:
+       wait_on_bit(&lo->plh_flags, NFS_LAYOUT_RETURN, TASK_UNINTERRUPTIBLE);
 out_put_layout_hdr:
        pnfs_free_lseg_list(&tmp_list);
        pnfs_put_layout_hdr(lo);
@@ -1456,18 +1458,15 @@ retry:
        /* lo ref dropped in pnfs_roc_release() */
        layoutreturn = pnfs_prepare_layoutreturn(lo, &stateid, &lc_cred, &iomode);
        /* If the creds don't match, we can't compound the layoutreturn */
-       if (!layoutreturn)
+       if (!layoutreturn || cred_fscmp(cred, lc_cred) != 0)
                goto out_noroc;
-       if (cred_fscmp(cred, lc_cred) != 0)
-               goto out_noroc_put_cred;
 
        roc = layoutreturn;
        pnfs_init_layoutreturn_args(args, lo, &stateid, iomode);
        res->lrs_present = 0;
        layoutreturn = false;
-
-out_noroc_put_cred:
        put_cred(lc_cred);
+
 out_noroc:
        spin_unlock(&ino->i_lock);
        rcu_read_unlock();
@@ -2023,6 +2022,7 @@ lookup_again:
                        goto lookup_again;
                }
 
+               spin_unlock(&ino->i_lock);
                first = true;
                status = nfs4_select_rw_stateid(ctx->state,
                                        iomode == IOMODE_RW ? FMODE_WRITE : FMODE_READ,
@@ -2032,12 +2032,12 @@ lookup_again:
                        trace_pnfs_update_layout(ino, pos, count,
                                        iomode, lo, lseg,
                                        PNFS_UPDATE_LAYOUT_INVALID_OPEN);
-                       spin_unlock(&ino->i_lock);
                        nfs4_schedule_stateid_recovery(server, ctx->state);
                        pnfs_clear_first_layoutget(lo);
                        pnfs_put_layout_hdr(lo);
                        goto lookup_again;
                }
+               spin_lock(&ino->i_lock);
        } else {
                nfs4_stateid_copy(&stateid, &lo->plh_stateid);
        }
index 59ef3b1..bdb6d0c 100644 (file)
@@ -185,7 +185,7 @@ static int __nfs_list_for_each_server(struct list_head *head,
 
        rcu_read_lock();
        list_for_each_entry_rcu(server, head, client_link) {
-               if (!nfs_sb_active(server->super))
+               if (!(server->super && nfs_sb_active(server->super)))
                        continue;
                rcu_read_unlock();
                if (last)
index c3b11a7..5cf9132 100644 (file)
@@ -1312,6 +1312,7 @@ nfsd4_run_cb_work(struct work_struct *work)
                container_of(work, struct nfsd4_callback, cb_work);
        struct nfs4_client *clp = cb->cb_clp;
        struct rpc_clnt *clnt;
+       int flags;
 
        if (cb->cb_need_restart) {
                cb->cb_need_restart = false;
@@ -1340,7 +1341,8 @@ nfsd4_run_cb_work(struct work_struct *work)
        }
 
        cb->cb_msg.rpc_cred = clp->cl_cb_cred;
-       rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
+       flags = clp->cl_minorversion ? RPC_TASK_NOCONNECT : RPC_TASK_SOFTCONN;
+       rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | flags,
                        cb->cb_ops ? &nfsd4_cb_ops : &nfsd4_cb_probe_ops, cb);
 }
 
index e32eced..c107caa 100644 (file)
@@ -267,6 +267,8 @@ find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
        if (!nbl) {
                nbl= kmalloc(sizeof(*nbl), GFP_KERNEL);
                if (nbl) {
+                       INIT_LIST_HEAD(&nbl->nbl_list);
+                       INIT_LIST_HEAD(&nbl->nbl_lru);
                        fh_copy_shallow(&nbl->nbl_fh, fh);
                        locks_init_lock(&nbl->nbl_lock);
                        nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,
index 8e4f1ac..1de77f1 100644 (file)
@@ -275,7 +275,6 @@ static ssize_t dlmfs_file_write(struct file *filp,
                                loff_t *ppos)
 {
        int bytes_left;
-       ssize_t writelen;
        char *lvb_buf;
        struct inode *inode = file_inode(filp);
 
@@ -285,32 +284,30 @@ static ssize_t dlmfs_file_write(struct file *filp,
        if (*ppos >= i_size_read(inode))
                return -ENOSPC;
 
+       /* don't write past the lvb */
+       if (count > i_size_read(inode) - *ppos)
+               count = i_size_read(inode) - *ppos;
+
        if (!count)
                return 0;
 
        if (!access_ok(buf, count))
                return -EFAULT;
 
-       /* don't write past the lvb */
-       if ((count + *ppos) > i_size_read(inode))
-               writelen = i_size_read(inode) - *ppos;
-       else
-               writelen = count - *ppos;
-
-       lvb_buf = kmalloc(writelen, GFP_NOFS);
+       lvb_buf = kmalloc(count, GFP_NOFS);
        if (!lvb_buf)
                return -ENOMEM;
 
-       bytes_left = copy_from_user(lvb_buf, buf, writelen);
-       writelen -= bytes_left;
-       if (writelen)
-               user_dlm_write_lvb(inode, lvb_buf, writelen);
+       bytes_left = copy_from_user(lvb_buf, buf, count);
+       count -= bytes_left;
+       if (count)
+               user_dlm_write_lvb(inode, lvb_buf, count);
 
        kfree(lvb_buf);
 
-       *ppos = *ppos + writelen;
-       mlog(0, "wrote %zd bytes\n", writelen);
-       return writelen;
+       *ppos = *ppos + count;
+       mlog(0, "wrote %zu bytes\n", count);
+       return count;
 }
 
 static void dlmfs_init_once(void *foo)
index 49f6d7f..1106137 100644 (file)
@@ -261,14 +261,13 @@ static int propagate_one(struct mount *m)
        child = copy_tree(last_source, last_source->mnt.mnt_root, type);
        if (IS_ERR(child))
                return PTR_ERR(child);
+       read_seqlock_excl(&mount_lock);
        mnt_set_mountpoint(m, mp, child);
+       if (m->mnt_master != dest_master)
+               SET_MNT_MARK(m->mnt_master);
+       read_sequnlock_excl(&mount_lock);
        last_dest = m;
        last_source = child;
-       if (m->mnt_master != dest_master) {
-               read_seqlock_excl(&mount_lock);
-               SET_MNT_MARK(m->mnt_master);
-               read_sequnlock_excl(&mount_lock);
-       }
        hlist_add_head(&child->mnt_hash, list);
        return count_mounts(m->mnt_ns, child);
 }
index 6042b64..eb2255e 100644 (file)
@@ -1573,6 +1573,7 @@ static ssize_t timens_offsets_write(struct file *file, const char __user *buf,
        noffsets = 0;
        for (pos = kbuf; pos; pos = next_line) {
                struct proc_timens_offset *off = &offsets[noffsets];
+               char clock[10];
                int err;
 
                /* Find the end of line and ensure we don't look past it */
@@ -1584,10 +1585,21 @@ static ssize_t timens_offsets_write(struct file *file, const char __user *buf,
                                next_line = NULL;
                }
 
-               err = sscanf(pos, "%u %lld %lu", &off->clockid,
+               err = sscanf(pos, "%9s %lld %lu", clock,
                                &off->val.tv_sec, &off->val.tv_nsec);
                if (err != 3 || off->val.tv_nsec >= NSEC_PER_SEC)
                        goto out;
+
+               clock[sizeof(clock) - 1] = 0;
+               if (strcmp(clock, "monotonic") == 0 ||
+                   strcmp(clock, __stringify(CLOCK_MONOTONIC)) == 0)
+                       off->clockid = CLOCK_MONOTONIC;
+               else if (strcmp(clock, "boottime") == 0 ||
+                        strcmp(clock, __stringify(CLOCK_BOOTTIME)) == 0)
+                       off->clockid = CLOCK_BOOTTIME;
+               else
+                       goto out;
+
                noffsets++;
                if (noffsets == ARRAY_SIZE(offsets)) {
                        if (next_line)
@@ -3274,7 +3286,6 @@ static const struct inode_operations proc_tgid_base_inode_operations = {
 void proc_flush_pid(struct pid *pid)
 {
        proc_invalidate_siblings_dcache(&pid->inodes, &pid->lock);
-       put_pid(pid);
 }
 
 static struct dentry *proc_pid_instantiate(struct dentry * dentry,
index b6f5d45..df2143e 100644 (file)
@@ -539,13 +539,13 @@ out:
        return err;
 }
 
-static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf,
+static ssize_t proc_sys_call_handler(struct file *filp, void __user *ubuf,
                size_t count, loff_t *ppos, int write)
 {
        struct inode *inode = file_inode(filp);
        struct ctl_table_header *head = grab_header(inode);
        struct ctl_table *table = PROC_I(inode)->sysctl_entry;
-       void *new_buf = NULL;
+       void *kbuf;
        ssize_t error;
 
        if (IS_ERR(head))
@@ -564,27 +564,38 @@ static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf,
        if (!table->proc_handler)
                goto out;
 
-       error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, buf, &count,
-                                          ppos, &new_buf);
+       if (write) {
+               kbuf = memdup_user_nul(ubuf, count);
+               if (IS_ERR(kbuf)) {
+                       error = PTR_ERR(kbuf);
+                       goto out;
+               }
+       } else {
+               error = -ENOMEM;
+               kbuf = kzalloc(count, GFP_KERNEL);
+               if (!kbuf)
+                       goto out;
+       }
+
+       error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, &kbuf, &count,
+                                          ppos);
        if (error)
-               goto out;
+               goto out_free_buf;
 
        /* careful: calling conventions are nasty here */
-       if (new_buf) {
-               mm_segment_t old_fs;
-
-               old_fs = get_fs();
-               set_fs(KERNEL_DS);
-               error = table->proc_handler(table, write, (void __user *)new_buf,
-                                           &count, ppos);
-               set_fs(old_fs);
-               kfree(new_buf);
-       } else {
-               error = table->proc_handler(table, write, buf, &count, ppos);
+       error = table->proc_handler(table, write, kbuf, &count, ppos);
+       if (error)
+               goto out_free_buf;
+
+       if (!write) {
+               error = -EFAULT;
+               if (copy_to_user(ubuf, kbuf, count))
+                       goto out_free_buf;
        }
 
-       if (!error)
-               error = count;
+       error = count;
+out_free_buf:
+       kfree(kbuf);
 out:
        sysctl_head_finish(head);
 
index 2633f10..cdbe929 100644 (file)
@@ -196,6 +196,13 @@ static void proc_kill_sb(struct super_block *sb)
        if (ns->proc_thread_self)
                dput(ns->proc_thread_self);
        kill_anon_super(sb);
+
+       /* Make the pid namespace safe for the next mount of proc */
+       ns->proc_self = NULL;
+       ns->proc_thread_self = NULL;
+       ns->pid_gid = GLOBAL_ROOT_GID;
+       ns->hide_pid = 0;
+
        put_pid_ns(ns);
 }
 
index 7dc800c..c663202 100644 (file)
@@ -266,7 +266,8 @@ static int vmcoredd_mmap_dumps(struct vm_area_struct *vma, unsigned long dst,
                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)) {
+                       if (remap_vmalloc_range_partial(vma, dst, buf, 0,
+                                                       tsz)) {
                                ret = -EFAULT;
                                goto out_unlock;
                        }
@@ -624,7 +625,7 @@ static int mmap_vmcore(struct file *file, struct vm_area_struct *vma)
                tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)start, size);
                kaddr = elfnotes_buf + start - elfcorebuf_sz - vmcoredd_orig_sz;
                if (remap_vmalloc_range_partial(vma, vma->vm_start + len,
-                                               kaddr, tsz))
+                                               kaddr, 0, tsz))
                        goto fail;
 
                size -= tsz;
index b6a4f69..7b4bac9 100644 (file)
@@ -2841,7 +2841,7 @@ const struct quotactl_ops dquot_quotactl_sysfile_ops = {
 EXPORT_SYMBOL(dquot_quotactl_sysfile_ops);
 
 static int do_proc_dqstats(struct ctl_table *table, int write,
-                    void __user *buffer, size_t *lenp, loff_t *ppos)
+                    void *buffer, size_t *lenp, loff_t *ppos)
 {
        unsigned int type = (unsigned long *)table->data - dqstats.stat;
        s64 value = percpu_counter_sum(&dqstats.counter[type]);
index cd35253..a288cd6 100644 (file)
@@ -1302,8 +1302,8 @@ int get_tree_bdev(struct fs_context *fc,
        mutex_lock(&bdev->bd_fsfreeze_mutex);
        if (bdev->bd_fsfreeze_count > 0) {
                mutex_unlock(&bdev->bd_fsfreeze_mutex);
-               blkdev_put(bdev, mode);
                warnf(fc, "%pg: Can't mount, blockdev is frozen", bdev);
+               blkdev_put(bdev, mode);
                return -EBUSY;
        }
 
index a7be7a9..8bf1d15 100644 (file)
@@ -911,7 +911,12 @@ xfs_eofblocks_worker(
 {
        struct xfs_mount *mp = container_of(to_delayed_work(work),
                                struct xfs_mount, m_eofblocks_work);
+
+       if (!sb_start_write_trylock(mp->m_super))
+               return;
        xfs_icache_free_eofblocks(mp, NULL);
+       sb_end_write(mp->m_super);
+
        xfs_queue_eofblocks(mp);
 }
 
@@ -938,7 +943,12 @@ xfs_cowblocks_worker(
 {
        struct xfs_mount *mp = container_of(to_delayed_work(work),
                                struct xfs_mount, m_cowblocks_work);
+
+       if (!sb_start_write_trylock(mp->m_super))
+               return;
        xfs_icache_free_cowblocks(mp, NULL);
+       sb_end_write(mp->m_super);
+
        xfs_queue_cowblocks(mp);
 }
 
index cdfb3cd..3099581 100644 (file)
@@ -2363,7 +2363,10 @@ xfs_file_ioctl(
                if (error)
                        return error;
 
-               return xfs_icache_free_eofblocks(mp, &keofb);
+               sb_start_write(mp->m_super);
+               error = xfs_icache_free_eofblocks(mp, &keofb);
+               sb_end_write(mp->m_super);
+               return error;
        }
 
        default:
index 50c4342..b2e4598 100644 (file)
@@ -167,8 +167,12 @@ typedef struct xfs_mount {
        struct xfs_kobj         m_error_meta_kobj;
        struct xfs_error_cfg    m_error_cfg[XFS_ERR_CLASS_MAX][XFS_ERR_ERRNO_MAX];
        struct xstats           m_stats;        /* per-fs stats */
-       struct ratelimit_state  m_flush_inodes_ratelimit;
 
+       /*
+        * Workqueue item so that we can coalesce multiple inode flush attempts
+        * into a single flush.
+        */
+       struct work_struct      m_flush_inodes_work;
        struct workqueue_struct *m_buf_workqueue;
        struct workqueue_struct *m_unwritten_workqueue;
        struct workqueue_struct *m_cil_workqueue;
index b0ce04f..107bf2a 100644 (file)
@@ -1051,6 +1051,7 @@ xfs_reflink_remap_extent(
                uirec.br_startblock = irec->br_startblock + rlen;
                uirec.br_startoff = irec->br_startoff + rlen;
                uirec.br_blockcount = unmap_len - rlen;
+               uirec.br_state = irec->br_state;
                unmap_len = rlen;
 
                /* If this isn't a real mapping, we're done. */
index abf06bf..424bb9a 100644 (file)
@@ -516,6 +516,20 @@ xfs_destroy_mount_workqueues(
        destroy_workqueue(mp->m_buf_workqueue);
 }
 
+static void
+xfs_flush_inodes_worker(
+       struct work_struct      *work)
+{
+       struct xfs_mount        *mp = container_of(work, struct xfs_mount,
+                                                  m_flush_inodes_work);
+       struct super_block      *sb = mp->m_super;
+
+       if (down_read_trylock(&sb->s_umount)) {
+               sync_inodes_sb(sb);
+               up_read(&sb->s_umount);
+       }
+}
+
 /*
  * Flush all dirty data to disk. Must not be called while holding an XFS_ILOCK
  * or a page lock. We use sync_inodes_sb() here to ensure we block while waiting
@@ -526,15 +540,15 @@ void
 xfs_flush_inodes(
        struct xfs_mount        *mp)
 {
-       struct super_block      *sb = mp->m_super;
-
-       if (!__ratelimit(&mp->m_flush_inodes_ratelimit))
+       /*
+        * If flush_work() returns true then that means we waited for a flush
+        * which was already in progress.  Don't bother running another scan.
+        */
+       if (flush_work(&mp->m_flush_inodes_work))
                return;
 
-       if (down_read_trylock(&sb->s_umount)) {
-               sync_inodes_sb(sb);
-               up_read(&sb->s_umount);
-       }
+       queue_work(mp->m_sync_workqueue, &mp->m_flush_inodes_work);
+       flush_work(&mp->m_flush_inodes_work);
 }
 
 /* Catch misguided souls that try to use this interface on XFS */
@@ -1369,17 +1383,6 @@ xfs_fc_fill_super(
        if (error)
                goto out_free_names;
 
-       /*
-        * Cap the number of invocations of xfs_flush_inodes to 16 for every
-        * quarter of a second.  The magic numbers here were determined by
-        * observation neither to cause stalls in writeback when there are a
-        * lot of IO threads and the fs is near ENOSPC, nor cause any fstest
-        * regressions.  YMMV.
-        */
-       ratelimit_state_init(&mp->m_flush_inodes_ratelimit, HZ / 4, 16);
-       ratelimit_set_flags(&mp->m_flush_inodes_ratelimit,
-                       RATELIMIT_MSG_ON_RELEASE);
-
        error = xfs_init_mount_workqueues(mp);
        if (error)
                goto out_close_devices;
@@ -1752,6 +1755,7 @@ static int xfs_init_fs_context(
        spin_lock_init(&mp->m_perag_lock);
        mutex_init(&mp->m_growlock);
        atomic_set(&mp->m_active_trans, 0);
+       INIT_WORK(&mp->m_flush_inodes_work, xfs_flush_inodes_worker);
        INIT_DELAYED_WORK(&mp->m_reclaim_work, xfs_reclaim_worker);
        INIT_DELAYED_WORK(&mp->m_eofblocks_work, xfs_eofblocks_worker);
        INIT_DELAYED_WORK(&mp->m_cowblocks_work, xfs_cowblocks_worker);
index 31b3bdb..021ef96 100644 (file)
@@ -13,7 +13,7 @@ STATIC int
 xfs_stats_clear_proc_handler(
        struct ctl_table        *ctl,
        int                     write,
-       void                    __user *buffer,
+       void                    *buffer,
        size_t                  *lenp,
        loff_t                  *ppos)
 {
@@ -33,7 +33,7 @@ STATIC int
 xfs_panic_mask_proc_handler(
        struct ctl_table        *ctl,
        int                     write,
-       void                    __user *buffer,
+       void                    *buffer,
        size_t                  *lenp,
        loff_t                  *ppos)
 {
index 36341df..44ec80e 100644 (file)
@@ -56,6 +56,7 @@ mandatory-y += topology.h
 mandatory-y += trace_clock.h
 mandatory-y += uaccess.h
 mandatory-y += unaligned.h
+mandatory-y += vermagic.h
 mandatory-y += vga.h
 mandatory-y += word-at-a-time.h
 mandatory-y += xor.h
index b3f1082..1c4fd95 100644 (file)
@@ -163,7 +163,7 @@ static inline int cpumask_to_vpset(struct hv_vpset *vpset,
        return nr_bank;
 }
 
-void hyperv_report_panic(struct pt_regs *regs, long err);
+void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die);
 void hyperv_report_panic_msg(phys_addr_t pa, size_t size);
 bool hv_is_hyperv_initialized(void);
 bool hv_is_hibernation_supported(void);
diff --git a/include/asm-generic/vermagic.h b/include/asm-generic/vermagic.h
new file mode 100644 (file)
index 0000000..084274a
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _ASM_GENERIC_VERMAGIC_H
+#define _ASM_GENERIC_VERMAGIC_H
+
+#define MODULE_ARCH_VERMAGIC ""
+
+#endif /* _ASM_GENERIC_VERMAGIC_H */
index 4fc87de..ee577a8 100644 (file)
@@ -54,7 +54,6 @@ enum wb_reason {
        WB_REASON_SYNC,
        WB_REASON_PERIODIC,
        WB_REASON_LAPTOP_TIMER,
-       WB_REASON_FREE_MORE_MEM,
        WB_REASON_FS_FREE_SPACE,
        /*
         * There is no bdi forker thread any more and works are done
index c1c0f9e..a0ee494 100644 (file)
@@ -319,7 +319,7 @@ struct bio_integrity_payload {
        struct work_struct      bip_work;       /* I/O completion */
 
        struct bio_vec          *bip_vec;
-       struct bio_vec          bip_inline_vecs[0];/* embedded bvec array */
+       struct bio_vec          bip_inline_vecs[];/* embedded bvec array */
 };
 
 #if defined(CONFIG_BLK_DEV_INTEGRITY)
index f389d7c..b45148b 100644 (file)
@@ -173,7 +173,7 @@ struct blk_mq_hw_ctx {
         * blocking (BLK_MQ_F_BLOCKING). Must be the last member - see also
         * blk_mq_hw_ctx_size().
         */
-       struct srcu_struct      srcu[0];
+       struct srcu_struct      srcu[];
 };
 
 /**
index 70254ae..31eb928 100644 (file)
@@ -198,7 +198,7 @@ struct bio {
         * double allocations for a small number of bio_vecs. This member
         * MUST obviously be kept at the very end of the bio.
         */
-       struct bio_vec          bi_inline_vecs[0];
+       struct bio_vec          bi_inline_vecs[];
 };
 
 #define BIO_RESET_BYTES                offsetof(struct bio, bi_max_vecs)
index c11b413..272626c 100644 (file)
@@ -57,8 +57,6 @@ struct bpf_cgroup_link {
        enum bpf_attach_type type;
 };
 
-extern const struct bpf_link_ops bpf_cgroup_link_lops;
-
 struct bpf_prog_list {
        struct list_head node;
        struct bpf_prog *prog;
@@ -100,8 +98,6 @@ int __cgroup_bpf_attach(struct cgroup *cgrp,
 int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
                        struct bpf_cgroup_link *link,
                        enum bpf_attach_type type);
-int __cgroup_bpf_replace(struct cgroup *cgrp, struct bpf_cgroup_link *link,
-                        struct bpf_prog *new_prog);
 int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
                       union bpf_attr __user *uattr);
 
@@ -112,8 +108,6 @@ int cgroup_bpf_attach(struct cgroup *cgrp,
                      u32 flags);
 int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
                      enum bpf_attach_type type);
-int cgroup_bpf_replace(struct bpf_link *link, struct bpf_prog *old_prog,
-                      struct bpf_prog *new_prog);
 int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
                     union bpf_attr __user *uattr);
 
@@ -138,8 +132,7 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor,
 
 int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
                                   struct ctl_table *table, int write,
-                                  void __user *buf, size_t *pcount,
-                                  loff_t *ppos, void **new_buf,
+                                  void **buf, size_t *pcount, loff_t *ppos,
                                   enum bpf_attach_type type);
 
 int __cgroup_bpf_run_filter_setsockopt(struct sock *sock, int *level,
@@ -302,12 +295,12 @@ int bpf_percpu_cgroup_storage_update(struct bpf_map *map, void *key,
 })
 
 
-#define BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, buf, count, pos, nbuf)  \
+#define BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, buf, count, pos)  \
 ({                                                                            \
        int __ret = 0;                                                         \
        if (cgroup_bpf_enabled)                                                \
                __ret = __cgroup_bpf_run_filter_sysctl(head, table, write,     \
-                                                      buf, count, pos, nbuf,  \
+                                                      buf, count, pos,        \
                                                       BPF_CGROUP_SYSCTL);     \
        __ret;                                                                 \
 })
@@ -354,7 +347,6 @@ int cgroup_bpf_prog_query(const union bpf_attr *attr,
 #else
 
 struct bpf_prog;
-struct bpf_link;
 struct cgroup_bpf {};
 static inline int cgroup_bpf_inherit(struct cgroup *cgrp) { return 0; }
 static inline void cgroup_bpf_offline(struct cgroup *cgrp) {}
@@ -378,13 +370,6 @@ static inline int cgroup_bpf_link_attach(const union bpf_attr *attr,
        return -EINVAL;
 }
 
-static inline int cgroup_bpf_replace(struct bpf_link *link,
-                                    struct bpf_prog *old_prog,
-                                    struct bpf_prog *new_prog)
-{
-       return -EINVAL;
-}
-
 static inline int cgroup_bpf_prog_query(const union bpf_attr *attr,
                                        union bpf_attr __user *uattr)
 {
@@ -429,7 +414,7 @@ static inline int bpf_percpu_cgroup_storage_update(struct bpf_map *map,
 #define BPF_CGROUP_RUN_PROG_UDP6_RECVMSG_LOCK(sk, uaddr) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type,major,minor,access) ({ 0; })
-#define BPF_CGROUP_RUN_PROG_SYSCTL(head,table,write,buf,count,pos,nbuf) ({ 0; })
+#define BPF_CGROUP_RUN_PROG_SYSCTL(head,table,write,buf,count,pos) ({ 0; })
 #define BPF_CGROUP_GETSOCKOPT_MAX_OPTLEN(optlen) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_GETSOCKOPT(sock, level, optname, optval, \
                                       optlen, max_optlen, retval) ({ retval; })
index fd2b232..1262ec4 100644 (file)
@@ -987,6 +987,7 @@ _out:                                                       \
 
 #ifdef CONFIG_BPF_SYSCALL
 DECLARE_PER_CPU(int, bpf_prog_active);
+extern struct mutex bpf_stats_enabled_mutex;
 
 /*
  * Block execution of BPF programs attached to instrumentation (perf,
@@ -1026,9 +1027,11 @@ extern const struct file_operations bpf_prog_fops;
        extern const struct bpf_verifier_ops _name ## _verifier_ops;
 #define BPF_MAP_TYPE(_id, _ops) \
        extern const struct bpf_map_ops _ops;
+#define BPF_LINK_TYPE(_id, _name)
 #include <linux/bpf_types.h>
 #undef BPF_PROG_TYPE
 #undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
 
 extern const struct bpf_prog_ops bpf_offload_prog_ops;
 extern const struct bpf_verifier_ops tc_cls_act_analyzer_ops;
@@ -1085,21 +1088,35 @@ int bpf_prog_new_fd(struct bpf_prog *prog);
 
 struct bpf_link {
        atomic64_t refcnt;
+       u32 id;
+       enum bpf_link_type type;
        const struct bpf_link_ops *ops;
        struct bpf_prog *prog;
        struct work_struct work;
 };
 
+struct bpf_link_primer {
+       struct bpf_link *link;
+       struct file *file;
+       int fd;
+       u32 id;
+};
+
 struct bpf_link_ops {
        void (*release)(struct bpf_link *link);
        void (*dealloc)(struct bpf_link *link);
-
+       int (*update_prog)(struct bpf_link *link, struct bpf_prog *new_prog,
+                          struct bpf_prog *old_prog);
+       void (*show_fdinfo)(const struct bpf_link *link, struct seq_file *seq);
+       int (*fill_link_info)(const struct bpf_link *link,
+                             struct bpf_link_info *info);
 };
 
-void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops,
-                  struct bpf_prog *prog);
-void bpf_link_cleanup(struct bpf_link *link, struct file *link_file,
-                     int link_fd);
+void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
+                  const struct bpf_link_ops *ops, struct bpf_prog *prog);
+int bpf_link_prime(struct bpf_link *link, struct bpf_link_primer *primer);
+int bpf_link_settle(struct bpf_link_primer *primer);
+void bpf_link_cleanup(struct bpf_link_primer *primer);
 void bpf_link_inc(struct bpf_link *link);
 void bpf_link_put(struct bpf_link *link);
 int bpf_link_new_fd(struct bpf_link *link);
@@ -1215,6 +1232,7 @@ int btf_check_type_match(struct bpf_verifier_env *env, struct bpf_prog *prog,
 
 struct bpf_prog *bpf_prog_by_id(u32 id);
 
+const struct bpf_func_proto *bpf_base_func_proto(enum bpf_func_id func_id);
 #else /* !CONFIG_BPF_SYSCALL */
 static inline struct bpf_prog *bpf_prog_get(u32 ufd)
 {
@@ -1365,6 +1383,12 @@ static inline struct bpf_prog *bpf_prog_by_id(u32 id)
 {
        return ERR_PTR(-ENOTSUPP);
 }
+
+static inline const struct bpf_func_proto *
+bpf_base_func_proto(enum bpf_func_id func_id)
+{
+       return NULL;
+}
 #endif /* CONFIG_BPF_SYSCALL */
 
 static inline struct bpf_prog *bpf_prog_get_type(u32 ufd,
@@ -1502,6 +1526,7 @@ extern const struct bpf_func_proto bpf_get_smp_processor_id_proto;
 extern const struct bpf_func_proto bpf_get_numa_node_id_proto;
 extern const struct bpf_func_proto bpf_tail_call_proto;
 extern const struct bpf_func_proto bpf_ktime_get_ns_proto;
+extern const struct bpf_func_proto bpf_ktime_get_boot_ns_proto;
 extern const struct bpf_func_proto bpf_get_current_pid_tgid_proto;
 extern const struct bpf_func_proto bpf_get_current_uid_gid_proto;
 extern const struct bpf_func_proto bpf_get_current_comm_proto;
@@ -1523,6 +1548,7 @@ extern const struct bpf_func_proto bpf_strtoul_proto;
 extern const struct bpf_func_proto bpf_tcp_sock_proto;
 extern const struct bpf_func_proto bpf_jiffies64_proto;
 extern const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto;
+extern const struct bpf_func_proto bpf_event_output_data_proto;
 
 const struct bpf_func_proto *bpf_tracing_func_proto(
        enum bpf_func_id func_id, const struct bpf_prog *prog);
@@ -1530,6 +1556,7 @@ const struct bpf_func_proto *bpf_tracing_func_proto(
 /* Shared helpers among cBPF and eBPF. */
 void bpf_user_rnd_init_once(void);
 u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
+u64 bpf_get_raw_cpu_id(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
 
 #if defined(CONFIG_NET)
 bool bpf_sock_common_is_valid_access(int off, int size,
index ba0c2d5..8345cdf 100644 (file)
@@ -118,3 +118,9 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_STACK, stack_map_ops)
 #if defined(CONFIG_BPF_JIT)
 BPF_MAP_TYPE(BPF_MAP_TYPE_STRUCT_OPS, bpf_struct_ops_map_ops)
 #endif
+
+BPF_LINK_TYPE(BPF_LINK_TYPE_RAW_TRACEPOINT, raw_tracepoint)
+BPF_LINK_TYPE(BPF_LINK_TYPE_TRACING, tracing)
+#ifdef CONFIG_CGROUP_BPF
+BPF_LINK_TYPE(BPF_LINK_TYPE_CGROUP, cgroup)
+#endif
index 6462c54..58d0150 100644 (file)
@@ -15,6 +15,7 @@
 #define PHY_ID_BCMAC131                        0x0143bc70
 #define PHY_ID_BCM5481                 0x0143bca0
 #define PHY_ID_BCM5395                 0x0143bcf0
+#define PHY_ID_BCM53125                        0x03625f20
 #define PHY_ID_BCM54810                        0x03625d00
 #define PHY_ID_BCM5482                 0x0143bcb0
 #define PHY_ID_BCM5411                 0x00206070
@@ -24,6 +25,7 @@
 #define PHY_ID_BCM5461                 0x002060c0
 #define PHY_ID_BCM54612E               0x03625e60
 #define PHY_ID_BCM54616S               0x03625d10
+#define PHY_ID_BCM54140                        0xae025009
 #define PHY_ID_BCM57780                        0x03625d90
 #define PHY_ID_BCM89610                        0x03625cd0
 
 #define MII_BCM54XX_SHD_VAL(x) ((x & 0x1f) << 10)
 #define MII_BCM54XX_SHD_DATA(x)        ((x & 0x3ff) << 0)
 
+#define MII_BCM54XX_RDB_ADDR   0x1e
+#define MII_BCM54XX_RDB_DATA   0x1f
+
 /*
  * AUXILIARY CONTROL SHADOW ACCESS REGISTERS.  (PHY REG 0x18)
  */
index e0b020e..15b765a 100644 (file)
@@ -189,6 +189,8 @@ struct buffer_head *__getblk_gfp(struct block_device *bdev, sector_t block,
 void __brelse(struct buffer_head *);
 void __bforget(struct buffer_head *);
 void __breadahead(struct block_device *, sector_t block, unsigned int size);
+void __breadahead_gfp(struct block_device *, sector_t block, unsigned int size,
+                 gfp_t gfp);
 struct buffer_head *__bread_gfp(struct block_device *,
                                sector_t block, unsigned size, gfp_t gfp);
 void invalidate_bh_lrus(void);
@@ -319,6 +321,12 @@ sb_breadahead(struct super_block *sb, sector_t block)
        __breadahead(sb->s_bdev, block, sb->s_blocksize);
 }
 
+static inline void
+sb_breadahead_unmovable(struct super_block *sb, sector_t block)
+{
+       __breadahead_gfp(sb->s_bdev, block, sb->s_blocksize, 0);
+}
+
 static inline struct buffer_head *
 sb_getblk(struct super_block *sb, sector_t block)
 {
index 511a373..5fd627e 100644 (file)
@@ -189,7 +189,7 @@ struct __packed pucan_rx_msg {
        u8      client;
        __le16  flags;
        __le32  can_id;
-       u8      d[0];
+       u8      d[];
 };
 
 /* uCAN error types */
@@ -266,7 +266,7 @@ struct __packed pucan_tx_msg {
        u8      client;
        __le16  flags;
        __le32  can_id;
-       u8      d[0];
+       u8      d[];
 };
 
 /* build the cmd opcode_channel field with respect to the correct endianness */
index 4b898cd..a0eabfb 100644 (file)
@@ -86,7 +86,7 @@ static inline unsigned long compact_gap(unsigned int order)
 #ifdef CONFIG_COMPACTION
 extern int sysctl_compact_memory;
 extern int sysctl_compaction_handler(struct ctl_table *table, int write,
-                       void __user *buffer, size_t *length, loff_t *ppos);
+                       void *buffer, size_t *length, loff_t *ppos);
 extern int sysctl_extfrag_threshold;
 extern int sysctl_compact_unevictable_allowed;
 
index abf4b4e..7a899e8 100644 (file)
@@ -22,4 +22,8 @@ extern void do_coredump(const kernel_siginfo_t *siginfo);
 static inline void do_coredump(const kernel_siginfo_t *siginfo) {}
 #endif
 
+extern int core_uses_pid;
+extern char core_pattern[];
+extern unsigned int core_pipe_limit;
+
 #endif /* _LINUX_COREDUMP_H */
index 02edeaf..be8aea0 100644 (file)
@@ -28,7 +28,7 @@ struct cpu_rmap {
        struct {
                u16     index;
                u16     dist;
-       }               near[0];
+       }               near[];
 };
 #define CPU_RMAP_DIST_INF 0xffff
 
index a274d95..63cb360 100644 (file)
@@ -103,8 +103,8 @@ void debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent,
                       u8 *value);
 void debugfs_create_u16(const char *name, umode_t mode, struct dentry *parent,
                        u16 *value);
-struct dentry *debugfs_create_u32(const char *name, umode_t mode,
-                                 struct dentry *parent, u32 *value);
+void debugfs_create_u32(const char *name, umode_t mode, struct dentry *parent,
+                       u32 *value);
 void debugfs_create_u64(const char *name, umode_t mode, struct dentry *parent,
                        u64 *value);
 struct dentry *debugfs_create_ulong(const char *name, umode_t mode,
@@ -250,12 +250,8 @@ static inline void debugfs_create_u8(const char *name, umode_t mode,
 static inline void debugfs_create_u16(const char *name, umode_t mode,
                                      struct dentry *parent, u16 *value) { }
 
-static inline struct dentry *debugfs_create_u32(const char *name, umode_t mode,
-                                               struct dentry *parent,
-                                               u32 *value)
-{
-       return ERR_PTR(-ENODEV);
-}
+static inline void debugfs_create_u32(const char *name, umode_t mode,
+                                     struct dentry *parent, u32 *value) { }
 
 static inline void debugfs_create_u64(const char *name, umode_t mode,
                                      struct dentry *parent, u64 *value) { }
index 8e68280..5e016a4 100644 (file)
@@ -65,4 +65,15 @@ static inline void ssleep(unsigned int seconds)
        msleep(seconds * 1000);
 }
 
+/* see Documentation/timers/timers-howto.rst for the thresholds */
+static inline void fsleep(unsigned long usecs)
+{
+       if (usecs <= 10)
+               udelay(usecs);
+       else if (usecs <= 20000)
+               usleep_range(usecs, 2 * usecs);
+       else
+               msleep(DIV_ROUND_UP(usecs, 1000));
+}
+
 #endif /* defined(_LINUX_DELAY_H) */
index 594fc66..2ace69e 100644 (file)
@@ -29,7 +29,7 @@ struct pubkey_hdr {
        uint32_t        timestamp;      /* key made, always 0 for now */
        uint8_t         algo;
        uint8_t         nmpi;
-       char            mpi[0];
+       char            mpi[];
 } __packed;
 
 struct signature_hdr {
@@ -39,7 +39,7 @@ struct signature_hdr {
        uint8_t         hash;
        uint8_t         keyid[8];
        uint8_t         nmpi;
-       char            mpi[0];
+       char            mpi[];
 } __packed;
 
 #if defined(CONFIG_SIGNATURE) || defined(CONFIG_SIGNATURE_MODULE)
index fc61f3c..9900222 100644 (file)
@@ -7,7 +7,7 @@ struct linux_dirent64 {
        s64             d_off;
        unsigned short  d_reclen;
        unsigned char   d_type;
-       char            d_name[0];
+       char            d_name[];
 };
 
 #endif
index 1ade486..57bcef6 100644 (file)
@@ -329,13 +329,12 @@ struct dma_buf {
 
 /**
  * struct dma_buf_attach_ops - importer operations for an attachment
- * @move_notify: [optional] notification that the DMA-buf is moving
  *
  * Attachment operations implemented by the importer.
  */
 struct dma_buf_attach_ops {
        /**
-        * @move_notify
+        * @move_notify: [optional] notification that the DMA-buf is moving
         *
         * If this callback is provided the framework can avoid pinning the
         * backing store while mappings exists.
index 21065c0..e1c0333 100644 (file)
@@ -83,9 +83,9 @@ enum dma_transfer_direction {
 /**
  * Interleaved Transfer Request
  * ----------------------------
- * A chunk is collection of contiguous bytes to be transfered.
+ * A chunk is collection of contiguous bytes to be transferred.
  * The gap(in bytes) between two chunks is called inter-chunk-gap(ICG).
- * ICGs may or maynot change between chunks.
+ * ICGs may or may not change between chunks.
  * A FRAME is the smallest series of contiguous {chunk,icg} pairs,
  *  that when repeated an integral number of times, specifies the transfer.
  * A transfer template is specification of a Frame, the number of times
@@ -341,13 +341,11 @@ struct dma_chan {
  * @chan: driver channel device
  * @device: sysfs device
  * @dev_id: parent dma_device dev_id
- * @idr_ref: reference count to gate release of dma_device dev_id
  */
 struct dma_chan_dev {
        struct dma_chan *chan;
        struct device device;
        int dev_id;
-       atomic_t *idr_ref;
 };
 
 /**
@@ -835,6 +833,8 @@ struct dma_device {
        int dev_id;
        struct device *dev;
        struct module *owner;
+       struct ida chan_ida;
+       struct mutex chan_mutex;        /* to protect chan_ida */
 
        u32 src_addr_widths;
        u32 dst_addr_widths;
@@ -1069,7 +1069,7 @@ static inline int dmaengine_terminate_all(struct dma_chan *chan)
  * dmaengine_synchronize() needs to be called before it is safe to free
  * any memory that is accessed by previously submitted descriptors or before
  * freeing any resources accessed from within the completion callback of any
- * perviously submitted descriptors.
+ * previously submitted descriptors.
  *
  * This function can be called from atomic context as well as from within a
  * complete callback of a descriptor submitted on the same channel.
@@ -1091,7 +1091,7 @@ static inline int dmaengine_terminate_async(struct dma_chan *chan)
  *
  * Synchronizes to the DMA channel termination to the current context. When this
  * function returns it is guaranteed that all transfers for previously issued
- * descriptors have stopped and and it is safe to free the memory assoicated
+ * descriptors have stopped and it is safe to free the memory associated
  * with them. Furthermore it is guaranteed that all complete callback functions
  * for a previously submitted descriptor have finished running and it is safe to
  * free resources accessed from within the complete callbacks.
index 564e96f..1c630e2 100644 (file)
@@ -101,7 +101,7 @@ struct enclosure_device {
        struct device edev;
        struct enclosure_component_callbacks *cb;
        int components;
-       struct enclosure_component component[0];
+       struct enclosure_component component[];
 };
 
 static inline struct enclosure_device *
index d249b88..ade6486 100644 (file)
@@ -36,7 +36,7 @@ struct em_cap_state {
 struct em_perf_domain {
        struct em_cap_state *table;
        int nr_cap_states;
-       unsigned long cpus[0];
+       unsigned long cpus[];
 };
 
 #ifdef CONFIG_ENERGY_MODEL
index 8801f1f..2e5debc 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/if_ether.h>
 #include <linux/netdevice.h>
 #include <linux/random.h>
+#include <linux/crc32.h>
 #include <asm/unaligned.h>
 #include <asm/bitsperlong.h>
 
@@ -265,6 +266,17 @@ static inline void eth_hw_addr_random(struct net_device *dev)
        eth_random_addr(dev->dev_addr);
 }
 
+/**
+ * eth_hw_addr_crc - Calculate CRC from netdev_hw_addr
+ * @ha: pointer to hardware address
+ *
+ * Calculate CRC from a hardware address as basis for filter hashes.
+ */
+static inline u32 eth_hw_addr_crc(struct netdev_hw_addr *ha)
+{
+       return ether_crc(ETH_ALEN, ha->addr);
+}
+
 /**
  * ether_addr_copy - Copy an Ethernet address
  * @dst: Pointer to a six-byte array Ethernet address destination
index c1d379b..a23b26e 100644 (file)
@@ -35,7 +35,7 @@ struct compat_ethtool_rxnfc {
        compat_u64                      data;
        struct compat_ethtool_rx_flow_spec fs;
        u32                             rule_cnt;
-       u32                             rule_locs[0];
+       u32                             rule_locs[];
 };
 
 #endif /* CONFIG_COMPAT */
@@ -462,7 +462,7 @@ int ethtool_check_ops(const struct ethtool_ops *ops);
 
 struct ethtool_rx_flow_rule {
        struct flow_rule        *rule;
-       unsigned long           priv[0];
+       unsigned long           priv[];
 };
 
 struct ethtool_rx_flow_spec_input {
index 142d102..122f800 100644 (file)
@@ -94,4 +94,6 @@ extern void fd_install(unsigned int fd, struct file *file);
 extern void flush_delayed_fput(void);
 extern void __fput_sync(struct file *);
 
+extern unsigned int sysctl_nr_open_min, sysctl_nr_open_max;
+
 #endif /* __LINUX_FILE_H */
index 9b5aa5c..af37318 100644 (file)
@@ -863,8 +863,6 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog);
 int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog,
                              bpf_aux_classic_check_t trans, bool save_orig);
 void bpf_prog_destroy(struct bpf_prog *fp);
-const struct bpf_func_proto *
-bpf_base_func_proto(enum bpf_func_id func_id);
 
 int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
 int sk_attach_bpf(u32 ufd, struct sock *sk);
index 4f6f59b..5ee9e58 100644 (file)
@@ -983,7 +983,7 @@ struct file_handle {
        __u32 handle_bytes;
        int handle_type;
        /* file identifier */
-       unsigned char f_handle[0];
+       unsigned char f_handle[];
 };
 
 static inline struct file *get_file(struct file *f)
@@ -3536,11 +3536,11 @@ ssize_t simple_attr_write(struct file *file, const char __user *buf,
 
 struct ctl_table;
 int proc_nr_files(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos);
+                 void *buffer, size_t *lenp, loff_t *ppos);
 int proc_nr_dentry(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos);
+                 void *buffer, size_t *lenp, loff_t *ppos);
 int proc_nr_inodes(struct ctl_table *table, int write,
-                  void __user *buffer, size_t *lenp, loff_t *ppos);
+                  void *buffer, size_t *lenp, loff_t *ppos);
 int __init get_filesystem_list(char *buf);
 
 #define __FMODE_EXEC           ((__force int) FMODE_EXEC)
index 7588456..884b8f8 100644 (file)
@@ -135,7 +135,7 @@ struct ptp_qoriq_registers {
 #define DEFAULT_CKSEL          1
 #define DEFAULT_TMR_PRSC       2
 #define DEFAULT_FIPER1_PERIOD  1000000000
-#define DEFAULT_FIPER2_PERIOD  100000
+#define DEFAULT_FIPER2_PERIOD  1000000000
 
 struct ptp_qoriq {
        void __iomem *base;
index db95244..ddfc377 100644 (file)
@@ -1005,8 +1005,7 @@ extern void disable_trace_on_warning(void);
 extern int __disable_trace_on_warning;
 
 int tracepoint_printk_sysctl(struct ctl_table *table, int write,
-                            void __user *buffer, size_t *lenp,
-                            loff_t *ppos);
+                            void *buffer, size_t *lenp, loff_t *ppos);
 
 #else /* CONFIG_TRACING */
 static inline void  disable_trace_on_warning(void) { }
index 5b14a0f..0bd5810 100644 (file)
@@ -76,7 +76,7 @@ struct gen_pool_chunk {
        void *owner;                    /* private data to retrieve at alloc time */
        unsigned long start_addr;       /* start address of memory chunk */
        unsigned long end_addr;         /* end address of memory chunk (inclusive) */
-       unsigned long bits[0];          /* bitmap for allocating memory chunk */
+       unsigned long bits[];           /* bitmap for allocating memory chunk */
 };
 
 /*
index 43a1cef..92c21c5 100644 (file)
@@ -105,14 +105,13 @@ struct hugepage_subpool *hugepage_new_subpool(struct hstate *h, long max_hpages,
 void hugepage_put_subpool(struct hugepage_subpool *spool);
 
 void reset_vma_resv_huge_pages(struct vm_area_struct *vma);
-int hugetlb_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);
-int hugetlb_overcommit_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);
-int hugetlb_treat_movable_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);
-
-#ifdef CONFIG_NUMA
-int hugetlb_mempolicy_sysctl_handler(struct ctl_table *, int,
-                                       void __user *, size_t *, loff_t *);
-#endif
+int hugetlb_sysctl_handler(struct ctl_table *, int, void *, size_t *, loff_t *);
+int hugetlb_overcommit_handler(struct ctl_table *, int, void *, size_t *,
+               loff_t *);
+int hugetlb_treat_movable_handler(struct ctl_table *, int, void *, size_t *,
+               loff_t *);
+int hugetlb_mempolicy_sysctl_handler(struct ctl_table *, int, void *, size_t *,
+               loff_t *);
 
 int copy_hugetlb_page_range(struct mm_struct *, struct mm_struct *, struct vm_area_struct *);
 long follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *,
index 456fc17..45d36ba 100644 (file)
@@ -461,12 +461,6 @@ i2c_new_scanned_device(struct i2c_adapter *adap,
                       unsigned short const *addr_list,
                       int (*probe)(struct i2c_adapter *adap, unsigned short addr));
 
-struct i2c_client *
-i2c_new_probed_device(struct i2c_adapter *adap,
-                      struct i2c_board_info *info,
-                      unsigned short const *addr_list,
-                      int (*probe)(struct i2c_adapter *adap, unsigned short addr));
-
 /* Common custom probe functions */
 int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr);
 
index 9e57c44..b3a8d30 100644 (file)
@@ -47,6 +47,8 @@ struct br_ip_list {
 #define BR_BCAST_FLOOD         BIT(14)
 #define BR_NEIGH_SUPPRESS      BIT(15)
 #define BR_ISOLATED            BIT(16)
+#define BR_MRP_AWARE           BIT(17)
+#define BR_MRP_LOST_CONT       BIT(18)
 
 #define BR_DEFAULT_AGEING_TIME (300 * HZ)
 
index 463047d..faa6586 100644 (file)
@@ -38,7 +38,7 @@ struct ip_sf_socklist {
        unsigned int            sl_max;
        unsigned int            sl_count;
        struct rcu_head         rcu;
-       __be32                  sl_addr[0];
+       __be32                  sl_addr[];
 };
 
 #define IP_SFLSIZE(count)      (sizeof(struct ip_sf_socklist) + \
index 98cb5ce..b824877 100644 (file)
@@ -18,7 +18,7 @@
 struct ihex_binrec {
        __be32 addr;
        __be16 len;
-       uint8_t data[0];
+       uint8_t data[];
 } __attribute__((packed));
 
 static inline uint16_t ihex_binrec_size(const struct ihex_binrec *p)
index 17f56a0..25c8750 100644 (file)
@@ -600,7 +600,7 @@ void iio_device_unregister(struct iio_dev *indio_dev);
  * 0 on success, negative error number on failure.
  */
 #define devm_iio_device_register(dev, indio_dev) \
-       __devm_iio_device_register((dev), (indio_dev), THIS_MODULE);
+       __devm_iio_device_register((dev), (indio_dev), THIS_MODULE)
 int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev,
                               struct module *this_mod);
 void devm_iio_device_unregister(struct device *dev, struct iio_dev *indio_dev);
index ce9ed1c..0ef2d80 100644 (file)
@@ -71,7 +71,11 @@ static inline size_t inet_diag_msg_attrs_size(void)
                + nla_total_size(1)  /* INET_DIAG_SKV6ONLY */
 #endif
                + nla_total_size(4)  /* INET_DIAG_MARK */
-               + nla_total_size(4); /* INET_DIAG_CLASS_ID */
+               + nla_total_size(4)  /* INET_DIAG_CLASS_ID */
+#ifdef CONFIG_SOCK_CGROUP_DATA
+               + nla_total_size_64bit(sizeof(u64))  /* INET_DIAG_CGROUP_ID */
+#endif
+               ;
 }
 int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
                             struct inet_diag_msg *r, int ext,
index 9315fbb..8d5bc2c 100644 (file)
@@ -573,8 +573,6 @@ enum {
 #define IRQ_DEFAULT_INIT_FLAGS ARCH_IRQ_INIT_FLAGS
 
 struct irqaction;
-extern int setup_irq(unsigned int irq, struct irqaction *new);
-extern void remove_irq(unsigned int irq, struct irqaction *act);
 extern int setup_percpu_irq(unsigned int irq, struct irqaction *new);
 extern void remove_percpu_irq(unsigned int irq, struct irqaction *act);
 
@@ -1043,7 +1041,7 @@ struct irq_chip_generic {
        unsigned long           unused;
        struct irq_domain       *domain;
        struct list_head        list;
-       struct irq_chip_type    chip_types[0];
+       struct irq_chip_type    chip_types[];
 };
 
 /**
@@ -1079,7 +1077,7 @@ struct irq_domain_chip_generic {
        unsigned int            irq_flags_to_clear;
        unsigned int            irq_flags_to_set;
        enum irq_gc_flags       gc_flags;
-       struct irq_chip_generic *gc[0];
+       struct irq_chip_generic *gc[];
 };
 
 /* Generic chip callback functions */
index 765d9b7..6c36b6c 100644 (file)
 
 #define GICR_TYPER_PLPIS               (1U << 0)
 #define GICR_TYPER_VLPIS               (1U << 1)
+#define GICR_TYPER_DIRTY               (1U << 2)
 #define GICR_TYPER_DirectLPIS          (1U << 3)
 #define GICR_TYPER_LAST                        (1U << 4)
 #define GICR_TYPER_RVPEID              (1U << 7)
@@ -686,6 +687,7 @@ struct rdists {
        bool                    has_vlpis;
        bool                    has_rvpeid;
        bool                    has_direct_lpi;
+       bool                    has_vpend_valid_dirty;
 };
 
 struct irq_domain;
index 04bdaf0..594265b 100644 (file)
@@ -312,7 +312,7 @@ DEFINE_INSN_CACHE_OPS(optinsn);
 #ifdef CONFIG_SYSCTL
 extern int sysctl_kprobes_optimization;
 extern int proc_kprobes_optimization_handler(struct ctl_table *table,
-                                            int write, void __user *buffer,
+                                            int write, void *buffer,
                                             size_t *length, loff_t *ppos);
 #endif
 extern void wait_for_kprobe_optimizer(void);
index 6d58beb..01276e3 100644 (file)
@@ -1048,7 +1048,7 @@ search_memslots(struct kvm_memslots *slots, gfn_t gfn)
                        start = slot + 1;
        }
 
-       if (gfn >= memslots[start].base_gfn &&
+       if (start < slots->used_slots && gfn >= memslots[start].base_gfn &&
            gfn < memslots[start].base_gfn + memslots[start].npages) {
                atomic_set(&slots->lru_slot, start);
                return &memslots[start];
index 9022f0c..abe3d95 100644 (file)
@@ -38,8 +38,8 @@ account_scheduler_latency(struct task_struct *task, int usecs, int inter)
 
 void clear_tsk_latency_tracing(struct task_struct *p);
 
-extern int sysctl_latencytop(struct ctl_table *table, int write,
-                       void __user *buffer, size_t *lenp, loff_t *ppos);
+int sysctl_latencytop(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos);
 
 #else
 
index d5ceb28..9dcaa3e 100644 (file)
@@ -34,7 +34,7 @@ struct list_lru_one {
 struct list_lru_memcg {
        struct rcu_head         rcu;
        /* array of per cgroup lists, indexed by memcg_cache_id */
-       struct list_lru_one     *lru[0];
+       struct list_lru_one     *lru[];
 };
 
 struct list_lru_node {
index 1b4150f..d275c72 100644 (file)
@@ -106,7 +106,7 @@ struct lruvec_stat {
  */
 struct memcg_shrinker_map {
        struct rcu_head rcu;
-       unsigned long map[0];
+       unsigned long map[];
 };
 
 /*
@@ -148,7 +148,7 @@ struct mem_cgroup_threshold_ary {
        /* Size of entries[] */
        unsigned int size;
        /* Array of thresholds */
-       struct mem_cgroup_threshold entries[0];
+       struct mem_cgroup_threshold entries[];
 };
 
 struct mem_cgroup_thresholds {
index 5613e67..b919d14 100644 (file)
@@ -92,6 +92,18 @@ struct mlx5_accel_esp_xfrm_attrs {
        union {
                struct aes_gcm_keymat aes_gcm;
        } keymat;
+
+       union {
+               __be32 a4;
+               __be32 a6[4];
+       } saddr;
+
+       union {
+               __be32 a4;
+               __be32 a6[4];
+       } daddr;
+
+       u8 is_ipv6;
 };
 
 struct mlx5_accel_esp_xfrm {
diff --git a/include/linux/mlx5/cmd.h b/include/linux/mlx5/cmd.h
deleted file mode 100644 (file)
index 68cd08f..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
- *
- * This software is available to you under a choice of one of two
- * licenses.  You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available from the file
- * COPYING in the main directory of this source tree, or the
- * OpenIB.org BSD license below:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      - Redistributions of source code must retain the above
- *        copyright notice, this list of conditions and the following
- *        disclaimer.
- *
- *      - Redistributions in binary form must reproduce the above
- *        copyright notice, this list of conditions and the following
- *        disclaimer in the documentation and/or other materials
- *        provided with the distribution.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#ifndef MLX5_CMD_H
-#define MLX5_CMD_H
-
-#include <linux/types.h>
-
-struct manage_pages_layout {
-       u64     ptr;
-       u32     reserved;
-       u16     num_entries;
-       u16     func_id;
-};
-
-
-struct mlx5_cmd_alloc_uar_imm_out {
-       u32     rsvd[3];
-       u32     uarn;
-};
-
-#endif /* MLX5_CMD_H */
index 40748fc..b5a9399 100644 (file)
@@ -188,7 +188,7 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
                        u32 *in, int inlen, u32 *out, int outlen);
 int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq);
 int mlx5_core_query_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
-                      u32 *out, int outlen);
+                      u32 *out);
 int mlx5_core_modify_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
                        u32 *in, int inlen);
 int mlx5_core_modify_cq_moderation(struct mlx5_core_dev *dev,
index 2b90097..1bc27ac 100644 (file)
@@ -364,6 +364,7 @@ enum {
 enum {
        MLX5_GENERAL_SUBTYPE_DELAY_DROP_TIMEOUT = 0x1,
        MLX5_GENERAL_SUBTYPE_PCI_POWER_CHANGE_EVENT = 0x5,
+       MLX5_GENERAL_SUBTYPE_PCI_SYNC_FOR_FW_UPDATE_EVENT = 0x8,
 };
 
 enum {
@@ -449,10 +450,12 @@ enum {
 
 enum {
        MLX5_OPC_MOD_TLS_TIS_STATIC_PARAMS = 0x1,
+       MLX5_OPC_MOD_TLS_TIR_STATIC_PARAMS = 0x2,
 };
 
 enum {
        MLX5_OPC_MOD_TLS_TIS_PROGRESS_PARAMS = 0x1,
+       MLX5_OPC_MOD_TLS_TIR_PROGRESS_PARAMS = 0x2,
 };
 
 enum {
@@ -689,6 +692,19 @@ struct mlx5_eqe_temp_warning {
        __be64 sensor_warning_lsb;
 } __packed;
 
+#define SYNC_RST_STATE_MASK    0xf
+
+enum sync_rst_state_type {
+       MLX5_SYNC_RST_STATE_RESET_REQUEST       = 0x0,
+       MLX5_SYNC_RST_STATE_RESET_NOW           = 0x1,
+       MLX5_SYNC_RST_STATE_RESET_ABORT         = 0x2,
+};
+
+struct mlx5_eqe_sync_fw_update {
+       u8 reserved_at_0[3];
+       u8 sync_rst_state;
+};
+
 union ev_data {
        __be32                          raw[7];
        struct mlx5_eqe_cmd             cmd;
@@ -707,6 +723,7 @@ union ev_data {
        struct mlx5_eqe_dct             dct;
        struct mlx5_eqe_temp_warning    temp_warning;
        struct mlx5_eqe_xrq_err         xrq_err;
+       struct mlx5_eqe_sync_fw_update  sync_fw_update;
 } __packed;
 
 struct mlx5_eqe {
@@ -749,7 +766,7 @@ struct mlx5_err_cqe {
 };
 
 struct mlx5_cqe64 {
-       u8              outer_l3_tunneled;
+       u8              tls_outer_l3_tunneled;
        u8              rsvd0;
        __be16          wqe_id;
        u8              lro_tcppsh_abort_dupack;
@@ -767,7 +784,12 @@ struct mlx5_cqe64 {
        u8              l4_l3_hdr_type;
        __be16          vlan_info;
        __be32          srqn; /* [31:24]: lro_num_seg, [23:0]: srqn */
-       __be32          imm_inval_pkey;
+       union {
+               __be32 immediate;
+               __be32 inval_rkey;
+               __be32 pkey;
+               __be32 ft_metadata;
+       };
        u8              rsvd40[4];
        __be32          byte_cnt;
        __be32          timestamp_h;
@@ -834,7 +856,12 @@ static inline u8 get_cqe_l3_hdr_type(struct mlx5_cqe64 *cqe)
 
 static inline bool cqe_is_tunneled(struct mlx5_cqe64 *cqe)
 {
-       return cqe->outer_l3_tunneled & 0x1;
+       return cqe->tls_outer_l3_tunneled & 0x1;
+}
+
+static inline u8 get_cqe_tls_offload(struct mlx5_cqe64 *cqe)
+{
+       return (cqe->tls_outer_l3_tunneled >> 3) & 0x3;
 }
 
 static inline bool cqe_has_vlan(struct mlx5_cqe64 *cqe)
@@ -922,6 +949,13 @@ enum {
        CQE_L4_OK       = 1 << 2,
 };
 
+enum {
+       CQE_TLS_OFFLOAD_NOT_DECRYPTED           = 0x0,
+       CQE_TLS_OFFLOAD_DECRYPTED               = 0x1,
+       CQE_TLS_OFFLOAD_RESYNC                  = 0x2,
+       CQE_TLS_OFFLOAD_ERROR                   = 0x3,
+};
+
 struct mlx5_sig_err_cqe {
        u8              rsvd0[16];
        __be32          expected_trans_sig;
@@ -1107,6 +1141,7 @@ enum mlx5_cap_type {
        MLX5_CAP_TLS,
        MLX5_CAP_VDPA_EMULATION = 0x13,
        MLX5_CAP_DEV_EVENT = 0x14,
+       MLX5_CAP_IPSEC,
        /* NUM OF CAP Types */
        MLX5_CAP_NUM
 };
@@ -1324,6 +1359,9 @@ enum mlx5_qcam_feature_groups {
        MLX5_GET64(device_virtio_emulation_cap, \
                (mdev)->caps.hca_cur[MLX5_CAP_VDPA_EMULATION], cap)
 
+#define MLX5_CAP_IPSEC(mdev, cap)\
+       MLX5_GET(ipsec_cap, (mdev)->caps.hca_cur[MLX5_CAP_IPSEC], cap)
+
 enum {
        MLX5_CMD_STAT_OK                        = 0x0,
        MLX5_CMD_STAT_INT_ERR                   = 0x1,
index 6f8f79e..d82dbba 100644 (file)
@@ -130,6 +130,7 @@ enum {
        MLX5_REG_NODE_DESC       = 0x6001,
        MLX5_REG_HOST_ENDIANNESS = 0x7004,
        MLX5_REG_MCIA            = 0x9014,
+       MLX5_REG_MFRL            = 0x9028,
        MLX5_REG_MLCR            = 0x902b,
        MLX5_REG_MTRC_CAP        = 0x9040,
        MLX5_REG_MTRC_CONF       = 0x9041,
@@ -541,7 +542,6 @@ struct mlx5_priv {
        struct mlx5_core_health health;
 
        /* start: qp staff */
-       struct mlx5_qp_table    qp_table;
        struct dentry          *qp_debugfs;
        struct dentry          *eq_debugfs;
        struct dentry          *cq_debugfs;
@@ -687,7 +687,6 @@ struct mlx5_core_dev {
        unsigned long           intf_state;
        struct mlx5_priv        priv;
        struct mlx5_profile     *profile;
-       atomic_t                num_qps;
        u32                     issi;
        struct mlx5e_resources  mlx5e_res;
        struct mlx5_dm          *dm;
@@ -903,6 +902,19 @@ int mlx5_cmd_exec_cb(struct mlx5_async_ctx *ctx, void *in, int in_size,
 
 int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out,
                  int out_size);
+
+#define mlx5_cmd_exec_inout(dev, ifc_cmd, in, out)                             \
+       ({                                                                     \
+               mlx5_cmd_exec(dev, in, MLX5_ST_SZ_BYTES(ifc_cmd##_in), out,    \
+                             MLX5_ST_SZ_BYTES(ifc_cmd##_out));                \
+       })
+
+#define mlx5_cmd_exec_in(dev, ifc_cmd, in)                                     \
+       ({                                                                     \
+               u32 _out[MLX5_ST_SZ_DW(ifc_cmd##_out)] = {};                   \
+               mlx5_cmd_exec_inout(dev, ifc_cmd, in, _out);                   \
+       })
+
 int mlx5_cmd_exec_polling(struct mlx5_core_dev *dev, void *in, int in_size,
                          void *out, int out_size);
 void mlx5_cmd_mbox_status(void *out, u8 *status, u32 *syndrome);
@@ -1069,7 +1081,8 @@ int mlx5_lag_query_cong_counters(struct mlx5_core_dev *dev,
 struct mlx5_uars_page *mlx5_get_uars_page(struct mlx5_core_dev *mdev);
 void mlx5_put_uars_page(struct mlx5_core_dev *mdev, struct mlx5_uars_page *up);
 int mlx5_dm_sw_icm_alloc(struct mlx5_core_dev *dev, enum mlx5_sw_icm_type type,
-                        u64 length, u16 uid, phys_addr_t *addr, u32 *obj_id);
+                        u64 length, u32 log_alignment, u16 uid,
+                        phys_addr_t *addr, u32 *obj_id);
 int mlx5_dm_sw_icm_dealloc(struct mlx5_core_dev *dev, enum mlx5_sw_icm_type type,
                           u64 length, u16 uid, phys_addr_t addr, u32 obj_id);
 
index 69b27c7..fb24384 100644 (file)
@@ -74,6 +74,7 @@ enum {
        MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE        = 0x0,
        MLX5_SET_HCA_CAP_OP_MOD_ODP                   = 0x2,
        MLX5_SET_HCA_CAP_OP_MOD_ATOMIC                = 0x3,
+       MLX5_SET_HCA_CAP_OP_MOD_ROCE                  = 0x4,
 };
 
 enum {
@@ -885,7 +886,8 @@ struct mlx5_ifc_per_protocol_networking_offload_caps_bits {
        u8         tunnel_stateless_vxlan_gpe[0x1];
        u8         tunnel_stateless_ipv4_over_vxlan[0x1];
        u8         tunnel_stateless_ip_over_ip[0x1];
-       u8         reserved_at_2a[0x6];
+       u8         insert_trailer[0x1];
+       u8         reserved_at_2b[0x5];
        u8         max_vxlan_udp_ports[0x8];
        u8         reserved_at_38[0x6];
        u8         max_geneve_opt_len[0x1];
@@ -903,7 +905,9 @@ struct mlx5_ifc_per_protocol_networking_offload_caps_bits {
 
 struct mlx5_ifc_roce_cap_bits {
        u8         roce_apm[0x1];
-       u8         reserved_at_1[0x1f];
+       u8         reserved_at_1[0x3];
+       u8         sw_r_roce_src_udp_port[0x1];
+       u8         reserved_at_5[0x1b];
 
        u8         reserved_at_20[0x60];
 
@@ -1097,6 +1101,23 @@ struct mlx5_ifc_tls_cap_bits {
        u8         reserved_at_20[0x7e0];
 };
 
+struct mlx5_ifc_ipsec_cap_bits {
+       u8         ipsec_full_offload[0x1];
+       u8         ipsec_crypto_offload[0x1];
+       u8         ipsec_esn[0x1];
+       u8         ipsec_crypto_esp_aes_gcm_256_encrypt[0x1];
+       u8         ipsec_crypto_esp_aes_gcm_128_encrypt[0x1];
+       u8         ipsec_crypto_esp_aes_gcm_256_decrypt[0x1];
+       u8         ipsec_crypto_esp_aes_gcm_128_decrypt[0x1];
+       u8         reserved_at_7[0x4];
+       u8         log_max_ipsec_offload[0x5];
+       u8         reserved_at_10[0x10];
+
+       u8         min_log_ipsec_full_replay_window[0x8];
+       u8         max_log_ipsec_full_replay_window[0x8];
+       u8         reserved_at_30[0x7d0];
+};
+
 enum {
        MLX5_WQ_TYPE_LINKED_LIST  = 0x0,
        MLX5_WQ_TYPE_CYCLIC       = 0x1,
@@ -1223,7 +1244,9 @@ struct mlx5_ifc_cmd_hca_cap_bits {
        u8         reserved_at_130[0xa];
        u8         log_max_ra_res_dc[0x6];
 
-       u8         reserved_at_140[0x9];
+       u8         reserved_at_140[0x6];
+       u8         release_all_pages[0x1];
+       u8         reserved_at_147[0x2];
        u8         roce_accl[0x1];
        u8         log_max_ra_req_qp[0x6];
        u8         reserved_at_150[0xa];
@@ -1296,7 +1319,9 @@ struct mlx5_ifc_cmd_hca_cap_bits {
        u8         wol_p[0x1];
 
        u8         stat_rate_support[0x10];
-       u8         reserved_at_1f0[0xc];
+       u8         reserved_at_1f0[0x1];
+       u8         pci_sync_for_fw_update_event[0x1];
+       u8         reserved_at_1f2[0xa];
        u8         cqe_version[0x4];
 
        u8         compact_address_vector[0x1];
@@ -1461,13 +1486,14 @@ struct mlx5_ifc_cmd_hca_cap_bits {
 
        u8         reserved_at_460[0x3];
        u8         log_max_uctx[0x5];
-       u8         reserved_at_468[0x3];
+       u8         reserved_at_468[0x2];
+       u8         ipsec_offload[0x1];
        u8         log_max_umem[0x5];
        u8         max_num_eqs[0x10];
 
        u8         reserved_at_480[0x1];
        u8         tls_tx[0x1];
-       u8         reserved_at_482[0x1];
+       u8         tls_rx[0x1];
        u8         log_max_l2_table[0x5];
        u8         reserved_at_488[0x8];
        u8         log_uar_page_sz[0x10];
@@ -3112,7 +3138,8 @@ struct mlx5_ifc_tirc_bits {
        u8         reserved_at_0[0x20];
 
        u8         disp_type[0x4];
-       u8         reserved_at_24[0x1c];
+       u8         tls_en[0x1];
+       u8         reserved_at_25[0x1b];
 
        u8         reserved_at_40[0x40];
 
@@ -4140,7 +4167,8 @@ enum {
        MLX5_SET_FTE_MODIFY_ENABLE_MASK_ACTION    = 0x0,
        MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_TAG  = 0x1,
        MLX5_SET_FTE_MODIFY_ENABLE_MASK_DESTINATION_LIST    = 0x2,
-       MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS    = 0x3
+       MLX5_SET_FTE_MODIFY_ENABLE_MASK_FLOW_COUNTERS    = 0x3,
+       MLX5_SET_FTE_MODIFY_ENABLE_MASK_IPSEC_OBJ_ID    = 0x4
 };
 
 struct mlx5_ifc_set_fte_out_bits {
@@ -5667,9 +5695,9 @@ struct mlx5_ifc_copy_action_in_bits {
        u8         reserved_at_38[0x8];
 };
 
-union mlx5_ifc_set_action_in_add_action_in_auto_bits {
-       struct mlx5_ifc_set_action_in_bits set_action_in;
-       struct mlx5_ifc_add_action_in_bits add_action_in;
+union mlx5_ifc_set_add_copy_action_in_auto_bits {
+       struct mlx5_ifc_set_action_in_bits  set_action_in;
+       struct mlx5_ifc_add_action_in_bits  add_action_in;
        struct mlx5_ifc_copy_action_in_bits copy_action_in;
        u8         reserved_at_0[0x40];
 };
@@ -5743,7 +5771,7 @@ struct mlx5_ifc_alloc_modify_header_context_in_bits {
        u8         reserved_at_68[0x10];
        u8         num_of_actions[0x8];
 
-       union mlx5_ifc_set_action_in_add_action_in_auto_bits actions[0];
+       union mlx5_ifc_set_add_copy_action_in_auto_bits actions[0];
 };
 
 struct mlx5_ifc_dealloc_modify_header_context_out_bits {
@@ -9680,6 +9708,29 @@ struct mlx5_ifc_mcda_reg_bits {
        u8         data[0][0x20];
 };
 
+enum {
+       MLX5_MFRL_REG_RESET_TYPE_FULL_CHIP = BIT(0),
+       MLX5_MFRL_REG_RESET_TYPE_NET_PORT_ALIVE = BIT(1),
+};
+
+enum {
+       MLX5_MFRL_REG_RESET_LEVEL0 = BIT(0),
+       MLX5_MFRL_REG_RESET_LEVEL3 = BIT(3),
+       MLX5_MFRL_REG_RESET_LEVEL6 = BIT(6),
+};
+
+struct mlx5_ifc_mfrl_reg_bits {
+       u8         reserved_at_0[0x20];
+
+       u8         reserved_at_20[0x2];
+       u8         pci_sync_for_fw_update_start[0x1];
+       u8         pci_sync_for_fw_update_resp[0x2];
+       u8         rst_type_sel[0x3];
+       u8         reserved_at_28[0x8];
+       u8         reset_type[0x8];
+       u8         reset_level[0x8];
+};
+
 struct mlx5_ifc_mirc_reg_bits {
        u8         reserved_at_0[0x18];
        u8         status_code[0x8];
@@ -9743,6 +9794,7 @@ union mlx5_ifc_ports_control_registers_document_bits {
        struct mlx5_ifc_mcc_reg_bits mcc_reg;
        struct mlx5_ifc_mcda_reg_bits mcda_reg;
        struct mlx5_ifc_mirc_reg_bits mirc_reg;
+       struct mlx5_ifc_mfrl_reg_bits mfrl_reg;
        u8         reserved_at_0[0x60e0];
 };
 
@@ -10465,10 +10517,62 @@ struct mlx5_ifc_affiliated_event_header_bits {
 
 enum {
        MLX5_HCA_CAP_GENERAL_OBJECT_TYPES_ENCRYPTION_KEY = BIT(0xc),
+       MLX5_HCA_CAP_GENERAL_OBJECT_TYPES_IPSEC = BIT(0x13),
 };
 
 enum {
        MLX5_GENERAL_OBJECT_TYPES_ENCRYPTION_KEY = 0xc,
+       MLX5_GENERAL_OBJECT_TYPES_IPSEC = 0x13,
+};
+
+enum {
+       MLX5_IPSEC_OBJECT_ICV_LEN_16B,
+       MLX5_IPSEC_OBJECT_ICV_LEN_12B,
+       MLX5_IPSEC_OBJECT_ICV_LEN_8B,
+};
+
+struct mlx5_ifc_ipsec_obj_bits {
+       u8         modify_field_select[0x40];
+       u8         full_offload[0x1];
+       u8         reserved_at_41[0x1];
+       u8         esn_en[0x1];
+       u8         esn_overlap[0x1];
+       u8         reserved_at_44[0x2];
+       u8         icv_length[0x2];
+       u8         reserved_at_48[0x4];
+       u8         aso_return_reg[0x4];
+       u8         reserved_at_50[0x10];
+
+       u8         esn_msb[0x20];
+
+       u8         reserved_at_80[0x8];
+       u8         dekn[0x18];
+
+       u8         salt[0x20];
+
+       u8         implicit_iv[0x40];
+
+       u8         reserved_at_100[0x700];
+};
+
+struct mlx5_ifc_create_ipsec_obj_in_bits {
+       struct mlx5_ifc_general_obj_in_cmd_hdr_bits general_obj_in_cmd_hdr;
+       struct mlx5_ifc_ipsec_obj_bits ipsec_object;
+};
+
+enum {
+       MLX5_MODIFY_IPSEC_BITMASK_ESN_OVERLAP = BIT(0),
+       MLX5_MODIFY_IPSEC_BITMASK_ESN_MSB = BIT(1),
+};
+
+struct mlx5_ifc_query_ipsec_obj_out_bits {
+       struct mlx5_ifc_general_obj_out_cmd_hdr_bits general_obj_out_cmd_hdr;
+       struct mlx5_ifc_ipsec_obj_bits ipsec_object;
+};
+
+struct mlx5_ifc_modify_ipsec_obj_in_bits {
+       struct mlx5_ifc_general_obj_in_cmd_hdr_bits general_obj_in_cmd_hdr;
+       struct mlx5_ifc_ipsec_obj_bits ipsec_object;
 };
 
 struct mlx5_ifc_encryption_key_obj_bits {
index ae63b1a..f23eb18 100644 (file)
@@ -229,6 +229,11 @@ enum {
 
 enum {
        MLX5_ETH_WQE_SVLAN              = 1 << 0,
+       MLX5_ETH_WQE_TRAILER_HDR_OUTER_IP_ASSOC = 1 << 26,
+       MLX5_ETH_WQE_TRAILER_HDR_OUTER_L4_ASSOC = 1 << 27,
+       MLX5_ETH_WQE_TRAILER_HDR_INNER_IP_ASSOC = 3 << 26,
+       MLX5_ETH_WQE_TRAILER_HDR_INNER_L4_ASSOC = 1 << 28,
+       MLX5_ETH_WQE_INSERT_TRAILER     = 1 << 30,
        MLX5_ETH_WQE_INSERT_VLAN        = 1 << 15,
 };
 
@@ -257,6 +262,7 @@ struct mlx5_wqe_eth_seg {
                        __be16 type;
                        __be16 vlan_tci;
                } insert;
+               __be32 trailer;
        };
 };
 
@@ -553,57 +559,8 @@ struct mlx5_qp_context {
        u8                      rsvd1[24];
 };
 
-static inline struct mlx5_core_qp *__mlx5_qp_lookup(struct mlx5_core_dev *dev, u32 qpn)
-{
-       return radix_tree_lookup(&dev->priv.qp_table.tree, qpn);
-}
-
-int mlx5_core_create_dct(struct mlx5_core_dev *dev,
-                        struct mlx5_core_dct *qp,
-                        u32 *in, int inlen,
-                        u32 *out, int outlen);
-int mlx5_core_create_qp(struct mlx5_core_dev *dev,
-                       struct mlx5_core_qp *qp,
-                       u32 *in,
-                       int inlen);
-int mlx5_core_qp_modify(struct mlx5_core_dev *dev, u16 opcode,
-                       u32 opt_param_mask, void *qpc,
-                       struct mlx5_core_qp *qp);
-int mlx5_core_destroy_qp(struct mlx5_core_dev *dev,
-                        struct mlx5_core_qp *qp);
-int mlx5_core_destroy_dct(struct mlx5_core_dev *dev,
-                         struct mlx5_core_dct *dct);
-int mlx5_core_qp_query(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp,
-                      u32 *out, int outlen);
-int mlx5_core_dct_query(struct mlx5_core_dev *dev, struct mlx5_core_dct *dct,
-                       u32 *out, int outlen);
-
-int mlx5_core_set_delay_drop(struct mlx5_core_dev *dev,
-                            u32 timeout_usec);
-
-int mlx5_core_xrcd_alloc(struct mlx5_core_dev *dev, u32 *xrcdn);
-int mlx5_core_xrcd_dealloc(struct mlx5_core_dev *dev, u32 xrcdn);
-void mlx5_init_qp_table(struct mlx5_core_dev *dev);
-void mlx5_cleanup_qp_table(struct mlx5_core_dev *dev);
 int mlx5_debug_qp_add(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp);
 void mlx5_debug_qp_remove(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp);
-int mlx5_core_create_rq_tracked(struct mlx5_core_dev *dev, u32 *in, int inlen,
-                               struct mlx5_core_qp *rq);
-void mlx5_core_destroy_rq_tracked(struct mlx5_core_dev *dev,
-                                 struct mlx5_core_qp *rq);
-int mlx5_core_create_sq_tracked(struct mlx5_core_dev *dev, u32 *in, int inlen,
-                               struct mlx5_core_qp *sq);
-void mlx5_core_destroy_sq_tracked(struct mlx5_core_dev *dev,
-                                 struct mlx5_core_qp *sq);
-int mlx5_core_alloc_q_counter(struct mlx5_core_dev *dev, u16 *counter_id);
-int mlx5_core_dealloc_q_counter(struct mlx5_core_dev *dev, u16 counter_id);
-int mlx5_core_query_q_counter(struct mlx5_core_dev *dev, u16 counter_id,
-                             int reset, void *out, int out_size);
-
-struct mlx5_core_rsc_common *mlx5_core_res_hold(struct mlx5_core_dev *dev,
-                                               int res_num,
-                                               enum mlx5_res_type res_type);
-void mlx5_core_res_put(struct mlx5_core_rsc_common *res);
 
 static inline const char *mlx5_qp_type_str(int type)
 {
index dc6b1e7..028f442 100644 (file)
@@ -39,27 +39,20 @@ int mlx5_core_alloc_transport_domain(struct mlx5_core_dev *dev, u32 *tdn);
 void mlx5_core_dealloc_transport_domain(struct mlx5_core_dev *dev, u32 tdn);
 int mlx5_core_create_rq(struct mlx5_core_dev *dev, u32 *in, int inlen,
                        u32 *rqn);
-int mlx5_core_modify_rq(struct mlx5_core_dev *dev, u32 rqn, u32 *in, int inlen);
+int mlx5_core_modify_rq(struct mlx5_core_dev *dev, u32 rqn, u32 *in);
 void mlx5_core_destroy_rq(struct mlx5_core_dev *dev, u32 rqn);
 int mlx5_core_query_rq(struct mlx5_core_dev *dev, u32 rqn, u32 *out);
 int mlx5_core_create_sq(struct mlx5_core_dev *dev, u32 *in, int inlen,
                        u32 *sqn);
-int mlx5_core_modify_sq(struct mlx5_core_dev *dev, u32 sqn, u32 *in, int inlen);
+int mlx5_core_modify_sq(struct mlx5_core_dev *dev, u32 sqn, u32 *in);
 void mlx5_core_destroy_sq(struct mlx5_core_dev *dev, u32 sqn);
 int mlx5_core_query_sq(struct mlx5_core_dev *dev, u32 sqn, u32 *out);
 int mlx5_core_query_sq_state(struct mlx5_core_dev *dev, u32 sqn, u8 *state);
-int mlx5_core_create_tir(struct mlx5_core_dev *dev, u32 *in, int inlen,
-                        u32 *tirn);
-int mlx5_core_create_tir_out(struct mlx5_core_dev *dev,
-                            u32 *in, int inlen,
-                            u32 *out, int outlen);
-int mlx5_core_modify_tir(struct mlx5_core_dev *dev, u32 tirn, u32 *in,
-                        int inlen);
+int mlx5_core_create_tir(struct mlx5_core_dev *dev, u32 *in, u32 *tirn);
+int mlx5_core_modify_tir(struct mlx5_core_dev *dev, u32 tirn, u32 *in);
 void mlx5_core_destroy_tir(struct mlx5_core_dev *dev, u32 tirn);
-int mlx5_core_create_tis(struct mlx5_core_dev *dev, u32 *in, int inlen,
-                        u32 *tisn);
-int mlx5_core_modify_tis(struct mlx5_core_dev *dev, u32 tisn, u32 *in,
-                        int inlen);
+int mlx5_core_create_tis(struct mlx5_core_dev *dev, u32 *in, u32 *tisn);
+int mlx5_core_modify_tis(struct mlx5_core_dev *dev, u32 tisn, u32 *in);
 void mlx5_core_destroy_tis(struct mlx5_core_dev *dev, u32 tisn);
 int mlx5_core_create_rqt(struct mlx5_core_dev *dev, u32 *in, int inlen,
                         u32 *rqtn);
index 16060fb..8170da1 100644 (file)
@@ -127,8 +127,7 @@ int mlx5_query_vport_down_stats(struct mlx5_core_dev *mdev, u16 vport,
                                u8 other_vport, u64 *rx_discard_vport_down,
                                u64 *tx_discard_vport_down);
 int mlx5_core_query_vport_counter(struct mlx5_core_dev *dev, u8 other_vport,
-                                 int vf, u8 port_num, void *out,
-                                 size_t out_sz);
+                                 int vf, u8 port_num, void *out);
 int mlx5_core_modify_hca_vport_context(struct mlx5_core_dev *dev,
                                       u8 other_vport, u8 port_num,
                                       int vf,
index 5a32342..a7b1ef8 100644 (file)
@@ -201,10 +201,10 @@ extern int sysctl_overcommit_memory;
 extern int sysctl_overcommit_ratio;
 extern unsigned long sysctl_overcommit_kbytes;
 
-extern int overcommit_ratio_handler(struct ctl_table *, int, void __user *,
-                                   size_t *, loff_t *);
-extern int overcommit_kbytes_handler(struct ctl_table *, int, void __user *,
-                                   size_t *, loff_t *);
+int overcommit_ratio_handler(struct ctl_table *, int, void *, size_t *,
+               loff_t *);
+int overcommit_kbytes_handler(struct ctl_table *, int, void *, size_t *,
+               loff_t *);
 
 #define nth_page(page,n) pfn_to_page(page_to_pfn((page)) + (n))
 
@@ -2957,8 +2957,8 @@ extern bool process_shares_mm(struct task_struct *p, struct mm_struct *mm);
 
 #ifdef CONFIG_SYSCTL
 extern int sysctl_drop_caches;
-int drop_caches_sysctl_handler(struct ctl_table *, int,
-                                       void __user *, size_t *, loff_t *);
+int drop_caches_sysctl_handler(struct ctl_table *, int, void *, size_t *,
+               loff_t *);
 #endif
 
 void drop_slab(void);
@@ -3140,5 +3140,7 @@ unsigned long wp_shared_mapping_range(struct address_space *mapping,
                                      pgoff_t first_index, pgoff_t nr);
 #endif
 
+extern int sysctl_nr_trim_pages;
+
 #endif /* __KERNEL__ */
 #endif /* _LINUX_MM_H */
index 1b9de7d..93cf20f 100644 (file)
@@ -909,24 +909,23 @@ static inline int is_highmem(struct zone *zone)
 
 /* These two functions are used to setup the per zone pages min values */
 struct ctl_table;
-int min_free_kbytes_sysctl_handler(struct ctl_table *, int,
-                                       void __user *, size_t *, loff_t *);
-int watermark_boost_factor_sysctl_handler(struct ctl_table *, int,
-                                       void __user *, size_t *, loff_t *);
-int watermark_scale_factor_sysctl_handler(struct ctl_table *, int,
-                                       void __user *, size_t *, loff_t *);
+
+int min_free_kbytes_sysctl_handler(struct ctl_table *, int, void *, size_t *,
+               loff_t *);
+int watermark_scale_factor_sysctl_handler(struct ctl_table *, int, void *,
+               size_t *, loff_t *);
 extern int sysctl_lowmem_reserve_ratio[MAX_NR_ZONES];
-int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *, int,
-                                       void __user *, size_t *, loff_t *);
+int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *, int, void *,
+               size_t *, loff_t *);
 int percpu_pagelist_fraction_sysctl_handler(struct ctl_table *, int,
-                                       void __user *, size_t *, loff_t *);
+               void *, size_t *, loff_t *);
 int sysctl_min_unmapped_ratio_sysctl_handler(struct ctl_table *, int,
-                       void __user *, size_t *, loff_t *);
+               void *, size_t *, loff_t *);
 int sysctl_min_slab_ratio_sysctl_handler(struct ctl_table *, int,
-                       void __user *, size_t *, loff_t *);
-
-extern int numa_zonelist_order_handler(struct ctl_table *, int,
-                       void __user *, size_t *, loff_t *);
+               void *, size_t *, loff_t *);
+int numa_zonelist_order_handler(struct ctl_table *, int,
+               void *, size_t *, loff_t *);
+extern int percpu_pagelist_fraction;
 extern char numa_zonelist_order[];
 #define NUMA_ZONELIST_ORDER_LEN        16
 
index 9d53c5a..2cc3cf8 100644 (file)
@@ -89,7 +89,7 @@ enum {
         * Add your fresh new feature above and remember to update
         * netdev_features_strings[] in net/core/ethtool.c and maybe
         * some feature mask #defines below. Please also describe it
-        * in Documentation/networking/netdev-features.txt.
+        * in Documentation/networking/netdev-features.rst.
         */
 
        /**/NETDEV_FEATURE_COUNT
index 130a668..7725efd 100644 (file)
@@ -288,6 +288,7 @@ enum netdev_state_t {
        __LINK_STATE_NOCARRIER,
        __LINK_STATE_LINKWATCH_PENDING,
        __LINK_STATE_DORMANT,
+       __LINK_STATE_TESTING,
 };
 
 
@@ -328,6 +329,7 @@ struct napi_struct {
 
        unsigned long           state;
        int                     weight;
+       int                     defer_hard_irqs_count;
        unsigned long           gro_bitmask;
        int                     (*poll)(struct napi_struct *, int);
 #ifdef CONFIG_NETPOLL
@@ -1803,13 +1805,11 @@ enum netdev_priv_flags {
  *     @phydev:        Physical device may attach itself
  *                     for hardware timestamping
  *     @sfp_bus:       attached &struct sfp_bus structure.
- *     @qdisc_tx_busylock_key: lockdep class annotating Qdisc->busylock
- *                             spinlock
- *     @qdisc_running_key:     lockdep class annotating Qdisc->running seqcount
- *     @qdisc_xmit_lock_key:   lockdep class annotating
- *                             netdev_queue->_xmit_lock spinlock
+ *
  *     @addr_list_lock_key:    lockdep class annotating
  *                             net_device->addr_list_lock spinlock
+ *     @qdisc_tx_busylock: lockdep class annotating Qdisc->busylock spinlock
+ *     @qdisc_running_key: lockdep class annotating Qdisc->running seqcount
  *
  *     @proto_down:    protocol port state information can be sent to the
  *                     switch driver and used to set the phys state of the
@@ -1994,6 +1994,7 @@ struct net_device {
 
        struct bpf_prog __rcu   *xdp_prog;
        unsigned long           gro_flush_timeout;
+       int                     napi_defer_hard_irqs;
        rx_handler_func_t __rcu *rx_handler;
        void __rcu              *rx_handler_data;
 
@@ -2109,10 +2110,9 @@ struct net_device {
 #endif
        struct phy_device       *phydev;
        struct sfp_bus          *sfp_bus;
-       struct lock_class_key   qdisc_tx_busylock_key;
-       struct lock_class_key   qdisc_running_key;
-       struct lock_class_key   qdisc_xmit_lock_key;
        struct lock_class_key   addr_list_lock_key;
+       struct lock_class_key   *qdisc_tx_busylock;
+       struct lock_class_key   *qdisc_running_key;
        bool                    proto_down;
        unsigned                wol_enabled:1;
 
@@ -2197,6 +2197,20 @@ static inline void netdev_for_each_tx_queue(struct net_device *dev,
                f(dev, &dev->_tx[i], arg);
 }
 
+#define netdev_lockdep_set_classes(dev)                                \
+{                                                              \
+       static struct lock_class_key qdisc_tx_busylock_key;     \
+       static struct lock_class_key qdisc_running_key;         \
+       static struct lock_class_key qdisc_xmit_lock_key;       \
+       unsigned int i;                                         \
+                                                               \
+       (dev)->qdisc_tx_busylock = &qdisc_tx_busylock_key;      \
+       (dev)->qdisc_running_key = &qdisc_running_key;          \
+       for (i = 0; i < (dev)->num_tx_queues; i++)              \
+               lockdep_set_class(&(dev)->_tx[i]._xmit_lock,    \
+                                 &qdisc_xmit_lock_key);        \
+}
+
 u16 netdev_pick_tx(struct net_device *dev, struct sk_buff *skb,
                     struct net_device *sb_dev);
 struct netdev_queue *netdev_core_pick_tx(struct net_device *dev,
@@ -3907,6 +3921,46 @@ static inline bool netif_dormant(const struct net_device *dev)
 }
 
 
+/**
+ *     netif_testing_on - mark device as under test.
+ *     @dev: network device
+ *
+ * Mark device as under test (as per RFC2863).
+ *
+ * The testing state indicates that some test(s) must be performed on
+ * the interface. After completion, of the test, the interface state
+ * will change to up, dormant, or down, as appropriate.
+ */
+static inline void netif_testing_on(struct net_device *dev)
+{
+       if (!test_and_set_bit(__LINK_STATE_TESTING, &dev->state))
+               linkwatch_fire_event(dev);
+}
+
+/**
+ *     netif_testing_off - set device as not under test.
+ *     @dev: network device
+ *
+ * Device is not in testing state.
+ */
+static inline void netif_testing_off(struct net_device *dev)
+{
+       if (test_and_clear_bit(__LINK_STATE_TESTING, &dev->state))
+               linkwatch_fire_event(dev);
+}
+
+/**
+ *     netif_testing - test if device is under test
+ *     @dev: network device
+ *
+ * Check if device is under test
+ */
+static inline bool netif_testing(const struct net_device *dev)
+{
+       return test_bit(__LINK_STATE_TESTING, &dev->state);
+}
+
+
 /**
  *     netif_oper_up - test if device is operational
  *     @dev: network device
index 4402304..e5f3e7d 100644 (file)
@@ -1317,11 +1317,13 @@ struct nfs41_impl_id {
        struct nfstime4                 date;
 };
 
+#define MAX_BIND_CONN_TO_SESSION_RETRIES 3
 struct nfs41_bind_conn_to_session_args {
        struct nfs_client               *client;
        struct nfs4_sessionid           sessionid;
        u32                             dir;
        bool                            use_conn_in_rdma_mode;
+       int                             retries;
 };
 
 struct nfs41_bind_conn_to_session_res {
index 9003e29..750c7f3 100644 (file)
@@ -202,16 +202,11 @@ static inline void watchdog_update_hrtimer_threshold(u64 period) { }
 #endif
 
 struct ctl_table;
-extern int proc_watchdog(struct ctl_table *, int ,
-                        void __user *, size_t *, loff_t *);
-extern int proc_nmi_watchdog(struct ctl_table *, int ,
-                            void __user *, size_t *, loff_t *);
-extern int proc_soft_watchdog(struct ctl_table *, int ,
-                             void __user *, size_t *, loff_t *);
-extern int proc_watchdog_thresh(struct ctl_table *, int ,
-                               void __user *, size_t *, loff_t *);
-extern int proc_watchdog_cpumask(struct ctl_table *, int,
-                                void __user *, size_t *, loff_t *);
+int proc_watchdog(struct ctl_table *, int, void *, size_t *, loff_t *);
+int proc_nmi_watchdog(struct ctl_table *, int , void *, size_t *, loff_t *);
+int proc_soft_watchdog(struct ctl_table *, int , void *, size_t *, loff_t *);
+int proc_watchdog_thresh(struct ctl_table *, int , void *, size_t *, loff_t *);
+int proc_watchdog_cpumask(struct ctl_table *, int, void *, size_t *, loff_t *);
 
 #ifdef CONFIG_HAVE_ACPI_APEI_NMI
 #include <asm/nmi.h>
index 491a2b7..0f61a4a 100644 (file)
@@ -30,7 +30,9 @@ extern struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np);
 extern int of_phy_register_fixed_link(struct device_node *np);
 extern void of_phy_deregister_fixed_link(struct device_node *np);
 extern bool of_phy_is_fixed_link(struct device_node *np);
-
+extern int of_mdiobus_phy_device_register(struct mii_bus *mdio,
+                                    struct phy_device *phy,
+                                    struct device_node *child, u32 addr);
 
 static inline int of_mdio_parse_addr(struct device *dev,
                                     const struct device_node *np)
@@ -118,6 +120,13 @@ static inline bool of_phy_is_fixed_link(struct device_node *np)
 {
        return false;
 }
+
+static inline int of_mdiobus_phy_device_register(struct mii_bus *mdio,
+                                           struct phy_device *phy,
+                                           struct device_node *child, u32 addr)
+{
+       return -ENOSYS;
+}
 #endif
 
 
index 9c3e761..347ea37 100644 (file)
@@ -1280,15 +1280,12 @@ extern int sysctl_perf_cpu_time_max_percent;
 
 extern void perf_sample_event_took(u64 sample_len_ns);
 
-extern int perf_proc_update_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos);
-extern int perf_cpu_time_max_percent_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos);
-
+int perf_proc_update_handler(struct ctl_table *table, int write,
+               void *buffer, size_t *lenp, loff_t *ppos);
+int perf_cpu_time_max_percent_handler(struct ctl_table *table, int write,
+               void *buffer, size_t *lenp, loff_t *ppos);
 int perf_event_max_stack_handler(struct ctl_table *table, int write,
-                                void __user *buffer, size_t *lenp, loff_t *ppos);
+               void *buffer, size_t *lenp, loff_t *ppos);
 
 /* Access to perf_event_open(2) syscall. */
 #define PERF_SECURITY_OPEN             0
index 2432ca4..a2b91b5 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/u64_stats_sync.h>
 #include <linux/irqreturn.h>
 #include <linux/iopoll.h>
+#include <linux/refcount.h>
 
 #include <linux/atomic.h>
 
@@ -227,6 +228,28 @@ struct mdio_bus_stats {
        struct u64_stats_sync syncp;
 };
 
+/* Represents a shared structure between different phydev's in the same
+ * package, for example a quad PHY. See phy_package_join() and
+ * phy_package_leave().
+ */
+struct phy_package_shared {
+       int addr;
+       refcount_t refcnt;
+       unsigned long flags;
+       size_t priv_size;
+
+       /* private data pointer */
+       /* note that this pointer is shared between different phydevs and
+        * the user has to take care of appropriate locking. It is allocated
+        * and freed automatically by phy_package_join() and
+        * phy_package_leave().
+        */
+       void *priv;
+};
+
+/* used as bit number in atomic bitops */
+#define PHY_SHARED_F_INIT_DONE 0
+
 /*
  * The Bus class for PHYs.  Devices which provide access to
  * PHYs should register using this structure
@@ -241,6 +264,9 @@ struct mii_bus {
        int (*reset)(struct mii_bus *bus);
        struct mdio_bus_stats stats[PHY_MAX_ADDR];
 
+       unsigned int is_managed:1;      /* is device-managed */
+       unsigned int is_managed_registered:1;
+
        /*
         * A lock to ensure that only one thing can read/write
         * the MDIO bus at a time
@@ -275,6 +301,12 @@ struct mii_bus {
        int reset_delay_us;
        /* RESET GPIO descriptor pointer */
        struct gpio_desc *reset_gpiod;
+
+       /* protect access to the shared element */
+       struct mutex shared_lock;
+
+       /* shared state across different PHYs */
+       struct phy_package_shared *shared[PHY_MAX_ADDR];
 };
 #define to_mii_bus(d) container_of(d, struct mii_bus, dev)
 
@@ -286,6 +318,20 @@ static inline struct mii_bus *mdiobus_alloc(void)
 
 int __mdiobus_register(struct mii_bus *bus, struct module *owner);
 #define mdiobus_register(bus) __mdiobus_register(bus, THIS_MODULE)
+static inline int devm_mdiobus_register(struct mii_bus *bus)
+{
+       int ret;
+
+       if (!bus->is_managed)
+               return -EPERM;
+
+       ret = mdiobus_register(bus);
+       if (!ret)
+               bus->is_managed_registered = 1;
+
+       return ret;
+}
+
 void mdiobus_unregister(struct mii_bus *bus);
 void mdiobus_free(struct mii_bus *bus);
 struct mii_bus *devm_mdiobus_alloc_size(struct device *dev, int sizeof_priv);
@@ -431,6 +477,9 @@ struct phy_device {
        int duplex;
        int pause;
        int asym_pause;
+       u8 master_slave_get;
+       u8 master_slave_set;
+       u8 master_slave_state;
 
        /* Union of PHY and Attached devices' supported link modes */
        /* See ethtool.h for more info */
@@ -461,6 +510,10 @@ struct phy_device {
        /* For use by PHYs to maintain extra state */
        void *priv;
 
+       /* shared data pointer */
+       /* For use by PHYs inside the same package that need a shared state. */
+       struct phy_package_shared *shared;
+
        /* Interrupt and Polling infrastructure */
        struct delayed_work state_queue;
 
@@ -1234,10 +1287,6 @@ static inline int genphy_config_aneg(struct phy_device *phydev)
        return __genphy_config_aneg(phydev, false);
 }
 
-static inline int genphy_no_soft_reset(struct phy_device *phydev)
-{
-       return 0;
-}
 static inline int genphy_no_ack_interrupt(struct phy_device *phydev)
 {
        return 0;
@@ -1341,6 +1390,10 @@ int phy_ethtool_get_link_ksettings(struct net_device *ndev,
 int phy_ethtool_set_link_ksettings(struct net_device *ndev,
                                   const struct ethtool_link_ksettings *cmd);
 int phy_ethtool_nway_reset(struct net_device *ndev);
+int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size);
+void phy_package_leave(struct phy_device *phydev);
+int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
+                         int addr, size_t priv_size);
 
 #if IS_ENABLED(CONFIG_PHYLIB)
 int __init mdio_bus_init(void);
@@ -1393,6 +1446,58 @@ static inline int phy_ethtool_get_stats(struct phy_device *phydev,
        return 0;
 }
 
+static inline int phy_package_read(struct phy_device *phydev, u32 regnum)
+{
+       struct phy_package_shared *shared = phydev->shared;
+
+       if (!shared)
+               return -EIO;
+
+       return mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
+}
+
+static inline int __phy_package_read(struct phy_device *phydev, u32 regnum)
+{
+       struct phy_package_shared *shared = phydev->shared;
+
+       if (!shared)
+               return -EIO;
+
+       return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
+}
+
+static inline int phy_package_write(struct phy_device *phydev,
+                                   u32 regnum, u16 val)
+{
+       struct phy_package_shared *shared = phydev->shared;
+
+       if (!shared)
+               return -EIO;
+
+       return mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
+}
+
+static inline int __phy_package_write(struct phy_device *phydev,
+                                     u32 regnum, u16 val)
+{
+       struct phy_package_shared *shared = phydev->shared;
+
+       if (!shared)
+               return -EIO;
+
+       return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
+}
+
+static inline bool phy_package_init_once(struct phy_device *phydev)
+{
+       struct phy_package_shared *shared = phydev->shared;
+
+       if (!shared)
+               return false;
+
+       return !test_and_set_bit(PHY_SHARED_F_INIT_DONE, &shared->flags);
+}
+
 extern struct bus_type mdio_bus_type;
 
 struct mdio_board_info {
index 3f8d37e..cc5b452 100644 (file)
@@ -67,6 +67,9 @@ struct phylink_config {
        struct device *dev;
        enum phylink_op_type type;
        bool pcs_poll;
+       bool poll_fixed_state;
+       void (*get_fixed_state)(struct phylink_config *config,
+                               struct phylink_link_state *state);
 };
 
 /**
@@ -366,9 +369,6 @@ void phylink_destroy(struct phylink *);
 int phylink_connect_phy(struct phylink *, struct phy_device *);
 int phylink_of_phy_connect(struct phylink *, struct device_node *, u32 flags);
 void phylink_disconnect_phy(struct phylink *);
-int phylink_fixed_state_cb(struct phylink *,
-                          void (*cb)(struct net_device *dev,
-                                     struct phylink_link_state *));
 
 void phylink_mac_change(struct phylink *, bool up);
 
index cc896f0..93543cb 100644 (file)
@@ -108,6 +108,9 @@ extern void transfer_pid(struct task_struct *old, struct task_struct *new,
 struct pid_namespace;
 extern struct pid_namespace init_pid_ns;
 
+extern int pid_max;
+extern int pid_max_min, pid_max_max;
+
 /*
  * look up a PID in the hash table. Must be called with the tasklist_lock
  * or rcu_read_lock() held.
index c588be8..0ecce6a 100644 (file)
@@ -185,6 +185,7 @@ int cros_ec_sensorhub_register_push_data(struct cros_ec_sensorhub *sensorhub,
 void cros_ec_sensorhub_unregister_push_data(struct cros_ec_sensorhub *sensorhub,
                                            u8 sensor_num);
 
+int cros_ec_sensorhub_ring_allocate(struct cros_ec_sensorhub *sensorhub);
 int cros_ec_sensorhub_ring_add(struct cros_ec_sensorhub *sensorhub);
 void cros_ec_sensorhub_ring_remove(void *arg);
 int cros_ec_sensorhub_ring_fifo_enable(struct cros_ec_sensorhub *sensorhub,
index 25f46a9..3e268e6 100644 (file)
@@ -83,7 +83,7 @@ struct wilco_ec_response {
        u16 result;
        u16 data_size;
        u8 reserved[2];
-       u8 data[0];
+       u8 data[];
 } __packed;
 
 /**
index b18dca6..c2a7cfb 100644 (file)
@@ -220,10 +220,8 @@ struct pnp_card {
 #define global_to_pnp_card(n) list_entry(n, struct pnp_card, global_list)
 #define protocol_to_pnp_card(n) list_entry(n, struct pnp_card, protocol_list)
 #define to_pnp_card(n) container_of(n, struct pnp_card, dev)
-#define pnp_for_each_card(card) \
-       for((card) = global_to_pnp_card(pnp_cards.next); \
-       (card) != global_to_pnp_card(&pnp_cards); \
-       (card) = global_to_pnp_card((card)->global_list.next))
+#define pnp_for_each_card(card)        \
+       list_for_each_entry(card, &pnp_cards, global_list)
 
 struct pnp_card_link {
        struct pnp_card *card;
@@ -276,14 +274,9 @@ struct pnp_dev {
 #define card_to_pnp_dev(n) list_entry(n, struct pnp_dev, card_list)
 #define protocol_to_pnp_dev(n) list_entry(n, struct pnp_dev, protocol_list)
 #define        to_pnp_dev(n) container_of(n, struct pnp_dev, dev)
-#define pnp_for_each_dev(dev) \
-       for((dev) = global_to_pnp_dev(pnp_global.next); \
-       (dev) != global_to_pnp_dev(&pnp_global); \
-       (dev) = global_to_pnp_dev((dev)->global_list.next))
-#define card_for_each_dev(card,dev) \
-       for((dev) = card_to_pnp_dev((card)->devices.next); \
-       (dev) != card_to_pnp_dev(&(card)->devices); \
-       (dev) = card_to_pnp_dev((dev)->card_list.next))
+#define pnp_for_each_dev(dev) list_for_each_entry(dev, &pnp_global, global_list)
+#define card_for_each_dev(card, dev)   \
+       list_for_each_entry(dev, &(card)->devices, card_list)
 #define pnp_dev_name(dev) (dev)->name
 
 static inline void *pnp_get_drvdata(struct pnp_dev *pdev)
@@ -437,14 +430,10 @@ struct pnp_protocol {
 };
 
 #define to_pnp_protocol(n) list_entry(n, struct pnp_protocol, protocol_list)
-#define protocol_for_each_card(protocol,card) \
-       for((card) = protocol_to_pnp_card((protocol)->cards.next); \
-       (card) != protocol_to_pnp_card(&(protocol)->cards); \
-       (card) = protocol_to_pnp_card((card)->protocol_list.next))
-#define protocol_for_each_dev(protocol,dev) \
-       for((dev) = protocol_to_pnp_dev((protocol)->devices.next); \
-       (dev) != protocol_to_pnp_dev(&(protocol)->devices); \
-       (dev) = protocol_to_pnp_dev((dev)->protocol_list.next))
+#define protocol_for_each_card(protocol, card) \
+       list_for_each_entry(card, &(protocol)->cards, protocol_list)
+#define protocol_for_each_dev(protocol, dev)   \
+       list_for_each_entry(dev, &(protocol)->devices, protocol_list)
 
 extern struct bus_type pnp_bus_type;
 
index 540595a..90797f1 100644 (file)
@@ -28,7 +28,7 @@ struct posix_acl {
        refcount_t              a_refcount;
        struct rcu_head         a_rcu;
        unsigned int            a_count;
-       struct posix_acl_entry  a_entries[0];
+       struct posix_acl_entry  a_entries[];
 };
 
 #define FOREACH_ACL_ENTRY(pa, acl, pe) \
index e061635..fcde077 100644 (file)
@@ -189,7 +189,7 @@ extern int printk_delay_msec;
 extern int dmesg_restrict;
 
 extern int
-devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, void __user *buf,
+devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, void *buf,
                          size_t *lenp, loff_t *ppos);
 
 extern void wake_up_klogd(void);
index 121a7ed..31144d9 100644 (file)
@@ -36,7 +36,7 @@ struct ptp_system_timestamp {
 };
 
 /**
- * struct ptp_clock_info - decribes a PTP hardware clock
+ * struct ptp_clock_info - describes a PTP hardware clock
  *
  * @owner:     The clock driver should set to THIS_MODULE.
  * @name:      A short "friendly name" to identify the clock and to
@@ -65,6 +65,9 @@ struct ptp_system_timestamp {
  *            parameter delta: Desired frequency offset from nominal frequency
  *            in parts per billion
  *
+ * @adjphase:  Adjusts the phase offset of the hardware clock.
+ *             parameter delta: Desired change in nanoseconds.
+ *
  * @adjtime:  Shifts the time of the hardware clock.
  *            parameter delta: Desired change in nanoseconds.
  *
@@ -128,6 +131,7 @@ struct ptp_clock_info {
        struct ptp_pin_desc *pin_config;
        int (*adjfine)(struct ptp_clock_info *ptp, long scaled_ppm);
        int (*adjfreq)(struct ptp_clock_info *ptp, s32 delta);
+       int (*adjphase)(struct ptp_clock_info *ptp, s32 phase);
        int (*adjtime)(struct ptp_clock_info *ptp, s64 delta);
        int (*gettime64)(struct ptp_clock_info *ptp, struct timespec64 *ts);
        int (*gettimex64)(struct ptp_clock_info *ptp, struct timespec64 *ts,
index 317bace..2cd6372 100644 (file)
@@ -100,7 +100,7 @@ struct rio_switch {
        u32 port_ok;
        struct rio_switch_ops *ops;
        spinlock_t lock;
-       struct rio_dev *nextdev[0];
+       struct rio_dev *nextdev[];
 };
 
 /**
@@ -201,7 +201,7 @@ struct rio_dev {
        u8 hopcount;
        struct rio_dev *prev;
        atomic_t state;
-       struct rio_switch rswitch[0];   /* RIO switch info */
+       struct rio_switch rswitch[];    /* RIO switch info */
 };
 
 #define rio_dev_g(n) list_entry(n, struct rio_dev, global_list)
index 5974ced..238bb85 100644 (file)
@@ -54,7 +54,7 @@ struct rs_codec {
  */
 struct rs_control {
        struct rs_codec *codec;
-       uint16_t        buffers[0];
+       uint16_t        buffers[];
 };
 
 /* General purpose RS codec, 8-bit data width, symbol width 1-15 bit  */
index d4f6215..7b4d3a4 100644 (file)
@@ -12,9 +12,8 @@ extern unsigned int  sysctl_hung_task_panic;
 extern unsigned long sysctl_hung_task_timeout_secs;
 extern unsigned long sysctl_hung_task_check_interval_secs;
 extern int sysctl_hung_task_warnings;
-extern int proc_dohung_task_timeout_secs(struct ctl_table *table, int write,
-                                        void __user *buffer,
-                                        size_t *lenp, loff_t *ppos);
+int proc_dohung_task_timeout_secs(struct ctl_table *table, int write,
+               void *buffer, size_t *lenp, loff_t *ppos);
 #else
 /* Avoid need for ifdefs elsewhere in the code */
 enum { sysctl_hung_task_timeout_secs = 0 };
@@ -43,8 +42,7 @@ extern __read_mostly unsigned int sysctl_sched_migration_cost;
 extern __read_mostly unsigned int sysctl_sched_nr_migrate;
 
 int sched_proc_update_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *length,
-               loff_t *ppos);
+               void *buffer, size_t *length, loff_t *ppos);
 #endif
 
 /*
@@ -72,33 +70,21 @@ extern unsigned int sysctl_sched_autogroup_enabled;
 extern int sysctl_sched_rr_timeslice;
 extern int sched_rr_timeslice;
 
-extern int sched_rr_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos);
-
-extern int sched_rt_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos);
-
-#ifdef CONFIG_UCLAMP_TASK
-extern int sysctl_sched_uclamp_handler(struct ctl_table *table, int write,
-                                      void __user *buffer, size_t *lenp,
-                                      loff_t *ppos);
-#endif
-
-extern int sysctl_numa_balancing(struct ctl_table *table, int write,
-                                void __user *buffer, size_t *lenp,
-                                loff_t *ppos);
-
-extern int sysctl_schedstats(struct ctl_table *table, int write,
-                                void __user *buffer, size_t *lenp,
-                                loff_t *ppos);
+int sched_rr_handler(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos);
+int sched_rt_handler(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos);
+int sysctl_sched_uclamp_handler(struct ctl_table *table, int write,
+               void *buffer, size_t *lenp, loff_t *ppos);
+int sysctl_numa_balancing(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos);
+int sysctl_schedstats(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos);
 
 #if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL)
 extern unsigned int sysctl_sched_energy_aware;
-extern int sched_energy_aware_handler(struct ctl_table *table, int write,
-                                void __user *buffer, size_t *lenp,
-                                loff_t *ppos);
+int sched_energy_aware_handler(struct ctl_table *table, int write,
+               void *buffer, size_t *lenp, loff_t *ppos);
 #endif
 
 #endif /* _LINUX_SCHED_SYSCTL_H */
index af9319e..95253ad 100644 (file)
@@ -142,7 +142,7 @@ struct sched_domain {
         * by attaching extra space to the end of the structure,
         * depending on how many CPUs the kernel has booted up with)
         */
-       unsigned long span[0];
+       unsigned long span[];
 };
 
 static inline struct cpumask *sched_domain_span(struct sched_domain *sd)
index a8d9310..6aa229b 100644 (file)
@@ -211,7 +211,7 @@ struct request_sock;
 
 #ifdef CONFIG_MMU
 extern int mmap_min_addr_handler(struct ctl_table *table, int write,
-                                void __user *buffer, size_t *lenp, loff_t *ppos);
+                                void *buffer, size_t *lenp, loff_t *ppos);
 #endif
 
 /* security_inode_init_security callback function to write xattrs */
index 3a2ac70..3000c52 100644 (file)
@@ -4162,7 +4162,7 @@ struct skb_ext {
        refcount_t refcnt;
        u8 offset[SKB_EXT_NUM]; /* in chunks of 8 bytes */
        u8 chunks;              /* same */
-       char data[0] __aligned(8);
+       char data[] __aligned(8);
 };
 
 struct skb_ext *__skb_ext_alloc(void);
index fbafb35..bd964c3 100644 (file)
@@ -177,6 +177,8 @@ struct plat_stmmacenet_data {
        struct stmmac_rxq_cfg rx_queues_cfg[MTL_MAX_RX_QUEUES];
        struct stmmac_txq_cfg tx_queues_cfg[MTL_MAX_TX_QUEUES];
        void (*fix_mac_speed)(void *priv, unsigned int speed);
+       int (*serdes_powerup)(struct net_device *ndev, void *priv);
+       void (*serdes_powerdown)(struct net_device *ndev, void *priv);
        int (*init)(struct platform_device *pdev, void *priv);
        void (*exit)(struct platform_device *pdev, void *priv);
        struct mac_device_info *(*setup)(void *priv);
index ca7e108..02e7a58 100644 (file)
@@ -71,7 +71,13 @@ struct rpc_clnt {
 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
        struct dentry           *cl_debugfs;    /* debugfs directory */
 #endif
-       struct rpc_xprt_iter    cl_xpi;
+       /* cl_work is only needed after cl_xpi is no longer used,
+        * and that are of similar size
+        */
+       union {
+               struct rpc_xprt_iter    cl_xpi;
+               struct work_struct      cl_work;
+       };
        const struct cred       *cl_cred;
 };
 
@@ -236,4 +242,9 @@ static inline int rpc_reply_expected(struct rpc_task *task)
                (task->tk_msg.rpc_proc->p_decode != NULL);
 }
 
+static inline void rpc_task_close_connection(struct rpc_task *task)
+{
+       if (task->tk_xprt)
+               xprt_force_disconnect(task->tk_xprt);
+}
 #endif /* _LINUX_SUNRPC_CLNT_H */
index 78fe2ac..cbcfbd0 100644 (file)
@@ -170,6 +170,7 @@ extern bool svc_rdma_post_recvs(struct svcxprt_rdma *rdma);
 extern void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma,
                                   struct svc_rdma_recv_ctxt *ctxt);
 extern void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma);
+extern void svc_rdma_release_rqst(struct svc_rqst *rqstp);
 extern int svc_rdma_recvfrom(struct svc_rqst *);
 
 /* svc_rdma_rw.c */
index b835d8d..e1bbf7a 100644 (file)
@@ -275,7 +275,7 @@ struct swap_info_struct {
                                         */
        struct work_struct discard_work; /* discard worker */
        struct swap_cluster_list discard_clusters; /* discard clusters list */
-       struct plist_node avail_lists[0]; /*
+       struct plist_node avail_lists[]; /*
                                           * entries in swap_avail_heads, one
                                           * entry per node.
                                           * Must be last as the number of the
index 02fa844..f2401e4 100644 (file)
@@ -44,35 +44,26 @@ struct ctl_dir;
 
 extern const int sysctl_vals[];
 
-typedef int proc_handler (struct ctl_table *ctl, int write,
-                         void __user *buffer, size_t *lenp, loff_t *ppos);
-
-extern int proc_dostring(struct ctl_table *, int,
-                        void __user *, size_t *, loff_t *);
-extern int proc_dointvec(struct ctl_table *, int,
-                        void __user *, size_t *, loff_t *);
-extern int proc_douintvec(struct ctl_table *, int,
-                        void __user *, size_t *, loff_t *);
-extern int proc_dointvec_minmax(struct ctl_table *, int,
-                               void __user *, size_t *, loff_t *);
-extern int proc_douintvec_minmax(struct ctl_table *table, int write,
-                                void __user *buffer, size_t *lenp,
-                                loff_t *ppos);
-extern int proc_dointvec_jiffies(struct ctl_table *, int,
-                                void __user *, size_t *, loff_t *);
-extern int proc_dointvec_userhz_jiffies(struct ctl_table *, int,
-                                       void __user *, size_t *, loff_t *);
-extern int proc_dointvec_ms_jiffies(struct ctl_table *, int,
-                                   void __user *, size_t *, loff_t *);
-extern int proc_doulongvec_minmax(struct ctl_table *, int,
-                                 void __user *, size_t *, loff_t *);
-extern int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int,
-                                     void __user *, size_t *, loff_t *);
-extern int proc_do_large_bitmap(struct ctl_table *, int,
-                               void __user *, size_t *, loff_t *);
-extern int proc_do_static_key(struct ctl_table *table, int write,
-                             void __user *buffer, size_t *lenp,
-                             loff_t *ppos);
+typedef int proc_handler(struct ctl_table *ctl, int write, void *buffer,
+               size_t *lenp, loff_t *ppos);
+
+int proc_dostring(struct ctl_table *, int, void *, size_t *, loff_t *);
+int proc_dointvec(struct ctl_table *, int, void *, size_t *, loff_t *);
+int proc_douintvec(struct ctl_table *, int, void *, size_t *, loff_t *);
+int proc_dointvec_minmax(struct ctl_table *, int, void *, size_t *, loff_t *);
+int proc_douintvec_minmax(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos);
+int proc_dointvec_jiffies(struct ctl_table *, int, void *, size_t *, loff_t *);
+int proc_dointvec_userhz_jiffies(struct ctl_table *, int, void *, size_t *,
+               loff_t *);
+int proc_dointvec_ms_jiffies(struct ctl_table *, int, void *, size_t *,
+               loff_t *);
+int proc_doulongvec_minmax(struct ctl_table *, int, void *, size_t *, loff_t *);
+int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int, void *,
+               size_t *, loff_t *);
+int proc_do_large_bitmap(struct ctl_table *, int, void *, size_t *, loff_t *);
+int proc_do_static_key(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos);
 
 /*
  * Register a set of sysctl names by calling register_sysctl_table
@@ -207,7 +198,15 @@ void unregister_sysctl_table(struct ctl_table_header * table);
 
 extern int sysctl_init(void);
 
+extern int pwrsw_enabled;
+extern int unaligned_enabled;
+extern int unaligned_dump_stack;
+extern int no_unaligned_warning;
+
 extern struct ctl_table sysctl_mount_point[];
+extern struct ctl_table random_table[];
+extern struct ctl_table firmware_config_table[];
+extern struct ctl_table epoll_table[];
 
 #else /* CONFIG_SYSCTL */
 static inline struct ctl_table_header *register_sysctl_table(struct ctl_table * table)
@@ -238,7 +237,7 @@ static inline void setup_sysctl_set(struct ctl_table_set *p,
 
 #endif /* CONFIG_SYSCTL */
 
-int sysctl_max_threads(struct ctl_table *table, int write,
-                      void __user *buffer, size_t *lenp, loff_t *ppos);
+int sysctl_max_threads(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos);
 
 #endif /* _LINUX_SYSCTL_H */
index 421c99c..e60db06 100644 (file)
@@ -78,47 +78,6 @@ struct tcp_sack_block {
 #define TCP_SACK_SEEN     (1 << 0)   /*1 = peer is SACK capable, */
 #define TCP_DSACK_SEEN    (1 << 2)   /*1 = DSACK was received from peer*/
 
-#if IS_ENABLED(CONFIG_MPTCP)
-struct mptcp_options_received {
-       u64     sndr_key;
-       u64     rcvr_key;
-       u64     data_ack;
-       u64     data_seq;
-       u32     subflow_seq;
-       u16     data_len;
-       u16     mp_capable : 1,
-               mp_join : 1,
-               dss : 1,
-               add_addr : 1,
-               rm_addr : 1,
-               family : 4,
-               echo : 1,
-               backup : 1;
-       u32     token;
-       u32     nonce;
-       u64     thmac;
-       u8      hmac[20];
-       u8      join_id;
-       u8      use_map:1,
-               dsn64:1,
-               data_fin:1,
-               use_ack:1,
-               ack64:1,
-               mpc_map:1,
-               __unused:2;
-       u8      addr_id;
-       u8      rm_id;
-       union {
-               struct in_addr  addr;
-#if IS_ENABLED(CONFIG_MPTCP_IPV6)
-               struct in6_addr addr6;
-#endif
-       };
-       u64     ahmac;
-       u16     port;
-};
-#endif
-
 struct tcp_options_received {
 /*     PAWS/RTTM data  */
        int     ts_recent_stamp;/* Time we stored ts_recent (for aging) */
@@ -136,9 +95,6 @@ struct tcp_options_received {
        u8      num_sacks;      /* Number of SACK blocks                */
        u16     user_mss;       /* mss requested by user in ioctl       */
        u16     mss_clamp;      /* Maximal mss, negotiated at connection setup */
-#if IS_ENABLED(CONFIG_MPTCP)
-       struct mptcp_options_received   mptcp;
-#endif
 };
 
 static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
@@ -148,13 +104,6 @@ static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
 #if IS_ENABLED(CONFIG_SMC)
        rx_opt->smc_ok = 0;
 #endif
-#if IS_ENABLED(CONFIG_MPTCP)
-       rx_opt->mptcp.mp_capable = 0;
-       rx_opt->mptcp.mp_join = 0;
-       rx_opt->mptcp.add_addr = 0;
-       rx_opt->mptcp.rm_addr = 0;
-       rx_opt->mptcp.dss = 0;
-#endif
 }
 
 /* This is the max number of SACKS that we'll generate and process. It's safe
@@ -268,6 +217,7 @@ struct tcp_sock {
        } rack;
        u16     advmss;         /* Advertised MSS                       */
        u8      compressed_ack;
+       u8      dup_ack_counter;
        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 */
index eb6cbdf..44a7f91 100644 (file)
@@ -295,7 +295,7 @@ struct bts_header {
        u32 magic;
        u32 version;
        u8 future[24];
-       u8 actions[0];
+       u8 actions[];
 } __attribute__ ((packed));
 
 /**
@@ -305,7 +305,7 @@ struct bts_header {
 struct bts_action {
        u16 type;
        u16 size;
-       u8 data[0];
+       u8 data[];
 } __attribute__ ((packed));
 
 struct bts_action_send {
@@ -315,7 +315,7 @@ struct bts_action_send {
 struct bts_action_wait {
        u32 msec;
        u32 size;
-       u8 data[0];
+       u8 data[];
 } __attribute__ ((packed));
 
 struct bts_action_delay {
index 0dc19a8..07910ae 100644 (file)
@@ -201,8 +201,7 @@ struct ctl_table;
 
 extern unsigned int sysctl_timer_migration;
 int timer_migration_handler(struct ctl_table *table, int write,
-                           void __user *buffer, size_t *lenp,
-                           loff_t *ppos);
+                           void *buffer, size_t *lenp, loff_t *ppos);
 #endif
 
 unsigned long __round_jiffies(unsigned long j, int cpu);
index 131ea1b..c253461 100644 (file)
@@ -28,7 +28,7 @@ struct tcpa_event {
        u32 event_type;
        u8 pcr_value[20];       /* SHA1 */
        u32 event_size;
-       u8 event_data[0];
+       u8 event_data[];
 };
 
 enum tcpa_event_types {
@@ -55,7 +55,7 @@ enum tcpa_event_types {
 struct tcpa_pc_event {
        u32 event_id;
        u32 event_size;
-       u8 event_data[0];
+       u8 event_data[];
 };
 
 enum tcpa_pc_event_ids {
@@ -102,7 +102,7 @@ struct tcg_pcr_event {
 
 struct tcg_event_field {
        u32 event_size;
-       u8 event[0];
+       u8 event[];
 } __packed;
 
 struct tcg_pcr_event2_head {
index 1fb11da..a1fecf3 100644 (file)
@@ -156,8 +156,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
  * Note, the proto and args passed in includes "__data" as the first parameter.
  * The reason for this is to handle the "void" prototype. If a tracepoint
  * has a "void" prototype, then it is invalid to declare a function
- * as "(void *, void)". The DECLARE_TRACE_NOARGS() will pass in just
- * "void *data", where as the DECLARE_TRACE() will pass in "void *data, proto".
+ * as "(void *, void)".
  */
 #define __DO_TRACE(tp, proto, args, cond, rcuidle)                     \
        do {                                                            \
@@ -373,25 +372,6 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
 # define __tracepoint_string
 #endif
 
-/*
- * The need for the DECLARE_TRACE_NOARGS() is to handle the prototype
- * (void). "void" is a special value in a function prototype and can
- * not be combined with other arguments. Since the DECLARE_TRACE()
- * macro adds a data element at the beginning of the prototype,
- * we need a way to differentiate "(void *data, proto)" from
- * "(void *data, void)". The second prototype is invalid.
- *
- * DECLARE_TRACE_NOARGS() passes "void" as the tracepoint prototype
- * and "void *__data" as the callback prototype.
- *
- * DECLARE_TRACE() passes "proto" as the tracepoint protoype and
- * "void *__data, proto" as the callback prototype.
- */
-#define DECLARE_TRACE_NOARGS(name)                                     \
-       __DECLARE_TRACE(name, void, ,                                   \
-                       cpu_online(raw_smp_processor_id()),             \
-                       void *__data, __data)
-
 #define DECLARE_TRACE(name, proto, args)                               \
        __DECLARE_TRACE(name, PARAMS(proto), PARAMS(args),              \
                        cpu_online(raw_smp_processor_id()),             \
index bd5fe0e..a99e9b8 100644 (file)
@@ -66,7 +66,7 @@ struct tty_buffer {
        int read;
        int flags;
        /* Data points here */
-       unsigned long data[0];
+       unsigned long data[];
 };
 
 /* Values for .flags field of tty_buffer */
index 733acfb..5453af8 100644 (file)
@@ -164,7 +164,7 @@ struct vdpa_config_ops {
        u64 (*get_vq_state)(struct vdpa_device *vdev, u16 idx);
 
        /* Device ops */
-       u16 (*get_vq_align)(struct vdpa_device *vdev);
+       u32 (*get_vq_align)(struct vdpa_device *vdev);
        u64 (*get_features)(struct vdpa_device *vdev);
        int (*set_features)(struct vdpa_device *vdev, u64 features);
        void (*set_config_cb)(struct vdpa_device *vdev,
index 9aced11..1eaaa93 100644 (file)
@@ -1,5 +1,13 @@
 /* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_VERMAGIC_H
+#define _LINUX_VERMAGIC_H
+
+#ifndef INCLUDE_VERMAGIC
+#error "This header can be included from kernel/module.c or *.mod.c only"
+#endif
+
 #include <generated/utsrelease.h>
+#include <asm/vermagic.h>
 
 /* Simply sanity version stamp for modules. */
 #ifdef CONFIG_SMP
@@ -24,9 +32,6 @@
 #else
 #define MODULE_VERMAGIC_MODVERSIONS ""
 #endif
-#ifndef MODULE_ARCH_VERMAGIC
-#define MODULE_ARCH_VERMAGIC ""
-#endif
 #ifdef RANDSTRUCT_PLUGIN
 #include <generated/randomize_layout_hash.h>
 #define MODULE_RANDSTRUCT_PLUGIN "RANDSTRUCT_PLUGIN_" RANDSTRUCT_HASHED_SEED
@@ -41,3 +46,4 @@
        MODULE_ARCH_VERMAGIC                                            \
        MODULE_RANDSTRUCT_PLUGIN
 
+#endif /* _LINUX_VERMAGIC_H */
index 15f906e..a493eac 100644 (file)
@@ -9,7 +9,6 @@
 #include <linux/device.h>
 #include <linux/mod_devicetable.h>
 #include <linux/gfp.h>
-#include <linux/vringh.h>
 
 /**
  * virtqueue - a queue to register buffers for sending or receiving.
index 0d1fe92..6f6ade6 100644 (file)
@@ -3,6 +3,8 @@
 #define _LINUX_VIRTIO_NET_H
 
 #include <linux/if_vlan.h>
+#include <uapi/linux/tcp.h>
+#include <uapi/linux/udp.h>
 #include <uapi/linux/virtio_net.h>
 
 static inline int virtio_net_hdr_set_proto(struct sk_buff *skb,
@@ -28,17 +30,25 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
                                        bool little_endian)
 {
        unsigned int gso_type = 0;
+       unsigned int thlen = 0;
+       unsigned int ip_proto;
 
        if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
                switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
                case VIRTIO_NET_HDR_GSO_TCPV4:
                        gso_type = SKB_GSO_TCPV4;
+                       ip_proto = IPPROTO_TCP;
+                       thlen = sizeof(struct tcphdr);
                        break;
                case VIRTIO_NET_HDR_GSO_TCPV6:
                        gso_type = SKB_GSO_TCPV6;
+                       ip_proto = IPPROTO_TCP;
+                       thlen = sizeof(struct tcphdr);
                        break;
                case VIRTIO_NET_HDR_GSO_UDP:
                        gso_type = SKB_GSO_UDP;
+                       ip_proto = IPPROTO_UDP;
+                       thlen = sizeof(struct udphdr);
                        break;
                default:
                        return -EINVAL;
@@ -57,16 +67,22 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
 
                if (!skb_partial_csum_set(skb, start, off))
                        return -EINVAL;
+
+               if (skb_transport_offset(skb) + thlen > skb_headlen(skb))
+                       return -EINVAL;
        } else {
                /* gso packets without NEEDS_CSUM do not set transport_offset.
                 * probe and drop if does not match one of the above types.
                 */
                if (gso_type && skb->network_header) {
+                       struct flow_keys_basic keys;
+
                        if (!skb->protocol)
                                virtio_net_hdr_set_proto(skb, hdr);
 retry:
-                       skb_probe_transport_header(skb);
-                       if (!skb_transport_header_was_set(skb)) {
+                       if (!skb_flow_dissect_flow_keys_basic(NULL, skb, &keys,
+                                                             NULL, 0, 0, 0,
+                                                             0)) {
                                /* UFO does not specify ipv4 or 6: try both */
                                if (gso_type & SKB_GSO_UDP &&
                                    skb->protocol == htons(ETH_P_IP)) {
@@ -75,6 +91,12 @@ retry:
                                }
                                return -EINVAL;
                        }
+
+                       if (keys.control.thoff + thlen > skb_headlen(skb) ||
+                           keys.basic.ip_proto != ip_proto)
+                               return -EINVAL;
+
+                       skb_set_transport_header(skb, keys.control.thoff);
                }
        }
 
index 71c81e0..dc636b7 100644 (file)
@@ -48,6 +48,7 @@ struct virtio_vsock_pkt {
        u32 len;
        u32 off;
        bool reply;
+       bool tap_delivered;
 };
 
 struct virtio_vsock_pkt_info {
index 0507a16..a95d3cc 100644 (file)
@@ -137,7 +137,7 @@ extern void vunmap(const void *addr);
 
 extern int remap_vmalloc_range_partial(struct vm_area_struct *vma,
                                       unsigned long uaddr, void *kaddr,
-                                      unsigned long size);
+                                      unsigned long pgoff, unsigned long size);
 
 extern int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,
                                                        unsigned long pgoff);
index 292485f..cb50715 100644 (file)
@@ -16,8 +16,8 @@ extern int sysctl_stat_interval;
 #define DISABLE_NUMA_STAT   0
 extern int sysctl_vm_numa_stat;
 DECLARE_STATIC_KEY_TRUE(vm_numa_stat_key);
-extern int sysctl_vm_numa_stat_handler(struct ctl_table *table,
-               int write, void __user *buffer, size_t *length, loff_t *ppos);
+int sysctl_vm_numa_stat_handler(struct ctl_table *table, int write,
+               void *buffer, size_t *length, loff_t *ppos);
 #endif
 
 struct reclaim_stat {
@@ -274,8 +274,8 @@ void cpu_vm_stats_fold(int cpu);
 void refresh_zone_stat_thresholds(void);
 
 struct ctl_table;
-int vmstat_refresh(struct ctl_table *, int write,
-                  void __user *buffer, size_t *lenp, loff_t *ppos);
+int vmstat_refresh(struct ctl_table *, int write, void *buffer, size_t *lenp,
+               loff_t *ppos);
 
 void drain_zonestat(struct zone *zone, struct per_cpu_pageset *);
 
index bd0503c..9e2763d 100644 (file)
 #include <linux/virtio_byteorder.h>
 #include <linux/uio.h>
 #include <linux/slab.h>
+#if IS_REACHABLE(CONFIG_VHOST_IOTLB)
 #include <linux/dma-direction.h>
 #include <linux/vhost_iotlb.h>
+#endif
 #include <asm/barrier.h>
 
 /* virtio_ring with information needed for host access. */
@@ -254,6 +256,8 @@ static inline __virtio64 cpu_to_vringh64(const struct vringh *vrh, u64 val)
        return __cpu_to_virtio64(vringh_is_little_endian(vrh), val);
 }
 
+#if IS_REACHABLE(CONFIG_VHOST_IOTLB)
+
 void vringh_set_iotlb(struct vringh *vrh, struct vhost_iotlb *iotlb);
 
 int vringh_init_iotlb(struct vringh *vrh, u64 features,
@@ -284,4 +288,6 @@ void vringh_notify_disable_iotlb(struct vringh *vrh);
 
 int vringh_need_notify_iotlb(struct vringh *vrh);
 
+#endif /* CONFIG_VHOST_IOTLB */
+
 #endif /* _LINUX_VRINGH_H */
index a19d845..f8a7e1a 100644 (file)
@@ -362,24 +362,18 @@ extern int vm_highmem_is_dirtyable;
 extern int block_dump;
 extern int laptop_mode;
 
-extern int dirty_background_ratio_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos);
-extern int dirty_background_bytes_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos);
-extern int dirty_ratio_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos);
-extern int dirty_bytes_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos);
+int dirty_background_ratio_handler(struct ctl_table *table, int write,
+               void *buffer, size_t *lenp, loff_t *ppos);
+int dirty_background_bytes_handler(struct ctl_table *table, int write,
+               void *buffer, size_t *lenp, loff_t *ppos);
+int dirty_ratio_handler(struct ctl_table *table, int write,
+               void *buffer, size_t *lenp, loff_t *ppos);
+int dirty_bytes_handler(struct ctl_table *table, int write,
+               void *buffer, size_t *lenp, loff_t *ppos);
 int dirtytime_interval_handler(struct ctl_table *table, int write,
-                              void __user *buffer, size_t *lenp, loff_t *ppos);
-
-struct ctl_table;
-int dirty_writeback_centisecs_handler(struct ctl_table *, int,
-                                     void __user *, size_t *, loff_t *);
+               void *buffer, size_t *lenp, loff_t *ppos);
+int dirty_writeback_centisecs_handler(struct ctl_table *table, int write,
+               void *buffer, size_t *lenp, loff_t *ppos);
 
 void global_dirty_limits(unsigned long *pbackground, unsigned long *pdirty);
 unsigned long wb_calc_thresh(struct bdi_writeback *wb, unsigned long thresh);
index 4cf6e11..47eaa34 100644 (file)
@@ -73,7 +73,7 @@ struct simple_xattr {
        struct list_head list;
        char *name;
        size_t size;
-       char value[0];
+       char value[];
 };
 
 /*
index e0eabe5..fdb0710 100644 (file)
@@ -6,8 +6,6 @@
 #define RTR_SOLICITATION_INTERVAL      (4*HZ)
 #define RTR_SOLICITATION_MAX_INTERVAL  (3600*HZ)       /* 1 hour */
 
-#define MIN_VALID_LIFETIME             (2*3600)        /* 2 hours */
-
 #define TEMP_VALID_LIFETIME            (7*86400)
 #define TEMP_PREFERRED_LIFETIME                (86400)
 #define REGEN_MAX_RETRY                        (3)
index 1576353..3fa7b1e 100644 (file)
@@ -139,6 +139,14 @@ struct bt_voice {
 #define BT_PHY_LE_CODED_TX     0x00002000
 #define BT_PHY_LE_CODED_RX     0x00004000
 
+#define BT_MODE                        15
+
+#define BT_MODE_BASIC          0x00
+#define BT_MODE_ERTM           0x01
+#define BT_MODE_STREAMING      0x02
+#define BT_MODE_LE_FLOWCTL     0x03
+#define BT_MODE_EXT_FLOWCTL    0x04
+
 __printf(1, 2)
 void bt_info(const char *fmt, ...);
 __printf(1, 2)
index 5f60e13..1da8cec 100644 (file)
@@ -53,6 +53,9 @@
 #define HCI_NOTIFY_CONN_ADD            1
 #define HCI_NOTIFY_CONN_DEL            2
 #define HCI_NOTIFY_VOICE_SETTING       3
+#define HCI_NOTIFY_ENABLE_SCO_CVSD     4
+#define HCI_NOTIFY_ENABLE_SCO_TRANSP   5
+#define HCI_NOTIFY_DISABLE_SCO         6
 
 /* HCI bus types */
 #define HCI_VIRTUAL    0
@@ -65,6 +68,7 @@
 #define HCI_SPI                7
 #define HCI_I2C                8
 #define HCI_SMD                9
+#define HCI_VIRTIO     10
 
 /* HCI controller types */
 #define HCI_PRIMARY    0x00
@@ -294,6 +298,7 @@ enum {
        HCI_FORCE_STATIC_ADDR,
        HCI_LL_RPA_RESOLUTION,
        HCI_CMD_PENDING,
+       HCI_FORCE_NO_MITM,
 
        __HCI_NUM_FLAGS,
 };
@@ -455,12 +460,11 @@ enum {
 #define HCI_LE_SLAVE_FEATURES          0x08
 #define HCI_LE_PING                    0x10
 #define HCI_LE_DATA_LEN_EXT            0x20
-#define HCI_LE_PHY_2M                  0x01
-#define HCI_LE_PHY_CODED               0x08
-#define HCI_LE_EXT_ADV                 0x10
+#define HCI_LE_LL_PRIVACY              0x40
 #define HCI_LE_EXT_SCAN_POLICY         0x80
 #define HCI_LE_PHY_2M                  0x01
 #define HCI_LE_PHY_CODED               0x08
+#define HCI_LE_EXT_ADV                 0x10
 #define HCI_LE_CHAN_SEL_ALG2           0x40
 #define HCI_LE_CIS_MASTER              0x10
 #define HCI_LE_CIS_SLAVE               0x20
@@ -1272,6 +1276,13 @@ struct hci_rp_read_data_block_size {
 
 #define HCI_OP_READ_LOCAL_CODECS       0x100b
 
+#define HCI_OP_READ_LOCAL_PAIRING_OPTS 0x100c
+struct hci_rp_read_local_pairing_opts {
+       __u8     status;
+       __u8     pairing_opts;
+       __u8     max_key_size;
+} __packed;
+
 #define HCI_OP_READ_PAGE_SCAN_ACTIVITY 0x0c1b
 struct hci_rp_read_page_scan_activity {
        __u8     status;
index d4e2877..239ab72 100644 (file)
@@ -312,6 +312,8 @@ struct hci_dev {
        __u16           conn_info_max_age;
        __u16           auth_payload_timeout;
        __u8            min_enc_key_size;
+       __u8            max_enc_key_size;
+       __u8            pairing_opts;
        __u8            ssp_debug_mode;
        __u8            hw_error_code;
        __u32           clock;
@@ -484,6 +486,11 @@ struct hci_dev {
        struct led_trigger      *power_led;
 #endif
 
+#if IS_ENABLED(CONFIG_BT_MSFTEXT)
+       __u16                   msft_opcode;
+       void                    *msft_data;
+#endif
+
        int (*open)(struct hci_dev *hdev);
        int (*close)(struct hci_dev *hdev);
        int (*flush)(struct hci_dev *hdev);
@@ -638,6 +645,7 @@ extern struct mutex hci_cb_list_lock;
        do {                                                    \
                hci_dev_clear_flag(hdev, HCI_LE_SCAN);          \
                hci_dev_clear_flag(hdev, HCI_LE_ADV);           \
+               hci_dev_clear_flag(hdev, HCI_LL_RPA_RESOLUTION);\
                hci_dev_clear_flag(hdev, HCI_PERIODIC_INQ);     \
        } while (0)
 
@@ -1116,6 +1124,14 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
 int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb);
 __printf(2, 3) void hci_set_hw_info(struct hci_dev *hdev, const char *fmt, ...);
 __printf(2, 3) void hci_set_fw_info(struct hci_dev *hdev, const char *fmt, ...);
+
+static inline void hci_set_msft_opcode(struct hci_dev *hdev, __u16 opcode)
+{
+#if IS_ENABLED(CONFIG_BT_MSFTEXT)
+       hdev->msft_opcode = opcode;
+#endif
+}
+
 int hci_dev_open(__u16 dev);
 int hci_dev_close(__u16 dev);
 int hci_dev_do_close(struct hci_dev *hdev);
index f41cd87..65dd6fd 100644 (file)
@@ -674,6 +674,13 @@ struct mgmt_cp_set_blocked_keys {
 
 #define MGMT_OP_SET_WIDEBAND_SPEECH    0x0047
 
+#define MGMT_OP_READ_SECURITY_INFO     0x0048
+#define MGMT_READ_SECURITY_INFO_SIZE   0
+struct mgmt_rp_read_security_info {
+       __le16   sec_len;
+       __u8     sec[0];
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16  opcode;
index dc2ce31..0b696da 100644 (file)
@@ -237,7 +237,6 @@ struct bonding {
        struct   dentry *debug_dir;
 #endif /* CONFIG_DEBUG_FS */
        struct rtnl_link_stats64 bond_stats;
-       struct lock_class_key stats_lock_key;
 };
 
 #define bond_slave_get_rcu(dev) \
index c78bd4f..46ac804 100644 (file)
@@ -905,6 +905,8 @@ struct survey_info {
  *     protocol frames.
  * @control_port_over_nl80211: TRUE if userspace expects to exchange control
  *     port frames over NL80211 instead of the network interface.
+ * @control_port_no_preauth: disables pre-auth rx over the nl80211 control
+ *     port for mac80211
  * @wep_keys: static WEP keys, if not NULL points to an array of
  *     CFG80211_MAX_WEP_KEYS WEP keys
  * @wep_tx_key: key index (0..3) of the default TX static WEP key
@@ -1222,6 +1224,7 @@ struct sta_txpwr {
  * @he_capa: HE capabilities of station
  * @he_capa_len: the length of the HE capabilities
  * @airtime_weight: airtime scheduler weight for this station
+ * @txpwr: transmit power for an associated station
  */
 struct station_parameters {
        const u8 *supported_rates;
@@ -4666,6 +4669,9 @@ struct wiphy_iftype_akm_suites {
  * @txq_memory_limit: configuration internal TX queue memory limit
  * @txq_quantum: configuration of internal TX queue scheduler quantum
  *
+ * @tx_queue_len: allow setting transmit queue len for drivers not using
+ *     wake_tx_queue
+ *
  * @support_mbssid: can HW support association with nontransmitted AP
  * @support_only_he_mbssid: don't parse MBSSID elements if it is not
  *     HE AP, in order to avoid compatibility issues.
@@ -4681,6 +4687,10 @@ struct wiphy_iftype_akm_suites {
  *     supported by the driver for each peer
  * @tid_config_support.max_retry: maximum supported retry count for
  *     long/short retry configuration
+ *
+ * @max_data_retry_count: maximum supported per TID retry count for
+ *     configuration through the %NL80211_TID_CONFIG_ATTR_RETRY_SHORT and
+ *     %NL80211_TID_CONFIG_ATTR_RETRY_LONG attributes
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -5201,7 +5211,7 @@ u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
  * Radiotap parsing functions -- for controlled injection support
  *
  * Implemented in net/wireless/radiotap.c
- * Documentation in Documentation/networking/radiotap-headers.txt
+ * Documentation in Documentation/networking/radiotap-headers.rst
  */
 
 struct radiotap_align_size {
index b39643e..0d9e86b 100644 (file)
@@ -2,7 +2,19 @@
 #define __LINUX_ERSPAN_H
 
 /*
- * GRE header for ERSPAN encapsulation (8 octets [34:41]) -- 8 bytes
+ * GRE header for ERSPAN type I encapsulation (4 octets [34:37])
+ *      0                   1                   2                   3
+ *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *     |0|0|0|0|0|00000|000000000|00000|    Protocol Type for ERSPAN   |
+ *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *  The Type I ERSPAN frame format is based on the barebones IP + GRE
+ *  encapsulation (as described above) on top of the raw mirrored frame.
+ *  There is no extra ERSPAN header.
+ *
+ *
+ * GRE header for ERSPAN type II and II encapsulation (8 octets [34:41])
  *       0                   1                   2                   3
  *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -43,7 +55,7 @@
  * |                  Platform Specific Info                       |
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *
- * GRE proto ERSPAN type II = 0x88BE, type III = 0x22EB
+ * GRE proto ERSPAN type I/II = 0x88BE, type III = 0x22EB
  */
 
 #include <uapi/linux/erspan.h>
@@ -139,6 +151,9 @@ static inline u8 get_hwid(const struct erspan_md2 *md2)
 
 static inline int erspan_hdr_len(int version)
 {
+       if (version == 0)
+               return 0;
+
        return sizeof(struct erspan_base_hdr) +
               (version == 1 ? ERSPAN_V1_MDSIZE : ERSPAN_V2_MDSIZE);
 }
index 3619c6a..4001ffb 100644 (file)
@@ -147,6 +147,7 @@ enum flow_action_id {
        FLOW_ACTION_MPLS_PUSH,
        FLOW_ACTION_MPLS_POP,
        FLOW_ACTION_MPLS_MANGLE,
+       FLOW_ACTION_GATE,
        NUM_FLOW_ACTIONS,
 };
 
@@ -166,15 +167,18 @@ enum flow_action_mangle_base {
 enum flow_action_hw_stats_bit {
        FLOW_ACTION_HW_STATS_IMMEDIATE_BIT,
        FLOW_ACTION_HW_STATS_DELAYED_BIT,
+       FLOW_ACTION_HW_STATS_DISABLED_BIT,
 };
 
 enum flow_action_hw_stats {
-       FLOW_ACTION_HW_STATS_DISABLED = 0,
+       FLOW_ACTION_HW_STATS_DONT_CARE = 0,
        FLOW_ACTION_HW_STATS_IMMEDIATE =
                BIT(FLOW_ACTION_HW_STATS_IMMEDIATE_BIT),
        FLOW_ACTION_HW_STATS_DELAYED = BIT(FLOW_ACTION_HW_STATS_DELAYED_BIT),
        FLOW_ACTION_HW_STATS_ANY = FLOW_ACTION_HW_STATS_IMMEDIATE |
                                   FLOW_ACTION_HW_STATS_DELAYED,
+       FLOW_ACTION_HW_STATS_DISABLED =
+               BIT(FLOW_ACTION_HW_STATS_DISABLED_BIT),
 };
 
 typedef void (*action_destr)(void *priv);
@@ -255,6 +259,15 @@ struct flow_action_entry {
                        u8              bos;
                        u8              ttl;
                } mpls_mangle;
+               struct {
+                       u32             index;
+                       s32             prio;
+                       u64             basetime;
+                       u64             cycletime;
+                       u64             cycletimeext;
+                       u32             num_entries;
+                       struct action_gate_entry *entries;
+               } gate;
        };
        struct flow_action_cookie *cookie; /* user defined action cookie */
 };
@@ -325,7 +338,11 @@ __flow_action_hw_stats_check(const struct flow_action *action,
                return true;
        if (!flow_action_mixed_hw_stats_check(action, extack))
                return false;
+
        action_entry = flow_action_first_entry_get(action);
+       if (action_entry->hw_stats == FLOW_ACTION_HW_STATS_DONT_CARE)
+               return true;
+
        if (!check_allow_bit &&
            action_entry->hw_stats != FLOW_ACTION_HW_STATS_ANY) {
                NL_SET_ERR_MSG_MOD(extack, "Driver supports only default HW stats type \"any\"");
index a01981d..212eb27 100644 (file)
@@ -190,7 +190,6 @@ struct inet6_dev {
        int                     dead;
 
        u32                     desync_factor;
-       u8                      rndid[8];
        struct list_head        tempaddr_list;
 
        struct in6_addr         token;
index c8e2beb..0f0d1ef 100644 (file)
@@ -99,6 +99,20 @@ static inline int IP_ECN_set_ce(struct iphdr *iph)
        return 1;
 }
 
+static inline int IP_ECN_set_ect1(struct iphdr *iph)
+{
+       u32 check = (__force u32)iph->check;
+
+       if ((iph->tos & INET_ECN_MASK) != INET_ECN_ECT_0)
+               return 0;
+
+       check += (__force u16)htons(0x100);
+
+       iph->check = (__force __sum16)(check + (check>=0xFFFF));
+       iph->tos ^= INET_ECN_MASK;
+       return 1;
+}
+
 static inline void IP_ECN_clear(struct iphdr *iph)
 {
        iph->tos &= ~INET_ECN_MASK;
@@ -134,6 +148,22 @@ static inline int IP6_ECN_set_ce(struct sk_buff *skb, struct ipv6hdr *iph)
        return 1;
 }
 
+static inline int IP6_ECN_set_ect1(struct sk_buff *skb, struct ipv6hdr *iph)
+{
+       __be32 from, to;
+
+       if ((ipv6_get_dsfield(iph) & INET_ECN_MASK) != INET_ECN_ECT_0)
+               return 0;
+
+       from = *(__be32 *)iph;
+       to = from ^ htonl(INET_ECN_MASK << 20);
+       *(__be32 *)iph = to;
+       if (skb->ip_summed == CHECKSUM_COMPLETE)
+               skb->csum = csum_add(csum_sub(skb->csum, (__force __wsum)from),
+                                    (__force __wsum)to);
+       return 1;
+}
+
 static inline void ipv6_copy_dscp(unsigned int dscp, struct ipv6hdr *inner)
 {
        dscp &= ~INET_ECN_MASK;
@@ -159,6 +189,25 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb)
        return 0;
 }
 
+static inline int INET_ECN_set_ect1(struct sk_buff *skb)
+{
+       switch (skb->protocol) {
+       case cpu_to_be16(ETH_P_IP):
+               if (skb_network_header(skb) + sizeof(struct iphdr) <=
+                   skb_tail_pointer(skb))
+                       return IP_ECN_set_ect1(ip_hdr(skb));
+               break;
+
+       case cpu_to_be16(ETH_P_IPV6):
+               if (skb_network_header(skb) + sizeof(struct ipv6hdr) <=
+                   skb_tail_pointer(skb))
+                       return IP6_ECN_set_ect1(skb, ipv6_hdr(skb));
+               break;
+       }
+
+       return 0;
+}
+
 /*
  * RFC 6040 4.2
  *  To decapsulate the inner header at the tunnel egress, a compliant
@@ -208,8 +257,12 @@ static inline int INET_ECN_decapsulate(struct sk_buff *skb,
        int rc;
 
        rc = __INET_ECN_decapsulate(outer, inner, &set_ce);
-       if (!rc && set_ce)
-               INET_ECN_set_ce(skb);
+       if (!rc) {
+               if (set_ce)
+                       INET_ECN_set_ce(skb);
+               else if ((outer & INET_ECN_MASK) == INET_ECN_ECT_1)
+                       INET_ECN_set_ect1(skb);
+       }
 
        return rc;
 }
index 80262d2..1d98828 100644 (file)
@@ -203,6 +203,7 @@ struct fib6_info {
 struct rt6_info {
        struct dst_entry                dst;
        struct fib6_info __rcu          *from;
+       int                             sernum;
 
        struct rt6key                   rt6i_dst;
        struct rt6key                   rt6i_src;
@@ -291,6 +292,9 @@ static inline u32 rt6_get_cookie(const struct rt6_info *rt)
        struct fib6_info *from;
        u32 cookie = 0;
 
+       if (rt->sernum)
+               return rt->sernum;
+
        rcu_read_lock();
 
        from = rcu_dereference(rt->from);
index f7543c0..e525f00 100644 (file)
@@ -123,7 +123,7 @@ int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg);
 int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
                  struct netlink_ext_ack *extack);
 int ip6_ins_rt(struct net *net, struct fib6_info *f6i);
-int ip6_del_rt(struct net *net, struct fib6_info *f6i);
+int ip6_del_rt(struct net *net, struct fib6_info *f6i, bool skip_notify);
 
 void rt6_flush_exceptions(struct fib6_info *f6i);
 void rt6_age_exceptions(struct fib6_info *f6i, struct fib6_gc_args *gc_args,
@@ -254,6 +254,7 @@ static inline bool ipv6_anycast_destination(const struct dst_entry *dst,
 
        return rt->rt6i_flags & RTF_ANYCAST ||
                (rt->rt6i_dst.plen < 127 &&
+                !(rt->rt6i_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) &&
                 ipv6_addr_equal(&rt->rt6i_dst.addr, daddr));
 }
 
index 1bf8065..955badd 100644 (file)
@@ -908,7 +908,6 @@ static inline int ip6_default_np_autolabel(struct net *net)
        }
 }
 #else
-static inline void ip6_set_txhash(struct sock *sk) { }
 static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb,
                                        __be32 flowlabel, bool autolabel,
                                        struct flowi6 *fl6)
index 3e7d2c0..a5f7c12 100644 (file)
@@ -48,7 +48,7 @@ struct ipv6_stub {
                            struct netlink_ext_ack *extack);
        void (*fib6_nh_release)(struct fib6_nh *fib6_nh);
        void (*fib6_update_sernum)(struct net *net, struct fib6_info *rt);
-       int (*ip6_del_rt)(struct net *net, struct fib6_info *rt);
+       int (*ip6_del_rt)(struct net *net, struct fib6_info *rt, bool skip_notify);
        void (*fib6_rt_update)(struct net *net, struct fib6_info *rt,
                               struct nl_info *info);
 
index b6b4de0..97fec4d 100644 (file)
@@ -6007,7 +6007,9 @@ enum rate_control_capabilities {
 struct rate_control_ops {
        unsigned long capa;
        const char *name;
-       void *(*alloc)(struct ieee80211_hw *hw, struct dentry *debugfsdir);
+       void *(*alloc)(struct ieee80211_hw *hw);
+       void (*add_debugfs)(struct ieee80211_hw *hw, void *priv,
+                           struct dentry *debugfsdir);
        void (*free)(void *priv);
 
        void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp);
index 0e7c547..e602756 100644 (file)
@@ -68,11 +68,9 @@ static inline bool rsk_is_mptcp(const struct request_sock *req)
        return tcp_rsk(req)->is_mptcp;
 }
 
-void mptcp_parse_option(const struct sk_buff *skb, const unsigned char *ptr,
-                       int opsize, struct tcp_options_received *opt_rx);
+void mptcp_space(const struct sock *ssk, int *space, int *full_space);
 bool mptcp_syn_options(struct sock *sk, const struct sk_buff *skb,
                       unsigned int *size, struct mptcp_out_options *opts);
-void mptcp_rcv_synsent(struct sock *sk);
 bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
                          struct mptcp_out_options *opts);
 bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
@@ -197,6 +195,7 @@ static inline bool mptcp_sk_is_subflow(const struct sock *sk)
        return false;
 }
 
+static inline void mptcp_space(const struct sock *ssk, int *s, int *fs) { }
 static inline void mptcp_seq_show(struct seq_file *seq) { }
 #endif /* CONFIG_MPTCP */
 
index 7d10711..9205a76 100644 (file)
@@ -41,7 +41,7 @@ enum {
        ND_OPT_DNSSL = 31,              /* RFC6106 */
        ND_OPT_6CO = 34,                /* RFC6775 */
        ND_OPT_CAPTIVE_PORTAL = 37,     /* RFC7710 */
-       ND_OPT_PREF64 = 38,             /* RFC-ietf-6man-ra-pref64-09 */
+       ND_OPT_PREF64 = 38,             /* RFC8781 */
        __ND_OPT_MAX
 };
 
index ab96fb5..8e001e0 100644 (file)
@@ -437,6 +437,13 @@ static inline int rt_genid_ipv4(const struct net *net)
        return atomic_read(&net->ipv4.rt_genid);
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static inline int rt_genid_ipv6(const struct net *net)
+{
+       return atomic_read(&net->ipv6.fib6_sernum);
+}
+#endif
+
 static inline void rt_genid_bump_ipv4(struct net *net)
 {
        atomic_inc(&net->ipv4.rt_genid);
index 6eb627b..d4e29c9 100644 (file)
@@ -243,6 +243,10 @@ struct nft_set_elem {
                u32             buf[NFT_DATA_VALUE_MAXLEN / sizeof(u32)];
                struct nft_data val;
        } key_end;
+       union {
+               u32             buf[NFT_DATA_VALUE_MAXLEN / sizeof(u32)];
+               struct nft_data val;
+       } data;
        void                    *priv;
 };
 
@@ -901,7 +905,7 @@ static inline void nft_set_elem_update_expr(const struct nft_set_ext *ext,
 {
        struct nft_expr *expr;
 
-       if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) {
+       if (__nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) {
                expr = nft_set_ext_expr(ext);
                expr->ops->eval(expr, regs, pkt);
        }
index 67c57d6..c0411f1 100644 (file)
@@ -182,19 +182,28 @@ enum {
        NLA_BITFIELD32,
        NLA_REJECT,
        NLA_EXACT_LEN,
-       NLA_EXACT_LEN_WARN,
        NLA_MIN_LEN,
        __NLA_TYPE_MAX,
 };
 
 #define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
 
+struct netlink_range_validation {
+       u64 min, max;
+};
+
+struct netlink_range_validation_signed {
+       s64 min, max;
+};
+
 enum nla_policy_validation {
        NLA_VALIDATE_NONE,
        NLA_VALIDATE_RANGE,
        NLA_VALIDATE_MIN,
        NLA_VALIDATE_MAX,
+       NLA_VALIDATE_RANGE_PTR,
        NLA_VALIDATE_FUNCTION,
+       NLA_VALIDATE_WARN_TOO_LONG,
 };
 
 /**
@@ -217,7 +226,7 @@ enum nla_policy_validation {
  *    NLA_NESTED,
  *    NLA_NESTED_ARRAY     Length verification is done by checking len of
  *                         nested header (or empty); len field is used if
- *                         validation_data is also used, for the max attr
+ *                         nested_policy is also used, for the max attr
  *                         number in the nested policy.
  *    NLA_U8, NLA_U16,
  *    NLA_U32, NLA_U64,
@@ -228,34 +237,32 @@ enum nla_policy_validation {
  *                         just like "All other"
  *    NLA_BITFIELD32       Unused
  *    NLA_REJECT           Unused
- *    NLA_EXACT_LEN        Attribute must have exactly this length, otherwise
- *                         it is rejected.
- *    NLA_EXACT_LEN_WARN   Attribute should have exactly this length, a warning
- *                         is logged if it is longer, shorter is rejected.
+ *    NLA_EXACT_LEN        Attribute should have exactly this length, otherwise
+ *                         it is rejected or warned about, the latter happening
+ *                         if and only if the `validation_type' is set to
+ *                         NLA_VALIDATE_WARN_TOO_LONG.
  *    NLA_MIN_LEN          Minimum length of attribute payload
  *    All other            Minimum length of attribute payload
  *
- * Meaning of `validation_data' field:
+ * Meaning of validation union:
  *    NLA_BITFIELD32       This is a 32-bit bitmap/bitselector attribute and
- *                         validation data must point to a u32 value of valid
- *                         flags
- *    NLA_REJECT           This attribute is always rejected and validation data
+ *                         `bitfield32_valid' is the u32 value of valid flags
+ *    NLA_REJECT           This attribute is always rejected and `reject_message'
  *                         may point to a string to report as the error instead
  *                         of the generic one in extended ACK.
- *    NLA_NESTED           Points to a nested policy to validate, must also set
- *                         `len' to the max attribute number.
+ *    NLA_NESTED           `nested_policy' to a nested policy to validate, must
+ *                         also set `len' to the max attribute number. Use the
+ *                         provided NLA_POLICY_NESTED() macro.
  *                         Note that nla_parse() will validate, but of course not
  *                         parse, the nested sub-policies.
- *    NLA_NESTED_ARRAY     Points to a nested policy to validate, must also set
- *                         `len' to the max attribute number. The difference to
- *                         NLA_NESTED is the structure - NLA_NESTED has the
- *                         nested attributes directly inside, while an array has
- *                         the nested attributes at another level down and the
- *                         attributes directly in the nesting don't matter.
- *    All other            Unused - but note that it's a union
- *
- * Meaning of `min' and `max' fields, use via NLA_POLICY_MIN, NLA_POLICY_MAX
- * and NLA_POLICY_RANGE:
+ *    NLA_NESTED_ARRAY     `nested_policy' points to a nested policy to validate,
+ *                         must also set `len' to the max attribute number. Use
+ *                         the provided NLA_POLICY_NESTED_ARRAY() macro.
+ *                         The difference to NLA_NESTED is the structure:
+ *                         NLA_NESTED has the nested attributes directly inside
+ *                         while an array has the nested attributes at another
+ *                         level down and the attribute types directly in the
+ *                         nesting don't matter.
  *    NLA_U8,
  *    NLA_U16,
  *    NLA_U32,
@@ -263,29 +270,47 @@ enum nla_policy_validation {
  *    NLA_S8,
  *    NLA_S16,
  *    NLA_S32,
- *    NLA_S64              These are used depending on the validation_type
- *                         field, if that is min/max/range then the minimum,
- *                         maximum and both are used (respectively) to check
+ *    NLA_S64              The `min' and `max' fields are used depending on the
+ *                         validation_type field, if that is min/max/range then
+ *                         the min, max or both are used (respectively) to check
  *                         the value of the integer attribute.
  *                         Note that in the interest of code simplicity and
  *                         struct size both limits are s16, so you cannot
  *                         enforce a range that doesn't fall within the range
  *                         of s16 - do that as usual in the code instead.
+ *                         Use the NLA_POLICY_MIN(), NLA_POLICY_MAX() and
+ *                         NLA_POLICY_RANGE() macros.
+ *    NLA_U8,
+ *    NLA_U16,
+ *    NLA_U32,
+ *    NLA_U64              If the validation_type field instead is set to
+ *                         NLA_VALIDATE_RANGE_PTR, `range' must be a pointer
+ *                         to a struct netlink_range_validation that indicates
+ *                         the min/max values.
+ *                         Use NLA_POLICY_FULL_RANGE().
+ *    NLA_S8,
+ *    NLA_S16,
+ *    NLA_S32,
+ *    NLA_S64              If the validation_type field instead is set to
+ *                         NLA_VALIDATE_RANGE_PTR, `range_signed' must be a
+ *                         pointer to a struct netlink_range_validation_signed
+ *                         that indicates the min/max values.
+ *                         Use NLA_POLICY_FULL_RANGE_SIGNED().
  *    All other            Unused - but note that it's a union
  *
  * Meaning of `validate' field, use via NLA_POLICY_VALIDATE_FN:
- *    NLA_BINARY           Validation function called for the attribute,
- *                         not compatible with use of the validation_data
- *                         as in NLA_BITFIELD32, NLA_REJECT, NLA_NESTED and
- *                         NLA_NESTED_ARRAY.
+ *    NLA_BINARY           Validation function called for the attribute.
  *    All other            Unused - but note that it's a union
  *
  * Example:
+ *
+ * static const u32 myvalidflags = 0xff231023;
+ *
  * static const struct nla_policy my_policy[ATTR_MAX+1] = {
  *     [ATTR_FOO] = { .type = NLA_U16 },
  *     [ATTR_BAR] = { .type = NLA_STRING, .len = BARSIZ },
  *     [ATTR_BAZ] = { .type = NLA_EXACT_LEN, .len = sizeof(struct mystruct) },
- *     [ATTR_GOO] = { .type = NLA_BITFIELD32, .validation_data = &myvalidflags },
+ *     [ATTR_GOO] = NLA_POLICY_BITFIELD32(myvalidflags),
  * };
  */
 struct nla_policy {
@@ -293,7 +318,11 @@ struct nla_policy {
        u8              validation_type;
        u16             len;
        union {
-               const void *validation_data;
+               const u32 bitfield32_valid;
+               const char *reject_message;
+               const struct nla_policy *nested_policy;
+               struct netlink_range_validation *range;
+               struct netlink_range_validation_signed *range_signed;
                struct {
                        s16 min, max;
                };
@@ -321,28 +350,39 @@ struct nla_policy {
 };
 
 #define NLA_POLICY_EXACT_LEN(_len)     { .type = NLA_EXACT_LEN, .len = _len }
-#define NLA_POLICY_EXACT_LEN_WARN(_len)        { .type = NLA_EXACT_LEN_WARN, \
-                                         .len = _len }
+#define NLA_POLICY_EXACT_LEN_WARN(_len) \
+       { .type = NLA_EXACT_LEN, .len = _len, \
+         .validation_type = NLA_VALIDATE_WARN_TOO_LONG, }
 #define NLA_POLICY_MIN_LEN(_len)       { .type = NLA_MIN_LEN, .len = _len }
 
 #define NLA_POLICY_ETH_ADDR            NLA_POLICY_EXACT_LEN(ETH_ALEN)
 #define NLA_POLICY_ETH_ADDR_COMPAT     NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN)
 
 #define _NLA_POLICY_NESTED(maxattr, policy) \
-       { .type = NLA_NESTED, .validation_data = policy, .len = maxattr }
+       { .type = NLA_NESTED, .nested_policy = policy, .len = maxattr }
 #define _NLA_POLICY_NESTED_ARRAY(maxattr, policy) \
-       { .type = NLA_NESTED_ARRAY, .validation_data = policy, .len = maxattr }
+       { .type = NLA_NESTED_ARRAY, .nested_policy = policy, .len = maxattr }
 #define NLA_POLICY_NESTED(policy) \
        _NLA_POLICY_NESTED(ARRAY_SIZE(policy) - 1, policy)
 #define NLA_POLICY_NESTED_ARRAY(policy) \
        _NLA_POLICY_NESTED_ARRAY(ARRAY_SIZE(policy) - 1, policy)
+#define NLA_POLICY_BITFIELD32(valid) \
+       { .type = NLA_BITFIELD32, .bitfield32_valid = valid }
 
 #define __NLA_ENSURE(condition) BUILD_BUG_ON_ZERO(!(condition))
+#define NLA_ENSURE_UINT_TYPE(tp)                       \
+       (__NLA_ENSURE(tp == NLA_U8 || tp == NLA_U16 ||  \
+                     tp == NLA_U32 || tp == NLA_U64 || \
+                     tp == NLA_MSECS) + tp)
+#define NLA_ENSURE_SINT_TYPE(tp)                       \
+       (__NLA_ENSURE(tp == NLA_S8 || tp == NLA_S16  || \
+                     tp == NLA_S32 || tp == NLA_S64) + tp)
 #define NLA_ENSURE_INT_TYPE(tp)                                \
        (__NLA_ENSURE(tp == NLA_S8 || tp == NLA_U8 ||   \
                      tp == NLA_S16 || tp == NLA_U16 || \
                      tp == NLA_S32 || tp == NLA_U32 || \
-                     tp == NLA_S64 || tp == NLA_U64) + tp)
+                     tp == NLA_S64 || tp == NLA_U64 || \
+                     tp == NLA_MSECS) + tp)
 #define NLA_ENSURE_NO_VALIDATION_PTR(tp)               \
        (__NLA_ENSURE(tp != NLA_BITFIELD32 &&           \
                      tp != NLA_REJECT &&               \
@@ -356,6 +396,18 @@ struct nla_policy {
        .max = _max                                     \
 }
 
+#define NLA_POLICY_FULL_RANGE(tp, _range) {            \
+       .type = NLA_ENSURE_UINT_TYPE(tp),               \
+       .validation_type = NLA_VALIDATE_RANGE_PTR,      \
+       .range = _range,                                \
+}
+
+#define NLA_POLICY_FULL_RANGE_SIGNED(tp, _range) {     \
+       .type = NLA_ENSURE_SINT_TYPE(tp),               \
+       .validation_type = NLA_VALIDATE_RANGE_PTR,      \
+       .range_signed = _range,                         \
+}
+
 #define NLA_POLICY_MIN(tp, _min) {                     \
        .type = NLA_ENSURE_INT_TYPE(tp),                \
        .validation_type = NLA_VALIDATE_MIN,            \
@@ -1876,4 +1928,15 @@ static inline bool nla_is_last(const struct nlattr *nla, int rem)
        return nla->nla_len == rem;
 }
 
+void nla_get_range_unsigned(const struct nla_policy *pt,
+                           struct netlink_range_validation *range);
+void nla_get_range_signed(const struct nla_policy *pt,
+                         struct netlink_range_validation_signed *range);
+
+int netlink_policy_dump_start(const struct nla_policy *policy,
+                             unsigned int maxtype,
+                             unsigned long *state);
+bool netlink_policy_dump_loop(unsigned long *state);
+int netlink_policy_dump_write(struct sk_buff *skb, unsigned long state);
+
 #endif
index 154b8f0..9e36738 100644 (file)
@@ -111,6 +111,8 @@ struct netns_ipv4 {
        int sysctl_tcp_early_demux;
        int sysctl_udp_early_demux;
 
+       int sysctl_nexthop_compat_mode;
+
        int sysctl_fwmark_reflect;
        int sysctl_tcp_fwmark_accept;
 #ifdef CONFIG_NET_L3_MASTER_DEV
@@ -171,6 +173,7 @@ struct netns_ipv4 {
        int sysctl_tcp_rmem[3];
        int sysctl_tcp_comp_sack_nr;
        unsigned long sysctl_tcp_comp_sack_delay_ns;
+       unsigned long sysctl_tcp_comp_sack_slack_ns;
        struct inet_timewait_death_row tcp_death_row;
        int sysctl_max_syn_backlog;
        int sysctl_tcp_fastopen;
index 25d2ec4..ab87a8b 100644 (file)
@@ -407,6 +407,7 @@ struct tcf_block {
        struct mutex lock;
        struct list_head chain_list;
        u32 index; /* block index for shared blocks */
+       u32 classid; /* which class this block belongs to */
        refcount_t refcnt;
        struct net *net;
        struct Qdisc *q;
@@ -710,11 +711,6 @@ static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i)
        }
 }
 
-static inline void qdisc_reset_all_tx(struct net_device *dev)
-{
-       qdisc_reset_all_tx_gt(dev, 0);
-}
-
 /* Are all TX queues of the device empty?  */
 static inline bool qdisc_all_tx_empty(const struct net_device *dev)
 {
index 6d84784..3e8c6d4 100644 (file)
@@ -2553,9 +2553,9 @@ sk_is_refcounted(struct sock *sk)
 }
 
 /**
- * skb_steal_sock
- * @skb to steal the socket from
- * @refcounted is set to true if the socket is reference-counted
+ * skb_steal_sock - steal a socket from an sk_buff
+ * @skb: sk_buff to steal the socket from
+ * @refcounted: is set to true if the socket is reference-counted
  */
 static inline struct sock *
 skb_steal_sock(struct sk_buff *skb, bool *refcounted)
index aee86a1..ae7aeb0 100644 (file)
@@ -40,6 +40,10 @@ enum switchdev_attr_id {
        SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
        SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED,
        SWITCHDEV_ATTR_ID_BRIDGE_MROUTER,
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+       SWITCHDEV_ATTR_ID_MRP_PORT_STATE,
+       SWITCHDEV_ATTR_ID_MRP_PORT_ROLE,
+#endif
 };
 
 struct switchdev_attr {
@@ -55,6 +59,11 @@ struct switchdev_attr {
                clock_t ageing_time;                    /* BRIDGE_AGEING_TIME */
                bool vlan_filtering;                    /* BRIDGE_VLAN_FILTERING */
                bool mc_disabled;                       /* MC_DISABLED */
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+               u8 mrp_port_state;                      /* MRP_PORT_STATE */
+               u8 mrp_port_role;                       /* MRP_PORT_ROLE */
+               u8 mrp_ring_state;                      /* MRP_RING_STATE */
+#endif
        } u;
 };
 
@@ -63,6 +72,12 @@ enum switchdev_obj_id {
        SWITCHDEV_OBJ_ID_PORT_VLAN,
        SWITCHDEV_OBJ_ID_PORT_MDB,
        SWITCHDEV_OBJ_ID_HOST_MDB,
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+       SWITCHDEV_OBJ_ID_MRP,
+       SWITCHDEV_OBJ_ID_RING_TEST_MRP,
+       SWITCHDEV_OBJ_ID_RING_ROLE_MRP,
+       SWITCHDEV_OBJ_ID_RING_STATE_MRP,
+#endif
 };
 
 struct switchdev_obj {
@@ -94,6 +109,53 @@ struct switchdev_obj_port_mdb {
 #define SWITCHDEV_OBJ_PORT_MDB(OBJ) \
        container_of((OBJ), struct switchdev_obj_port_mdb, obj)
 
+
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+/* SWITCHDEV_OBJ_ID_MRP */
+struct switchdev_obj_mrp {
+       struct switchdev_obj obj;
+       struct net_device *p_port;
+       struct net_device *s_port;
+       u32 ring_id;
+};
+
+#define SWITCHDEV_OBJ_MRP(OBJ) \
+       container_of((OBJ), struct switchdev_obj_mrp, obj)
+
+/* SWITCHDEV_OBJ_ID_RING_TEST_MRP */
+struct switchdev_obj_ring_test_mrp {
+       struct switchdev_obj obj;
+       /* The value is in us and a value of 0 represents to stop */
+       u32 interval;
+       u8 max_miss;
+       u32 ring_id;
+       u32 period;
+};
+
+#define SWITCHDEV_OBJ_RING_TEST_MRP(OBJ) \
+       container_of((OBJ), struct switchdev_obj_ring_test_mrp, obj)
+
+/* SWICHDEV_OBJ_ID_RING_ROLE_MRP */
+struct switchdev_obj_ring_role_mrp {
+       struct switchdev_obj obj;
+       u8 ring_role;
+       u32 ring_id;
+};
+
+#define SWITCHDEV_OBJ_RING_ROLE_MRP(OBJ) \
+       container_of((OBJ), struct switchdev_obj_ring_role_mrp, obj)
+
+struct switchdev_obj_ring_state_mrp {
+       struct switchdev_obj obj;
+       u8 ring_state;
+       u32 ring_id;
+};
+
+#define SWITCHDEV_OBJ_RING_STATE_MRP(OBJ) \
+       container_of((OBJ), struct switchdev_obj_ring_state_mrp, obj)
+
+#endif
+
 typedef int switchdev_obj_dump_cb_t(struct switchdev_obj *obj);
 
 enum switchdev_notifier_type {
diff --git a/include/net/tc_act/tc_gate.h b/include/net/tc_act/tc_gate.h
new file mode 100644 (file)
index 0000000..8bc6be8
--- /dev/null
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright 2020 NXP */
+
+#ifndef __NET_TC_GATE_H
+#define __NET_TC_GATE_H
+
+#include <net/act_api.h>
+#include <linux/tc_act/tc_gate.h>
+
+struct action_gate_entry {
+       u8                      gate_state;
+       u32                     interval;
+       s32                     ipv;
+       s32                     maxoctets;
+};
+
+struct tcfg_gate_entry {
+       int                     index;
+       u8                      gate_state;
+       u32                     interval;
+       s32                     ipv;
+       s32                     maxoctets;
+       struct list_head        list;
+};
+
+struct tcf_gate_params {
+       s32                     tcfg_priority;
+       u64                     tcfg_basetime;
+       u64                     tcfg_cycletime;
+       u64                     tcfg_cycletime_ext;
+       u32                     tcfg_flags;
+       s32                     tcfg_clockid;
+       size_t                  num_entries;
+       struct list_head        entries;
+};
+
+#define GATE_ACT_GATE_OPEN     BIT(0)
+#define GATE_ACT_PENDING       BIT(1)
+
+struct tcf_gate {
+       struct tc_action        common;
+       struct tcf_gate_params  param;
+       u8                      current_gate_status;
+       ktime_t                 current_close_time;
+       u32                     current_entry_octets;
+       s32                     current_max_octets;
+       struct tcfg_gate_entry  *next_entry;
+       struct hrtimer          hitimer;
+       enum tk_offsets         tk_offset;
+};
+
+#define to_gate(a) ((struct tcf_gate *)a)
+
+static inline bool is_tcf_gate(const struct tc_action *a)
+{
+#ifdef CONFIG_NET_CLS_ACT
+       if (a->ops && a->ops->id == TCA_ID_GATE)
+               return true;
+#endif
+       return false;
+}
+
+static inline u32 tcf_gate_index(const struct tc_action *a)
+{
+       return a->tcfa_index;
+}
+
+static inline s32 tcf_gate_prio(const struct tc_action *a)
+{
+       s32 tcfg_prio;
+
+       tcfg_prio = to_gate(a)->param.tcfg_priority;
+
+       return tcfg_prio;
+}
+
+static inline u64 tcf_gate_basetime(const struct tc_action *a)
+{
+       u64 tcfg_basetime;
+
+       tcfg_basetime = to_gate(a)->param.tcfg_basetime;
+
+       return tcfg_basetime;
+}
+
+static inline u64 tcf_gate_cycletime(const struct tc_action *a)
+{
+       u64 tcfg_cycletime;
+
+       tcfg_cycletime = to_gate(a)->param.tcfg_cycletime;
+
+       return tcfg_cycletime;
+}
+
+static inline u64 tcf_gate_cycletimeext(const struct tc_action *a)
+{
+       u64 tcfg_cycletimeext;
+
+       tcfg_cycletimeext = to_gate(a)->param.tcfg_cycletime_ext;
+
+       return tcfg_cycletimeext;
+}
+
+static inline u32 tcf_gate_num_entries(const struct tc_action *a)
+{
+       u32 num_entries;
+
+       num_entries = to_gate(a)->param.num_entries;
+
+       return num_entries;
+}
+
+static inline struct action_gate_entry
+                       *tcf_gate_get_list(const struct tc_action *a)
+{
+       struct action_gate_entry *oe;
+       struct tcf_gate_params *p;
+       struct tcfg_gate_entry *entry;
+       u32 num_entries;
+       int i = 0;
+
+       p = &to_gate(a)->param;
+       num_entries = p->num_entries;
+
+       list_for_each_entry(entry, &p->entries, list)
+               i++;
+
+       if (i != num_entries)
+               return NULL;
+
+       oe = kcalloc(num_entries, sizeof(*oe), GFP_ATOMIC);
+       if (!oe)
+               return NULL;
+
+       i = 0;
+       list_for_each_entry(entry, &p->entries, list) {
+               oe[i].gate_state = entry->gate_state;
+               oe[i].interval = entry->interval;
+               oe[i].ipv = entry->ipv;
+               oe[i].maxoctets = entry->maxoctets;
+               i++;
+       }
+
+       return oe;
+}
+#endif
index 5fa9eac..43b87a8 100644 (file)
@@ -51,7 +51,7 @@ extern struct inet_hashinfo tcp_hashinfo;
 extern struct percpu_counter tcp_orphan_count;
 void tcp_time_wait(struct sock *sk, int state, int timeo);
 
-#define MAX_TCP_HEADER (128 + MAX_HEADER)
+#define MAX_TCP_HEADER L1_CACHE_ALIGN(128 + MAX_HEADER)
 #define MAX_TCP_OPTION_SPACE 40
 #define TCP_MIN_SND_MSS                48
 #define TCP_MIN_GSO_SIZE       (TCP_MIN_SND_MSS - MAX_TCP_OPTION_SPACE)
@@ -126,6 +126,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);
                                  * to combine FIN-WAIT-2 timeout with
                                  * TIME-WAIT timer.
                                  */
+#define TCP_FIN_TIMEOUT_MAX (120 * HZ) /* max TCP_LINGER2 value (two minutes) */
 
 #define TCP_DELACK_MAX ((unsigned)(HZ/5))      /* maximal time to delay before sending an ACK */
 #if HZ >= 100
@@ -1288,26 +1289,22 @@ static inline bool tcp_needs_internal_pacing(const struct sock *sk)
        return smp_load_acquire(&sk->sk_pacing_status) == SK_PACING_NEEDED;
 }
 
-/* Return in jiffies the delay before one skb is sent.
- * If @skb is NULL, we look at EDT for next packet being sent on the socket.
+/* Estimates in how many jiffies next packet for this flow can be sent.
+ * Scheduling a retransmit timer too early would be silly.
  */
-static inline unsigned long tcp_pacing_delay(const struct sock *sk,
-                                            const struct sk_buff *skb)
+static inline unsigned long tcp_pacing_delay(const struct sock *sk)
 {
-       s64 pacing_delay = skb ? skb->tstamp : tcp_sk(sk)->tcp_wstamp_ns;
+       s64 delay = tcp_sk(sk)->tcp_wstamp_ns - tcp_sk(sk)->tcp_clock_cache;
 
-       pacing_delay -= tcp_sk(sk)->tcp_clock_cache;
-
-       return pacing_delay > 0 ? nsecs_to_jiffies(pacing_delay) : 0;
+       return delay > 0 ? nsecs_to_jiffies(delay) : 0;
 }
 
 static inline void tcp_reset_xmit_timer(struct sock *sk,
                                        const int what,
                                        unsigned long when,
-                                       const unsigned long max_when,
-                                       const struct sk_buff *skb)
+                                       const unsigned long max_when)
 {
-       inet_csk_reset_xmit_timer(sk, what, when + tcp_pacing_delay(sk, skb),
+       inet_csk_reset_xmit_timer(sk, what, when + tcp_pacing_delay(sk),
                                  max_when);
 }
 
@@ -1335,8 +1332,7 @@ static inline void tcp_check_probe_timer(struct sock *sk)
 {
        if (!tcp_sk(sk)->packets_out && !inet_csk(sk)->icsk_pending)
                tcp_reset_xmit_timer(sk, ICSK_TIME_PROBE0,
-                                    tcp_probe0_base(sk), TCP_RTO_MAX,
-                                    NULL);
+                                    tcp_probe0_base(sk), TCP_RTO_MAX);
 }
 
 static inline void tcp_init_wl(struct tcp_sock *tp, u32 seq)
index 40c6d33..3cc6d5d 100644 (file)
@@ -181,4 +181,6 @@ bool xdp_attachment_flags_ok(struct xdp_attachment_info *info,
 void xdp_attachment_setup(struct xdp_attachment_info *info,
                          struct netdev_bpf *bpf);
 
+#define DEV_MAP_BULK_SIZE 16
+
 #endif /* __LINUX_NET_XDP_H__ */
index ebffcb3..4953e99 100644 (file)
@@ -92,6 +92,8 @@
 #define OCELOT_SPEED_100               2
 #define OCELOT_SPEED_10                        3
 
+#define OCELOT_PTP_PINS_NUM            4
+
 #define TARGET_OFFSET                  24
 #define REG_MASK                       GENMASK(TARGET_OFFSET - 1, 0)
 #define REG(reg, offset)               [reg & REG_MASK] = offset
@@ -385,6 +387,8 @@ enum ocelot_reg {
        PTP_PIN_TOD_SEC_MSB,
        PTP_PIN_TOD_SEC_LSB,
        PTP_PIN_TOD_NSEC,
+       PTP_PIN_WF_HIGH_PERIOD,
+       PTP_PIN_WF_LOW_PERIOD,
        PTP_CFG_MISC,
        PTP_CLK_CFG_ADJ_CFG,
        PTP_CLK_CFG_ADJ_FREQ,
@@ -440,10 +444,11 @@ enum ocelot_regfield {
        REGFIELD_MAX
 };
 
-enum ocelot_clk_pins {
-       ALT_PPS_PIN     = 1,
-       EXT_CLK_PIN,
-       ALT_LDST_PIN,
+enum ocelot_ptp_pins {
+       PTP_PIN_0,
+       PTP_PIN_1,
+       PTP_PIN_2,
+       PTP_PIN_3,
        TOD_ACC_PIN
 };
 
@@ -476,6 +481,8 @@ struct ocelot_port {
 
        void __iomem                    *regs;
 
+       bool                            vlan_aware;
+
        /* Ingress default VLAN (pvid) */
        u16                             pvid;
 
@@ -500,6 +507,7 @@ struct ocelot {
        unsigned int                    num_stats;
 
        int                             shared_queue_sz;
+       int                             num_mact_rows;
 
        struct net_device               *hw_bridge_dev;
        u16                             bridge_mask;
@@ -547,6 +555,7 @@ struct ocelot {
        struct mutex                    ptp_lock;
        /* Protects the PTP clock */
        spinlock_t                      ptp_clock_lock;
+       struct ptp_pin_desc             ptp_pins[OCELOT_PTP_PINS_NUM];
 };
 
 struct ocelot_policer {
@@ -610,7 +619,7 @@ int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
 int ocelot_fdb_dump(struct ocelot *ocelot, int port,
                    dsa_fdb_dump_cb_t *cb, void *data);
 int ocelot_fdb_add(struct ocelot *ocelot, int port,
-                  const unsigned char *addr, u16 vid, bool vlan_aware);
+                  const unsigned char *addr, u16 vid);
 int ocelot_fdb_del(struct ocelot *ocelot, int port,
                   const unsigned char *addr, u16 vid);
 int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
@@ -618,7 +627,6 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
 int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid);
 int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr);
 int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr);
-int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts);
 int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
                                 struct sk_buff *skb);
 void ocelot_get_txtstamp(struct ocelot *ocelot);
diff --git a/include/soc/mscc/ocelot_ptp.h b/include/soc/mscc/ocelot_ptp.h
new file mode 100644 (file)
index 0000000..4a6b2f7
--- /dev/null
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Microsemi Ocelot Switch driver
+ *
+ * License: Dual MIT/GPL
+ * Copyright (c) 2017 Microsemi Corporation
+ * Copyright 2020 NXP
+ */
+
+#ifndef _MSCC_OCELOT_PTP_H_
+#define _MSCC_OCELOT_PTP_H_
+
+#include <linux/ptp_clock_kernel.h>
+#include <soc/mscc/ocelot.h>
+
+#define PTP_PIN_CFG_RSZ                        0x20
+#define PTP_PIN_TOD_SEC_MSB_RSZ                PTP_PIN_CFG_RSZ
+#define PTP_PIN_TOD_SEC_LSB_RSZ                PTP_PIN_CFG_RSZ
+#define PTP_PIN_TOD_NSEC_RSZ           PTP_PIN_CFG_RSZ
+#define PTP_PIN_WF_HIGH_PERIOD_RSZ     PTP_PIN_CFG_RSZ
+#define PTP_PIN_WF_LOW_PERIOD_RSZ      PTP_PIN_CFG_RSZ
+
+#define PTP_PIN_CFG_DOM                        BIT(0)
+#define PTP_PIN_CFG_SYNC               BIT(2)
+#define PTP_PIN_CFG_ACTION(x)          ((x) << 3)
+#define PTP_PIN_CFG_ACTION_MASK                PTP_PIN_CFG_ACTION(0x7)
+
+enum {
+       PTP_PIN_ACTION_IDLE = 0,
+       PTP_PIN_ACTION_LOAD,
+       PTP_PIN_ACTION_SAVE,
+       PTP_PIN_ACTION_CLOCK,
+       PTP_PIN_ACTION_DELTA,
+       PTP_PIN_ACTION_NOSYNC,
+       PTP_PIN_ACTION_SYNC,
+};
+
+#define PTP_CFG_MISC_PTP_EN            BIT(2)
+
+#define PSEC_PER_SEC                   1000000000000LL
+
+#define PTP_CFG_CLK_ADJ_CFG_ENA                BIT(0)
+#define PTP_CFG_CLK_ADJ_CFG_DIR                BIT(1)
+
+#define PTP_CFG_CLK_ADJ_FREQ_NS                BIT(30)
+
+int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts);
+int ocelot_ptp_settime64(struct ptp_clock_info *ptp,
+                        const struct timespec64 *ts);
+int ocelot_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta);
+int ocelot_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm);
+int ocelot_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
+                     enum ptp_pin_function func, unsigned int chan);
+int ocelot_ptp_enable(struct ptp_clock_info *ptp,
+                     struct ptp_clock_request *rq, int on);
+int ocelot_init_timestamp(struct ocelot *ocelot, struct ptp_clock_info *info);
+int ocelot_deinit_timestamp(struct ocelot *ocelot);
+#endif
index 0dd52b0..361cb64 100644 (file)
@@ -168,7 +168,6 @@ int tegra_io_pad_power_disable(enum tegra_io_pad id);
 int tegra_io_rail_power_on(unsigned int id);
 int tegra_io_rail_power_off(unsigned int id);
 
-enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void);
 void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode);
 void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode);
 
@@ -220,11 +219,6 @@ static inline int tegra_io_rail_power_off(unsigned int id)
        return -ENOSYS;
 }
 
-static inline enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)
-{
-       return TEGRA_SUSPEND_NONE;
-}
-
 static inline void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode)
 {
 }
@@ -235,4 +229,13 @@ static inline void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode)
 
 #endif /* CONFIG_SOC_TEGRA_PMC */
 
+#if defined(CONFIG_SOC_TEGRA_PMC) && defined(CONFIG_PM_SLEEP)
+enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void);
+#else
+static inline enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)
+{
+       return TEGRA_SUSPEND_NONE;
+}
+#endif
+
 #endif /* __SOC_TEGRA_PMC_H__ */
index 3ee8036..225154a 100644 (file)
@@ -494,6 +494,11 @@ void snd_hda_update_power_acct(struct hda_codec *codec);
 static inline void snd_hda_set_power_save(struct hda_bus *bus, int delay) {}
 #endif
 
+static inline bool hda_codec_need_resume(struct hda_codec *codec)
+{
+       return !codec->relaxed_resume && codec->jacktbl.used;
+}
+
 #ifdef CONFIG_SND_HDA_PATCH_LOADER
 /*
  * patch firmware
index d4825b8..b33abe9 100644 (file)
@@ -351,7 +351,6 @@ struct snd_soc_dai {
 
        /* bit field */
        unsigned int probed:1;
-       unsigned int started[SNDRV_PCM_STREAM_LAST + 1];
 };
 
 static inline struct snd_soc_pcm_stream *
index 13458e4..946f88a 100644 (file)
@@ -790,6 +790,9 @@ struct snd_soc_dai_link {
        const struct snd_soc_pcm_stream *params;
        unsigned int num_params;
 
+       struct snd_soc_dapm_widget *playback_widget;
+       struct snd_soc_dapm_widget *capture_widget;
+
        unsigned int dai_fmt;           /* format to set on init */
 
        enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */
index 7ecaa65..c2f580f 100644 (file)
@@ -130,7 +130,7 @@ DEFINE_EVENT(iocg_inuse_update, iocost_inuse_reset,
 
 TRACE_EVENT(iocost_ioc_vrate_adj,
 
-       TP_PROTO(struct ioc *ioc, u64 new_vrate, u32 (*missed_ppm)[2],
+       TP_PROTO(struct ioc *ioc, u64 new_vrate, u32 *missed_ppm,
                u32 rq_wait_pct, int nr_lagging, int nr_shortages,
                int nr_surpluses),
 
@@ -155,8 +155,8 @@ TRACE_EVENT(iocost_ioc_vrate_adj,
                __entry->old_vrate = atomic64_read(&ioc->vtime_rate);;
                __entry->new_vrate = new_vrate;
                __entry->busy_level = ioc->busy_level;
-               __entry->read_missed_ppm = (*missed_ppm)[READ];
-               __entry->write_missed_ppm = (*missed_ppm)[WRITE];
+               __entry->read_missed_ppm = missed_ppm[READ];
+               __entry->write_missed_ppm = missed_ppm[WRITE];
                __entry->rq_wait_pct = rq_wait_pct;
                __entry->nr_lagging = nr_lagging;
                __entry->nr_shortages = nr_shortages;
diff --git a/include/trace/events/qrtr.h b/include/trace/events/qrtr.h
new file mode 100644 (file)
index 0000000..b1de14c
--- /dev/null
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM qrtr
+
+#if !defined(_TRACE_QRTR_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_QRTR_H
+
+#include <linux/qrtr.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(qrtr_ns_service_announce_new,
+
+       TP_PROTO(__le32 service, __le32 instance, __le32 node, __le32 port),
+
+       TP_ARGS(service, instance, node, port),
+
+       TP_STRUCT__entry(
+               __field(__le32, service)
+               __field(__le32, instance)
+               __field(__le32, node)
+               __field(__le32, port)
+       ),
+
+       TP_fast_assign(
+               __entry->service = service;
+               __entry->instance = instance;
+               __entry->node = node;
+               __entry->port = port;
+       ),
+
+       TP_printk("advertising new server [%d:%x]@[%d:%d]",
+                 __entry->service, __entry->instance, __entry->node,
+                 __entry->port
+       )
+);
+
+TRACE_EVENT(qrtr_ns_service_announce_del,
+
+       TP_PROTO(__le32 service, __le32 instance, __le32 node, __le32 port),
+
+       TP_ARGS(service, instance, node, port),
+
+       TP_STRUCT__entry(
+               __field(__le32, service)
+               __field(__le32, instance)
+               __field(__le32, node)
+               __field(__le32, port)
+       ),
+
+       TP_fast_assign(
+               __entry->service = service;
+               __entry->instance = instance;
+               __entry->node = node;
+               __entry->port = port;
+       ),
+
+       TP_printk("advertising removal of server [%d:%x]@[%d:%d]",
+                 __entry->service, __entry->instance, __entry->node,
+                 __entry->port
+       )
+);
+
+TRACE_EVENT(qrtr_ns_server_add,
+
+       TP_PROTO(__le32 service, __le32 instance, __le32 node, __le32 port),
+
+       TP_ARGS(service, instance, node, port),
+
+       TP_STRUCT__entry(
+               __field(__le32, service)
+               __field(__le32, instance)
+               __field(__le32, node)
+               __field(__le32, port)
+       ),
+
+       TP_fast_assign(
+               __entry->service = service;
+               __entry->instance = instance;
+               __entry->node = node;
+               __entry->port = port;
+       ),
+
+       TP_printk("add server [%d:%x]@[%d:%d]",
+                 __entry->service, __entry->instance, __entry->node,
+                 __entry->port
+       )
+);
+
+TRACE_EVENT(qrtr_ns_message,
+
+       TP_PROTO(const char * const ctrl_pkt_str, __u32 sq_node, __u32 sq_port),
+
+       TP_ARGS(ctrl_pkt_str, sq_node, sq_port),
+
+       TP_STRUCT__entry(
+               __string(ctrl_pkt_str, ctrl_pkt_str)
+               __field(__u32, sq_node)
+               __field(__u32, sq_port)
+       ),
+
+       TP_fast_assign(
+               __assign_str(ctrl_pkt_str, ctrl_pkt_str);
+               __entry->sq_node = sq_node;
+               __entry->sq_port = sq_port;
+       ),
+
+       TP_printk("%s from %d:%d",
+                 __get_str(ctrl_pkt_str), __entry->sq_node, __entry->sq_port
+       )
+);
+
+#endif /* _TRACE_QRTR_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 051f26f..132c3c7 100644 (file)
@@ -692,11 +692,10 @@ TRACE_EVENT(xprtrdma_prepsend_failed,
 
 TRACE_EVENT(xprtrdma_post_send,
        TP_PROTO(
-               const struct rpcrdma_req *req,
-               int status
+               const struct rpcrdma_req *req
        ),
 
-       TP_ARGS(req, status),
+       TP_ARGS(req),
 
        TP_STRUCT__entry(
                __field(const void *, req)
@@ -705,7 +704,6 @@ TRACE_EVENT(xprtrdma_post_send,
                __field(unsigned int, client_id)
                __field(int, num_sge)
                __field(int, signaled)
-               __field(int, status)
        ),
 
        TP_fast_assign(
@@ -718,15 +716,13 @@ TRACE_EVENT(xprtrdma_post_send,
                __entry->sc = req->rl_sendctx;
                __entry->num_sge = req->rl_wr.num_sge;
                __entry->signaled = req->rl_wr.send_flags & IB_SEND_SIGNALED;
-               __entry->status = status;
        ),
 
-       TP_printk("task:%u@%u req=%p sc=%p (%d SGE%s) %sstatus=%d",
+       TP_printk("task:%u@%u req=%p sc=%p (%d SGE%s) %s",
                __entry->task_id, __entry->client_id,
                __entry->req, __entry->sc, __entry->num_sge,
                (__entry->num_sge == 1 ? "" : "s"),
-               (__entry->signaled ? "signaled " : ""),
-               __entry->status
+               (__entry->signaled ? "signaled" : "")
        )
 );
 
@@ -1695,17 +1691,15 @@ DECLARE_EVENT_CLASS(svcrdma_sendcomp_event,
 
 TRACE_EVENT(svcrdma_post_send,
        TP_PROTO(
-               const struct ib_send_wr *wr,
-               int status
+               const struct ib_send_wr *wr
        ),
 
-       TP_ARGS(wr, status),
+       TP_ARGS(wr),
 
        TP_STRUCT__entry(
                __field(const void *, cqe)
                __field(unsigned int, num_sge)
                __field(u32, inv_rkey)
-               __field(int, status)
        ),
 
        TP_fast_assign(
@@ -1713,12 +1707,11 @@ TRACE_EVENT(svcrdma_post_send,
                __entry->num_sge = wr->num_sge;
                __entry->inv_rkey = (wr->opcode == IB_WR_SEND_WITH_INV) ?
                                        wr->ex.invalidate_rkey : 0;
-               __entry->status = status;
        ),
 
-       TP_printk("cqe=%p num_sge=%u inv_rkey=0x%08x status=%d",
+       TP_printk("cqe=%p num_sge=%u inv_rkey=0x%08x",
                __entry->cqe, __entry->num_sge,
-               __entry->inv_rkey, __entry->status
+               __entry->inv_rkey
        )
 );
 
@@ -1783,26 +1776,23 @@ TRACE_EVENT(svcrdma_wc_receive,
 TRACE_EVENT(svcrdma_post_rw,
        TP_PROTO(
                const void *cqe,
-               int sqecount,
-               int status
+               int sqecount
        ),
 
-       TP_ARGS(cqe, sqecount, status),
+       TP_ARGS(cqe, sqecount),
 
        TP_STRUCT__entry(
                __field(const void *, cqe)
                __field(int, sqecount)
-               __field(int, status)
        ),
 
        TP_fast_assign(
                __entry->cqe = cqe;
                __entry->sqecount = sqecount;
-               __entry->status = status;
        ),
 
-       TP_printk("cqe=%p sqecount=%d status=%d",
-               __entry->cqe, __entry->sqecount, __entry->status
+       TP_printk("cqe=%p sqecount=%d",
+               __entry->cqe, __entry->sqecount
        )
 );
 
@@ -1870,6 +1860,34 @@ DECLARE_EVENT_CLASS(svcrdma_sendqueue_event,
 DEFINE_SQ_EVENT(full);
 DEFINE_SQ_EVENT(retry);
 
+TRACE_EVENT(svcrdma_sq_post_err,
+       TP_PROTO(
+               const struct svcxprt_rdma *rdma,
+               int status
+       ),
+
+       TP_ARGS(rdma, status),
+
+       TP_STRUCT__entry(
+               __field(int, avail)
+               __field(int, depth)
+               __field(int, status)
+               __string(addr, rdma->sc_xprt.xpt_remotebuf)
+       ),
+
+       TP_fast_assign(
+               __entry->avail = atomic_read(&rdma->sc_sq_avail);
+               __entry->depth = rdma->sc_sq_depth;
+               __entry->status = status;
+               __assign_str(addr, rdma->sc_xprt.xpt_remotebuf);
+       ),
+
+       TP_printk("addr=%s sc_sq_avail=%d/%d status=%d",
+               __get_str(addr), __entry->avail, __entry->depth,
+               __entry->status
+       )
+);
+
 #endif /* _TRACE_RPCRDMA_H */
 
 #include <trace/define_trace.h>
index 37342a1..7848141 100644 (file)
@@ -46,7 +46,7 @@ TRACE_EVENT(wbt_stat,
        ),
 
        TP_printk("%s: rmean=%llu, rmin=%llu, rmax=%llu, rsamples=%llu, "
-                 "wmean=%llu, wmin=%llu, wmax=%llu, wsamples=%llu\n",
+                 "wmean=%llu, wmin=%llu, wmax=%llu, wsamples=%llu",
                  __entry->name, __entry->rmean, __entry->rmin, __entry->rmax,
                  __entry->rnr_samples, __entry->wmean, __entry->wmin,
                  __entry->wmax, __entry->wnr_samples)
@@ -73,7 +73,7 @@ TRACE_EVENT(wbt_lat,
                __entry->lat = div_u64(lat, 1000);
        ),
 
-       TP_printk("%s: latency %lluus\n", __entry->name,
+       TP_printk("%s: latency %lluus", __entry->name,
                        (unsigned long long) __entry->lat)
 );
 
@@ -115,7 +115,7 @@ TRACE_EVENT(wbt_step,
                __entry->max    = max;
        ),
 
-       TP_printk("%s: %s: step=%d, window=%luus, background=%u, normal=%u, max=%u\n",
+       TP_printk("%s: %s: step=%d, window=%luus, background=%u, normal=%u, max=%u",
                  __entry->name, __entry->msg, __entry->step, __entry->window,
                  __entry->bg, __entry->normal, __entry->max)
 );
@@ -148,7 +148,7 @@ TRACE_EVENT(wbt_timer,
                __entry->inflight       = inflight;
        ),
 
-       TP_printk("%s: status=%u, step=%d, inflight=%u\n", __entry->name,
+       TP_printk("%s: status=%u, step=%d, inflight=%u", __entry->name,
                  __entry->status, __entry->step, __entry->inflight)
 );
 
index d94def2..85a33be 100644 (file)
@@ -36,7 +36,6 @@
        EM( WB_REASON_SYNC,                     "sync")                 \
        EM( WB_REASON_PERIODIC,                 "periodic")             \
        EM( WB_REASON_LAPTOP_TIMER,             "laptop_timer")         \
-       EM( WB_REASON_FREE_MORE_MEM,            "free_more_memory")     \
        EM( WB_REASON_FS_FREE_SPACE,            "fs_free_space")        \
        EMe(WB_REASON_FORKER_THREAD,            "forker_thread")
 
index 65f6972..d28b4ce 100644 (file)
@@ -346,6 +346,10 @@ struct drm_amdgpu_gem_userptr {
 #define AMDGPU_TILING_DCC_PITCH_MAX_MASK               0x3FFF
 #define AMDGPU_TILING_DCC_INDEPENDENT_64B_SHIFT                43
 #define AMDGPU_TILING_DCC_INDEPENDENT_64B_MASK         0x1
+#define AMDGPU_TILING_DCC_INDEPENDENT_128B_SHIFT       44
+#define AMDGPU_TILING_DCC_INDEPENDENT_128B_MASK                0x1
+#define AMDGPU_TILING_SCANOUT_SHIFT                    63
+#define AMDGPU_TILING_SCANOUT_MASK                     0x1
 
 /* Set/Get helpers for tiling flags. */
 #define AMDGPU_TILING_SET(field, value) \
index 2e29a67..101b0c8 100644 (file)
@@ -73,7 +73,7 @@ struct bpf_insn {
 /* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */
 struct bpf_lpm_trie_key {
        __u32   prefixlen;      /* up to 32 for AF_INET, 128 for AF_INET6 */
-       __u8    data[]; /* Arbitrary size */
+       __u8    data[0];        /* Arbitrary size */
 };
 
 struct bpf_cgroup_storage_key {
@@ -113,6 +113,9 @@ enum bpf_cmd {
        BPF_MAP_DELETE_BATCH,
        BPF_LINK_CREATE,
        BPF_LINK_UPDATE,
+       BPF_LINK_GET_FD_BY_ID,
+       BPF_LINK_GET_NEXT_ID,
+       BPF_ENABLE_STATS,
 };
 
 enum bpf_map_type {
@@ -220,6 +223,15 @@ enum bpf_attach_type {
 
 #define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
 
+enum bpf_link_type {
+       BPF_LINK_TYPE_UNSPEC = 0,
+       BPF_LINK_TYPE_RAW_TRACEPOINT = 1,
+       BPF_LINK_TYPE_TRACING = 2,
+       BPF_LINK_TYPE_CGROUP = 3,
+
+       MAX_BPF_LINK_TYPE,
+};
+
 /* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
  *
  * NONE(default): No further bpf programs allowed in the subtree.
@@ -379,6 +391,12 @@ enum {
  */
 #define BPF_F_QUERY_EFFECTIVE  (1U << 0)
 
+/* type for BPF_ENABLE_STATS */
+enum bpf_stats_type {
+       /* enabled run_time_ns and run_cnt */
+       BPF_STATS_RUN_TIME = 0,
+};
+
 enum bpf_stack_build_id_status {
        /* user space need an empty entry to identify end of a trace */
        BPF_STACK_BUILD_ID_EMPTY = 0,
@@ -523,6 +541,7 @@ union bpf_attr {
                        __u32           prog_id;
                        __u32           map_id;
                        __u32           btf_id;
+                       __u32           link_id;
                };
                __u32           next_id;
                __u32           open_flags;
@@ -589,6 +608,10 @@ union bpf_attr {
                __u32           old_prog_fd;
        } link_update;
 
+       struct { /* struct used by BPF_ENABLE_STATS command */
+               __u32           type;
+       } enable_stats;
+
 } __attribute__((aligned(8)));
 
 /* The description below is an attempt at providing documentation to eBPF
@@ -652,6 +675,8 @@ union bpf_attr {
  * u64 bpf_ktime_get_ns(void)
  *     Description
  *             Return the time elapsed since system boot, in nanoseconds.
+ *             Does not include time the system was suspended.
+ *             See: clock_gettime(CLOCK_MONOTONIC)
  *     Return
  *             Current *ktime*.
  *
@@ -1562,7 +1587,7 @@ union bpf_attr {
  *     Return
  *             0
  *
- * int bpf_setsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen)
+ * int bpf_setsockopt(void *bpf_socket, int level, int optname, void *optval, int optlen)
  *     Description
  *             Emulate a call to **setsockopt()** on the socket associated to
  *             *bpf_socket*, which must be a full socket. The *level* at
@@ -1570,6 +1595,11 @@ union bpf_attr {
  *             must be specified, see **setsockopt(2)** for more information.
  *             The option value of length *optlen* is pointed by *optval*.
  *
+ *             *bpf_socket* should be one of the following:
+ *             * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.
+ *             * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**
+ *               and **BPF_CGROUP_INET6_CONNECT**.
+ *
  *             This helper actually implements a subset of **setsockopt()**.
  *             It supports the following *level*\ s:
  *
@@ -1642,7 +1672,7 @@ union bpf_attr {
  *             ifindex, but doesn't require a map to do so.
  *     Return
  *             **XDP_REDIRECT** on success, or the value of the two lower bits
- *             of the **flags* argument on error.
+ *             of the *flags* argument on error.
  *
  * int bpf_sk_redirect_map(struct sk_buff *skb, struct bpf_map *map, u32 key, u64 flags)
  *     Description
@@ -1764,7 +1794,7 @@ union bpf_attr {
  *     Return
  *             0 on success, or a negative error in case of failure.
  *
- * int bpf_getsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen)
+ * int bpf_getsockopt(void *bpf_socket, int level, int optname, void *optval, int optlen)
  *     Description
  *             Emulate a call to **getsockopt()** on the socket associated to
  *             *bpf_socket*, which must be a full socket. The *level* at
@@ -1773,6 +1803,11 @@ union bpf_attr {
  *             The retrieved value is stored in the structure pointed by
  *             *opval* and of length *optlen*.
  *
+ *             *bpf_socket* should be one of the following:
+ *             * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.
+ *             * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**
+ *               and **BPF_CGROUP_INET6_CONNECT**.
+ *
  *             This helper actually implements a subset of **getsockopt()**.
  *             It supports the following *level*\ s:
  *
@@ -3025,6 +3060,14 @@ union bpf_attr {
  *             * **-EOPNOTSUPP**       Unsupported operation, for example a
  *                                     call from outside of TC ingress.
  *             * **-ESOCKTNOSUPPORT**  Socket type not supported (reuseport).
+ *
+ * u64 bpf_ktime_get_boot_ns(void)
+ *     Description
+ *             Return the time elapsed since system boot, in nanoseconds.
+ *             Does include the time the system was suspended.
+ *             See: clock_gettime(CLOCK_BOOTTIME)
+ *     Return
+ *             Current *ktime*.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -3151,7 +3194,8 @@ union bpf_attr {
        FN(xdp_output),                 \
        FN(get_netns_cookie),           \
        FN(get_current_ancestor_cgroup_id),     \
-       FN(sk_assign),
+       FN(sk_assign),                  \
+       FN(ktime_get_boot_ns),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -3598,6 +3642,25 @@ struct bpf_btf_info {
        __u32 id;
 } __attribute__((aligned(8)));
 
+struct bpf_link_info {
+       __u32 type;
+       __u32 id;
+       __u32 prog_id;
+       union {
+               struct {
+                       __aligned_u64 tp_name; /* in/out: tp_name buffer ptr */
+                       __u32 tp_name_len;     /* in/out: tp_name buffer len */
+               } raw_tracepoint;
+               struct {
+                       __u32 attach_type;
+               } tracing;
+               struct {
+                       __u64 cgroup_id;
+                       __u32 attach_type;
+               } cgroup;
+       };
+} __attribute__((aligned(8)));
+
 /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
  * by user and intended to be used by socket (e.g. to bind to, depends on
  * attach attach type).
index 8134924..e6b6cb0 100644 (file)
@@ -36,12 +36,10 @@ struct btrfs_ioctl_vol_args {
 #define BTRFS_DEVICE_PATH_NAME_MAX     1024
 #define BTRFS_SUBVOL_NAME_MAX          4039
 
-/*
- * Deprecated since 5.7:
- *
- * BTRFS_SUBVOL_CREATE_ASYNC   (1ULL << 0)
- */
-
+#ifndef __KERNEL__
+/* Deprecated since 5.7 */
+# define BTRFS_SUBVOL_CREATE_ASYNC     (1ULL << 0)
+#endif
 #define BTRFS_SUBVOL_RDONLY            (1ULL << 1)
 #define BTRFS_SUBVOL_QGROUP_INHERIT    (1ULL << 2)
 
index dbc7092..7f30393 100644 (file)
@@ -39,6 +39,12 @@ struct dma_buf_sync {
 
 #define DMA_BUF_BASE           'b'
 #define DMA_BUF_IOCTL_SYNC     _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
+
+/* 32/64bitness of this uapi was botched in android, there's no difference
+ * between them in actual uapi, they're just different numbers.
+ */
 #define DMA_BUF_SET_NAME       _IOW(DMA_BUF_BASE, 1, const char *)
+#define DMA_BUF_SET_NAME_A     _IOW(DMA_BUF_BASE, 1, u32)
+#define DMA_BUF_SET_NAME_B     _IOW(DMA_BUF_BASE, 1, u64)
 
 #endif
index 0cca196..ca5cb3e 100644 (file)
@@ -36,7 +36,7 @@ struct sock_extended_err {
  *
  *     The timestamping interfaces SO_TIMESTAMPING, MSG_TSTAMP_*
  *     communicate network timestamps by passing this struct in a cmsg with
- *     recvmsg(). See Documentation/networking/timestamping.txt for details.
+ *     recvmsg(). See Documentation/networking/timestamping.rst for details.
  *     User space sees a timespec definition that matches either
  *     __kernel_timespec or __kernel_old_timespec, in the kernel we
  *     require two structure definitions to provide both.
index 92f737f..f4662b3 100644 (file)
@@ -1666,6 +1666,18 @@ static inline int ethtool_validate_duplex(__u8 duplex)
        return 0;
 }
 
+#define MASTER_SLAVE_CFG_UNSUPPORTED           0
+#define MASTER_SLAVE_CFG_UNKNOWN               1
+#define MASTER_SLAVE_CFG_MASTER_PREFERRED      2
+#define MASTER_SLAVE_CFG_SLAVE_PREFERRED       3
+#define MASTER_SLAVE_CFG_MASTER_FORCE          4
+#define MASTER_SLAVE_CFG_SLAVE_FORCE           5
+#define MASTER_SLAVE_STATE_UNSUPPORTED         0
+#define MASTER_SLAVE_STATE_UNKNOWN             1
+#define MASTER_SLAVE_STATE_MASTER              2
+#define MASTER_SLAVE_STATE_SLAVE               3
+#define MASTER_SLAVE_STATE_ERR                 4
+
 /* Which connector port. */
 #define PORT_TP                        0x00
 #define PORT_AUI               0x01
@@ -1904,7 +1916,9 @@ struct ethtool_link_settings {
        __u8    eth_tp_mdix_ctrl;
        __s8    link_mode_masks_nwords;
        __u8    transceiver;
-       __u8    reserved1[3];
+       __u8    master_slave_cfg;
+       __u8    master_slave_state;
+       __u8    reserved1[1];
        __u32   reserved[7];
        __u32   link_mode_masks[0];
        /* layout of link_mode_masks fields:
index 7fde763..bf1d310 100644 (file)
@@ -216,6 +216,8 @@ enum {
        ETHTOOL_A_LINKMODES_PEER,               /* bitset */
        ETHTOOL_A_LINKMODES_SPEED,              /* u32 */
        ETHTOOL_A_LINKMODES_DUPLEX,             /* u8 */
+       ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG,   /* u8 */
+       ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE, /* u8 */
 
        /* add new constants above here */
        __ETHTOOL_A_LINKMODES_CNT,
index 877f7fa..9c0636e 100644 (file)
@@ -48,6 +48,7 @@ enum {
        CTRL_CMD_NEWMCAST_GRP,
        CTRL_CMD_DELMCAST_GRP,
        CTRL_CMD_GETMCAST_GRP, /* unused */
+       CTRL_CMD_GETPOLICY,
        __CTRL_CMD_MAX,
 };
 
@@ -62,6 +63,7 @@ enum {
        CTRL_ATTR_MAXATTR,
        CTRL_ATTR_OPS,
        CTRL_ATTR_MCAST_GROUPS,
+       CTRL_ATTR_POLICY,
        __CTRL_ATTR_MAX,
 };
 
index 991b2b7..8f24404 100644 (file)
@@ -119,8 +119,8 @@ enum hv_fcopy_op {
 
 struct hv_fcopy_hdr {
        __u32 operation;
-       uuid_le service_id0; /* currently unused */
-       uuid_le service_id1; /* currently unused */
+       __u8 service_id0[16]; /* currently unused */
+       __u8 service_id1[16]; /* currently unused */
 } __attribute__((packed));
 
 #define OVER_WRITE     0x1
index be714cd..797ba2c 100644 (file)
@@ -178,6 +178,7 @@ enum {
 enum {
        IF_LINK_MODE_DEFAULT,
        IF_LINK_MODE_DORMANT,   /* limit upward transition to dormant */
+       IF_LINK_MODE_TESTING,   /* limit upward transition to testing */
 };
 
 /*
index b122cfa..6838780 100644 (file)
@@ -60,7 +60,7 @@ struct arc_rfc1201 {
        __u8  proto;            /* protocol ID field - varies           */
        __u8  split_flag;       /* for use with split packets           */
        __be16   sequence;      /* sequence number                      */
-       __u8  payload[];        /* space remaining in packet (504 bytes)*/
+       __u8  payload[0];       /* space remaining in packet (504 bytes)*/
 };
 #define RFC1201_HDR_SIZE 4
 
@@ -69,7 +69,7 @@ struct arc_rfc1201 {
  */
 struct arc_rfc1051 {
        __u8 proto;             /* ARC_P_RFC1051_ARP/RFC1051_IP */
-       __u8 payload[]; /* 507 bytes                    */
+       __u8 payload[0];        /* 507 bytes                    */
 };
 #define RFC1051_HDR_SIZE 1
 
@@ -80,7 +80,7 @@ struct arc_rfc1051 {
 struct arc_eth_encap {
        __u8 proto;             /* Always ARC_P_ETHER                   */
        struct ethhdr eth;      /* standard ethernet header (yuck!)     */
-       __u8 payload[]; /* 493 bytes                            */
+       __u8 payload[0];        /* 493 bytes                            */
 };
 #define ETH_ENCAP_HDR_SIZE 14
 
index bfe621e..bd8c954 100644 (file)
@@ -120,6 +120,7 @@ enum {
        IFLA_BRIDGE_MODE,
        IFLA_BRIDGE_VLAN_INFO,
        IFLA_BRIDGE_VLAN_TUNNEL_INFO,
+       IFLA_BRIDGE_MRP,
        __IFLA_BRIDGE_MAX,
 };
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
@@ -157,6 +158,47 @@ struct bridge_vlan_xstats {
        __u32 pad2;
 };
 
+enum {
+       IFLA_BRIDGE_MRP_UNSPEC,
+       IFLA_BRIDGE_MRP_INSTANCE,
+       IFLA_BRIDGE_MRP_PORT_STATE,
+       IFLA_BRIDGE_MRP_PORT_ROLE,
+       IFLA_BRIDGE_MRP_RING_STATE,
+       IFLA_BRIDGE_MRP_RING_ROLE,
+       IFLA_BRIDGE_MRP_START_TEST,
+       __IFLA_BRIDGE_MRP_MAX,
+};
+
+struct br_mrp_instance {
+       __u32 ring_id;
+       __u32 p_ifindex;
+       __u32 s_ifindex;
+};
+
+struct br_mrp_port_role {
+       __u32 ring_id;
+       __u32 role;
+};
+
+struct br_mrp_ring_state {
+       __u32 ring_id;
+       __u32 ring_state;
+};
+
+struct br_mrp_ring_role {
+       __u32 ring_id;
+       __u32 ring_role;
+};
+
+struct br_mrp_start_test {
+       __u32 ring_id;
+       __u32 interval;
+       __u32 max_miss;
+       __u32 period;
+};
+
+#define IFLA_BRIDGE_MRP_MAX (__IFLA_BRIDGE_MRP_MAX - 1)
+
 struct bridge_stp_xstats {
        __u64 transition_blk;
        __u64 transition_fwd;
index f6ceb2e..d6de2b1 100644 (file)
@@ -92,6 +92,7 @@
 #define ETH_P_PREAUTH  0x88C7          /* 802.11 Preauthentication */
 #define ETH_P_TIPC     0x88CA          /* TIPC                         */
 #define ETH_P_LLDP     0x88CC          /* Link Layer Discovery Protocol */
+#define ETH_P_MRP      0x88E3          /* Media Redundancy Protocol    */
 #define ETH_P_MACSEC   0x88E5          /* 802.1ae MACsec */
 #define ETH_P_8021AH   0x88E7          /* 802.1ah Backbone Service Tag */
 #define ETH_P_MVRP     0x88F5          /* 802.1Q MVRP                  */
index 127c704..a009365 100644 (file)
@@ -343,6 +343,7 @@ enum {
        IFLA_BRPORT_NEIGH_SUPPRESS,
        IFLA_BRPORT_ISOLATED,
        IFLA_BRPORT_BACKUP_PORT,
+       IFLA_BRPORT_MRP_RING_OPEN,
        __IFLA_BRPORT_MAX
 };
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
index 5d96244..3a5938e 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <linux/types.h>
 
-/* Documentation/networking/x25-iface.txt */
+/* Documentation/networking/x25-iface.rst */
 #define X25_IFACE_DATA         0x00
 #define X25_IFACE_CONNECT      0x01
 #define X25_IFACE_DISCONNECT   0x02
index 57cc429..e6f183e 100644 (file)
@@ -96,6 +96,7 @@ enum {
        INET_DIAG_BC_MARK_COND,
        INET_DIAG_BC_S_EQ,
        INET_DIAG_BC_D_EQ,
+       INET_DIAG_BC_CGROUP_COND,   /* u64 cgroup v2 ID */
 };
 
 struct inet_diag_hostcond {
@@ -157,6 +158,7 @@ enum {
        INET_DIAG_MD5SIG,
        INET_DIAG_ULP_INFO,
        INET_DIAG_SK_BPF_STORAGES,
+       INET_DIAG_CGROUP_ID,
        __INET_DIAG_MAX,
 };
 
index 90f9b4e..39f7c44 100644 (file)
 /* 1000BASE-T Control register */
 #define ADVERTISE_1000FULL     0x0200  /* Advertise 1000BASE-T full duplex */
 #define ADVERTISE_1000HALF     0x0100  /* Advertise 1000BASE-T half duplex */
+#define CTL1000_PREFER_MASTER  0x0400  /* prefer to operate as master */
 #define CTL1000_AS_MASTER      0x0800
 #define CTL1000_ENABLE_MASTER  0x1000
 
 /* 1000BASE-T Status register */
 #define LPA_1000MSFAIL         0x8000  /* Master/Slave resolution failure */
+#define LPA_1000MSRES          0x4000  /* Master/Slave resolution status */
 #define LPA_1000LOCALRXOK      0x2000  /* Link partner local receiver status */
 #define LPA_1000REMRXOK                0x1000  /* Link partner remote receiver status */
 #define LPA_1000FULL           0x0800  /* Link partner 1000BASE-T full duplex */
index 98e29e7..00c0812 100644 (file)
@@ -57,7 +57,7 @@ struct mmc_ioc_cmd {
  */
 struct mmc_ioc_multi_cmd {
        __u64 num_of_cmds;
-       struct mmc_ioc_cmd cmds[];
+       struct mmc_ioc_cmd cmds[0];
 };
 
 #define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd)
diff --git a/include/uapi/linux/mrp_bridge.h b/include/uapi/linux/mrp_bridge.h
new file mode 100644 (file)
index 0000000..2600cdf
--- /dev/null
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+
+#ifndef _UAPI_LINUX_MRP_BRIDGE_H_
+#define _UAPI_LINUX_MRP_BRIDGE_H_
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+
+#define MRP_MAX_FRAME_LENGTH           200
+#define MRP_DEFAULT_PRIO               0x8000
+#define MRP_DOMAIN_UUID_LENGTH         16
+#define MRP_VERSION                    1
+#define MRP_FRAME_PRIO                 7
+
+enum br_mrp_ring_role_type {
+       BR_MRP_RING_ROLE_DISABLED,
+       BR_MRP_RING_ROLE_MRC,
+       BR_MRP_RING_ROLE_MRM,
+};
+
+enum br_mrp_ring_state_type {
+       BR_MRP_RING_STATE_OPEN,
+       BR_MRP_RING_STATE_CLOSED,
+};
+
+enum br_mrp_port_state_type {
+       BR_MRP_PORT_STATE_DISABLED,
+       BR_MRP_PORT_STATE_BLOCKED,
+       BR_MRP_PORT_STATE_FORWARDING,
+       BR_MRP_PORT_STATE_NOT_CONNECTED,
+};
+
+enum br_mrp_port_role_type {
+       BR_MRP_PORT_ROLE_PRIMARY,
+       BR_MRP_PORT_ROLE_SECONDARY,
+       BR_MRP_PORT_ROLE_NONE,
+};
+
+enum br_mrp_tlv_header_type {
+       BR_MRP_TLV_HEADER_END = 0x0,
+       BR_MRP_TLV_HEADER_COMMON = 0x1,
+       BR_MRP_TLV_HEADER_RING_TEST = 0x2,
+       BR_MRP_TLV_HEADER_RING_TOPO = 0x3,
+       BR_MRP_TLV_HEADER_RING_LINK_DOWN = 0x4,
+       BR_MRP_TLV_HEADER_RING_LINK_UP = 0x5,
+};
+
+struct br_mrp_tlv_hdr {
+       __u8 type;
+       __u8 length;
+};
+
+struct br_mrp_end_hdr {
+       struct br_mrp_tlv_hdr hdr;
+};
+
+struct br_mrp_common_hdr {
+       __u16 seq_id;
+       __u8 domain[MRP_DOMAIN_UUID_LENGTH];
+};
+
+struct br_mrp_ring_test_hdr {
+       __u16 prio;
+       __u8 sa[ETH_ALEN];
+       __u16 port_role;
+       __u16 state;
+       __u16 transitions;
+       __u32 timestamp;
+};
+
+struct br_mrp_ring_topo_hdr {
+       __u16 prio;
+       __u8 sa[ETH_ALEN];
+       __u16 interval;
+};
+
+struct br_mrp_ring_link_hdr {
+       __u8 sa[ETH_ALEN];
+       __u16 port_role;
+       __u16 interval;
+       __u16 blocked;
+};
+
+#endif
index 67e31f3..66048cc 100644 (file)
@@ -29,12 +29,12 @@ struct net_dm_config_entry {
 
 struct net_dm_config_msg {
        __u32 entries;
-       struct net_dm_config_entry options[];
+       struct net_dm_config_entry options[0];
 };
 
 struct net_dm_alert_msg {
        __u32 entries;
-       struct net_dm_drop_point points[];
+       struct net_dm_drop_point points[0];
 };
 
 struct net_dm_user_msg {
index b6f0bb1..4b33950 100644 (file)
@@ -114,15 +114,19 @@ enum ip_conntrack_status {
        IPS_OFFLOAD_BIT = 14,
        IPS_OFFLOAD = (1 << IPS_OFFLOAD_BIT),
 
+       /* Conntrack has been offloaded to hardware. */
+       IPS_HW_OFFLOAD_BIT = 15,
+       IPS_HW_OFFLOAD = (1 << IPS_HW_OFFLOAD_BIT),
+
        /* Be careful here, modifying these bits can make things messy,
         * so don't let users modify them directly.
         */
        IPS_UNCHANGEABLE_MASK = (IPS_NAT_DONE_MASK | IPS_NAT_MASK |
                                 IPS_EXPECTED | IPS_CONFIRMED | IPS_DYING |
                                 IPS_SEQ_ADJUST | IPS_TEMPLATE | IPS_UNTRACKED |
-                                IPS_OFFLOAD),
+                                IPS_OFFLOAD | IPS_HW_OFFLOAD),
 
-       __IPS_MAX_BIT = 15,
+       __IPS_MAX_BIT = 16,
 };
 
 /* Connection tracking event types */
index 4a95c0d..a64586e 100644 (file)
@@ -11,6 +11,7 @@
 #define NF_NAT_RANGE_PERSISTENT                        (1 << 3)
 #define NF_NAT_RANGE_PROTO_RANDOM_FULLY                (1 << 4)
 #define NF_NAT_RANGE_PROTO_OFFSET              (1 << 5)
+#define NF_NAT_RANGE_NETMAP                    (1 << 6)
 
 #define NF_NAT_RANGE_PROTO_RANDOM_ALL          \
        (NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY)
@@ -18,7 +19,8 @@
 #define NF_NAT_RANGE_MASK                                      \
        (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED |  \
         NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PERSISTENT |  \
-        NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET)
+        NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET | \
+        NF_NAT_RANGE_NETMAP)
 
 struct nf_nat_ipv4_range {
        unsigned int                    flags;
index 30f2a87..4565456 100644 (file)
@@ -276,6 +276,7 @@ enum nft_rule_compat_attributes {
  * @NFT_SET_TIMEOUT: set uses timeouts
  * @NFT_SET_EVAL: set can be updated from the evaluation path
  * @NFT_SET_OBJECT: set contains stateful objects
+ * @NFT_SET_CONCAT: set contains a concatenation
  */
 enum nft_set_flags {
        NFT_SET_ANONYMOUS               = 0x1,
@@ -285,6 +286,7 @@ enum nft_set_flags {
        NFT_SET_TIMEOUT                 = 0x10,
        NFT_SET_EVAL                    = 0x20,
        NFT_SET_OBJECT                  = 0x40,
+       NFT_SET_CONCAT                  = 0x80,
 };
 
 /**
index 434e650..49ddcdc 100644 (file)
@@ -48,6 +48,7 @@ struct idletimer_tg_info_v1 {
 
        char label[MAX_IDLETIMER_LABEL_SIZE];
 
+       __u8 send_nl_msg;   /* unused: for compatibility with Android */
        __u8 timer_type;
 
        /* for kernel module internal use only */
index 73b26a2..9acf757 100644 (file)
@@ -40,7 +40,7 @@ struct ebt_mac_wormhash_tuple {
 struct ebt_mac_wormhash {
        int table[257];
        int poolsize;
-       struct ebt_mac_wormhash_tuple pool[];
+       struct ebt_mac_wormhash_tuple pool[0];
 };
 
 #define ebt_mac_wormhash_size(x) ((x) ? sizeof(struct ebt_mac_wormhash) \
index 0a4d733..eac8a6a 100644 (file)
@@ -249,4 +249,107 @@ struct nla_bitfield32 {
        __u32 selector;
 };
 
+/*
+ * policy descriptions - it's specific to each family how this is used
+ * Normally, it should be retrieved via a dump inside another attribute
+ * specifying where it applies.
+ */
+
+/**
+ * enum netlink_attribute_type - type of an attribute
+ * @NL_ATTR_TYPE_INVALID: unused
+ * @NL_ATTR_TYPE_FLAG: flag attribute (present/not present)
+ * @NL_ATTR_TYPE_U8: 8-bit unsigned attribute
+ * @NL_ATTR_TYPE_U16: 16-bit unsigned attribute
+ * @NL_ATTR_TYPE_U32: 32-bit unsigned attribute
+ * @NL_ATTR_TYPE_U64: 64-bit unsigned attribute
+ * @NL_ATTR_TYPE_S8: 8-bit signed attribute
+ * @NL_ATTR_TYPE_S16: 16-bit signed attribute
+ * @NL_ATTR_TYPE_S32: 32-bit signed attribute
+ * @NL_ATTR_TYPE_S64: 64-bit signed attribute
+ * @NL_ATTR_TYPE_BINARY: binary data, min/max length may be specified
+ * @NL_ATTR_TYPE_STRING: string, min/max length may be specified
+ * @NL_ATTR_TYPE_NUL_STRING: NUL-terminated string,
+ *     min/max length may be specified
+ * @NL_ATTR_TYPE_NESTED: nested, i.e. the content of this attribute
+ *     consists of sub-attributes. The nested policy and maxtype
+ *     inside may be specified.
+ * @NL_ATTR_TYPE_NESTED_ARRAY: nested array, i.e. the content of this
+ *     attribute contains sub-attributes whose type is irrelevant
+ *     (just used to separate the array entries) and each such array
+ *     entry has attributes again, the policy for those inner ones
+ *     and the corresponding maxtype may be specified.
+ * @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute
+ */
+enum netlink_attribute_type {
+       NL_ATTR_TYPE_INVALID,
+
+       NL_ATTR_TYPE_FLAG,
+
+       NL_ATTR_TYPE_U8,
+       NL_ATTR_TYPE_U16,
+       NL_ATTR_TYPE_U32,
+       NL_ATTR_TYPE_U64,
+
+       NL_ATTR_TYPE_S8,
+       NL_ATTR_TYPE_S16,
+       NL_ATTR_TYPE_S32,
+       NL_ATTR_TYPE_S64,
+
+       NL_ATTR_TYPE_BINARY,
+       NL_ATTR_TYPE_STRING,
+       NL_ATTR_TYPE_NUL_STRING,
+
+       NL_ATTR_TYPE_NESTED,
+       NL_ATTR_TYPE_NESTED_ARRAY,
+
+       NL_ATTR_TYPE_BITFIELD32,
+};
+
+/**
+ * enum netlink_policy_type_attr - policy type attributes
+ * @NL_POLICY_TYPE_ATTR_UNSPEC: unused
+ * @NL_POLICY_TYPE_ATTR_TYPE: type of the attribute,
+ *     &enum netlink_attribute_type (U32)
+ * @NL_POLICY_TYPE_ATTR_MIN_VALUE_S: minimum value for signed
+ *     integers (S64)
+ * @NL_POLICY_TYPE_ATTR_MAX_VALUE_S: maximum value for signed
+ *     integers (S64)
+ * @NL_POLICY_TYPE_ATTR_MIN_VALUE_U: minimum value for unsigned
+ *     integers (U64)
+ * @NL_POLICY_TYPE_ATTR_MAX_VALUE_U: maximum value for unsigned
+ *     integers (U64)
+ * @NL_POLICY_TYPE_ATTR_MIN_LENGTH: minimum length for binary
+ *     attributes, no minimum if not given (U32)
+ * @NL_POLICY_TYPE_ATTR_MAX_LENGTH: maximum length for binary
+ *     attributes, no maximum if not given (U32)
+ * @NL_POLICY_TYPE_ATTR_POLICY_IDX: sub policy for nested and
+ *     nested array types (U32)
+ * @NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE: maximum sub policy
+ *     attribute for nested and nested array types, this can
+ *     in theory be < the size of the policy pointed to by
+ *     the index, if limited inside the nesting (U32)
+ * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the
+ *     bitfield32 type (U32)
+ * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment
+ */
+enum netlink_policy_type_attr {
+       NL_POLICY_TYPE_ATTR_UNSPEC,
+       NL_POLICY_TYPE_ATTR_TYPE,
+       NL_POLICY_TYPE_ATTR_MIN_VALUE_S,
+       NL_POLICY_TYPE_ATTR_MAX_VALUE_S,
+       NL_POLICY_TYPE_ATTR_MIN_VALUE_U,
+       NL_POLICY_TYPE_ATTR_MAX_VALUE_U,
+       NL_POLICY_TYPE_ATTR_MIN_LENGTH,
+       NL_POLICY_TYPE_ATTR_MAX_LENGTH,
+       NL_POLICY_TYPE_ATTR_POLICY_IDX,
+       NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
+       NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
+       NL_POLICY_TYPE_ATTR_PAD,
+
+       /* keep last */
+       __NL_POLICY_TYPE_ATTR_MAX,
+       NL_POLICY_TYPE_ATTR_MAX = __NL_POLICY_TYPE_ATTR_MAX - 1
+};
+
 #endif /* _UAPI__LINUX_NETLINK_H */
index 9f06d29..fc672b2 100644 (file)
@@ -134,6 +134,7 @@ enum tca_id {
        TCA_ID_CTINFO,
        TCA_ID_MPLS,
        TCA_ID_CT,
+       TCA_ID_GATE,
        /* other actions go here */
        __TCA_ID_MAX = 255
 };
index 0c02737..a95f3ae 100644 (file)
@@ -913,6 +913,10 @@ enum {
 
        TCA_FQ_TIMER_SLACK,     /* timer slack */
 
+       TCA_FQ_HORIZON,         /* time horizon in us */
+
+       TCA_FQ_HORIZON_DROP,    /* drop packets beyond horizon, or cap their EDT */
+
        __TCA_FQ_MAX
 };
 
@@ -932,6 +936,8 @@ struct tc_fq_qd_stats {
        __u32   throttled_flows;
        __u32   unthrottle_latency_ns;
        __u64   ce_mark;                /* packets above ce_threshold */
+       __u64   horizon_drops;
+       __u64   horizon_caps;
 };
 
 /* Heavy-Hitter Filter */
index 9dc9d00..ff070aa 100644 (file)
@@ -89,7 +89,9 @@ struct ptp_clock_caps {
        int n_pins;    /* Number of input/output pins. */
        /* Whether the clock supports precise system-device cross timestamps */
        int cross_timestamping;
-       int rsv[13];   /* Reserved for future use. */
+       /* Whether the clock supports adjust phase */
+       int adjust_phase;
+       int rsv[12];   /* Reserved for future use. */
 };
 
 struct ptp_extts_request {
diff --git a/include/uapi/linux/tc_act/tc_gate.h b/include/uapi/linux/tc_act/tc_gate.h
new file mode 100644 (file)
index 0000000..f214b3a
--- /dev/null
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/* Copyright 2020 NXP */
+
+#ifndef __LINUX_TC_GATE_H
+#define __LINUX_TC_GATE_H
+
+#include <linux/pkt_cls.h>
+
+struct tc_gate {
+       tc_gen;
+};
+
+enum {
+       TCA_GATE_ENTRY_UNSPEC,
+       TCA_GATE_ENTRY_INDEX,
+       TCA_GATE_ENTRY_GATE,
+       TCA_GATE_ENTRY_INTERVAL,
+       TCA_GATE_ENTRY_IPV,
+       TCA_GATE_ENTRY_MAX_OCTETS,
+       __TCA_GATE_ENTRY_MAX,
+};
+#define TCA_GATE_ENTRY_MAX (__TCA_GATE_ENTRY_MAX - 1)
+
+enum {
+       TCA_GATE_ONE_ENTRY_UNSPEC,
+       TCA_GATE_ONE_ENTRY,
+       __TCA_GATE_ONE_ENTRY_MAX,
+};
+#define TCA_GATE_ONE_ENTRY_MAX (__TCA_GATE_ONE_ENTRY_MAX - 1)
+
+enum {
+       TCA_GATE_UNSPEC,
+       TCA_GATE_TM,
+       TCA_GATE_PARMS,
+       TCA_GATE_PAD,
+       TCA_GATE_PRIORITY,
+       TCA_GATE_ENTRY_LIST,
+       TCA_GATE_BASE_TIME,
+       TCA_GATE_CYCLE_TIME,
+       TCA_GATE_CYCLE_TIME_EXT,
+       TCA_GATE_FLAGS,
+       TCA_GATE_CLOCKID,
+       __TCA_GATE_MAX,
+};
+#define TCA_GATE_MAX (__TCA_GATE_MAX - 1)
+
+#endif
index 1997439..dc3e656 100644 (file)
@@ -48,8 +48,15 @@ struct virtio_balloon_config {
        __u32 num_pages;
        /* Number of pages we've actually got in balloon. */
        __u32 actual;
-       /* Free page report command id, readonly by guest */
-       __u32 free_page_report_cmd_id;
+       /*
+        * Free page hint command id, readonly by guest.
+        * Was previously named free_page_report_cmd_id so we
+        * need to carry that name for legacy support.
+        */
+       union {
+               __u32 free_page_hint_cmd_id;
+               __u32 free_page_report_cmd_id;  /* deprecated */
+       };
        /* Stores PAGE_POISON if page poisoning is in use */
        __u32 poison_val;
 };
index 7f59308..3ae65e9 100644 (file)
@@ -209,7 +209,7 @@ struct fc_bsg_host_vendor {
        __u64 vendor_id;
 
        /* start of vendor command area */
-       __u32 vendor_cmd[];
+       __u32 vendor_cmd[0];
 };
 
 /* Response:
index 5cbc9fc..7955c56 100644 (file)
@@ -73,8 +73,8 @@ struct vdso_timestamp {
  *
  * @offset is used by the special time namespace VVAR pages which are
  * installed instead of the real VVAR page. These namespace pages must set
- * @seq to 1 and @clock_mode to VLOCK_TIMENS to force the code into the
- * time namespace slow path. The namespace aware functions retrieve the
+ * @seq to 1 and @clock_mode to VDSO_CLOCKMODE_TIMENS to force the code into
+ * the time namespace slow path. The namespace aware functions retrieve the
  * real system wide VVAR page, read host time and add the per clock offset.
  * For clocks which are not affected by time namespace adjustment the
  * offset must be zero.
index affd665..d1b8644 100644 (file)
@@ -24,7 +24,7 @@ static void *get_ipc(struct ctl_table *table)
 
 #ifdef CONFIG_PROC_SYSCTL
 static int proc_ipc_dointvec(struct ctl_table *table, int write,
-       void __user *buffer, size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table ipc_table;
 
@@ -35,7 +35,7 @@ static int proc_ipc_dointvec(struct ctl_table *table, int write,
 }
 
 static int proc_ipc_dointvec_minmax(struct ctl_table *table, int write,
-       void __user *buffer, size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table ipc_table;
 
@@ -46,7 +46,7 @@ static int proc_ipc_dointvec_minmax(struct ctl_table *table, int write,
 }
 
 static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
-       void __user *buffer, size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ipc_namespace *ns = current->nsproxy->ipc_ns;
        int err = proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos);
@@ -59,7 +59,7 @@ static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
 }
 
 static int proc_ipc_doulongvec_minmax(struct ctl_table *table, int write,
-       void __user *buffer, size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table ipc_table;
        memcpy(&ipc_table, table, sizeof(ipc_table));
@@ -70,7 +70,7 @@ static int proc_ipc_doulongvec_minmax(struct ctl_table *table, int write,
 }
 
 static int proc_ipc_auto_msgmni(struct ctl_table *table, int write,
-       void __user *buffer, size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table ipc_table;
        int dummy = 0;
index 7c00f28..72a92a0 100644 (file)
@@ -19,7 +19,7 @@ static void *get_mq(struct ctl_table *table)
 }
 
 static int proc_mq_dointvec(struct ctl_table *table, int write,
-                           void __user *buffer, size_t *lenp, loff_t *ppos)
+                           void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table mq_table;
        memcpy(&mq_table, table, sizeof(mq_table));
@@ -29,7 +29,7 @@ static int proc_mq_dointvec(struct ctl_table *table, int write,
 }
 
 static int proc_mq_dointvec_minmax(struct ctl_table *table, int write,
-       void __user *buffer, size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table mq_table;
        memcpy(&mq_table, table, sizeof(mq_table));
index b69c8b4..87f31bf 100644 (file)
@@ -1326,6 +1326,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
        case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2:
                if (!audit_enabled && msg_type != AUDIT_USER_AVC)
                        return 0;
+               /* exit early if there isn't at least one character to print */
+               if (data_len < 2)
+                       return -EINVAL;
 
                err = audit_filter(msg_type, AUDIT_FILTER_USER);
                if (err == 1) { /* match or error */
index f025046..6b12f06 100644 (file)
@@ -30,7 +30,7 @@ struct bpf_lru_node {
 struct bpf_lru_list {
        struct list_head lists[NR_BPF_LRU_LIST_T];
        unsigned int counts[NR_BPF_LRU_LIST_COUNT];
-       /* The next inacitve list rotation starts from here */
+       /* The next inactive list rotation starts from here */
        struct list_head *next_inactive_rotation;
 
        raw_spinlock_t lock ____cacheline_aligned_in_smp;
index d65c691..a2cfba8 100644 (file)
@@ -3482,6 +3482,7 @@ extern char __weak __stop_BTF[];
 extern struct btf *btf_vmlinux;
 
 #define BPF_MAP_TYPE(_id, _ops)
+#define BPF_LINK_TYPE(_id, _name)
 static union {
        struct bpf_ctx_convert {
 #define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
@@ -3508,6 +3509,7 @@ static u8 bpf_ctx_convert_map[] = {
        0, /* avoid empty array */
 };
 #undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
 
 static const struct btf_member *
 btf_get_prog_ctx_type(struct bpf_verifier_log *log, struct btf *btf,
index cb305e7..5c0e964 100644 (file)
@@ -557,8 +557,9 @@ found:
  *
  * Must be called with cgroup_mutex held.
  */
-int __cgroup_bpf_replace(struct cgroup *cgrp, struct bpf_cgroup_link *link,
-                        struct bpf_prog *new_prog)
+static int __cgroup_bpf_replace(struct cgroup *cgrp,
+                               struct bpf_cgroup_link *link,
+                               struct bpf_prog *new_prog)
 {
        struct list_head *progs = &cgrp->bpf.progs[link->type];
        struct bpf_prog *old_prog;
@@ -583,6 +584,30 @@ int __cgroup_bpf_replace(struct cgroup *cgrp, struct bpf_cgroup_link *link,
        return 0;
 }
 
+static int cgroup_bpf_replace(struct bpf_link *link, struct bpf_prog *new_prog,
+                             struct bpf_prog *old_prog)
+{
+       struct bpf_cgroup_link *cg_link;
+       int ret;
+
+       cg_link = container_of(link, struct bpf_cgroup_link, link);
+
+       mutex_lock(&cgroup_mutex);
+       /* link might have been auto-released by dying cgroup, so fail */
+       if (!cg_link->cgroup) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+       if (old_prog && link->prog != old_prog) {
+               ret = -EPERM;
+               goto out_unlock;
+       }
+       ret = __cgroup_bpf_replace(cg_link->cgroup, cg_link, new_prog);
+out_unlock:
+       mutex_unlock(&cgroup_mutex);
+       return ret;
+}
+
 static struct bpf_prog_list *find_detach_entry(struct list_head *progs,
                                               struct bpf_prog *prog,
                                               struct bpf_cgroup_link *link,
@@ -808,17 +833,56 @@ static void bpf_cgroup_link_dealloc(struct bpf_link *link)
        kfree(cg_link);
 }
 
-const struct bpf_link_ops bpf_cgroup_link_lops = {
+static void bpf_cgroup_link_show_fdinfo(const struct bpf_link *link,
+                                       struct seq_file *seq)
+{
+       struct bpf_cgroup_link *cg_link =
+               container_of(link, struct bpf_cgroup_link, link);
+       u64 cg_id = 0;
+
+       mutex_lock(&cgroup_mutex);
+       if (cg_link->cgroup)
+               cg_id = cgroup_id(cg_link->cgroup);
+       mutex_unlock(&cgroup_mutex);
+
+       seq_printf(seq,
+                  "cgroup_id:\t%llu\n"
+                  "attach_type:\t%d\n",
+                  cg_id,
+                  cg_link->type);
+}
+
+static int bpf_cgroup_link_fill_link_info(const struct bpf_link *link,
+                                         struct bpf_link_info *info)
+{
+       struct bpf_cgroup_link *cg_link =
+               container_of(link, struct bpf_cgroup_link, link);
+       u64 cg_id = 0;
+
+       mutex_lock(&cgroup_mutex);
+       if (cg_link->cgroup)
+               cg_id = cgroup_id(cg_link->cgroup);
+       mutex_unlock(&cgroup_mutex);
+
+       info->cgroup.cgroup_id = cg_id;
+       info->cgroup.attach_type = cg_link->type;
+       return 0;
+}
+
+static const struct bpf_link_ops bpf_cgroup_link_lops = {
        .release = bpf_cgroup_link_release,
        .dealloc = bpf_cgroup_link_dealloc,
+       .update_prog = cgroup_bpf_replace,
+       .show_fdinfo = bpf_cgroup_link_show_fdinfo,
+       .fill_link_info = bpf_cgroup_link_fill_link_info,
 };
 
 int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
 {
+       struct bpf_link_primer link_primer;
        struct bpf_cgroup_link *link;
-       struct file *link_file;
        struct cgroup *cgrp;
-       int err, link_fd;
+       int err;
 
        if (attr->link_create.flags)
                return -EINVAL;
@@ -832,26 +896,25 @@ int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
                err = -ENOMEM;
                goto out_put_cgroup;
        }
-       bpf_link_init(&link->link, &bpf_cgroup_link_lops, prog);
+       bpf_link_init(&link->link, BPF_LINK_TYPE_CGROUP, &bpf_cgroup_link_lops,
+                     prog);
        link->cgroup = cgrp;
        link->type = attr->link_create.attach_type;
 
-       link_file = bpf_link_new_file(&link->link, &link_fd);
-       if (IS_ERR(link_file)) {
+       err  = bpf_link_prime(&link->link, &link_primer);
+       if (err) {
                kfree(link);
-               err = PTR_ERR(link_file);
                goto out_put_cgroup;
        }
 
        err = cgroup_bpf_attach(cgrp, NULL, NULL, link, link->type,
                                BPF_F_ALLOW_MULTI);
        if (err) {
-               bpf_link_cleanup(&link->link, link_file, link_fd);
+               bpf_link_cleanup(&link_primer);
                goto out_put_cgroup;
        }
 
-       fd_install(link_fd, link_file);
-       return link_fd;
+       return bpf_link_settle(&link_primer);
 
 out_put_cgroup:
        cgroup_put(cgrp);
@@ -1054,36 +1117,21 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor,
 
        return !allow;
 }
-EXPORT_SYMBOL(__cgroup_bpf_check_dev_permission);
 
 static const struct bpf_func_proto *
 cgroup_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
        switch (func_id) {
-       case BPF_FUNC_map_lookup_elem:
-               return &bpf_map_lookup_elem_proto;
-       case BPF_FUNC_map_update_elem:
-               return &bpf_map_update_elem_proto;
-       case BPF_FUNC_map_delete_elem:
-               return &bpf_map_delete_elem_proto;
-       case BPF_FUNC_map_push_elem:
-               return &bpf_map_push_elem_proto;
-       case BPF_FUNC_map_pop_elem:
-               return &bpf_map_pop_elem_proto;
-       case BPF_FUNC_map_peek_elem:
-               return &bpf_map_peek_elem_proto;
        case BPF_FUNC_get_current_uid_gid:
                return &bpf_get_current_uid_gid_proto;
        case BPF_FUNC_get_local_storage:
                return &bpf_get_local_storage_proto;
        case BPF_FUNC_get_current_cgroup_id:
                return &bpf_get_current_cgroup_id_proto;
-       case BPF_FUNC_trace_printk:
-               if (capable(CAP_SYS_ADMIN))
-                       return bpf_get_trace_printk_proto();
-               /* fall through */
+       case BPF_FUNC_perf_event_output:
+               return &bpf_event_output_data_proto;
        default:
-               return NULL;
+               return bpf_base_func_proto(func_id);
        }
 }
 
@@ -1137,16 +1185,13 @@ const struct bpf_verifier_ops cg_dev_verifier_ops = {
  * @head: sysctl table header
  * @table: sysctl table
  * @write: sysctl is being read (= 0) or written (= 1)
- * @buf: pointer to buffer passed by user space
+ * @buf: pointer to buffer (in and out)
  * @pcount: value-result argument: value is size of buffer pointed to by @buf,
  *     result is size of @new_buf if program set new value, initial value
  *     otherwise
  * @ppos: value-result argument: value is position at which read from or write
  *     to sysctl is happening, result is new position if program overrode it,
  *     initial value otherwise
- * @new_buf: pointer to pointer to new buffer that will be allocated if program
- *     overrides new value provided by user space on sysctl write
- *     NOTE: it's caller responsibility to free *new_buf if it was set
  * @type: type of program to be executed
  *
  * Program is run when sysctl is being accessed, either read or written, and
@@ -1157,8 +1202,7 @@ const struct bpf_verifier_ops cg_dev_verifier_ops = {
  */
 int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
                                   struct ctl_table *table, int write,
-                                  void __user *buf, size_t *pcount,
-                                  loff_t *ppos, void **new_buf,
+                                  void **buf, size_t *pcount, loff_t *ppos,
                                   enum bpf_attach_type type)
 {
        struct bpf_sysctl_kern ctx = {
@@ -1173,36 +1217,28 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
                .new_updated = 0,
        };
        struct cgroup *cgrp;
+       loff_t pos = 0;
        int ret;
 
        ctx.cur_val = kmalloc_track_caller(ctx.cur_len, GFP_KERNEL);
-       if (ctx.cur_val) {
-               mm_segment_t old_fs;
-               loff_t pos = 0;
-
-               old_fs = get_fs();
-               set_fs(KERNEL_DS);
-               if (table->proc_handler(table, 0, (void __user *)ctx.cur_val,
-                                       &ctx.cur_len, &pos)) {
-                       /* Let BPF program decide how to proceed. */
-                       ctx.cur_len = 0;
-               }
-               set_fs(old_fs);
-       } else {
+       if (!ctx.cur_val ||
+           table->proc_handler(table, 0, ctx.cur_val, &ctx.cur_len, &pos)) {
                /* Let BPF program decide how to proceed. */
                ctx.cur_len = 0;
        }
 
-       if (write && buf && *pcount) {
+       if (write && *buf && *pcount) {
                /* BPF program should be able to override new value with a
                 * buffer bigger than provided by user.
                 */
                ctx.new_val = kmalloc_track_caller(PAGE_SIZE, GFP_KERNEL);
                ctx.new_len = min_t(size_t, PAGE_SIZE, *pcount);
-               if (!ctx.new_val ||
-                   copy_from_user(ctx.new_val, buf, ctx.new_len))
+               if (ctx.new_val) {
+                       memcpy(ctx.new_val, *buf, ctx.new_len);
+               } else {
                        /* Let BPF program decide how to proceed. */
                        ctx.new_len = 0;
+               }
        }
 
        rcu_read_lock();
@@ -1213,7 +1249,8 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
        kfree(ctx.cur_val);
 
        if (ret == 1 && ctx.new_updated) {
-               *new_buf = ctx.new_val;
+               kfree(*buf);
+               *buf = ctx.new_val;
                *pcount = ctx.new_len;
        } else {
                kfree(ctx.new_val);
@@ -1221,7 +1258,6 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
 
        return ret == 1 ? 0 : -EPERM;
 }
-EXPORT_SYMBOL(__cgroup_bpf_run_filter_sysctl);
 
 #ifdef CONFIG_NET
 static bool __cgroup_bpf_prog_array_is_empty(struct cgroup *cgrp,
@@ -1326,7 +1362,6 @@ out:
                sockopt_free_buf(&ctx);
        return ret;
 }
-EXPORT_SYMBOL(__cgroup_bpf_run_filter_setsockopt);
 
 int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
                                       int optname, char __user *optval,
@@ -1413,7 +1448,6 @@ out:
        sockopt_free_buf(&ctx);
        return ret;
 }
-EXPORT_SYMBOL(__cgroup_bpf_run_filter_getsockopt);
 #endif
 
 static ssize_t sysctl_cpy_dir(const struct ctl_dir *dir, char **bufp,
index 916f513..6aa11de 100644 (file)
@@ -2136,6 +2136,11 @@ BPF_CALL_0(bpf_user_rnd_u32)
        return res;
 }
 
+BPF_CALL_0(bpf_get_raw_cpu_id)
+{
+       return raw_smp_processor_id();
+}
+
 /* Weak definitions of helper functions in case we don't have bpf syscall. */
 const struct bpf_func_proto bpf_map_lookup_elem_proto __weak;
 const struct bpf_func_proto bpf_map_update_elem_proto __weak;
@@ -2151,6 +2156,7 @@ const struct bpf_func_proto bpf_get_prandom_u32_proto __weak;
 const struct bpf_func_proto bpf_get_smp_processor_id_proto __weak;
 const struct bpf_func_proto bpf_get_numa_node_id_proto __weak;
 const struct bpf_func_proto bpf_ktime_get_ns_proto __weak;
+const struct bpf_func_proto bpf_ktime_get_boot_ns_proto __weak;
 
 const struct bpf_func_proto bpf_get_current_pid_tgid_proto __weak;
 const struct bpf_func_proto bpf_get_current_uid_gid_proto __weak;
index 70f71b1..3fe0b00 100644 (file)
@@ -469,7 +469,7 @@ static int cpu_map_update_elem(struct bpf_map *map, void *key, void *value,
                return -EOVERFLOW;
 
        /* Make sure CPU is a valid possible cpu */
-       if (!cpu_possible(key_cpu))
+       if (key_cpu >= nr_cpumask_bits || !cpu_possible(key_cpu))
                return -ENODEV;
 
        if (qsize == 0) {
index 58bdca5..a51d9fb 100644 (file)
@@ -52,7 +52,6 @@
 #define DEV_CREATE_FLAG_MASK \
        (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
 
-#define DEV_MAP_BULK_SIZE 16
 struct xdp_dev_bulk_queue {
        struct xdp_frame *q[DEV_MAP_BULK_SIZE];
        struct list_head flush_node;
index bafc53d..5c0290e 100644 (file)
@@ -151,7 +151,19 @@ BPF_CALL_0(bpf_ktime_get_ns)
 
 const struct bpf_func_proto bpf_ktime_get_ns_proto = {
        .func           = bpf_ktime_get_ns,
-       .gpl_only       = true,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+};
+
+BPF_CALL_0(bpf_ktime_get_boot_ns)
+{
+       /* NMI safe access to clock boottime */
+       return ktime_get_boot_fast_ns();
+}
+
+const struct bpf_func_proto bpf_ktime_get_boot_ns_proto = {
+       .func           = bpf_ktime_get_boot_ns,
+       .gpl_only       = false,
        .ret_type       = RET_INTEGER,
 };
 
@@ -562,3 +574,78 @@ const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto = {
        .arg3_type      = ARG_PTR_TO_UNINIT_MEM,
        .arg4_type      = ARG_CONST_SIZE,
 };
+
+static const struct bpf_func_proto bpf_get_raw_smp_processor_id_proto = {
+       .func           = bpf_get_raw_cpu_id,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+};
+
+BPF_CALL_5(bpf_event_output_data, void *, ctx, struct bpf_map *, map,
+          u64, flags, void *, data, u64, size)
+{
+       if (unlikely(flags & ~(BPF_F_INDEX_MASK)))
+               return -EINVAL;
+
+       return bpf_event_output(map, flags, data, size, NULL, 0, NULL);
+}
+
+const struct bpf_func_proto bpf_event_output_data_proto =  {
+       .func           = bpf_event_output_data,
+       .gpl_only       = true,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_CONST_MAP_PTR,
+       .arg3_type      = ARG_ANYTHING,
+       .arg4_type      = ARG_PTR_TO_MEM,
+       .arg5_type      = ARG_CONST_SIZE_OR_ZERO,
+};
+
+const struct bpf_func_proto *
+bpf_base_func_proto(enum bpf_func_id func_id)
+{
+       switch (func_id) {
+       case BPF_FUNC_map_lookup_elem:
+               return &bpf_map_lookup_elem_proto;
+       case BPF_FUNC_map_update_elem:
+               return &bpf_map_update_elem_proto;
+       case BPF_FUNC_map_delete_elem:
+               return &bpf_map_delete_elem_proto;
+       case BPF_FUNC_map_push_elem:
+               return &bpf_map_push_elem_proto;
+       case BPF_FUNC_map_pop_elem:
+               return &bpf_map_pop_elem_proto;
+       case BPF_FUNC_map_peek_elem:
+               return &bpf_map_peek_elem_proto;
+       case BPF_FUNC_get_prandom_u32:
+               return &bpf_get_prandom_u32_proto;
+       case BPF_FUNC_get_smp_processor_id:
+               return &bpf_get_raw_smp_processor_id_proto;
+       case BPF_FUNC_get_numa_node_id:
+               return &bpf_get_numa_node_id_proto;
+       case BPF_FUNC_tail_call:
+               return &bpf_tail_call_proto;
+       case BPF_FUNC_ktime_get_ns:
+               return &bpf_ktime_get_ns_proto;
+       case BPF_FUNC_ktime_get_boot_ns:
+               return &bpf_ktime_get_boot_ns_proto;
+       default:
+               break;
+       }
+
+       if (!capable(CAP_SYS_ADMIN))
+               return NULL;
+
+       switch (func_id) {
+       case BPF_FUNC_spin_lock:
+               return &bpf_spin_lock_proto;
+       case BPF_FUNC_spin_unlock:
+               return &bpf_spin_unlock_proto;
+       case BPF_FUNC_trace_printk:
+               return bpf_get_trace_printk_proto();
+       case BPF_FUNC_jiffies64:
+               return &bpf_jiffies64_proto;
+       default:
+               return NULL;
+       }
+}
index 64783da..bb1ab7d 100644 (file)
@@ -42,6 +42,8 @@ static DEFINE_IDR(prog_idr);
 static DEFINE_SPINLOCK(prog_idr_lock);
 static DEFINE_IDR(map_idr);
 static DEFINE_SPINLOCK(map_idr_lock);
+static DEFINE_IDR(link_idr);
+static DEFINE_SPINLOCK(link_idr_lock);
 
 int sysctl_unprivileged_bpf_disabled __read_mostly;
 
@@ -49,9 +51,11 @@ static const struct bpf_map_ops * const bpf_map_types[] = {
 #define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type)
 #define BPF_MAP_TYPE(_id, _ops) \
        [_id] = &_ops,
+#define BPF_LINK_TYPE(_id, _name)
 #include <linux/bpf_types.h>
 #undef BPF_PROG_TYPE
 #undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
 };
 
 /*
@@ -586,9 +590,7 @@ static void bpf_map_mmap_open(struct vm_area_struct *vma)
 {
        struct bpf_map *map = vma->vm_file->private_data;
 
-       bpf_map_inc_with_uref(map);
-
-       if (vma->vm_flags & VM_WRITE) {
+       if (vma->vm_flags & VM_MAYWRITE) {
                mutex_lock(&map->freeze_mutex);
                map->writecnt++;
                mutex_unlock(&map->freeze_mutex);
@@ -600,13 +602,11 @@ static void bpf_map_mmap_close(struct vm_area_struct *vma)
 {
        struct bpf_map *map = vma->vm_file->private_data;
 
-       if (vma->vm_flags & VM_WRITE) {
+       if (vma->vm_flags & VM_MAYWRITE) {
                mutex_lock(&map->freeze_mutex);
                map->writecnt--;
                mutex_unlock(&map->freeze_mutex);
        }
-
-       bpf_map_put_with_uref(map);
 }
 
 static const struct vm_operations_struct bpf_map_default_vmops = {
@@ -635,14 +635,16 @@ static int bpf_map_mmap(struct file *filp, struct vm_area_struct *vma)
        /* set default open/close callbacks */
        vma->vm_ops = &bpf_map_default_vmops;
        vma->vm_private_data = map;
+       vma->vm_flags &= ~VM_MAYEXEC;
+       if (!(vma->vm_flags & VM_WRITE))
+               /* disallow re-mapping with PROT_WRITE */
+               vma->vm_flags &= ~VM_MAYWRITE;
 
        err = map->ops->map_mmap(map, vma);
        if (err)
                goto out;
 
-       bpf_map_inc_with_uref(map);
-
-       if (vma->vm_flags & VM_WRITE)
+       if (vma->vm_flags & VM_MAYWRITE)
                map->writecnt++;
 out:
        mutex_unlock(&map->freeze_mutex);
@@ -1548,9 +1550,11 @@ static const struct bpf_prog_ops * const bpf_prog_types[] = {
 #define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
        [_id] = & _name ## _prog_ops,
 #define BPF_MAP_TYPE(_id, _ops)
+#define BPF_LINK_TYPE(_id, _name)
 #include <linux/bpf_types.h>
 #undef BPF_PROG_TYPE
 #undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
 };
 
 static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog)
@@ -2183,25 +2187,39 @@ static int bpf_obj_get(const union bpf_attr *attr)
                                attr->file_flags);
 }
 
-void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops,
-                  struct bpf_prog *prog)
+void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
+                  const struct bpf_link_ops *ops, struct bpf_prog *prog)
 {
        atomic64_set(&link->refcnt, 1);
+       link->type = type;
+       link->id = 0;
        link->ops = ops;
        link->prog = prog;
 }
 
+static void bpf_link_free_id(int id)
+{
+       if (!id)
+               return;
+
+       spin_lock_bh(&link_idr_lock);
+       idr_remove(&link_idr, id);
+       spin_unlock_bh(&link_idr_lock);
+}
+
 /* Clean up bpf_link and corresponding anon_inode file and FD. After
  * anon_inode is created, bpf_link can't be just kfree()'d due to deferred
- * anon_inode's release() call. This helper manages marking bpf_link as
- * defunct, releases anon_inode file and puts reserved FD.
+ * anon_inode's release() call. This helper marksbpf_link as
+ * defunct, releases anon_inode file and puts reserved FD. bpf_prog's refcnt
+ * is not decremented, it's the responsibility of a calling code that failed
+ * to complete bpf_link initialization.
  */
-void bpf_link_cleanup(struct bpf_link *link, struct file *link_file,
-                     int link_fd)
+void bpf_link_cleanup(struct bpf_link_primer *primer)
 {
-       link->prog = NULL;
-       fput(link_file);
-       put_unused_fd(link_fd);
+       primer->link->prog = NULL;
+       bpf_link_free_id(primer->id);
+       fput(primer->file);
+       put_unused_fd(primer->fd);
 }
 
 void bpf_link_inc(struct bpf_link *link)
@@ -2212,6 +2230,7 @@ void bpf_link_inc(struct bpf_link *link)
 /* bpf_link_free is guaranteed to be called from process context */
 static void bpf_link_free(struct bpf_link *link)
 {
+       bpf_link_free_id(link->id);
        if (link->prog) {
                /* detach BPF program, clean up used resources */
                link->ops->release(link);
@@ -2253,39 +2272,39 @@ static int bpf_link_release(struct inode *inode, struct file *filp)
 }
 
 #ifdef CONFIG_PROC_FS
-static const struct bpf_link_ops bpf_raw_tp_lops;
-static const struct bpf_link_ops bpf_tracing_link_lops;
+#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type)
+#define BPF_MAP_TYPE(_id, _ops)
+#define BPF_LINK_TYPE(_id, _name) [_id] = #_name,
+static const char *bpf_link_type_strs[] = {
+       [BPF_LINK_TYPE_UNSPEC] = "<invalid>",
+#include <linux/bpf_types.h>
+};
+#undef BPF_PROG_TYPE
+#undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
 
 static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
 {
        const struct bpf_link *link = filp->private_data;
        const struct bpf_prog *prog = link->prog;
        char prog_tag[sizeof(prog->tag) * 2 + 1] = { };
-       const char *link_type;
-
-       if (link->ops == &bpf_raw_tp_lops)
-               link_type = "raw_tracepoint";
-       else if (link->ops == &bpf_tracing_link_lops)
-               link_type = "tracing";
-#ifdef CONFIG_CGROUP_BPF
-       else if (link->ops == &bpf_cgroup_link_lops)
-               link_type = "cgroup";
-#endif
-       else
-               link_type = "unknown";
 
        bin2hex(prog_tag, prog->tag, sizeof(prog->tag));
        seq_printf(m,
                   "link_type:\t%s\n"
+                  "link_id:\t%u\n"
                   "prog_tag:\t%s\n"
                   "prog_id:\t%u\n",
-                  link_type,
+                  bpf_link_type_strs[link->type],
+                  link->id,
                   prog_tag,
                   prog->aux->id);
+       if (link->ops->show_fdinfo)
+               link->ops->show_fdinfo(link, m);
 }
 #endif
 
-const struct file_operations bpf_link_fops = {
+static const struct file_operations bpf_link_fops = {
 #ifdef CONFIG_PROC_FS
        .show_fdinfo    = bpf_link_show_fdinfo,
 #endif
@@ -2294,36 +2313,77 @@ const struct file_operations bpf_link_fops = {
        .write          = bpf_dummy_write,
 };
 
-int bpf_link_new_fd(struct bpf_link *link)
+static int bpf_link_alloc_id(struct bpf_link *link)
 {
-       return anon_inode_getfd("bpf-link", &bpf_link_fops, link, O_CLOEXEC);
-}
+       int id;
+
+       idr_preload(GFP_KERNEL);
+       spin_lock_bh(&link_idr_lock);
+       id = idr_alloc_cyclic(&link_idr, link, 1, INT_MAX, GFP_ATOMIC);
+       spin_unlock_bh(&link_idr_lock);
+       idr_preload_end();
 
-/* Similar to bpf_link_new_fd, create anon_inode for given bpf_link, but
- * instead of immediately installing fd in fdtable, just reserve it and
- * return. Caller then need to either install it with fd_install(fd, file) or
- * release with put_unused_fd(fd).
- * This is useful for cases when bpf_link attachment/detachment are
- * complicated and expensive operations and should be delayed until all the fd
- * reservation and anon_inode creation succeeds.
+       return id;
+}
+
+/* Prepare bpf_link to be exposed to user-space by allocating anon_inode file,
+ * reserving unused FD and allocating ID from link_idr. This is to be paired
+ * with bpf_link_settle() to install FD and ID and expose bpf_link to
+ * user-space, if bpf_link is successfully attached. If not, bpf_link and
+ * pre-allocated resources are to be freed with bpf_cleanup() call. All the
+ * transient state is passed around in struct bpf_link_primer.
+ * This is preferred way to create and initialize bpf_link, especially when
+ * there are complicated and expensive operations inbetween creating bpf_link
+ * itself and attaching it to BPF hook. By using bpf_link_prime() and
+ * bpf_link_settle() kernel code using bpf_link doesn't have to perform
+ * expensive (and potentially failing) roll back operations in a rare case
+ * that file, FD, or ID can't be allocated.
  */
-struct file *bpf_link_new_file(struct bpf_link *link, int *reserved_fd)
+int bpf_link_prime(struct bpf_link *link, struct bpf_link_primer *primer)
 {
        struct file *file;
-       int fd;
+       int fd, id;
 
        fd = get_unused_fd_flags(O_CLOEXEC);
        if (fd < 0)
-               return ERR_PTR(fd);
+               return fd;
+
+
+       id = bpf_link_alloc_id(link);
+       if (id < 0) {
+               put_unused_fd(fd);
+               return id;
+       }
 
        file = anon_inode_getfile("bpf_link", &bpf_link_fops, link, O_CLOEXEC);
        if (IS_ERR(file)) {
+               bpf_link_free_id(id);
                put_unused_fd(fd);
-               return file;
+               return PTR_ERR(file);
        }
 
-       *reserved_fd = fd;
-       return file;
+       primer->link = link;
+       primer->file = file;
+       primer->fd = fd;
+       primer->id = id;
+       return 0;
+}
+
+int bpf_link_settle(struct bpf_link_primer *primer)
+{
+       /* make bpf_link fetchable by ID */
+       spin_lock_bh(&link_idr_lock);
+       primer->link->id = primer->id;
+       spin_unlock_bh(&link_idr_lock);
+       /* make bpf_link fetchable by FD */
+       fd_install(primer->fd, primer->file);
+       /* pass through installed FD */
+       return primer->fd;
+}
+
+int bpf_link_new_fd(struct bpf_link *link)
+{
+       return anon_inode_getfd("bpf-link", &bpf_link_fops, link, O_CLOEXEC);
 }
 
 struct bpf_link *bpf_link_get_from_fd(u32 ufd)
@@ -2347,6 +2407,7 @@ struct bpf_link *bpf_link_get_from_fd(u32 ufd)
 
 struct bpf_tracing_link {
        struct bpf_link link;
+       enum bpf_attach_type attach_type;
 };
 
 static void bpf_tracing_link_release(struct bpf_link *link)
@@ -2362,16 +2423,40 @@ static void bpf_tracing_link_dealloc(struct bpf_link *link)
        kfree(tr_link);
 }
 
+static void bpf_tracing_link_show_fdinfo(const struct bpf_link *link,
+                                        struct seq_file *seq)
+{
+       struct bpf_tracing_link *tr_link =
+               container_of(link, struct bpf_tracing_link, link);
+
+       seq_printf(seq,
+                  "attach_type:\t%d\n",
+                  tr_link->attach_type);
+}
+
+static int bpf_tracing_link_fill_link_info(const struct bpf_link *link,
+                                          struct bpf_link_info *info)
+{
+       struct bpf_tracing_link *tr_link =
+               container_of(link, struct bpf_tracing_link, link);
+
+       info->tracing.attach_type = tr_link->attach_type;
+
+       return 0;
+}
+
 static const struct bpf_link_ops bpf_tracing_link_lops = {
        .release = bpf_tracing_link_release,
        .dealloc = bpf_tracing_link_dealloc,
+       .show_fdinfo = bpf_tracing_link_show_fdinfo,
+       .fill_link_info = bpf_tracing_link_fill_link_info,
 };
 
 static int bpf_tracing_prog_attach(struct bpf_prog *prog)
 {
+       struct bpf_link_primer link_primer;
        struct bpf_tracing_link *link;
-       struct file *link_file;
-       int link_fd, err;
+       int err;
 
        switch (prog->type) {
        case BPF_PROG_TYPE_TRACING:
@@ -2404,24 +2489,23 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
                err = -ENOMEM;
                goto out_put_prog;
        }
-       bpf_link_init(&link->link, &bpf_tracing_link_lops, prog);
+       bpf_link_init(&link->link, BPF_LINK_TYPE_TRACING,
+                     &bpf_tracing_link_lops, prog);
+       link->attach_type = prog->expected_attach_type;
 
-       link_file = bpf_link_new_file(&link->link, &link_fd);
-       if (IS_ERR(link_file)) {
+       err = bpf_link_prime(&link->link, &link_primer);
+       if (err) {
                kfree(link);
-               err = PTR_ERR(link_file);
                goto out_put_prog;
        }
 
        err = bpf_trampoline_link_prog(prog);
        if (err) {
-               bpf_link_cleanup(&link->link, link_file, link_fd);
+               bpf_link_cleanup(&link_primer);
                goto out_put_prog;
        }
 
-       fd_install(link_fd, link_file);
-       return link_fd;
-
+       return bpf_link_settle(&link_primer);
 out_put_prog:
        bpf_prog_put(prog);
        return err;
@@ -2449,22 +2533,69 @@ static void bpf_raw_tp_link_dealloc(struct bpf_link *link)
        kfree(raw_tp);
 }
 
-static const struct bpf_link_ops bpf_raw_tp_lops = {
+static void bpf_raw_tp_link_show_fdinfo(const struct bpf_link *link,
+                                       struct seq_file *seq)
+{
+       struct bpf_raw_tp_link *raw_tp_link =
+               container_of(link, struct bpf_raw_tp_link, link);
+
+       seq_printf(seq,
+                  "tp_name:\t%s\n",
+                  raw_tp_link->btp->tp->name);
+}
+
+static int bpf_raw_tp_link_fill_link_info(const struct bpf_link *link,
+                                         struct bpf_link_info *info)
+{
+       struct bpf_raw_tp_link *raw_tp_link =
+               container_of(link, struct bpf_raw_tp_link, link);
+       char __user *ubuf = u64_to_user_ptr(info->raw_tracepoint.tp_name);
+       const char *tp_name = raw_tp_link->btp->tp->name;
+       u32 ulen = info->raw_tracepoint.tp_name_len;
+       size_t tp_len = strlen(tp_name);
+
+       if (ulen && !ubuf)
+               return -EINVAL;
+
+       info->raw_tracepoint.tp_name_len = tp_len + 1;
+
+       if (!ubuf)
+               return 0;
+
+       if (ulen >= tp_len + 1) {
+               if (copy_to_user(ubuf, tp_name, tp_len + 1))
+                       return -EFAULT;
+       } else {
+               char zero = '\0';
+
+               if (copy_to_user(ubuf, tp_name, ulen - 1))
+                       return -EFAULT;
+               if (put_user(zero, ubuf + ulen - 1))
+                       return -EFAULT;
+               return -ENOSPC;
+       }
+
+       return 0;
+}
+
+static const struct bpf_link_ops bpf_raw_tp_link_lops = {
        .release = bpf_raw_tp_link_release,
        .dealloc = bpf_raw_tp_link_dealloc,
+       .show_fdinfo = bpf_raw_tp_link_show_fdinfo,
+       .fill_link_info = bpf_raw_tp_link_fill_link_info,
 };
 
 #define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd
 
 static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
 {
+       struct bpf_link_primer link_primer;
        struct bpf_raw_tp_link *link;
        struct bpf_raw_event_map *btp;
-       struct file *link_file;
        struct bpf_prog *prog;
        const char *tp_name;
        char buf[128];
-       int link_fd, err;
+       int err;
 
        if (CHECK_ATTR(BPF_RAW_TRACEPOINT_OPEN))
                return -EINVAL;
@@ -2517,24 +2648,23 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
                err = -ENOMEM;
                goto out_put_btp;
        }
-       bpf_link_init(&link->link, &bpf_raw_tp_lops, prog);
+       bpf_link_init(&link->link, BPF_LINK_TYPE_RAW_TRACEPOINT,
+                     &bpf_raw_tp_link_lops, prog);
        link->btp = btp;
 
-       link_file = bpf_link_new_file(&link->link, &link_fd);
-       if (IS_ERR(link_file)) {
+       err = bpf_link_prime(&link->link, &link_primer);
+       if (err) {
                kfree(link);
-               err = PTR_ERR(link_file);
                goto out_put_btp;
        }
 
        err = bpf_probe_register(link->btp, prog);
        if (err) {
-               bpf_link_cleanup(&link->link, link_file, link_fd);
+               bpf_link_cleanup(&link_primer);
                goto out_put_btp;
        }
 
-       fd_install(link_fd, link_file);
-       return link_fd;
+       return bpf_link_settle(&link_primer);
 
 out_put_btp:
        bpf_put_raw_tracepoint(btp);
@@ -3315,6 +3445,42 @@ static int bpf_btf_get_info_by_fd(struct btf *btf,
        return btf_get_info_by_fd(btf, attr, uattr);
 }
 
+static int bpf_link_get_info_by_fd(struct bpf_link *link,
+                                 const union bpf_attr *attr,
+                                 union bpf_attr __user *uattr)
+{
+       struct bpf_link_info __user *uinfo = u64_to_user_ptr(attr->info.info);
+       struct bpf_link_info info;
+       u32 info_len = attr->info.info_len;
+       int err;
+
+       err = bpf_check_uarg_tail_zero(uinfo, sizeof(info), info_len);
+       if (err)
+               return err;
+       info_len = min_t(u32, sizeof(info), info_len);
+
+       memset(&info, 0, sizeof(info));
+       if (copy_from_user(&info, uinfo, info_len))
+               return -EFAULT;
+
+       info.type = link->type;
+       info.id = link->id;
+       info.prog_id = link->prog->aux->id;
+
+       if (link->ops->fill_link_info) {
+               err = link->ops->fill_link_info(link, &info);
+               if (err)
+                       return err;
+       }
+
+       if (copy_to_user(uinfo, &info, info_len) ||
+           put_user(info_len, &uattr->info.info_len))
+               return -EFAULT;
+
+       return 0;
+}
+
+
 #define BPF_OBJ_GET_INFO_BY_FD_LAST_FIELD info.info
 
 static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
@@ -3339,6 +3505,9 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
                                             uattr);
        else if (f.file->f_op == &btf_fops)
                err = bpf_btf_get_info_by_fd(f.file->private_data, attr, uattr);
+       else if (f.file->f_op == &bpf_link_fops)
+               err = bpf_link_get_info_by_fd(f.file->private_data,
+                                             attr, uattr);
        else
                err = -EINVAL;
 
@@ -3466,7 +3635,7 @@ static int bpf_task_fd_query(const union bpf_attr *attr,
        if (file->f_op == &bpf_link_fops) {
                struct bpf_link *link = file->private_data;
 
-               if (link->ops == &bpf_raw_tp_lops) {
+               if (link->ops == &bpf_raw_tp_link_lops) {
                        struct bpf_raw_tp_link *raw_tp =
                                container_of(link, struct bpf_raw_tp_link, link);
                        struct bpf_raw_event_map *btp = raw_tp->btp;
@@ -3630,8 +3799,10 @@ static int link_update(union bpf_attr *attr)
                return PTR_ERR(link);
 
        new_prog = bpf_prog_get(attr->link_update.new_prog_fd);
-       if (IS_ERR(new_prog))
-               return PTR_ERR(new_prog);
+       if (IS_ERR(new_prog)) {
+               ret = PTR_ERR(new_prog);
+               goto out_put_link;
+       }
 
        if (flags & BPF_F_REPLACE) {
                old_prog = bpf_prog_get(attr->link_update.old_prog_fd);
@@ -3640,24 +3811,122 @@ static int link_update(union bpf_attr *attr)
                        old_prog = NULL;
                        goto out_put_progs;
                }
-       }
-
-#ifdef CONFIG_CGROUP_BPF
-       if (link->ops == &bpf_cgroup_link_lops) {
-               ret = cgroup_bpf_replace(link, old_prog, new_prog);
+       } else if (attr->link_update.old_prog_fd) {
+               ret = -EINVAL;
                goto out_put_progs;
        }
-#endif
-       ret = -EINVAL;
+
+       if (link->ops->update_prog)
+               ret = link->ops->update_prog(link, new_prog, old_prog);
+       else
+               ret = EINVAL;
 
 out_put_progs:
        if (old_prog)
                bpf_prog_put(old_prog);
        if (ret)
                bpf_prog_put(new_prog);
+out_put_link:
+       bpf_link_put(link);
        return ret;
 }
 
+static int bpf_link_inc_not_zero(struct bpf_link *link)
+{
+       return atomic64_fetch_add_unless(&link->refcnt, 1, 0) ? 0 : -ENOENT;
+}
+
+#define BPF_LINK_GET_FD_BY_ID_LAST_FIELD link_id
+
+static int bpf_link_get_fd_by_id(const union bpf_attr *attr)
+{
+       struct bpf_link *link;
+       u32 id = attr->link_id;
+       int fd, err;
+
+       if (CHECK_ATTR(BPF_LINK_GET_FD_BY_ID))
+               return -EINVAL;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       spin_lock_bh(&link_idr_lock);
+       link = idr_find(&link_idr, id);
+       /* before link is "settled", ID is 0, pretend it doesn't exist yet */
+       if (link) {
+               if (link->id)
+                       err = bpf_link_inc_not_zero(link);
+               else
+                       err = -EAGAIN;
+       } else {
+               err = -ENOENT;
+       }
+       spin_unlock_bh(&link_idr_lock);
+
+       if (err)
+               return err;
+
+       fd = bpf_link_new_fd(link);
+       if (fd < 0)
+               bpf_link_put(link);
+
+       return fd;
+}
+
+DEFINE_MUTEX(bpf_stats_enabled_mutex);
+
+static int bpf_stats_release(struct inode *inode, struct file *file)
+{
+       mutex_lock(&bpf_stats_enabled_mutex);
+       static_key_slow_dec(&bpf_stats_enabled_key.key);
+       mutex_unlock(&bpf_stats_enabled_mutex);
+       return 0;
+}
+
+static const struct file_operations bpf_stats_fops = {
+       .release = bpf_stats_release,
+};
+
+static int bpf_enable_runtime_stats(void)
+{
+       int fd;
+
+       mutex_lock(&bpf_stats_enabled_mutex);
+
+       /* Set a very high limit to avoid overflow */
+       if (static_key_count(&bpf_stats_enabled_key.key) > INT_MAX / 2) {
+               mutex_unlock(&bpf_stats_enabled_mutex);
+               return -EBUSY;
+       }
+
+       fd = anon_inode_getfd("bpf-stats", &bpf_stats_fops, NULL, O_CLOEXEC);
+       if (fd >= 0)
+               static_key_slow_inc(&bpf_stats_enabled_key.key);
+
+       mutex_unlock(&bpf_stats_enabled_mutex);
+       return fd;
+}
+
+#define BPF_ENABLE_STATS_LAST_FIELD enable_stats.type
+
+static int bpf_enable_stats(union bpf_attr *attr)
+{
+
+       if (CHECK_ATTR(BPF_ENABLE_STATS))
+               return -EINVAL;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       switch (attr->enable_stats.type) {
+       case BPF_STATS_RUN_TIME:
+               return bpf_enable_runtime_stats();
+       default:
+               break;
+       }
+       return -EINVAL;
+}
+
 SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
 {
        union bpf_attr attr;
@@ -3775,6 +4044,16 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
        case BPF_LINK_UPDATE:
                err = link_update(&attr);
                break;
+       case BPF_LINK_GET_FD_BY_ID:
+               err = bpf_link_get_fd_by_id(&attr);
+               break;
+       case BPF_LINK_GET_NEXT_ID:
+               err = bpf_obj_get_next_id(&attr, uattr,
+                                         &link_idr, &link_idr_lock);
+               break;
+       case BPF_ENABLE_STATS:
+               err = bpf_enable_stats(&attr);
+               break;
        default:
                err = -EINVAL;
                break;
index 04c6630..70ad009 100644 (file)
@@ -28,9 +28,11 @@ static const struct bpf_verifier_ops * const bpf_verifier_ops[] = {
 #define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
        [_id] = & _name ## _verifier_ops,
 #define BPF_MAP_TYPE(_id, _ops)
+#define BPF_LINK_TYPE(_id, _name)
 #include <linux/bpf_types.h>
 #undef BPF_PROG_TYPE
 #undef BPF_MAP_TYPE
+#undef BPF_LINK_TYPE
 };
 
 /* bpf_check() is a static code analyzer that walks eBPF program
@@ -168,6 +170,8 @@ struct bpf_verifier_stack_elem {
        int insn_idx;
        int prev_insn_idx;
        struct bpf_verifier_stack_elem *next;
+       /* length of verifier log at the time this state was pushed on stack */
+       u32 log_pos;
 };
 
 #define BPF_COMPLEXITY_LIMIT_JMP_SEQ   8192
@@ -283,6 +287,18 @@ void bpf_verifier_vlog(struct bpf_verifier_log *log, const char *fmt,
                log->ubuf = NULL;
 }
 
+static void bpf_vlog_reset(struct bpf_verifier_log *log, u32 new_pos)
+{
+       char zero = 0;
+
+       if (!bpf_verifier_log_needed(log))
+               return;
+
+       log->len_used = new_pos;
+       if (put_user(zero, log->ubuf + new_pos))
+               log->ubuf = NULL;
+}
+
 /* log_level controls verbosity level of eBPF verifier.
  * bpf_verifier_log_write() is used to dump the verification trace to the log,
  * so the user can figure out what's wrong with the program
@@ -413,11 +429,30 @@ static bool is_release_function(enum bpf_func_id func_id)
        return func_id == BPF_FUNC_sk_release;
 }
 
-static bool is_acquire_function(enum bpf_func_id func_id)
+static bool may_be_acquire_function(enum bpf_func_id func_id)
 {
        return func_id == BPF_FUNC_sk_lookup_tcp ||
                func_id == BPF_FUNC_sk_lookup_udp ||
-               func_id == BPF_FUNC_skc_lookup_tcp;
+               func_id == BPF_FUNC_skc_lookup_tcp ||
+               func_id == BPF_FUNC_map_lookup_elem;
+}
+
+static bool is_acquire_function(enum bpf_func_id func_id,
+                               const struct bpf_map *map)
+{
+       enum bpf_map_type map_type = map ? map->map_type : BPF_MAP_TYPE_UNSPEC;
+
+       if (func_id == BPF_FUNC_sk_lookup_tcp ||
+           func_id == BPF_FUNC_sk_lookup_udp ||
+           func_id == BPF_FUNC_skc_lookup_tcp)
+               return true;
+
+       if (func_id == BPF_FUNC_map_lookup_elem &&
+           (map_type == BPF_MAP_TYPE_SOCKMAP ||
+            map_type == BPF_MAP_TYPE_SOCKHASH))
+               return true;
+
+       return false;
 }
 
 static bool is_ptr_cast_function(enum bpf_func_id func_id)
@@ -846,7 +881,7 @@ static void update_branch_counts(struct bpf_verifier_env *env, struct bpf_verifi
 }
 
 static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
-                    int *insn_idx)
+                    int *insn_idx, bool pop_log)
 {
        struct bpf_verifier_state *cur = env->cur_state;
        struct bpf_verifier_stack_elem *elem, *head = env->head;
@@ -860,6 +895,8 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
                if (err)
                        return err;
        }
+       if (pop_log)
+               bpf_vlog_reset(&env->log, head->log_pos);
        if (insn_idx)
                *insn_idx = head->insn_idx;
        if (prev_insn_idx)
@@ -887,6 +924,7 @@ static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,
        elem->insn_idx = insn_idx;
        elem->prev_insn_idx = prev_insn_idx;
        elem->next = env->head;
+       elem->log_pos = env->log.len_used;
        env->head = elem;
        env->stack_size++;
        err = copy_verifier_state(&elem->st, cur);
@@ -915,7 +953,7 @@ err:
        free_verifier_state(env->cur_state, true);
        env->cur_state = NULL;
        /* pop all elements and return */
-       while (!pop_stack(env, NULL, NULL));
+       while (!pop_stack(env, NULL, NULL, false));
        return NULL;
 }
 
@@ -1255,8 +1293,7 @@ static void __mark_reg_unknown(const struct bpf_verifier_env *env,
        reg->type = SCALAR_VALUE;
        reg->var_off = tnum_unknown;
        reg->frameno = 0;
-       reg->precise = env->subprog_cnt > 1 || !env->allow_ptr_leaks ?
-                      true : false;
+       reg->precise = env->subprog_cnt > 1 || !env->allow_ptr_leaks;
        __mark_reg_unbounded(reg);
 }
 
@@ -2119,6 +2156,15 @@ static bool register_is_const(struct bpf_reg_state *reg)
        return reg->type == SCALAR_VALUE && tnum_is_const(reg->var_off);
 }
 
+static bool __is_pointer_value(bool allow_ptr_leaks,
+                              const struct bpf_reg_state *reg)
+{
+       if (allow_ptr_leaks)
+               return false;
+
+       return reg->type != SCALAR_VALUE;
+}
+
 static void save_register_state(struct bpf_func_state *state,
                                int spi, struct bpf_reg_state *reg)
 {
@@ -2309,6 +2355,16 @@ static int check_stack_read(struct bpf_verifier_env *env,
                         * which resets stack/reg liveness for state transitions
                         */
                        state->regs[value_regno].live |= REG_LIVE_WRITTEN;
+               } else if (__is_pointer_value(env->allow_ptr_leaks, reg)) {
+                       /* If value_regno==-1, the caller is asking us whether
+                        * it is acceptable to use this value as a SCALAR_VALUE
+                        * (e.g. for XADD).
+                        * We must not allow unprivileged callers to do that
+                        * with spilled pointers.
+                        */
+                       verbose(env, "leaking pointer from stack off %d\n",
+                               off);
+                       return -EACCES;
                }
                mark_reg_read(env, reg, reg->parent, REG_LIVE_READ64);
        } else {
@@ -2674,15 +2730,6 @@ static int check_sock_access(struct bpf_verifier_env *env, int insn_idx,
        return -EACCES;
 }
 
-static bool __is_pointer_value(bool allow_ptr_leaks,
-                              const struct bpf_reg_state *reg)
-{
-       if (allow_ptr_leaks)
-               return false;
-
-       return reg->type != SCALAR_VALUE;
-}
-
 static struct bpf_reg_state *reg_state(struct bpf_verifier_env *env, int regno)
 {
        return cur_regs(env) + regno;
@@ -3090,7 +3137,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
        if (ret < 0)
                return ret;
 
-       if (atype == BPF_READ) {
+       if (atype == BPF_READ && value_regno >= 0) {
                if (ret == SCALAR_VALUE) {
                        mark_reg_unknown(env, regs, value_regno);
                        return 0;
@@ -3906,7 +3953,8 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env,
                    func_id != BPF_FUNC_sock_map_update &&
                    func_id != BPF_FUNC_map_delete_elem &&
                    func_id != BPF_FUNC_msg_redirect_map &&
-                   func_id != BPF_FUNC_sk_select_reuseport)
+                   func_id != BPF_FUNC_sk_select_reuseport &&
+                   func_id != BPF_FUNC_map_lookup_elem)
                        goto error;
                break;
        case BPF_MAP_TYPE_SOCKHASH:
@@ -3914,7 +3962,8 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env,
                    func_id != BPF_FUNC_sock_hash_update &&
                    func_id != BPF_FUNC_map_delete_elem &&
                    func_id != BPF_FUNC_msg_redirect_hash &&
-                   func_id != BPF_FUNC_sk_select_reuseport)
+                   func_id != BPF_FUNC_sk_select_reuseport &&
+                   func_id != BPF_FUNC_map_lookup_elem)
                        goto error;
                break;
        case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY:
@@ -4084,7 +4133,7 @@ static bool check_refcount_ok(const struct bpf_func_proto *fn, int func_id)
        /* A reference acquiring function cannot acquire
         * another refcounted ptr.
         */
-       if (is_acquire_function(func_id) && count)
+       if (may_be_acquire_function(func_id) && count)
                return false;
 
        /* We only support one arg being unreferenced at the moment,
@@ -4595,7 +4644,7 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
        if (is_ptr_cast_function(func_id)) {
                /* For release_reference() */
                regs[BPF_REG_0].ref_obj_id = meta.ref_obj_id;
-       } else if (is_acquire_function(func_id)) {
+       } else if (is_acquire_function(func_id, meta.map_ptr)) {
                int id = acquire_reference_state(env, insn_idx);
 
                if (id < 0)
@@ -5600,7 +5649,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
 {
        struct bpf_reg_state *regs = cur_regs(env);
        u8 opcode = BPF_OP(insn->code);
-       bool src_known, dst_known;
+       bool src_known;
        s64 smin_val, smax_val;
        u64 umin_val, umax_val;
        s32 s32_min_val, s32_max_val;
@@ -5622,7 +5671,6 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
 
        if (alu32) {
                src_known = tnum_subreg_is_const(src_reg.var_off);
-               dst_known = tnum_subreg_is_const(dst_reg->var_off);
                if ((src_known &&
                     (s32_min_val != s32_max_val || u32_min_val != u32_max_val)) ||
                    s32_min_val > s32_max_val || u32_min_val > u32_max_val) {
@@ -5634,7 +5682,6 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
                }
        } else {
                src_known = tnum_is_const(src_reg.var_off);
-               dst_known = tnum_is_const(dst_reg->var_off);
                if ((src_known &&
                     (smin_val != smax_val || umin_val != umax_val)) ||
                    smin_val > smax_val || umin_val > umax_val) {
@@ -6506,12 +6553,16 @@ static void mark_ptr_or_null_reg(struct bpf_func_state *state,
                if (is_null) {
                        reg->type = SCALAR_VALUE;
                } else if (reg->type == PTR_TO_MAP_VALUE_OR_NULL) {
-                       if (reg->map_ptr->inner_map_meta) {
+                       const struct bpf_map *map = reg->map_ptr;
+
+                       if (map->inner_map_meta) {
                                reg->type = CONST_PTR_TO_MAP;
-                               reg->map_ptr = reg->map_ptr->inner_map_meta;
-                       } else if (reg->map_ptr->map_type ==
-                                  BPF_MAP_TYPE_XSKMAP) {
+                               reg->map_ptr = map->inner_map_meta;
+                       } else if (map->map_type == BPF_MAP_TYPE_XSKMAP) {
                                reg->type = PTR_TO_XDP_SOCK;
+                       } else if (map->map_type == BPF_MAP_TYPE_SOCKMAP ||
+                                  map->map_type == BPF_MAP_TYPE_SOCKHASH) {
+                               reg->type = PTR_TO_SOCKET;
                        } else {
                                reg->type = PTR_TO_MAP_VALUE;
                        }
@@ -8400,6 +8451,7 @@ static bool reg_type_mismatch(enum bpf_reg_type src, enum bpf_reg_type prev)
 
 static int do_check(struct bpf_verifier_env *env)
 {
+       bool pop_log = !(env->log.level & BPF_LOG_LEVEL2);
        struct bpf_verifier_state *state = env->cur_state;
        struct bpf_insn *insns = env->prog->insnsi;
        struct bpf_reg_state *regs;
@@ -8676,7 +8728,7 @@ static int do_check(struct bpf_verifier_env *env)
 process_bpf_exit:
                                update_branch_counts(env, env->cur_state);
                                err = pop_stack(env, &prev_insn_idx,
-                                               &env->insn_idx);
+                                               &env->insn_idx, pop_log);
                                if (err < 0) {
                                        if (err != -ENOENT)
                                                return err;
@@ -10199,6 +10251,7 @@ static void sanitize_insn_aux_data(struct bpf_verifier_env *env)
 
 static int do_check_common(struct bpf_verifier_env *env, int subprog)
 {
+       bool pop_log = !(env->log.level & BPF_LOG_LEVEL2);
        struct bpf_verifier_state *state;
        struct bpf_reg_state *regs;
        int ret, i;
@@ -10261,7 +10314,9 @@ out:
                free_verifier_state(env->cur_state, true);
                env->cur_state = NULL;
        }
-       while (!pop_stack(env, NULL, NULL));
+       while (!pop_stack(env, NULL, NULL, false));
+       if (!ret && pop_log)
+               bpf_vlog_reset(&env->log, 0);
        free_states(env);
        if (ret)
                /* clean aux data in case subprog was rejected */
@@ -10488,6 +10543,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
                                return -EINVAL;
                        }
                        env->ops = bpf_verifier_ops[tgt_prog->type];
+                       prog->expected_attach_type = tgt_prog->expected_attach_type;
                }
                if (!tgt_prog->jited) {
                        verbose(env, "Can attach to only JITed progs\n");
@@ -10832,6 +10888,13 @@ err_release_maps:
                 * them now. Otherwise free_used_maps() will release them.
                 */
                release_maps(env);
+
+       /* extension progs temporarily inherit the attach_type of their targets
+          for verification purposes, so set it back to zero before returning
+        */
+       if (env->prog->type == BPF_PROG_TYPE_EXT)
+               env->prog->expected_attach_type = 0;
+
        *prog = env->prog;
 err_unlock:
        if (!is_priv)
index 06b5ea9..557a9b9 100644 (file)
@@ -6508,33 +6508,6 @@ int cgroup_bpf_attach(struct cgroup *cgrp,
        return ret;
 }
 
-int cgroup_bpf_replace(struct bpf_link *link, struct bpf_prog *old_prog,
-                      struct bpf_prog *new_prog)
-{
-       struct bpf_cgroup_link *cg_link;
-       int ret;
-
-       if (link->ops != &bpf_cgroup_link_lops)
-               return -EINVAL;
-
-       cg_link = container_of(link, struct bpf_cgroup_link, link);
-
-       mutex_lock(&cgroup_mutex);
-       /* link might have been auto-released by dying cgroup, so fail */
-       if (!cg_link->cgroup) {
-               ret = -EINVAL;
-               goto out_unlock;
-       }
-       if (old_prog && link->prog != old_prog) {
-               ret = -EPERM;
-               goto out_unlock;
-       }
-       ret = __cgroup_bpf_replace(cg_link->cgroup, cg_link, new_prog);
-out_unlock:
-       mutex_unlock(&cgroup_mutex);
-       return ret;
-}
-
 int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
                      enum bpf_attach_type type)
 {
index c2b41a2..bdb1533 100644 (file)
@@ -236,7 +236,7 @@ exit_put:
  * sysctl_perf_event_max_contexts_per_stack.
  */
 int perf_event_max_stack_handler(struct ctl_table *table, int write,
-                                void __user *buffer, size_t *lenp, loff_t *ppos)
+                                void *buffer, size_t *lenp, loff_t *ppos)
 {
        int *value = table->data;
        int new_value = *value, ret;
index bc9b98a..4681396 100644 (file)
@@ -437,8 +437,7 @@ static void update_perf_cpu_limits(void)
 static bool perf_rotate_context(struct perf_cpu_context *cpuctx);
 
 int perf_proc_update_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
        int perf_cpu = sysctl_perf_cpu_time_max_percent;
@@ -462,8 +461,7 @@ int perf_proc_update_handler(struct ctl_table *table, int write,
 int sysctl_perf_cpu_time_max_percent __read_mostly = DEFAULT_CPU_TIME_MAX_PERCENT;
 
 int perf_cpu_time_max_percent_handler(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp,
-                               loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
 
@@ -7491,10 +7489,17 @@ static void perf_event_task_output(struct perf_event *event,
                goto out;
 
        task_event->event_id.pid = perf_event_pid(event, task);
-       task_event->event_id.ppid = perf_event_pid(event, current);
-
        task_event->event_id.tid = perf_event_tid(event, task);
-       task_event->event_id.ptid = perf_event_tid(event, current);
+
+       if (task_event->event_id.header.type == PERF_RECORD_EXIT) {
+               task_event->event_id.ppid = perf_event_pid(event,
+                                                       task->real_parent);
+               task_event->event_id.ptid = perf_event_pid(event,
+                                                       task->real_parent);
+       } else {  /* PERF_RECORD_FORK */
+               task_event->event_id.ppid = perf_event_pid(event, current);
+               task_event->event_id.ptid = perf_event_tid(event, current);
+       }
 
        task_event->event_id.time = perf_event_clock(event);
 
index 389a88c..ce2a75b 100644 (file)
@@ -219,6 +219,7 @@ repeat:
 
        write_unlock_irq(&tasklist_lock);
        proc_flush_pid(thread_pid);
+       put_pid(thread_pid);
        release_thread(p);
        put_task_struct_rcu_user(p);
 
index 4385f3d..8c700f8 100644 (file)
@@ -2605,6 +2605,14 @@ noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs,
        struct clone_args args;
        pid_t *kset_tid = kargs->set_tid;
 
+       BUILD_BUG_ON(offsetofend(struct clone_args, tls) !=
+                    CLONE_ARGS_SIZE_VER0);
+       BUILD_BUG_ON(offsetofend(struct clone_args, set_tid_size) !=
+                    CLONE_ARGS_SIZE_VER1);
+       BUILD_BUG_ON(offsetofend(struct clone_args, cgroup) !=
+                    CLONE_ARGS_SIZE_VER2);
+       BUILD_BUG_ON(sizeof(struct clone_args) != CLONE_ARGS_SIZE_VER2);
+
        if (unlikely(usize > PAGE_SIZE))
                return -E2BIG;
        if (unlikely(usize < CLONE_ARGS_SIZE_VER0))
@@ -2631,7 +2639,8 @@ noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs,
                     !valid_signal(args.exit_signal)))
                return -EINVAL;
 
-       if ((args.flags & CLONE_INTO_CGROUP) && args.cgroup < 0)
+       if ((args.flags & CLONE_INTO_CGROUP) &&
+           (args.cgroup > INT_MAX || usize < CLONE_ARGS_SIZE_VER2))
                return -EINVAL;
 
        *kargs = (struct kernel_clone_args){
index fe40c65..453a8a0 100644 (file)
@@ -1690,34 +1690,6 @@ out_mput:
        return ret;
 }
 
-/**
- *     setup_irq - setup an interrupt
- *     @irq: Interrupt line to setup
- *     @act: irqaction for the interrupt
- *
- * Used to statically setup interrupts in the early boot process.
- */
-int setup_irq(unsigned int irq, struct irqaction *act)
-{
-       int retval;
-       struct irq_desc *desc = irq_to_desc(irq);
-
-       if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
-               return -EINVAL;
-
-       retval = irq_chip_pm_get(&desc->irq_data);
-       if (retval < 0)
-               return retval;
-
-       retval = __setup_irq(irq, desc, act);
-
-       if (retval)
-               irq_chip_pm_put(&desc->irq_data);
-
-       return retval;
-}
-EXPORT_SYMBOL_GPL(setup_irq);
-
 /*
  * Internal function to unregister an irqaction - used to free
  * regular and special interrupts that are part of the architecture.
@@ -1858,22 +1830,6 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id)
        return action;
 }
 
-/**
- *     remove_irq - free an interrupt
- *     @irq: Interrupt line to free
- *     @act: irqaction for the interrupt
- *
- * Used to remove interrupts statically setup by the early boot process.
- */
-void remove_irq(unsigned int irq, struct irqaction *act)
-{
-       struct irq_desc *desc = irq_to_desc(irq);
-
-       if (desc && !WARN_ON(irq_settings_is_per_cpu_devid(desc)))
-               __free_irq(desc, act->dev_id);
-}
-EXPORT_SYMBOL_GPL(remove_irq);
-
 /**
  *     free_irq - free an interrupt allocated with request_irq
  *     @irq: Interrupt line to free
index 2625c24..ffbe03a 100644 (file)
@@ -892,7 +892,7 @@ static void unoptimize_all_kprobes(void)
 static DEFINE_MUTEX(kprobe_sysctl_mutex);
 int sysctl_kprobes_optimization;
 int proc_kprobes_optimization_handler(struct ctl_table *table, int write,
-                                     void __user *buffer, size_t *length,
+                                     void *buffer, size_t *length,
                                      loff_t *ppos)
 {
        int ret;
index 8d1c158..166d7bf 100644 (file)
@@ -269,8 +269,8 @@ static int __init init_lstats_procfs(void)
        return 0;
 }
 
-int sysctl_latencytop(struct ctl_table *table, int write,
-                       void __user *buffer, size_t *lenp, loff_t *ppos)
+int sysctl_latencytop(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos)
 {
        int err;
 
index 646f1e2..8833e84 100644 (file)
@@ -4,6 +4,9 @@
    Copyright (C) 2001 Rusty Russell, 2002, 2010 Rusty Russell IBM.
 
 */
+
+#define INCLUDE_VERMAGIC
+
 #include <linux/export.h>
 #include <linux/extable.h>
 #include <linux/moduleloader.h>
index 01f8ba3..3ccaba5 100644 (file)
@@ -263,7 +263,7 @@ void zap_pid_ns_processes(struct pid_namespace *pid_ns)
 
 #ifdef CONFIG_CHECKPOINT_RESTORE
 static int pid_ns_ctl_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct pid_namespace *pid_ns = task_active_pid_ns(current);
        struct ctl_table tmp = *table;
index 86aba87..30bd28d 100644 (file)
@@ -898,6 +898,13 @@ static int software_resume(void)
        error = freeze_processes();
        if (error)
                goto Close_Finish;
+
+       error = freeze_kernel_threads();
+       if (error) {
+               thaw_processes();
+               goto Close_Finish;
+       }
+
        error = load_image_and_restore();
        thaw_processes();
  Finish:
index 9a9b615..471f649 100644 (file)
@@ -173,7 +173,7 @@ __setup("printk.devkmsg=", control_devkmsg);
 char devkmsg_log_str[DEVKMSG_STR_MAX_SIZE] = "ratelimit";
 
 int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write,
-                             void __user *buffer, size_t *lenp, loff_t *ppos)
+                             void *buffer, size_t *lenp, loff_t *ppos)
 {
        char old_str[DEVKMSG_STR_MAX_SIZE];
        unsigned int old;
index 06548e2..d9a49cd 100644 (file)
@@ -825,7 +825,7 @@ static __always_inline void rcu_nmi_enter_common(bool irq)
                        rcu_cleanup_after_idle();
 
                incby = 1;
-       } else if (tick_nohz_full_cpu(rdp->cpu) &&
+       } else if (irq && tick_nohz_full_cpu(rdp->cpu) &&
                   rdp->dynticks_nmi_nesting == DYNTICK_IRQ_NONIDLE &&
                   READ_ONCE(rdp->rcu_urgent_qs) &&
                   !READ_ONCE(rdp->rcu_forced_tick)) {
index 3a61a3b..3e89a04 100644 (file)
@@ -1110,8 +1110,7 @@ static void uclamp_update_root_tg(void) { }
 #endif
 
 int sysctl_sched_uclamp_handler(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp,
-                               loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        bool update_root_tg = false;
        int old_min, old_max;
@@ -1232,13 +1231,8 @@ static void uclamp_fork(struct task_struct *p)
                return;
 
        for_each_clamp_id(clamp_id) {
-               unsigned int clamp_value = uclamp_none(clamp_id);
-
-               /* By default, RT tasks always get 100% boost */
-               if (unlikely(rt_task(p) && clamp_id == UCLAMP_MIN))
-                       clamp_value = uclamp_none(UCLAMP_MAX);
-
-               uclamp_se_set(&p->uclamp_req[clamp_id], clamp_value, false);
+               uclamp_se_set(&p->uclamp_req[clamp_id],
+                             uclamp_none(clamp_id), false);
        }
 }
 
@@ -2723,7 +2717,7 @@ void set_numabalancing_state(bool enabled)
 
 #ifdef CONFIG_PROC_SYSCTL
 int sysctl_numa_balancing(struct ctl_table *table, int write,
-                        void __user *buffer, size_t *lenp, loff_t *ppos)
+                         void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table t;
        int err;
@@ -2797,8 +2791,8 @@ static void __init init_schedstats(void)
 }
 
 #ifdef CONFIG_PROC_SYSCTL
-int sysctl_schedstats(struct ctl_table *table, int write,
-                        void __user *buffer, size_t *lenp, loff_t *ppos)
+int sysctl_schedstats(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos)
 {
        struct ctl_table t;
        int err;
index dac9104..ff9435d 100644 (file)
@@ -1003,12 +1003,12 @@ u64 kcpustat_field(struct kernel_cpustat *kcpustat,
                   enum cpu_usage_stat usage, int cpu)
 {
        u64 *cpustat = kcpustat->cpustat;
+       u64 val = cpustat[usage];
        struct rq *rq;
-       u64 val;
        int err;
 
        if (!vtime_accounting_enabled_cpu(cpu))
-               return cpustat[usage];
+               return val;
 
        rq = cpu_rq(cpu);
 
index 02f323b..b6077fd 100644 (file)
@@ -645,8 +645,7 @@ struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq)
  */
 
 int sched_proc_update_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
        unsigned int factor = get_update_sysctl_factor();
index 008d6ac..808244f 100644 (file)
@@ -149,6 +149,9 @@ __setup("nohz_full=", housekeeping_nohz_full_setup);
 static int __init housekeeping_isolcpus_setup(char *str)
 {
        unsigned int flags = 0;
+       bool illegal = false;
+       char *par;
+       int len;
 
        while (isalpha(*str)) {
                if (!strncmp(str, "nohz,", 5)) {
@@ -169,8 +172,22 @@ static int __init housekeeping_isolcpus_setup(char *str)
                        continue;
                }
 
-               pr_warn("isolcpus: Error, unknown flag\n");
-               return 0;
+               /*
+                * Skip unknown sub-parameter and validate that it is not
+                * containing an invalid character.
+                */
+               for (par = str, len = 0; *str && *str != ','; str++, len++) {
+                       if (!isalpha(*str) && *str != '_')
+                               illegal = true;
+               }
+
+               if (illegal) {
+                       pr_warn("isolcpus: Invalid flag %.*s\n", len, par);
+                       return 0;
+               }
+
+               pr_info("isolcpus: Skipped unknown flag %.*s\n", len, par);
+               str++;
        }
 
        /* Default behaviour for isolcpus without flags */
index df11d88..45da29d 100644 (file)
@@ -2714,9 +2714,8 @@ static void sched_rt_do_global(void)
        def_rt_bandwidth.rt_period = ns_to_ktime(global_rt_period());
 }
 
-int sched_rt_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos)
+int sched_rt_handler(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos)
 {
        int old_period, old_runtime;
        static DEFINE_MUTEX(mutex);
@@ -2754,9 +2753,8 @@ undo:
        return ret;
 }
 
-int sched_rr_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos)
+int sched_rr_handler(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos)
 {
        int ret;
        static DEFINE_MUTEX(mutex);
index 8344757..fa64b2e 100644 (file)
@@ -209,7 +209,7 @@ bool sched_energy_update;
 
 #ifdef CONFIG_PROC_SYSCTL
 int sched_energy_aware_handler(struct ctl_table *table, int write,
-                        void __user *buffer, size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret, state;
 
index 55a6184..d653d84 100644 (file)
@@ -1776,7 +1776,7 @@ static void audit_actions_logged(u32 actions_logged, u32 old_actions_logged,
 }
 
 static int seccomp_actions_logged_handler(struct ctl_table *ro_table, int write,
-                                         void __user *buffer, size_t *lenp,
+                                         void *buffer, size_t *lenp,
                                          loff_t *ppos)
 {
        int ret;
index e58a6c6..284fc16 100644 (file)
@@ -1510,15 +1510,15 @@ int kill_pid_usb_asyncio(int sig, int errno, sigval_t addr,
        unsigned long flags;
        int ret = -EINVAL;
 
+       if (!valid_signal(sig))
+               return ret;
+
        clear_siginfo(&info);
        info.si_signo = sig;
        info.si_errno = errno;
        info.si_code = SI_ASYNCIO;
        *((sigval_t *)&info.si_pid) = addr;
 
-       if (!valid_signal(sig))
-               return ret;
-
        rcu_read_lock();
        p = pid_task(pid, PIDTYPE_PID);
        if (!p) {
@@ -1557,12 +1557,8 @@ static int kill_something_info(int sig, struct kernel_siginfo *info, pid_t pid)
 {
        int ret;
 
-       if (pid > 0) {
-               rcu_read_lock();
-               ret = kill_pid_info(sig, info, find_vpid(pid));
-               rcu_read_unlock();
-               return ret;
-       }
+       if (pid > 0)
+               return kill_proc_info(sig, info, pid);
 
        /* -INT_MIN is undefined.  Exclude this case to avoid a UBSAN warning */
        if (pid == INT_MIN)
@@ -1993,8 +1989,12 @@ bool do_notify_parent(struct task_struct *tsk, int sig)
                if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN)
                        sig = 0;
        }
+       /*
+        * Send with __send_signal as si_pid and si_uid are in the
+        * parent's namespaces.
+        */
        if (valid_signal(sig) && sig)
-               __group_send_sig_info(sig, &info, tsk->parent);
+               __send_signal(sig, &info, tsk->parent, PIDTYPE_TGID, false);
        __wake_up_parent(tsk, tsk->parent);
        spin_unlock_irqrestore(&psig->siglock, flags);
 
index 8a176d8..7adfe5d 100644 (file)
@@ -68,6 +68,9 @@
 #include <linux/bpf.h>
 #include <linux/mount.h>
 #include <linux/userfaultfd_k.h>
+#include <linux/coredump.h>
+#include <linux/latencytop.h>
+#include <linux/pid.h>
 
 #include "../lib/kstrtox.h"
 
 
 #if defined(CONFIG_SYSCTL)
 
-/* External variables not in a header file. */
-extern int suid_dumpable;
-#ifdef CONFIG_COREDUMP
-extern int core_uses_pid;
-extern char core_pattern[];
-extern unsigned int core_pipe_limit;
-#endif
-extern int pid_max;
-extern int pid_max_min, pid_max_max;
-extern int percpu_pagelist_fraction;
-extern int latencytop_enabled;
-extern unsigned int sysctl_nr_open_min, sysctl_nr_open_max;
-#ifndef CONFIG_MMU
-extern int sysctl_nr_trim_pages;
-#endif
-
 /* Constants used for minimum and  maximum */
 #ifdef CONFIG_LOCKUP_DETECTOR
 static int sixty = 60;
@@ -160,24 +147,6 @@ static unsigned long hung_task_timeout_max = (LONG_MAX/HZ);
 #ifdef CONFIG_INOTIFY_USER
 #include <linux/inotify.h>
 #endif
-#ifdef CONFIG_SPARC
-#endif
-
-#ifdef CONFIG_PARISC
-extern int pwrsw_enabled;
-#endif
-
-#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_ALLOW
-extern int unaligned_enabled;
-#endif
-
-#ifdef CONFIG_IA64
-extern int unaligned_dump_stack;
-#endif
-
-#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_NO_WARN
-extern int no_unaligned_warning;
-#endif
 
 #ifdef CONFIG_PROC_SYSCTL
 
@@ -207,87 +176,13 @@ enum sysctl_writes_mode {
 };
 
 static enum sysctl_writes_mode sysctl_writes_strict = SYSCTL_WRITES_STRICT;
-
-static int proc_do_cad_pid(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos);
-static int proc_taint(struct ctl_table *table, int write,
-                              void __user *buffer, size_t *lenp, loff_t *ppos);
-#ifdef CONFIG_COMPACTION
-static int proc_dointvec_minmax_warn_RT_change(struct ctl_table *table,
-                                              int write, void __user *buffer,
-                                              size_t *lenp, loff_t *ppos);
-#endif
-#endif
-
-#ifdef CONFIG_PRINTK
-static int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos);
-#endif
-
-static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp, loff_t *ppos);
-#ifdef CONFIG_COREDUMP
-static int proc_dostring_coredump(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp, loff_t *ppos);
-#endif
-static int proc_dopipe_max_size(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp, loff_t *ppos);
-
-#ifdef CONFIG_MAGIC_SYSRQ
-static int sysrq_sysctl_handler(struct ctl_table *table, int write,
-                       void __user *buffer, size_t *lenp, loff_t *ppos);
-#endif
-
-static struct ctl_table kern_table[];
-static struct ctl_table vm_table[];
-static struct ctl_table fs_table[];
-static struct ctl_table debug_table[];
-static struct ctl_table dev_table[];
-extern struct ctl_table random_table[];
-#ifdef CONFIG_EPOLL
-extern struct ctl_table epoll_table[];
-#endif
-
-#ifdef CONFIG_FW_LOADER_USER_HELPER
-extern struct ctl_table firmware_config_table[];
-#endif
+#endif /* CONFIG_PROC_SYSCTL */
 
 #if defined(HAVE_ARCH_PICK_MMAP_LAYOUT) || \
     defined(CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT)
 int sysctl_legacy_va_layout;
 #endif
 
-/* The default sysctl tables: */
-
-static struct ctl_table sysctl_base_table[] = {
-       {
-               .procname       = "kernel",
-               .mode           = 0555,
-               .child          = kern_table,
-       },
-       {
-               .procname       = "vm",
-               .mode           = 0555,
-               .child          = vm_table,
-       },
-       {
-               .procname       = "fs",
-               .mode           = 0555,
-               .child          = fs_table,
-       },
-       {
-               .procname       = "debug",
-               .mode           = 0555,
-               .child          = debug_table,
-       },
-       {
-               .procname       = "dev",
-               .mode           = 0555,
-               .child          = dev_table,
-       },
-       { }
-};
-
 #ifdef CONFIG_SCHED_DEBUG
 static int min_sched_granularity_ns = 100000;          /* 100 usecs */
 static int max_sched_granularity_ns = NSEC_PER_SEC;    /* 1 second */
@@ -304,1685 +199,53 @@ static int min_extfrag_threshold;
 static int max_extfrag_threshold = 1000;
 #endif
 
-static struct ctl_table kern_table[] = {
-       {
-               .procname       = "sched_child_runs_first",
-               .data           = &sysctl_sched_child_runs_first,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#ifdef CONFIG_SCHED_DEBUG
-       {
-               .procname       = "sched_min_granularity_ns",
-               .data           = &sysctl_sched_min_granularity,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = sched_proc_update_handler,
-               .extra1         = &min_sched_granularity_ns,
-               .extra2         = &max_sched_granularity_ns,
-       },
-       {
-               .procname       = "sched_latency_ns",
-               .data           = &sysctl_sched_latency,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = sched_proc_update_handler,
-               .extra1         = &min_sched_granularity_ns,
-               .extra2         = &max_sched_granularity_ns,
-       },
-       {
-               .procname       = "sched_wakeup_granularity_ns",
-               .data           = &sysctl_sched_wakeup_granularity,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = sched_proc_update_handler,
-               .extra1         = &min_wakeup_granularity_ns,
-               .extra2         = &max_wakeup_granularity_ns,
-       },
-#ifdef CONFIG_SMP
-       {
-               .procname       = "sched_tunable_scaling",
-               .data           = &sysctl_sched_tunable_scaling,
-               .maxlen         = sizeof(enum sched_tunable_scaling),
-               .mode           = 0644,
-               .proc_handler   = sched_proc_update_handler,
-               .extra1         = &min_sched_tunable_scaling,
-               .extra2         = &max_sched_tunable_scaling,
-       },
-       {
-               .procname       = "sched_migration_cost_ns",
-               .data           = &sysctl_sched_migration_cost,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "sched_nr_migrate",
-               .data           = &sysctl_sched_nr_migrate,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#ifdef CONFIG_SCHEDSTATS
-       {
-               .procname       = "sched_schedstats",
-               .data           = NULL,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = sysctl_schedstats,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif /* CONFIG_SCHEDSTATS */
-#endif /* CONFIG_SMP */
-#ifdef CONFIG_NUMA_BALANCING
-       {
-               .procname       = "numa_balancing_scan_delay_ms",
-               .data           = &sysctl_numa_balancing_scan_delay,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "numa_balancing_scan_period_min_ms",
-               .data           = &sysctl_numa_balancing_scan_period_min,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "numa_balancing_scan_period_max_ms",
-               .data           = &sysctl_numa_balancing_scan_period_max,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "numa_balancing_scan_size_mb",
-               .data           = &sysctl_numa_balancing_scan_size,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ONE,
-       },
-       {
-               .procname       = "numa_balancing",
-               .data           = NULL, /* filled in by handler */
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = sysctl_numa_balancing,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif /* CONFIG_NUMA_BALANCING */
-#endif /* CONFIG_SCHED_DEBUG */
-       {
-               .procname       = "sched_rt_period_us",
-               .data           = &sysctl_sched_rt_period,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = sched_rt_handler,
-       },
-       {
-               .procname       = "sched_rt_runtime_us",
-               .data           = &sysctl_sched_rt_runtime,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = sched_rt_handler,
-       },
-       {
-               .procname       = "sched_rr_timeslice_ms",
-               .data           = &sysctl_sched_rr_timeslice,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = sched_rr_handler,
-       },
-#ifdef CONFIG_UCLAMP_TASK
-       {
-               .procname       = "sched_util_clamp_min",
-               .data           = &sysctl_sched_uclamp_util_min,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = sysctl_sched_uclamp_handler,
-       },
-       {
-               .procname       = "sched_util_clamp_max",
-               .data           = &sysctl_sched_uclamp_util_max,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = sysctl_sched_uclamp_handler,
-       },
-#endif
-#ifdef CONFIG_SCHED_AUTOGROUP
-       {
-               .procname       = "sched_autogroup_enabled",
-               .data           = &sysctl_sched_autogroup_enabled,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif
-#ifdef CONFIG_CFS_BANDWIDTH
-       {
-               .procname       = "sched_cfs_bandwidth_slice_us",
-               .data           = &sysctl_sched_cfs_bandwidth_slice,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ONE,
-       },
-#endif
-#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL)
-       {
-               .procname       = "sched_energy_aware",
-               .data           = &sysctl_sched_energy_aware,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = sched_energy_aware_handler,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif
-#ifdef CONFIG_PROVE_LOCKING
-       {
-               .procname       = "prove_locking",
-               .data           = &prove_locking,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#ifdef CONFIG_LOCK_STAT
-       {
-               .procname       = "lock_stat",
-               .data           = &lock_stat,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-       {
-               .procname       = "panic",
-               .data           = &panic_timeout,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#ifdef CONFIG_COREDUMP
-       {
-               .procname       = "core_uses_pid",
-               .data           = &core_uses_pid,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "core_pattern",
-               .data           = core_pattern,
-               .maxlen         = CORENAME_MAX_SIZE,
-               .mode           = 0644,
-               .proc_handler   = proc_dostring_coredump,
-       },
-       {
-               .procname       = "core_pipe_limit",
-               .data           = &core_pipe_limit,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
+#endif /* CONFIG_SYSCTL */
+
+#ifdef CONFIG_BPF_SYSCALL
+static int bpf_stats_handler(struct ctl_table *table, int write,
+                            void __user *buffer, size_t *lenp,
+                            loff_t *ppos)
+{
+       struct static_key *key = (struct static_key *)table->data;
+       static int saved_val;
+       int val, ret;
+       struct ctl_table tmp = {
+               .data   = &val,
+               .maxlen = sizeof(val),
+               .mode   = table->mode,
+               .extra1 = SYSCTL_ZERO,
+               .extra2 = SYSCTL_ONE,
+       };
+
+       if (write && !capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       mutex_lock(&bpf_stats_enabled_mutex);
+       val = saved_val;
+       ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+       if (write && !ret && val != saved_val) {
+               if (val)
+                       static_key_slow_inc(key);
+               else
+                       static_key_slow_dec(key);
+               saved_val = val;
+       }
+       mutex_unlock(&bpf_stats_enabled_mutex);
+       return ret;
+}
 #endif
-#ifdef CONFIG_PROC_SYSCTL
-       {
-               .procname       = "tainted",
-               .maxlen         = sizeof(long),
-               .mode           = 0644,
-               .proc_handler   = proc_taint,
-       },
-       {
-               .procname       = "sysctl_writes_strict",
-               .data           = &sysctl_writes_strict,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &neg_one,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif
-#ifdef CONFIG_LATENCYTOP
-       {
-               .procname       = "latencytop",
-               .data           = &latencytop_enabled,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = sysctl_latencytop,
-       },
-#endif
-#ifdef CONFIG_BLK_DEV_INITRD
-       {
-               .procname       = "real-root-dev",
-               .data           = &real_root_dev,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-       {
-               .procname       = "print-fatal-signals",
-               .data           = &print_fatal_signals,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#ifdef CONFIG_SPARC
-       {
-               .procname       = "reboot-cmd",
-               .data           = reboot_command,
-               .maxlen         = 256,
-               .mode           = 0644,
-               .proc_handler   = proc_dostring,
-       },
-       {
-               .procname       = "stop-a",
-               .data           = &stop_a_enabled,
-               .maxlen         = sizeof (int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "scons-poweroff",
-               .data           = &scons_pwroff,
-               .maxlen         = sizeof (int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#ifdef CONFIG_SPARC64
-       {
-               .procname       = "tsb-ratio",
-               .data           = &sysctl_tsb_ratio,
-               .maxlen         = sizeof (int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#ifdef CONFIG_PARISC
-       {
-               .procname       = "soft-power",
-               .data           = &pwrsw_enabled,
-               .maxlen         = sizeof (int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_ALLOW
-       {
-               .procname       = "unaligned-trap",
-               .data           = &unaligned_enabled,
-               .maxlen         = sizeof (int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-       {
-               .procname       = "ctrl-alt-del",
-               .data           = &C_A_D,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#ifdef CONFIG_FUNCTION_TRACER
-       {
-               .procname       = "ftrace_enabled",
-               .data           = &ftrace_enabled,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = ftrace_enable_sysctl,
-       },
-#endif
-#ifdef CONFIG_STACK_TRACER
-       {
-               .procname       = "stack_tracer_enabled",
-               .data           = &stack_tracer_enabled,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = stack_trace_sysctl,
-       },
-#endif
-#ifdef CONFIG_TRACING
-       {
-               .procname       = "ftrace_dump_on_oops",
-               .data           = &ftrace_dump_on_oops,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "traceoff_on_warning",
-               .data           = &__disable_trace_on_warning,
-               .maxlen         = sizeof(__disable_trace_on_warning),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "tracepoint_printk",
-               .data           = &tracepoint_printk,
-               .maxlen         = sizeof(tracepoint_printk),
-               .mode           = 0644,
-               .proc_handler   = tracepoint_printk_sysctl,
-       },
-#endif
-#ifdef CONFIG_KEXEC_CORE
-       {
-               .procname       = "kexec_load_disabled",
-               .data           = &kexec_load_disabled,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               /* only handle a transition from default "0" to "1" */
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ONE,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif
-#ifdef CONFIG_MODULES
-       {
-               .procname       = "modprobe",
-               .data           = &modprobe_path,
-               .maxlen         = KMOD_PATH_LEN,
-               .mode           = 0644,
-               .proc_handler   = proc_dostring,
-       },
-       {
-               .procname       = "modules_disabled",
-               .data           = &modules_disabled,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               /* only handle a transition from default "0" to "1" */
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ONE,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif
-#ifdef CONFIG_UEVENT_HELPER
-       {
-               .procname       = "hotplug",
-               .data           = &uevent_helper,
-               .maxlen         = UEVENT_HELPER_PATH_LEN,
-               .mode           = 0644,
-               .proc_handler   = proc_dostring,
-       },
-#endif
-#ifdef CONFIG_CHR_DEV_SG
-       {
-               .procname       = "sg-big-buff",
-               .data           = &sg_big_buff,
-               .maxlen         = sizeof (int),
-               .mode           = 0444,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#ifdef CONFIG_BSD_PROCESS_ACCT
-       {
-               .procname       = "acct",
-               .data           = &acct_parm,
-               .maxlen         = 3*sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#ifdef CONFIG_MAGIC_SYSRQ
-       {
-               .procname       = "sysrq",
-               .data           = NULL,
-               .maxlen         = sizeof (int),
-               .mode           = 0644,
-               .proc_handler   = sysrq_sysctl_handler,
-       },
-#endif
-#ifdef CONFIG_PROC_SYSCTL
-       {
-               .procname       = "cad_pid",
-               .data           = NULL,
-               .maxlen         = sizeof (int),
-               .mode           = 0600,
-               .proc_handler   = proc_do_cad_pid,
-       },
-#endif
-       {
-               .procname       = "threads-max",
-               .data           = NULL,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = sysctl_max_threads,
-       },
-       {
-               .procname       = "random",
-               .mode           = 0555,
-               .child          = random_table,
-       },
-       {
-               .procname       = "usermodehelper",
-               .mode           = 0555,
-               .child          = usermodehelper_table,
-       },
-#ifdef CONFIG_FW_LOADER_USER_HELPER
-       {
-               .procname       = "firmware_config",
-               .mode           = 0555,
-               .child          = firmware_config_table,
-       },
-#endif
-       {
-               .procname       = "overflowuid",
-               .data           = &overflowuid,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &minolduid,
-               .extra2         = &maxolduid,
-       },
-       {
-               .procname       = "overflowgid",
-               .data           = &overflowgid,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &minolduid,
-               .extra2         = &maxolduid,
-       },
-#ifdef CONFIG_S390
-       {
-               .procname       = "userprocess_debug",
-               .data           = &show_unhandled_signals,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-       {
-               .procname       = "pid_max",
-               .data           = &pid_max,
-               .maxlen         = sizeof (int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &pid_max_min,
-               .extra2         = &pid_max_max,
-       },
-       {
-               .procname       = "panic_on_oops",
-               .data           = &panic_on_oops,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "panic_print",
-               .data           = &panic_print,
-               .maxlen         = sizeof(unsigned long),
-               .mode           = 0644,
-               .proc_handler   = proc_doulongvec_minmax,
-       },
-#if defined CONFIG_PRINTK
-       {
-               .procname       = "printk",
-               .data           = &console_loglevel,
-               .maxlen         = 4*sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "printk_ratelimit",
-               .data           = &printk_ratelimit_state.interval,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_jiffies,
-       },
-       {
-               .procname       = "printk_ratelimit_burst",
-               .data           = &printk_ratelimit_state.burst,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "printk_delay",
-               .data           = &printk_delay_msec,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &ten_thousand,
-       },
-       {
-               .procname       = "printk_devkmsg",
-               .data           = devkmsg_log_str,
-               .maxlen         = DEVKMSG_STR_MAX_SIZE,
-               .mode           = 0644,
-               .proc_handler   = devkmsg_sysctl_set_loglvl,
-       },
-       {
-               .procname       = "dmesg_restrict",
-               .data           = &dmesg_restrict,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax_sysadmin,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-       {
-               .procname       = "kptr_restrict",
-               .data           = &kptr_restrict,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax_sysadmin,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &two,
-       },
-#endif
-       {
-               .procname       = "ngroups_max",
-               .data           = &ngroups_max,
-               .maxlen         = sizeof (int),
-               .mode           = 0444,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "cap_last_cap",
-               .data           = (void *)&cap_last_cap,
-               .maxlen         = sizeof(int),
-               .mode           = 0444,
-               .proc_handler   = proc_dointvec,
-       },
-#if defined(CONFIG_LOCKUP_DETECTOR)
-       {
-               .procname       = "watchdog",
-               .data           = &watchdog_user_enabled,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_watchdog,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-       {
-               .procname       = "watchdog_thresh",
-               .data           = &watchdog_thresh,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_watchdog_thresh,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &sixty,
-       },
-       {
-               .procname       = "nmi_watchdog",
-               .data           = &nmi_watchdog_user_enabled,
-               .maxlen         = sizeof(int),
-               .mode           = NMI_WATCHDOG_SYSCTL_PERM,
-               .proc_handler   = proc_nmi_watchdog,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-       {
-               .procname       = "watchdog_cpumask",
-               .data           = &watchdog_cpumask_bits,
-               .maxlen         = NR_CPUS,
-               .mode           = 0644,
-               .proc_handler   = proc_watchdog_cpumask,
-       },
-#ifdef CONFIG_SOFTLOCKUP_DETECTOR
-       {
-               .procname       = "soft_watchdog",
-               .data           = &soft_watchdog_user_enabled,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_soft_watchdog,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-       {
-               .procname       = "softlockup_panic",
-               .data           = &softlockup_panic,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#ifdef CONFIG_SMP
-       {
-               .procname       = "softlockup_all_cpu_backtrace",
-               .data           = &sysctl_softlockup_all_cpu_backtrace,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif /* CONFIG_SMP */
-#endif
-#ifdef CONFIG_HARDLOCKUP_DETECTOR
-       {
-               .procname       = "hardlockup_panic",
-               .data           = &hardlockup_panic,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#ifdef CONFIG_SMP
-       {
-               .procname       = "hardlockup_all_cpu_backtrace",
-               .data           = &sysctl_hardlockup_all_cpu_backtrace,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif /* CONFIG_SMP */
-#endif
-#endif
-
-#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86)
-       {
-               .procname       = "unknown_nmi_panic",
-               .data           = &unknown_nmi_panic,
-               .maxlen         = sizeof (int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#if defined(CONFIG_X86)
-       {
-               .procname       = "panic_on_unrecovered_nmi",
-               .data           = &panic_on_unrecovered_nmi,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "panic_on_io_nmi",
-               .data           = &panic_on_io_nmi,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#ifdef CONFIG_DEBUG_STACKOVERFLOW
-       {
-               .procname       = "panic_on_stackoverflow",
-               .data           = &sysctl_panic_on_stackoverflow,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-       {
-               .procname       = "bootloader_type",
-               .data           = &bootloader_type,
-               .maxlen         = sizeof (int),
-               .mode           = 0444,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "bootloader_version",
-               .data           = &bootloader_version,
-               .maxlen         = sizeof (int),
-               .mode           = 0444,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "io_delay_type",
-               .data           = &io_delay_type,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#if defined(CONFIG_MMU)
-       {
-               .procname       = "randomize_va_space",
-               .data           = &randomize_va_space,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#if defined(CONFIG_S390) && defined(CONFIG_SMP)
-       {
-               .procname       = "spin_retry",
-               .data           = &spin_retry,
-               .maxlen         = sizeof (int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#if    defined(CONFIG_ACPI_SLEEP) && defined(CONFIG_X86)
-       {
-               .procname       = "acpi_video_flags",
-               .data           = &acpi_realmode_flags,
-               .maxlen         = sizeof (unsigned long),
-               .mode           = 0644,
-               .proc_handler   = proc_doulongvec_minmax,
-       },
-#endif
-#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_NO_WARN
-       {
-               .procname       = "ignore-unaligned-usertrap",
-               .data           = &no_unaligned_warning,
-               .maxlen         = sizeof (int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#ifdef CONFIG_IA64
-       {
-               .procname       = "unaligned-dump-stack",
-               .data           = &unaligned_dump_stack,
-               .maxlen         = sizeof (int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#ifdef CONFIG_DETECT_HUNG_TASK
-       {
-               .procname       = "hung_task_panic",
-               .data           = &sysctl_hung_task_panic,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-       {
-               .procname       = "hung_task_check_count",
-               .data           = &sysctl_hung_task_check_count,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-       },
-       {
-               .procname       = "hung_task_timeout_secs",
-               .data           = &sysctl_hung_task_timeout_secs,
-               .maxlen         = sizeof(unsigned long),
-               .mode           = 0644,
-               .proc_handler   = proc_dohung_task_timeout_secs,
-               .extra2         = &hung_task_timeout_max,
-       },
-       {
-               .procname       = "hung_task_check_interval_secs",
-               .data           = &sysctl_hung_task_check_interval_secs,
-               .maxlen         = sizeof(unsigned long),
-               .mode           = 0644,
-               .proc_handler   = proc_dohung_task_timeout_secs,
-               .extra2         = &hung_task_timeout_max,
-       },
-       {
-               .procname       = "hung_task_warnings",
-               .data           = &sysctl_hung_task_warnings,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &neg_one,
-       },
-#endif
-#ifdef CONFIG_RT_MUTEXES
-       {
-               .procname       = "max_lock_depth",
-               .data           = &max_lock_depth,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-       {
-               .procname       = "poweroff_cmd",
-               .data           = &poweroff_cmd,
-               .maxlen         = POWEROFF_CMD_PATH_LEN,
-               .mode           = 0644,
-               .proc_handler   = proc_dostring,
-       },
-#ifdef CONFIG_KEYS
-       {
-               .procname       = "keys",
-               .mode           = 0555,
-               .child          = key_sysctls,
-       },
-#endif
-#ifdef CONFIG_PERF_EVENTS
-       /*
-        * User-space scripts rely on the existence of this file
-        * as a feature check for perf_events being enabled.
-        *
-        * So it's an ABI, do not remove!
-        */
-       {
-               .procname       = "perf_event_paranoid",
-               .data           = &sysctl_perf_event_paranoid,
-               .maxlen         = sizeof(sysctl_perf_event_paranoid),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "perf_event_mlock_kb",
-               .data           = &sysctl_perf_event_mlock,
-               .maxlen         = sizeof(sysctl_perf_event_mlock),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "perf_event_max_sample_rate",
-               .data           = &sysctl_perf_event_sample_rate,
-               .maxlen         = sizeof(sysctl_perf_event_sample_rate),
-               .mode           = 0644,
-               .proc_handler   = perf_proc_update_handler,
-               .extra1         = SYSCTL_ONE,
-       },
-       {
-               .procname       = "perf_cpu_time_max_percent",
-               .data           = &sysctl_perf_cpu_time_max_percent,
-               .maxlen         = sizeof(sysctl_perf_cpu_time_max_percent),
-               .mode           = 0644,
-               .proc_handler   = perf_cpu_time_max_percent_handler,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &one_hundred,
-       },
-       {
-               .procname       = "perf_event_max_stack",
-               .data           = &sysctl_perf_event_max_stack,
-               .maxlen         = sizeof(sysctl_perf_event_max_stack),
-               .mode           = 0644,
-               .proc_handler   = perf_event_max_stack_handler,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &six_hundred_forty_kb,
-       },
-       {
-               .procname       = "perf_event_max_contexts_per_stack",
-               .data           = &sysctl_perf_event_max_contexts_per_stack,
-               .maxlen         = sizeof(sysctl_perf_event_max_contexts_per_stack),
-               .mode           = 0644,
-               .proc_handler   = perf_event_max_stack_handler,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &one_thousand,
-       },
-#endif
-       {
-               .procname       = "panic_on_warn",
-               .data           = &panic_on_warn,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
-       {
-               .procname       = "timer_migration",
-               .data           = &sysctl_timer_migration,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = timer_migration_handler,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif
-#ifdef CONFIG_BPF_SYSCALL
-       {
-               .procname       = "unprivileged_bpf_disabled",
-               .data           = &sysctl_unprivileged_bpf_disabled,
-               .maxlen         = sizeof(sysctl_unprivileged_bpf_disabled),
-               .mode           = 0644,
-               /* only handle a transition from default "0" to "1" */
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ONE,
-               .extra2         = SYSCTL_ONE,
-       },
-       {
-               .procname       = "bpf_stats_enabled",
-               .data           = &bpf_stats_enabled_key.key,
-               .maxlen         = sizeof(bpf_stats_enabled_key),
-               .mode           = 0644,
-               .proc_handler   = proc_do_static_key,
-       },
-#endif
-#if defined(CONFIG_TREE_RCU)
-       {
-               .procname       = "panic_on_rcu_stall",
-               .data           = &sysctl_panic_on_rcu_stall,
-               .maxlen         = sizeof(sysctl_panic_on_rcu_stall),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif
-#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
-       {
-               .procname       = "stack_erasing",
-               .data           = NULL,
-               .maxlen         = sizeof(int),
-               .mode           = 0600,
-               .proc_handler   = stack_erasing_sysctl,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif
-       { }
-};
-
-static struct ctl_table vm_table[] = {
-       {
-               .procname       = "overcommit_memory",
-               .data           = &sysctl_overcommit_memory,
-               .maxlen         = sizeof(sysctl_overcommit_memory),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &two,
-       },
-       {
-               .procname       = "panic_on_oom",
-               .data           = &sysctl_panic_on_oom,
-               .maxlen         = sizeof(sysctl_panic_on_oom),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &two,
-       },
-       {
-               .procname       = "oom_kill_allocating_task",
-               .data           = &sysctl_oom_kill_allocating_task,
-               .maxlen         = sizeof(sysctl_oom_kill_allocating_task),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "oom_dump_tasks",
-               .data           = &sysctl_oom_dump_tasks,
-               .maxlen         = sizeof(sysctl_oom_dump_tasks),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-       {
-               .procname       = "overcommit_ratio",
-               .data           = &sysctl_overcommit_ratio,
-               .maxlen         = sizeof(sysctl_overcommit_ratio),
-               .mode           = 0644,
-               .proc_handler   = overcommit_ratio_handler,
-       },
-       {
-               .procname       = "overcommit_kbytes",
-               .data           = &sysctl_overcommit_kbytes,
-               .maxlen         = sizeof(sysctl_overcommit_kbytes),
-               .mode           = 0644,
-               .proc_handler   = overcommit_kbytes_handler,
-       },
-       {
-               .procname       = "page-cluster", 
-               .data           = &page_cluster,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-       },
-       {
-               .procname       = "dirty_background_ratio",
-               .data           = &dirty_background_ratio,
-               .maxlen         = sizeof(dirty_background_ratio),
-               .mode           = 0644,
-               .proc_handler   = dirty_background_ratio_handler,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &one_hundred,
-       },
-       {
-               .procname       = "dirty_background_bytes",
-               .data           = &dirty_background_bytes,
-               .maxlen         = sizeof(dirty_background_bytes),
-               .mode           = 0644,
-               .proc_handler   = dirty_background_bytes_handler,
-               .extra1         = &one_ul,
-       },
-       {
-               .procname       = "dirty_ratio",
-               .data           = &vm_dirty_ratio,
-               .maxlen         = sizeof(vm_dirty_ratio),
-               .mode           = 0644,
-               .proc_handler   = dirty_ratio_handler,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &one_hundred,
-       },
-       {
-               .procname       = "dirty_bytes",
-               .data           = &vm_dirty_bytes,
-               .maxlen         = sizeof(vm_dirty_bytes),
-               .mode           = 0644,
-               .proc_handler   = dirty_bytes_handler,
-               .extra1         = &dirty_bytes_min,
-       },
-       {
-               .procname       = "dirty_writeback_centisecs",
-               .data           = &dirty_writeback_interval,
-               .maxlen         = sizeof(dirty_writeback_interval),
-               .mode           = 0644,
-               .proc_handler   = dirty_writeback_centisecs_handler,
-       },
-       {
-               .procname       = "dirty_expire_centisecs",
-               .data           = &dirty_expire_interval,
-               .maxlen         = sizeof(dirty_expire_interval),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-       },
-       {
-               .procname       = "dirtytime_expire_seconds",
-               .data           = &dirtytime_expire_interval,
-               .maxlen         = sizeof(dirtytime_expire_interval),
-               .mode           = 0644,
-               .proc_handler   = dirtytime_interval_handler,
-               .extra1         = SYSCTL_ZERO,
-       },
-       {
-               .procname       = "swappiness",
-               .data           = &vm_swappiness,
-               .maxlen         = sizeof(vm_swappiness),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &one_hundred,
-       },
-#ifdef CONFIG_HUGETLB_PAGE
-       {
-               .procname       = "nr_hugepages",
-               .data           = NULL,
-               .maxlen         = sizeof(unsigned long),
-               .mode           = 0644,
-               .proc_handler   = hugetlb_sysctl_handler,
-       },
-#ifdef CONFIG_NUMA
-       {
-               .procname       = "nr_hugepages_mempolicy",
-               .data           = NULL,
-               .maxlen         = sizeof(unsigned long),
-               .mode           = 0644,
-               .proc_handler   = &hugetlb_mempolicy_sysctl_handler,
-       },
-       {
-               .procname               = "numa_stat",
-               .data                   = &sysctl_vm_numa_stat,
-               .maxlen                 = sizeof(int),
-               .mode                   = 0644,
-               .proc_handler   = sysctl_vm_numa_stat_handler,
-               .extra1                 = SYSCTL_ZERO,
-               .extra2                 = SYSCTL_ONE,
-       },
-#endif
-        {
-               .procname       = "hugetlb_shm_group",
-               .data           = &sysctl_hugetlb_shm_group,
-               .maxlen         = sizeof(gid_t),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-        },
-       {
-               .procname       = "nr_overcommit_hugepages",
-               .data           = NULL,
-               .maxlen         = sizeof(unsigned long),
-               .mode           = 0644,
-               .proc_handler   = hugetlb_overcommit_handler,
-       },
-#endif
-       {
-               .procname       = "lowmem_reserve_ratio",
-               .data           = &sysctl_lowmem_reserve_ratio,
-               .maxlen         = sizeof(sysctl_lowmem_reserve_ratio),
-               .mode           = 0644,
-               .proc_handler   = lowmem_reserve_ratio_sysctl_handler,
-       },
-       {
-               .procname       = "drop_caches",
-               .data           = &sysctl_drop_caches,
-               .maxlen         = sizeof(int),
-               .mode           = 0200,
-               .proc_handler   = drop_caches_sysctl_handler,
-               .extra1         = SYSCTL_ONE,
-               .extra2         = &four,
-       },
-#ifdef CONFIG_COMPACTION
-       {
-               .procname       = "compact_memory",
-               .data           = &sysctl_compact_memory,
-               .maxlen         = sizeof(int),
-               .mode           = 0200,
-               .proc_handler   = sysctl_compaction_handler,
-       },
-       {
-               .procname       = "extfrag_threshold",
-               .data           = &sysctl_extfrag_threshold,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &min_extfrag_threshold,
-               .extra2         = &max_extfrag_threshold,
-       },
-       {
-               .procname       = "compact_unevictable_allowed",
-               .data           = &sysctl_compact_unevictable_allowed,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax_warn_RT_change,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-
-#endif /* CONFIG_COMPACTION */
-       {
-               .procname       = "min_free_kbytes",
-               .data           = &min_free_kbytes,
-               .maxlen         = sizeof(min_free_kbytes),
-               .mode           = 0644,
-               .proc_handler   = min_free_kbytes_sysctl_handler,
-               .extra1         = SYSCTL_ZERO,
-       },
-       {
-               .procname       = "watermark_boost_factor",
-               .data           = &watermark_boost_factor,
-               .maxlen         = sizeof(watermark_boost_factor),
-               .mode           = 0644,
-               .proc_handler   = watermark_boost_factor_sysctl_handler,
-               .extra1         = SYSCTL_ZERO,
-       },
-       {
-               .procname       = "watermark_scale_factor",
-               .data           = &watermark_scale_factor,
-               .maxlen         = sizeof(watermark_scale_factor),
-               .mode           = 0644,
-               .proc_handler   = watermark_scale_factor_sysctl_handler,
-               .extra1         = SYSCTL_ONE,
-               .extra2         = &one_thousand,
-       },
-       {
-               .procname       = "percpu_pagelist_fraction",
-               .data           = &percpu_pagelist_fraction,
-               .maxlen         = sizeof(percpu_pagelist_fraction),
-               .mode           = 0644,
-               .proc_handler   = percpu_pagelist_fraction_sysctl_handler,
-               .extra1         = SYSCTL_ZERO,
-       },
-#ifdef CONFIG_MMU
-       {
-               .procname       = "max_map_count",
-               .data           = &sysctl_max_map_count,
-               .maxlen         = sizeof(sysctl_max_map_count),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-       },
-#else
-       {
-               .procname       = "nr_trim_pages",
-               .data           = &sysctl_nr_trim_pages,
-               .maxlen         = sizeof(sysctl_nr_trim_pages),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-       },
-#endif
-       {
-               .procname       = "laptop_mode",
-               .data           = &laptop_mode,
-               .maxlen         = sizeof(laptop_mode),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_jiffies,
-       },
-       {
-               .procname       = "block_dump",
-               .data           = &block_dump,
-               .maxlen         = sizeof(block_dump),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-               .extra1         = SYSCTL_ZERO,
-       },
-       {
-               .procname       = "vfs_cache_pressure",
-               .data           = &sysctl_vfs_cache_pressure,
-               .maxlen         = sizeof(sysctl_vfs_cache_pressure),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-               .extra1         = SYSCTL_ZERO,
-       },
-#if defined(HAVE_ARCH_PICK_MMAP_LAYOUT) || \
-    defined(CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT)
-       {
-               .procname       = "legacy_va_layout",
-               .data           = &sysctl_legacy_va_layout,
-               .maxlen         = sizeof(sysctl_legacy_va_layout),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-               .extra1         = SYSCTL_ZERO,
-       },
-#endif
-#ifdef CONFIG_NUMA
-       {
-               .procname       = "zone_reclaim_mode",
-               .data           = &node_reclaim_mode,
-               .maxlen         = sizeof(node_reclaim_mode),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-               .extra1         = SYSCTL_ZERO,
-       },
-       {
-               .procname       = "min_unmapped_ratio",
-               .data           = &sysctl_min_unmapped_ratio,
-               .maxlen         = sizeof(sysctl_min_unmapped_ratio),
-               .mode           = 0644,
-               .proc_handler   = sysctl_min_unmapped_ratio_sysctl_handler,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &one_hundred,
-       },
-       {
-               .procname       = "min_slab_ratio",
-               .data           = &sysctl_min_slab_ratio,
-               .maxlen         = sizeof(sysctl_min_slab_ratio),
-               .mode           = 0644,
-               .proc_handler   = sysctl_min_slab_ratio_sysctl_handler,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &one_hundred,
-       },
-#endif
-#ifdef CONFIG_SMP
-       {
-               .procname       = "stat_interval",
-               .data           = &sysctl_stat_interval,
-               .maxlen         = sizeof(sysctl_stat_interval),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_jiffies,
-       },
-       {
-               .procname       = "stat_refresh",
-               .data           = NULL,
-               .maxlen         = 0,
-               .mode           = 0600,
-               .proc_handler   = vmstat_refresh,
-       },
-#endif
-#ifdef CONFIG_MMU
-       {
-               .procname       = "mmap_min_addr",
-               .data           = &dac_mmap_min_addr,
-               .maxlen         = sizeof(unsigned long),
-               .mode           = 0644,
-               .proc_handler   = mmap_min_addr_handler,
-       },
-#endif
-#ifdef CONFIG_NUMA
-       {
-               .procname       = "numa_zonelist_order",
-               .data           = &numa_zonelist_order,
-               .maxlen         = NUMA_ZONELIST_ORDER_LEN,
-               .mode           = 0644,
-               .proc_handler   = numa_zonelist_order_handler,
-       },
-#endif
-#if (defined(CONFIG_X86_32) && !defined(CONFIG_UML))|| \
-   (defined(CONFIG_SUPERH) && defined(CONFIG_VSYSCALL))
-       {
-               .procname       = "vdso_enabled",
-#ifdef CONFIG_X86_32
-               .data           = &vdso32_enabled,
-               .maxlen         = sizeof(vdso32_enabled),
-#else
-               .data           = &vdso_enabled,
-               .maxlen         = sizeof(vdso_enabled),
-#endif
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-               .extra1         = SYSCTL_ZERO,
-       },
-#endif
-#ifdef CONFIG_HIGHMEM
-       {
-               .procname       = "highmem_is_dirtyable",
-               .data           = &vm_highmem_is_dirtyable,
-               .maxlen         = sizeof(vm_highmem_is_dirtyable),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif
-#ifdef CONFIG_MEMORY_FAILURE
-       {
-               .procname       = "memory_failure_early_kill",
-               .data           = &sysctl_memory_failure_early_kill,
-               .maxlen         = sizeof(sysctl_memory_failure_early_kill),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-       {
-               .procname       = "memory_failure_recovery",
-               .data           = &sysctl_memory_failure_recovery,
-               .maxlen         = sizeof(sysctl_memory_failure_recovery),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif
-       {
-               .procname       = "user_reserve_kbytes",
-               .data           = &sysctl_user_reserve_kbytes,
-               .maxlen         = sizeof(sysctl_user_reserve_kbytes),
-               .mode           = 0644,
-               .proc_handler   = proc_doulongvec_minmax,
-       },
-       {
-               .procname       = "admin_reserve_kbytes",
-               .data           = &sysctl_admin_reserve_kbytes,
-               .maxlen         = sizeof(sysctl_admin_reserve_kbytes),
-               .mode           = 0644,
-               .proc_handler   = proc_doulongvec_minmax,
-       },
-#ifdef CONFIG_HAVE_ARCH_MMAP_RND_BITS
-       {
-               .procname       = "mmap_rnd_bits",
-               .data           = &mmap_rnd_bits,
-               .maxlen         = sizeof(mmap_rnd_bits),
-               .mode           = 0600,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = (void *)&mmap_rnd_bits_min,
-               .extra2         = (void *)&mmap_rnd_bits_max,
-       },
-#endif
-#ifdef CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS
-       {
-               .procname       = "mmap_rnd_compat_bits",
-               .data           = &mmap_rnd_compat_bits,
-               .maxlen         = sizeof(mmap_rnd_compat_bits),
-               .mode           = 0600,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = (void *)&mmap_rnd_compat_bits_min,
-               .extra2         = (void *)&mmap_rnd_compat_bits_max,
-       },
-#endif
-#ifdef CONFIG_USERFAULTFD
-       {
-               .procname       = "unprivileged_userfaultfd",
-               .data           = &sysctl_unprivileged_userfaultfd,
-               .maxlen         = sizeof(sysctl_unprivileged_userfaultfd),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif
-       { }
-};
-
-static struct ctl_table fs_table[] = {
-       {
-               .procname       = "inode-nr",
-               .data           = &inodes_stat,
-               .maxlen         = 2*sizeof(long),
-               .mode           = 0444,
-               .proc_handler   = proc_nr_inodes,
-       },
-       {
-               .procname       = "inode-state",
-               .data           = &inodes_stat,
-               .maxlen         = 7*sizeof(long),
-               .mode           = 0444,
-               .proc_handler   = proc_nr_inodes,
-       },
-       {
-               .procname       = "file-nr",
-               .data           = &files_stat,
-               .maxlen         = sizeof(files_stat),
-               .mode           = 0444,
-               .proc_handler   = proc_nr_files,
-       },
-       {
-               .procname       = "file-max",
-               .data           = &files_stat.max_files,
-               .maxlen         = sizeof(files_stat.max_files),
-               .mode           = 0644,
-               .proc_handler   = proc_doulongvec_minmax,
-               .extra1         = &zero_ul,
-               .extra2         = &long_max,
-       },
-       {
-               .procname       = "nr_open",
-               .data           = &sysctl_nr_open,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &sysctl_nr_open_min,
-               .extra2         = &sysctl_nr_open_max,
-       },
-       {
-               .procname       = "dentry-state",
-               .data           = &dentry_stat,
-               .maxlen         = 6*sizeof(long),
-               .mode           = 0444,
-               .proc_handler   = proc_nr_dentry,
-       },
-       {
-               .procname       = "overflowuid",
-               .data           = &fs_overflowuid,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &minolduid,
-               .extra2         = &maxolduid,
-       },
-       {
-               .procname       = "overflowgid",
-               .data           = &fs_overflowgid,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &minolduid,
-               .extra2         = &maxolduid,
-       },
-#ifdef CONFIG_FILE_LOCKING
-       {
-               .procname       = "leases-enable",
-               .data           = &leases_enable,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#ifdef CONFIG_DNOTIFY
-       {
-               .procname       = "dir-notify-enable",
-               .data           = &dir_notify_enable,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#ifdef CONFIG_MMU
-#ifdef CONFIG_FILE_LOCKING
-       {
-               .procname       = "lease-break-time",
-               .data           = &lease_break_time,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec,
-       },
-#endif
-#ifdef CONFIG_AIO
-       {
-               .procname       = "aio-nr",
-               .data           = &aio_nr,
-               .maxlen         = sizeof(aio_nr),
-               .mode           = 0444,
-               .proc_handler   = proc_doulongvec_minmax,
-       },
-       {
-               .procname       = "aio-max-nr",
-               .data           = &aio_max_nr,
-               .maxlen         = sizeof(aio_max_nr),
-               .mode           = 0644,
-               .proc_handler   = proc_doulongvec_minmax,
-       },
-#endif /* CONFIG_AIO */
-#ifdef CONFIG_INOTIFY_USER
-       {
-               .procname       = "inotify",
-               .mode           = 0555,
-               .child          = inotify_table,
-       },
-#endif 
-#ifdef CONFIG_EPOLL
-       {
-               .procname       = "epoll",
-               .mode           = 0555,
-               .child          = epoll_table,
-       },
-#endif
-#endif
-       {
-               .procname       = "protected_symlinks",
-               .data           = &sysctl_protected_symlinks,
-               .maxlen         = sizeof(int),
-               .mode           = 0600,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-       {
-               .procname       = "protected_hardlinks",
-               .data           = &sysctl_protected_hardlinks,
-               .maxlen         = sizeof(int),
-               .mode           = 0600,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-       {
-               .procname       = "protected_fifos",
-               .data           = &sysctl_protected_fifos,
-               .maxlen         = sizeof(int),
-               .mode           = 0600,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &two,
-       },
-       {
-               .procname       = "protected_regular",
-               .data           = &sysctl_protected_regular,
-               .maxlen         = sizeof(int),
-               .mode           = 0600,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &two,
-       },
-       {
-               .procname       = "suid_dumpable",
-               .data           = &suid_dumpable,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax_coredump,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = &two,
-       },
-#if defined(CONFIG_BINFMT_MISC) || defined(CONFIG_BINFMT_MISC_MODULE)
-       {
-               .procname       = "binfmt_misc",
-               .mode           = 0555,
-               .child          = sysctl_mount_point,
-       },
-#endif
-       {
-               .procname       = "pipe-max-size",
-               .data           = &pipe_max_size,
-               .maxlen         = sizeof(pipe_max_size),
-               .mode           = 0644,
-               .proc_handler   = proc_dopipe_max_size,
-       },
-       {
-               .procname       = "pipe-user-pages-hard",
-               .data           = &pipe_user_pages_hard,
-               .maxlen         = sizeof(pipe_user_pages_hard),
-               .mode           = 0644,
-               .proc_handler   = proc_doulongvec_minmax,
-       },
-       {
-               .procname       = "pipe-user-pages-soft",
-               .data           = &pipe_user_pages_soft,
-               .maxlen         = sizeof(pipe_user_pages_soft),
-               .mode           = 0644,
-               .proc_handler   = proc_doulongvec_minmax,
-       },
-       {
-               .procname       = "mount-max",
-               .data           = &sysctl_mount_max,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = SYSCTL_ONE,
-       },
-       { }
-};
-
-static struct ctl_table debug_table[] = {
-#ifdef CONFIG_SYSCTL_EXCEPTION_TRACE
-       {
-               .procname       = "exception-trace",
-               .data           = &show_unhandled_signals,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec
-       },
-#endif
-#if defined(CONFIG_OPTPROBES)
-       {
-               .procname       = "kprobes-optimization",
-               .data           = &sysctl_kprobes_optimization,
-               .maxlen         = sizeof(int),
-               .mode           = 0644,
-               .proc_handler   = proc_kprobes_optimization_handler,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
-       },
-#endif
-       { }
-};
-
-static struct ctl_table dev_table[] = {
-       { }
-};
-
-int __init sysctl_init(void)
-{
-       struct ctl_table_header *hdr;
-
-       hdr = register_sysctl_table(sysctl_base_table);
-       kmemleak_not_leak(hdr);
-       return 0;
-}
-
-#endif /* CONFIG_SYSCTL */
-
-/*
- * /proc/sys support
- */
-
+
+/*
+ * /proc/sys support
+ */
+
 #ifdef CONFIG_PROC_SYSCTL
 
 static int _proc_do_string(char *data, int maxlen, int write,
-                          char __user *buffer,
-                          size_t *lenp, loff_t *ppos)
+               char *buffer, size_t *lenp, loff_t *ppos)
 {
        size_t len;
-       char __user *p;
-       char c;
+       char c, *p;
 
        if (!data || !maxlen || !*lenp) {
                *lenp = 0;
@@ -2007,8 +270,7 @@ static int _proc_do_string(char *data, int maxlen, int write,
                *ppos += *lenp;
                p = buffer;
                while ((p - buffer) < *lenp && len < maxlen - 1) {
-                       if (get_user(c, p++))
-                               return -EFAULT;
+                       c = *(p++);
                        if (c == 0 || c == '\n')
                                break;
                        data[len++] = c;
@@ -2030,11 +292,9 @@ static int _proc_do_string(char *data, int maxlen, int write,
                if (len > *lenp)
                        len = *lenp;
                if (len)
-                       if (copy_to_user(buffer, data, len))
-                               return -EFAULT;
+                       memcpy(buffer, data, len);
                if (len < *lenp) {
-                       if (put_user('\n', buffer + len))
-                               return -EFAULT;
+                       buffer[len] = '\n';
                        len++;
                }
                *lenp = len;
@@ -2095,13 +355,13 @@ static bool proc_first_pos_non_zero_ignore(loff_t *ppos,
  * Returns 0 on success.
  */
 int proc_dostring(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos)
+                 void *buffer, size_t *lenp, loff_t *ppos)
 {
        if (write)
                proc_first_pos_non_zero_ignore(ppos, table);
 
-       return _proc_do_string((char *)(table->data), table->maxlen, write,
-                              (char __user *)buffer, lenp, ppos);
+       return _proc_do_string(table->data, table->maxlen, write, buffer, lenp,
+                       ppos);
 }
 
 static size_t proc_skip_spaces(char **buf)
@@ -2232,11 +492,10 @@ static int proc_get_long(char **buf, size_t *size,
  * @val: the integer to be converted
  * @neg: sign of the number, %TRUE for negative
  *
- * In case of success %0 is returned and @buf and @size are updated with
- * the amount of bytes written.
+ * In case of success @buf and @size are updated with the amount of bytes
+ * written.
  */
-static int proc_put_long(void __user **buf, size_t *size, unsigned long val,
-                         bool neg)
+static void proc_put_long(void **buf, size_t *size, unsigned long val, bool neg)
 {
        int len;
        char tmp[TMPBUFLEN], *p = tmp;
@@ -2245,24 +504,22 @@ static int proc_put_long(void __user **buf, size_t *size, unsigned long val,
        len = strlen(tmp);
        if (len > *size)
                len = *size;
-       if (copy_to_user(*buf, tmp, len))
-               return -EFAULT;
+       memcpy(*buf, tmp, len);
        *size -= len;
        *buf += len;
-       return 0;
 }
 #undef TMPBUFLEN
 
-static int proc_put_char(void __user **buf, size_t *size, char c)
+static void proc_put_char(void **buf, size_t *size, char c)
 {
        if (*size) {
-               char __user **buffer = (char __user **)buf;
-               if (put_user(c, *buffer))
-                       return -EFAULT;
-               (*size)--, (*buffer)++;
+               char **buffer = (char **)buf;
+               **buffer = c;
+
+               (*size)--;
+               (*buffer)++;
                *buf = *buffer;
        }
-       return 0;
 }
 
 static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
@@ -2310,7 +567,7 @@ static int do_proc_douintvec_conv(unsigned long *lvalp,
 static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
 
 static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
-                 int write, void __user *buffer,
+                 int write, void *buffer,
                  size_t *lenp, loff_t *ppos,
                  int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
                              int write, void *data),
@@ -2318,7 +575,7 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
 {
        int *i, vleft, first = 1, err = 0;
        size_t left;
-       char *kbuf = NULL, *p;
+       char *p;
        
        if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) {
                *lenp = 0;
@@ -2338,9 +595,7 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
 
                if (left > PAGE_SIZE - 1)
                        left = PAGE_SIZE - 1;
-               p = kbuf = memdup_user_nul(buffer, left);
-               if (IS_ERR(kbuf))
-                       return PTR_ERR(kbuf);
+               p = buffer;
        }
 
        for (; left && vleft--; i++, first=0) {
@@ -2367,24 +622,17 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
                                break;
                        }
                        if (!first)
-                               err = proc_put_char(&buffer, &left, '\t');
-                       if (err)
-                               break;
-                       err = proc_put_long(&buffer, &left, lval, neg);
-                       if (err)
-                               break;
+                               proc_put_char(&buffer, &left, '\t');
+                       proc_put_long(&buffer, &left, lval, neg);
                }
        }
 
        if (!write && !first && left && !err)
-               err = proc_put_char(&buffer, &left, '\n');
+               proc_put_char(&buffer, &left, '\n');
        if (write && !err && left)
                left -= proc_skip_spaces(&p);
-       if (write) {
-               kfree(kbuf);
-               if (first)
-                       return err ? : -EINVAL;
-       }
+       if (write && first)
+               return err ? : -EINVAL;
        *lenp -= left;
 out:
        *ppos += *lenp;
@@ -2392,7 +640,7 @@ out:
 }
 
 static int do_proc_dointvec(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos,
+                 void *buffer, size_t *lenp, loff_t *ppos,
                  int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
                              int write, void *data),
                  void *data)
@@ -2403,7 +651,7 @@ static int do_proc_dointvec(struct ctl_table *table, int write,
 
 static int do_proc_douintvec_w(unsigned int *tbl_data,
                               struct ctl_table *table,
-                              void __user *buffer,
+                              void *buffer,
                               size_t *lenp, loff_t *ppos,
                               int (*conv)(unsigned long *lvalp,
                                           unsigned int *valp,
@@ -2414,7 +662,7 @@ static int do_proc_douintvec_w(unsigned int *tbl_data,
        int err = 0;
        size_t left;
        bool neg;
-       char *kbuf = NULL, *p;
+       char *p = buffer;
 
        left = *lenp;
 
@@ -2424,10 +672,6 @@ static int do_proc_douintvec_w(unsigned int *tbl_data,
        if (left > PAGE_SIZE - 1)
                left = PAGE_SIZE - 1;
 
-       p = kbuf = memdup_user_nul(buffer, left);
-       if (IS_ERR(kbuf))
-               return -EINVAL;
-
        left -= proc_skip_spaces(&p);
        if (!left) {
                err = -EINVAL;
@@ -2451,7 +695,6 @@ static int do_proc_douintvec_w(unsigned int *tbl_data,
                left -= proc_skip_spaces(&p);
 
 out_free:
-       kfree(kbuf);
        if (err)
                return -EINVAL;
 
@@ -2463,7 +706,7 @@ bail_early:
        return err;
 }
 
-static int do_proc_douintvec_r(unsigned int *tbl_data, void __user *buffer,
+static int do_proc_douintvec_r(unsigned int *tbl_data, void *buffer,
                               size_t *lenp, loff_t *ppos,
                               int (*conv)(unsigned long *lvalp,
                                           unsigned int *valp,
@@ -2481,11 +724,11 @@ static int do_proc_douintvec_r(unsigned int *tbl_data, void __user *buffer,
                goto out;
        }
 
-       err = proc_put_long(&buffer, &left, lval, false);
-       if (err || !left)
+       proc_put_long(&buffer, &left, lval, false);
+       if (!left)
                goto out;
 
-       err = proc_put_char(&buffer, &left, '\n');
+       proc_put_char(&buffer, &left, '\n');
 
 out:
        *lenp -= left;
@@ -2495,7 +738,7 @@ out:
 }
 
 static int __do_proc_douintvec(void *tbl_data, struct ctl_table *table,
-                              int write, void __user *buffer,
+                              int write, void *buffer,
                               size_t *lenp, loff_t *ppos,
                               int (*conv)(unsigned long *lvalp,
                                           unsigned int *valp,
@@ -2531,7 +774,7 @@ static int __do_proc_douintvec(void *tbl_data, struct ctl_table *table,
 }
 
 static int do_proc_douintvec(struct ctl_table *table, int write,
-                            void __user *buffer, size_t *lenp, loff_t *ppos,
+                            void *buffer, size_t *lenp, loff_t *ppos,
                             int (*conv)(unsigned long *lvalp,
                                         unsigned int *valp,
                                         int write, void *data),
@@ -2554,16 +797,15 @@ static int do_proc_douintvec(struct ctl_table *table, int write,
  *
  * Returns 0 on success.
  */
-int proc_dointvec(struct ctl_table *table, int write,
-                    void __user *buffer, size_t *lenp, loff_t *ppos)
+int proc_dointvec(struct ctl_table *table, int write, void *buffer,
+                 size_t *lenp, loff_t *ppos)
 {
        return do_proc_dointvec(table, write, buffer, lenp, ppos, NULL, NULL);
 }
 
 #ifdef CONFIG_COMPACTION
 static int proc_dointvec_minmax_warn_RT_change(struct ctl_table *table,
-                                              int write, void __user *buffer,
-                                              size_t *lenp, loff_t *ppos)
+               int write, void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret, old;
 
@@ -2595,8 +837,8 @@ static int proc_dointvec_minmax_warn_RT_change(struct ctl_table *table,
  *
  * Returns 0 on success.
  */
-int proc_douintvec(struct ctl_table *table, int write,
-                    void __user *buffer, size_t *lenp, loff_t *ppos)
+int proc_douintvec(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos)
 {
        return do_proc_douintvec(table, write, buffer, lenp, ppos,
                                 do_proc_douintvec_conv, NULL);
@@ -2607,7 +849,7 @@ int proc_douintvec(struct ctl_table *table, int write,
  * This means we can safely use a temporary.
  */
 static int proc_taint(struct ctl_table *table, int write,
-                              void __user *buffer, size_t *lenp, loff_t *ppos)
+                              void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table t;
        unsigned long tmptaint = get_taint();
@@ -2639,7 +881,7 @@ static int proc_taint(struct ctl_table *table, int write,
 
 #ifdef CONFIG_PRINTK
 static int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        if (write && !capable(CAP_SYS_ADMIN))
                return -EPERM;
@@ -2705,7 +947,7 @@ static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp,
  * Returns 0 on success or -EINVAL on write when the range check fails.
  */
 int proc_dointvec_minmax(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos)
+                 void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct do_proc_dointvec_minmax_conv_param param = {
                .min = (int *) table->extra1,
@@ -2774,7 +1016,7 @@ static int do_proc_douintvec_minmax_conv(unsigned long *lvalp,
  * Returns 0 on success or -ERANGE on write when the range check fails.
  */
 int proc_douintvec_minmax(struct ctl_table *table, int write,
-                         void __user *buffer, size_t *lenp, loff_t *ppos)
+                         void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct do_proc_douintvec_minmax_conv_param param = {
                .min = (unsigned int *) table->extra1,
@@ -2805,7 +1047,7 @@ static int do_proc_dopipe_max_size_conv(unsigned long *lvalp,
 }
 
 static int proc_dopipe_max_size(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        return do_proc_douintvec(table, write, buffer, lenp, ppos,
                                 do_proc_dopipe_max_size_conv, NULL);
@@ -2826,7 +1068,7 @@ static void validate_coredump_safety(void)
 }
 
 static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int error = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
        if (!error)
@@ -2836,7 +1078,7 @@ static int proc_dointvec_minmax_coredump(struct ctl_table *table, int write,
 
 #ifdef CONFIG_COREDUMP
 static int proc_dostring_coredump(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos)
+                 void *buffer, size_t *lenp, loff_t *ppos)
 {
        int error = proc_dostring(table, write, buffer, lenp, ppos);
        if (!error)
@@ -2847,7 +1089,7 @@ static int proc_dostring_coredump(struct ctl_table *table, int write,
 
 #ifdef CONFIG_MAGIC_SYSRQ
 static int sysrq_sysctl_handler(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int tmp, ret;
 
@@ -2865,16 +1107,14 @@ static int sysrq_sysctl_handler(struct ctl_table *table, int write,
 }
 #endif
 
-static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write,
-                                    void __user *buffer,
-                                    size_t *lenp, loff_t *ppos,
-                                    unsigned long convmul,
-                                    unsigned long convdiv)
+static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table,
+               int write, void *buffer, size_t *lenp, loff_t *ppos,
+               unsigned long convmul, unsigned long convdiv)
 {
        unsigned long *i, *min, *max;
        int vleft, first = 1, err = 0;
        size_t left;
-       char *kbuf = NULL, *p;
+       char *p;
 
        if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
                *lenp = 0;
@@ -2893,9 +1133,7 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
 
                if (left > PAGE_SIZE - 1)
                        left = PAGE_SIZE - 1;
-               p = kbuf = memdup_user_nul(buffer, left);
-               if (IS_ERR(kbuf))
-                       return PTR_ERR(kbuf);
+               p = buffer;
        }
 
        for (; left && vleft--; i++, first = 0) {
@@ -2923,26 +1161,18 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
                        *i = val;
                } else {
                        val = convdiv * (*i) / convmul;
-                       if (!first) {
-                               err = proc_put_char(&buffer, &left, '\t');
-                               if (err)
-                                       break;
-                       }
-                       err = proc_put_long(&buffer, &left, val, false);
-                       if (err)
-                               break;
+                       if (!first)
+                               proc_put_char(&buffer, &left, '\t');
+                       proc_put_long(&buffer, &left, val, false);
                }
        }
 
        if (!write && !first && left && !err)
-               err = proc_put_char(&buffer, &left, '\n');
+               proc_put_char(&buffer, &left, '\n');
        if (write && !err)
                left -= proc_skip_spaces(&p);
-       if (write) {
-               kfree(kbuf);
-               if (first)
-                       return err ? : -EINVAL;
-       }
+       if (write && first)
+               return err ? : -EINVAL;
        *lenp -= left;
 out:
        *ppos += *lenp;
@@ -2950,10 +1180,8 @@ out:
 }
 
 static int do_proc_doulongvec_minmax(struct ctl_table *table, int write,
-                                    void __user *buffer,
-                                    size_t *lenp, loff_t *ppos,
-                                    unsigned long convmul,
-                                    unsigned long convdiv)
+               void *buffer, size_t *lenp, loff_t *ppos, unsigned long convmul,
+               unsigned long convdiv)
 {
        return __do_proc_doulongvec_minmax(table->data, table, write,
                        buffer, lenp, ppos, convmul, convdiv);
@@ -2976,7 +1204,7 @@ static int do_proc_doulongvec_minmax(struct ctl_table *table, int write,
  * Returns 0 on success.
  */
 int proc_doulongvec_minmax(struct ctl_table *table, int write,
-                          void __user *buffer, size_t *lenp, loff_t *ppos)
+                          void *buffer, size_t *lenp, loff_t *ppos)
 {
     return do_proc_doulongvec_minmax(table, write, buffer, lenp, ppos, 1l, 1l);
 }
@@ -2999,8 +1227,7 @@ int proc_doulongvec_minmax(struct ctl_table *table, int write,
  * Returns 0 on success.
  */
 int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write,
-                                     void __user *buffer,
-                                     size_t *lenp, loff_t *ppos)
+                                     void *buffer, size_t *lenp, loff_t *ppos)
 {
     return do_proc_doulongvec_minmax(table, write, buffer,
                                     lenp, ppos, HZ, 1000l);
@@ -3094,7 +1321,7 @@ static int do_proc_dointvec_ms_jiffies_conv(bool *negp, unsigned long *lvalp,
  * Returns 0 on success.
  */
 int proc_dointvec_jiffies(struct ctl_table *table, int write,
-                         void __user *buffer, size_t *lenp, loff_t *ppos)
+                         void *buffer, size_t *lenp, loff_t *ppos)
 {
     return do_proc_dointvec(table,write,buffer,lenp,ppos,
                            do_proc_dointvec_jiffies_conv,NULL);
@@ -3116,7 +1343,7 @@ int proc_dointvec_jiffies(struct ctl_table *table, int write,
  * Returns 0 on success.
  */
 int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write,
-                                void __user *buffer, size_t *lenp, loff_t *ppos)
+                                void *buffer, size_t *lenp, loff_t *ppos)
 {
     return do_proc_dointvec(table,write,buffer,lenp,ppos,
                            do_proc_dointvec_userhz_jiffies_conv,NULL);
@@ -3138,15 +1365,15 @@ int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write,
  *
  * Returns 0 on success.
  */
-int proc_dointvec_ms_jiffies(struct ctl_table *table, int write,
-                            void __user *buffer, size_t *lenp, loff_t *ppos)
+int proc_dointvec_ms_jiffies(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos)
 {
        return do_proc_dointvec(table, write, buffer, lenp, ppos,
                                do_proc_dointvec_ms_jiffies_conv, NULL);
 }
 
-static int proc_do_cad_pid(struct ctl_table *table, int write,
-                          void __user *buffer, size_t *lenp, loff_t *ppos)
+static int proc_do_cad_pid(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos)
 {
        struct pid *new_pid;
        pid_t tmp;
@@ -3185,7 +1412,7 @@ static int proc_do_cad_pid(struct ctl_table *table, int write,
  * Returns 0 on success.
  */
 int proc_do_large_bitmap(struct ctl_table *table, int write,
-                        void __user *buffer, size_t *lenp, loff_t *ppos)
+                        void *buffer, size_t *lenp, loff_t *ppos)
 {
        int err = 0;
        bool first = 1;
@@ -3201,7 +1428,7 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
        }
 
        if (write) {
-               char *kbuf, *p;
+               char *p = buffer;
                size_t skipped = 0;
 
                if (left > PAGE_SIZE - 1) {
@@ -3210,15 +1437,9 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
                        skipped = *lenp - left;
                }
 
-               p = kbuf = memdup_user_nul(buffer, left);
-               if (IS_ERR(kbuf))
-                       return PTR_ERR(kbuf);
-
                tmp_bitmap = bitmap_zalloc(bitmap_len, GFP_KERNEL);
-               if (!tmp_bitmap) {
-                       kfree(kbuf);
+               if (!tmp_bitmap)
                        return -ENOMEM;
-               }
                proc_skip_char(&p, &left, '\n');
                while (!err && left) {
                        unsigned long val_a, val_b;
@@ -3282,7 +1503,6 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
                        first = 0;
                        proc_skip_char(&p, &left, '\n');
                }
-               kfree(kbuf);
                left += skipped;
        } else {
                unsigned long bit_a, bit_b = 0;
@@ -3294,27 +1514,17 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
                        bit_b = find_next_zero_bit(bitmap, bitmap_len,
                                                   bit_a + 1) - 1;
 
-                       if (!first) {
-                               err = proc_put_char(&buffer, &left, ',');
-                               if (err)
-                                       break;
-                       }
-                       err = proc_put_long(&buffer, &left, bit_a, false);
-                       if (err)
-                               break;
+                       if (!first)
+                               proc_put_char(&buffer, &left, ',');
+                       proc_put_long(&buffer, &left, bit_a, false);
                        if (bit_a != bit_b) {
-                               err = proc_put_char(&buffer, &left, '-');
-                               if (err)
-                                       break;
-                               err = proc_put_long(&buffer, &left, bit_b, false);
-                               if (err)
-                                       break;
+                               proc_put_char(&buffer, &left, '-');
+                               proc_put_long(&buffer, &left, bit_b, false);
                        }
 
                        first = 0; bit_b++;
                }
-               if (!err)
-                       err = proc_put_char(&buffer, &left, '\n');
+               proc_put_char(&buffer, &left, '\n');
        }
 
        if (!err) {
@@ -3328,113 +1538,1804 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
                *ppos += *lenp;
        }
 
-       bitmap_free(tmp_bitmap);
-       return err;
+       bitmap_free(tmp_bitmap);
+       return err;
+}
+
+#else /* CONFIG_PROC_SYSCTL */
+
+int proc_dostring(struct ctl_table *table, int write,
+                 void *buffer, size_t *lenp, loff_t *ppos)
+{
+       return -ENOSYS;
+}
+
+int proc_dointvec(struct ctl_table *table, int write,
+                 void *buffer, size_t *lenp, loff_t *ppos)
+{
+       return -ENOSYS;
+}
+
+int proc_douintvec(struct ctl_table *table, int write,
+                 void *buffer, size_t *lenp, loff_t *ppos)
+{
+       return -ENOSYS;
+}
+
+int proc_dointvec_minmax(struct ctl_table *table, int write,
+                   void *buffer, size_t *lenp, loff_t *ppos)
+{
+       return -ENOSYS;
 }
 
-#else /* CONFIG_PROC_SYSCTL */
+int proc_douintvec_minmax(struct ctl_table *table, int write,
+                         void *buffer, size_t *lenp, loff_t *ppos)
+{
+       return -ENOSYS;
+}
 
-int proc_dostring(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos)
+int proc_dointvec_jiffies(struct ctl_table *table, int write,
+                   void *buffer, size_t *lenp, loff_t *ppos)
 {
        return -ENOSYS;
 }
 
-int proc_dointvec(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos)
+int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write,
+                   void *buffer, size_t *lenp, loff_t *ppos)
 {
        return -ENOSYS;
 }
 
-int proc_douintvec(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos)
+int proc_dointvec_ms_jiffies(struct ctl_table *table, int write,
+                            void *buffer, size_t *lenp, loff_t *ppos)
 {
        return -ENOSYS;
 }
 
-int proc_dointvec_minmax(struct ctl_table *table, int write,
-                   void __user *buffer, size_t *lenp, loff_t *ppos)
+int proc_doulongvec_minmax(struct ctl_table *table, int write,
+                   void *buffer, size_t *lenp, loff_t *ppos)
 {
        return -ENOSYS;
 }
 
-int proc_douintvec_minmax(struct ctl_table *table, int write,
-                         void __user *buffer, size_t *lenp, loff_t *ppos)
+int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write,
+                                     void *buffer, size_t *lenp, loff_t *ppos)
+{
+       return -ENOSYS;
+}
+
+int proc_do_large_bitmap(struct ctl_table *table, int write,
+                        void *buffer, size_t *lenp, loff_t *ppos)
 {
        return -ENOSYS;
 }
 
-int proc_dointvec_jiffies(struct ctl_table *table, int write,
-                   void __user *buffer, size_t *lenp, loff_t *ppos)
-{
-       return -ENOSYS;
-}
+#endif /* CONFIG_PROC_SYSCTL */
+
+#if defined(CONFIG_SYSCTL)
+int proc_do_static_key(struct ctl_table *table, int write,
+                      void *buffer, size_t *lenp, loff_t *ppos)
+{
+       struct static_key *key = (struct static_key *)table->data;
+       static DEFINE_MUTEX(static_key_mutex);
+       int val, ret;
+       struct ctl_table tmp = {
+               .data   = &val,
+               .maxlen = sizeof(val),
+               .mode   = table->mode,
+               .extra1 = SYSCTL_ZERO,
+               .extra2 = SYSCTL_ONE,
+       };
+
+       if (write && !capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       mutex_lock(&static_key_mutex);
+       val = static_key_enabled(key);
+       ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+       if (write && !ret) {
+               if (val)
+                       static_key_enable(key);
+               else
+                       static_key_disable(key);
+       }
+       mutex_unlock(&static_key_mutex);
+       return ret;
+}
+
+static struct ctl_table kern_table[] = {
+       {
+               .procname       = "sched_child_runs_first",
+               .data           = &sysctl_sched_child_runs_first,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#ifdef CONFIG_SCHED_DEBUG
+       {
+               .procname       = "sched_min_granularity_ns",
+               .data           = &sysctl_sched_min_granularity,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = sched_proc_update_handler,
+               .extra1         = &min_sched_granularity_ns,
+               .extra2         = &max_sched_granularity_ns,
+       },
+       {
+               .procname       = "sched_latency_ns",
+               .data           = &sysctl_sched_latency,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = sched_proc_update_handler,
+               .extra1         = &min_sched_granularity_ns,
+               .extra2         = &max_sched_granularity_ns,
+       },
+       {
+               .procname       = "sched_wakeup_granularity_ns",
+               .data           = &sysctl_sched_wakeup_granularity,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = sched_proc_update_handler,
+               .extra1         = &min_wakeup_granularity_ns,
+               .extra2         = &max_wakeup_granularity_ns,
+       },
+#ifdef CONFIG_SMP
+       {
+               .procname       = "sched_tunable_scaling",
+               .data           = &sysctl_sched_tunable_scaling,
+               .maxlen         = sizeof(enum sched_tunable_scaling),
+               .mode           = 0644,
+               .proc_handler   = sched_proc_update_handler,
+               .extra1         = &min_sched_tunable_scaling,
+               .extra2         = &max_sched_tunable_scaling,
+       },
+       {
+               .procname       = "sched_migration_cost_ns",
+               .data           = &sysctl_sched_migration_cost,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "sched_nr_migrate",
+               .data           = &sysctl_sched_nr_migrate,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#ifdef CONFIG_SCHEDSTATS
+       {
+               .procname       = "sched_schedstats",
+               .data           = NULL,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = sysctl_schedstats,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif /* CONFIG_SCHEDSTATS */
+#endif /* CONFIG_SMP */
+#ifdef CONFIG_NUMA_BALANCING
+       {
+               .procname       = "numa_balancing_scan_delay_ms",
+               .data           = &sysctl_numa_balancing_scan_delay,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "numa_balancing_scan_period_min_ms",
+               .data           = &sysctl_numa_balancing_scan_period_min,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "numa_balancing_scan_period_max_ms",
+               .data           = &sysctl_numa_balancing_scan_period_max,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "numa_balancing_scan_size_mb",
+               .data           = &sysctl_numa_balancing_scan_size,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ONE,
+       },
+       {
+               .procname       = "numa_balancing",
+               .data           = NULL, /* filled in by handler */
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = sysctl_numa_balancing,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif /* CONFIG_NUMA_BALANCING */
+#endif /* CONFIG_SCHED_DEBUG */
+       {
+               .procname       = "sched_rt_period_us",
+               .data           = &sysctl_sched_rt_period,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = sched_rt_handler,
+       },
+       {
+               .procname       = "sched_rt_runtime_us",
+               .data           = &sysctl_sched_rt_runtime,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = sched_rt_handler,
+       },
+       {
+               .procname       = "sched_rr_timeslice_ms",
+               .data           = &sysctl_sched_rr_timeslice,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = sched_rr_handler,
+       },
+#ifdef CONFIG_UCLAMP_TASK
+       {
+               .procname       = "sched_util_clamp_min",
+               .data           = &sysctl_sched_uclamp_util_min,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = sysctl_sched_uclamp_handler,
+       },
+       {
+               .procname       = "sched_util_clamp_max",
+               .data           = &sysctl_sched_uclamp_util_max,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = sysctl_sched_uclamp_handler,
+       },
+#endif
+#ifdef CONFIG_SCHED_AUTOGROUP
+       {
+               .procname       = "sched_autogroup_enabled",
+               .data           = &sysctl_sched_autogroup_enabled,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif
+#ifdef CONFIG_CFS_BANDWIDTH
+       {
+               .procname       = "sched_cfs_bandwidth_slice_us",
+               .data           = &sysctl_sched_cfs_bandwidth_slice,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ONE,
+       },
+#endif
+#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL)
+       {
+               .procname       = "sched_energy_aware",
+               .data           = &sysctl_sched_energy_aware,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = sched_energy_aware_handler,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif
+#ifdef CONFIG_PROVE_LOCKING
+       {
+               .procname       = "prove_locking",
+               .data           = &prove_locking,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#ifdef CONFIG_LOCK_STAT
+       {
+               .procname       = "lock_stat",
+               .data           = &lock_stat,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+       {
+               .procname       = "panic",
+               .data           = &panic_timeout,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#ifdef CONFIG_COREDUMP
+       {
+               .procname       = "core_uses_pid",
+               .data           = &core_uses_pid,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "core_pattern",
+               .data           = core_pattern,
+               .maxlen         = CORENAME_MAX_SIZE,
+               .mode           = 0644,
+               .proc_handler   = proc_dostring_coredump,
+       },
+       {
+               .procname       = "core_pipe_limit",
+               .data           = &core_pipe_limit,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#ifdef CONFIG_PROC_SYSCTL
+       {
+               .procname       = "tainted",
+               .maxlen         = sizeof(long),
+               .mode           = 0644,
+               .proc_handler   = proc_taint,
+       },
+       {
+               .procname       = "sysctl_writes_strict",
+               .data           = &sysctl_writes_strict,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &neg_one,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif
+#ifdef CONFIG_LATENCYTOP
+       {
+               .procname       = "latencytop",
+               .data           = &latencytop_enabled,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = sysctl_latencytop,
+       },
+#endif
+#ifdef CONFIG_BLK_DEV_INITRD
+       {
+               .procname       = "real-root-dev",
+               .data           = &real_root_dev,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+       {
+               .procname       = "print-fatal-signals",
+               .data           = &print_fatal_signals,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#ifdef CONFIG_SPARC
+       {
+               .procname       = "reboot-cmd",
+               .data           = reboot_command,
+               .maxlen         = 256,
+               .mode           = 0644,
+               .proc_handler   = proc_dostring,
+       },
+       {
+               .procname       = "stop-a",
+               .data           = &stop_a_enabled,
+               .maxlen         = sizeof (int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "scons-poweroff",
+               .data           = &scons_pwroff,
+               .maxlen         = sizeof (int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#ifdef CONFIG_SPARC64
+       {
+               .procname       = "tsb-ratio",
+               .data           = &sysctl_tsb_ratio,
+               .maxlen         = sizeof (int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#ifdef CONFIG_PARISC
+       {
+               .procname       = "soft-power",
+               .data           = &pwrsw_enabled,
+               .maxlen         = sizeof (int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_ALLOW
+       {
+               .procname       = "unaligned-trap",
+               .data           = &unaligned_enabled,
+               .maxlen         = sizeof (int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+       {
+               .procname       = "ctrl-alt-del",
+               .data           = &C_A_D,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#ifdef CONFIG_FUNCTION_TRACER
+       {
+               .procname       = "ftrace_enabled",
+               .data           = &ftrace_enabled,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = ftrace_enable_sysctl,
+       },
+#endif
+#ifdef CONFIG_STACK_TRACER
+       {
+               .procname       = "stack_tracer_enabled",
+               .data           = &stack_tracer_enabled,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = stack_trace_sysctl,
+       },
+#endif
+#ifdef CONFIG_TRACING
+       {
+               .procname       = "ftrace_dump_on_oops",
+               .data           = &ftrace_dump_on_oops,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "traceoff_on_warning",
+               .data           = &__disable_trace_on_warning,
+               .maxlen         = sizeof(__disable_trace_on_warning),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "tracepoint_printk",
+               .data           = &tracepoint_printk,
+               .maxlen         = sizeof(tracepoint_printk),
+               .mode           = 0644,
+               .proc_handler   = tracepoint_printk_sysctl,
+       },
+#endif
+#ifdef CONFIG_KEXEC_CORE
+       {
+               .procname       = "kexec_load_disabled",
+               .data           = &kexec_load_disabled,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               /* only handle a transition from default "0" to "1" */
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ONE,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif
+#ifdef CONFIG_MODULES
+       {
+               .procname       = "modprobe",
+               .data           = &modprobe_path,
+               .maxlen         = KMOD_PATH_LEN,
+               .mode           = 0644,
+               .proc_handler   = proc_dostring,
+       },
+       {
+               .procname       = "modules_disabled",
+               .data           = &modules_disabled,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               /* only handle a transition from default "0" to "1" */
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ONE,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif
+#ifdef CONFIG_UEVENT_HELPER
+       {
+               .procname       = "hotplug",
+               .data           = &uevent_helper,
+               .maxlen         = UEVENT_HELPER_PATH_LEN,
+               .mode           = 0644,
+               .proc_handler   = proc_dostring,
+       },
+#endif
+#ifdef CONFIG_CHR_DEV_SG
+       {
+               .procname       = "sg-big-buff",
+               .data           = &sg_big_buff,
+               .maxlen         = sizeof (int),
+               .mode           = 0444,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#ifdef CONFIG_BSD_PROCESS_ACCT
+       {
+               .procname       = "acct",
+               .data           = &acct_parm,
+               .maxlen         = 3*sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#ifdef CONFIG_MAGIC_SYSRQ
+       {
+               .procname       = "sysrq",
+               .data           = NULL,
+               .maxlen         = sizeof (int),
+               .mode           = 0644,
+               .proc_handler   = sysrq_sysctl_handler,
+       },
+#endif
+#ifdef CONFIG_PROC_SYSCTL
+       {
+               .procname       = "cad_pid",
+               .data           = NULL,
+               .maxlen         = sizeof (int),
+               .mode           = 0600,
+               .proc_handler   = proc_do_cad_pid,
+       },
+#endif
+       {
+               .procname       = "threads-max",
+               .data           = NULL,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = sysctl_max_threads,
+       },
+       {
+               .procname       = "random",
+               .mode           = 0555,
+               .child          = random_table,
+       },
+       {
+               .procname       = "usermodehelper",
+               .mode           = 0555,
+               .child          = usermodehelper_table,
+       },
+#ifdef CONFIG_FW_LOADER_USER_HELPER
+       {
+               .procname       = "firmware_config",
+               .mode           = 0555,
+               .child          = firmware_config_table,
+       },
+#endif
+       {
+               .procname       = "overflowuid",
+               .data           = &overflowuid,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &minolduid,
+               .extra2         = &maxolduid,
+       },
+       {
+               .procname       = "overflowgid",
+               .data           = &overflowgid,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &minolduid,
+               .extra2         = &maxolduid,
+       },
+#ifdef CONFIG_S390
+       {
+               .procname       = "userprocess_debug",
+               .data           = &show_unhandled_signals,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+       {
+               .procname       = "pid_max",
+               .data           = &pid_max,
+               .maxlen         = sizeof (int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &pid_max_min,
+               .extra2         = &pid_max_max,
+       },
+       {
+               .procname       = "panic_on_oops",
+               .data           = &panic_on_oops,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "panic_print",
+               .data           = &panic_print,
+               .maxlen         = sizeof(unsigned long),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+       },
+#if defined CONFIG_PRINTK
+       {
+               .procname       = "printk",
+               .data           = &console_loglevel,
+               .maxlen         = 4*sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "printk_ratelimit",
+               .data           = &printk_ratelimit_state.interval,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_jiffies,
+       },
+       {
+               .procname       = "printk_ratelimit_burst",
+               .data           = &printk_ratelimit_state.burst,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "printk_delay",
+               .data           = &printk_delay_msec,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &ten_thousand,
+       },
+       {
+               .procname       = "printk_devkmsg",
+               .data           = devkmsg_log_str,
+               .maxlen         = DEVKMSG_STR_MAX_SIZE,
+               .mode           = 0644,
+               .proc_handler   = devkmsg_sysctl_set_loglvl,
+       },
+       {
+               .procname       = "dmesg_restrict",
+               .data           = &dmesg_restrict,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax_sysadmin,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+       {
+               .procname       = "kptr_restrict",
+               .data           = &kptr_restrict,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax_sysadmin,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &two,
+       },
+#endif
+       {
+               .procname       = "ngroups_max",
+               .data           = &ngroups_max,
+               .maxlen         = sizeof (int),
+               .mode           = 0444,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "cap_last_cap",
+               .data           = (void *)&cap_last_cap,
+               .maxlen         = sizeof(int),
+               .mode           = 0444,
+               .proc_handler   = proc_dointvec,
+       },
+#if defined(CONFIG_LOCKUP_DETECTOR)
+       {
+               .procname       = "watchdog",
+               .data           = &watchdog_user_enabled,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_watchdog,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+       {
+               .procname       = "watchdog_thresh",
+               .data           = &watchdog_thresh,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_watchdog_thresh,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &sixty,
+       },
+       {
+               .procname       = "nmi_watchdog",
+               .data           = &nmi_watchdog_user_enabled,
+               .maxlen         = sizeof(int),
+               .mode           = NMI_WATCHDOG_SYSCTL_PERM,
+               .proc_handler   = proc_nmi_watchdog,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+       {
+               .procname       = "watchdog_cpumask",
+               .data           = &watchdog_cpumask_bits,
+               .maxlen         = NR_CPUS,
+               .mode           = 0644,
+               .proc_handler   = proc_watchdog_cpumask,
+       },
+#ifdef CONFIG_SOFTLOCKUP_DETECTOR
+       {
+               .procname       = "soft_watchdog",
+               .data           = &soft_watchdog_user_enabled,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_soft_watchdog,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+       {
+               .procname       = "softlockup_panic",
+               .data           = &softlockup_panic,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#ifdef CONFIG_SMP
+       {
+               .procname       = "softlockup_all_cpu_backtrace",
+               .data           = &sysctl_softlockup_all_cpu_backtrace,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif /* CONFIG_SMP */
+#endif
+#ifdef CONFIG_HARDLOCKUP_DETECTOR
+       {
+               .procname       = "hardlockup_panic",
+               .data           = &hardlockup_panic,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#ifdef CONFIG_SMP
+       {
+               .procname       = "hardlockup_all_cpu_backtrace",
+               .data           = &sysctl_hardlockup_all_cpu_backtrace,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif /* CONFIG_SMP */
+#endif
+#endif
+
+#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86)
+       {
+               .procname       = "unknown_nmi_panic",
+               .data           = &unknown_nmi_panic,
+               .maxlen         = sizeof (int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#if defined(CONFIG_X86)
+       {
+               .procname       = "panic_on_unrecovered_nmi",
+               .data           = &panic_on_unrecovered_nmi,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "panic_on_io_nmi",
+               .data           = &panic_on_io_nmi,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+       {
+               .procname       = "panic_on_stackoverflow",
+               .data           = &sysctl_panic_on_stackoverflow,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+       {
+               .procname       = "bootloader_type",
+               .data           = &bootloader_type,
+               .maxlen         = sizeof (int),
+               .mode           = 0444,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "bootloader_version",
+               .data           = &bootloader_version,
+               .maxlen         = sizeof (int),
+               .mode           = 0444,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "io_delay_type",
+               .data           = &io_delay_type,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#if defined(CONFIG_MMU)
+       {
+               .procname       = "randomize_va_space",
+               .data           = &randomize_va_space,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#if defined(CONFIG_S390) && defined(CONFIG_SMP)
+       {
+               .procname       = "spin_retry",
+               .data           = &spin_retry,
+               .maxlen         = sizeof (int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#if    defined(CONFIG_ACPI_SLEEP) && defined(CONFIG_X86)
+       {
+               .procname       = "acpi_video_flags",
+               .data           = &acpi_realmode_flags,
+               .maxlen         = sizeof (unsigned long),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+       },
+#endif
+#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_NO_WARN
+       {
+               .procname       = "ignore-unaligned-usertrap",
+               .data           = &no_unaligned_warning,
+               .maxlen         = sizeof (int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#ifdef CONFIG_IA64
+       {
+               .procname       = "unaligned-dump-stack",
+               .data           = &unaligned_dump_stack,
+               .maxlen         = sizeof (int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#ifdef CONFIG_DETECT_HUNG_TASK
+       {
+               .procname       = "hung_task_panic",
+               .data           = &sysctl_hung_task_panic,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+       {
+               .procname       = "hung_task_check_count",
+               .data           = &sysctl_hung_task_check_count,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+       },
+       {
+               .procname       = "hung_task_timeout_secs",
+               .data           = &sysctl_hung_task_timeout_secs,
+               .maxlen         = sizeof(unsigned long),
+               .mode           = 0644,
+               .proc_handler   = proc_dohung_task_timeout_secs,
+               .extra2         = &hung_task_timeout_max,
+       },
+       {
+               .procname       = "hung_task_check_interval_secs",
+               .data           = &sysctl_hung_task_check_interval_secs,
+               .maxlen         = sizeof(unsigned long),
+               .mode           = 0644,
+               .proc_handler   = proc_dohung_task_timeout_secs,
+               .extra2         = &hung_task_timeout_max,
+       },
+       {
+               .procname       = "hung_task_warnings",
+               .data           = &sysctl_hung_task_warnings,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &neg_one,
+       },
+#endif
+#ifdef CONFIG_RT_MUTEXES
+       {
+               .procname       = "max_lock_depth",
+               .data           = &max_lock_depth,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+       {
+               .procname       = "poweroff_cmd",
+               .data           = &poweroff_cmd,
+               .maxlen         = POWEROFF_CMD_PATH_LEN,
+               .mode           = 0644,
+               .proc_handler   = proc_dostring,
+       },
+#ifdef CONFIG_KEYS
+       {
+               .procname       = "keys",
+               .mode           = 0555,
+               .child          = key_sysctls,
+       },
+#endif
+#ifdef CONFIG_PERF_EVENTS
+       /*
+        * User-space scripts rely on the existence of this file
+        * as a feature check for perf_events being enabled.
+        *
+        * So it's an ABI, do not remove!
+        */
+       {
+               .procname       = "perf_event_paranoid",
+               .data           = &sysctl_perf_event_paranoid,
+               .maxlen         = sizeof(sysctl_perf_event_paranoid),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "perf_event_mlock_kb",
+               .data           = &sysctl_perf_event_mlock,
+               .maxlen         = sizeof(sysctl_perf_event_mlock),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "perf_event_max_sample_rate",
+               .data           = &sysctl_perf_event_sample_rate,
+               .maxlen         = sizeof(sysctl_perf_event_sample_rate),
+               .mode           = 0644,
+               .proc_handler   = perf_proc_update_handler,
+               .extra1         = SYSCTL_ONE,
+       },
+       {
+               .procname       = "perf_cpu_time_max_percent",
+               .data           = &sysctl_perf_cpu_time_max_percent,
+               .maxlen         = sizeof(sysctl_perf_cpu_time_max_percent),
+               .mode           = 0644,
+               .proc_handler   = perf_cpu_time_max_percent_handler,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &one_hundred,
+       },
+       {
+               .procname       = "perf_event_max_stack",
+               .data           = &sysctl_perf_event_max_stack,
+               .maxlen         = sizeof(sysctl_perf_event_max_stack),
+               .mode           = 0644,
+               .proc_handler   = perf_event_max_stack_handler,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &six_hundred_forty_kb,
+       },
+       {
+               .procname       = "perf_event_max_contexts_per_stack",
+               .data           = &sysctl_perf_event_max_contexts_per_stack,
+               .maxlen         = sizeof(sysctl_perf_event_max_contexts_per_stack),
+               .mode           = 0644,
+               .proc_handler   = perf_event_max_stack_handler,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &one_thousand,
+       },
+#endif
+       {
+               .procname       = "panic_on_warn",
+               .data           = &panic_on_warn,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
+       {
+               .procname       = "timer_migration",
+               .data           = &sysctl_timer_migration,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = timer_migration_handler,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif
+#ifdef CONFIG_BPF_SYSCALL
+       {
+               .procname       = "unprivileged_bpf_disabled",
+               .data           = &sysctl_unprivileged_bpf_disabled,
+               .maxlen         = sizeof(sysctl_unprivileged_bpf_disabled),
+               .mode           = 0644,
+               /* only handle a transition from default "0" to "1" */
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ONE,
+               .extra2         = SYSCTL_ONE,
+       },
+       {
+               .procname       = "bpf_stats_enabled",
+               .data           = &bpf_stats_enabled_key.key,
+               .maxlen         = sizeof(bpf_stats_enabled_key),
+               .mode           = 0644,
+               .proc_handler   = bpf_stats_handler,
+       },
+#endif
+#if defined(CONFIG_TREE_RCU)
+       {
+               .procname       = "panic_on_rcu_stall",
+               .data           = &sysctl_panic_on_rcu_stall,
+               .maxlen         = sizeof(sysctl_panic_on_rcu_stall),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif
+#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE
+       {
+               .procname       = "stack_erasing",
+               .data           = NULL,
+               .maxlen         = sizeof(int),
+               .mode           = 0600,
+               .proc_handler   = stack_erasing_sysctl,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif
+       { }
+};
 
-int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write,
-                   void __user *buffer, size_t *lenp, loff_t *ppos)
-{
-       return -ENOSYS;
-}
+static struct ctl_table vm_table[] = {
+       {
+               .procname       = "overcommit_memory",
+               .data           = &sysctl_overcommit_memory,
+               .maxlen         = sizeof(sysctl_overcommit_memory),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &two,
+       },
+       {
+               .procname       = "panic_on_oom",
+               .data           = &sysctl_panic_on_oom,
+               .maxlen         = sizeof(sysctl_panic_on_oom),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &two,
+       },
+       {
+               .procname       = "oom_kill_allocating_task",
+               .data           = &sysctl_oom_kill_allocating_task,
+               .maxlen         = sizeof(sysctl_oom_kill_allocating_task),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "oom_dump_tasks",
+               .data           = &sysctl_oom_dump_tasks,
+               .maxlen         = sizeof(sysctl_oom_dump_tasks),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+       {
+               .procname       = "overcommit_ratio",
+               .data           = &sysctl_overcommit_ratio,
+               .maxlen         = sizeof(sysctl_overcommit_ratio),
+               .mode           = 0644,
+               .proc_handler   = overcommit_ratio_handler,
+       },
+       {
+               .procname       = "overcommit_kbytes",
+               .data           = &sysctl_overcommit_kbytes,
+               .maxlen         = sizeof(sysctl_overcommit_kbytes),
+               .mode           = 0644,
+               .proc_handler   = overcommit_kbytes_handler,
+       },
+       {
+               .procname       = "page-cluster",
+               .data           = &page_cluster,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+       },
+       {
+               .procname       = "dirty_background_ratio",
+               .data           = &dirty_background_ratio,
+               .maxlen         = sizeof(dirty_background_ratio),
+               .mode           = 0644,
+               .proc_handler   = dirty_background_ratio_handler,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &one_hundred,
+       },
+       {
+               .procname       = "dirty_background_bytes",
+               .data           = &dirty_background_bytes,
+               .maxlen         = sizeof(dirty_background_bytes),
+               .mode           = 0644,
+               .proc_handler   = dirty_background_bytes_handler,
+               .extra1         = &one_ul,
+       },
+       {
+               .procname       = "dirty_ratio",
+               .data           = &vm_dirty_ratio,
+               .maxlen         = sizeof(vm_dirty_ratio),
+               .mode           = 0644,
+               .proc_handler   = dirty_ratio_handler,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &one_hundred,
+       },
+       {
+               .procname       = "dirty_bytes",
+               .data           = &vm_dirty_bytes,
+               .maxlen         = sizeof(vm_dirty_bytes),
+               .mode           = 0644,
+               .proc_handler   = dirty_bytes_handler,
+               .extra1         = &dirty_bytes_min,
+       },
+       {
+               .procname       = "dirty_writeback_centisecs",
+               .data           = &dirty_writeback_interval,
+               .maxlen         = sizeof(dirty_writeback_interval),
+               .mode           = 0644,
+               .proc_handler   = dirty_writeback_centisecs_handler,
+       },
+       {
+               .procname       = "dirty_expire_centisecs",
+               .data           = &dirty_expire_interval,
+               .maxlen         = sizeof(dirty_expire_interval),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+       },
+       {
+               .procname       = "dirtytime_expire_seconds",
+               .data           = &dirtytime_expire_interval,
+               .maxlen         = sizeof(dirtytime_expire_interval),
+               .mode           = 0644,
+               .proc_handler   = dirtytime_interval_handler,
+               .extra1         = SYSCTL_ZERO,
+       },
+       {
+               .procname       = "swappiness",
+               .data           = &vm_swappiness,
+               .maxlen         = sizeof(vm_swappiness),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &one_hundred,
+       },
+#ifdef CONFIG_HUGETLB_PAGE
+       {
+               .procname       = "nr_hugepages",
+               .data           = NULL,
+               .maxlen         = sizeof(unsigned long),
+               .mode           = 0644,
+               .proc_handler   = hugetlb_sysctl_handler,
+       },
+#ifdef CONFIG_NUMA
+       {
+               .procname       = "nr_hugepages_mempolicy",
+               .data           = NULL,
+               .maxlen         = sizeof(unsigned long),
+               .mode           = 0644,
+               .proc_handler   = &hugetlb_mempolicy_sysctl_handler,
+       },
+       {
+               .procname               = "numa_stat",
+               .data                   = &sysctl_vm_numa_stat,
+               .maxlen                 = sizeof(int),
+               .mode                   = 0644,
+               .proc_handler   = sysctl_vm_numa_stat_handler,
+               .extra1                 = SYSCTL_ZERO,
+               .extra2                 = SYSCTL_ONE,
+       },
+#endif
+        {
+               .procname       = "hugetlb_shm_group",
+               .data           = &sysctl_hugetlb_shm_group,
+               .maxlen         = sizeof(gid_t),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+        },
+       {
+               .procname       = "nr_overcommit_hugepages",
+               .data           = NULL,
+               .maxlen         = sizeof(unsigned long),
+               .mode           = 0644,
+               .proc_handler   = hugetlb_overcommit_handler,
+       },
+#endif
+       {
+               .procname       = "lowmem_reserve_ratio",
+               .data           = &sysctl_lowmem_reserve_ratio,
+               .maxlen         = sizeof(sysctl_lowmem_reserve_ratio),
+               .mode           = 0644,
+               .proc_handler   = lowmem_reserve_ratio_sysctl_handler,
+       },
+       {
+               .procname       = "drop_caches",
+               .data           = &sysctl_drop_caches,
+               .maxlen         = sizeof(int),
+               .mode           = 0200,
+               .proc_handler   = drop_caches_sysctl_handler,
+               .extra1         = SYSCTL_ONE,
+               .extra2         = &four,
+       },
+#ifdef CONFIG_COMPACTION
+       {
+               .procname       = "compact_memory",
+               .data           = &sysctl_compact_memory,
+               .maxlen         = sizeof(int),
+               .mode           = 0200,
+               .proc_handler   = sysctl_compaction_handler,
+       },
+       {
+               .procname       = "extfrag_threshold",
+               .data           = &sysctl_extfrag_threshold,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &min_extfrag_threshold,
+               .extra2         = &max_extfrag_threshold,
+       },
+       {
+               .procname       = "compact_unevictable_allowed",
+               .data           = &sysctl_compact_unevictable_allowed,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax_warn_RT_change,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
 
-int proc_dointvec_ms_jiffies(struct ctl_table *table, int write,
-                            void __user *buffer, size_t *lenp, loff_t *ppos)
-{
-       return -ENOSYS;
-}
+#endif /* CONFIG_COMPACTION */
+       {
+               .procname       = "min_free_kbytes",
+               .data           = &min_free_kbytes,
+               .maxlen         = sizeof(min_free_kbytes),
+               .mode           = 0644,
+               .proc_handler   = min_free_kbytes_sysctl_handler,
+               .extra1         = SYSCTL_ZERO,
+       },
+       {
+               .procname       = "watermark_boost_factor",
+               .data           = &watermark_boost_factor,
+               .maxlen         = sizeof(watermark_boost_factor),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+       },
+       {
+               .procname       = "watermark_scale_factor",
+               .data           = &watermark_scale_factor,
+               .maxlen         = sizeof(watermark_scale_factor),
+               .mode           = 0644,
+               .proc_handler   = watermark_scale_factor_sysctl_handler,
+               .extra1         = SYSCTL_ONE,
+               .extra2         = &one_thousand,
+       },
+       {
+               .procname       = "percpu_pagelist_fraction",
+               .data           = &percpu_pagelist_fraction,
+               .maxlen         = sizeof(percpu_pagelist_fraction),
+               .mode           = 0644,
+               .proc_handler   = percpu_pagelist_fraction_sysctl_handler,
+               .extra1         = SYSCTL_ZERO,
+       },
+#ifdef CONFIG_MMU
+       {
+               .procname       = "max_map_count",
+               .data           = &sysctl_max_map_count,
+               .maxlen         = sizeof(sysctl_max_map_count),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+       },
+#else
+       {
+               .procname       = "nr_trim_pages",
+               .data           = &sysctl_nr_trim_pages,
+               .maxlen         = sizeof(sysctl_nr_trim_pages),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+       },
+#endif
+       {
+               .procname       = "laptop_mode",
+               .data           = &laptop_mode,
+               .maxlen         = sizeof(laptop_mode),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_jiffies,
+       },
+       {
+               .procname       = "block_dump",
+               .data           = &block_dump,
+               .maxlen         = sizeof(block_dump),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+               .extra1         = SYSCTL_ZERO,
+       },
+       {
+               .procname       = "vfs_cache_pressure",
+               .data           = &sysctl_vfs_cache_pressure,
+               .maxlen         = sizeof(sysctl_vfs_cache_pressure),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+               .extra1         = SYSCTL_ZERO,
+       },
+#if defined(HAVE_ARCH_PICK_MMAP_LAYOUT) || \
+    defined(CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT)
+       {
+               .procname       = "legacy_va_layout",
+               .data           = &sysctl_legacy_va_layout,
+               .maxlen         = sizeof(sysctl_legacy_va_layout),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+               .extra1         = SYSCTL_ZERO,
+       },
+#endif
+#ifdef CONFIG_NUMA
+       {
+               .procname       = "zone_reclaim_mode",
+               .data           = &node_reclaim_mode,
+               .maxlen         = sizeof(node_reclaim_mode),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+               .extra1         = SYSCTL_ZERO,
+       },
+       {
+               .procname       = "min_unmapped_ratio",
+               .data           = &sysctl_min_unmapped_ratio,
+               .maxlen         = sizeof(sysctl_min_unmapped_ratio),
+               .mode           = 0644,
+               .proc_handler   = sysctl_min_unmapped_ratio_sysctl_handler,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &one_hundred,
+       },
+       {
+               .procname       = "min_slab_ratio",
+               .data           = &sysctl_min_slab_ratio,
+               .maxlen         = sizeof(sysctl_min_slab_ratio),
+               .mode           = 0644,
+               .proc_handler   = sysctl_min_slab_ratio_sysctl_handler,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &one_hundred,
+       },
+#endif
+#ifdef CONFIG_SMP
+       {
+               .procname       = "stat_interval",
+               .data           = &sysctl_stat_interval,
+               .maxlen         = sizeof(sysctl_stat_interval),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_jiffies,
+       },
+       {
+               .procname       = "stat_refresh",
+               .data           = NULL,
+               .maxlen         = 0,
+               .mode           = 0600,
+               .proc_handler   = vmstat_refresh,
+       },
+#endif
+#ifdef CONFIG_MMU
+       {
+               .procname       = "mmap_min_addr",
+               .data           = &dac_mmap_min_addr,
+               .maxlen         = sizeof(unsigned long),
+               .mode           = 0644,
+               .proc_handler   = mmap_min_addr_handler,
+       },
+#endif
+#ifdef CONFIG_NUMA
+       {
+               .procname       = "numa_zonelist_order",
+               .data           = &numa_zonelist_order,
+               .maxlen         = NUMA_ZONELIST_ORDER_LEN,
+               .mode           = 0644,
+               .proc_handler   = numa_zonelist_order_handler,
+       },
+#endif
+#if (defined(CONFIG_X86_32) && !defined(CONFIG_UML))|| \
+   (defined(CONFIG_SUPERH) && defined(CONFIG_VSYSCALL))
+       {
+               .procname       = "vdso_enabled",
+#ifdef CONFIG_X86_32
+               .data           = &vdso32_enabled,
+               .maxlen         = sizeof(vdso32_enabled),
+#else
+               .data           = &vdso_enabled,
+               .maxlen         = sizeof(vdso_enabled),
+#endif
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+               .extra1         = SYSCTL_ZERO,
+       },
+#endif
+#ifdef CONFIG_HIGHMEM
+       {
+               .procname       = "highmem_is_dirtyable",
+               .data           = &vm_highmem_is_dirtyable,
+               .maxlen         = sizeof(vm_highmem_is_dirtyable),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif
+#ifdef CONFIG_MEMORY_FAILURE
+       {
+               .procname       = "memory_failure_early_kill",
+               .data           = &sysctl_memory_failure_early_kill,
+               .maxlen         = sizeof(sysctl_memory_failure_early_kill),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+       {
+               .procname       = "memory_failure_recovery",
+               .data           = &sysctl_memory_failure_recovery,
+               .maxlen         = sizeof(sysctl_memory_failure_recovery),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif
+       {
+               .procname       = "user_reserve_kbytes",
+               .data           = &sysctl_user_reserve_kbytes,
+               .maxlen         = sizeof(sysctl_user_reserve_kbytes),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+       },
+       {
+               .procname       = "admin_reserve_kbytes",
+               .data           = &sysctl_admin_reserve_kbytes,
+               .maxlen         = sizeof(sysctl_admin_reserve_kbytes),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+       },
+#ifdef CONFIG_HAVE_ARCH_MMAP_RND_BITS
+       {
+               .procname       = "mmap_rnd_bits",
+               .data           = &mmap_rnd_bits,
+               .maxlen         = sizeof(mmap_rnd_bits),
+               .mode           = 0600,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = (void *)&mmap_rnd_bits_min,
+               .extra2         = (void *)&mmap_rnd_bits_max,
+       },
+#endif
+#ifdef CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS
+       {
+               .procname       = "mmap_rnd_compat_bits",
+               .data           = &mmap_rnd_compat_bits,
+               .maxlen         = sizeof(mmap_rnd_compat_bits),
+               .mode           = 0600,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = (void *)&mmap_rnd_compat_bits_min,
+               .extra2         = (void *)&mmap_rnd_compat_bits_max,
+       },
+#endif
+#ifdef CONFIG_USERFAULTFD
+       {
+               .procname       = "unprivileged_userfaultfd",
+               .data           = &sysctl_unprivileged_userfaultfd,
+               .maxlen         = sizeof(sysctl_unprivileged_userfaultfd),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif
+       { }
+};
 
-int proc_doulongvec_minmax(struct ctl_table *table, int write,
-                   void __user *buffer, size_t *lenp, loff_t *ppos)
-{
-       return -ENOSYS;
-}
+static struct ctl_table fs_table[] = {
+       {
+               .procname       = "inode-nr",
+               .data           = &inodes_stat,
+               .maxlen         = 2*sizeof(long),
+               .mode           = 0444,
+               .proc_handler   = proc_nr_inodes,
+       },
+       {
+               .procname       = "inode-state",
+               .data           = &inodes_stat,
+               .maxlen         = 7*sizeof(long),
+               .mode           = 0444,
+               .proc_handler   = proc_nr_inodes,
+       },
+       {
+               .procname       = "file-nr",
+               .data           = &files_stat,
+               .maxlen         = sizeof(files_stat),
+               .mode           = 0444,
+               .proc_handler   = proc_nr_files,
+       },
+       {
+               .procname       = "file-max",
+               .data           = &files_stat.max_files,
+               .maxlen         = sizeof(files_stat.max_files),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+               .extra1         = &zero_ul,
+               .extra2         = &long_max,
+       },
+       {
+               .procname       = "nr_open",
+               .data           = &sysctl_nr_open,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &sysctl_nr_open_min,
+               .extra2         = &sysctl_nr_open_max,
+       },
+       {
+               .procname       = "dentry-state",
+               .data           = &dentry_stat,
+               .maxlen         = 6*sizeof(long),
+               .mode           = 0444,
+               .proc_handler   = proc_nr_dentry,
+       },
+       {
+               .procname       = "overflowuid",
+               .data           = &fs_overflowuid,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &minolduid,
+               .extra2         = &maxolduid,
+       },
+       {
+               .procname       = "overflowgid",
+               .data           = &fs_overflowgid,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &minolduid,
+               .extra2         = &maxolduid,
+       },
+#ifdef CONFIG_FILE_LOCKING
+       {
+               .procname       = "leases-enable",
+               .data           = &leases_enable,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#ifdef CONFIG_DNOTIFY
+       {
+               .procname       = "dir-notify-enable",
+               .data           = &dir_notify_enable,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#ifdef CONFIG_MMU
+#ifdef CONFIG_FILE_LOCKING
+       {
+               .procname       = "lease-break-time",
+               .data           = &lease_break_time,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec,
+       },
+#endif
+#ifdef CONFIG_AIO
+       {
+               .procname       = "aio-nr",
+               .data           = &aio_nr,
+               .maxlen         = sizeof(aio_nr),
+               .mode           = 0444,
+               .proc_handler   = proc_doulongvec_minmax,
+       },
+       {
+               .procname       = "aio-max-nr",
+               .data           = &aio_max_nr,
+               .maxlen         = sizeof(aio_max_nr),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+       },
+#endif /* CONFIG_AIO */
+#ifdef CONFIG_INOTIFY_USER
+       {
+               .procname       = "inotify",
+               .mode           = 0555,
+               .child          = inotify_table,
+       },
+#endif 
+#ifdef CONFIG_EPOLL
+       {
+               .procname       = "epoll",
+               .mode           = 0555,
+               .child          = epoll_table,
+       },
+#endif
+#endif
+       {
+               .procname       = "protected_symlinks",
+               .data           = &sysctl_protected_symlinks,
+               .maxlen         = sizeof(int),
+               .mode           = 0600,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+       {
+               .procname       = "protected_hardlinks",
+               .data           = &sysctl_protected_hardlinks,
+               .maxlen         = sizeof(int),
+               .mode           = 0600,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+       {
+               .procname       = "protected_fifos",
+               .data           = &sysctl_protected_fifos,
+               .maxlen         = sizeof(int),
+               .mode           = 0600,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &two,
+       },
+       {
+               .procname       = "protected_regular",
+               .data           = &sysctl_protected_regular,
+               .maxlen         = sizeof(int),
+               .mode           = 0600,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &two,
+       },
+       {
+               .procname       = "suid_dumpable",
+               .data           = &suid_dumpable,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax_coredump,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = &two,
+       },
+#if defined(CONFIG_BINFMT_MISC) || defined(CONFIG_BINFMT_MISC_MODULE)
+       {
+               .procname       = "binfmt_misc",
+               .mode           = 0555,
+               .child          = sysctl_mount_point,
+       },
+#endif
+       {
+               .procname       = "pipe-max-size",
+               .data           = &pipe_max_size,
+               .maxlen         = sizeof(pipe_max_size),
+               .mode           = 0644,
+               .proc_handler   = proc_dopipe_max_size,
+       },
+       {
+               .procname       = "pipe-user-pages-hard",
+               .data           = &pipe_user_pages_hard,
+               .maxlen         = sizeof(pipe_user_pages_hard),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+       },
+       {
+               .procname       = "pipe-user-pages-soft",
+               .data           = &pipe_user_pages_soft,
+               .maxlen         = sizeof(pipe_user_pages_soft),
+               .mode           = 0644,
+               .proc_handler   = proc_doulongvec_minmax,
+       },
+       {
+               .procname       = "mount-max",
+               .data           = &sysctl_mount_max,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ONE,
+       },
+       { }
+};
 
-int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write,
-                                     void __user *buffer,
-                                     size_t *lenp, loff_t *ppos)
-{
-    return -ENOSYS;
-}
+static struct ctl_table debug_table[] = {
+#ifdef CONFIG_SYSCTL_EXCEPTION_TRACE
+       {
+               .procname       = "exception-trace",
+               .data           = &show_unhandled_signals,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec
+       },
+#endif
+#if defined(CONFIG_OPTPROBES)
+       {
+               .procname       = "kprobes-optimization",
+               .data           = &sysctl_kprobes_optimization,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_kprobes_optimization_handler,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
+#endif
+       { }
+};
 
-int proc_do_large_bitmap(struct ctl_table *table, int write,
-                        void __user *buffer, size_t *lenp, loff_t *ppos)
-{
-       return -ENOSYS;
-}
+static struct ctl_table dev_table[] = {
+       { }
+};
 
-#endif /* CONFIG_PROC_SYSCTL */
+static struct ctl_table sysctl_base_table[] = {
+       {
+               .procname       = "kernel",
+               .mode           = 0555,
+               .child          = kern_table,
+       },
+       {
+               .procname       = "vm",
+               .mode           = 0555,
+               .child          = vm_table,
+       },
+       {
+               .procname       = "fs",
+               .mode           = 0555,
+               .child          = fs_table,
+       },
+       {
+               .procname       = "debug",
+               .mode           = 0555,
+               .child          = debug_table,
+       },
+       {
+               .procname       = "dev",
+               .mode           = 0555,
+               .child          = dev_table,
+       },
+       { }
+};
 
-#if defined(CONFIG_SYSCTL)
-int proc_do_static_key(struct ctl_table *table, int write,
-                      void __user *buffer, size_t *lenp,
-                      loff_t *ppos)
+int __init sysctl_init(void)
 {
-       struct static_key *key = (struct static_key *)table->data;
-       static DEFINE_MUTEX(static_key_mutex);
-       int val, ret;
-       struct ctl_table tmp = {
-               .data   = &val,
-               .maxlen = sizeof(val),
-               .mode   = table->mode,
-               .extra1 = SYSCTL_ZERO,
-               .extra2 = SYSCTL_ONE,
-       };
-
-       if (write && !capable(CAP_SYS_ADMIN))
-               return -EPERM;
+       struct ctl_table_header *hdr;
 
-       mutex_lock(&static_key_mutex);
-       val = static_key_enabled(key);
-       ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
-       if (write && !ret) {
-               if (val)
-                       static_key_enable(key);
-               else
-                       static_key_disable(key);
-       }
-       mutex_unlock(&static_key_mutex);
-       return ret;
+       hdr = register_sysctl_table(sysctl_base_table);
+       kmemleak_not_leak(hdr);
+       return 0;
 }
-#endif
+#endif /* CONFIG_SYSCTL */
 /*
  * No sense putting this after each symbol definition, twice,
  * exception granted :-)
index 3b30288..53bce34 100644 (file)
@@ -338,7 +338,20 @@ static struct user_namespace *timens_owner(struct ns_common *ns)
 
 static void show_offset(struct seq_file *m, int clockid, struct timespec64 *ts)
 {
-       seq_printf(m, "%d %lld %ld\n", clockid, ts->tv_sec, ts->tv_nsec);
+       char *clock;
+
+       switch (clockid) {
+       case CLOCK_BOOTTIME:
+               clock = "boottime";
+               break;
+       case CLOCK_MONOTONIC:
+               clock = "monotonic";
+               break;
+       default:
+               clock = "unknown";
+               break;
+       }
+       seq_printf(m, "%-10s %10lld %9ld\n", clock, ts->tv_sec, ts->tv_nsec);
 }
 
 void proc_timens_show_offsets(struct task_struct *p, struct seq_file *m)
index a5221ab..398e6ea 100644 (file)
@@ -249,8 +249,7 @@ void timers_update_nohz(void)
 }
 
 int timer_migration_handler(struct ctl_table *table, int write,
-                           void __user *buffer, size_t *lenp,
-                           loff_t *ppos)
+                           void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
 
index ca17967..e875c95 100644 (file)
@@ -797,6 +797,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_map_peek_elem_proto;
        case BPF_FUNC_ktime_get_ns:
                return &bpf_ktime_get_ns_proto;
+       case BPF_FUNC_ktime_get_boot_ns:
+               return &bpf_ktime_get_boot_ns_proto;
        case BPF_FUNC_tail_call:
                return &bpf_tail_call_proto;
        case BPF_FUNC_get_current_pid_tgid:
index 041694a..bd030b1 100644 (file)
@@ -5165,6 +5165,7 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
                        list_del_rcu(&direct->next);
                        synchronize_rcu_tasks();
                        kfree(direct);
+                       kfree(entry);
                        ftrace_direct_func_count--;
                }
        }
index 8d2b988..167a74a 100644 (file)
@@ -2661,7 +2661,7 @@ static void output_printk(struct trace_event_buffer *fbuffer)
 }
 
 int tracepoint_printk_sysctl(struct ctl_table *table, int write,
-                            void __user *buffer, size_t *lenp,
+                            void *buffer, size_t *lenp,
                             loff_t *ppos)
 {
        int save_tracepoint_printk;
index 5f6834a..fcab11c 100644 (file)
@@ -3320,6 +3320,9 @@ static void __destroy_hist_field(struct hist_field *hist_field)
        kfree(hist_field->name);
        kfree(hist_field->type);
 
+       kfree(hist_field->system);
+       kfree(hist_field->event_name);
+
        kfree(hist_field);
 }
 
@@ -4382,6 +4385,7 @@ static struct hist_field *create_var(struct hist_trigger_data *hist_data,
                goto out;
        }
 
+       var->ref = 1;
        var->flags = HIST_FIELD_FL_VAR;
        var->var.idx = idx;
        var->var.hist_data = var->hist_data = hist_data;
@@ -5011,6 +5015,9 @@ static void destroy_field_vars(struct hist_trigger_data *hist_data)
 
        for (i = 0; i < hist_data->n_field_vars; i++)
                destroy_field_var(hist_data->field_vars[i]);
+
+       for (i = 0; i < hist_data->n_save_vars; i++)
+               destroy_field_var(hist_data->save_vars[i]);
 }
 
 static void save_field_var(struct hist_trigger_data *hist_data,
index dd34a1b..3a74736 100644 (file)
@@ -1088,14 +1088,10 @@ register_snapshot_trigger(char *glob, struct event_trigger_ops *ops,
                          struct event_trigger_data *data,
                          struct trace_event_file *file)
 {
-       int ret = register_trigger(glob, ops, data, file);
-
-       if (ret > 0 && tracing_alloc_snapshot_instance(file->tr) != 0) {
-               unregister_trigger(glob, ops, data, file);
-               ret = 0;
-       }
+       if (tracing_alloc_snapshot_instance(file->tr) != 0)
+               return 0;
 
-       return ret;
+       return register_trigger(glob, ops, data, file);
 }
 
 static int
index 9e31bfc..74738c9 100644 (file)
@@ -283,7 +283,7 @@ int tracing_map_add_key_field(struct tracing_map *map,
        return idx;
 }
 
-void tracing_map_array_clear(struct tracing_map_array *a)
+static void tracing_map_array_clear(struct tracing_map_array *a)
 {
        unsigned int i;
 
@@ -294,7 +294,7 @@ void tracing_map_array_clear(struct tracing_map_array *a)
                memset(a->pages[i], 0, PAGE_SIZE);
 }
 
-void tracing_map_array_free(struct tracing_map_array *a)
+static void tracing_map_array_free(struct tracing_map_array *a)
 {
        unsigned int i;
 
@@ -316,7 +316,7 @@ void tracing_map_array_free(struct tracing_map_array *a)
        kfree(a);
 }
 
-struct tracing_map_array *tracing_map_array_alloc(unsigned int n_elts,
+static struct tracing_map_array *tracing_map_array_alloc(unsigned int n_elts,
                                                  unsigned int entry_size)
 {
        struct tracing_map_array *a;
index 7f255b5..9788ed4 100644 (file)
@@ -630,7 +630,7 @@ int call_usermodehelper(const char *path, char **argv, char **envp, int wait)
 EXPORT_SYMBOL(call_usermodehelper);
 
 static int proc_cap_handler(struct ctl_table *table, int write,
-                        void __user *buffer, size_t *lenp, loff_t *ppos)
+                        void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table t;
        unsigned long cap_array[_KERNEL_CAPABILITY_U32S];
index 3732c88..4ca61d4 100644 (file)
@@ -30,7 +30,7 @@ static void *get_uts(struct ctl_table *table)
  *     to observe. Should this be in kernel/sys.c ????
  */
 static int proc_do_uts_string(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos)
+                 void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table uts_table;
        int r;
index b6b1f54..53ff2c8 100644 (file)
@@ -661,7 +661,7 @@ static void proc_watchdog_update(void)
  * proc_soft_watchdog | soft_watchdog_user_enabled | SOFT_WATCHDOG_ENABLED
  */
 static int proc_watchdog_common(int which, struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int err, old, *param = table->data;
 
@@ -688,7 +688,7 @@ static int proc_watchdog_common(int which, struct ctl_table *table, int write,
  * /proc/sys/kernel/watchdog
  */
 int proc_watchdog(struct ctl_table *table, int write,
-                 void __user *buffer, size_t *lenp, loff_t *ppos)
+                 void *buffer, size_t *lenp, loff_t *ppos)
 {
        return proc_watchdog_common(NMI_WATCHDOG_ENABLED|SOFT_WATCHDOG_ENABLED,
                                    table, write, buffer, lenp, ppos);
@@ -698,7 +698,7 @@ int proc_watchdog(struct ctl_table *table, int write,
  * /proc/sys/kernel/nmi_watchdog
  */
 int proc_nmi_watchdog(struct ctl_table *table, int write,
-                     void __user *buffer, size_t *lenp, loff_t *ppos)
+                     void *buffer, size_t *lenp, loff_t *ppos)
 {
        if (!nmi_watchdog_available && write)
                return -ENOTSUPP;
@@ -710,7 +710,7 @@ int proc_nmi_watchdog(struct ctl_table *table, int write,
  * /proc/sys/kernel/soft_watchdog
  */
 int proc_soft_watchdog(struct ctl_table *table, int write,
-                       void __user *buffer, size_t *lenp, loff_t *ppos)
+                       void *buffer, size_t *lenp, loff_t *ppos)
 {
        return proc_watchdog_common(SOFT_WATCHDOG_ENABLED,
                                    table, write, buffer, lenp, ppos);
@@ -720,7 +720,7 @@ int proc_soft_watchdog(struct ctl_table *table, int write,
  * /proc/sys/kernel/watchdog_thresh
  */
 int proc_watchdog_thresh(struct ctl_table *table, int write,
-                        void __user *buffer, size_t *lenp, loff_t *ppos)
+                        void *buffer, size_t *lenp, loff_t *ppos)
 {
        int err, old;
 
@@ -743,7 +743,7 @@ int proc_watchdog_thresh(struct ctl_table *table, int write,
  * been brought online, if desired.
  */
 int proc_watchdog_cpumask(struct ctl_table *table, int write,
-                         void __user *buffer, size_t *lenp, loff_t *ppos)
+                         void *buffer, size_t *lenp, loff_t *ppos)
 {
        int err;
 
index 50c1f5f..21d9c5f 100644 (file)
@@ -242,6 +242,8 @@ config DEBUG_INFO_DWARF4
 config DEBUG_INFO_BTF
        bool "Generate BTF typeinfo"
        depends on DEBUG_INFO
+       depends on !DEBUG_INFO_SPLIT && !DEBUG_INFO_REDUCED
+       depends on !GCC_PLUGIN_RANDSTRUCT || COMPILE_TEST
        help
          Generate deduplicated BTF type information from DWARF debug info.
          Turning this on expects presence of pahole tool, which will convert
index 7a6430a..ccb2ffa 100644 (file)
@@ -93,7 +93,7 @@ static void kunit_print_ok_not_ok(void *test_or_suite,
         * representation.
         */
        if (suite)
-               pr_info("%s %zd - %s",
+               pr_info("%s %zd - %s\n",
                        kunit_status_to_string(is_ok),
                        test_number, description);
        else
index 2dceaca..891e1c3 100644 (file)
@@ -722,22 +722,22 @@ do {                                                                      \
 do { \
        if (__builtin_constant_p(bh) && (bh) == 0) \
                __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2" \
-               : "=r" ((USItype)(sh)), \
-               "=&r" ((USItype)(sl)) \
+               : "=r" (sh), \
+               "=&r" (sl) \
                : "%r" ((USItype)(ah)), \
                "%r" ((USItype)(al)), \
                "rI" ((USItype)(bl))); \
        else if (__builtin_constant_p(bh) && (bh) == ~(USItype) 0) \
                __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2" \
-               : "=r" ((USItype)(sh)), \
-               "=&r" ((USItype)(sl)) \
+               : "=r" (sh), \
+               "=&r" (sl) \
                : "%r" ((USItype)(ah)), \
                "%r" ((USItype)(al)), \
                "rI" ((USItype)(bl))); \
        else \
                __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3" \
-               : "=r" ((USItype)(sh)), \
-               "=&r" ((USItype)(sl)) \
+               : "=r" (sh), \
+               "=&r" (sl) \
                : "%r" ((USItype)(ah)), \
                "r" ((USItype)(bh)), \
                "%r" ((USItype)(al)), \
@@ -747,36 +747,36 @@ do { \
 do { \
        if (__builtin_constant_p(ah) && (ah) == 0) \
                __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2" \
-               : "=r" ((USItype)(sh)), \
-               "=&r" ((USItype)(sl)) \
+               : "=r" (sh), \
+               "=&r" (sl) \
                : "r" ((USItype)(bh)), \
                "rI" ((USItype)(al)), \
                "r" ((USItype)(bl))); \
        else if (__builtin_constant_p(ah) && (ah) == ~(USItype) 0) \
                __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2" \
-               : "=r" ((USItype)(sh)), \
-               "=&r" ((USItype)(sl)) \
+               : "=r" (sh), \
+               "=&r" (sl) \
                : "r" ((USItype)(bh)), \
                "rI" ((USItype)(al)), \
                "r" ((USItype)(bl))); \
        else if (__builtin_constant_p(bh) && (bh) == 0) \
                __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2" \
-               : "=r" ((USItype)(sh)), \
-               "=&r" ((USItype)(sl)) \
+               : "=r" (sh), \
+               "=&r" (sl) \
                : "r" ((USItype)(ah)), \
                "rI" ((USItype)(al)), \
                "r" ((USItype)(bl))); \
        else if (__builtin_constant_p(bh) && (bh) == ~(USItype) 0) \
                __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2" \
-               : "=r" ((USItype)(sh)), \
-               "=&r" ((USItype)(sl)) \
+               : "=r" (sh), \
+               "=&r" (sl) \
                : "r" ((USItype)(ah)), \
                "rI" ((USItype)(al)), \
                "r" ((USItype)(bl))); \
        else \
                __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2" \
-               : "=r" ((USItype)(sh)), \
-               "=&r" ((USItype)(sl)) \
+               : "=r" (sh), \
+               "=&r" (sl) \
                : "r" ((USItype)(ah)), \
                "r" ((USItype)(bh)), \
                "rI" ((USItype)(al)), \
@@ -787,7 +787,7 @@ do { \
 do { \
        USItype __m0 = (m0), __m1 = (m1); \
        __asm__ ("mulhwu %0,%1,%2" \
-       : "=r" ((USItype) ph) \
+       : "=r" (ph) \
        : "%r" (__m0), \
        "r" (__m1)); \
        (pl) = __m0 * __m1; \
index cace9b3..bc5b5cf 100644 (file)
@@ -44,8 +44,22 @@ static const u8 nla_attr_minlen[NLA_TYPE_MAX+1] = {
        [NLA_S64]       = sizeof(s64),
 };
 
+/*
+ * Nested policies might refer back to the original
+ * policy in some cases, and userspace could try to
+ * abuse that and recurse by nesting in the right
+ * ways. Limit recursion to avoid this problem.
+ */
+#define MAX_POLICY_RECURSION_DEPTH     10
+
+static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype,
+                               const struct nla_policy *policy,
+                               unsigned int validate,
+                               struct netlink_ext_ack *extack,
+                               struct nlattr **tb, unsigned int depth);
+
 static int validate_nla_bitfield32(const struct nlattr *nla,
-                                  const u32 *valid_flags_mask)
+                                  const u32 valid_flags_mask)
 {
        const struct nla_bitfield32 *bf = nla_data(nla);
 
@@ -53,11 +67,11 @@ static int validate_nla_bitfield32(const struct nlattr *nla,
                return -EINVAL;
 
        /*disallow invalid bit selector */
-       if (bf->selector & ~*valid_flags_mask)
+       if (bf->selector & ~valid_flags_mask)
                return -EINVAL;
 
        /*disallow invalid bit values */
-       if (bf->value & ~*valid_flags_mask)
+       if (bf->value & ~valid_flags_mask)
                return -EINVAL;
 
        /*disallow valid bit values that are not selected*/
@@ -70,7 +84,7 @@ static int validate_nla_bitfield32(const struct nlattr *nla,
 static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
                              const struct nla_policy *policy,
                              struct netlink_ext_ack *extack,
-                             unsigned int validate)
+                             unsigned int validate, unsigned int depth)
 {
        const struct nlattr *entry;
        int rem;
@@ -87,8 +101,9 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
                        return -ERANGE;
                }
 
-               ret = __nla_validate(nla_data(entry), nla_len(entry),
-                                    maxtype, policy, validate, extack);
+               ret = __nla_validate_parse(nla_data(entry), nla_len(entry),
+                                          maxtype, policy, validate, extack,
+                                          NULL, depth + 1);
                if (ret < 0)
                        return ret;
        }
@@ -96,17 +111,58 @@ static int nla_validate_array(const struct nlattr *head, int len, int maxtype,
        return 0;
 }
 
-static int nla_validate_int_range(const struct nla_policy *pt,
-                                 const struct nlattr *nla,
-                                 struct netlink_ext_ack *extack)
+void nla_get_range_unsigned(const struct nla_policy *pt,
+                           struct netlink_range_validation *range)
 {
-       bool validate_min, validate_max;
-       s64 value;
+       WARN_ON_ONCE(pt->validation_type != NLA_VALIDATE_RANGE_PTR &&
+                    (pt->min < 0 || pt->max < 0));
 
-       validate_min = pt->validation_type == NLA_VALIDATE_RANGE ||
-                      pt->validation_type == NLA_VALIDATE_MIN;
-       validate_max = pt->validation_type == NLA_VALIDATE_RANGE ||
-                      pt->validation_type == NLA_VALIDATE_MAX;
+       range->min = 0;
+
+       switch (pt->type) {
+       case NLA_U8:
+               range->max = U8_MAX;
+               break;
+       case NLA_U16:
+               range->max = U16_MAX;
+               break;
+       case NLA_U32:
+               range->max = U32_MAX;
+               break;
+       case NLA_U64:
+       case NLA_MSECS:
+               range->max = U64_MAX;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return;
+       }
+
+       switch (pt->validation_type) {
+       case NLA_VALIDATE_RANGE:
+               range->min = pt->min;
+               range->max = pt->max;
+               break;
+       case NLA_VALIDATE_RANGE_PTR:
+               *range = *pt->range;
+               break;
+       case NLA_VALIDATE_MIN:
+               range->min = pt->min;
+               break;
+       case NLA_VALIDATE_MAX:
+               range->max = pt->max;
+               break;
+       default:
+               break;
+       }
+}
+
+static int nla_validate_int_range_unsigned(const struct nla_policy *pt,
+                                          const struct nlattr *nla,
+                                          struct netlink_ext_ack *extack)
+{
+       struct netlink_range_validation range;
+       u64 value;
 
        switch (pt->type) {
        case NLA_U8:
@@ -118,6 +174,77 @@ static int nla_validate_int_range(const struct nla_policy *pt,
        case NLA_U32:
                value = nla_get_u32(nla);
                break;
+       case NLA_U64:
+       case NLA_MSECS:
+               value = nla_get_u64(nla);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       nla_get_range_unsigned(pt, &range);
+
+       if (value < range.min || value > range.max) {
+               NL_SET_ERR_MSG_ATTR(extack, nla,
+                                   "integer out of range");
+               return -ERANGE;
+       }
+
+       return 0;
+}
+
+void nla_get_range_signed(const struct nla_policy *pt,
+                         struct netlink_range_validation_signed *range)
+{
+       switch (pt->type) {
+       case NLA_S8:
+               range->min = S8_MIN;
+               range->max = S8_MAX;
+               break;
+       case NLA_S16:
+               range->min = S16_MIN;
+               range->max = S16_MAX;
+               break;
+       case NLA_S32:
+               range->min = S32_MIN;
+               range->max = S32_MAX;
+               break;
+       case NLA_S64:
+               range->min = S64_MIN;
+               range->max = S64_MAX;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return;
+       }
+
+       switch (pt->validation_type) {
+       case NLA_VALIDATE_RANGE:
+               range->min = pt->min;
+               range->max = pt->max;
+               break;
+       case NLA_VALIDATE_RANGE_PTR:
+               *range = *pt->range_signed;
+               break;
+       case NLA_VALIDATE_MIN:
+               range->min = pt->min;
+               break;
+       case NLA_VALIDATE_MAX:
+               range->max = pt->max;
+               break;
+       default:
+               break;
+       }
+}
+
+static int nla_validate_int_range_signed(const struct nla_policy *pt,
+                                        const struct nlattr *nla,
+                                        struct netlink_ext_ack *extack)
+{
+       struct netlink_range_validation_signed range;
+       s64 value;
+
+       switch (pt->type) {
        case NLA_S8:
                value = nla_get_s8(nla);
                break;
@@ -130,22 +257,13 @@ static int nla_validate_int_range(const struct nla_policy *pt,
        case NLA_S64:
                value = nla_get_s64(nla);
                break;
-       case NLA_U64:
-               /* treat this one specially, since it may not fit into s64 */
-               if ((validate_min && nla_get_u64(nla) < pt->min) ||
-                   (validate_max && nla_get_u64(nla) > pt->max)) {
-                       NL_SET_ERR_MSG_ATTR(extack, nla,
-                                           "integer out of range");
-                       return -ERANGE;
-               }
-               return 0;
        default:
-               WARN_ON(1);
                return -EINVAL;
        }
 
-       if ((validate_min && value < pt->min) ||
-           (validate_max && value > pt->max)) {
+       nla_get_range_signed(pt, &range);
+
+       if (value < range.min || value > range.max) {
                NL_SET_ERR_MSG_ATTR(extack, nla,
                                    "integer out of range");
                return -ERANGE;
@@ -154,9 +272,31 @@ static int nla_validate_int_range(const struct nla_policy *pt,
        return 0;
 }
 
+static int nla_validate_int_range(const struct nla_policy *pt,
+                                 const struct nlattr *nla,
+                                 struct netlink_ext_ack *extack)
+{
+       switch (pt->type) {
+       case NLA_U8:
+       case NLA_U16:
+       case NLA_U32:
+       case NLA_U64:
+       case NLA_MSECS:
+               return nla_validate_int_range_unsigned(pt, nla, extack);
+       case NLA_S8:
+       case NLA_S16:
+       case NLA_S32:
+       case NLA_S64:
+               return nla_validate_int_range_signed(pt, nla, extack);
+       default:
+               WARN_ON(1);
+               return -EINVAL;
+       }
+}
+
 static int validate_nla(const struct nlattr *nla, int maxtype,
                        const struct nla_policy *policy, unsigned int validate,
-                       struct netlink_ext_ack *extack)
+                       struct netlink_ext_ack *extack, unsigned int depth)
 {
        u16 strict_start_type = policy[0].strict_start_type;
        const struct nla_policy *pt;
@@ -174,7 +314,9 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
        BUG_ON(pt->type > NLA_TYPE_MAX);
 
        if ((nla_attr_len[pt->type] && attrlen != nla_attr_len[pt->type]) ||
-           (pt->type == NLA_EXACT_LEN_WARN && attrlen != pt->len)) {
+           (pt->type == NLA_EXACT_LEN &&
+            pt->validation_type == NLA_VALIDATE_WARN_TOO_LONG &&
+            attrlen != pt->len)) {
                pr_warn_ratelimited("netlink: '%s': attribute type %d has an invalid length.\n",
                                    current->comm, type);
                if (validate & NL_VALIDATE_STRICT_ATTRS) {
@@ -200,15 +342,10 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
        }
 
        switch (pt->type) {
-       case NLA_EXACT_LEN:
-               if (attrlen != pt->len)
-                       goto out_err;
-               break;
-
        case NLA_REJECT:
-               if (extack && pt->validation_data) {
+               if (extack && pt->reject_message) {
                        NL_SET_BAD_ATTR(extack, nla);
-                       extack->_msg = pt->validation_data;
+                       extack->_msg = pt->reject_message;
                        return -EINVAL;
                }
                err = -EINVAL;
@@ -223,7 +360,7 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
                if (attrlen != sizeof(struct nla_bitfield32))
                        goto out_err;
 
-               err = validate_nla_bitfield32(nla, pt->validation_data);
+               err = validate_nla_bitfield32(nla, pt->bitfield32_valid);
                if (err)
                        goto out_err;
                break;
@@ -268,10 +405,11 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
                        break;
                if (attrlen < NLA_HDRLEN)
                        goto out_err;
-               if (pt->validation_data) {
-                       err = __nla_validate(nla_data(nla), nla_len(nla), pt->len,
-                                            pt->validation_data, validate,
-                                            extack);
+               if (pt->nested_policy) {
+                       err = __nla_validate_parse(nla_data(nla), nla_len(nla),
+                                                  pt->len, pt->nested_policy,
+                                                  validate, extack, NULL,
+                                                  depth + 1);
                        if (err < 0) {
                                /*
                                 * return directly to preserve the inner
@@ -289,12 +427,12 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
                        break;
                if (attrlen < NLA_HDRLEN)
                        goto out_err;
-               if (pt->validation_data) {
+               if (pt->nested_policy) {
                        int err;
 
                        err = nla_validate_array(nla_data(nla), nla_len(nla),
-                                                pt->len, pt->validation_data,
-                                                extack, validate);
+                                                pt->len, pt->nested_policy,
+                                                extack, validate, depth);
                        if (err < 0) {
                                /*
                                 * return directly to preserve the inner
@@ -317,6 +455,13 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
                        goto out_err;
                break;
 
+       case NLA_EXACT_LEN:
+               if (pt->validation_type != NLA_VALIDATE_WARN_TOO_LONG) {
+                       if (attrlen != pt->len)
+                               goto out_err;
+                       break;
+               }
+               /* fall through */
        default:
                if (pt->len)
                        minlen = pt->len;
@@ -332,6 +477,7 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
        case NLA_VALIDATE_NONE:
                /* nothing to do */
                break;
+       case NLA_VALIDATE_RANGE_PTR:
        case NLA_VALIDATE_RANGE:
        case NLA_VALIDATE_MIN:
        case NLA_VALIDATE_MAX:
@@ -358,11 +504,17 @@ static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype,
                                const struct nla_policy *policy,
                                unsigned int validate,
                                struct netlink_ext_ack *extack,
-                               struct nlattr **tb)
+                               struct nlattr **tb, unsigned int depth)
 {
        const struct nlattr *nla;
        int rem;
 
+       if (depth >= MAX_POLICY_RECURSION_DEPTH) {
+               NL_SET_ERR_MSG(extack,
+                              "allowed policy recursion depth exceeded");
+               return -EINVAL;
+       }
+
        if (tb)
                memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
 
@@ -379,7 +531,7 @@ static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype,
                }
                if (policy) {
                        int err = validate_nla(nla, maxtype, policy,
-                                              validate, extack);
+                                              validate, extack, depth);
 
                        if (err < 0)
                                return err;
@@ -421,7 +573,7 @@ int __nla_validate(const struct nlattr *head, int len, int maxtype,
                   struct netlink_ext_ack *extack)
 {
        return __nla_validate_parse(head, len, maxtype, policy, validate,
-                                   extack, NULL);
+                                   extack, NULL, 0);
 }
 EXPORT_SYMBOL(__nla_validate);
 
@@ -476,7 +628,7 @@ int __nla_parse(struct nlattr **tb, int maxtype,
                struct netlink_ext_ack *extack)
 {
        return __nla_validate_parse(head, len, maxtype, policy, validate,
-                                   extack, tb);
+                                   extack, tb, 0);
 }
 EXPORT_SYMBOL(__nla_parse);
 
index 46f0fcc..d8cfb7b 100644 (file)
@@ -2463,7 +2463,7 @@ int sysctl_compact_memory;
  * /proc/sys/vm/compact_memory
  */
 int sysctl_compaction_handler(struct ctl_table *table, int write,
-                       void __user *buffer, size_t *length, loff_t *ppos)
+                       void *buffer, size_t *length, loff_t *ppos)
 {
        if (write)
                compact_nodes();
index 6076df8..50681f0 100644 (file)
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -1088,7 +1088,7 @@ retry:
                 * potentially allocating memory.
                 */
                if (fatal_signal_pending(current)) {
-                       ret = -ERESTARTSYS;
+                       ret = -EINTR;
                        goto out;
                }
                cond_resched();
index cd45915..f9a9732 100644 (file)
@@ -3352,7 +3352,7 @@ static unsigned int cpuset_mems_nr(unsigned int *array)
 #ifdef CONFIG_SYSCTL
 static int hugetlb_sysctl_handler_common(bool obey_mempolicy,
                         struct ctl_table *table, int write,
-                        void __user *buffer, size_t *length, loff_t *ppos)
+                        void *buffer, size_t *length, loff_t *ppos)
 {
        struct hstate *h = &default_hstate;
        unsigned long tmp = h->max_huge_pages;
@@ -3375,7 +3375,7 @@ out:
 }
 
 int hugetlb_sysctl_handler(struct ctl_table *table, int write,
-                         void __user *buffer, size_t *length, loff_t *ppos)
+                         void *buffer, size_t *length, loff_t *ppos)
 {
 
        return hugetlb_sysctl_handler_common(false, table, write,
@@ -3384,7 +3384,7 @@ int hugetlb_sysctl_handler(struct ctl_table *table, int write,
 
 #ifdef CONFIG_NUMA
 int hugetlb_mempolicy_sysctl_handler(struct ctl_table *table, int write,
-                         void __user *buffer, size_t *length, loff_t *ppos)
+                         void *buffer, size_t *length, loff_t *ppos)
 {
        return hugetlb_sysctl_handler_common(true, table, write,
                                                        buffer, length, ppos);
@@ -3392,8 +3392,7 @@ int hugetlb_mempolicy_sysctl_handler(struct ctl_table *table, int write,
 #endif /* CONFIG_NUMA */
 
 int hugetlb_overcommit_handler(struct ctl_table *table, int write,
-                       void __user *buffer,
-                       size_t *length, loff_t *ppos)
+               void *buffer, size_t *length, loff_t *ppos)
 {
        struct hstate *h = &default_hstate;
        unsigned long tmp;
@@ -5365,8 +5364,8 @@ pte_t *huge_pte_offset(struct mm_struct *mm,
 {
        pgd_t *pgd;
        p4d_t *p4d;
-       pud_t *pud;
-       pmd_t *pmd;
+       pud_t *pud, pud_entry;
+       pmd_t *pmd, pmd_entry;
 
        pgd = pgd_offset(mm, addr);
        if (!pgd_present(*pgd))
@@ -5376,17 +5375,19 @@ pte_t *huge_pte_offset(struct mm_struct *mm,
                return NULL;
 
        pud = pud_offset(p4d, addr);
-       if (sz != PUD_SIZE && pud_none(*pud))
+       pud_entry = READ_ONCE(*pud);
+       if (sz != PUD_SIZE && pud_none(pud_entry))
                return NULL;
        /* hugepage or swap? */
-       if (pud_huge(*pud) || !pud_present(*pud))
+       if (pud_huge(pud_entry) || !pud_present(pud_entry))
                return (pte_t *)pud;
 
        pmd = pmd_offset(pud, addr);
-       if (sz != PMD_SIZE && pmd_none(*pmd))
+       pmd_entry = READ_ONCE(*pmd);
+       if (sz != PMD_SIZE && pmd_none(pmd_entry))
                return NULL;
        /* hugepage or swap? */
-       if (pmd_huge(*pmd) || !pmd_present(*pmd))
+       if (pmd_huge(pmd_entry) || !pmd_present(pmd_entry))
                return (pte_t *)pmd;
 
        return NULL;
index a558da9..281c001 100644 (file)
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -2112,8 +2112,16 @@ static void cmp_and_merge_page(struct page *page, struct rmap_item *rmap_item)
 
                down_read(&mm->mmap_sem);
                vma = find_mergeable_vma(mm, rmap_item->address);
-               err = try_to_merge_one_page(vma, page,
-                                           ZERO_PAGE(rmap_item->address));
+               if (vma) {
+                       err = try_to_merge_one_page(vma, page,
+                                       ZERO_PAGE(rmap_item->address));
+               } else {
+                       /*
+                        * If the vma is out of date, we do not need to
+                        * continue.
+                        */
+                       err = 0;
+               }
                up_read(&mm->mmap_sem);
                /*
                 * In case of failure, the page was not really empty, so we
index 4bb30ed..8cbd8c1 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/swapops.h>
 #include <linux/shmem_fs.h>
 #include <linux/mmu_notifier.h>
+#include <linux/sched/mm.h>
 
 #include <asm/tlb.h>
 
@@ -1090,6 +1091,23 @@ int do_madvise(unsigned long start, size_t len_in, int behavior)
        if (write) {
                if (down_write_killable(&current->mm->mmap_sem))
                        return -EINTR;
+
+               /*
+                * We may have stolen the mm from another process
+                * that is undergoing core dumping.
+                *
+                * Right now that's io_ring, in the future it may
+                * be remote process management and not "current"
+                * at all.
+                *
+                * We need to fix core dumping to not do this,
+                * but for now we have the mmget_still_valid()
+                * model.
+                */
+               if (!mmget_still_valid(current->mm)) {
+                       up_write(&current->mm->mmap_sem);
+                       return -EINTR;
+               }
        } else {
                down_read(&current->mm->mmap_sem);
        }
index a7e282e..c881abe 100644 (file)
@@ -413,9 +413,20 @@ static unsigned long move_vma(struct vm_area_struct *vma,
                        /* Always put back VM_ACCOUNT since we won't unmap */
                        vma->vm_flags |= VM_ACCOUNT;
 
-                       vm_acct_memory(vma_pages(new_vma));
+                       vm_acct_memory(new_len >> PAGE_SHIFT);
                }
 
+               /*
+                * VMAs can actually be merged back together in copy_vma
+                * calling merge_vma. This can happen with anonymous vmas
+                * which have not yet been faulted, so if we were to consider
+                * this VMA split we'll end up adding VM_ACCOUNT on the
+                * next VMA, which is completely unrelated if this VMA
+                * was re-merged.
+                */
+               if (split && new_vma == vma)
+                       split = 0;
+
                /* We always clear VM_LOCKED[ONFAULT] on the old vma */
                vma->vm_flags &= VM_LOCKED_CLEAR_MASK;
 
index 7326b54..d3ee4c4 100644 (file)
@@ -512,8 +512,7 @@ bool node_dirty_ok(struct pglist_data *pgdat)
 }
 
 int dirty_background_ratio_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
 
@@ -524,8 +523,7 @@ int dirty_background_ratio_handler(struct ctl_table *table, int write,
 }
 
 int dirty_background_bytes_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
 
@@ -535,9 +533,8 @@ int dirty_background_bytes_handler(struct ctl_table *table, int write,
        return ret;
 }
 
-int dirty_ratio_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos)
+int dirty_ratio_handler(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos)
 {
        int old_ratio = vm_dirty_ratio;
        int ret;
@@ -551,8 +548,7 @@ int dirty_ratio_handler(struct ctl_table *table, int write,
 }
 
 int dirty_bytes_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *lenp,
-               loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        unsigned long old_bytes = vm_dirty_bytes;
        int ret;
@@ -1972,7 +1968,7 @@ bool wb_over_bg_thresh(struct bdi_writeback *wb)
  * sysctl handler for /proc/sys/vm/dirty_writeback_centisecs
  */
 int dirty_writeback_centisecs_handler(struct ctl_table *table, int write,
-       void __user *buffer, size_t *length, loff_t *ppos)
+               void *buffer, size_t *length, loff_t *ppos)
 {
        unsigned int old_interval = dirty_writeback_interval;
        int ret;
index 69827d4..0c43e9a 100644 (file)
@@ -5546,21 +5546,11 @@ char numa_zonelist_order[] = "Node";
  * sysctl handler for numa_zonelist_order
  */
 int numa_zonelist_order_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *length,
-               loff_t *ppos)
+               void *buffer, size_t *length, loff_t *ppos)
 {
-       char *str;
-       int ret;
-
-       if (!write)
-               return proc_dostring(table, write, buffer, length, ppos);
-       str = memdup_user_nul(buffer, 16);
-       if (IS_ERR(str))
-               return PTR_ERR(str);
-
-       ret = __parse_numa_zonelist_order(str);
-       kfree(str);
-       return ret;
+       if (write)
+               return __parse_numa_zonelist_order(buffer);
+       return proc_dostring(table, write, buffer, length, ppos);
 }
 
 
@@ -7963,7 +7953,7 @@ core_initcall(init_per_zone_wmark_min)
  *     changes.
  */
 int min_free_kbytes_sysctl_handler(struct ctl_table *table, int write,
-       void __user *buffer, size_t *length, loff_t *ppos)
+               void *buffer, size_t *length, loff_t *ppos)
 {
        int rc;
 
@@ -7978,20 +7968,8 @@ int min_free_kbytes_sysctl_handler(struct ctl_table *table, int write,
        return 0;
 }
 
-int watermark_boost_factor_sysctl_handler(struct ctl_table *table, int write,
-       void __user *buffer, size_t *length, loff_t *ppos)
-{
-       int rc;
-
-       rc = proc_dointvec_minmax(table, write, buffer, length, ppos);
-       if (rc)
-               return rc;
-
-       return 0;
-}
-
 int watermark_scale_factor_sysctl_handler(struct ctl_table *table, int write,
-       void __user *buffer, size_t *length, loff_t *ppos)
+               void *buffer, size_t *length, loff_t *ppos)
 {
        int rc;
 
@@ -8021,7 +7999,7 @@ static void setup_min_unmapped_ratio(void)
 
 
 int sysctl_min_unmapped_ratio_sysctl_handler(struct ctl_table *table, int write,
-       void __user *buffer, size_t *length, loff_t *ppos)
+               void *buffer, size_t *length, loff_t *ppos)
 {
        int rc;
 
@@ -8048,7 +8026,7 @@ static void setup_min_slab_ratio(void)
 }
 
 int sysctl_min_slab_ratio_sysctl_handler(struct ctl_table *table, int write,
-       void __user *buffer, size_t *length, loff_t *ppos)
+               void *buffer, size_t *length, loff_t *ppos)
 {
        int rc;
 
@@ -8072,7 +8050,7 @@ int sysctl_min_slab_ratio_sysctl_handler(struct ctl_table *table, int write,
  * if in function of the boot time zone sizes.
  */
 int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *table, int write,
-       void __user *buffer, size_t *length, loff_t *ppos)
+               void *buffer, size_t *length, loff_t *ppos)
 {
        proc_dointvec_minmax(table, write, buffer, length, ppos);
        setup_per_zone_lowmem_reserve();
@@ -8094,7 +8072,7 @@ static void __zone_pcp_update(struct zone *zone)
  * pagelist can have before it gets flushed back to buddy allocator.
  */
 int percpu_pagelist_fraction_sysctl_handler(struct ctl_table *table, int write,
-       void __user *buffer, size_t *length, loff_t *ppos)
+               void *buffer, size_t *length, loff_t *ppos)
 {
        struct zone *zone;
        int old_percpu_pagelist_fraction;
index d722eb8..bd88400 100644 (file)
@@ -952,7 +952,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend,
                                VM_BUG_ON_PAGE(PageWriteback(page), page);
                                if (shmem_punch_compound(page, start, end))
                                        truncate_inode_page(mapping, page);
-                               else {
+                               else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
                                        /* Wipe the page and don't get stuck */
                                        clear_highpage(page);
                                        flush_dcache_page(page);
@@ -2179,7 +2179,11 @@ int shmem_lock(struct file *file, int lock, struct user_struct *user)
        struct shmem_inode_info *info = SHMEM_I(inode);
        int retval = -ENOMEM;
 
-       spin_lock_irq(&info->lock);
+       /*
+        * What serializes the accesses to info->flags?
+        * ipc_lock_object() when called from shmctl_do_lock(),
+        * no serialization needed when called from shm_destroy().
+        */
        if (lock && !(info->flags & VM_LOCKED)) {
                if (!user_shm_lock(inode->i_size, user))
                        goto out_nomem;
@@ -2194,7 +2198,6 @@ int shmem_lock(struct file *file, int lock, struct user_struct *user)
        retval = 0;
 
 out_nomem:
-       spin_unlock_irq(&info->lock);
        return retval;
 }
 
@@ -2399,11 +2402,11 @@ static int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,
 
        lru_cache_add_anon(page);
 
-       spin_lock(&info->lock);
+       spin_lock_irq(&info->lock);
        info->alloced++;
        inode->i_blocks += BLOCKS_PER_PAGE;
        shmem_recalc_inode(inode);
-       spin_unlock(&info->lock);
+       spin_unlock_irq(&info->lock);
 
        inc_mm_counter(dst_mm, mm_counter_file(page));
        page_add_file_rmap(page, false);
index 332d4b4..9bf4495 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -3533,6 +3533,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order)
 {
        slab_flags_t flags = s->flags;
        unsigned int size = s->object_size;
+       unsigned int freepointer_area;
        unsigned int order;
 
        /*
@@ -3541,6 +3542,13 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order)
         * the possible location of the free pointer.
         */
        size = ALIGN(size, sizeof(void *));
+       /*
+        * This is the area of the object where a freepointer can be
+        * safely written. If redzoning adds more to the inuse size, we
+        * can't use that portion for writing the freepointer, so
+        * s->offset must be limited within this for the general case.
+        */
+       freepointer_area = size;
 
 #ifdef CONFIG_SLUB_DEBUG
        /*
@@ -3582,13 +3590,13 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order)
                 */
                s->offset = size;
                size += sizeof(void *);
-       } else if (size > sizeof(void *)) {
+       } else if (freepointer_area > sizeof(void *)) {
                /*
                 * Store freelist pointer near middle of object to keep
                 * it away from the edges of the object to avoid small
                 * sized over/underflows from neighboring allocations.
                 */
-               s->offset = ALIGN(size / 2, sizeof(void *));
+               s->offset = ALIGN(freepointer_area / 2, sizeof(void *));
        }
 
 #ifdef CONFIG_SLUB_DEBUG
index 988d11e..8defc8e 100644 (file)
--- a/mm/util.c
+++ b/mm/util.c
@@ -717,9 +717,8 @@ int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT;
 unsigned long sysctl_user_reserve_kbytes __read_mostly = 1UL << 17; /* 128MB */
 unsigned long sysctl_admin_reserve_kbytes __read_mostly = 1UL << 13; /* 8MB */
 
-int overcommit_ratio_handler(struct ctl_table *table, int write,
-                            void __user *buffer, size_t *lenp,
-                            loff_t *ppos)
+int overcommit_ratio_handler(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos)
 {
        int ret;
 
@@ -729,9 +728,8 @@ int overcommit_ratio_handler(struct ctl_table *table, int write,
        return ret;
 }
 
-int overcommit_kbytes_handler(struct ctl_table *table, int write,
-                            void __user *buffer, size_t *lenp,
-                            loff_t *ppos)
+int overcommit_kbytes_handler(struct ctl_table *table, int write, void *buffer,
+               size_t *lenp, loff_t *ppos)
 {
        int ret;
 
index 399f219..9a8227a 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/llist.h>
 #include <linux/bitops.h>
 #include <linux/rbtree_augmented.h>
+#include <linux/overflow.h>
 
 #include <linux/uaccess.h>
 #include <asm/tlbflush.h>
@@ -3054,6 +3055,7 @@ finished:
  * @vma:               vma to cover
  * @uaddr:             target user address to start at
  * @kaddr:             virtual address of vmalloc kernel memory
+ * @pgoff:             offset from @kaddr to start at
  * @size:              size of map area
  *
  * Returns:    0 for success, -Exxx on failure
@@ -3066,9 +3068,15 @@ finished:
  * Similar to remap_pfn_range() (see mm/memory.c)
  */
 int remap_vmalloc_range_partial(struct vm_area_struct *vma, unsigned long uaddr,
-                               void *kaddr, unsigned long size)
+                               void *kaddr, unsigned long pgoff,
+                               unsigned long size)
 {
        struct vm_struct *area;
+       unsigned long off;
+       unsigned long end_index;
+
+       if (check_shl_overflow(pgoff, PAGE_SHIFT, &off))
+               return -EINVAL;
 
        size = PAGE_ALIGN(size);
 
@@ -3082,8 +3090,10 @@ int remap_vmalloc_range_partial(struct vm_area_struct *vma, unsigned long uaddr,
        if (!(area->flags & (VM_USERMAP | VM_DMA_COHERENT)))
                return -EINVAL;
 
-       if (kaddr + size > area->addr + get_vm_area_size(area))
+       if (check_add_overflow(size, off, &end_index) ||
+           end_index > get_vm_area_size(area))
                return -EINVAL;
+       kaddr += off;
 
        do {
                struct page *page = vmalloc_to_page(kaddr);
@@ -3122,7 +3132,7 @@ int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,
                                                unsigned long pgoff)
 {
        return remap_vmalloc_range_partial(vma, vma->vm_start,
-                                          addr + (pgoff << PAGE_SHIFT),
+                                          addr, pgoff,
                                           vma->vm_end - vma->vm_start);
 }
 EXPORT_SYMBOL(remap_vmalloc_range);
index 96d21a7..c03a8c9 100644 (file)
@@ -76,7 +76,7 @@ static void invalid_numa_statistics(void)
 static DEFINE_MUTEX(vm_numa_stat_lock);
 
 int sysctl_vm_numa_stat_handler(struct ctl_table *table, int write,
-               void __user *buffer, size_t *length, loff_t *ppos)
+               void *buffer, size_t *length, loff_t *ppos)
 {
        int ret, oldval;
 
@@ -1751,7 +1751,7 @@ static void refresh_vm_stats(struct work_struct *work)
 }
 
 int vmstat_refresh(struct ctl_table *table, int write,
-                  void __user *buffer, size_t *lenp, loff_t *ppos)
+                  void *buffer, size_t *lenp, loff_t *ppos)
 {
        long val;
        int err;
index 990b9fd..319220b 100644 (file)
@@ -489,6 +489,25 @@ static void vlan_dev_set_rx_mode(struct net_device *vlan_dev)
        dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
 }
 
+/*
+ * vlan network devices have devices nesting below it, and are a special
+ * "super class" of normal network devices; split their locks off into a
+ * separate class since they always nest.
+ */
+static struct lock_class_key vlan_netdev_xmit_lock_key;
+
+static void vlan_dev_set_lockdep_one(struct net_device *dev,
+                                    struct netdev_queue *txq,
+                                    void *unused)
+{
+       lockdep_set_class(&txq->_xmit_lock, &vlan_netdev_xmit_lock_key);
+}
+
+static void vlan_dev_set_lockdep_class(struct net_device *dev)
+{
+       netdev_for_each_tx_queue(dev, vlan_dev_set_lockdep_one, NULL);
+}
+
 static const struct header_ops vlan_header_ops = {
        .create  = vlan_dev_hard_header,
        .parse   = eth_header_parse,
@@ -579,6 +598,8 @@ static int vlan_dev_init(struct net_device *dev)
 
        SET_NETDEV_DEVTYPE(dev, &vlan_type);
 
+       vlan_dev_set_lockdep_class(dev);
+
        vlan->vlan_pcpu_stats = netdev_alloc_pcpu_stats(struct vlan_pcpu_stats);
        if (!vlan->vlan_pcpu_stats)
                return -ENOMEM;
index df8d8c9..c5ba2d1 100644 (file)
@@ -86,7 +86,7 @@ config INET
          "Sysctl support" below, you can change various aspects of the
          behavior of the TCP/IP code by writing to the (virtual) files in
          /proc/sys/net/ipv4/*; the options are explained in the file
-         <file:Documentation/networking/ip-sysctl.txt>.
+         <file:Documentation/networking/ip-sysctl.rst>.
 
          Short answer: say Y.
 
@@ -344,7 +344,7 @@ config NET_PKTGEN
          what was just said, you don't need it: say N.
 
          Documentation on how to use the packet generator can be found
-         at <file:Documentation/networking/pktgen.txt>.
+         at <file:Documentation/networking/pktgen.rst>.
 
          To compile this code as a module, choose M here: the
          module will be called pktgen.
index 271f682..e61dcc9 100644 (file)
@@ -16,7 +16,7 @@ config ATM
          of your ATM card below.
 
          Note that you need a set of user-space programs to actually make use
-         of ATM.  See the file <file:Documentation/networking/atm.txt> for
+         of ATM.  See the file <file:Documentation/networking/atm.rst> for
          further details.
 
 config ATM_CLIP
index 0ce530a..8575f5d 100644 (file)
@@ -177,18 +177,18 @@ static void vcc_destroy_socket(struct sock *sk)
 
        set_bit(ATM_VF_CLOSE, &vcc->flags);
        clear_bit(ATM_VF_READY, &vcc->flags);
-       if (vcc->dev) {
-               if (vcc->dev->ops->close)
-                       vcc->dev->ops->close(vcc);
-               if (vcc->push)
-                       vcc->push(vcc, NULL); /* atmarpd has no push */
-               module_put(vcc->owner);
-
-               while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
-                       atm_return(vcc, skb->truesize);
-                       kfree_skb(skb);
-               }
+       if (vcc->dev && vcc->dev->ops->close)
+               vcc->dev->ops->close(vcc);
+       if (vcc->push)
+               vcc->push(vcc, NULL); /* atmarpd has no push */
+       module_put(vcc->owner);
+
+       while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
+               atm_return(vcc, skb->truesize);
+               kfree_skb(skb);
+       }
 
+       if (vcc->dev && vcc->dev->ops->owner) {
                module_put(vcc->dev->ops->owner);
                atm_dev_put(vcc->dev);
        }
index 25fa3a7..ca37f5a 100644 (file)
@@ -1264,6 +1264,12 @@ static void lec_arp_clear_vccs(struct lec_arp_table *entry)
                entry->vcc = NULL;
        }
        if (entry->recv_vcc) {
+               struct atm_vcc *vcc = entry->recv_vcc;
+               struct lec_vcc_priv *vpriv = LEC_VCC_PRIV(vcc);
+
+               kfree(vpriv);
+               vcc->user_back = NULL;
+
                entry->recv_vcc->push = entry->old_recv_push;
                vcc_release_async(entry->recv_vcc, -EPIPE);
                entry->recv_vcc = NULL;
index 043fd54..97d686d 100644 (file)
@@ -40,7 +40,7 @@ config AX25
          radio as well as information about how to configure an AX.25 port is
          contained in the AX25-HOWTO, available from
          <http://www.tldp.org/docs.html#howto>. You might also want to
-         check out the file <file:Documentation/networking/ax25.txt> in the
+         check out the file <file:Documentation/networking/ax25.rst> in the
          kernel source. More information about digital amateur radio in
          general is on the WWW at
          <http://www.tapr.org/>.
@@ -88,7 +88,7 @@ config NETROM
          users as well as information about how to configure an AX.25 port is
          contained in the Linux Ham Wiki, available from
          <http://www.linux-ax25.org>. You also might want to check out the
-         file <file:Documentation/networking/ax25.txt>. More information about
+         file <file:Documentation/networking/ax25.rst>. More information about
          digital amateur radio in general is on the WWW at
          <http://www.tapr.org/>.
 
@@ -107,7 +107,7 @@ config ROSE
          users as well as information about how to configure an AX.25 port is
          contained in the Linux Ham Wiki, available from
          <http://www.linux-ax25.org>.  You also might want to check out the
-         file <file:Documentation/networking/ax25.txt>. More information about
+         file <file:Documentation/networking/ax25.rst>. More information about
          digital amateur radio in general is on the WWW at
          <http://www.tapr.org/>.
 
index a7c8dd7..e87f19c 100644 (file)
@@ -280,7 +280,7 @@ batadv_iv_ogm_emit_send_time(const struct batadv_priv *bat_priv)
        unsigned int msecs;
 
        msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER;
-       msecs += prandom_u32() % (2 * BATADV_JITTER);
+       msecs += prandom_u32_max(2 * BATADV_JITTER);
 
        return jiffies + msecs_to_jiffies(msecs);
 }
@@ -288,7 +288,7 @@ batadv_iv_ogm_emit_send_time(const struct batadv_priv *bat_priv)
 /* when do we schedule a ogm packet to be sent */
 static unsigned long batadv_iv_ogm_fwd_send_time(void)
 {
-       return jiffies + msecs_to_jiffies(prandom_u32() % (BATADV_JITTER / 2));
+       return jiffies + msecs_to_jiffies(prandom_u32_max(BATADV_JITTER / 2));
 }
 
 /* apply hop penalty for a normal link */
index 1e3172d..353e49c 100644 (file)
@@ -49,7 +49,7 @@ static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface)
        unsigned int msecs;
 
        msecs = atomic_read(&hard_iface->bat_v.elp_interval) - BATADV_JITTER;
-       msecs += prandom_u32() % (2 * BATADV_JITTER);
+       msecs += prandom_u32_max(2 * BATADV_JITTER);
 
        queue_delayed_work(batadv_event_workqueue, &hard_iface->bat_v.elp_wq,
                           msecs_to_jiffies(msecs));
index 9694662..18028b9 100644 (file)
@@ -88,7 +88,7 @@ static void batadv_v_ogm_start_queue_timer(struct batadv_hard_iface *hard_iface)
        unsigned int msecs = BATADV_MAX_AGGREGATION_MS * 1000;
 
        /* msecs * [0.9, 1.1] */
-       msecs += prandom_u32() % (msecs / 5) - (msecs / 10);
+       msecs += prandom_u32_max(msecs / 5) - (msecs / 10);
        queue_delayed_work(batadv_event_workqueue, &hard_iface->bat_v.aggr_wq,
                           msecs_to_jiffies(msecs / 1000));
 }
@@ -107,7 +107,7 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
                return;
 
        msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER;
-       msecs += prandom_u32() % (2 * BATADV_JITTER);
+       msecs += prandom_u32_max(2 * BATADV_JITTER);
        queue_delayed_work(batadv_event_workqueue, &bat_priv->bat_v.ogm_wq,
                           msecs_to_jiffies(msecs));
 }
@@ -893,7 +893,7 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset,
 
        orig_node = batadv_v_ogm_orig_get(bat_priv, ogm_packet->orig);
        if (!orig_node)
-               return;
+               goto out;
 
        neigh_node = batadv_neigh_node_get_or_create(orig_node, if_incoming,
                                                     ethhdr->h_source);
index 2bff2f4..4e03166 100644 (file)
@@ -163,11 +163,6 @@ static inline void batadv_dat_init_own_addr(struct batadv_priv *bat_priv,
 {
 }
 
-static inline void batadv_arp_change_timeout(struct net_device *soft_iface,
-                                            const char *name)
-{
-}
-
 static inline int batadv_dat_init(struct batadv_priv *bat_priv)
 {
        return 0;
index 2a234d0..61d8dbe 100644 (file)
@@ -13,7 +13,7 @@
 #define BATADV_DRIVER_DEVICE "batman-adv"
 
 #ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2020.1"
+#define BATADV_SOURCE_VERSION "2020.2"
 #endif
 
 /* B.A.T.M.A.N. parameters */
index 8f0717c..b0469d1 100644 (file)
@@ -1009,15 +1009,8 @@ static struct batadv_nc_path *batadv_nc_get_path(struct batadv_priv *bat_priv,
  */
 static u8 batadv_nc_random_weight_tq(u8 tq)
 {
-       u8 rand_val, rand_tq;
-
-       get_random_bytes(&rand_val, sizeof(rand_val));
-
        /* randomize the estimated packet loss (max TQ - estimated TQ) */
-       rand_tq = rand_val * (BATADV_TQ_MAX_VALUE - tq);
-
-       /* normalize the randomized packet loss */
-       rand_tq /= BATADV_TQ_MAX_VALUE;
+       u8 rand_tq = prandom_u32_max(BATADV_TQ_MAX_VALUE + 1 - tq);
 
        /* convert to (randomized) estimated tq again */
        return BATADV_TQ_MAX_VALUE - rand_tq;
index 5f05a72..822af54 100644 (file)
@@ -739,6 +739,34 @@ static int batadv_interface_kill_vid(struct net_device *dev, __be16 proto,
        return 0;
 }
 
+/* batman-adv network devices have devices nesting below it and are a special
+ * "super class" of normal network devices; split their locks off into a
+ * separate class since they always nest.
+ */
+static struct lock_class_key batadv_netdev_xmit_lock_key;
+
+/**
+ * batadv_set_lockdep_class_one() - Set lockdep class for a single tx queue
+ * @dev: device which owns the tx queue
+ * @txq: tx queue to modify
+ * @_unused: always NULL
+ */
+static void batadv_set_lockdep_class_one(struct net_device *dev,
+                                        struct netdev_queue *txq,
+                                        void *_unused)
+{
+       lockdep_set_class(&txq->_xmit_lock, &batadv_netdev_xmit_lock_key);
+}
+
+/**
+ * batadv_set_lockdep_class() - Set txq and addr_list lockdep class
+ * @dev: network device to modify
+ */
+static void batadv_set_lockdep_class(struct net_device *dev)
+{
+       netdev_for_each_tx_queue(dev, batadv_set_lockdep_class_one, NULL);
+}
+
 /**
  * batadv_softif_init_late() - late stage initialization of soft interface
  * @dev: registered network device to modify
@@ -752,6 +780,8 @@ static int batadv_softif_init_late(struct net_device *dev)
        int ret;
        size_t cnt_len = sizeof(u64) * BATADV_CNT_NUM;
 
+       batadv_set_lockdep_class(dev);
+
        bat_priv = netdev_priv(dev);
        bat_priv->soft_iface = dev;
 
index c45962d..0f962dc 100644 (file)
@@ -1150,7 +1150,7 @@ static ssize_t batadv_store_throughput_override(struct kobject *kobj,
        ret = batadv_parse_throughput(net_dev, buff, "throughput_override",
                                      &tp_override);
        if (!ret)
-               return count;
+               goto out;
 
        old_tp_override = atomic_read(&hard_iface->bat_v.throughput_override);
        if (old_tp_override == tp_override)
@@ -1190,6 +1190,7 @@ static ssize_t batadv_show_throughput_override(struct kobject *kobj,
 
        tp_override = atomic_read(&hard_iface->bat_v.throughput_override);
 
+       batadv_hardif_put(hard_iface);
        return sprintf(buff, "%u.%u MBit\n", tp_override / 10,
                       tp_override % 10);
 }
index f631b1e..a875475 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/percpu.h>
 #include <linux/printk.h>
 #include <linux/tracepoint.h>
-#include <linux/types.h>
 
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM batadv
index 4a17a66..d152b8e 100644 (file)
@@ -1086,7 +1086,7 @@ struct batadv_priv_bla {
  * struct batadv_priv_debug_log - debug logging data
  */
 struct batadv_priv_debug_log {
-       /** @log_buff: buffer holding the logs (ring bufer) */
+       /** @log_buff: buffer holding the logs (ring buffer) */
        char log_buff[BATADV_LOG_BUF_LEN];
 
        /** @log_start: index of next character to read */
index 4febc82..bb55d92 100644 (file)
@@ -571,7 +571,15 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
        return err < 0 ? NET_XMIT_DROP : err;
 }
 
+static int bt_dev_init(struct net_device *dev)
+{
+       netdev_lockdep_set_classes(dev);
+
+       return 0;
+}
+
 static const struct net_device_ops netdev_ops = {
+       .ndo_init               = bt_dev_init,
        .ndo_start_xmit         = bt_xmit,
 };
 
index 165148c..9e25c65 100644 (file)
@@ -93,6 +93,21 @@ config BT_LEDS
          This option selects a few LED triggers for different
          Bluetooth events.
 
+config BT_MSFTEXT
+       bool "Enable Microsoft extensions"
+       depends on BT
+       help
+         This options enables support for the Microsoft defined HCI
+         vendor extensions.
+
+config BT_DEBUGFS
+       bool "Export Bluetooth internals in debugfs"
+       depends on BT && DEBUG_FS
+       default y
+       help
+         Provide extensive information about internal Bluetooth states
+         in debugfs.
+
 config BT_SELFTEST
        bool "Bluetooth self testing support"
        depends on BT && DEBUG_KERNEL
@@ -120,12 +135,4 @@ config BT_SELFTEST_SMP
          Run test cases for SMP cryptographic functionality, including both
          legacy SMP as well as the Secure Connections features.
 
-config BT_DEBUGFS
-       bool "Export Bluetooth internals in debugfs"
-       depends on BT && DEBUG_FS
-       default y
-       help
-         Provide extensive information about internal Bluetooth states
-         in debugfs.
-
 source "drivers/bluetooth/Kconfig"
index fda41c0..41dd541 100644 (file)
@@ -19,5 +19,6 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
 bluetooth-$(CONFIG_BT_BREDR) += sco.o
 bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
 bluetooth-$(CONFIG_BT_LEDS) += leds.o
+bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o
 bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
 bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
index e245bc1..07c34c5 100644 (file)
@@ -122,8 +122,18 @@ static void hci_conn_cleanup(struct hci_conn *conn)
 
        hci_conn_hash_del(hdev, conn);
 
-       if (hdev->notify)
-               hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
+       if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
+               switch (conn->setting & SCO_AIRMODE_MASK) {
+               case SCO_AIRMODE_CVSD:
+               case SCO_AIRMODE_TRANSP:
+                       if (hdev->notify)
+                               hdev->notify(hdev, HCI_NOTIFY_DISABLE_SCO);
+                       break;
+               }
+       } else {
+               if (hdev->notify)
+                       hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
+       }
 
        hci_conn_del_sysfs(conn);
 
@@ -577,8 +587,15 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
        hci_dev_hold(hdev);
 
        hci_conn_hash_add(hdev, conn);
-       if (hdev->notify)
-               hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
+
+       /* The SCO and eSCO connections will only be notified when their
+        * setup has been completed. This is different to ACL links which
+        * can be notified right away.
+        */
+       if (conn->type != SCO_LINK && conn->type != ESCO_LINK) {
+               if (hdev->notify)
+                       hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
+       }
 
        hci_conn_init_sysfs(conn);
 
index 2e7bc2d..51d3992 100644 (file)
@@ -44,6 +44,7 @@
 #include "hci_debugfs.h"
 #include "smp.h"
 #include "leds.h"
+#include "msft.h"
 
 static void hci_rx_work(struct work_struct *work);
 static void hci_cmd_work(struct work_struct *work);
@@ -637,6 +638,14 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt)
                if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT)
                        events[0] |= 0x40;      /* LE Data Length Change */
 
+               /* If the controller supports LL Privacy feature, enable
+                * the corresponding event.
+                */
+               if (hdev->le_features[0] & HCI_LE_LL_PRIVACY)
+                       events[1] |= 0x02;      /* LE Enhanced Connection
+                                                * Complete
+                                                */
+
                /* If the controller supports Extended Scanner Filter
                 * Policies, enable the correspondig event.
                 */
@@ -710,14 +719,6 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt)
                                                 * Report
                                                 */
 
-               /* If the controller supports the LE Extended Create Connection
-                * command, enable the corresponding event.
-                */
-               if (use_ext_conn(hdev))
-                       events[1] |= 0x02;      /* LE Enhanced Connection
-                                                * Complete
-                                                */
-
                /* If the controller supports the LE Extended Advertising
                 * command, enable the corresponding event.
                 */
@@ -826,6 +827,10 @@ static int hci_init4_req(struct hci_request *req, unsigned long opt)
        if (hdev->commands[29] & 0x20)
                hci_req_add(req, HCI_OP_READ_LOCAL_CODECS, 0, NULL);
 
+       /* Read local pairing options if the HCI command is supported */
+       if (hdev->commands[41] & 0x08)
+               hci_req_add(req, HCI_OP_READ_LOCAL_PAIRING_OPTS, 0, NULL);
+
        /* Get MWS transport configuration if the HCI command is supported */
        if (hdev->commands[30] & 0x08)
                hci_req_add(req, HCI_OP_GET_MWS_TRANSPORT_CONFIG, 0, NULL);
@@ -1563,6 +1568,8 @@ setup_failed:
            hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) && hdev->set_diag)
                ret = hdev->set_diag(hdev, true);
 
+       msft_do_open(hdev);
+
        clear_bit(HCI_INIT, &hdev->flags);
 
        if (!ret) {
@@ -1758,6 +1765,8 @@ int hci_dev_do_close(struct hci_dev *hdev)
 
        hci_sock_dev_event(hdev, HCI_DEV_DOWN);
 
+       msft_do_close(hdev);
+
        if (hdev->flush)
                hdev->flush(hdev);
 
@@ -4240,6 +4249,54 @@ static void __check_timeout(struct hci_dev *hdev, unsigned int cnt)
        }
 }
 
+/* Schedule SCO */
+static void hci_sched_sco(struct hci_dev *hdev)
+{
+       struct hci_conn *conn;
+       struct sk_buff *skb;
+       int quote;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!hci_conn_num(hdev, SCO_LINK))
+               return;
+
+       while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
+               while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+                       BT_DBG("skb %p len %d", skb, skb->len);
+                       hci_send_frame(hdev, skb);
+
+                       conn->sent++;
+                       if (conn->sent == ~0)
+                               conn->sent = 0;
+               }
+       }
+}
+
+static void hci_sched_esco(struct hci_dev *hdev)
+{
+       struct hci_conn *conn;
+       struct sk_buff *skb;
+       int quote;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!hci_conn_num(hdev, ESCO_LINK))
+               return;
+
+       while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK,
+                                                    &quote))) {
+               while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
+                       BT_DBG("skb %p len %d", skb, skb->len);
+                       hci_send_frame(hdev, skb);
+
+                       conn->sent++;
+                       if (conn->sent == ~0)
+                               conn->sent = 0;
+               }
+       }
+}
+
 static void hci_sched_acl_pkt(struct hci_dev *hdev)
 {
        unsigned int cnt = hdev->acl_cnt;
@@ -4271,6 +4328,10 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
                        hdev->acl_cnt--;
                        chan->sent++;
                        chan->conn->sent++;
+
+                       /* Send pending SCO packets right away */
+                       hci_sched_sco(hdev);
+                       hci_sched_esco(hdev);
                }
        }
 
@@ -4355,54 +4416,6 @@ static void hci_sched_acl(struct hci_dev *hdev)
        }
 }
 
-/* Schedule SCO */
-static void hci_sched_sco(struct hci_dev *hdev)
-{
-       struct hci_conn *conn;
-       struct sk_buff *skb;
-       int quote;
-
-       BT_DBG("%s", hdev->name);
-
-       if (!hci_conn_num(hdev, SCO_LINK))
-               return;
-
-       while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
-               while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
-                       BT_DBG("skb %p len %d", skb, skb->len);
-                       hci_send_frame(hdev, skb);
-
-                       conn->sent++;
-                       if (conn->sent == ~0)
-                               conn->sent = 0;
-               }
-       }
-}
-
-static void hci_sched_esco(struct hci_dev *hdev)
-{
-       struct hci_conn *conn;
-       struct sk_buff *skb;
-       int quote;
-
-       BT_DBG("%s", hdev->name);
-
-       if (!hci_conn_num(hdev, ESCO_LINK))
-               return;
-
-       while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK,
-                                                    &quote))) {
-               while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
-                       BT_DBG("skb %p len %d", skb, skb->len);
-                       hci_send_frame(hdev, skb);
-
-                       conn->sent++;
-                       if (conn->sent == ~0)
-                               conn->sent = 0;
-               }
-       }
-}
-
 static void hci_sched_le(struct hci_dev *hdev)
 {
        struct hci_chan *chan;
@@ -4437,6 +4450,10 @@ static void hci_sched_le(struct hci_dev *hdev)
                        cnt--;
                        chan->sent++;
                        chan->conn->sent++;
+
+                       /* Send pending SCO packets right away */
+                       hci_sched_sco(hdev);
+                       hci_sched_esco(hdev);
                }
        }
 
@@ -4459,9 +4476,9 @@ static void hci_tx_work(struct work_struct *work)
 
        if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
                /* Schedule queues and send stuff to HCI driver */
-               hci_sched_acl(hdev);
                hci_sched_sco(hdev);
                hci_sched_esco(hdev);
+               hci_sched_acl(hdev);
                hci_sched_le(hdev);
        }
 
index 6b1314c..5e8af26 100644 (file)
@@ -1075,6 +1075,50 @@ DEFINE_SIMPLE_ATTRIBUTE(auth_payload_timeout_fops,
                        auth_payload_timeout_get,
                        auth_payload_timeout_set, "%llu\n");
 
+static ssize_t force_no_mitm_read(struct file *file,
+                                 char __user *user_buf,
+                                 size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = hci_dev_test_flag(hdev, HCI_FORCE_NO_MITM) ? 'Y' : 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_no_mitm_write(struct file *file,
+                                  const char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf) - 1));
+       bool enable;
+
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       if (enable == hci_dev_test_flag(hdev, HCI_FORCE_NO_MITM))
+               return -EALREADY;
+
+       hci_dev_change_flag(hdev, HCI_FORCE_NO_MITM);
+
+       return count;
+}
+
+static const struct file_operations force_no_mitm_fops = {
+       .open           = simple_open,
+       .read           = force_no_mitm_read,
+       .write          = force_no_mitm_write,
+       .llseek         = default_llseek,
+};
+
 DEFINE_QUIRK_ATTRIBUTE(quirk_strict_duplicate_filter,
                       HCI_QUIRK_STRICT_DUPLICATE_FILTER);
 DEFINE_QUIRK_ATTRIBUTE(quirk_simultaneous_discovery,
@@ -1134,6 +1178,8 @@ void hci_debugfs_create_le(struct hci_dev *hdev)
                            &max_key_size_fops);
        debugfs_create_file("auth_payload_timeout", 0644, hdev->debugfs, hdev,
                            &auth_payload_timeout_fops);
+       debugfs_create_file("force_no_mitm", 0644, hdev->debugfs, hdev,
+                           &force_no_mitm_fops);
 
        debugfs_create_file("quirk_strict_duplicate_filter", 0644,
                            hdev->debugfs, hdev,
index 0a591be..966fc54 100644 (file)
@@ -35,6 +35,7 @@
 #include "a2mp.h"
 #include "amp.h"
 #include "smp.h"
+#include "msft.h"
 
 #define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \
                 "\x00\x00\x00\x00\x00\x00\x00\x00"
@@ -746,6 +747,23 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb)
                bacpy(&hdev->setup_addr, &rp->bdaddr);
 }
 
+static void hci_cc_read_local_pairing_opts(struct hci_dev *hdev,
+                                          struct sk_buff *skb)
+{
+       struct hci_rp_read_local_pairing_opts *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (rp->status)
+               return;
+
+       if (hci_dev_test_flag(hdev, HCI_SETUP) ||
+           hci_dev_test_flag(hdev, HCI_CONFIG)) {
+               hdev->pairing_opts = rp->pairing_opts;
+               hdev->max_enc_key_size = rp->max_key_size;
+       }
+}
+
 static void hci_cc_read_page_scan_activity(struct hci_dev *hdev,
                                           struct sk_buff *skb)
 {
@@ -2607,8 +2625,16 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
        if (ev->status) {
                hci_connect_cfm(conn, ev->status);
                hci_conn_del(conn);
-       } else if (ev->link_type != ACL_LINK)
+       } else if (ev->link_type == SCO_LINK) {
+               switch (conn->setting & SCO_AIRMODE_MASK) {
+               case SCO_AIRMODE_CVSD:
+                       if (hdev->notify)
+                               hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_CVSD);
+                       break;
+               }
+
                hci_connect_cfm(conn, ev->status);
+       }
 
 unlock:
        hci_dev_unlock(hdev);
@@ -3334,6 +3360,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
                hci_cc_read_bd_addr(hdev, skb);
                break;
 
+       case HCI_OP_READ_LOCAL_PAIRING_OPTS:
+               hci_cc_read_local_pairing_opts(hdev, skb);
+               break;
+
        case HCI_OP_READ_PAGE_SCAN_ACTIVITY:
                hci_cc_read_page_scan_activity(hdev, skb);
                break;
@@ -4307,6 +4337,19 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
                break;
        }
 
+       bt_dev_dbg(hdev, "SCO connected with air mode: %02x", ev->air_mode);
+
+       switch (conn->setting & SCO_AIRMODE_MASK) {
+       case SCO_AIRMODE_CVSD:
+               if (hdev->notify)
+                       hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_CVSD);
+               break;
+       case SCO_AIRMODE_TRANSP:
+               if (hdev->notify)
+                       hdev->notify(hdev, HCI_NOTIFY_ENABLE_SCO_TRANSP);
+               break;
+       }
+
        hci_connect_cfm(conn, ev->status);
        if (ev->status)
                hci_conn_del(conn);
@@ -5269,7 +5312,7 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
                case HCI_AUTO_CONN_ALWAYS:
                        /* Devices advertising with ADV_IND or ADV_DIRECT_IND
                         * are triggering a connection attempt. This means
-                        * that incoming connectioms from slave device are
+                        * that incoming connections from slave device are
                         * accepted and also outgoing connections to slave
                         * devices are established when found.
                         */
@@ -5353,7 +5396,8 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
 
        /* Adjust for actual length */
        if (len != real_len) {
-               bt_dev_err_ratelimited(hdev, "advertising data len corrected");
+               bt_dev_err_ratelimited(hdev, "advertising data len corrected %u -> %u",
+                                      len, real_len);
                len = real_len;
        }
 
@@ -6145,6 +6189,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_num_comp_blocks_evt(hdev, skb);
                break;
 
+       case HCI_EV_VENDOR:
+               msft_vendor_evt(hdev, skb);
+               break;
+
        default:
                BT_DBG("%s event 0x%2.2x", hdev->name, event);
                break;
index 649e1e5..9ea4010 100644 (file)
@@ -2723,6 +2723,8 @@ static int active_scan(struct hci_request *req, unsigned long opt)
        uint16_t interval = opt;
        struct hci_dev *hdev = req->hdev;
        u8 own_addr_type;
+       /* White list is not used for discovery */
+       u8 filter_policy = 0x00;
        int err;
 
        BT_DBG("%s", hdev->name);
@@ -2744,7 +2746,7 @@ static int active_scan(struct hci_request *req, unsigned long opt)
                own_addr_type = ADDR_LE_DEV_PUBLIC;
 
        hci_req_start_scan(req, LE_SCAN_ACTIVE, interval, DISCOV_LE_SCAN_WIN,
-                          own_addr_type, 0);
+                          own_addr_type, filter_policy);
        return 0;
 }
 
index 117ba20..1cea42e 100644 (file)
@@ -395,6 +395,24 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
        return sizeof(struct sockaddr_l2);
 }
 
+static int l2cap_get_mode(struct l2cap_chan *chan)
+{
+       switch (chan->mode) {
+       case L2CAP_MODE_BASIC:
+               return BT_MODE_BASIC;
+       case L2CAP_MODE_ERTM:
+               return BT_MODE_ERTM;
+       case L2CAP_MODE_STREAMING:
+               return BT_MODE_STREAMING;
+       case L2CAP_MODE_LE_FLOWCTL:
+               return BT_MODE_LE_FLOWCTL;
+       case L2CAP_MODE_EXT_FLOWCTL:
+               return BT_MODE_EXT_FLOWCTL;
+       }
+
+       return -EINVAL;
+}
+
 static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
                                     char __user *optval, int __user *optlen)
 {
@@ -424,6 +442,20 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
                        break;
                }
 
+               /* Only BR/EDR modes are supported here */
+               switch (chan->mode) {
+               case L2CAP_MODE_BASIC:
+               case L2CAP_MODE_ERTM:
+               case L2CAP_MODE_STREAMING:
+                       break;
+               default:
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (err < 0)
+                       break;
+
                memset(&opts, 0, sizeof(opts));
                opts.imtu     = chan->imtu;
                opts.omtu     = chan->omtu;
@@ -508,7 +540,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
        struct bt_security sec;
        struct bt_power pwr;
        u32 phys;
-       int len, err = 0;
+       int len, mode, err = 0;
 
        BT_DBG("sk %p", sk);
 
@@ -624,6 +656,27 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
                        err = -EFAULT;
                break;
 
+       case BT_MODE:
+               if (!enable_ecred) {
+                       err = -ENOPROTOOPT;
+                       break;
+               }
+
+               if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               mode = l2cap_get_mode(chan);
+               if (mode < 0) {
+                       err = mode;
+                       break;
+               }
+
+               if (put_user(mode, (u8 __user *) optval))
+                       err = -EFAULT;
+               break;
+
        default:
                err = -ENOPROTOOPT;
                break;
@@ -698,10 +751,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
                        break;
                }
 
-               chan->mode = opts.mode;
-               switch (chan->mode) {
-               case L2CAP_MODE_LE_FLOWCTL:
-                       break;
+               /* Only BR/EDR modes are supported here */
+               switch (opts.mode) {
                case L2CAP_MODE_BASIC:
                        clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
                        break;
@@ -715,6 +766,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
                        break;
                }
 
+               if (err < 0)
+                       break;
+
+               chan->mode = opts.mode;
+
                BT_DBG("mode 0x%2.2x", chan->mode);
 
                chan->imtu = opts.imtu;
@@ -763,6 +819,45 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
        return err;
 }
 
+static int l2cap_set_mode(struct l2cap_chan *chan, u8 mode)
+{
+       switch (mode) {
+       case BT_MODE_BASIC:
+               if (bdaddr_type_is_le(chan->src_type))
+                       return -EINVAL;
+               mode = L2CAP_MODE_BASIC;
+               clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
+               break;
+       case BT_MODE_ERTM:
+               if (!disable_ertm || bdaddr_type_is_le(chan->src_type))
+                       return -EINVAL;
+               mode = L2CAP_MODE_ERTM;
+               break;
+       case BT_MODE_STREAMING:
+               if (!disable_ertm || bdaddr_type_is_le(chan->src_type))
+                       return -EINVAL;
+               mode = L2CAP_MODE_STREAMING;
+               break;
+       case BT_MODE_LE_FLOWCTL:
+               if (!bdaddr_type_is_le(chan->src_type))
+                       return -EINVAL;
+               mode = L2CAP_MODE_LE_FLOWCTL;
+               break;
+       case BT_MODE_EXT_FLOWCTL:
+               /* TODO: Add support for ECRED PDUs to BR/EDR */
+               if (!bdaddr_type_is_le(chan->src_type))
+                       return -EINVAL;
+               mode = L2CAP_MODE_EXT_FLOWCTL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       chan->mode = mode;
+
+       return 0;
+}
+
 static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
                                 char __user *optval, unsigned int optlen)
 {
@@ -968,6 +1063,39 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
 
                break;
 
+       case BT_MODE:
+               if (!enable_ecred) {
+                       err = -ENOPROTOOPT;
+                       break;
+               }
+
+               BT_DBG("sk->sk_state %u", sk->sk_state);
+
+               if (sk->sk_state != BT_BOUND) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (get_user(opt, (u8 __user *) optval)) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               BT_DBG("opt %u", opt);
+
+               err = l2cap_set_mode(chan, opt);
+               if (err)
+                       break;
+
+               BT_DBG("mode 0x%2.2x", chan->mode);
+
+               break;
+
        default:
                err = -ENOPROTOOPT;
                break;
index 6552003..f8c0a4f 100644 (file)
@@ -38,7 +38,7 @@
 #include "mgmt_util.h"
 
 #define MGMT_VERSION   1
-#define MGMT_REVISION  16
+#define MGMT_REVISION  17
 
 static const u16 mgmt_commands[] = {
        MGMT_OP_READ_INDEX_LIST,
@@ -108,6 +108,7 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_SET_APPEARANCE,
        MGMT_OP_SET_BLOCKED_KEYS,
        MGMT_OP_SET_WIDEBAND_SPEECH,
+       MGMT_OP_READ_SECURITY_INFO,
 };
 
 static const u16 mgmt_events[] = {
@@ -155,6 +156,7 @@ static const u16 mgmt_untrusted_commands[] = {
        MGMT_OP_READ_CONFIG_INFO,
        MGMT_OP_READ_EXT_INDEX_LIST,
        MGMT_OP_READ_EXT_INFO,
+       MGMT_OP_READ_SECURITY_INFO,
 };
 
 static const u16 mgmt_untrusted_events[] = {
@@ -3659,6 +3661,55 @@ unlock:
        return err;
 }
 
+static int read_security_info(struct sock *sk, struct hci_dev *hdev,
+                             void *data, u16 data_len)
+{
+       char buf[16];
+       struct mgmt_rp_read_security_info *rp = (void *)buf;
+       u16 sec_len = 0;
+       u8 flags = 0;
+
+       bt_dev_dbg(hdev, "sock %p", sk);
+
+       memset(&buf, 0, sizeof(buf));
+
+       hci_dev_lock(hdev);
+
+       /* When the Read Simple Pairing Options command is supported, then
+        * the remote public key validation is supported.
+        */
+       if (hdev->commands[41] & 0x08)
+               flags |= 0x01;  /* Remote public key validation (BR/EDR) */
+
+       flags |= 0x02;          /* Remote public key validation (LE) */
+
+       /* When the Read Encryption Key Size command is supported, then the
+        * encryption key size is enforced.
+        */
+       if (hdev->commands[20] & 0x10)
+               flags |= 0x04;  /* Encryption key size enforcement (BR/EDR) */
+
+       flags |= 0x08;          /* Encryption key size enforcement (LE) */
+
+       sec_len = eir_append_data(rp->sec, sec_len, 0x01, &flags, 1);
+
+       /* When the Read Simple Pairing Options command is supported, then
+        * also max encryption key size information is provided.
+        */
+       if (hdev->commands[41] & 0x08)
+               sec_len = eir_append_le16(rp->sec, sec_len, 0x02,
+                                         hdev->max_enc_key_size);
+
+       sec_len = eir_append_le16(rp->sec, sec_len, 0x03, SMP_MAX_ENC_KEY_SIZE);
+
+       rp->sec_len = cpu_to_le16(sec_len);
+
+       hci_dev_unlock(hdev);
+
+       return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_SECURITY_INFO, 0,
+                                rp, sizeof(*rp) + sec_len);
+}
+
 static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
                                         u16 opcode, struct sk_buff *skb)
 {
@@ -7099,6 +7150,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
        { set_blocked_keys,        MGMT_OP_SET_BLOCKED_KEYS_SIZE,
                                                HCI_MGMT_VAR_LEN },
        { set_wideband_speech,     MGMT_SETTING_SIZE },
+       { read_security_info,      MGMT_READ_SECURITY_INFO_SIZE,
+                                               HCI_MGMT_UNTRUSTED },
 };
 
 void mgmt_index_added(struct hci_dev *hdev)
diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c
new file mode 100644 (file)
index 0000000..d6c4e6b
--- /dev/null
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 Google Corporation
+ */
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "msft.h"
+
+#define MSFT_OP_READ_SUPPORTED_FEATURES                0x00
+struct msft_cp_read_supported_features {
+       __u8   sub_opcode;
+} __packed;
+struct msft_rp_read_supported_features {
+       __u8   status;
+       __u8   sub_opcode;
+       __le64 features;
+       __u8   evt_prefix_len;
+       __u8   evt_prefix[0];
+} __packed;
+
+struct msft_data {
+       __u64 features;
+       __u8  evt_prefix_len;
+       __u8  *evt_prefix;
+};
+
+static bool read_supported_features(struct hci_dev *hdev,
+                                   struct msft_data *msft)
+{
+       struct msft_cp_read_supported_features cp;
+       struct msft_rp_read_supported_features *rp;
+       struct sk_buff *skb;
+
+       cp.sub_opcode = MSFT_OP_READ_SUPPORTED_FEATURES;
+
+       skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp,
+                            HCI_CMD_TIMEOUT);
+       if (IS_ERR(skb)) {
+               bt_dev_err(hdev, "Failed to read MSFT supported features (%ld)",
+                          PTR_ERR(skb));
+               return false;
+       }
+
+       if (skb->len < sizeof(*rp)) {
+               bt_dev_err(hdev, "MSFT supported features length mismatch");
+               goto failed;
+       }
+
+       rp = (struct msft_rp_read_supported_features *)skb->data;
+
+       if (rp->sub_opcode != MSFT_OP_READ_SUPPORTED_FEATURES)
+               goto failed;
+
+       if (rp->evt_prefix_len > 0) {
+               msft->evt_prefix = kmemdup(rp->evt_prefix, rp->evt_prefix_len,
+                                          GFP_KERNEL);
+               if (!msft->evt_prefix)
+                       goto failed;
+       }
+
+       msft->evt_prefix_len = rp->evt_prefix_len;
+       msft->features = __le64_to_cpu(rp->features);
+
+       kfree_skb(skb);
+       return true;
+
+failed:
+       kfree_skb(skb);
+       return false;
+}
+
+void msft_do_open(struct hci_dev *hdev)
+{
+       struct msft_data *msft;
+
+       if (hdev->msft_opcode == HCI_OP_NOP)
+               return;
+
+       bt_dev_dbg(hdev, "Initialize MSFT extension");
+
+       msft = kzalloc(sizeof(*msft), GFP_KERNEL);
+       if (!msft)
+               return;
+
+       if (!read_supported_features(hdev, msft)) {
+               kfree(msft);
+               return;
+       }
+
+       hdev->msft_data = msft;
+}
+
+void msft_do_close(struct hci_dev *hdev)
+{
+       struct msft_data *msft = hdev->msft_data;
+
+       if (!msft)
+               return;
+
+       bt_dev_dbg(hdev, "Cleanup of MSFT extension");
+
+       hdev->msft_data = NULL;
+
+       kfree(msft->evt_prefix);
+       kfree(msft);
+}
+
+void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct msft_data *msft = hdev->msft_data;
+       u8 event;
+
+       if (!msft)
+               return;
+
+       /* When the extension has defined an event prefix, check that it
+        * matches, and otherwise just return.
+        */
+       if (msft->evt_prefix_len > 0) {
+               if (skb->len < msft->evt_prefix_len)
+                       return;
+
+               if (memcmp(skb->data, msft->evt_prefix, msft->evt_prefix_len))
+                       return;
+
+               skb_pull(skb, msft->evt_prefix_len);
+       }
+
+       /* Every event starts at least with an event code and the rest of
+        * the data is variable and depends on the event code.
+        */
+       if (skb->len < 1)
+               return;
+
+       event = *skb->data;
+       skb_pull(skb, 1);
+
+       bt_dev_dbg(hdev, "MSFT vendor event %u", event);
+}
diff --git a/net/bluetooth/msft.h b/net/bluetooth/msft.h
new file mode 100644 (file)
index 0000000..5aa9130
--- /dev/null
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 Google Corporation
+ */
+
+#if IS_ENABLED(CONFIG_BT_MSFTEXT)
+
+void msft_do_open(struct hci_dev *hdev);
+void msft_do_close(struct hci_dev *hdev);
+void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb);
+
+#else
+
+static inline void msft_do_open(struct hci_dev *hdev) {}
+static inline void msft_do_close(struct hci_dev *hdev) {}
+static inline void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb) {}
+
+#endif
index 1476a91..df22cbf 100644 (file)
@@ -854,7 +854,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
        struct l2cap_chan *chan = conn->smp;
        struct smp_chan *smp = chan->data;
        u32 passkey = 0;
-       int ret = 0;
+       int ret;
 
        /* Initialize key for JUST WORKS */
        memset(smp->tk, 0, sizeof(smp->tk));
@@ -883,9 +883,16 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
            hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
                smp->method = JUST_WORKS;
 
-       /* If Just Works, Continue with Zero TK */
+       /* If Just Works, Continue with Zero TK and ask user-space for
+        * confirmation */
        if (smp->method == JUST_WORKS) {
-               set_bit(SMP_FLAG_TK_VALID, &smp->flags);
+               ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
+                                               hcon->type,
+                                               hcon->dst_type,
+                                               passkey, 1);
+               if (ret)
+                       return ret;
+               set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
                return 0;
        }
 
@@ -2194,7 +2201,7 @@ mackey_and_ltk:
        if (err)
                return SMP_UNSPECIFIED;
 
-       if (smp->method == JUST_WORKS || smp->method == REQ_OOB) {
+       if (smp->method == REQ_OOB) {
                if (hcon->out) {
                        sc_dhkey_check(smp);
                        SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
@@ -2209,6 +2216,9 @@ mackey_and_ltk:
        confirm_hint = 0;
 
 confirm:
+       if (smp->method == JUST_WORKS)
+               confirm_hint = 1;
+
        err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type,
                                        hcon->dst_type, passkey, confirm_hint);
        if (err)
@@ -2385,12 +2395,17 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
                        authreq |= SMP_AUTH_CT2;
        }
 
-       /* Require MITM if IO Capability allows or the security level
-        * requires it.
+       /* Don't attempt to set MITM if setting is overridden by debugfs
+        * Needed to pass certification test SM/MAS/PKE/BV-01-C
         */
-       if (hcon->io_capability != HCI_IO_NO_INPUT_OUTPUT ||
-           hcon->pending_sec_level > BT_SECURITY_MEDIUM)
-               authreq |= SMP_AUTH_MITM;
+       if (!hci_dev_test_flag(hcon->hdev, HCI_FORCE_NO_MITM)) {
+               /* Require MITM if IO Capability allows or the security level
+                * requires it.
+                */
+               if (hcon->io_capability != HCI_IO_NO_INPUT_OUTPUT ||
+                   hcon->pending_sec_level > BT_SECURITY_MEDIUM)
+                       authreq |= SMP_AUTH_MITM;
+       }
 
        if (hcon->role == HCI_ROLE_MASTER) {
                struct smp_cmd_pairing cp;
index e4fb050..51a6414 100644 (file)
@@ -61,3 +61,15 @@ config BRIDGE_VLAN_FILTERING
          Say N to exclude this support and reduce the binary size.
 
          If unsure, say Y.
+
+config BRIDGE_MRP
+       bool "MRP protocol"
+       depends on BRIDGE
+       default n
+       help
+         If you say Y here, then the Ethernet bridge will be able to run MRP
+         protocol to detect loops
+
+         Say N to exclude this support and reduce the binary size.
+
+         If unsure, say N.
index 49da7ae..ccb3942 100644 (file)
@@ -25,3 +25,5 @@ bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o br_vlan_tunnel.o br_vlan_opt
 bridge-$(CONFIG_NET_SWITCHDEV) += br_switchdev.o
 
 obj-$(CONFIG_NETFILTER) += netfilter/
+
+bridge-$(CONFIG_BRIDGE_MRP)    += br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o
index 0e3dbc5..8ec1362 100644 (file)
@@ -463,6 +463,9 @@ void br_dev_setup(struct net_device *dev)
        spin_lock_init(&br->lock);
        INIT_LIST_HEAD(&br->port_list);
        INIT_HLIST_HEAD(&br->fdb_list);
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+       INIT_LIST_HEAD(&br->mrp_list);
+#endif
        spin_lock_init(&br->hash_lock);
 
        br->bridge_id.prio[0] = 0x80;
index 4fe30b1..ca685c0 100644 (file)
@@ -333,6 +333,8 @@ static void del_nbp(struct net_bridge_port *p)
        br_stp_disable_port(p);
        spin_unlock_bh(&br->lock);
 
+       br_mrp_port_del(br, p);
+
        br_ifinfo_notify(RTM_DELLINK, NULL, p);
 
        list_del_rcu(&p->list);
index fcc2608..d5c34f3 100644 (file)
@@ -342,6 +342,9 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
                }
        }
 
+       if (unlikely(br_mrp_process(p, skb)))
+               return RX_HANDLER_PASS;
+
 forward:
        switch (p->state) {
        case BR_STATE_FORWARDING:
index ae22d78..5e71fc8 100644 (file)
@@ -242,8 +242,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
                        return -EPERM;
 
-               br_stp_set_enabled(br, args[1]);
-               ret = 0;
+               ret = br_stp_set_enabled(br, args[1], NULL);
                break;
 
        case BRCTL_SET_BRIDGE_PRIORITY:
diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c
new file mode 100644 (file)
index 0000000..d7bc09d
--- /dev/null
@@ -0,0 +1,559 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/mrp_bridge.h>
+#include "br_private_mrp.h"
+
+static const u8 mrp_test_dmac[ETH_ALEN] = { 0x1, 0x15, 0x4e, 0x0, 0x0, 0x1 };
+
+static struct net_bridge_port *br_mrp_get_port(struct net_bridge *br,
+                                              u32 ifindex)
+{
+       struct net_bridge_port *res = NULL;
+       struct net_bridge_port *port;
+
+       list_for_each_entry(port, &br->port_list, list) {
+               if (port->dev->ifindex == ifindex) {
+                       res = port;
+                       break;
+               }
+       }
+
+       return res;
+}
+
+static struct br_mrp *br_mrp_find_id(struct net_bridge *br, u32 ring_id)
+{
+       struct br_mrp *res = NULL;
+       struct br_mrp *mrp;
+
+       list_for_each_entry_rcu(mrp, &br->mrp_list, list,
+                               lockdep_rtnl_is_held()) {
+               if (mrp->ring_id == ring_id) {
+                       res = mrp;
+                       break;
+               }
+       }
+
+       return res;
+}
+
+static struct br_mrp *br_mrp_find_port(struct net_bridge *br,
+                                      struct net_bridge_port *p)
+{
+       struct br_mrp *res = NULL;
+       struct br_mrp *mrp;
+
+       list_for_each_entry_rcu(mrp, &br->mrp_list, list,
+                               lockdep_rtnl_is_held()) {
+               if (rcu_access_pointer(mrp->p_port) == p ||
+                   rcu_access_pointer(mrp->s_port) == p) {
+                       res = mrp;
+                       break;
+               }
+       }
+
+       return res;
+}
+
+static int br_mrp_next_seq(struct br_mrp *mrp)
+{
+       mrp->seq_id++;
+       return mrp->seq_id;
+}
+
+static struct sk_buff *br_mrp_skb_alloc(struct net_bridge_port *p,
+                                       const u8 *src, const u8 *dst)
+{
+       struct ethhdr *eth_hdr;
+       struct sk_buff *skb;
+       u16 *version;
+
+       skb = dev_alloc_skb(MRP_MAX_FRAME_LENGTH);
+       if (!skb)
+               return NULL;
+
+       skb->dev = p->dev;
+       skb->protocol = htons(ETH_P_MRP);
+       skb->priority = MRP_FRAME_PRIO;
+       skb_reserve(skb, sizeof(*eth_hdr));
+
+       eth_hdr = skb_push(skb, sizeof(*eth_hdr));
+       ether_addr_copy(eth_hdr->h_dest, dst);
+       ether_addr_copy(eth_hdr->h_source, src);
+       eth_hdr->h_proto = htons(ETH_P_MRP);
+
+       version = skb_put(skb, sizeof(*version));
+       *version = cpu_to_be16(MRP_VERSION);
+
+       return skb;
+}
+
+static void br_mrp_skb_tlv(struct sk_buff *skb,
+                          enum br_mrp_tlv_header_type type,
+                          u8 length)
+{
+       struct br_mrp_tlv_hdr *hdr;
+
+       hdr = skb_put(skb, sizeof(*hdr));
+       hdr->type = type;
+       hdr->length = length;
+}
+
+static void br_mrp_skb_common(struct sk_buff *skb, struct br_mrp *mrp)
+{
+       struct br_mrp_common_hdr *hdr;
+
+       br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_COMMON, sizeof(*hdr));
+
+       hdr = skb_put(skb, sizeof(*hdr));
+       hdr->seq_id = cpu_to_be16(br_mrp_next_seq(mrp));
+       memset(hdr->domain, 0xff, MRP_DOMAIN_UUID_LENGTH);
+}
+
+static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,
+                                            struct net_bridge_port *p,
+                                            enum br_mrp_port_role_type port_role)
+{
+       struct br_mrp_ring_test_hdr *hdr = NULL;
+       struct sk_buff *skb = NULL;
+
+       if (!p)
+               return NULL;
+
+       skb = br_mrp_skb_alloc(p, p->dev->dev_addr, mrp_test_dmac);
+       if (!skb)
+               return NULL;
+
+       br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_RING_TEST, sizeof(*hdr));
+       hdr = skb_put(skb, sizeof(*hdr));
+
+       hdr->prio = cpu_to_be16(MRP_DEFAULT_PRIO);
+       ether_addr_copy(hdr->sa, p->br->dev->dev_addr);
+       hdr->port_role = cpu_to_be16(port_role);
+       hdr->state = cpu_to_be16(mrp->ring_state);
+       hdr->transitions = cpu_to_be16(mrp->ring_transitions);
+       hdr->timestamp = cpu_to_be32(jiffies_to_msecs(jiffies));
+
+       br_mrp_skb_common(skb, mrp);
+       br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_END, 0x0);
+
+       return skb;
+}
+
+static void br_mrp_test_work_expired(struct work_struct *work)
+{
+       struct delayed_work *del_work = to_delayed_work(work);
+       struct br_mrp *mrp = container_of(del_work, struct br_mrp, test_work);
+       struct net_bridge_port *p;
+       bool notify_open = false;
+       struct sk_buff *skb;
+
+       if (time_before_eq(mrp->test_end, jiffies))
+               return;
+
+       if (mrp->test_count_miss < mrp->test_max_miss) {
+               mrp->test_count_miss++;
+       } else {
+               /* Notify that the ring is open only if the ring state is
+                * closed, otherwise it would continue to notify at every
+                * interval.
+                */
+               if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED)
+                       notify_open = true;
+       }
+
+       rcu_read_lock();
+
+       p = rcu_dereference(mrp->p_port);
+       if (p) {
+               skb = br_mrp_alloc_test_skb(mrp, p, BR_MRP_PORT_ROLE_PRIMARY);
+               if (!skb)
+                       goto out;
+
+               skb_reset_network_header(skb);
+               dev_queue_xmit(skb);
+
+               if (notify_open && !mrp->ring_role_offloaded)
+                       br_mrp_port_open(p->dev, true);
+       }
+
+       p = rcu_dereference(mrp->s_port);
+       if (p) {
+               skb = br_mrp_alloc_test_skb(mrp, p, BR_MRP_PORT_ROLE_SECONDARY);
+               if (!skb)
+                       goto out;
+
+               skb_reset_network_header(skb);
+               dev_queue_xmit(skb);
+
+               if (notify_open && !mrp->ring_role_offloaded)
+                       br_mrp_port_open(p->dev, true);
+       }
+
+out:
+       rcu_read_unlock();
+
+       queue_delayed_work(system_wq, &mrp->test_work,
+                          usecs_to_jiffies(mrp->test_interval));
+}
+
+/* Deletes the MRP instance.
+ * note: called under rtnl_lock
+ */
+static void br_mrp_del_impl(struct net_bridge *br, struct br_mrp *mrp)
+{
+       struct net_bridge_port *p;
+
+       /* Stop sending MRP_Test frames */
+       cancel_delayed_work_sync(&mrp->test_work);
+       br_mrp_switchdev_send_ring_test(br, mrp, 0, 0, 0);
+
+       br_mrp_switchdev_del(br, mrp);
+
+       /* Reset the ports */
+       p = rtnl_dereference(mrp->p_port);
+       if (p) {
+               spin_lock_bh(&br->lock);
+               p->state = BR_STATE_FORWARDING;
+               p->flags &= ~BR_MRP_AWARE;
+               spin_unlock_bh(&br->lock);
+               br_mrp_port_switchdev_set_state(p, BR_STATE_FORWARDING);
+               rcu_assign_pointer(mrp->p_port, NULL);
+       }
+
+       p = rtnl_dereference(mrp->s_port);
+       if (p) {
+               spin_lock_bh(&br->lock);
+               p->state = BR_STATE_FORWARDING;
+               p->flags &= ~BR_MRP_AWARE;
+               spin_unlock_bh(&br->lock);
+               br_mrp_port_switchdev_set_state(p, BR_STATE_FORWARDING);
+               rcu_assign_pointer(mrp->s_port, NULL);
+       }
+
+       list_del_rcu(&mrp->list);
+       kfree_rcu(mrp, rcu);
+}
+
+/* Adds a new MRP instance.
+ * note: called under rtnl_lock
+ */
+int br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance)
+{
+       struct net_bridge_port *p;
+       struct br_mrp *mrp;
+       int err;
+
+       /* If the ring exists, it is not possible to create another one with the
+        * same ring_id
+        */
+       mrp = br_mrp_find_id(br, instance->ring_id);
+       if (mrp)
+               return -EINVAL;
+
+       if (!br_mrp_get_port(br, instance->p_ifindex) ||
+           !br_mrp_get_port(br, instance->s_ifindex))
+               return -EINVAL;
+
+       mrp = kzalloc(sizeof(*mrp), GFP_KERNEL);
+       if (!mrp)
+               return -ENOMEM;
+
+       mrp->ring_id = instance->ring_id;
+
+       p = br_mrp_get_port(br, instance->p_ifindex);
+       spin_lock_bh(&br->lock);
+       p->state = BR_STATE_FORWARDING;
+       p->flags |= BR_MRP_AWARE;
+       spin_unlock_bh(&br->lock);
+       rcu_assign_pointer(mrp->p_port, p);
+
+       p = br_mrp_get_port(br, instance->s_ifindex);
+       spin_lock_bh(&br->lock);
+       p->state = BR_STATE_FORWARDING;
+       p->flags |= BR_MRP_AWARE;
+       spin_unlock_bh(&br->lock);
+       rcu_assign_pointer(mrp->s_port, p);
+
+       INIT_DELAYED_WORK(&mrp->test_work, br_mrp_test_work_expired);
+       list_add_tail_rcu(&mrp->list, &br->mrp_list);
+
+       err = br_mrp_switchdev_add(br, mrp);
+       if (err)
+               goto delete_mrp;
+
+       return 0;
+
+delete_mrp:
+       br_mrp_del_impl(br, mrp);
+
+       return err;
+}
+
+/* Deletes the MRP instance from which the port is part of
+ * note: called under rtnl_lock
+ */
+void br_mrp_port_del(struct net_bridge *br, struct net_bridge_port *p)
+{
+       struct br_mrp *mrp = br_mrp_find_port(br, p);
+
+       /* If the port is not part of a MRP instance just bail out */
+       if (!mrp)
+               return;
+
+       br_mrp_del_impl(br, mrp);
+}
+
+/* Deletes existing MRP instance based on ring_id
+ * note: called under rtnl_lock
+ */
+int br_mrp_del(struct net_bridge *br, struct br_mrp_instance *instance)
+{
+       struct br_mrp *mrp = br_mrp_find_id(br, instance->ring_id);
+
+       if (!mrp)
+               return -EINVAL;
+
+       br_mrp_del_impl(br, mrp);
+
+       return 0;
+}
+
+/* Set port state, port state can be forwarding, blocked or disabled
+ * note: already called with rtnl_lock
+ */
+int br_mrp_set_port_state(struct net_bridge_port *p,
+                         enum br_mrp_port_state_type state)
+{
+       if (!p || !(p->flags & BR_MRP_AWARE))
+               return -EINVAL;
+
+       spin_lock_bh(&p->br->lock);
+
+       if (state == BR_MRP_PORT_STATE_FORWARDING)
+               p->state = BR_STATE_FORWARDING;
+       else
+               p->state = BR_STATE_BLOCKING;
+
+       spin_unlock_bh(&p->br->lock);
+
+       br_mrp_port_switchdev_set_state(p, state);
+
+       return 0;
+}
+
+/* Set port role, port role can be primary or secondary
+ * note: already called with rtnl_lock
+ */
+int br_mrp_set_port_role(struct net_bridge_port *p,
+                        struct br_mrp_port_role *role)
+{
+       struct br_mrp *mrp;
+
+       if (!p || !(p->flags & BR_MRP_AWARE))
+               return -EINVAL;
+
+       mrp = br_mrp_find_id(p->br, role->ring_id);
+
+       if (!mrp)
+               return -EINVAL;
+
+       if (role->role == BR_MRP_PORT_ROLE_PRIMARY)
+               rcu_assign_pointer(mrp->p_port, p);
+       else
+               rcu_assign_pointer(mrp->s_port, p);
+
+       br_mrp_port_switchdev_set_role(p, role->role);
+
+       return 0;
+}
+
+/* Set ring state, ring state can be only Open or Closed
+ * note: already called with rtnl_lock
+ */
+int br_mrp_set_ring_state(struct net_bridge *br,
+                         struct br_mrp_ring_state *state)
+{
+       struct br_mrp *mrp = br_mrp_find_id(br, state->ring_id);
+
+       if (!mrp)
+               return -EINVAL;
+
+       if (mrp->ring_state == BR_MRP_RING_STATE_CLOSED &&
+           state->ring_state != BR_MRP_RING_STATE_CLOSED)
+               mrp->ring_transitions++;
+
+       mrp->ring_state = state->ring_state;
+
+       br_mrp_switchdev_set_ring_state(br, mrp, state->ring_state);
+
+       return 0;
+}
+
+/* Set ring role, ring role can be only MRM(Media Redundancy Manager) or
+ * MRC(Media Redundancy Client).
+ * note: already called with rtnl_lock
+ */
+int br_mrp_set_ring_role(struct net_bridge *br,
+                        struct br_mrp_ring_role *role)
+{
+       struct br_mrp *mrp = br_mrp_find_id(br, role->ring_id);
+       int err;
+
+       if (!mrp)
+               return -EINVAL;
+
+       mrp->ring_role = role->ring_role;
+
+       /* If there is an error just bailed out */
+       err = br_mrp_switchdev_set_ring_role(br, mrp, role->ring_role);
+       if (err && err != -EOPNOTSUPP)
+               return err;
+
+       /* Now detect if the HW actually applied the role or not. If the HW
+        * applied the role it means that the SW will not to do those operations
+        * anymore. For example if the role ir MRM then the HW will notify the
+        * SW when ring is open, but if the is not pushed to the HW the SW will
+        * need to detect when the ring is open
+        */
+       mrp->ring_role_offloaded = err == -EOPNOTSUPP ? 0 : 1;
+
+       return 0;
+}
+
+/* Start to generate MRP test frames, the frames are generated by HW and if it
+ * fails, they are generated by the SW.
+ * note: already called with rtnl_lock
+ */
+int br_mrp_start_test(struct net_bridge *br,
+                     struct br_mrp_start_test *test)
+{
+       struct br_mrp *mrp = br_mrp_find_id(br, test->ring_id);
+
+       if (!mrp)
+               return -EINVAL;
+
+       /* Try to push it to the HW and if it fails then continue to generate in
+        * SW and if that also fails then return error
+        */
+       if (!br_mrp_switchdev_send_ring_test(br, mrp, test->interval,
+                                            test->max_miss, test->period))
+               return 0;
+
+       mrp->test_interval = test->interval;
+       mrp->test_end = jiffies + usecs_to_jiffies(test->period);
+       mrp->test_max_miss = test->max_miss;
+       mrp->test_count_miss = 0;
+       queue_delayed_work(system_wq, &mrp->test_work,
+                          usecs_to_jiffies(test->interval));
+
+       return 0;
+}
+
+/* Process only MRP Test frame. All the other MRP frames are processed by
+ * userspace application
+ * note: already called with rcu_read_lock
+ */
+static void br_mrp_mrm_process(struct br_mrp *mrp, struct net_bridge_port *port,
+                              struct sk_buff *skb)
+{
+       const struct br_mrp_tlv_hdr *hdr;
+       struct br_mrp_tlv_hdr _hdr;
+
+       /* Each MRP header starts with a version field which is 16 bits.
+        * Therefore skip the version and get directly the TLV header.
+        */
+       hdr = skb_header_pointer(skb, sizeof(uint16_t), sizeof(_hdr), &_hdr);
+       if (!hdr)
+               return;
+
+       if (hdr->type != BR_MRP_TLV_HEADER_RING_TEST)
+               return;
+
+       mrp->test_count_miss = 0;
+
+       /* Notify the userspace that the ring is closed only when the ring is
+        * not closed
+        */
+       if (mrp->ring_state != BR_MRP_RING_STATE_CLOSED)
+               br_mrp_port_open(port->dev, false);
+}
+
+/* This will just forward the frame to the other mrp ring port(MRC role) or will
+ * not do anything.
+ * note: already called with rcu_read_lock
+ */
+static int br_mrp_rcv(struct net_bridge_port *p,
+                     struct sk_buff *skb, struct net_device *dev)
+{
+       struct net_device *s_dev, *p_dev, *d_dev;
+       struct net_bridge_port *p_port, *s_port;
+       struct net_bridge *br;
+       struct sk_buff *nskb;
+       struct br_mrp *mrp;
+
+       /* If port is disabled don't accept any frames */
+       if (p->state == BR_STATE_DISABLED)
+               return 0;
+
+       br = p->br;
+       mrp =  br_mrp_find_port(br, p);
+       if (unlikely(!mrp))
+               return 0;
+
+       p_port = rcu_dereference(mrp->p_port);
+       if (!p_port)
+               return 0;
+
+       s_port = rcu_dereference(mrp->s_port);
+       if (!s_port)
+               return 0;
+
+       /* If the role is MRM then don't forward the frames */
+       if (mrp->ring_role == BR_MRP_RING_ROLE_MRM) {
+               br_mrp_mrm_process(mrp, p, skb);
+               return 1;
+       }
+
+       /* Clone the frame and forward it on the other MRP port */
+       nskb = skb_clone(skb, GFP_ATOMIC);
+       if (!nskb)
+               return 0;
+
+       p_dev = p_port->dev;
+       s_dev = s_port->dev;
+
+       if (p_dev == dev)
+               d_dev = s_dev;
+       else
+               d_dev = p_dev;
+
+       nskb->dev = d_dev;
+       skb_push(nskb, ETH_HLEN);
+       dev_queue_xmit(nskb);
+
+       return 1;
+}
+
+/* Check if the frame was received on a port that is part of MRP ring
+ * and if the frame has MRP eth. In that case process the frame otherwise do
+ * normal forwarding.
+ * note: already called with rcu_read_lock
+ */
+int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
+{
+       /* If there is no MRP instance do normal forwarding */
+       if (likely(!(p->flags & BR_MRP_AWARE)))
+               goto out;
+
+       if (unlikely(skb->protocol == htons(ETH_P_MRP)))
+               return br_mrp_rcv(p, skb, p->dev);
+
+out:
+       return 0;
+}
+
+bool br_mrp_enabled(struct net_bridge *br)
+{
+       return !list_empty(&br->mrp_list);
+}
diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c
new file mode 100644 (file)
index 0000000..5038966
--- /dev/null
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <net/genetlink.h>
+
+#include <uapi/linux/mrp_bridge.h>
+#include "br_private.h"
+#include "br_private_mrp.h"
+
+static const struct nla_policy br_mrp_policy[IFLA_BRIDGE_MRP_MAX + 1] = {
+       [IFLA_BRIDGE_MRP_UNSPEC]        = { .type = NLA_REJECT },
+       [IFLA_BRIDGE_MRP_INSTANCE]      = { .type = NLA_EXACT_LEN,
+                                   .len = sizeof(struct br_mrp_instance)},
+       [IFLA_BRIDGE_MRP_PORT_STATE]    = { .type = NLA_U32 },
+       [IFLA_BRIDGE_MRP_PORT_ROLE]     = { .type = NLA_EXACT_LEN,
+                                   .len = sizeof(struct br_mrp_port_role)},
+       [IFLA_BRIDGE_MRP_RING_STATE]    = { .type = NLA_EXACT_LEN,
+                                   .len = sizeof(struct br_mrp_ring_state)},
+       [IFLA_BRIDGE_MRP_RING_ROLE]     = { .type = NLA_EXACT_LEN,
+                                   .len = sizeof(struct br_mrp_ring_role)},
+       [IFLA_BRIDGE_MRP_START_TEST]    = { .type = NLA_EXACT_LEN,
+                                   .len = sizeof(struct br_mrp_start_test)},
+};
+
+int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p,
+                struct nlattr *attr, int cmd, struct netlink_ext_ack *extack)
+{
+       struct nlattr *tb[IFLA_BRIDGE_MRP_MAX + 1];
+       int err;
+
+       if (br->stp_enabled != BR_NO_STP) {
+               NL_SET_ERR_MSG_MOD(extack, "MRP can't be enabled if STP is already enabled\n");
+               return -EINVAL;
+       }
+
+       err = nla_parse_nested(tb, IFLA_BRIDGE_MRP_MAX, attr,
+                              br_mrp_policy, extack);
+       if (err)
+               return err;
+
+       if (tb[IFLA_BRIDGE_MRP_INSTANCE]) {
+               struct br_mrp_instance *instance =
+                       nla_data(tb[IFLA_BRIDGE_MRP_INSTANCE]);
+
+               if (cmd == RTM_SETLINK)
+                       err = br_mrp_add(br, instance);
+               else
+                       err = br_mrp_del(br, instance);
+               if (err)
+                       return err;
+       }
+
+       if (tb[IFLA_BRIDGE_MRP_PORT_STATE]) {
+               enum br_mrp_port_state_type state =
+                       nla_get_u32(tb[IFLA_BRIDGE_MRP_PORT_STATE]);
+
+               err = br_mrp_set_port_state(p, state);
+               if (err)
+                       return err;
+       }
+
+       if (tb[IFLA_BRIDGE_MRP_PORT_ROLE]) {
+               struct br_mrp_port_role *role =
+                       nla_data(tb[IFLA_BRIDGE_MRP_PORT_ROLE]);
+
+               err = br_mrp_set_port_role(p, role);
+               if (err)
+                       return err;
+       }
+
+       if (tb[IFLA_BRIDGE_MRP_RING_STATE]) {
+               struct br_mrp_ring_state *state =
+                       nla_data(tb[IFLA_BRIDGE_MRP_RING_STATE]);
+
+               err = br_mrp_set_ring_state(br, state);
+               if (err)
+                       return err;
+       }
+
+       if (tb[IFLA_BRIDGE_MRP_RING_ROLE]) {
+               struct br_mrp_ring_role *role =
+                       nla_data(tb[IFLA_BRIDGE_MRP_RING_ROLE]);
+
+               err = br_mrp_set_ring_role(br, role);
+               if (err)
+                       return err;
+       }
+
+       if (tb[IFLA_BRIDGE_MRP_START_TEST]) {
+               struct br_mrp_start_test *test =
+                       nla_data(tb[IFLA_BRIDGE_MRP_START_TEST]);
+
+               err = br_mrp_start_test(br, test);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+int br_mrp_port_open(struct net_device *dev, u8 loc)
+{
+       struct net_bridge_port *p;
+       int err = 0;
+
+       p = br_port_get_rcu(dev);
+       if (!p) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (loc)
+               p->flags |= BR_MRP_LOST_CONT;
+       else
+               p->flags &= ~BR_MRP_LOST_CONT;
+
+       br_ifinfo_notify(RTM_NEWLINK, NULL, p);
+
+out:
+       return err;
+}
diff --git a/net/bridge/br_mrp_switchdev.c b/net/bridge/br_mrp_switchdev.c
new file mode 100644 (file)
index 0000000..51cb1d5
--- /dev/null
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <net/switchdev.h>
+
+#include "br_private_mrp.h"
+
+int br_mrp_switchdev_add(struct net_bridge *br, struct br_mrp *mrp)
+{
+       struct switchdev_obj_mrp mrp_obj = {
+               .obj.orig_dev = br->dev,
+               .obj.id = SWITCHDEV_OBJ_ID_MRP,
+               .p_port = rtnl_dereference(mrp->p_port)->dev,
+               .s_port = rtnl_dereference(mrp->s_port)->dev,
+               .ring_id = mrp->ring_id,
+       };
+       int err;
+
+       err = switchdev_port_obj_add(br->dev, &mrp_obj.obj, NULL);
+
+       if (err && err != -EOPNOTSUPP)
+               return err;
+
+       return 0;
+}
+
+int br_mrp_switchdev_del(struct net_bridge *br, struct br_mrp *mrp)
+{
+       struct switchdev_obj_mrp mrp_obj = {
+               .obj.orig_dev = br->dev,
+               .obj.id = SWITCHDEV_OBJ_ID_MRP,
+               .p_port = NULL,
+               .s_port = NULL,
+               .ring_id = mrp->ring_id,
+       };
+       int err;
+
+       err = switchdev_port_obj_del(br->dev, &mrp_obj.obj);
+
+       if (err && err != -EOPNOTSUPP)
+               return err;
+
+       return 0;
+}
+
+int br_mrp_switchdev_set_ring_role(struct net_bridge *br,
+                                  struct br_mrp *mrp,
+                                  enum br_mrp_ring_role_type role)
+{
+       struct switchdev_obj_ring_role_mrp mrp_role = {
+               .obj.orig_dev = br->dev,
+               .obj.id = SWITCHDEV_OBJ_ID_RING_ROLE_MRP,
+               .ring_role = role,
+               .ring_id = mrp->ring_id,
+       };
+       int err;
+
+       if (role == BR_MRP_RING_ROLE_DISABLED)
+               err = switchdev_port_obj_del(br->dev, &mrp_role.obj);
+       else
+               err = switchdev_port_obj_add(br->dev, &mrp_role.obj, NULL);
+
+       return err;
+}
+
+int br_mrp_switchdev_send_ring_test(struct net_bridge *br,
+                                   struct br_mrp *mrp, u32 interval,
+                                   u8 max_miss, u32 period)
+{
+       struct switchdev_obj_ring_test_mrp test = {
+               .obj.orig_dev = br->dev,
+               .obj.id = SWITCHDEV_OBJ_ID_RING_TEST_MRP,
+               .interval = interval,
+               .max_miss = max_miss,
+               .ring_id = mrp->ring_id,
+               .period = period,
+       };
+       int err;
+
+       if (interval == 0)
+               err = switchdev_port_obj_del(br->dev, &test.obj);
+       else
+               err = switchdev_port_obj_add(br->dev, &test.obj, NULL);
+
+       return err;
+}
+
+int br_mrp_switchdev_set_ring_state(struct net_bridge *br,
+                                   struct br_mrp *mrp,
+                                   enum br_mrp_ring_state_type state)
+{
+       struct switchdev_obj_ring_state_mrp mrp_state = {
+               .obj.orig_dev = br->dev,
+               .obj.id = SWITCHDEV_OBJ_ID_RING_STATE_MRP,
+               .ring_state = state,
+               .ring_id = mrp->ring_id,
+       };
+       int err;
+
+       err = switchdev_port_obj_add(br->dev, &mrp_state.obj, NULL);
+
+       if (err && err != -EOPNOTSUPP)
+               return err;
+
+       return 0;
+}
+
+int br_mrp_port_switchdev_set_state(struct net_bridge_port *p,
+                                   enum br_mrp_port_state_type state)
+{
+       struct switchdev_attr attr = {
+               .orig_dev = p->dev,
+               .id = SWITCHDEV_ATTR_ID_MRP_PORT_STATE,
+               .u.mrp_port_state = state,
+       };
+       int err;
+
+       err = switchdev_port_attr_set(p->dev, &attr);
+       if (err && err != -EOPNOTSUPP)
+               br_warn(p->br, "error setting offload MRP state on port %u(%s)\n",
+                       (unsigned int)p->port_no, p->dev->name);
+
+       return err;
+}
+
+int br_mrp_port_switchdev_set_role(struct net_bridge_port *p,
+                                  enum br_mrp_port_role_type role)
+{
+       struct switchdev_attr attr = {
+               .orig_dev = p->dev,
+               .id = SWITCHDEV_ATTR_ID_MRP_PORT_ROLE,
+               .u.mrp_port_role = role,
+       };
+       int err;
+
+       err = switchdev_port_attr_set(p->dev, &attr);
+       if (err && err != -EOPNOTSUPP)
+               return err;
+
+       return 0;
+}
index 59980ec..04c3f9a 100644 (file)
@@ -1027,7 +1027,7 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net,
 #ifdef CONFIG_SYSCTL
 static
 int brnf_sysctl_call_tables(struct ctl_table *ctl, int write,
-                           void __user *buffer, size_t *lenp, loff_t *ppos)
+                           void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
 
index 43dab40..240e260 100644 (file)
@@ -151,6 +151,7 @@ static inline size_t br_port_info_size(void)
                + nla_total_size(sizeof(u8))    /* IFLA_BRPORT_MULTICAST_ROUTER */
 #endif
                + nla_total_size(sizeof(u16))   /* IFLA_BRPORT_GROUP_FWD_MASK */
+               + nla_total_size(sizeof(u8))    /* IFLA_BRPORT_MRP_RING_OPEN */
                + 0;
 }
 
@@ -213,6 +214,8 @@ static int br_port_fill_attrs(struct sk_buff *skb,
            nla_put_u16(skb, IFLA_BRPORT_GROUP_FWD_MASK, p->group_fwd_mask) ||
            nla_put_u8(skb, IFLA_BRPORT_NEIGH_SUPPRESS,
                       !!(p->flags & BR_NEIGH_SUPPRESS)) ||
+           nla_put_u8(skb, IFLA_BRPORT_MRP_RING_OPEN, !!(p->flags &
+                                                         BR_MRP_LOST_CONT)) ||
            nla_put_u8(skb, IFLA_BRPORT_ISOLATED, !!(p->flags & BR_ISOLATED)))
                return -EMSGSIZE;
 
@@ -612,6 +615,7 @@ int br_process_vlan_info(struct net_bridge *br,
                                               v - 1, rtm_cmd);
                                v_change_start = 0;
                        }
+                       cond_resched();
                }
                /* v_change_start is set only if the last/whole range changed */
                if (v_change_start)
@@ -669,6 +673,11 @@ static int br_afspec(struct net_bridge *br,
                        if (err)
                                return err;
                        break;
+               case IFLA_BRIDGE_MRP:
+                       err = br_mrp_parse(br, p, attr, cmd, extack);
+                       if (err)
+                               return err;
+                       break;
                }
        }
 
@@ -1101,7 +1110,9 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
        if (data[IFLA_BR_STP_STATE]) {
                u32 stp_enabled = nla_get_u32(data[IFLA_BR_STP_STATE]);
 
-               br_stp_set_enabled(br, stp_enabled);
+               err = br_stp_set_enabled(br, stp_enabled, extack);
+               if (err)
+                       return err;
        }
 
        if (data[IFLA_BR_PRIORITY]) {
index 1f97703..78d3a95 100644 (file)
@@ -428,6 +428,10 @@ struct net_bridge {
        int offload_fwd_mark;
 #endif
        struct hlist_head               fdb_list;
+
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+       struct list_head                __rcu mrp_list;
+#endif
 };
 
 struct br_input_skb_cb {
@@ -1279,7 +1283,8 @@ int br_set_ageing_time(struct net_bridge *br, clock_t ageing_time);
 /* br_stp_if.c */
 void br_stp_enable_bridge(struct net_bridge *br);
 void br_stp_disable_bridge(struct net_bridge *br);
-void br_stp_set_enabled(struct net_bridge *br, unsigned long val);
+int br_stp_set_enabled(struct net_bridge *br, unsigned long val,
+                      struct netlink_ext_ack *extack);
 void br_stp_enable_port(struct net_bridge_port *p);
 void br_stp_disable_port(struct net_bridge_port *p);
 bool br_stp_recalculate_bridge_id(struct net_bridge *br);
@@ -1304,6 +1309,37 @@ unsigned long br_timer_value(const struct timer_list *timer);
 extern int (*br_fdb_test_addr_hook)(struct net_device *dev, unsigned char *addr);
 #endif
 
+/* br_mrp.c */
+#if IS_ENABLED(CONFIG_BRIDGE_MRP)
+int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p,
+                struct nlattr *attr, int cmd, struct netlink_ext_ack *extack);
+int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb);
+bool br_mrp_enabled(struct net_bridge *br);
+void br_mrp_port_del(struct net_bridge *br, struct net_bridge_port *p);
+#else
+static inline int br_mrp_parse(struct net_bridge *br, struct net_bridge_port *p,
+                              struct nlattr *attr, int cmd,
+                              struct netlink_ext_ack *extack)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline int br_mrp_process(struct net_bridge_port *p, struct sk_buff *skb)
+{
+       return 0;
+}
+
+static inline bool br_mrp_enabled(struct net_bridge *br)
+{
+       return false;
+}
+
+static inline void br_mrp_port_del(struct net_bridge *br,
+                                  struct net_bridge_port *p)
+{
+}
+#endif
+
 /* br_netlink.c */
 extern struct rtnl_link_ops br_link_ops;
 int br_netlink_init(void);
diff --git a/net/bridge/br_private_mrp.h b/net/bridge/br_private_mrp.h
new file mode 100644 (file)
index 0000000..2921a4b
--- /dev/null
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _BR_PRIVATE_MRP_H_
+#define _BR_PRIVATE_MRP_H_
+
+#include "br_private.h"
+#include <uapi/linux/mrp_bridge.h>
+
+struct br_mrp {
+       /* list of mrp instances */
+       struct list_head                __rcu list;
+
+       struct net_bridge_port __rcu    *p_port;
+       struct net_bridge_port __rcu    *s_port;
+
+       u32                             ring_id;
+
+       enum br_mrp_ring_role_type      ring_role;
+       u8                              ring_role_offloaded;
+       enum br_mrp_ring_state_type     ring_state;
+       u32                             ring_transitions;
+
+       struct delayed_work             test_work;
+       u32                             test_interval;
+       unsigned long                   test_end;
+       u32                             test_count_miss;
+       u32                             test_max_miss;
+
+       u32                             seq_id;
+
+       struct rcu_head                 rcu;
+};
+
+/* br_mrp.c */
+int br_mrp_add(struct net_bridge *br, struct br_mrp_instance *instance);
+int br_mrp_del(struct net_bridge *br, struct br_mrp_instance *instance);
+int br_mrp_set_port_state(struct net_bridge_port *p,
+                         enum br_mrp_port_state_type state);
+int br_mrp_set_port_role(struct net_bridge_port *p,
+                        struct br_mrp_port_role *role);
+int br_mrp_set_ring_state(struct net_bridge *br,
+                         struct br_mrp_ring_state *state);
+int br_mrp_set_ring_role(struct net_bridge *br, struct br_mrp_ring_role *role);
+int br_mrp_start_test(struct net_bridge *br, struct br_mrp_start_test *test);
+
+/* br_mrp_switchdev.c */
+int br_mrp_switchdev_add(struct net_bridge *br, struct br_mrp *mrp);
+int br_mrp_switchdev_del(struct net_bridge *br, struct br_mrp *mrp);
+int br_mrp_switchdev_set_ring_role(struct net_bridge *br, struct br_mrp *mrp,
+                                  enum br_mrp_ring_role_type role);
+int br_mrp_switchdev_set_ring_state(struct net_bridge *br, struct br_mrp *mrp,
+                                   enum br_mrp_ring_state_type state);
+int br_mrp_switchdev_send_ring_test(struct net_bridge *br, struct br_mrp *mrp,
+                                   u32 interval, u8 max_miss, u32 period);
+int br_mrp_port_switchdev_set_state(struct net_bridge_port *p,
+                                   enum br_mrp_port_state_type state);
+int br_mrp_port_switchdev_set_role(struct net_bridge_port *p,
+                                  enum br_mrp_port_role_type role);
+
+/* br_mrp_netlink.c  */
+int br_mrp_port_open(struct net_device *dev, u8 loc);
+
+#endif /* _BR_PRIVATE_MRP_H */
index 1f14b84..3e88be7 100644 (file)
@@ -36,6 +36,12 @@ void br_set_state(struct net_bridge_port *p, unsigned int state)
        };
        int err;
 
+       /* Don't change the state of the ports if they are driven by a different
+        * protocol.
+        */
+       if (p->flags & BR_MRP_AWARE)
+               return;
+
        p->state = state;
        err = switchdev_port_attr_set(p->dev, &attr);
        if (err && err != -EOPNOTSUPP)
index d174d3a..a42850b 100644 (file)
@@ -196,10 +196,17 @@ static void br_stp_stop(struct net_bridge *br)
        br->stp_enabled = BR_NO_STP;
 }
 
-void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
+int br_stp_set_enabled(struct net_bridge *br, unsigned long val,
+                      struct netlink_ext_ack *extack)
 {
        ASSERT_RTNL();
 
+       if (br_mrp_enabled(br)) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "STP can't be enabled if MRP is already enabled\n");
+               return -EINVAL;
+       }
+
        if (val) {
                if (br->stp_enabled == BR_NO_STP)
                        br_stp_start(br);
@@ -207,6 +214,8 @@ void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
                if (br->stp_enabled != BR_NO_STP)
                        br_stp_stop(br);
        }
+
+       return 0;
 }
 
 /* called under bridge lock */
index 9ab0f00..7db06e3 100644 (file)
@@ -126,9 +126,7 @@ static ssize_t stp_state_show(struct device *d,
 
 static int set_stp_state(struct net_bridge *br, unsigned long val)
 {
-       br_stp_set_enabled(br, val);
-
-       return 0;
+       return br_stp_set_enabled(br, val, NULL);
 }
 
 static ssize_t stp_state_store(struct device *d,
index 195d2d6..c10e5a5 100644 (file)
@@ -142,7 +142,7 @@ static void caif_flow_cb(struct sk_buff *skb)
 
        spin_lock_bh(&caifd->flow_lock);
        send_xoff = caifd->xoff;
-       caifd->xoff = 0;
+       caifd->xoff = false;
        dtor = caifd->xoff_skb_dtor;
 
        if (WARN_ON(caifd->xoff_skb != skb))
@@ -220,7 +220,7 @@ static int transmit(struct cflayer *layer, struct cfpkt *pkt)
        pr_debug("queue has stopped(%d) or is full (%d > %d)\n",
                        netif_queue_stopped(caifd->netdev),
                        qlen, high);
-       caifd->xoff = 1;
+       caifd->xoff = true;
        caifd->xoff_skb = skb;
        caifd->xoff_skb_dtor = skb->destructor;
        skb->destructor = caif_flow_cb;
@@ -407,7 +407,7 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what,
                        break;
                }
 
-               caifd->xoff = 0;
+               caifd->xoff = false;
                cfcnfg_set_phy_state(cfg, &caifd->layer, true);
                rcu_read_unlock();
 
@@ -442,7 +442,7 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what,
                if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL)
                        caifd->xoff_skb->destructor = caifd->xoff_skb_dtor;
 
-               caifd->xoff = 0;
+               caifd->xoff = false;
                caifd->xoff_skb_dtor = NULL;
                caifd->xoff_skb = NULL;
 
index a566289..79b6a04 100644 (file)
@@ -211,7 +211,8 @@ static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow,
        }
 }
 
-static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t chnl_net_start_xmit(struct sk_buff *skb,
+                                      struct net_device *dev)
 {
        struct chnl_net *priv;
        struct cfpkt *pkt = NULL;
index 2e8e6f9..d7bec7a 100644 (file)
@@ -39,6 +39,6 @@ config CEPH_LIB_USE_DNS_RESOLVER
          be resolved using the CONFIG_DNS_RESOLVER facility.
 
          For information on how to use CONFIG_DNS_RESOLVER consult
-         Documentation/networking/dns_resolver.txt
+         Documentation/networking/dns_resolver.rst
 
          If unsure, say N.
index 9c9e763..f8d8392 100644 (file)
@@ -398,6 +398,74 @@ static RAW_NOTIFIER_HEAD(netdev_chain);
 DEFINE_PER_CPU_ALIGNED(struct softnet_data, softnet_data);
 EXPORT_PER_CPU_SYMBOL(softnet_data);
 
+#ifdef CONFIG_LOCKDEP
+/*
+ * register_netdevice() inits txq->_xmit_lock and sets lockdep class
+ * according to dev->type
+ */
+static const unsigned short netdev_lock_type[] = {
+        ARPHRD_NETROM, ARPHRD_ETHER, ARPHRD_EETHER, ARPHRD_AX25,
+        ARPHRD_PRONET, ARPHRD_CHAOS, ARPHRD_IEEE802, ARPHRD_ARCNET,
+        ARPHRD_APPLETLK, ARPHRD_DLCI, ARPHRD_ATM, ARPHRD_METRICOM,
+        ARPHRD_IEEE1394, ARPHRD_EUI64, ARPHRD_INFINIBAND, ARPHRD_SLIP,
+        ARPHRD_CSLIP, ARPHRD_SLIP6, ARPHRD_CSLIP6, ARPHRD_RSRVD,
+        ARPHRD_ADAPT, ARPHRD_ROSE, ARPHRD_X25, ARPHRD_HWX25,
+        ARPHRD_PPP, ARPHRD_CISCO, ARPHRD_LAPB, ARPHRD_DDCMP,
+        ARPHRD_RAWHDLC, ARPHRD_TUNNEL, ARPHRD_TUNNEL6, ARPHRD_FRAD,
+        ARPHRD_SKIP, ARPHRD_LOOPBACK, ARPHRD_LOCALTLK, ARPHRD_FDDI,
+        ARPHRD_BIF, ARPHRD_SIT, ARPHRD_IPDDP, ARPHRD_IPGRE,
+        ARPHRD_PIMREG, ARPHRD_HIPPI, ARPHRD_ASH, ARPHRD_ECONET,
+        ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL,
+        ARPHRD_FCFABRIC, ARPHRD_IEEE80211, ARPHRD_IEEE80211_PRISM,
+        ARPHRD_IEEE80211_RADIOTAP, ARPHRD_PHONET, ARPHRD_PHONET_PIPE,
+        ARPHRD_IEEE802154, ARPHRD_VOID, ARPHRD_NONE};
+
+static const char *const netdev_lock_name[] = {
+       "_xmit_NETROM", "_xmit_ETHER", "_xmit_EETHER", "_xmit_AX25",
+       "_xmit_PRONET", "_xmit_CHAOS", "_xmit_IEEE802", "_xmit_ARCNET",
+       "_xmit_APPLETLK", "_xmit_DLCI", "_xmit_ATM", "_xmit_METRICOM",
+       "_xmit_IEEE1394", "_xmit_EUI64", "_xmit_INFINIBAND", "_xmit_SLIP",
+       "_xmit_CSLIP", "_xmit_SLIP6", "_xmit_CSLIP6", "_xmit_RSRVD",
+       "_xmit_ADAPT", "_xmit_ROSE", "_xmit_X25", "_xmit_HWX25",
+       "_xmit_PPP", "_xmit_CISCO", "_xmit_LAPB", "_xmit_DDCMP",
+       "_xmit_RAWHDLC", "_xmit_TUNNEL", "_xmit_TUNNEL6", "_xmit_FRAD",
+       "_xmit_SKIP", "_xmit_LOOPBACK", "_xmit_LOCALTLK", "_xmit_FDDI",
+       "_xmit_BIF", "_xmit_SIT", "_xmit_IPDDP", "_xmit_IPGRE",
+       "_xmit_PIMREG", "_xmit_HIPPI", "_xmit_ASH", "_xmit_ECONET",
+       "_xmit_IRDA", "_xmit_FCPP", "_xmit_FCAL", "_xmit_FCPL",
+       "_xmit_FCFABRIC", "_xmit_IEEE80211", "_xmit_IEEE80211_PRISM",
+       "_xmit_IEEE80211_RADIOTAP", "_xmit_PHONET", "_xmit_PHONET_PIPE",
+       "_xmit_IEEE802154", "_xmit_VOID", "_xmit_NONE"};
+
+static struct lock_class_key netdev_xmit_lock_key[ARRAY_SIZE(netdev_lock_type)];
+
+static inline unsigned short netdev_lock_pos(unsigned short dev_type)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(netdev_lock_type); i++)
+               if (netdev_lock_type[i] == dev_type)
+                       return i;
+       /* the last key is used by default */
+       return ARRAY_SIZE(netdev_lock_type) - 1;
+}
+
+static inline void netdev_set_xmit_lockdep_class(spinlock_t *lock,
+                                                unsigned short dev_type)
+{
+       int i;
+
+       i = netdev_lock_pos(dev_type);
+       lockdep_set_class_and_name(lock, &netdev_xmit_lock_key[i],
+                                  netdev_lock_name[i]);
+}
+#else
+static inline void netdev_set_xmit_lockdep_class(spinlock_t *lock,
+                                                unsigned short dev_type)
+{
+}
+#endif
+
 /*******************************************************************************
  *
  *             Protocol management and registration routines
@@ -4140,7 +4208,8 @@ EXPORT_SYMBOL(netdev_max_backlog);
 
 int netdev_tstamp_prequeue __read_mostly = 1;
 int netdev_budget __read_mostly = 300;
-unsigned int __read_mostly netdev_budget_usecs = 2000;
+/* Must be at least 2 jiffes to guarantee 1 jiffy timeout */
+unsigned int __read_mostly netdev_budget_usecs = 2 * USEC_PER_SEC / HZ;
 int weight_p __read_mostly = 64;           /* old backlog weight */
 int dev_weight_rx_bias __read_mostly = 1;  /* bias for backlog weight */
 int dev_weight_tx_bias __read_mostly = 1;  /* bias for output_queue quota */
@@ -6226,7 +6295,8 @@ EXPORT_SYMBOL(__napi_schedule_irqoff);
 
 bool napi_complete_done(struct napi_struct *n, int work_done)
 {
-       unsigned long flags, val, new;
+       unsigned long flags, val, new, timeout = 0;
+       bool ret = true;
 
        /*
         * 1) Don't let napi dequeue from the cpu poll list
@@ -6238,20 +6308,23 @@ bool napi_complete_done(struct napi_struct *n, int work_done)
                                 NAPIF_STATE_IN_BUSY_POLL)))
                return false;
 
+       if (work_done) {
+               if (n->gro_bitmask)
+                       timeout = READ_ONCE(n->dev->gro_flush_timeout);
+               n->defer_hard_irqs_count = READ_ONCE(n->dev->napi_defer_hard_irqs);
+       }
+       if (n->defer_hard_irqs_count > 0) {
+               n->defer_hard_irqs_count--;
+               timeout = READ_ONCE(n->dev->gro_flush_timeout);
+               if (timeout)
+                       ret = false;
+       }
        if (n->gro_bitmask) {
-               unsigned long timeout = 0;
-
-               if (work_done)
-                       timeout = n->dev->gro_flush_timeout;
-
                /* When the NAPI instance uses a timeout and keeps postponing
                 * it, we need to bound somehow the time packets are kept in
                 * the GRO layer
                 */
                napi_gro_flush(n, !!timeout);
-               if (timeout)
-                       hrtimer_start(&n->timer, ns_to_ktime(timeout),
-                                     HRTIMER_MODE_REL_PINNED);
        }
 
        gro_normal_list(n);
@@ -6283,7 +6356,10 @@ bool napi_complete_done(struct napi_struct *n, int work_done)
                return false;
        }
 
-       return true;
+       if (timeout)
+               hrtimer_start(&n->timer, ns_to_ktime(timeout),
+                             HRTIMER_MODE_REL_PINNED);
+       return ret;
 }
 EXPORT_SYMBOL(napi_complete_done);
 
@@ -6463,7 +6539,7 @@ static enum hrtimer_restart napi_watchdog(struct hrtimer *timer)
        /* Note : we use a relaxed variant of napi_schedule_prep() not setting
         * NAPI_STATE_MISSED, since we do not react to a device IRQ.
         */
-       if (napi->gro_bitmask && !napi_disable_pending(napi) &&
+       if (!napi_disable_pending(napi) &&
            !test_and_set_bit(NAPI_STATE_SCHED, &napi->state))
                __napi_schedule_irqoff(napi);
 
@@ -8666,8 +8742,8 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
        const struct net_device_ops *ops = dev->netdev_ops;
        enum bpf_netdev_command query;
        u32 prog_id, expected_id = 0;
-       struct bpf_prog *prog = NULL;
        bpf_op_t bpf_op, bpf_chk;
+       struct bpf_prog *prog;
        bool offload;
        int err;
 
@@ -8733,6 +8809,7 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
        } else {
                if (!prog_id)
                        return 0;
+               prog = NULL;
        }
 
        err = dev_xdp_install(dev, bpf_op, extack, flags, prog);
@@ -9134,6 +9211,11 @@ void netif_stacked_transfer_operstate(const struct net_device *rootdev,
        else
                netif_dormant_off(dev);
 
+       if (rootdev->operstate == IF_OPER_TESTING)
+               netif_testing_on(dev);
+       else
+               netif_testing_off(dev);
+
        if (netif_carrier_ok(rootdev))
                netif_carrier_on(dev);
        else
@@ -9194,7 +9276,7 @@ static void netdev_init_one_queue(struct net_device *dev,
 {
        /* Initialize queue lock */
        spin_lock_init(&queue->_xmit_lock);
-       lockdep_set_class(&queue->_xmit_lock, &dev->qdisc_xmit_lock_key);
+       netdev_set_xmit_lockdep_class(&queue->_xmit_lock, dev->type);
        queue->xmit_lock_owner = -1;
        netdev_queue_numa_node_write(queue, NUMA_NO_NODE);
        queue->dev = dev;
@@ -9241,22 +9323,6 @@ void netif_tx_stop_all_queues(struct net_device *dev)
 }
 EXPORT_SYMBOL(netif_tx_stop_all_queues);
 
-static void netdev_register_lockdep_key(struct net_device *dev)
-{
-       lockdep_register_key(&dev->qdisc_tx_busylock_key);
-       lockdep_register_key(&dev->qdisc_running_key);
-       lockdep_register_key(&dev->qdisc_xmit_lock_key);
-       lockdep_register_key(&dev->addr_list_lock_key);
-}
-
-static void netdev_unregister_lockdep_key(struct net_device *dev)
-{
-       lockdep_unregister_key(&dev->qdisc_tx_busylock_key);
-       lockdep_unregister_key(&dev->qdisc_running_key);
-       lockdep_unregister_key(&dev->qdisc_xmit_lock_key);
-       lockdep_unregister_key(&dev->addr_list_lock_key);
-}
-
 void netdev_update_lockdep_key(struct net_device *dev)
 {
        lockdep_unregister_key(&dev->addr_list_lock_key);
@@ -9823,7 +9889,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
 
        dev_net_set(dev, &init_net);
 
-       netdev_register_lockdep_key(dev);
+       lockdep_register_key(&dev->addr_list_lock_key);
 
        dev->gso_max_size = GSO_MAX_SIZE;
        dev->gso_max_segs = GSO_MAX_SEGS;
@@ -9912,7 +9978,7 @@ void free_netdev(struct net_device *dev)
        free_percpu(dev->xdp_bulkq);
        dev->xdp_bulkq = NULL;
 
-       netdev_unregister_lockdep_key(dev);
+       lockdep_unregister_key(&dev->addr_list_lock_key);
 
        /*  Compatibility with error handling in drivers */
        if (dev->reg_state == NETREG_UNINITIALIZED) {
index 80f9772..20f935f 100644 (file)
@@ -3716,24 +3716,26 @@ nla_put_failure:
        return err;
 }
 
-static void devlink_nl_region_notify(struct devlink_region *region,
-                                    struct devlink_snapshot *snapshot,
-                                    enum devlink_command cmd)
+static struct sk_buff *
+devlink_nl_region_notify_build(struct devlink_region *region,
+                              struct devlink_snapshot *snapshot,
+                              enum devlink_command cmd, u32 portid, u32 seq)
 {
        struct devlink *devlink = region->devlink;
        struct sk_buff *msg;
        void *hdr;
        int err;
 
-       WARN_ON(cmd != DEVLINK_CMD_REGION_NEW && cmd != DEVLINK_CMD_REGION_DEL);
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
-               return;
+               return ERR_PTR(-ENOMEM);
 
-       hdr = genlmsg_put(msg, 0, 0, &devlink_nl_family, 0, cmd);
-       if (!hdr)
+       hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, 0, cmd);
+       if (!hdr) {
+               err = -EMSGSIZE;
                goto out_free_msg;
+       }
 
        err = devlink_nl_put_handle(msg, devlink);
        if (err)
@@ -3757,15 +3759,30 @@ static void devlink_nl_region_notify(struct devlink_region *region,
        }
        genlmsg_end(msg, hdr);
 
-       genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
-                               msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
-
-       return;
+       return msg;
 
 out_cancel_msg:
        genlmsg_cancel(msg, hdr);
 out_free_msg:
        nlmsg_free(msg);
+       return ERR_PTR(err);
+}
+
+static void devlink_nl_region_notify(struct devlink_region *region,
+                                    struct devlink_snapshot *snapshot,
+                                    enum devlink_command cmd)
+{
+       struct devlink *devlink = region->devlink;
+       struct sk_buff *msg;
+
+       WARN_ON(cmd != DEVLINK_CMD_REGION_NEW && cmd != DEVLINK_CMD_REGION_DEL);
+
+       msg = devlink_nl_region_notify_build(region, snapshot, cmd, 0, 0);
+       if (IS_ERR(msg))
+               return;
+
+       genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
+                               msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
 }
 
 /**
@@ -4069,6 +4086,8 @@ static int
 devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info)
 {
        struct devlink *devlink = info->user_ptr[0];
+       struct devlink_snapshot *snapshot;
+       struct nlattr *snapshot_id_attr;
        struct devlink_region *region;
        const char *region_name;
        u32 snapshot_id;
@@ -4080,11 +4099,6 @@ devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
        }
 
-       if (!info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) {
-               NL_SET_ERR_MSG_MOD(info->extack, "No snapshot id provided");
-               return -EINVAL;
-       }
-
        region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]);
        region = devlink_region_get_by_name(devlink, region_name);
        if (!region) {
@@ -4102,16 +4116,25 @@ devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info)
                return -ENOSPC;
        }
 
-       snapshot_id = nla_get_u32(info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]);
+       snapshot_id_attr = info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID];
+       if (snapshot_id_attr) {
+               snapshot_id = nla_get_u32(snapshot_id_attr);
 
-       if (devlink_region_snapshot_get_by_id(region, snapshot_id)) {
-               NL_SET_ERR_MSG_MOD(info->extack, "The requested snapshot id is already in use");
-               return -EEXIST;
-       }
+               if (devlink_region_snapshot_get_by_id(region, snapshot_id)) {
+                       NL_SET_ERR_MSG_MOD(info->extack, "The requested snapshot id is already in use");
+                       return -EEXIST;
+               }
 
-       err = __devlink_snapshot_id_insert(devlink, snapshot_id);
-       if (err)
-               return err;
+               err = __devlink_snapshot_id_insert(devlink, snapshot_id);
+               if (err)
+                       return err;
+       } else {
+               err = __devlink_region_snapshot_id_get(devlink, &snapshot_id);
+               if (err) {
+                       NL_SET_ERR_MSG_MOD(info->extack, "Failed to allocate a new snapshot id");
+                       return err;
+               }
+       }
 
        err = region->ops->snapshot(devlink, info->extack, &data);
        if (err)
@@ -4121,6 +4144,27 @@ devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info)
        if (err)
                goto err_snapshot_create;
 
+       if (!snapshot_id_attr) {
+               struct sk_buff *msg;
+
+               snapshot = devlink_region_snapshot_get_by_id(region,
+                                                            snapshot_id);
+               if (WARN_ON(!snapshot))
+                       return -EINVAL;
+
+               msg = devlink_nl_region_notify_build(region, snapshot,
+                                                    DEVLINK_CMD_REGION_NEW,
+                                                    info->snd_portid,
+                                                    info->snd_seq);
+               err = PTR_ERR_OR_ZERO(msg);
+               if (err)
+                       goto err_notify;
+
+               err = genlmsg_reply(msg, info);
+               if (err)
+                       goto err_notify;
+       }
+
        return 0;
 
 err_snapshot_create:
@@ -4128,6 +4172,10 @@ err_snapshot_create:
 err_snapshot_capture:
        __devlink_snapshot_id_decrement(devlink, snapshot_id);
        return err;
+
+err_notify:
+       devlink_region_snapshot_del(region, snapshot);
+       return err;
 }
 
 static int devlink_nl_cmd_region_read_chunk_fill(struct sk_buff *msg,
@@ -4283,6 +4331,11 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
                end_offset = nla_get_u64(attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR]);
                end_offset += nla_get_u64(attrs[DEVLINK_ATTR_REGION_CHUNK_LEN]);
                dump = false;
+
+               if (start_offset == end_offset) {
+                       err = 0;
+                       goto nla_put_failure;
+               }
        }
 
        err = devlink_nl_region_read_snapshot_fill(skb, devlink,
@@ -5363,6 +5416,7 @@ int devlink_health_report(struct devlink_health_reporter *reporter,
 {
        enum devlink_health_reporter_state prev_health_state;
        struct devlink *devlink = reporter->devlink;
+       unsigned long recover_ts_threshold;
 
        /* write a log message of the current error */
        WARN_ON(!msg);
@@ -5373,10 +5427,12 @@ int devlink_health_report(struct devlink_health_reporter *reporter,
        devlink_recover_notify(reporter, DEVLINK_CMD_HEALTH_REPORTER_RECOVER);
 
        /* abort if the previous error wasn't recovered */
+       recover_ts_threshold = reporter->last_recovery_ts +
+                              msecs_to_jiffies(reporter->graceful_period);
        if (reporter->auto_recover &&
            (prev_health_state != DEVLINK_HEALTH_REPORTER_STATE_HEALTHY ||
-            jiffies - reporter->last_recovery_ts <
-            msecs_to_jiffies(reporter->graceful_period))) {
+            (reporter->last_recovery_ts && reporter->recovery_count &&
+             time_is_after_jiffies(recover_ts_threshold)))) {
                trace_devlink_health_recover_aborted(devlink,
                                                     reporter->ops->name,
                                                     reporter->health_state,
index 8e33cec..2ee7bc4 100644 (file)
@@ -213,6 +213,7 @@ static void sched_send_work(struct timer_list *t)
 static void trace_drop_common(struct sk_buff *skb, void *location)
 {
        struct net_dm_alert_msg *msg;
+       struct net_dm_drop_point *point;
        struct nlmsghdr *nlh;
        struct nlattr *nla;
        int i;
@@ -231,11 +232,13 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
        nlh = (struct nlmsghdr *)dskb->data;
        nla = genlmsg_data(nlmsg_data(nlh));
        msg = nla_data(nla);
+       point = msg->points;
        for (i = 0; i < msg->entries; i++) {
-               if (!memcmp(&location, msg->points[i].pc, sizeof(void *))) {
-                       msg->points[i].count++;
+               if (!memcmp(&location, &point->pc, sizeof(void *))) {
+                       point->count++;
                        goto out;
                }
+               point++;
        }
        if (msg->entries == dm_hit_limit)
                goto out;
@@ -244,8 +247,8 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
         */
        __nla_reserve_nohdr(dskb, sizeof(struct net_dm_drop_point));
        nla->nla_len += NLA_ALIGN(sizeof(struct net_dm_drop_point));
-       memcpy(msg->points[msg->entries].pc, &location, sizeof(void *));
-       msg->points[msg->entries].count = 1;
+       memcpy(point->pc, &location, sizeof(void *));
+       point->count = 1;
        msg->entries++;
 
        if (!timer_pending(&data->send_timer)) {
index 7628b94..dfaf5df 100644 (file)
@@ -256,17 +256,6 @@ BPF_CALL_2(bpf_skb_load_helper_32_no_cache, const struct sk_buff *, skb,
                                          offset);
 }
 
-BPF_CALL_0(bpf_get_raw_cpu_id)
-{
-       return raw_smp_processor_id();
-}
-
-static const struct bpf_func_proto bpf_get_raw_smp_processor_id_proto = {
-       .func           = bpf_get_raw_cpu_id,
-       .gpl_only       = false,
-       .ret_type       = RET_INTEGER,
-};
-
 static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg,
                              struct bpf_insn *insn_buf)
 {
@@ -4205,36 +4194,19 @@ static const struct bpf_func_proto bpf_get_socket_uid_proto = {
        .arg1_type      = ARG_PTR_TO_CTX,
 };
 
-BPF_CALL_5(bpf_event_output_data, void *, ctx, struct bpf_map *, map, u64, flags,
-          void *, data, u64, size)
-{
-       if (unlikely(flags & ~(BPF_F_INDEX_MASK)))
-               return -EINVAL;
-
-       return bpf_event_output(map, flags, data, size, NULL, 0, NULL);
-}
+#define SOCKOPT_CC_REINIT (1 << 0)
 
-static const struct bpf_func_proto bpf_event_output_data_proto =  {
-       .func           = bpf_event_output_data,
-       .gpl_only       = true,
-       .ret_type       = RET_INTEGER,
-       .arg1_type      = ARG_PTR_TO_CTX,
-       .arg2_type      = ARG_CONST_MAP_PTR,
-       .arg3_type      = ARG_ANYTHING,
-       .arg4_type      = ARG_PTR_TO_MEM,
-       .arg5_type      = ARG_CONST_SIZE_OR_ZERO,
-};
-
-BPF_CALL_5(bpf_setsockopt, struct bpf_sock_ops_kern *, bpf_sock,
-          int, level, int, optname, char *, optval, int, optlen)
+static int _bpf_setsockopt(struct sock *sk, int level, int optname,
+                          char *optval, int optlen, u32 flags)
 {
-       struct sock *sk = bpf_sock->sk;
        int ret = 0;
        int val;
 
        if (!sk_fullsock(sk))
                return -EINVAL;
 
+       sock_owned_by_me(sk);
+
        if (level == SOL_SOCKET) {
                if (optlen != sizeof(int))
                        return -EINVAL;
@@ -4329,7 +4301,7 @@ BPF_CALL_5(bpf_setsockopt, struct bpf_sock_ops_kern *, bpf_sock,
                   sk->sk_prot->setsockopt == tcp_setsockopt) {
                if (optname == TCP_CONGESTION) {
                        char name[TCP_CA_NAME_MAX];
-                       bool reinit = bpf_sock->op > BPF_SOCK_OPS_NEEDS_ECN;
+                       bool reinit = flags & SOCKOPT_CC_REINIT;
 
                        strncpy(name, optval, min_t(long, optlen,
                                                    TCP_CA_NAME_MAX-1));
@@ -4376,24 +4348,14 @@ BPF_CALL_5(bpf_setsockopt, struct bpf_sock_ops_kern *, bpf_sock,
        return ret;
 }
 
-static const struct bpf_func_proto bpf_setsockopt_proto = {
-       .func           = bpf_setsockopt,
-       .gpl_only       = false,
-       .ret_type       = RET_INTEGER,
-       .arg1_type      = ARG_PTR_TO_CTX,
-       .arg2_type      = ARG_ANYTHING,
-       .arg3_type      = ARG_ANYTHING,
-       .arg4_type      = ARG_PTR_TO_MEM,
-       .arg5_type      = ARG_CONST_SIZE,
-};
-
-BPF_CALL_5(bpf_getsockopt, struct bpf_sock_ops_kern *, bpf_sock,
-          int, level, int, optname, char *, optval, int, optlen)
+static int _bpf_getsockopt(struct sock *sk, int level, int optname,
+                          char *optval, int optlen)
 {
-       struct sock *sk = bpf_sock->sk;
-
        if (!sk_fullsock(sk))
                goto err_clear;
+
+       sock_owned_by_me(sk);
+
 #ifdef CONFIG_INET
        if (level == SOL_TCP && sk->sk_prot->getsockopt == tcp_getsockopt) {
                struct inet_connection_sock *icsk;
@@ -4459,8 +4421,71 @@ err_clear:
        return -EINVAL;
 }
 
-static const struct bpf_func_proto bpf_getsockopt_proto = {
-       .func           = bpf_getsockopt,
+BPF_CALL_5(bpf_sock_addr_setsockopt, struct bpf_sock_addr_kern *, ctx,
+          int, level, int, optname, char *, optval, int, optlen)
+{
+       u32 flags = 0;
+       return _bpf_setsockopt(ctx->sk, level, optname, optval, optlen,
+                              flags);
+}
+
+static const struct bpf_func_proto bpf_sock_addr_setsockopt_proto = {
+       .func           = bpf_sock_addr_setsockopt,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_ANYTHING,
+       .arg3_type      = ARG_ANYTHING,
+       .arg4_type      = ARG_PTR_TO_MEM,
+       .arg5_type      = ARG_CONST_SIZE,
+};
+
+BPF_CALL_5(bpf_sock_addr_getsockopt, struct bpf_sock_addr_kern *, ctx,
+          int, level, int, optname, char *, optval, int, optlen)
+{
+       return _bpf_getsockopt(ctx->sk, level, optname, optval, optlen);
+}
+
+static const struct bpf_func_proto bpf_sock_addr_getsockopt_proto = {
+       .func           = bpf_sock_addr_getsockopt,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_ANYTHING,
+       .arg3_type      = ARG_ANYTHING,
+       .arg4_type      = ARG_PTR_TO_UNINIT_MEM,
+       .arg5_type      = ARG_CONST_SIZE,
+};
+
+BPF_CALL_5(bpf_sock_ops_setsockopt, struct bpf_sock_ops_kern *, bpf_sock,
+          int, level, int, optname, char *, optval, int, optlen)
+{
+       u32 flags = 0;
+       if (bpf_sock->op > BPF_SOCK_OPS_NEEDS_ECN)
+               flags |= SOCKOPT_CC_REINIT;
+       return _bpf_setsockopt(bpf_sock->sk, level, optname, optval, optlen,
+                              flags);
+}
+
+static const struct bpf_func_proto bpf_sock_ops_setsockopt_proto = {
+       .func           = bpf_sock_ops_setsockopt,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_CTX,
+       .arg2_type      = ARG_ANYTHING,
+       .arg3_type      = ARG_ANYTHING,
+       .arg4_type      = ARG_PTR_TO_MEM,
+       .arg5_type      = ARG_CONST_SIZE,
+};
+
+BPF_CALL_5(bpf_sock_ops_getsockopt, struct bpf_sock_ops_kern *, bpf_sock,
+          int, level, int, optname, char *, optval, int, optlen)
+{
+       return _bpf_getsockopt(bpf_sock->sk, level, optname, optval, optlen);
+}
+
+static const struct bpf_func_proto bpf_sock_ops_getsockopt_proto = {
+       .func           = bpf_sock_ops_getsockopt,
        .gpl_only       = false,
        .ret_type       = RET_INTEGER,
        .arg1_type      = ARG_PTR_TO_CTX,
@@ -5925,7 +5950,7 @@ BPF_CALL_3(bpf_sk_assign, struct sk_buff *, skb, struct sock *, sk, u64, flags)
                return -EOPNOTSUPP;
        if (unlikely(dev_net(skb->dev) != sock_net(sk)))
                return -ENETUNREACH;
-       if (unlikely(sk->sk_reuseport))
+       if (unlikely(sk_fullsock(sk) && sk->sk_reuseport))
                return -ESOCKTNOSUPPORT;
        if (sk_is_refcounted(sk) &&
            unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
@@ -5983,52 +6008,7 @@ bool bpf_helper_changes_pkt_data(void *func)
        return false;
 }
 
-const struct bpf_func_proto *
-bpf_base_func_proto(enum bpf_func_id func_id)
-{
-       switch (func_id) {
-       case BPF_FUNC_map_lookup_elem:
-               return &bpf_map_lookup_elem_proto;
-       case BPF_FUNC_map_update_elem:
-               return &bpf_map_update_elem_proto;
-       case BPF_FUNC_map_delete_elem:
-               return &bpf_map_delete_elem_proto;
-       case BPF_FUNC_map_push_elem:
-               return &bpf_map_push_elem_proto;
-       case BPF_FUNC_map_pop_elem:
-               return &bpf_map_pop_elem_proto;
-       case BPF_FUNC_map_peek_elem:
-               return &bpf_map_peek_elem_proto;
-       case BPF_FUNC_get_prandom_u32:
-               return &bpf_get_prandom_u32_proto;
-       case BPF_FUNC_get_smp_processor_id:
-               return &bpf_get_raw_smp_processor_id_proto;
-       case BPF_FUNC_get_numa_node_id:
-               return &bpf_get_numa_node_id_proto;
-       case BPF_FUNC_tail_call:
-               return &bpf_tail_call_proto;
-       case BPF_FUNC_ktime_get_ns:
-               return &bpf_ktime_get_ns_proto;
-       default:
-               break;
-       }
-
-       if (!capable(CAP_SYS_ADMIN))
-               return NULL;
-
-       switch (func_id) {
-       case BPF_FUNC_spin_lock:
-               return &bpf_spin_lock_proto;
-       case BPF_FUNC_spin_unlock:
-               return &bpf_spin_unlock_proto;
-       case BPF_FUNC_trace_printk:
-               return bpf_get_trace_printk_proto();
-       case BPF_FUNC_jiffies64:
-               return &bpf_jiffies64_proto;
-       default:
-               return NULL;
-       }
-}
+const struct bpf_func_proto bpf_event_output_data_proto __weak;
 
 static const struct bpf_func_proto *
 sock_filter_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
@@ -6119,6 +6099,22 @@ sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_sk_storage_get_proto;
        case BPF_FUNC_sk_storage_delete:
                return &bpf_sk_storage_delete_proto;
+       case BPF_FUNC_setsockopt:
+               switch (prog->expected_attach_type) {
+               case BPF_CGROUP_INET4_CONNECT:
+               case BPF_CGROUP_INET6_CONNECT:
+                       return &bpf_sock_addr_setsockopt_proto;
+               default:
+                       return NULL;
+               }
+       case BPF_FUNC_getsockopt:
+               switch (prog->expected_attach_type) {
+               case BPF_CGROUP_INET4_CONNECT:
+               case BPF_CGROUP_INET6_CONNECT:
+                       return &bpf_sock_addr_getsockopt_proto;
+               default:
+                       return NULL;
+               }
        default:
                return bpf_base_func_proto(func_id);
        }
@@ -6213,6 +6209,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
                return &bpf_skb_adjust_room_proto;
        case BPF_FUNC_skb_change_tail:
                return &bpf_skb_change_tail_proto;
+       case BPF_FUNC_skb_change_head:
+               return &bpf_skb_change_head_proto;
        case BPF_FUNC_skb_get_tunnel_key:
                return &bpf_skb_get_tunnel_key_proto;
        case BPF_FUNC_skb_set_tunnel_key:
@@ -6335,9 +6333,9 @@ sock_ops_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
        switch (func_id) {
        case BPF_FUNC_setsockopt:
-               return &bpf_setsockopt_proto;
+               return &bpf_sock_ops_setsockopt_proto;
        case BPF_FUNC_getsockopt:
-               return &bpf_getsockopt_proto;
+               return &bpf_sock_ops_getsockopt_proto;
        case BPF_FUNC_sock_ops_cb_flags_set:
                return &bpf_sock_ops_cb_flags_set_proto;
        case BPF_FUNC_sock_map_update:
@@ -8786,6 +8784,10 @@ BPF_CALL_4(sk_select_reuseport, struct sk_reuseport_kern *, reuse_kern,
 
        reuse = rcu_dereference(selected_sk->sk_reuseport_cb);
        if (!reuse) {
+               /* Lookup in sock_map can return TCP ESTABLISHED sockets. */
+               if (sk_is_refcounted(selected_sk))
+                       sock_put(selected_sk);
+
                /* reuseport_array has only sk with non NULL sk_reuseport_cb.
                 * The only (!reuse) case here is - the sk has already been
                 * unhashed (e.g. by close()), so treat it as -ENOENT.
index 1d653fb..e491b08 100644 (file)
@@ -6,7 +6,7 @@
  *           Jamal Hadi Salim
  *           Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
- * See Documentation/networking/gen_stats.txt
+ * See Documentation/networking/gen_stats.rst
  */
 
 #include <linux/types.h>
index f153e06..75431ca 100644 (file)
@@ -34,6 +34,9 @@ static DEFINE_SPINLOCK(lweventlist_lock);
 
 static unsigned char default_operstate(const struct net_device *dev)
 {
+       if (netif_testing(dev))
+               return IF_OPER_TESTING;
+
        if (!netif_carrier_ok(dev))
                return (dev->ifindex != dev_get_iflink(dev) ?
                        IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN);
@@ -55,11 +58,15 @@ static void rfc2863_policy(struct net_device *dev)
        write_lock_bh(&dev_base_lock);
 
        switch(dev->link_mode) {
+       case IF_LINK_MODE_TESTING:
+               if (operstate == IF_OPER_UP)
+                       operstate = IF_OPER_TESTING;
+               break;
+
        case IF_LINK_MODE_DORMANT:
                if (operstate == IF_OPER_UP)
                        operstate = IF_OPER_DORMANT;
                break;
-
        case IF_LINK_MODE_DEFAULT:
        default:
                break;
@@ -74,7 +81,8 @@ static void rfc2863_policy(struct net_device *dev)
 void linkwatch_init_dev(struct net_device *dev)
 {
        /* Handle pre-registration link state changes */
-       if (!netif_carrier_ok(dev) || netif_dormant(dev))
+       if (!netif_carrier_ok(dev) || netif_dormant(dev) ||
+           netif_testing(dev))
                rfc2863_policy(dev);
 }
 
index 39d37d0..b607ea6 100644 (file)
@@ -1956,6 +1956,9 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
                                   NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
        }
 
+       if (protocol)
+               neigh->protocol = protocol;
+
        if (ndm->ndm_flags & NTF_EXT_LEARNED)
                flags |= NEIGH_UPDATE_F_EXT_LEARNED;
 
@@ -1969,9 +1972,6 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
                err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags,
                                     NETLINK_CB(skb).portid, extack);
 
-       if (protocol)
-               neigh->protocol = protocol;
-
        neigh_release(neigh);
 
 out:
@@ -3379,7 +3379,7 @@ EXPORT_SYMBOL(neigh_app_ns);
 static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN);
 
 static int proc_unres_qlen(struct ctl_table *ctl, int write,
-                          void __user *buffer, size_t *lenp, loff_t *ppos)
+                          void *buffer, size_t *lenp, loff_t *ppos)
 {
        int size, ret;
        struct ctl_table tmp = *ctl;
@@ -3443,8 +3443,8 @@ static void neigh_proc_update(struct ctl_table *ctl, int write)
 }
 
 static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write,
-                                          void __user *buffer,
-                                          size_t *lenp, loff_t *ppos)
+                                          void *buffer, size_t *lenp,
+                                          loff_t *ppos)
 {
        struct ctl_table tmp = *ctl;
        int ret;
@@ -3457,8 +3457,8 @@ static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write,
        return ret;
 }
 
-int neigh_proc_dointvec(struct ctl_table *ctl, int write,
-                       void __user *buffer, size_t *lenp, loff_t *ppos)
+int neigh_proc_dointvec(struct ctl_table *ctl, int write, void *buffer,
+                       size_t *lenp, loff_t *ppos)
 {
        int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
@@ -3467,8 +3467,7 @@ int neigh_proc_dointvec(struct ctl_table *ctl, int write,
 }
 EXPORT_SYMBOL(neigh_proc_dointvec);
 
-int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write,
-                               void __user *buffer,
+int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, void *buffer,
                                size_t *lenp, loff_t *ppos)
 {
        int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos);
@@ -3479,8 +3478,8 @@ int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write,
 EXPORT_SYMBOL(neigh_proc_dointvec_jiffies);
 
 static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write,
-                                             void __user *buffer,
-                                             size_t *lenp, loff_t *ppos)
+                                             void *buffer, size_t *lenp,
+                                             loff_t *ppos)
 {
        int ret = proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos);
 
@@ -3489,8 +3488,7 @@ static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write,
 }
 
 int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write,
-                                  void __user *buffer,
-                                  size_t *lenp, loff_t *ppos)
+                                  void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret = proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos);
 
@@ -3500,8 +3498,8 @@ int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write,
 EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies);
 
 static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write,
-                                         void __user *buffer,
-                                         size_t *lenp, loff_t *ppos)
+                                         void *buffer, size_t *lenp,
+                                         loff_t *ppos)
 {
        int ret = proc_unres_qlen(ctl, write, buffer, lenp, ppos);
 
@@ -3510,8 +3508,8 @@ static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write,
 }
 
 static int neigh_proc_base_reachable_time(struct ctl_table *ctl, int write,
-                                         void __user *buffer,
-                                         size_t *lenp, loff_t *ppos)
+                                         void *buffer, size_t *lenp,
+                                         loff_t *ppos)
 {
        struct neigh_parms *p = ctl->extra2;
        int ret;
index cf02157..880e89c 100644 (file)
@@ -80,7 +80,7 @@ static ssize_t netdev_store(struct device *dev, struct device_attribute *attr,
        struct net_device *netdev = to_net_dev(dev);
        struct net *net = dev_net(netdev);
        unsigned long new;
-       int ret = -EINVAL;
+       int ret;
 
        if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
@@ -243,6 +243,18 @@ static ssize_t duplex_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(duplex);
 
+static ssize_t testing_show(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       struct net_device *netdev = to_net_dev(dev);
+
+       if (netif_running(netdev))
+               return sprintf(buf, fmt_dec, !!netif_testing(netdev));
+
+       return -EINVAL;
+}
+static DEVICE_ATTR_RO(testing);
+
 static ssize_t dormant_show(struct device *dev,
                            struct device_attribute *attr, char *buf)
 {
@@ -260,7 +272,7 @@ static const char *const operstates[] = {
        "notpresent", /* currently unused */
        "down",
        "lowerlayerdown",
-       "testing", /* currently unused */
+       "testing",
        "dormant",
        "up"
 };
@@ -355,7 +367,7 @@ NETDEVICE_SHOW_RW(tx_queue_len, fmt_dec);
 
 static int change_gro_flush_timeout(struct net_device *dev, unsigned long val)
 {
-       dev->gro_flush_timeout = val;
+       WRITE_ONCE(dev->gro_flush_timeout, val);
        return 0;
 }
 
@@ -370,6 +382,23 @@ static ssize_t gro_flush_timeout_store(struct device *dev,
 }
 NETDEVICE_SHOW_RW(gro_flush_timeout, fmt_ulong);
 
+static int change_napi_defer_hard_irqs(struct net_device *dev, unsigned long val)
+{
+       WRITE_ONCE(dev->napi_defer_hard_irqs, val);
+       return 0;
+}
+
+static ssize_t napi_defer_hard_irqs_store(struct device *dev,
+                                         struct device_attribute *attr,
+                                         const char *buf, size_t len)
+{
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       return netdev_store(dev, attr, buf, len, change_napi_defer_hard_irqs);
+}
+NETDEVICE_SHOW_RW(napi_defer_hard_irqs, fmt_dec);
+
 static ssize_t ifalias_store(struct device *dev, struct device_attribute *attr,
                             const char *buf, size_t len)
 {
@@ -524,6 +553,7 @@ static struct attribute *net_class_attrs[] __ro_after_init = {
        &dev_attr_speed.attr,
        &dev_attr_duplex.attr,
        &dev_attr_dormant.attr,
+       &dev_attr_testing.attr,
        &dev_attr_operstate.attr,
        &dev_attr_carrier_changes.attr,
        &dev_attr_ifalias.attr,
@@ -532,6 +562,7 @@ static struct attribute *net_class_attrs[] __ro_after_init = {
        &dev_attr_flags.attr,
        &dev_attr_tx_queue_len.attr,
        &dev_attr_gro_flush_timeout.attr,
+       &dev_attr_napi_defer_hard_irqs.attr,
        &dev_attr_phys_port_id.attr,
        &dev_attr_phys_port_name.attr,
        &dev_attr_phys_switch_id.attr,
index b4c87fe..41b24cd 100644 (file)
@@ -127,10 +127,8 @@ static int write_classid(struct cgroup_subsys_state *css, struct cftype *cft,
        cs->classid = (u32)value;
 
        css_task_iter_start(css, 0, &it);
-       while ((p = css_task_iter_next(&it))) {
+       while ((p = css_task_iter_next(&it)))
                update_classid_task(p, cs->classid);
-               cond_resched();
-       }
        css_task_iter_end(&it);
 
        return 0;
index 849380a..15b366a 100644 (file)
@@ -69,10 +69,11 @@ module_param(carrier_timeout, uint, 0644);
 #define np_notice(np, fmt, ...)                                \
        pr_notice("%s: " fmt, np->name, ##__VA_ARGS__)
 
-static int netpoll_start_xmit(struct sk_buff *skb, struct net_device *dev,
-                             struct netdev_queue *txq)
+static netdev_tx_t netpoll_start_xmit(struct sk_buff *skb,
+                                     struct net_device *dev,
+                                     struct netdev_queue *txq)
 {
-       int status = NETDEV_TX_OK;
+       netdev_tx_t status = NETDEV_TX_OK;
        netdev_features_t features;
 
        features = netif_skb_features(skb);
@@ -307,7 +308,7 @@ static int netpoll_owner_active(struct net_device *dev)
 void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
                             struct net_device *dev)
 {
-       int status = NETDEV_TX_BUSY;
+       netdev_tx_t status = NETDEV_TX_BUSY;
        unsigned long tries;
        /* It is up to the caller to keep npinfo alive. */
        struct netpoll_info *npinfo;
index 08e2811..b53b6d3 100644 (file)
@@ -56,7 +56,7 @@
  * Integrated to 2.5.x 021029 --Lucio Maciel (luciomaciel@zipmail.com.br)
  *
  * 021124 Finished major redesign and rewrite for new functionality.
- * See Documentation/networking/pktgen.txt for how to use this.
+ * See Documentation/networking/pktgen.rst for how to use this.
  *
  * The new operation:
  * For each CPU one thread/process is created at start. This process checks
index 709ebbf..2269199 100644 (file)
@@ -829,11 +829,18 @@ static void set_operstate(struct net_device *dev, unsigned char transition)
        switch (transition) {
        case IF_OPER_UP:
                if ((operstate == IF_OPER_DORMANT ||
+                    operstate == IF_OPER_TESTING ||
                     operstate == IF_OPER_UNKNOWN) &&
-                   !netif_dormant(dev))
+                   !netif_dormant(dev) && !netif_testing(dev))
                        operstate = IF_OPER_UP;
                break;
 
+       case IF_OPER_TESTING:
+               if (operstate == IF_OPER_UP ||
+                   operstate == IF_OPER_UNKNOWN)
+                       operstate = IF_OPER_TESTING;
+               break;
+
        case IF_OPER_DORMANT:
                if (operstate == IF_OPER_UP ||
                    operstate == IF_OPER_UNKNOWN)
@@ -3990,8 +3997,8 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh,
        struct ndmsg *ndm;
        struct nlattr *tb[NDA_MAX+1];
        struct net_device *dev;
-       int err = -EINVAL;
        __u8 *addr;
+       int err;
        u16 vid;
 
        if (!netlink_capable(skb, CAP_NET_ADMIN))
index 7e29590..1bf0c3d 100644 (file)
@@ -102,7 +102,7 @@ EXPORT_SYMBOL(sysctl_max_skb_frags);
 static void skb_panic(struct sk_buff *skb, unsigned int sz, void *addr,
                      const char msg[])
 {
-       pr_emerg("%s: text:%p len:%d put:%d head:%p data:%p tail:%#lx end:%#lx dev:%s\n",
+       pr_emerg("%s: text:%px len:%d put:%d head:%px data:%px tail:%#lx end:%#lx dev:%s\n",
                 msg, addr, skb->len, sz, skb->head, skb->data,
                 (unsigned long)skb->tail, (unsigned long)skb->end,
                 skb->dev ? skb->dev->name : "<NULL>");
index ce1d8dc..b714162 100644 (file)
@@ -1872,7 +1872,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
                 * as not suitable for copying when cloning.
                 */
                if (sk_user_data_is_nocopy(newsk))
-                       RCU_INIT_POINTER(newsk->sk_user_data, NULL);
+                       newsk->sk_user_data = NULL;
 
                newsk->sk_err      = 0;
                newsk->sk_err_soft = 0;
@@ -2364,7 +2364,6 @@ static void sk_leave_memory_pressure(struct sock *sk)
        }
 }
 
-/* On 32bit arches, an skb frag is limited to 2^15 */
 #define SKB_FRAG_PAGE_ORDER    get_order(32768)
 DEFINE_STATIC_KEY_FALSE(net_high_order_alloc_disable_key);
 
index b08dfae..00a26cf 100644 (file)
@@ -343,7 +343,14 @@ static struct sock *__sock_map_lookup_elem(struct bpf_map *map, u32 key)
 
 static void *sock_map_lookup(struct bpf_map *map, void *key)
 {
-       return __sock_map_lookup_elem(map, *(u32 *)key);
+       struct sock *sk;
+
+       sk = __sock_map_lookup_elem(map, *(u32 *)key);
+       if (!sk || !sk_fullsock(sk))
+               return NULL;
+       if (sk_is_refcounted(sk) && !refcount_inc_not_zero(&sk->sk_refcnt))
+               return NULL;
+       return sk;
 }
 
 static void *sock_map_lookup_sys(struct bpf_map *map, void *key)
@@ -1051,7 +1058,14 @@ static void *sock_hash_lookup_sys(struct bpf_map *map, void *key)
 
 static void *sock_hash_lookup(struct bpf_map *map, void *key)
 {
-       return __sock_hash_lookup_elem(map, key);
+       struct sock *sk;
+
+       sk = __sock_hash_lookup_elem(map, key);
+       if (!sk || !sk_fullsock(sk))
+               return NULL;
+       if (sk_is_refcounted(sk) && !refcount_inc_not_zero(&sk->sk_refcnt))
+               return NULL;
+       return sk;
 }
 
 static void sock_hash_release_progs(struct bpf_map *map)
index 9f9e00b..0ddb13a 100644 (file)
@@ -45,7 +45,7 @@ EXPORT_SYMBOL(sysctl_devconf_inherit_init_net);
 
 #ifdef CONFIG_RPS
 static int rps_sock_flow_sysctl(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        unsigned int orig_size, size;
        int ret, i;
@@ -115,8 +115,7 @@ static int rps_sock_flow_sysctl(struct ctl_table *table, int write,
 static DEFINE_MUTEX(flow_limit_update_mutex);
 
 static int flow_limit_cpu_sysctl(struct ctl_table *table, int write,
-                                void __user *buffer, size_t *lenp,
-                                loff_t *ppos)
+                                void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct sd_flow_limit *cur;
        struct softnet_data *sd;
@@ -180,10 +179,7 @@ write_unlock:
                }
                if (len < *lenp)
                        kbuf[len++] = '\n';
-               if (copy_to_user(buffer, kbuf, len)) {
-                       ret = -EFAULT;
-                       goto done;
-               }
+               memcpy(buffer, kbuf, len);
                *lenp = len;
                *ppos += len;
        }
@@ -194,8 +190,7 @@ done:
 }
 
 static int flow_limit_table_len_sysctl(struct ctl_table *table, int write,
-                                      void __user *buffer, size_t *lenp,
-                                      loff_t *ppos)
+                                      void *buffer, size_t *lenp, loff_t *ppos)
 {
        unsigned int old, *ptr;
        int ret;
@@ -217,7 +212,7 @@ static int flow_limit_table_len_sysctl(struct ctl_table *table, int write,
 
 #ifdef CONFIG_NET_SCHED
 static int set_default_qdisc(struct ctl_table *table, int write,
-                            void __user *buffer, size_t *lenp, loff_t *ppos)
+                            void *buffer, size_t *lenp, loff_t *ppos)
 {
        char id[IFNAMSIZ];
        struct ctl_table tbl = {
@@ -236,7 +231,7 @@ static int set_default_qdisc(struct ctl_table *table, int write,
 #endif
 
 static int proc_do_dev_weight(struct ctl_table *table, int write,
-                          void __user *buffer, size_t *lenp, loff_t *ppos)
+                          void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
 
@@ -251,7 +246,7 @@ static int proc_do_dev_weight(struct ctl_table *table, int write,
 }
 
 static int proc_do_rss_key(struct ctl_table *table, int write,
-                          void __user *buffer, size_t *lenp, loff_t *ppos)
+                          void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table fake_table;
        char buf[NETDEV_RSS_KEY_LEN * 3];
@@ -264,7 +259,7 @@ static int proc_do_rss_key(struct ctl_table *table, int write,
 
 #ifdef CONFIG_BPF_JIT
 static int proc_dointvec_minmax_bpf_enable(struct ctl_table *table, int write,
-                                          void __user *buffer, size_t *lenp,
+                                          void *buffer, size_t *lenp,
                                           loff_t *ppos)
 {
        int ret, jit_enable = *(int *)table->data;
@@ -291,8 +286,7 @@ static int proc_dointvec_minmax_bpf_enable(struct ctl_table *table, int write,
 # ifdef CONFIG_HAVE_EBPF_JIT
 static int
 proc_dointvec_minmax_bpf_restricted(struct ctl_table *table, int write,
-                                   void __user *buffer, size_t *lenp,
-                                   loff_t *ppos)
+                                   void *buffer, size_t *lenp, loff_t *ppos)
 {
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
@@ -303,8 +297,7 @@ proc_dointvec_minmax_bpf_restricted(struct ctl_table *table, int write,
 
 static int
 proc_dolongvec_minmax_bpf_restricted(struct ctl_table *table, int write,
-                                    void __user *buffer, size_t *lenp,
-                                    loff_t *ppos)
+                                    void *buffer, size_t *lenp, loff_t *ppos)
 {
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
index 9c3b27c..7dce4f6 100644 (file)
@@ -108,11 +108,6 @@ extern int  sysctl_dccp_sync_ratelimit;
 #define ADD48(a, b)     (((a) + (b)) & UINT48_MAX)
 #define SUB48(a, b)     ADD48((a), COMPLEMENT48(b))
 
-static inline void dccp_set_seqno(u64 *seqno, u64 value)
-{
-       *seqno = value & UINT48_MAX;
-}
-
 static inline void dccp_inc_seqno(u64 *seqno)
 {
        *seqno = ADD48(*seqno, 1);
index 0935453..8f98fb2 100644 (file)
@@ -15,7 +15,7 @@ config DECNET
          <http://linux-decnet.sourceforge.net/>.
 
          More detailed documentation is available in
-         <file:Documentation/networking/decnet.txt>.
+         <file:Documentation/networking/decnet.rst>.
 
          Be sure to say Y to "/proc file system support" and "Sysctl support"
          below when using DECnet, since you will need sysctl support to aid
@@ -40,4 +40,4 @@ config DECNET_ROUTER
          filtering" option will be required for the forthcoming routing daemon
          to work.
 
-         See <file:Documentation/networking/decnet.txt> for more information.
+         See <file:Documentation/networking/decnet.rst> for more information.
index cca7ae7..65abcf1 100644 (file)
@@ -160,8 +160,8 @@ static int max_t3[] = { 8191 }; /* Must fit in 16 bits when multiplied by BCT3MU
 static int min_priority[1];
 static int max_priority[] = { 127 }; /* From DECnet spec */
 
-static int dn_forwarding_proc(struct ctl_table *, int,
-                       void __user *, size_t *, loff_t *);
+static int dn_forwarding_proc(struct ctl_table *, int, void *, size_t *,
+               loff_t *);
 static struct dn_dev_sysctl_table {
        struct ctl_table_header *sysctl_header;
        struct ctl_table dn_dev_vars[5];
@@ -245,8 +245,7 @@ static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms)
 }
 
 static int dn_forwarding_proc(struct ctl_table *table, int write,
-                               void __user *buffer,
-                               size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
 #ifdef CONFIG_DECNET_ROUTER
        struct net_device *dev = table->extra1;
index 55bf64a..deae519 100644 (file)
@@ -134,8 +134,7 @@ static int parse_addr(__le16 *addr, char *str)
 }
 
 static int dn_node_address_handler(struct ctl_table *table, int write,
-                               void __user *buffer,
-                               size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        char addr[DN_ASCBUF_LEN];
        size_t len;
@@ -148,10 +147,7 @@ static int dn_node_address_handler(struct ctl_table *table, int write,
 
        if (write) {
                len = (*lenp < DN_ASCBUF_LEN) ? *lenp : (DN_ASCBUF_LEN-1);
-
-               if (copy_from_user(addr, buffer, len))
-                       return -EFAULT;
-
+               memcpy(addr, buffer, len);
                addr[len] = 0;
                strip_it(addr);
 
@@ -173,11 +169,9 @@ static int dn_node_address_handler(struct ctl_table *table, int write,
        len = strlen(addr);
        addr[len++] = '\n';
 
-       if (len > *lenp) len = *lenp;
-
-       if (copy_to_user(buffer, addr, len))
-               return -EFAULT;
-
+       if (len > *lenp)
+               len = *lenp;
+       memcpy(buffer, addr, len);
        *lenp = len;
        *ppos += len;
 
@@ -185,8 +179,7 @@ static int dn_node_address_handler(struct ctl_table *table, int write,
 }
 
 static int dn_def_dev_handler(struct ctl_table *table, int write,
-                               void __user *buffer,
-                               size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        size_t len;
        struct net_device *dev;
@@ -201,9 +194,7 @@ static int dn_def_dev_handler(struct ctl_table *table, int write,
                if (*lenp > 16)
                        return -E2BIG;
 
-               if (copy_from_user(devname, buffer, *lenp))
-                       return -EFAULT;
-
+               memcpy(devname, buffer, *lenp);
                devname[*lenp] = 0;
                strip_it(devname);
 
@@ -238,9 +229,7 @@ static int dn_def_dev_handler(struct ctl_table *table, int write,
 
        if (len > *lenp) len = *lenp;
 
-       if (copy_to_user(buffer, devname, len))
-               return -EFAULT;
-
+       memcpy(buffer, devname, len);
        *lenp = len;
        *ppos += len;
 
index 0a1c223..255df9b 100644 (file)
@@ -19,7 +19,7 @@ config DNS_RESOLVER
          SMB2 later.  DNS Resolver is supported by the userspace upcall
          helper "/sbin/dns.resolver" via /etc/request-key.conf.
 
-         See <file:Documentation/networking/dns_resolver.txt> for further
+         See <file:Documentation/networking/dns_resolver.rst> for further
          information.
 
          To compile this as a module, choose M here: the module will be called
index ad53eb3..3aced95 100644 (file)
@@ -1,6 +1,6 @@
 /* Key type used to cache DNS lookups made by the kernel
  *
- * See Documentation/networking/dns_resolver.txt
+ * See Documentation/networking/dns_resolver.rst
  *
  *   Copyright (c) 2007 Igor Mammedov
  *   Author(s): Igor Mammedov (niallain@gmail.com)
index cab4e0d..82b084c 100644 (file)
@@ -1,7 +1,7 @@
 /* Upcall routine, designed to work as a key type and working through
  * /sbin/request-key to contact userspace when handling DNS queries.
  *
- * See Documentation/networking/dns_resolver.txt
+ * See Documentation/networking/dns_resolver.rst
  *
  *   Copyright (c) 2007 Igor Mammedov
  *   Author(s): Igor Mammedov (niallain@gmail.com)
index 92663dc..7396130 100644 (file)
@@ -9,6 +9,7 @@ menuconfig NET_DSA
        tristate "Distributed Switch Architecture"
        depends on HAVE_NET_DSA
        depends on BRIDGE || BRIDGE=n
+       select GRO_CELLS
        select NET_SWITCHDEV
        select PHYLINK
        select NET_DEVLINK
index ee2610c..0384a91 100644 (file)
@@ -234,7 +234,7 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
        if (dsa_skb_defer_rx_timestamp(p, skb))
                return 0;
 
-       netif_receive_skb(skb);
+       gro_cells_receive(&p->gcells, skb);
 
        return 0;
 }
index 9a271a5..d90665b 100644 (file)
@@ -459,7 +459,7 @@ static int dsa_tree_setup_switches(struct dsa_switch_tree *dst)
        list_for_each_entry(dp, &dst->ports, list) {
                err = dsa_port_setup(dp);
                if (err)
-                       goto teardown;
+                       continue;
        }
 
        return 0;
index 904cc7c..6d9a1ef 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/netdevice.h>
 #include <linux/netpoll.h>
 #include <net/dsa.h>
+#include <net/gro_cells.h>
 
 enum {
        DSA_NOTIFIER_AGEING_TIME,
@@ -77,6 +78,8 @@ struct dsa_slave_priv {
 
        struct pcpu_sw_netstats *stats64;
 
+       struct gro_cells        gcells;
+
        /* DSA port data, such as switch, port index, etc. */
        struct dsa_port         *dp;
 
index b5c535a..a621367 100644 (file)
@@ -289,7 +289,8 @@ static void dsa_master_ndo_teardown(struct net_device *dev)
 {
        struct dsa_port *cpu_dp = dev->dsa_ptr;
 
-       dev->netdev_ops = cpu_dp->orig_ndo_ops;
+       if (cpu_dp->orig_ndo_ops)
+               dev->netdev_ops = cpu_dp->orig_ndo_ops;
        cpu_dp->orig_ndo_ops = NULL;
 }
 
index 231b2d4..a58fdd3 100644 (file)
@@ -670,11 +670,16 @@ int dsa_port_link_register_of(struct dsa_port *dp)
 {
        struct dsa_switch *ds = dp->ds;
        struct device_node *phy_np;
+       int port = dp->index;
 
        if (!ds->ops->adjust_link) {
                phy_np = of_parse_phandle(dp->dn, "phy-handle", 0);
-               if (of_phy_is_fixed_link(dp->dn) || phy_np)
+               if (of_phy_is_fixed_link(dp->dn) || phy_np) {
+                       if (ds->ops->phylink_mac_link_down)
+                               ds->ops->phylink_mac_link_down(ds, port,
+                                       MLO_AN_FIXED, PHY_INTERFACE_MODE_NA);
                        return dsa_port_phylink_register(dp);
+               }
                return 0;
        }
 
index e94eb1a..ea0fcf7 100644 (file)
@@ -856,20 +856,18 @@ dsa_slave_add_cls_matchall_mirred(struct net_device *dev,
        struct dsa_port *to_dp;
        int err;
 
-       act = &cls->rule->action.entries[0];
-
        if (!ds->ops->port_mirror_add)
                return -EOPNOTSUPP;
 
-       if (!act->dev)
-               return -EINVAL;
-
        if (!flow_action_basic_hw_stats_check(&cls->rule->action,
                                              cls->common.extack))
                return -EOPNOTSUPP;
 
        act = &cls->rule->action.entries[0];
 
+       if (!act->dev)
+               return -EINVAL;
+
        if (!dsa_slave_dev_check(act->dev))
                return -EOPNOTSUPP;
 
@@ -1590,10 +1588,10 @@ void dsa_port_phylink_mac_change(struct dsa_switch *ds, int port, bool up)
 }
 EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_change);
 
-static void dsa_slave_phylink_fixed_state(struct net_device *dev,
+static void dsa_slave_phylink_fixed_state(struct phylink_config *config,
                                          struct phylink_link_state *state)
 {
-       struct dsa_port *dp = dsa_slave_to_port(dev);
+       struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
        struct dsa_switch *ds = dp->ds;
 
        /* No need to check that this operation is valid, the callback would
@@ -1633,6 +1631,15 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev)
        dp->pl_config.dev = &slave_dev->dev;
        dp->pl_config.type = PHYLINK_NETDEV;
 
+       /* The get_fixed_state callback takes precedence over polling the
+        * link GPIO in PHYLINK (see phylink_get_fixed_state).  Only set
+        * this if the switch provides such a callback.
+        */
+       if (ds->ops->phylink_fixed_state) {
+               dp->pl_config.get_fixed_state = dsa_slave_phylink_fixed_state;
+               dp->pl_config.poll_fixed_state = true;
+       }
+
        dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), mode,
                                &dsa_port_phylink_mac_ops);
        if (IS_ERR(dp->pl)) {
@@ -1641,13 +1648,6 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev)
                return PTR_ERR(dp->pl);
        }
 
-       /* Register only if the switch provides such a callback, since this
-        * callback takes precedence over polling the link GPIO in PHYLINK
-        * (see phylink_get_fixed_state).
-        */
-       if (ds->ops->phylink_fixed_state)
-               phylink_fixed_state_cb(dp->pl, dsa_slave_phylink_fixed_state);
-
        if (ds->ops->get_phy_flags)
                phy_flags = ds->ops->get_phy_flags(ds, dp->index);
 
@@ -1669,6 +1669,15 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev)
        return ret;
 }
 
+static struct lock_class_key dsa_slave_netdev_xmit_lock_key;
+static void dsa_slave_set_lockdep_class_one(struct net_device *dev,
+                                           struct netdev_queue *txq,
+                                           void *_unused)
+{
+       lockdep_set_class(&txq->_xmit_lock,
+                         &dsa_slave_netdev_xmit_lock_key);
+}
+
 int dsa_slave_suspend(struct net_device *slave_dev)
 {
        struct dsa_port *dp = dsa_slave_to_port(slave_dev);
@@ -1752,6 +1761,9 @@ int dsa_slave_create(struct dsa_port *port)
                slave_dev->max_mtu = ETH_MAX_MTU;
        SET_NETDEV_DEVTYPE(slave_dev, &dsa_type);
 
+       netdev_for_each_tx_queue(slave_dev, dsa_slave_set_lockdep_class_one,
+                                NULL);
+
        SET_NETDEV_DEV(slave_dev, port->ds->dev);
        slave_dev->dev.of_node = port->dn;
        slave_dev->vlan_features = master->vlan_features;
@@ -1762,6 +1774,11 @@ int dsa_slave_create(struct dsa_port *port)
                free_netdev(slave_dev);
                return -ENOMEM;
        }
+
+       ret = gro_cells_init(&p->gcells, slave_dev);
+       if (ret)
+               goto out_free;
+
        p->dp = port;
        INIT_LIST_HEAD(&p->mall_tc_list);
        p->xmit = cpu_dp->tag_ops->xmit;
@@ -1770,18 +1787,16 @@ int dsa_slave_create(struct dsa_port *port)
        rtnl_lock();
        ret = dsa_slave_change_mtu(slave_dev, ETH_DATA_LEN);
        rtnl_unlock();
-       if (ret && ret != -EOPNOTSUPP) {
-               dev_err(ds->dev, "error %d setting MTU on port %d\n",
-                       ret, port->index);
-               goto out_free;
-       }
+       if (ret)
+               dev_warn(ds->dev, "nonfatal error %d setting MTU on port %d\n",
+                        ret, port->index);
 
        netif_carrier_off(slave_dev);
 
        ret = dsa_slave_phy_setup(slave_dev);
        if (ret) {
                netdev_err(master, "error %d setting up slave phy\n", ret);
-               goto out_free;
+               goto out_gcells;
        }
 
        dsa_slave_notify(slave_dev, DSA_PORT_REGISTER);
@@ -1800,6 +1815,8 @@ out_phy:
        phylink_disconnect_phy(p->dp->pl);
        rtnl_unlock();
        phylink_destroy(p->dp->pl);
+out_gcells:
+       gro_cells_destroy(&p->gcells);
 out_free:
        free_percpu(p->stats64);
        free_netdev(slave_dev);
@@ -1820,6 +1837,7 @@ void dsa_slave_destroy(struct net_device *slave_dev)
        dsa_slave_notify(slave_dev, DSA_PORT_UNREGISTER);
        unregister_netdev(slave_dev);
        phylink_destroy(dp->pl);
+       gro_cells_destroy(&p->gcells);
        free_percpu(p->stats64);
        free_netdev(slave_dev);
 }
index 89d0b18..52102ab 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/phy.h>
 #include <linux/bitops.h>
 #include <linux/uaccess.h>
-#include <linux/vermagic.h>
 #include <linux/vmalloc.h>
 #include <linux/sfp.h>
 #include <linux/slab.h>
@@ -28,7 +27,7 @@
 #include <net/xdp_sock.h>
 #include <net/flow_offload.h>
 #include <linux/ethtool_netlink.h>
-
+#include <generated/utsrelease.h>
 #include "common.h"
 
 /*
@@ -553,6 +552,8 @@ static int ethtool_get_link_ksettings(struct net_device *dev,
        link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS;
        link_ksettings.base.link_mode_masks_nwords
                = __ETHTOOL_LINK_MODE_MASK_NU32;
+       link_ksettings.base.master_slave_cfg = MASTER_SLAVE_CFG_UNSUPPORTED;
+       link_ksettings.base.master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
 
        return store_link_ksettings_for_user(useraddr, &link_ksettings);
 }
@@ -590,6 +591,10 @@ static int ethtool_set_link_ksettings(struct net_device *dev,
            != link_ksettings.base.link_mode_masks_nwords)
                return -EINVAL;
 
+       if (link_ksettings.base.master_slave_cfg ||
+           link_ksettings.base.master_slave_state)
+               return -EINVAL;
+
        err = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
        if (err >= 0) {
                ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL);
@@ -1746,7 +1751,9 @@ static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
        if (!data)
                return -ENOMEM;
 
+       netif_testing_on(dev);
        ops->self_test(dev, &test, data);
+       netif_testing_off(dev);
 
        ret = -EFAULT;
        if (copy_to_user(useraddr, &test, sizeof(test)))
index 452608c..fd4f3e5 100644 (file)
@@ -27,6 +27,8 @@ linkmodes_get_policy[ETHTOOL_A_LINKMODES_MAX + 1] = {
        [ETHTOOL_A_LINKMODES_PEER]              = { .type = NLA_REJECT },
        [ETHTOOL_A_LINKMODES_SPEED]             = { .type = NLA_REJECT },
        [ETHTOOL_A_LINKMODES_DUPLEX]            = { .type = NLA_REJECT },
+       [ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]  = { .type = NLA_REJECT },
+       [ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE]        = { .type = NLA_REJECT },
 };
 
 static int linkmodes_prepare_data(const struct ethnl_req_info *req_base,
@@ -63,6 +65,7 @@ static int linkmodes_reply_size(const struct ethnl_req_info *req_base,
 {
        const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base);
        const struct ethtool_link_ksettings *ksettings = &data->ksettings;
+       const struct ethtool_link_settings *lsettings = &ksettings->base;
        bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
        int len, ret;
 
@@ -86,6 +89,12 @@ static int linkmodes_reply_size(const struct ethnl_req_info *req_base,
                len += ret;
        }
 
+       if (lsettings->master_slave_cfg != MASTER_SLAVE_CFG_UNSUPPORTED)
+               len += nla_total_size(sizeof(u8));
+
+       if (lsettings->master_slave_state != MASTER_SLAVE_STATE_UNSUPPORTED)
+               len += nla_total_size(sizeof(u8));
+
        return len;
 }
 
@@ -122,6 +131,16 @@ static int linkmodes_fill_reply(struct sk_buff *skb,
            nla_put_u8(skb, ETHTOOL_A_LINKMODES_DUPLEX, lsettings->duplex))
                return -EMSGSIZE;
 
+       if (lsettings->master_slave_cfg != MASTER_SLAVE_CFG_UNSUPPORTED &&
+           nla_put_u8(skb, ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG,
+                      lsettings->master_slave_cfg))
+               return -EMSGSIZE;
+
+       if (lsettings->master_slave_state != MASTER_SLAVE_STATE_UNSUPPORTED &&
+           nla_put_u8(skb, ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE,
+                      lsettings->master_slave_state))
+               return -EMSGSIZE;
+
        return 0;
 }
 
@@ -249,6 +268,8 @@ linkmodes_set_policy[ETHTOOL_A_LINKMODES_MAX + 1] = {
        [ETHTOOL_A_LINKMODES_PEER]              = { .type = NLA_REJECT },
        [ETHTOOL_A_LINKMODES_SPEED]             = { .type = NLA_U32 },
        [ETHTOOL_A_LINKMODES_DUPLEX]            = { .type = NLA_U8 },
+       [ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]  = { .type = NLA_U8 },
+       [ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE]        = { .type = NLA_REJECT },
 };
 
 /* Set advertised link modes to all supported modes matching requested speed
@@ -287,14 +308,45 @@ static bool ethnl_auto_linkmodes(struct ethtool_link_ksettings *ksettings,
                             __ETHTOOL_LINK_MODE_MASK_NBITS);
 }
 
+static bool ethnl_validate_master_slave_cfg(u8 cfg)
+{
+       switch (cfg) {
+       case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+       case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+       case MASTER_SLAVE_CFG_MASTER_FORCE:
+       case MASTER_SLAVE_CFG_SLAVE_FORCE:
+               return true;
+       }
+
+       return false;
+}
+
 static int ethnl_update_linkmodes(struct genl_info *info, struct nlattr **tb,
                                  struct ethtool_link_ksettings *ksettings,
                                  bool *mod)
 {
        struct ethtool_link_settings *lsettings = &ksettings->base;
        bool req_speed, req_duplex;
+       const struct nlattr *master_slave_cfg;
        int ret;
 
+       master_slave_cfg = tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG];
+       if (master_slave_cfg) {
+               u8 cfg = nla_get_u8(master_slave_cfg);
+
+               if (lsettings->master_slave_cfg == MASTER_SLAVE_CFG_UNSUPPORTED) {
+                       NL_SET_ERR_MSG_ATTR(info->extack, master_slave_cfg,
+                                           "master/slave configuration not supported by device");
+                       return -EOPNOTSUPP;
+               }
+
+               if (!ethnl_validate_master_slave_cfg(cfg)) {
+                       NL_SET_ERR_MSG_ATTR(info->extack, master_slave_cfg,
+                                           "master/slave value is invalid");
+                       return -EOPNOTSUPP;
+               }
+       }
+
        *mod = false;
        req_speed = tb[ETHTOOL_A_LINKMODES_SPEED];
        req_duplex = tb[ETHTOOL_A_LINKMODES_DUPLEX];
@@ -311,6 +363,7 @@ static int ethnl_update_linkmodes(struct genl_info *info, struct nlattr **tb,
                         mod);
        ethnl_update_u8(&lsettings->duplex, tb[ETHTOOL_A_LINKMODES_DUPLEX],
                        mod);
+       ethnl_update_u8(&lsettings->master_slave_cfg, master_slave_cfg, mod);
 
        if (!tb[ETHTOOL_A_LINKMODES_OURS] && lsettings->autoneg &&
            (req_speed || req_duplex) &&
index fc70273..cd99f54 100644 (file)
@@ -125,13 +125,11 @@ int hsr_get_max_mtu(struct hsr_priv *hsr)
 static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu)
 {
        struct hsr_priv *hsr;
-       struct hsr_port *master;
 
        hsr = netdev_priv(dev);
-       master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
 
        if (new_mtu > hsr_get_max_mtu(hsr)) {
-               netdev_info(master->dev, "A HSR master's MTU cannot be greater than the smallest MTU of its slaves minus the HSR Tag length (%d octets).\n",
+               netdev_info(dev, "A HSR master's MTU cannot be greater than the smallest MTU of its slaves minus the HSR Tag length (%d octets).\n",
                            HSR_HLEN);
                return -EINVAL;
        }
index 26d6c39..e2564de 100644 (file)
 #include "hsr_framereg.h"
 #include "hsr_slave.h"
 
+static bool hsr_slave_empty(struct hsr_priv *hsr)
+{
+       struct hsr_port *port;
+
+       hsr_for_each_port(hsr, port)
+               if (port->type != HSR_PT_MASTER)
+                       return false;
+       return true;
+}
+
 static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
                             void *ptr)
 {
-       struct net_device *dev;
        struct hsr_port *port, *master;
+       struct net_device *dev;
        struct hsr_priv *hsr;
+       LIST_HEAD(list_kill);
        int mtu_max;
        int res;
 
@@ -85,8 +96,15 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
                master->dev->mtu = mtu_max;
                break;
        case NETDEV_UNREGISTER:
-               if (!is_hsr_master(dev))
+               if (!is_hsr_master(dev)) {
+                       master = hsr_port_get_hsr(port->hsr, HSR_PT_MASTER);
                        hsr_del_port(port);
+                       if (hsr_slave_empty(master->hsr)) {
+                               unregister_netdevice_queue(master->dev,
+                                                          &list_kill);
+                               unregister_netdevice_many(&list_kill);
+                       }
+               }
                break;
        case NETDEV_PRE_TYPE_CHANGE:
                /* HSR works only on Ethernet devices. Refuse slave to change
index 7321cf8..f741934 100644 (file)
@@ -62,15 +62,6 @@ struct hsr_tag {
  * with the path field in-between, which seems strange. I'm guessing the MAC
  * address definition is in error.
  */
-static inline u16 get_hsr_tag_path(struct hsr_tag *ht)
-{
-       return ntohs(ht->path_and_LSDU_size) >> 12;
-}
-
-static inline u16 get_hsr_tag_LSDU_size(struct hsr_tag *ht)
-{
-       return ntohs(ht->path_and_LSDU_size) & 0x0FFF;
-}
 
 static inline void set_hsr_tag_path(struct hsr_tag *ht, u16 path)
 {
@@ -103,16 +94,6 @@ struct hsr_sup_payload {
        unsigned char   macaddress_A[ETH_ALEN];
 } __packed;
 
-static inline u16 get_hsr_stag_path(struct hsr_sup_tag *hst)
-{
-       return get_hsr_tag_path((struct hsr_tag *)hst);
-}
-
-static inline u16 get_hsr_stag_HSR_ver(struct hsr_sup_tag *hst)
-{
-       return get_hsr_tag_LSDU_size((struct hsr_tag *)hst);
-}
-
 static inline void set_hsr_stag_path(struct hsr_sup_tag *hst, u16 path)
 {
        set_hsr_tag_path((struct hsr_tag *)hst, path);
index 5465a39..1decb25 100644 (file)
@@ -69,10 +69,16 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev,
        else
                multicast_spec = nla_get_u8(data[IFLA_HSR_MULTICAST_SPEC]);
 
-       if (!data[IFLA_HSR_VERSION])
+       if (!data[IFLA_HSR_VERSION]) {
                hsr_version = 0;
-       else
+       } else {
                hsr_version = nla_get_u8(data[IFLA_HSR_VERSION]);
+               if (hsr_version > 1) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "Only versions 0..1 are supported");
+                       return -EINVAL;
+               }
+       }
 
        return hsr_dev_finalize(dev, link, multicast_spec, hsr_version, extack);
 }
index f4b9f7a..25b6ffb 100644 (file)
@@ -18,7 +18,7 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
 {
        struct sk_buff *skb = *pskb;
        struct hsr_port *port;
-       u16 protocol;
+       __be16 protocol;
 
        if (!skb_mac_header_was_set(skb)) {
                WARN_ONCE(1, "%s: skb invalid", __func__);
index c0b107c..3297e7f 100644 (file)
@@ -58,6 +58,13 @@ static const struct header_ops lowpan_header_ops = {
        .create = lowpan_header_create,
 };
 
+static int lowpan_dev_init(struct net_device *ldev)
+{
+       netdev_lockdep_set_classes(ldev);
+
+       return 0;
+}
+
 static int lowpan_open(struct net_device *dev)
 {
        if (!open_count)
@@ -89,6 +96,7 @@ static int lowpan_get_iflink(const struct net_device *dev)
 }
 
 static const struct net_device_ops lowpan_netdev_ops = {
+       .ndo_init               = lowpan_dev_init,
        .ndo_start_xmit         = lowpan_xmit,
        .ndo_open               = lowpan_open,
        .ndo_stop               = lowpan_stop,
index 25a8888..5da4733 100644 (file)
@@ -49,7 +49,7 @@ config IP_ADVANCED_ROUTER
 
          Note that some distributions enable it in startup scripts.
          For details about rp_filter strict and loose mode read
-         <file:Documentation/networking/ip-sysctl.txt>.
+         <file:Documentation/networking/ip-sysctl.rst>.
 
          If unsure, say N here.
 
index cf58e29..6177c4b 100644 (file)
@@ -1835,6 +1835,7 @@ static __net_init int inet_init_net(struct net *net)
        net->ipv4.sysctl_ip_early_demux = 1;
        net->ipv4.sysctl_udp_early_demux = 1;
        net->ipv4.sysctl_tcp_early_demux = 1;
+       net->ipv4.sysctl_nexthop_compat_mode = 1;
 #ifdef CONFIG_SYSCTL
        net->ipv4.sysctl_ip_prot_sock = PROT_SOCK;
 #endif
@@ -1914,7 +1915,7 @@ static int __init inet_init(void)
 {
        struct inet_protosw *q;
        struct list_head *r;
-       int rc = -EINVAL;
+       int rc;
 
        sock_skb_cb_check_size(sizeof(struct inet_skb_parm));
 
index 30fa42f..fc94f82 100644 (file)
@@ -614,12 +614,15 @@ struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
        return NULL;
 }
 
-static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa)
+static int ip_mc_autojoin_config(struct net *net, bool join,
+                                const struct in_ifaddr *ifa)
 {
+#if defined(CONFIG_IP_MULTICAST)
        struct ip_mreqn mreq = {
                .imr_multiaddr.s_addr = ifa->ifa_address,
                .imr_ifindex = ifa->ifa_dev->dev->ifindex,
        };
+       struct sock *sk = net->ipv4.mc_autojoin_sk;
        int ret;
 
        ASSERT_RTNL();
@@ -632,6 +635,9 @@ static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa)
        release_sock(sk);
 
        return ret;
+#else
+       return -EOPNOTSUPP;
+#endif
 }
 
 static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -675,7 +681,7 @@ static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
                        continue;
 
                if (ipv4_is_multicast(ifa->ifa_address))
-                       ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa);
+                       ip_mc_autojoin_config(net, false, ifa);
                __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
                return 0;
        }
@@ -940,8 +946,7 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
                 */
                set_ifa_lifetime(ifa, valid_lft, prefered_lft);
                if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
-                       int ret = ip_mc_config(net->ipv4.mc_autojoin_sk,
-                                              true, ifa);
+                       int ret = ip_mc_autojoin_config(net, true, ifa);
 
                        if (ret < 0) {
                                inet_free_ifa(ifa);
@@ -2361,8 +2366,7 @@ static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
 }
 
 static int devinet_conf_proc(struct ctl_table *ctl, int write,
-                            void __user *buffer,
-                            size_t *lenp, loff_t *ppos)
+                            void *buffer, size_t *lenp, loff_t *ppos)
 {
        int old_value = *(int *)ctl->data;
        int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
@@ -2414,8 +2418,7 @@ static int devinet_conf_proc(struct ctl_table *ctl, int write,
 }
 
 static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
-                                 void __user *buffer,
-                                 size_t *lenp, loff_t *ppos)
+                                 void *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
        int val = *valp;
@@ -2458,8 +2461,7 @@ static int devinet_sysctl_forward(struct ctl_table *ctl, int write,
 }
 
 static int ipv4_doint_and_flush(struct ctl_table *ctl, int write,
-                               void __user *buffer,
-                               size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
        int val = *valp;
index 6ed8c93..e53871e 100644 (file)
@@ -1780,6 +1780,8 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event,
                        goto nla_put_failure;
                if (nexthop_is_blackhole(fi->nh))
                        rtm->rtm_type = RTN_BLACKHOLE;
+               if (!fi->fib_net->ipv4.sysctl_nexthop_compat_mode)
+                       goto offload;
        }
 
        if (nhs == 1) {
@@ -1805,6 +1807,7 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event,
                        goto nla_put_failure;
        }
 
+offload:
        if (fri->offload)
                rtm->rtm_flags |= RTM_F_OFFLOAD;
        if (fri->trap)
@@ -2014,7 +2017,7 @@ static void fib_select_default(const struct flowi4 *flp, struct fib_result *res)
 
        hlist_for_each_entry_rcu(fa, fa_head, fa_list) {
                struct fib_info *next_fi = fa->fa_info;
-               struct fib_nh *nh;
+               struct fib_nh_common *nhc;
 
                if (fa->fa_slen != slen)
                        continue;
@@ -2037,8 +2040,8 @@ static void fib_select_default(const struct flowi4 *flp, struct fib_result *res)
                    fa->fa_type != RTN_UNICAST)
                        continue;
 
-               nh = fib_info_nh(next_fi, 0);
-               if (!nh->fib_nh_gw4 || nh->fib_nh_scope != RT_SCOPE_LINK)
+               nhc = fib_info_nhc(next_fi, 0);
+               if (!nhc->nhc_gw_family || nhc->nhc_scope != RT_SCOPE_LINK)
                        continue;
 
                fib_alias_accessed(fa);
index fc61f51..956a806 100644 (file)
@@ -853,7 +853,7 @@ static bool icmp_unreach(struct sk_buff *skb)
                case ICMP_FRAG_NEEDED:
                        /* for documentation of the ip_no_pmtu_disc
                         * values please see
-                        * Documentation/networking/ip-sysctl.txt
+                        * Documentation/networking/ip-sysctl.rst
                         */
                        switch (net->ipv4.sysctl_ip_no_pmtu_disc) {
                        default:
index 5d50aad..125f4f8 100644 (file)
@@ -43,6 +43,9 @@ struct inet_diag_entry {
        u16 userlocks;
        u32 ifindex;
        u32 mark;
+#ifdef CONFIG_SOCK_CGROUP_DATA
+       u64 cgroup_id;
+#endif
 };
 
 static DEFINE_MUTEX(inet_diag_table_mutex);
@@ -162,6 +165,13 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
                        goto errout;
        }
 
+#ifdef CONFIG_SOCK_CGROUP_DATA
+       if (nla_put_u64_64bit(skb, INET_DIAG_CGROUP_ID,
+                             cgroup_id(sock_cgroup_ptr(&sk->sk_cgrp_data)),
+                             INET_DIAG_PAD))
+               goto errout;
+#endif
+
        r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk));
        r->idiag_inode = sock_i_ino(sk);
 
@@ -675,6 +685,16 @@ static int inet_diag_bc_run(const struct nlattr *_bc,
                                yes = 0;
                        break;
                }
+#ifdef CONFIG_SOCK_CGROUP_DATA
+               case INET_DIAG_BC_CGROUP_COND: {
+                       u64 cgroup_id;
+
+                       cgroup_id = get_unaligned((const u64 *)(op + 1));
+                       if (cgroup_id != entry->cgroup_id)
+                               yes = 0;
+                       break;
+               }
+#endif
                }
 
                if (yes) {
@@ -725,6 +745,10 @@ int inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk)
                entry.mark = inet_rsk(inet_reqsk(sk))->ir_mark;
        else
                entry.mark = 0;
+#ifdef CONFIG_SOCK_CGROUP_DATA
+       entry.cgroup_id = sk_fullsock(sk) ?
+               cgroup_id(sock_cgroup_ptr(&sk->sk_cgrp_data)) : 0;
+#endif
 
        return inet_diag_bc_run(bc, &entry);
 }
@@ -814,6 +838,15 @@ static bool valid_markcond(const struct inet_diag_bc_op *op, int len,
        return len >= *min_len;
 }
 
+#ifdef CONFIG_SOCK_CGROUP_DATA
+static bool valid_cgroupcond(const struct inet_diag_bc_op *op, int len,
+                            int *min_len)
+{
+       *min_len += sizeof(u64);
+       return len >= *min_len;
+}
+#endif
+
 static int inet_diag_bc_audit(const struct nlattr *attr,
                              const struct sk_buff *skb)
 {
@@ -856,6 +889,12 @@ static int inet_diag_bc_audit(const struct nlattr *attr,
                        if (!valid_markcond(bc, len, &min_len))
                                return -EINVAL;
                        break;
+#ifdef CONFIG_SOCK_CGROUP_DATA
+               case INET_DIAG_BC_CGROUP_COND:
+                       if (!valid_cgroupcond(bc, len, &min_len))
+                               return -EINVAL;
+                       break;
+#endif
                case INET_DIAG_BC_AUTO:
                case INET_DIAG_BC_JMP:
                case INET_DIAG_BC_NOP:
index 029b24e..e29cd48 100644 (file)
@@ -248,6 +248,15 @@ static void gre_err(struct sk_buff *skb, u32 info)
        ipgre_err(skb, info, &tpi);
 }
 
+static bool is_erspan_type1(int gre_hdr_len)
+{
+       /* Both ERSPAN type I (version 0) and type II (version 1) use
+        * protocol 0x88BE, but the type I has only 4-byte GRE header,
+        * while type II has 8-byte.
+        */
+       return gre_hdr_len == 4;
+}
+
 static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
                      int gre_hdr_len)
 {
@@ -262,17 +271,26 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
        int len;
 
        itn = net_generic(net, erspan_net_id);
-
        iph = ip_hdr(skb);
-       ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len);
-       ver = ershdr->ver;
-
-       tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex,
-                                 tpi->flags | TUNNEL_KEY,
-                                 iph->saddr, iph->daddr, tpi->key);
+       if (is_erspan_type1(gre_hdr_len)) {
+               ver = 0;
+               tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex,
+                                         tpi->flags | TUNNEL_NO_KEY,
+                                         iph->saddr, iph->daddr, 0);
+       } else {
+               ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len);
+               ver = ershdr->ver;
+               tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex,
+                                         tpi->flags | TUNNEL_KEY,
+                                         iph->saddr, iph->daddr, tpi->key);
+       }
 
        if (tunnel) {
-               len = gre_hdr_len + erspan_hdr_len(ver);
+               if (is_erspan_type1(gre_hdr_len))
+                       len = gre_hdr_len;
+               else
+                       len = gre_hdr_len + erspan_hdr_len(ver);
+
                if (unlikely(!pskb_may_pull(skb, len)))
                        return PACKET_REJECT;
 
@@ -665,7 +683,10 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
        }
 
        /* Push ERSPAN header */
-       if (tunnel->erspan_ver == 1) {
+       if (tunnel->erspan_ver == 0) {
+               proto = htons(ETH_P_ERSPAN);
+               tunnel->parms.o_flags &= ~TUNNEL_SEQ;
+       } else if (tunnel->erspan_ver == 1) {
                erspan_build_header(skb, ntohl(tunnel->parms.o_key),
                                    tunnel->index,
                                    truncate, true);
@@ -1066,7 +1087,10 @@ static int erspan_validate(struct nlattr *tb[], struct nlattr *data[],
        if (ret)
                return ret;
 
-       /* ERSPAN should only have GRE sequence and key flag */
+       if (nla_get_u8(data[IFLA_GRE_ERSPAN_VER]) == 0)
+               return 0;
+
+       /* ERSPAN type II/III should only have GRE sequence and key flag */
        if (data[IFLA_GRE_OFLAGS])
                flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]);
        if (data[IFLA_GRE_IFLAGS])
@@ -1174,7 +1198,7 @@ static int erspan_netlink_parms(struct net_device *dev,
        if (data[IFLA_GRE_ERSPAN_VER]) {
                t->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);
 
-               if (t->erspan_ver != 1 && t->erspan_ver != 2)
+               if (t->erspan_ver > 2)
                        return -EINVAL;
        }
 
@@ -1259,7 +1283,11 @@ static int erspan_tunnel_init(struct net_device *dev)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
 
-       tunnel->tun_hlen = 8;
+       if (tunnel->erspan_ver == 0)
+               tunnel->tun_hlen = 4; /* 4-byte GRE hdr. */
+       else
+               tunnel->tun_hlen = 8; /* 8-byte GRE hdr. */
+
        tunnel->parms.iph.protocol = IPPROTO_GRE;
        tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
                       erspan_hdr_len(tunnel->erspan_ver);
@@ -1456,8 +1484,8 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
        struct ip_tunnel_parm *p = &t->parms;
        __be16 o_flags = p->o_flags;
 
-       if (t->erspan_ver == 1 || t->erspan_ver == 2) {
-               if (!t->collect_md)
+       if (t->erspan_ver <= 2) {
+               if (t->erspan_ver != 0 && !t->collect_md)
                        o_flags |= TUNNEL_KEY;
 
                if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, t->erspan_ver))
@@ -1466,7 +1494,7 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
                if (t->erspan_ver == 1) {
                        if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, t->index))
                                goto nla_put_failure;
-               } else {
+               } else if (t->erspan_ver == 2) {
                        if (nla_put_u8(skb, IFLA_GRE_ERSPAN_DIR, t->dir))
                                goto nla_put_failure;
                        if (nla_put_u16(skb, IFLA_GRE_ERSPAN_HWID, t->hwid))
index fdfca53..3957364 100644 (file)
@@ -784,7 +784,8 @@ static void __remove_nexthop_fib(struct net *net, struct nexthop *nh)
        list_for_each_entry_safe(f6i, tmp, &nh->f6i_list, nh_list) {
                /* __ip6_del_rt does a release, so do a hold here */
                fib6_info_hold(f6i);
-               ipv6_stub->ip6_del_rt(net, f6i);
+               ipv6_stub->ip6_del_rt(net, f6i,
+                                     !net->ipv4.sysctl_nexthop_compat_mode);
        }
 }
 
@@ -1041,7 +1042,7 @@ out:
        if (!rc) {
                nh_base_seq_inc(net);
                nexthop_notify(RTM_NEWNEXTHOP, new_nh, &cfg->nlinfo);
-               if (replace_notify)
+               if (replace_notify && net->ipv4.sysctl_nexthop_compat_mode)
                        nexthop_replace_notify(net, new_nh, &cfg->nlinfo);
        }
 
index 788c69d..041f4dc 100644 (file)
@@ -3336,8 +3336,7 @@ static int ip_rt_gc_elasticity __read_mostly      = 8;
 static int ip_min_valid_pmtu __read_mostly     = IPV4_MIN_MTU;
 
 static int ipv4_sysctl_rtcache_flush(struct ctl_table *__ctl, int write,
-                                       void __user *buffer,
-                                       size_t *lenp, loff_t *ppos)
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net = (struct net *)__ctl->extra1;
 
index 81b267e..5653e3b 100644 (file)
@@ -71,8 +71,7 @@ static void set_local_port_range(struct net *net, int range[2])
 
 /* Validate changes from /proc interface. */
 static int ipv4_local_port_range(struct ctl_table *table, int write,
-                                void __user *buffer,
-                                size_t *lenp, loff_t *ppos)
+                                void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net =
                container_of(table->data, struct net, ipv4.ip_local_ports.range);
@@ -107,7 +106,7 @@ static int ipv4_local_port_range(struct ctl_table *table, int write,
 
 /* Validate changes from /proc interface. */
 static int ipv4_privileged_ports(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net = container_of(table->data, struct net,
            ipv4.sysctl_ip_prot_sock);
@@ -168,8 +167,7 @@ static void set_ping_group_range(struct ctl_table *table, kgid_t low, kgid_t hig
 
 /* Validate changes from /proc interface. */
 static int ipv4_ping_group_range(struct ctl_table *table, int write,
-                                void __user *buffer,
-                                size_t *lenp, loff_t *ppos)
+                                void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct user_namespace *user_ns = current_user_ns();
        int ret;
@@ -204,8 +202,7 @@ static int ipv4_ping_group_range(struct ctl_table *table, int write,
 }
 
 static int ipv4_fwd_update_priority(struct ctl_table *table, int write,
-                                   void __user *buffer,
-                                   size_t *lenp, loff_t *ppos)
+                                   void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net;
        int ret;
@@ -221,7 +218,7 @@ static int ipv4_fwd_update_priority(struct ctl_table *table, int write,
 }
 
 static int proc_tcp_congestion_control(struct ctl_table *ctl, int write,
-                                      void __user *buffer, size_t *lenp, loff_t *ppos)
+                                      void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net = container_of(ctl->data, struct net,
                                       ipv4.tcp_congestion_control);
@@ -241,9 +238,8 @@ static int proc_tcp_congestion_control(struct ctl_table *ctl, int write,
 }
 
 static int proc_tcp_available_congestion_control(struct ctl_table *ctl,
-                                                int write,
-                                                void __user *buffer, size_t *lenp,
-                                                loff_t *ppos)
+                                                int write, void *buffer,
+                                                size_t *lenp, loff_t *ppos)
 {
        struct ctl_table tbl = { .maxlen = TCP_CA_BUF_MAX, };
        int ret;
@@ -258,9 +254,8 @@ static int proc_tcp_available_congestion_control(struct ctl_table *ctl,
 }
 
 static int proc_allowed_congestion_control(struct ctl_table *ctl,
-                                          int write,
-                                          void __user *buffer, size_t *lenp,
-                                          loff_t *ppos)
+                                          int write, void *buffer,
+                                          size_t *lenp, loff_t *ppos)
 {
        struct ctl_table tbl = { .maxlen = TCP_CA_BUF_MAX };
        int ret;
@@ -296,8 +291,7 @@ static int sscanf_key(char *buf, __le32 *key)
 }
 
 static int proc_tcp_fastopen_key(struct ctl_table *table, int write,
-                                void __user *buffer, size_t *lenp,
-                                loff_t *ppos)
+                                void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net = container_of(table->data, struct net,
            ipv4.sysctl_tcp_fastopen);
@@ -399,7 +393,7 @@ static void proc_configure_early_demux(int enabled, int protocol)
 }
 
 static int proc_tcp_early_demux(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret = 0;
 
@@ -415,7 +409,7 @@ static int proc_tcp_early_demux(struct ctl_table *table, int write,
 }
 
 static int proc_udp_early_demux(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret = 0;
 
@@ -431,8 +425,7 @@ static int proc_udp_early_demux(struct ctl_table *table, int write,
 }
 
 static int proc_tfo_blackhole_detect_timeout(struct ctl_table *table,
-                                            int write,
-                                            void __user *buffer,
+                                            int write, void *buffer,
                                             size_t *lenp, loff_t *ppos)
 {
        struct net *net = container_of(table->data, struct net,
@@ -447,8 +440,7 @@ static int proc_tfo_blackhole_detect_timeout(struct ctl_table *table,
 }
 
 static int proc_tcp_available_ulp(struct ctl_table *ctl,
-                                 int write,
-                                 void __user *buffer, size_t *lenp,
+                                 int write, void *buffer, size_t *lenp,
                                  loff_t *ppos)
 {
        struct ctl_table tbl = { .maxlen = TCP_ULP_BUF_MAX, };
@@ -466,7 +458,7 @@ static int proc_tcp_available_ulp(struct ctl_table *ctl,
 
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 static int proc_fib_multipath_hash_policy(struct ctl_table *table, int write,
-                                         void __user *buffer, size_t *lenp,
+                                         void *buffer, size_t *lenp,
                                          loff_t *ppos)
 {
        struct net *net = container_of(table->data, struct net,
@@ -710,6 +702,15 @@ static struct ctl_table ipv4_net_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_tcp_early_demux
        },
+       {
+               .procname       = "nexthop_compat_mode",
+               .data           = &init_net.ipv4.sysctl_nexthop_compat_mode,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = SYSCTL_ZERO,
+               .extra2         = SYSCTL_ONE,
+       },
        {
                .procname       = "ip_default_ttl",
                .data           = &init_net.ipv4.sysctl_ip_default_ttl,
@@ -1320,6 +1321,13 @@ static struct ctl_table ipv4_net_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_doulongvec_minmax,
        },
+       {
+               .procname       = "tcp_comp_sack_slack_ns",
+               .data           = &init_net.ipv4.sysctl_tcp_comp_sack_slack_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,
index 6d87de4..8c12501 100644 (file)
@@ -3035,8 +3035,8 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
        case TCP_LINGER2:
                if (val < 0)
                        tp->linger2 = -1;
-               else if (val > net->ipv4.sysctl_tcp_fin_timeout / HZ)
-                       tp->linger2 = 0;
+               else if (val > TCP_FIN_TIMEOUT_MAX / HZ)
+                       tp->linger2 = TCP_FIN_TIMEOUT_MAX;
                else
                        tp->linger2 = val * HZ;
                break;
index bf4ced9..66e55e5 100644 (file)
@@ -3014,7 +3014,7 @@ void tcp_rearm_rto(struct sock *sk)
                        rto = usecs_to_jiffies(max_t(int, delta_us, 1));
                }
                tcp_reset_xmit_timer(sk, ICSK_TIME_RETRANS, rto,
-                                    TCP_RTO_MAX, tcp_rtx_queue_head(sk));
+                                    TCP_RTO_MAX);
        }
 }
 
@@ -3291,7 +3291,7 @@ static void tcp_ack_probe(struct sock *sk)
                unsigned long when = tcp_probe0_when(sk, TCP_RTO_MAX);
 
                tcp_reset_xmit_timer(sk, ICSK_TIME_PROBE0,
-                                    when, TCP_RTO_MAX, NULL);
+                                    when, TCP_RTO_MAX);
        }
 }
 
@@ -3926,10 +3926,6 @@ void tcp_parse_options(const struct net *net,
                                 */
                                break;
 #endif
-                       case TCPOPT_MPTCP:
-                               mptcp_parse_option(skb, ptr, opsize, opt_rx);
-                               break;
-
                        case TCPOPT_FASTOPEN:
                                tcp_parse_fastopen_option(
                                        opsize - TCPOLEN_FASTOPEN_BASE,
@@ -4327,6 +4323,33 @@ static void tcp_sack_maybe_coalesce(struct tcp_sock *tp)
        }
 }
 
+static void tcp_sack_compress_send_ack(struct sock *sk)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       if (!tp->compressed_ack)
+               return;
+
+       if (hrtimer_try_to_cancel(&tp->compressed_ack_timer) == 1)
+               __sock_put(sk);
+
+       /* Since we have to send one ack finally,
+        * substract one from tp->compressed_ack to keep
+        * LINUX_MIB_TCPACKCOMPRESSED accurate.
+        */
+       NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPACKCOMPRESSED,
+                     tp->compressed_ack - 1);
+
+       tp->compressed_ack = 0;
+       tcp_send_ack(sk);
+}
+
+/* Reasonable amount of sack blocks included in TCP SACK option
+ * The max is 4, but this becomes 3 if TCP timestamps are there.
+ * Given that SACK packets might be lost, be conservative and use 2.
+ */
+#define TCP_SACK_BLOCKS_EXPECTED 2
+
 static void tcp_sack_new_ofo_skb(struct sock *sk, u32 seq, u32 end_seq)
 {
        struct tcp_sock *tp = tcp_sk(sk);
@@ -4339,6 +4362,8 @@ static void tcp_sack_new_ofo_skb(struct sock *sk, u32 seq, u32 end_seq)
 
        for (this_sack = 0; this_sack < cur_sacks; this_sack++, sp++) {
                if (tcp_sack_extend(sp, seq, end_seq)) {
+                       if (this_sack >= TCP_SACK_BLOCKS_EXPECTED)
+                               tcp_sack_compress_send_ack(sk);
                        /* Rotate this_sack to the first one. */
                        for (; this_sack > 0; this_sack--, sp--)
                                swap(*sp, *(sp - 1));
@@ -4348,6 +4373,9 @@ static void tcp_sack_new_ofo_skb(struct sock *sk, u32 seq, u32 end_seq)
                }
        }
 
+       if (this_sack >= TCP_SACK_BLOCKS_EXPECTED)
+               tcp_sack_compress_send_ack(sk);
+
        /* Could not find an adjacent existing SACK, build a new one,
         * put it at the front, and shift everyone else down.  We
         * always know there is at least one SACK present already here.
@@ -4355,8 +4383,6 @@ 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_FASTRETRANS_THRESH)
-                       tcp_send_ack(sk);
                this_sack--;
                tp->rx_opt.num_sacks--;
                sp--;
@@ -5275,15 +5301,13 @@ send_now:
 
        if (tp->compressed_ack_rcv_nxt != tp->rcv_nxt) {
                tp->compressed_ack_rcv_nxt = tp->rcv_nxt;
-               if (tp->compressed_ack > TCP_FASTRETRANS_THRESH)
-                       NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPACKCOMPRESSED,
-                                     tp->compressed_ack - TCP_FASTRETRANS_THRESH);
-               tp->compressed_ack = 0;
+               tp->dup_ack_counter = 0;
        }
-
-       if (++tp->compressed_ack <= TCP_FASTRETRANS_THRESH)
+       if (tp->dup_ack_counter < TCP_FASTRETRANS_THRESH) {
+               tp->dup_ack_counter++;
                goto send_now;
-
+       }
+       tp->compressed_ack++;
        if (hrtimer_is_queued(&tp->compressed_ack_timer))
                return;
 
@@ -5296,8 +5320,9 @@ send_now:
        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);
+       hrtimer_start_range_ns(&tp->compressed_ack_timer, ns_to_ktime(delay),
+                              sock_net(sk)->ipv4.sysctl_tcp_comp_sack_slack_ns,
+                              HRTIMER_MODE_REL_PINNED_SOFT);
 }
 
 static inline void tcp_ack_snd_check(struct sock *sk)
@@ -5990,9 +6015,6 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
                tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
                tcp_initialize_rcv_mss(sk);
 
-               if (sk_is_mptcp(sk))
-                       mptcp_rcv_synsent(sk);
-
                /* Remember, tcp_poll() does not lock socket!
                 * Change state from SYN-SENT only after copied_seq
                 * is initialized. */
index 83a5d24..6c05f1c 100644 (file)
@@ -2780,6 +2780,7 @@ static int __net_init tcp_sk_init(struct net *net)
                       sizeof(init_net.ipv4.sysctl_tcp_wmem));
        }
        net->ipv4.sysctl_tcp_comp_sack_delay_ns = NSEC_PER_MSEC;
+       net->ipv4.sysctl_tcp_comp_sack_slack_ns = 100 * NSEC_PER_USEC;
        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);
index 2f45cde..a50e199 100644 (file)
@@ -184,10 +184,10 @@ static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts,
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
-       if (unlikely(tp->compressed_ack > TCP_FASTRETRANS_THRESH)) {
+       if (unlikely(tp->compressed_ack)) {
                NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPACKCOMPRESSED,
-                             tp->compressed_ack - TCP_FASTRETRANS_THRESH);
-               tp->compressed_ack = TCP_FASTRETRANS_THRESH;
+                             tp->compressed_ack);
+               tp->compressed_ack = 0;
                if (hrtimer_try_to_cancel(&tp->compressed_ack_timer) == 1)
                        __sock_put(sk);
        }
@@ -2593,8 +2593,7 @@ bool tcp_schedule_loss_probe(struct sock *sk, bool advancing_rto)
        if (rto_delta_us > 0)
                timeout = min_t(u32, timeout, usecs_to_jiffies(rto_delta_us));
 
-       tcp_reset_xmit_timer(sk, ICSK_TIME_LOSS_PROBE, timeout,
-                            TCP_RTO_MAX, NULL);
+       tcp_reset_xmit_timer(sk, ICSK_TIME_LOSS_PROBE, timeout, TCP_RTO_MAX);
        return true;
 }
 
@@ -2772,8 +2771,12 @@ u32 __tcp_select_window(struct sock *sk)
        int mss = icsk->icsk_ack.rcv_mss;
        int free_space = tcp_space(sk);
        int allowed_space = tcp_full_space(sk);
-       int full_space = min_t(int, tp->window_clamp, allowed_space);
-       int window;
+       int full_space, window;
+
+       if (sk_is_mptcp(sk))
+               mptcp_space(sk, &free_space, &allowed_space);
+
+       full_space = min_t(int, tp->window_clamp, allowed_space);
 
        if (unlikely(mss > full_space)) {
                mss = full_space;
@@ -3109,6 +3112,7 @@ void tcp_xmit_retransmit_queue(struct sock *sk)
        const struct inet_connection_sock *icsk = inet_csk(sk);
        struct sk_buff *skb, *rtx_head, *hole = NULL;
        struct tcp_sock *tp = tcp_sk(sk);
+       bool rearm_timer = false;
        u32 max_segs;
        int mib_idx;
 
@@ -3131,7 +3135,7 @@ void tcp_xmit_retransmit_queue(struct sock *sk)
 
                segs = tp->snd_cwnd - tcp_packets_in_flight(tp);
                if (segs <= 0)
-                       return;
+                       break;
                sacked = TCP_SKB_CB(skb)->sacked;
                /* In case tcp_shift_skb_data() have aggregated large skbs,
                 * we need to make sure not sending too bigs TSO packets
@@ -3156,10 +3160,10 @@ void tcp_xmit_retransmit_queue(struct sock *sk)
                        continue;
 
                if (tcp_small_queue_check(sk, skb, 1))
-                       return;
+                       break;
 
                if (tcp_retransmit_skb(sk, skb, segs))
-                       return;
+                       break;
 
                NET_ADD_STATS(sock_net(sk), mib_idx, tcp_skb_pcount(skb));
 
@@ -3168,11 +3172,13 @@ void tcp_xmit_retransmit_queue(struct sock *sk)
 
                if (skb == rtx_head &&
                    icsk->icsk_pending != ICSK_TIME_REO_TIMEOUT)
-                       tcp_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
-                                            inet_csk(sk)->icsk_rto,
-                                            TCP_RTO_MAX,
-                                            skb);
+                       rearm_timer = true;
+
        }
+       if (rearm_timer)
+               tcp_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
+                                    inet_csk(sk)->icsk_rto,
+                                    TCP_RTO_MAX);
 }
 
 /* We allow to exceed memory limits for FIN packets to expedite
@@ -3903,7 +3909,7 @@ void tcp_send_probe0(struct sock *sk)
                 */
                timeout = TCP_RESOURCE_PROBE_INTERVAL;
        }
-       tcp_reset_xmit_timer(sk, ICSK_TIME_PROBE0, timeout, TCP_RTO_MAX, NULL);
+       tcp_reset_xmit_timer(sk, ICSK_TIME_PROBE0, timeout, TCP_RTO_MAX);
 }
 
 int tcp_rtx_synack(const struct sock *sk, struct request_sock *req)
index c3f26dc..ada046f 100644 (file)
@@ -753,8 +753,14 @@ static enum hrtimer_restart tcp_compressed_ack_kick(struct hrtimer *timer)
 
        bh_lock_sock(sk);
        if (!sock_owned_by_user(sk)) {
-               if (tp->compressed_ack > TCP_FASTRETRANS_THRESH)
+               if (tp->compressed_ack) {
+                       /* Since we have to send one ack finally,
+                        * substract one from tp->compressed_ack to keep
+                        * LINUX_MIB_TCPACKCOMPRESSED accurate.
+                        */
+                       tp->compressed_ack--;
                        tcp_send_ack(sk);
+               }
        } else {
                if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED,
                                      &sk->sk_tsq_flags))
index 89ba7c8..30ddb9d 100644 (file)
@@ -58,9 +58,7 @@ int xfrm4_output_finish(struct sock *sk, struct sk_buff *skb)
 {
        memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
 
-#ifdef CONFIG_NETFILTER
        IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
-#endif
 
        return xfrm_output(sk, skb);
 }
index 2ccaee9..5a6111d 100644 (file)
@@ -13,7 +13,7 @@ menuconfig IPV6
          For general information about IPv6, see
          <https://en.wikipedia.org/wiki/IPv6>.
          For specific information about IPv6 under Linux, see
-         Documentation/networking/ipv6.txt and read the HOWTO at
+         Documentation/networking/ipv6.rst and read the HOWTO at
          <http://www.tldp.org/HOWTO/Linux+IPv6-HOWTO/>
 
          To compile this protocol support as a module, choose M here: the
index 24e319d..fd885f0 100644 (file)
@@ -135,8 +135,7 @@ static inline void addrconf_sysctl_unregister(struct inet6_dev *idev)
 }
 #endif
 
-static void ipv6_regen_rndid(struct inet6_dev *idev);
-static void ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr);
+static void ipv6_gen_rnd_iid(struct in6_addr *addr);
 
 static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
 static int ipv6_count_addresses(const struct inet6_dev *idev);
@@ -432,8 +431,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
            dev->type == ARPHRD_SIT ||
            dev->type == ARPHRD_NONE) {
                ndev->cnf.use_tempaddr = -1;
-       } else
-               ipv6_regen_rndid(ndev);
+       }
 
        ndev->token = in6addr_any;
 
@@ -1238,7 +1236,7 @@ cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires,
                                        ifp->idev->dev, 0, RTF_DEFAULT, true);
        if (f6i) {
                if (del_rt)
-                       ip6_del_rt(dev_net(ifp->idev->dev), f6i);
+                       ip6_del_rt(dev_net(ifp->idev->dev), f6i, false);
                else {
                        if (!(f6i->fib6_flags & RTF_EXPIRES))
                                fib6_set_expires(f6i, expires);
@@ -1306,29 +1304,21 @@ out:
        in6_ifa_put(ifp);
 }
 
-static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp,
-                               struct inet6_ifaddr *ift,
-                               bool block)
+static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
 {
        struct inet6_dev *idev = ifp->idev;
-       struct in6_addr addr, *tmpaddr;
        unsigned long tmp_tstamp, age;
        unsigned long regen_advance;
-       struct ifa6_config cfg;
-       int ret = 0;
        unsigned long now = jiffies;
-       long max_desync_factor;
        s32 cnf_temp_preferred_lft;
+       struct inet6_ifaddr *ift;
+       struct ifa6_config cfg;
+       long max_desync_factor;
+       struct in6_addr addr;
+       int ret = 0;
 
        write_lock_bh(&idev->lock);
-       if (ift) {
-               spin_lock_bh(&ift->lock);
-               memcpy(&addr.s6_addr[8], &ift->addr.s6_addr[8], 8);
-               spin_unlock_bh(&ift->lock);
-               tmpaddr = &addr;
-       } else {
-               tmpaddr = NULL;
-       }
+
 retry:
        in6_dev_hold(idev);
        if (idev->cnf.use_tempaddr <= 0) {
@@ -1351,8 +1341,8 @@ retry:
        }
        in6_ifa_hold(ifp);
        memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
-       ipv6_try_regen_rndid(idev, tmpaddr);
-       memcpy(&addr.s6_addr[8], idev->rndid, 8);
+       ipv6_gen_rnd_iid(&addr);
+
        age = (now - ifp->tstamp) / HZ;
 
        regen_advance = idev->cnf.regen_max_retry *
@@ -1417,7 +1407,6 @@ retry:
                in6_ifa_put(ifp);
                in6_dev_put(idev);
                pr_info("%s: retry temporary address regeneration\n", __func__);
-               tmpaddr = &addr;
                write_lock_bh(&idev->lock);
                goto retry;
        }
@@ -2032,7 +2021,7 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
                if (ifpub) {
                        in6_ifa_hold(ifpub);
                        spin_unlock_bh(&ifp->lock);
-                       ipv6_create_tempaddr(ifpub, ifp, true);
+                       ipv6_create_tempaddr(ifpub, true);
                        in6_ifa_put(ifpub);
                } else {
                        spin_unlock_bh(&ifp->lock);
@@ -2329,40 +2318,38 @@ static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)
        return err;
 }
 
-/* (re)generation of randomized interface identifier (RFC 3041 3.2, 3.5) */
-static void ipv6_regen_rndid(struct inet6_dev *idev)
+/* Generation of a randomized Interface Identifier
+ * draft-ietf-6man-rfc4941bis, Section 3.3.1
+ */
+
+static void ipv6_gen_rnd_iid(struct in6_addr *addr)
 {
 regen:
-       get_random_bytes(idev->rndid, sizeof(idev->rndid));
-       idev->rndid[0] &= ~0x02;
+       get_random_bytes(&addr->s6_addr[8], 8);
 
-       /*
-        * <draft-ietf-ipngwg-temp-addresses-v2-00.txt>:
-        * check if generated address is not inappropriate
+       /* <draft-ietf-6man-rfc4941bis-08.txt>, Section 3.3.1:
+        * check if generated address is not inappropriate:
         *
-        *  - Reserved subnet anycast (RFC 2526)
-        *      11111101 11....11 1xxxxxxx
-        *  - ISATAP (RFC4214) 6.1
-        *      00-00-5E-FE-xx-xx-xx-xx
-        *  - value 0
-        *  - XXX: already assigned to an address on the device
+        * - Reserved IPv6 Interface Identifers
+        * - XXX: already assigned to an address on the device
         */
-       if (idev->rndid[0] == 0xfd &&
-           (idev->rndid[1]&idev->rndid[2]&idev->rndid[3]&idev->rndid[4]&idev->rndid[5]&idev->rndid[6]) == 0xff &&
-           (idev->rndid[7]&0x80))
+
+       /* Subnet-router anycast: 0000:0000:0000:0000 */
+       if (!(addr->s6_addr32[2] | addr->s6_addr32[3]))
                goto regen;
-       if ((idev->rndid[0]|idev->rndid[1]) == 0) {
-               if (idev->rndid[2] == 0x5e && idev->rndid[3] == 0xfe)
-                       goto regen;
-               if ((idev->rndid[2]|idev->rndid[3]|idev->rndid[4]|idev->rndid[5]|idev->rndid[6]|idev->rndid[7]) == 0x00)
-                       goto regen;
-       }
-}
 
-static void  ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr)
-{
-       if (tmpaddr && memcmp(idev->rndid, &tmpaddr->s6_addr[8], 8) == 0)
-               ipv6_regen_rndid(idev);
+       /* IANA Ethernet block: 0200:5EFF:FE00:0000-0200:5EFF:FE00:5212
+        * Proxy Mobile IPv6:   0200:5EFF:FE00:5213
+        * IANA Ethernet block: 0200:5EFF:FE00:5214-0200:5EFF:FEFF:FFFF
+        */
+       if (ntohl(addr->s6_addr32[2]) == 0x02005eff &&
+           (ntohl(addr->s6_addr32[3]) & 0Xff000000) == 0xfe000000)
+               goto regen;
+
+       /* Reserved subnet anycast addresses */
+       if (ntohl(addr->s6_addr32[2]) == 0xfdffffff &&
+           ntohl(addr->s6_addr32[3]) >= 0Xffffff80)
+               goto regen;
 }
 
 /*
@@ -2544,7 +2531,7 @@ static void manage_tempaddrs(struct inet6_dev *idev,
                 * no temporary address currently exists.
                 */
                read_unlock_bh(&idev->lock);
-               ipv6_create_tempaddr(ifp, NULL, false);
+               ipv6_create_tempaddr(ifp, false);
        } else {
                read_unlock_bh(&idev->lock);
        }
@@ -2564,7 +2551,7 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
                                 __u32 valid_lft, u32 prefered_lft)
 {
        struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
-       int create = 0, update_lft = 0;
+       int create = 0;
 
        if (!ifp && valid_lft) {
                int max_addresses = in6_dev->cnf.max_addresses;
@@ -2608,32 +2595,19 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
                unsigned long now;
                u32 stored_lft;
 
-               /* update lifetime (RFC2462 5.5.3 e) */
+               /* Update lifetime (RFC4862 5.5.3 e)
+                * We deviate from RFC4862 by honoring all Valid Lifetimes to
+                * improve the reaction of SLAAC to renumbering events
+                * (draft-gont-6man-slaac-renum-06, Section 4.2)
+                */
                spin_lock_bh(&ifp->lock);
                now = jiffies;
                if (ifp->valid_lft > (now - ifp->tstamp) / HZ)
                        stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;
                else
                        stored_lft = 0;
-               if (!create && stored_lft) {
-                       const u32 minimum_lft = min_t(u32,
-                               stored_lft, MIN_VALID_LIFETIME);
-                       valid_lft = max(valid_lft, minimum_lft);
-
-                       /* RFC4862 Section 5.5.3e:
-                        * "Note that the preferred lifetime of the
-                        *  corresponding address is always reset to
-                        *  the Preferred Lifetime in the received
-                        *  Prefix Information option, regardless of
-                        *  whether the valid lifetime is also reset or
-                        *  ignored."
-                        *
-                        * So we should always update prefered_lft here.
-                        */
-                       update_lft = 1;
-               }
 
-               if (update_lft) {
+               if (!create && stored_lft) {
                        ifp->valid_lft = valid_lft;
                        ifp->prefered_lft = prefered_lft;
                        ifp->tstamp = now;
@@ -2731,7 +2705,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
                if (rt) {
                        /* Autoconf prefix route */
                        if (valid_lft == 0) {
-                               ip6_del_rt(net, rt);
+                               ip6_del_rt(net, rt, false);
                                rt = NULL;
                        } else if (addrconf_finite_timeout(rt_expires)) {
                                /* not infinity */
@@ -3826,7 +3800,7 @@ restart:
                spin_unlock_bh(&ifa->lock);
 
                if (rt)
-                       ip6_del_rt(net, rt);
+                       ip6_del_rt(net, rt, false);
 
                if (state != INET6_IFADDR_STATE_DEAD) {
                        __ipv6_ifa_notify(RTM_DELADDR, ifa);
@@ -4544,7 +4518,7 @@ restart:
                                                ifpub->regen_count = 0;
                                                spin_unlock(&ifpub->lock);
                                                rcu_read_unlock_bh();
-                                               ipv6_create_tempaddr(ifpub, ifp, true);
+                                               ipv6_create_tempaddr(ifpub, true);
                                                in6_ifa_put(ifpub);
                                                in6_ifa_put(ifp);
                                                rcu_read_lock_bh();
@@ -4665,7 +4639,7 @@ static int modify_prefix_route(struct inet6_ifaddr *ifp,
        prio = ifp->rt_priority ? : IP6_RT_PRIO_ADDRCONF;
        if (f6i->fib6_metric != prio) {
                /* delete old one */
-               ip6_del_rt(dev_net(ifp->idev->dev), f6i);
+               ip6_del_rt(dev_net(ifp->idev->dev), f6i, false);
 
                /* add new one */
                addrconf_prefix_route(modify_peer ? &ifp->peer_addr : &ifp->addr,
@@ -6086,10 +6060,10 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
                                                       ifp->idev->dev, 0, 0,
                                                       false);
                        if (rt)
-                               ip6_del_rt(net, rt);
+                               ip6_del_rt(net, rt, false);
                }
                if (ifp->rt) {
-                       ip6_del_rt(net, ifp->rt);
+                       ip6_del_rt(net, ifp->rt, false);
                        ifp->rt = NULL;
                }
                rt_genid_bump_ipv6(net);
@@ -6108,9 +6082,8 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 
 #ifdef CONFIG_SYSCTL
 
-static
-int addrconf_sysctl_forward(struct ctl_table *ctl, int write,
-                          void __user *buffer, size_t *lenp, loff_t *ppos)
+static int addrconf_sysctl_forward(struct ctl_table *ctl, int write,
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
        int val = *valp;
@@ -6134,9 +6107,8 @@ int addrconf_sysctl_forward(struct ctl_table *ctl, int write,
        return ret;
 }
 
-static
-int addrconf_sysctl_mtu(struct ctl_table *ctl, int write,
-                       void __user *buffer, size_t *lenp, loff_t *ppos)
+static int addrconf_sysctl_mtu(struct ctl_table *ctl, int write,
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct inet6_dev *idev = ctl->extra1;
        int min_mtu = IPV6_MIN_MTU;
@@ -6206,9 +6178,8 @@ static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int newf)
        return 0;
 }
 
-static
-int addrconf_sysctl_disable(struct ctl_table *ctl, int write,
-                           void __user *buffer, size_t *lenp, loff_t *ppos)
+static int addrconf_sysctl_disable(struct ctl_table *ctl, int write,
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
        int val = *valp;
@@ -6232,9 +6203,8 @@ int addrconf_sysctl_disable(struct ctl_table *ctl, int write,
        return ret;
 }
 
-static
-int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write,
-                             void __user *buffer, size_t *lenp, loff_t *ppos)
+static int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write,
+               void *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
        int ret;
@@ -6275,7 +6245,7 @@ int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write,
 }
 
 static int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write,
-                                        void __user *buffer, size_t *lenp,
+                                        void *buffer, size_t *lenp,
                                         loff_t *ppos)
 {
        int ret = 0;
@@ -6337,7 +6307,7 @@ out:
 }
 
 static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
-                                        void __user *buffer, size_t *lenp,
+                                        void *buffer, size_t *lenp,
                                         loff_t *ppos)
 {
        int err;
@@ -6404,8 +6374,7 @@ out:
 
 static
 int addrconf_sysctl_ignore_routes_with_linkdown(struct ctl_table *ctl,
-                                               int write,
-                                               void __user *buffer,
+                                               int write, void *buffer,
                                                size_t *lenp,
                                                loff_t *ppos)
 {
@@ -6505,10 +6474,8 @@ int addrconf_disable_policy(struct ctl_table *ctl, int *valp, int val)
        return 0;
 }
 
-static
-int addrconf_sysctl_disable_policy(struct ctl_table *ctl, int write,
-                                  void __user *buffer, size_t *lenp,
-                                  loff_t *ppos)
+static int addrconf_sysctl_disable_policy(struct ctl_table *ctl, int write,
+                                  void *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
        int val = *valp;
index ea00ce3..9ebf3fe 100644 (file)
@@ -185,7 +185,8 @@ static int eafnosupport_fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
        return -EAFNOSUPPORT;
 }
 
-static int eafnosupport_ip6_del_rt(struct net *net, struct fib6_info *rt)
+static int eafnosupport_ip6_del_rt(struct net *net, struct fib6_info *rt,
+                                  bool skip_notify)
 {
        return -EAFNOSUPPORT;
 }
index fed91ab..8932612 100644 (file)
@@ -364,7 +364,7 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr)
        ipv6_del_acaddr_hash(aca);
        addrconf_leave_solict(idev, &aca->aca_addr);
 
-       ip6_del_rt(dev_net(idev->dev), aca->aca_rt);
+       ip6_del_rt(dev_net(idev->dev), aca->aca_rt, false);
 
        aca_put(aca);
        return 0;
@@ -393,7 +393,7 @@ void ipv6_ac_destroy_dev(struct inet6_dev *idev)
 
                addrconf_leave_solict(idev, &aca->aca_addr);
 
-               ip6_del_rt(dev_net(idev->dev), aca->aca_rt);
+               ip6_del_rt(dev_net(idev->dev), aca->aca_rt, false);
 
                aca_put(aca);
 
index 2688f3e..fc50003 100644 (file)
@@ -229,6 +229,25 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type,
        return res;
 }
 
+static bool icmpv6_rt_has_prefsrc(struct sock *sk, u8 type,
+                                 struct flowi6 *fl6)
+{
+       struct net *net = sock_net(sk);
+       struct dst_entry *dst;
+       bool res = false;
+
+       dst = ip6_route_output(net, sk, fl6);
+       if (!dst->error) {
+               struct rt6_info *rt = (struct rt6_info *)dst;
+               struct in6_addr prefsrc;
+
+               rt6_get_prefsrc(rt, &prefsrc);
+               res = !ipv6_addr_any(&prefsrc);
+       }
+       dst_release(dst);
+       return res;
+}
+
 /*
  *     an inline helper for the "simple" if statement below
  *     checks if parameter problem report is caused by an
@@ -527,7 +546,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
                saddr = force_saddr;
        if (saddr) {
                fl6.saddr = *saddr;
-       } else {
+       } else if (!icmpv6_rt_has_prefsrc(sk, type, &fl6)) {
                /* select a more meaningful saddr from input if */
                struct net_device *in_netdev;
 
index bb6fc0d..ad5f6f6 100644 (file)
@@ -68,11 +68,6 @@ static inline struct ila_addr *ila_a2i(struct in6_addr *addr)
        return (struct ila_addr *)addr;
 }
 
-static inline bool ila_addr_is_ila(struct ila_addr *iaddr)
-{
-       return (iaddr->ident.type != ILA_ATYPE_IID);
-}
-
 struct ila_params {
        struct ila_locator locator;
        struct ila_locator locator_match;
index 5fc1f4e..a1ac0e3 100644 (file)
@@ -601,8 +601,6 @@ out_ret:
        return ret;
 }
 
-#define ILA_HASH_TABLE_SIZE 1024
-
 int ila_xlat_init_net(struct net *net)
 {
        struct ila_net *ilan = net_generic(net, ila_net_id);
index debdaeb..18d0540 100644 (file)
@@ -183,15 +183,14 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                                        retv = -EBUSY;
                                        break;
                                }
-                       } else if (sk->sk_protocol == IPPROTO_TCP) {
-                               if (sk->sk_prot != &tcpv6_prot) {
-                                       retv = -EBUSY;
-                                       break;
-                               }
-                               break;
-                       } else {
+                       }
+                       if (sk->sk_protocol == IPPROTO_TCP &&
+                           sk->sk_prot != &tcpv6_prot) {
+                               retv = -EBUSY;
                                break;
                        }
+                       if (sk->sk_protocol != IPPROTO_TCP)
+                               break;
                        if (sk->sk_state != TCP_ESTABLISHED) {
                                retv = -ENOTCONN;
                                break;
index 1ecd4e9..27f29b9 100644 (file)
@@ -1302,7 +1302,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
                }
        }
        if (rt && lifetime == 0) {
-               ip6_del_rt(net, rt);
+               ip6_del_rt(net, rt, false);
                rt = NULL;
        }
 
@@ -1835,7 +1835,8 @@ static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
        }
 }
 
-int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
+int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void *buffer,
+               size_t *lenp, loff_t *ppos)
 {
        struct net_device *dev = ctl->extra1;
        struct inet6_dev *idev;
index 310cbdd..1ff1423 100644 (file)
@@ -984,7 +984,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
                                        gwaddr, dev);
 
        if (rt && !lifetime) {
-               ip6_del_rt(net, rt);
+               ip6_del_rt(net, rt, false);
                rt = NULL;
        }
 
@@ -1385,9 +1385,18 @@ static struct rt6_info *ip6_rt_pcpu_alloc(const struct fib6_result *res)
        }
        ip6_rt_copy_init(pcpu_rt, res);
        pcpu_rt->rt6i_flags |= RTF_PCPU;
+
+       if (f6i->nh)
+               pcpu_rt->sernum = rt_genid_ipv6(dev_net(dev));
+
        return pcpu_rt;
 }
 
+static bool rt6_is_valid(const struct rt6_info *rt6)
+{
+       return rt6->sernum == rt_genid_ipv6(dev_net(rt6->dst.dev));
+}
+
 /* It should be called with rcu_read_lock() acquired */
 static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res)
 {
@@ -1395,6 +1404,19 @@ static struct rt6_info *rt6_get_pcpu_route(const struct fib6_result *res)
 
        pcpu_rt = this_cpu_read(*res->nh->rt6i_pcpu);
 
+       if (pcpu_rt && pcpu_rt->sernum && !rt6_is_valid(pcpu_rt)) {
+               struct rt6_info *prev, **p;
+
+               p = this_cpu_ptr(res->nh->rt6i_pcpu);
+               prev = xchg(p, NULL);
+               if (prev) {
+                       dst_dev_put(&prev->dst);
+                       dst_release(&prev->dst);
+               }
+
+               pcpu_rt = NULL;
+       }
+
        return pcpu_rt;
 }
 
@@ -2593,6 +2615,9 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
 
        rt = container_of(dst, struct rt6_info, dst);
 
+       if (rt->sernum)
+               return rt6_is_valid(rt) ? dst : NULL;
+
        rcu_read_lock();
 
        /* All IPV6 dsts are created with ->obsolete set to the value
@@ -3729,9 +3754,12 @@ out:
        return err;
 }
 
-int ip6_del_rt(struct net *net, struct fib6_info *rt)
+int ip6_del_rt(struct net *net, struct fib6_info *rt, bool skip_notify)
 {
-       struct nl_info info = { .nl_net = net };
+       struct nl_info info = {
+               .nl_net = net,
+               .skip_notify = skip_notify
+       };
 
        return __ip6_del_rt(rt, &info);
 }
@@ -4252,7 +4280,7 @@ restart:
                    (!idev || idev->cnf.accept_ra != 2) &&
                    fib6_info_hold_safe(rt)) {
                        rcu_read_unlock();
-                       ip6_del_rt(net, rt);
+                       ip6_del_rt(net, rt, false);
                        goto restart;
                }
        }
@@ -5554,7 +5582,8 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
                if (nexthop_is_blackhole(rt->nh))
                        rtm->rtm_type = RTN_BLACKHOLE;
 
-               if (rt6_fill_node_nexthop(skb, rt->nh, &nh_flags) < 0)
+               if (net->ipv4.sysctl_nexthop_compat_mode &&
+                   rt6_fill_node_nexthop(skb, rt->nh, &nh_flags) < 0)
                        goto nla_put_failure;
 
                rtm->rtm_flags |= nh_flags;
@@ -6088,9 +6117,8 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v)
 
 #ifdef CONFIG_SYSCTL
 
-static
-int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
-                             void __user *buffer, size_t *lenp, loff_t *ppos)
+static int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
+                             void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net;
        int delay;
index d38b476..307f336 100644 (file)
@@ -8,6 +8,7 @@
 #include <net/rpl.h>
 
 #define IPV6_PFXTAIL_LEN(x) (sizeof(struct in6_addr) - (x))
+#define IPV6_RPL_BEST_ADDR_COMPRESSION 15
 
 static void ipv6_rpl_addr_decompress(struct in6_addr *dst,
                                     const struct in6_addr *daddr,
@@ -73,7 +74,7 @@ static unsigned char ipv6_rpl_srh_calc_cmpri(const struct ipv6_rpl_sr_hdr *inhdr
                }
        }
 
-       return plen;
+       return IPV6_RPL_BEST_ADDR_COMPRESSION;
 }
 
 static unsigned char ipv6_rpl_srh_calc_cmpre(const struct in6_addr *daddr,
@@ -83,10 +84,10 @@ static unsigned char ipv6_rpl_srh_calc_cmpre(const struct in6_addr *daddr,
 
        for (plen = 0; plen < sizeof(*daddr); plen++) {
                if (daddr->s6_addr[plen] != last_segment->s6_addr[plen])
-                       break;
+                       return plen;
        }
 
-       return plen;
+       return IPV6_RPL_BEST_ADDR_COMPRESSION;
 }
 
 void ipv6_rpl_srh_compress(struct ipv6_rpl_sr_hdr *outhdr,
index 75421a4..37b4342 100644 (file)
@@ -27,8 +27,9 @@
 
 bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
 {
-       int trailing;
        unsigned int tlv_offset;
+       int max_last_entry;
+       int trailing;
 
        if (srh->type != IPV6_SRCRT_TYPE_4)
                return false;
@@ -36,7 +37,12 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
        if (((srh->hdrlen + 1) << 3) != len)
                return false;
 
-       if (srh->segments_left > srh->first_segment)
+       max_last_entry = (srh->hdrlen / 2) - 1;
+
+       if (srh->first_segment > max_last_entry)
+               return false;
+
+       if (srh->segments_left > srh->first_segment + 1)
                return false;
 
        tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4);
@@ -434,7 +440,7 @@ static struct genl_family seg6_genl_family __ro_after_init = {
 
 int __init seg6_init(void)
 {
-       int err = -ENOMEM;
+       int err;
 
        err = genl_register_family(&seg6_genl_family);
        if (err)
index 63b657a..fac2135 100644 (file)
@@ -26,8 +26,7 @@ static int auto_flowlabels_min;
 static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX;
 
 static int proc_rt6_multipath_hash_policy(struct ctl_table *table, int write,
-                                         void __user *buffer, size_t *lenp,
-                                         loff_t *ppos)
+                                         void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net;
        int ret;
index fbe51d4..e34167f 100644 (file)
@@ -111,9 +111,7 @@ int xfrm6_output_finish(struct sock *sk, struct sk_buff *skb)
 {
        memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
 
-#ifdef CONFIG_NETFILTER
        IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
-#endif
 
        return xfrm_output(sk, skb);
 }
index d3b520b..fd5ac27 100644 (file)
@@ -56,6 +56,7 @@ static int l2tp_eth_dev_init(struct net_device *dev)
 {
        eth_hw_addr_random(dev);
        eth_broadcast_addr(dev->broadcast);
+       netdev_lockdep_set_classes(dev);
 
        return 0;
 }
index f5a9bdc..ebb381c 100644 (file)
@@ -920,51 +920,51 @@ static const struct genl_ops l2tp_nl_ops[] = {
                .cmd = L2TP_CMD_TUNNEL_CREATE,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = l2tp_nl_cmd_tunnel_create,
-               .flags = GENL_ADMIN_PERM,
+               .flags = GENL_UNS_ADMIN_PERM,
        },
        {
                .cmd = L2TP_CMD_TUNNEL_DELETE,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = l2tp_nl_cmd_tunnel_delete,
-               .flags = GENL_ADMIN_PERM,
+               .flags = GENL_UNS_ADMIN_PERM,
        },
        {
                .cmd = L2TP_CMD_TUNNEL_MODIFY,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = l2tp_nl_cmd_tunnel_modify,
-               .flags = GENL_ADMIN_PERM,
+               .flags = GENL_UNS_ADMIN_PERM,
        },
        {
                .cmd = L2TP_CMD_TUNNEL_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = l2tp_nl_cmd_tunnel_get,
                .dumpit = l2tp_nl_cmd_tunnel_dump,
-               .flags = GENL_ADMIN_PERM,
+               .flags = GENL_UNS_ADMIN_PERM,
        },
        {
                .cmd = L2TP_CMD_SESSION_CREATE,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = l2tp_nl_cmd_session_create,
-               .flags = GENL_ADMIN_PERM,
+               .flags = GENL_UNS_ADMIN_PERM,
        },
        {
                .cmd = L2TP_CMD_SESSION_DELETE,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = l2tp_nl_cmd_session_delete,
-               .flags = GENL_ADMIN_PERM,
+               .flags = GENL_UNS_ADMIN_PERM,
        },
        {
                .cmd = L2TP_CMD_SESSION_MODIFY,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = l2tp_nl_cmd_session_modify,
-               .flags = GENL_ADMIN_PERM,
+               .flags = GENL_UNS_ADMIN_PERM,
        },
        {
                .cmd = L2TP_CMD_SESSION_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = l2tp_nl_cmd_session_get,
                .dumpit = l2tp_nl_cmd_session_dump,
-               .flags = GENL_ADMIN_PERM,
+               .flags = GENL_UNS_ADMIN_PERM,
        },
 };
 
index 6acfc99..5b50e8d 100644 (file)
@@ -15,7 +15,7 @@ config LAPB
          currently supports LAPB only over Ethernet connections. If you want
          to use LAPB connections over Ethernet, say Y here and to "LAPB over
          Ethernet driver" below. Read
-         <file:Documentation/networking/lapb-module.txt> for technical
+         <file:Documentation/networking/lapb-module.rst> for technical
          details.
 
          To compile this driver as a module, choose M here: the
index 8345926..6423173 100644 (file)
@@ -1069,7 +1069,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
                if (hw->max_signal <= 0) {
                        result = -EINVAL;
-                       goto fail_wiphy_register;
+                       goto fail_workqueue;
                }
        }
 
@@ -1135,7 +1135,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
        result = ieee80211_init_cipher_suites(local);
        if (result < 0)
-               goto fail_wiphy_register;
+               goto fail_workqueue;
 
        if (!local->ops->remain_on_channel)
                local->hw.wiphy->max_remain_on_channel_duration = 5000;
@@ -1161,10 +1161,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
        local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;
 
-       result = wiphy_register(local->hw.wiphy);
-       if (result < 0)
-               goto fail_wiphy_register;
-
        /*
         * We use the number of queues for feature tests (QoS, HT) internally
         * so restrict them appropriately.
@@ -1187,8 +1183,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        local->tx_headroom = max_t(unsigned int , local->hw.extra_tx_headroom,
                                   IEEE80211_TX_STATUS_HEADROOM);
 
-       debugfs_hw_add(local);
-
        /*
         * if the driver doesn't specify a max listen interval we
         * use 5 which should be a safe default
@@ -1217,9 +1211,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                goto fail_flows;
 
        rtnl_lock();
-
        result = ieee80211_init_rate_ctrl_alg(local,
                                              hw->rate_control_algorithm);
+       rtnl_unlock();
        if (result < 0) {
                wiphy_debug(local->hw.wiphy,
                            "Failed to initialize rate control algorithm\n");
@@ -1273,6 +1267,15 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                local->sband_allocated |= BIT(band);
        }
 
+       result = wiphy_register(local->hw.wiphy);
+       if (result < 0)
+               goto fail_wiphy_register;
+
+       debugfs_hw_add(local);
+       rate_control_add_debugfs(local);
+
+       rtnl_lock();
+
        /* add one default STA interface if supported */
        if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
            !ieee80211_hw_check(hw, NO_AUTO_VIF)) {
@@ -1312,17 +1315,17 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 #if defined(CONFIG_INET) || defined(CONFIG_IPV6)
  fail_ifa:
 #endif
+       wiphy_unregister(local->hw.wiphy);
+ fail_wiphy_register:
        rtnl_lock();
        rate_control_deinitialize(local);
        ieee80211_remove_interfaces(local);
- fail_rate:
        rtnl_unlock();
+ fail_rate:
  fail_flows:
        ieee80211_led_exit(local);
        destroy_workqueue(local->workqueue);
  fail_workqueue:
-       wiphy_unregister(local->hw.wiphy);
- fail_wiphy_register:
        if (local->wiphy_ciphers_allocated)
                kfree(local->hw.wiphy->cipher_suites);
        kfree(local->int_scan_req);
@@ -1372,8 +1375,8 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
        skb_queue_purge(&local->skb_queue_unreliable);
        skb_queue_purge(&local->skb_queue_tdls_chsw);
 
-       destroy_workqueue(local->workqueue);
        wiphy_unregister(local->hw.wiphy);
+       destroy_workqueue(local->workqueue);
        ieee80211_led_exit(local);
        kfree(local->int_scan_req);
 }
index d09b3c7..36978a0 100644 (file)
@@ -1257,15 +1257,15 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
                    sdata->u.mesh.mshcfg.rssi_threshold < rx_status->signal)
                        mesh_neighbour_update(sdata, mgmt->sa, &elems,
                                              rx_status);
+
+               if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT &&
+                   !sdata->vif.csa_active)
+                       ieee80211_mesh_process_chnswitch(sdata, &elems, true);
        }
 
        if (ifmsh->sync_ops)
                ifmsh->sync_ops->rx_bcn_presp(sdata,
                        stype, mgmt, &elems, rx_status);
-
-       if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT &&
-           !sdata->vif.csa_active)
-               ieee80211_mesh_process_chnswitch(sdata, &elems, true);
 }
 
 int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
@@ -1373,6 +1373,9 @@ static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
        ieee802_11_parse_elems(pos, len - baselen, true, &elems,
                               mgmt->bssid, NULL);
 
+       if (!mesh_matches_local(sdata, &elems))
+               return;
+
        ifmsh->chsw_ttl = elems.mesh_chansw_params_ie->mesh_ttl;
        if (!--ifmsh->chsw_ttl)
                fwd_csa = false;
index a1e9fc7..b051f12 100644 (file)
@@ -214,17 +214,16 @@ static ssize_t rcname_read(struct file *file, char __user *userbuf,
                                       ref->ops->name, len);
 }
 
-static const struct file_operations rcname_ops = {
+const struct file_operations rcname_ops = {
        .read = rcname_read,
        .open = simple_open,
        .llseek = default_llseek,
 };
 #endif
 
-static struct rate_control_ref *rate_control_alloc(const char *name,
-                                           struct ieee80211_local *local)
+static struct rate_control_ref *
+rate_control_alloc(const char *name, struct ieee80211_local *local)
 {
-       struct dentry *debugfsdir = NULL;
        struct rate_control_ref *ref;
 
        ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL);
@@ -234,13 +233,7 @@ static struct rate_control_ref *rate_control_alloc(const char *name,
        if (!ref->ops)
                goto free;
 
-#ifdef CONFIG_MAC80211_DEBUGFS
-       debugfsdir = debugfs_create_dir("rc", local->hw.wiphy->debugfsdir);
-       local->debugfs.rcdir = debugfsdir;
-       debugfs_create_file("name", 0400, debugfsdir, ref, &rcname_ops);
-#endif
-
-       ref->priv = ref->ops->alloc(&local->hw, debugfsdir);
+       ref->priv = ref->ops->alloc(&local->hw);
        if (!ref->priv)
                goto free;
        return ref;
index 5397c6d..79b44d3 100644 (file)
@@ -60,6 +60,29 @@ static inline void rate_control_add_sta_debugfs(struct sta_info *sta)
 #endif
 }
 
+extern const struct file_operations rcname_ops;
+
+static inline void rate_control_add_debugfs(struct ieee80211_local *local)
+{
+#ifdef CONFIG_MAC80211_DEBUGFS
+       struct dentry *debugfsdir;
+
+       if (!local->rate_ctrl)
+               return;
+
+       if (!local->rate_ctrl->ops->add_debugfs)
+               return;
+
+       debugfsdir = debugfs_create_dir("rc", local->hw.wiphy->debugfsdir);
+       local->debugfs.rcdir = debugfsdir;
+       debugfs_create_file("name", 0400, debugfsdir,
+                           local->rate_ctrl, &rcname_ops);
+
+       local->rate_ctrl->ops->add_debugfs(&local->hw, local->rate_ctrl->priv,
+                                          debugfsdir);
+#endif
+}
+
 void ieee80211_check_rate_mask(struct ieee80211_sub_if_data *sdata);
 
 /* Get a reference to the rate control algorithm. If `name' is NULL, get the
index 694a319..5dc3e5b 100644 (file)
@@ -1635,7 +1635,7 @@ minstrel_ht_init_cck_rates(struct minstrel_priv *mp)
 }
 
 static void *
-minstrel_ht_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
+minstrel_ht_alloc(struct ieee80211_hw *hw)
 {
        struct minstrel_priv *mp;
 
@@ -1673,7 +1673,17 @@ minstrel_ht_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
        mp->update_interval = HZ / 10;
        mp->new_avg = true;
 
+       minstrel_ht_init_cck_rates(mp);
+
+       return mp;
+}
+
 #ifdef CONFIG_MAC80211_DEBUGFS
+static void minstrel_ht_add_debugfs(struct ieee80211_hw *hw, void *priv,
+                                   struct dentry *debugfsdir)
+{
+       struct minstrel_priv *mp = priv;
+
        mp->fixed_rate_idx = (u32) -1;
        debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
                           &mp->fixed_rate_idx);
@@ -1681,12 +1691,8 @@ minstrel_ht_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
                           &mp->sample_switch);
        debugfs_create_bool("new_avg", S_IRUGO | S_IWUSR, debugfsdir,
                           &mp->new_avg);
-#endif
-
-       minstrel_ht_init_cck_rates(mp);
-
-       return mp;
 }
+#endif
 
 static void
 minstrel_ht_free(void *priv)
@@ -1725,6 +1731,7 @@ static const struct rate_control_ops mac80211_minstrel_ht = {
        .alloc = minstrel_ht_alloc,
        .free = minstrel_ht_free,
 #ifdef CONFIG_MAC80211_DEBUGFS
+       .add_debugfs = minstrel_ht_add_debugfs,
        .add_sta_debugfs = minstrel_ht_add_sta_debugfs,
 #endif
        .get_expected_throughput = minstrel_ht_get_expected_throughput,
index f8d5c25..cd8487b 100644 (file)
@@ -231,7 +231,8 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
        struct sta_info *sta;
        int i = 0;
 
-       list_for_each_entry_rcu(sta, &local->sta_list, list) {
+       list_for_each_entry_rcu(sta, &local->sta_list, list,
+                               lockdep_is_held(&local->sta_mtx)) {
                if (sdata != sta->sdata)
                        continue;
                if (i < idx) {
index 82846ac..9849c14 100644 (file)
@@ -2144,7 +2144,7 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
 
                /*
                 * Please update the file
-                * Documentation/networking/mac80211-injection.txt
+                * Documentation/networking/mac80211-injection.rst
                 * when parsing new fields here.
                 */
 
index 4701edf..a42e4ed 100644 (file)
@@ -1362,8 +1362,7 @@ done:
        (&((struct mpls_dev *)0)->field)
 
 static int mpls_conf_proc(struct ctl_table *ctl, int write,
-                         void __user *buffer,
-                         size_t *lenp, loff_t *ppos)
+                         void *buffer, size_t *lenp, loff_t *ppos)
 {
        int oval = *(int *)ctl->data;
        int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
@@ -2594,7 +2593,7 @@ nolabels:
 }
 
 static int mpls_platform_labels(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net = table->data;
        int platform_labels = net->mpls.platform_labels;
index faf5758..45497af 100644 (file)
@@ -16,10 +16,10 @@ static bool mptcp_cap_flag_sha256(u8 flags)
        return (flags & MPTCP_CAP_FLAG_MASK) == MPTCP_CAP_HMAC_SHA256;
 }
 
-void mptcp_parse_option(const struct sk_buff *skb, const unsigned char *ptr,
-                       int opsize, struct tcp_options_received *opt_rx)
+static void mptcp_parse_option(const struct sk_buff *skb,
+                              const unsigned char *ptr, int opsize,
+                              struct mptcp_options_received *mp_opt)
 {
-       struct mptcp_options_received *mp_opt = &opt_rx->mptcp;
        u8 subtype = *ptr >> 4;
        int expected_opsize;
        u8 version;
@@ -283,12 +283,20 @@ void mptcp_parse_option(const struct sk_buff *skb, const unsigned char *ptr,
 }
 
 void mptcp_get_options(const struct sk_buff *skb,
-                      struct tcp_options_received *opt_rx)
+                      struct mptcp_options_received *mp_opt)
 {
-       const unsigned char *ptr;
        const struct tcphdr *th = tcp_hdr(skb);
-       int length = (th->doff * 4) - sizeof(struct tcphdr);
+       const unsigned char *ptr;
+       int length;
+
+       /* initialize option status */
+       mp_opt->mp_capable = 0;
+       mp_opt->mp_join = 0;
+       mp_opt->add_addr = 0;
+       mp_opt->rm_addr = 0;
+       mp_opt->dss = 0;
 
+       length = (th->doff * 4) - sizeof(struct tcphdr);
        ptr = (const unsigned char *)(th + 1);
 
        while (length > 0) {
@@ -308,7 +316,7 @@ void mptcp_get_options(const struct sk_buff *skb,
                        if (opsize > length)
                                return; /* don't parse partial options */
                        if (opcode == TCPOPT_MPTCP)
-                               mptcp_parse_option(skb, ptr, opsize, opt_rx);
+                               mptcp_parse_option(skb, ptr, opsize, mp_opt);
                        ptr += opsize - 2;
                        length -= opsize;
                }
@@ -344,28 +352,6 @@ bool mptcp_syn_options(struct sock *sk, const struct sk_buff *skb,
        return false;
 }
 
-void mptcp_rcv_synsent(struct sock *sk)
-{
-       struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
-       struct tcp_sock *tp = tcp_sk(sk);
-
-       if (subflow->request_mptcp && tp->rx_opt.mptcp.mp_capable) {
-               subflow->mp_capable = 1;
-               subflow->can_ack = 1;
-               subflow->remote_key = tp->rx_opt.mptcp.sndr_key;
-               pr_debug("subflow=%p, remote_key=%llu", subflow,
-                        subflow->remote_key);
-       } else if (subflow->request_join && tp->rx_opt.mptcp.mp_join) {
-               subflow->mp_join = 1;
-               subflow->thmac = tp->rx_opt.mptcp.thmac;
-               subflow->remote_nonce = tp->rx_opt.mptcp.nonce;
-               pr_debug("subflow=%p, thmac=%llu, remote_nonce=%u", subflow,
-                        subflow->thmac, subflow->remote_nonce);
-       } else if (subflow->request_mptcp) {
-               tcp_sk(sk)->is_mptcp = 0;
-       }
-}
-
 /* MP_JOIN client subflow must wait for 4th ack before sending any data:
  * TCP can't schedule delack timer before the subflow is fully established.
  * MPTCP uses the delack timer to do 3rd ack retransmissions
@@ -709,7 +695,7 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *sk,
        if (TCP_SKB_CB(skb)->seq != subflow->ssn_offset + 1)
                return subflow->mp_capable;
 
-       if (mp_opt->use_ack) {
+       if (mp_opt->dss && mp_opt->use_ack) {
                /* subflows are fully established as soon as we get any
                 * additional ack.
                 */
@@ -717,8 +703,6 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *sk,
                goto fully_established;
        }
 
-       WARN_ON_ONCE(subflow->can_ack);
-
        /* If the first established packet does not contain MP_CAPABLE + data
         * then fallback to TCP
         */
@@ -728,6 +712,8 @@ static bool check_fully_established(struct mptcp_sock *msk, struct sock *sk,
                return false;
        }
 
+       if (unlikely(!READ_ONCE(msk->pm.server_side)))
+               pr_warn_once("bogus mpc option on established client sk");
        subflow->fully_established = 1;
        subflow->remote_key = mp_opt->sndr_key;
        subflow->can_ack = 1;
@@ -819,41 +805,41 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb,
 {
        struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
        struct mptcp_sock *msk = mptcp_sk(subflow->conn);
-       struct mptcp_options_received *mp_opt;
+       struct mptcp_options_received mp_opt;
        struct mptcp_ext *mpext;
 
-       mp_opt = &opt_rx->mptcp;
-       if (!check_fully_established(msk, sk, subflow, skb, mp_opt))
+       mptcp_get_options(skb, &mp_opt);
+       if (!check_fully_established(msk, sk, subflow, skb, &mp_opt))
                return;
 
-       if (mp_opt->add_addr && add_addr_hmac_valid(msk, mp_opt)) {
+       if (mp_opt.add_addr && add_addr_hmac_valid(msk, &mp_opt)) {
                struct mptcp_addr_info addr;
 
-               addr.port = htons(mp_opt->port);
-               addr.id = mp_opt->addr_id;
-               if (mp_opt->family == MPTCP_ADDR_IPVERSION_4) {
+               addr.port = htons(mp_opt.port);
+               addr.id = mp_opt.addr_id;
+               if (mp_opt.family == MPTCP_ADDR_IPVERSION_4) {
                        addr.family = AF_INET;
-                       addr.addr = mp_opt->addr;
+                       addr.addr = mp_opt.addr;
                }
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
-               else if (mp_opt->family == MPTCP_ADDR_IPVERSION_6) {
+               else if (mp_opt.family == MPTCP_ADDR_IPVERSION_6) {
                        addr.family = AF_INET6;
-                       addr.addr6 = mp_opt->addr6;
+                       addr.addr6 = mp_opt.addr6;
                }
 #endif
-               if (!mp_opt->echo)
+               if (!mp_opt.echo)
                        mptcp_pm_add_addr_received(msk, &addr);
-               mp_opt->add_addr = 0;
+               mp_opt.add_addr = 0;
        }
 
-       if (!mp_opt->dss)
+       if (!mp_opt.dss)
                return;
 
        /* we can't wait for recvmsg() to update the ack_seq, otherwise
         * monodirectional flows will stuck
         */
-       if (mp_opt->use_ack)
-               update_una(msk, mp_opt);
+       if (mp_opt.use_ack)
+               update_una(msk, &mp_opt);
 
        mpext = skb_ext_add(skb, SKB_EXT_MPTCP);
        if (!mpext)
@@ -861,8 +847,8 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb,
 
        memset(mpext, 0, sizeof(*mpext));
 
-       if (mp_opt->use_map) {
-               if (mp_opt->mpc_map) {
+       if (mp_opt.use_map) {
+               if (mp_opt.mpc_map) {
                        /* this is an MP_CAPABLE carrying MPTCP data
                         * we know this map the first chunk of data
                         */
@@ -872,16 +858,16 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb,
                        mpext->subflow_seq = 1;
                        mpext->dsn64 = 1;
                        mpext->mpc_map = 1;
+                       mpext->data_fin = 0;
                } else {
-                       mpext->data_seq = mp_opt->data_seq;
-                       mpext->subflow_seq = mp_opt->subflow_seq;
-                       mpext->dsn64 = mp_opt->dsn64;
+                       mpext->data_seq = mp_opt.data_seq;
+                       mpext->subflow_seq = mp_opt.subflow_seq;
+                       mpext->dsn64 = mp_opt.dsn64;
+                       mpext->data_fin = mp_opt.data_fin;
                }
-               mpext->data_len = mp_opt->data_len;
+               mpext->data_len = mp_opt.data_len;
                mpext->use_map = 1;
        }
-
-       mpext->data_fin = mp_opt->data_fin;
 }
 
 void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
index 86d61ab..b78edf2 100644 (file)
@@ -599,12 +599,14 @@ static int mptcp_nl_fill_addr(struct sk_buff *skb,
            nla_put_s32(skb, MPTCP_PM_ADDR_ATTR_IF_IDX, entry->ifindex))
                goto nla_put_failure;
 
-       if (addr->family == AF_INET)
-               nla_put_in_addr(skb, MPTCP_PM_ADDR_ATTR_ADDR4,
-                               addr->addr.s_addr);
+       if (addr->family == AF_INET &&
+           nla_put_in_addr(skb, MPTCP_PM_ADDR_ATTR_ADDR4,
+                           addr->addr.s_addr))
+               goto nla_put_failure;
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
-       else if (addr->family == AF_INET6)
-               nla_put_in6_addr(skb, MPTCP_PM_ADDR_ATTR_ADDR6, &addr->addr6);
+       else if (addr->family == AF_INET6 &&
+                nla_put_in6_addr(skb, MPTCP_PM_ADDR_ATTR_ADDR6, &addr->addr6))
+               goto nla_put_failure;
 #endif
        nla_nest_end(skb, attr);
        return 0;
index 939a504..e1f2301 100644 (file)
@@ -97,12 +97,7 @@ static struct socket *__mptcp_tcp_fallback(struct mptcp_sock *msk)
        if (likely(!__mptcp_needs_tcp_fallback(msk)))
                return NULL;
 
-       if (msk->subflow) {
-               release_sock((struct sock *)msk);
-               return msk->subflow;
-       }
-
-       return NULL;
+       return msk->subflow;
 }
 
 static bool __mptcp_can_create_subflow(const struct mptcp_sock *msk)
@@ -734,9 +729,10 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
                        goto out;
        }
 
+fallback:
        ssock = __mptcp_tcp_fallback(msk);
        if (unlikely(ssock)) {
-fallback:
+               release_sock(sk);
                pr_debug("fallback passthrough");
                ret = sock_sendmsg(ssock, msg);
                return ret >= 0 ? ret + copied : (copied ? copied : ret);
@@ -769,8 +765,14 @@ fallback:
                if (ret < 0)
                        break;
                if (ret == 0 && unlikely(__mptcp_needs_tcp_fallback(msk))) {
+                       /* Can happen for passive sockets:
+                        * 3WHS negotiated MPTCP, but first packet after is
+                        * plain TCP (e.g. due to middlebox filtering unknown
+                        * options).
+                        *
+                        * Fall back to TCP.
+                        */
                        release_sock(ssk);
-                       ssock = __mptcp_tcp_fallback(msk);
                        goto fallback;
                }
 
@@ -883,6 +885,7 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
        ssock = __mptcp_tcp_fallback(msk);
        if (unlikely(ssock)) {
 fallback:
+               release_sock(sk);
                pr_debug("fallback-read subflow=%p",
                         mptcp_subflow_ctx(ssock->sk));
                copied = sock_recvmsg(ssock, msg, flags);
@@ -1313,11 +1316,12 @@ static void mptcp_copy_inaddrs(struct sock *msk, const struct sock *ssk)
 
 static int mptcp_disconnect(struct sock *sk, int flags)
 {
-       lock_sock(sk);
-       __mptcp_clear_xmit(sk);
-       release_sock(sk);
-       mptcp_cancel_work(sk);
-       return tcp_disconnect(sk, flags);
+       /* Should never be called.
+        * inet_stream_connect() calls ->disconnect, but that
+        * refers to the subflow socket, not the mptcp one.
+        */
+       WARN_ON_ONCE(1);
+       return 0;
 }
 
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
@@ -1329,7 +1333,9 @@ static struct ipv6_pinfo *mptcp_inet6_sk(const struct sock *sk)
 }
 #endif
 
-struct sock *mptcp_sk_clone(const struct sock *sk, struct request_sock *req)
+struct sock *mptcp_sk_clone(const struct sock *sk,
+                           const struct mptcp_options_received *mp_opt,
+                           struct request_sock *req)
 {
        struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req);
        struct sock *nsk = sk_clone_lock(sk, GFP_ATOMIC);
@@ -1352,26 +1358,30 @@ struct sock *mptcp_sk_clone(const struct sock *sk, struct request_sock *req)
        msk->subflow = NULL;
 
        if (unlikely(mptcp_token_new_accept(subflow_req->token, nsk))) {
+               nsk->sk_state = TCP_CLOSE;
                bh_unlock_sock(nsk);
 
                /* we can't call into mptcp_close() here - possible BH context
-                * free the sock directly
+                * free the sock directly.
+                * sk_clone_lock() sets nsk refcnt to two, hence call sk_free()
+                * too.
                 */
-               nsk->sk_prot->destroy(nsk);
+               sk_common_release(nsk);
                sk_free(nsk);
                return NULL;
        }
 
        msk->write_seq = subflow_req->idsn + 1;
        atomic64_set(&msk->snd_una, msk->write_seq);
-       if (subflow_req->remote_key_valid) {
+       if (mp_opt->mp_capable) {
                msk->can_ack = true;
-               msk->remote_key = subflow_req->remote_key;
+               msk->remote_key = mp_opt->sndr_key;
                mptcp_crypto_key_sha(msk->remote_key, NULL, &ack_seq);
                ack_seq++;
                msk->ack_seq = ack_seq;
        }
 
+       sock_reset_flag(nsk, SOCK_RCU_FREE);
        /* will be fully established after successful MPC subflow creation */
        inet_sk_state_store(nsk, TCP_SYN_RECV);
        bh_unlock_sock(nsk);
@@ -1428,6 +1438,7 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
                newsk = new_mptcp_sock;
                mptcp_copy_inaddrs(newsk, ssk);
                list_add(&subflow->node, &msk->conn_list);
+               inet_sk_state_store(newsk, TCP_ESTABLISHED);
 
                bh_unlock_sock(new_mptcp_sock);
 
@@ -1467,12 +1478,11 @@ static int mptcp_setsockopt(struct sock *sk, int level, int optname,
         */
        lock_sock(sk);
        ssock = __mptcp_tcp_fallback(msk);
+       release_sock(sk);
        if (ssock)
                return tcp_setsockopt(ssock->sk, level, optname, optval,
                                      optlen);
 
-       release_sock(sk);
-
        return -EOPNOTSUPP;
 }
 
@@ -1492,12 +1502,11 @@ static int mptcp_getsockopt(struct sock *sk, int level, int optname,
         */
        lock_sock(sk);
        ssock = __mptcp_tcp_fallback(msk);
+       release_sock(sk);
        if (ssock)
                return tcp_getsockopt(ssock->sk, level, optname, optval,
                                      option);
 
-       release_sock(sk);
-
        return -EOPNOTSUPP;
 }
 
@@ -1774,6 +1783,8 @@ static int mptcp_listen(struct socket *sock, int backlog)
                goto unlock;
        }
 
+       sock_set_flag(sock->sk, SOCK_RCU_FREE);
+
        err = ssock->ops->listen(ssock, backlog);
        inet_sk_state_store(sock->sk, inet_sk_state_load(ssock->sk));
        if (!err)
index 6744800..e4ca632 100644 (file)
 #define MPTCP_WORK_RTX         2
 #define MPTCP_WORK_EOF         3
 
+struct mptcp_options_received {
+       u64     sndr_key;
+       u64     rcvr_key;
+       u64     data_ack;
+       u64     data_seq;
+       u32     subflow_seq;
+       u16     data_len;
+       u16     mp_capable : 1,
+               mp_join : 1,
+               dss : 1,
+               add_addr : 1,
+               rm_addr : 1,
+               family : 4,
+               echo : 1,
+               backup : 1;
+       u32     token;
+       u32     nonce;
+       u64     thmac;
+       u8      hmac[20];
+       u8      join_id;
+       u8      use_map:1,
+               dsn64:1,
+               data_fin:1,
+               use_ack:1,
+               ack64:1,
+               mpc_map:1,
+               __unused:2;
+       u8      addr_id;
+       u8      rm_id;
+       union {
+               struct in_addr  addr;
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+               struct in6_addr addr6;
+#endif
+       };
+       u64     ahmac;
+       u16     port;
+};
+
 static inline __be32 mptcp_option(u8 subopt, u8 len, u8 nib, u8 field)
 {
        return htonl((TCPOPT_MPTCP << 24) | (len << 16) | (subopt << 12) |
@@ -206,12 +245,10 @@ struct mptcp_subflow_request_sock {
        struct  tcp_request_sock sk;
        u16     mp_capable : 1,
                mp_join : 1,
-               backup : 1,
-               remote_key_valid : 1;
+               backup : 1;
        u8      local_id;
        u8      remote_id;
        u64     local_key;
-       u64     remote_key;
        u64     idsn;
        u32     token;
        u32     ssn_offset;
@@ -332,9 +369,11 @@ void mptcp_proto_init(void);
 int mptcp_proto_v6_init(void);
 #endif
 
-struct sock *mptcp_sk_clone(const struct sock *sk, struct request_sock *req);
+struct sock *mptcp_sk_clone(const struct sock *sk,
+                           const struct mptcp_options_received *mp_opt,
+                           struct request_sock *req);
 void mptcp_get_options(const struct sk_buff *skb,
-                      struct tcp_options_received *opt_rx);
+                      struct mptcp_options_received *mp_opt);
 
 void mptcp_finish_connect(struct sock *sk);
 void mptcp_data_ready(struct sock *sk, struct sock *ssk);
index 50a8bea..009d5c4 100644 (file)
@@ -124,16 +124,14 @@ static void subflow_init_req(struct request_sock *req,
 {
        struct mptcp_subflow_context *listener = mptcp_subflow_ctx(sk_listener);
        struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req);
-       struct tcp_options_received rx_opt;
+       struct mptcp_options_received mp_opt;
 
        pr_debug("subflow_req=%p, listener=%p", subflow_req, listener);
 
-       memset(&rx_opt.mptcp, 0, sizeof(rx_opt.mptcp));
-       mptcp_get_options(skb, &rx_opt);
+       mptcp_get_options(skb, &mp_opt);
 
        subflow_req->mp_capable = 0;
        subflow_req->mp_join = 0;
-       subflow_req->remote_key_valid = 0;
 
 #ifdef CONFIG_TCP_MD5SIG
        /* no MPTCP if MD5SIG is enabled on this socket or we may run out of
@@ -143,16 +141,16 @@ static void subflow_init_req(struct request_sock *req,
                return;
 #endif
 
-       if (rx_opt.mptcp.mp_capable) {
+       if (mp_opt.mp_capable) {
                SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_MPCAPABLEPASSIVE);
 
-               if (rx_opt.mptcp.mp_join)
+               if (mp_opt.mp_join)
                        return;
-       } else if (rx_opt.mptcp.mp_join) {
+       } else if (mp_opt.mp_join) {
                SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINSYNRX);
        }
 
-       if (rx_opt.mptcp.mp_capable && listener->request_mptcp) {
+       if (mp_opt.mp_capable && listener->request_mptcp) {
                int err;
 
                err = mptcp_token_new_request(req);
@@ -160,13 +158,13 @@ static void subflow_init_req(struct request_sock *req,
                        subflow_req->mp_capable = 1;
 
                subflow_req->ssn_offset = TCP_SKB_CB(skb)->seq;
-       } else if (rx_opt.mptcp.mp_join && listener->request_mptcp) {
+       } else if (mp_opt.mp_join && listener->request_mptcp) {
                subflow_req->ssn_offset = TCP_SKB_CB(skb)->seq;
                subflow_req->mp_join = 1;
-               subflow_req->backup = rx_opt.mptcp.backup;
-               subflow_req->remote_id = rx_opt.mptcp.join_id;
-               subflow_req->token = rx_opt.mptcp.token;
-               subflow_req->remote_nonce = rx_opt.mptcp.nonce;
+               subflow_req->backup = mp_opt.backup;
+               subflow_req->remote_id = mp_opt.join_id;
+               subflow_req->token = mp_opt.token;
+               subflow_req->remote_nonce = mp_opt.nonce;
                pr_debug("token=%u, remote_nonce=%u", subflow_req->token,
                         subflow_req->remote_nonce);
                if (!subflow_token_join_request(req, skb)) {
@@ -222,23 +220,47 @@ static bool subflow_thmac_valid(struct mptcp_subflow_context *subflow)
 static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
 {
        struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
+       struct mptcp_options_received mp_opt;
        struct sock *parent = subflow->conn;
+       struct tcp_sock *tp = tcp_sk(sk);
 
        subflow->icsk_af_ops->sk_rx_dst_set(sk, skb);
 
-       if (inet_sk_state_load(parent) != TCP_ESTABLISHED) {
+       if (inet_sk_state_load(parent) == TCP_SYN_SENT) {
                inet_sk_state_store(parent, TCP_ESTABLISHED);
                parent->sk_state_change(parent);
        }
 
-       if (subflow->conn_finished || !tcp_sk(sk)->is_mptcp)
+       /* be sure no special action on any packet other than syn-ack */
+       if (subflow->conn_finished)
+               return;
+
+       subflow->conn_finished = 1;
+
+       mptcp_get_options(skb, &mp_opt);
+       if (subflow->request_mptcp && mp_opt.mp_capable) {
+               subflow->mp_capable = 1;
+               subflow->can_ack = 1;
+               subflow->remote_key = mp_opt.sndr_key;
+               pr_debug("subflow=%p, remote_key=%llu", subflow,
+                        subflow->remote_key);
+       } else if (subflow->request_join && mp_opt.mp_join) {
+               subflow->mp_join = 1;
+               subflow->thmac = mp_opt.thmac;
+               subflow->remote_nonce = mp_opt.nonce;
+               pr_debug("subflow=%p, thmac=%llu, remote_nonce=%u", subflow,
+                        subflow->thmac, subflow->remote_nonce);
+       } else if (subflow->request_mptcp) {
+               tp->is_mptcp = 0;
+       }
+
+       if (!tp->is_mptcp)
                return;
 
        if (subflow->mp_capable) {
                pr_debug("subflow=%p, remote_key=%llu", mptcp_subflow_ctx(sk),
                         subflow->remote_key);
                mptcp_finish_connect(sk);
-               subflow->conn_finished = 1;
 
                if (skb) {
                        pr_debug("synack seq=%u", TCP_SKB_CB(skb)->seq);
@@ -265,7 +287,6 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
                if (!mptcp_finish_join(sk))
                        goto do_reset;
 
-               subflow->conn_finished = 1;
                MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_JOINSYNACKRX);
        } else {
 do_reset:
@@ -323,7 +344,7 @@ drop:
 
 /* validate hmac received in third ACK */
 static bool subflow_hmac_valid(const struct request_sock *req,
-                              const struct tcp_options_received *rx_opt)
+                              const struct mptcp_options_received *mp_opt)
 {
        const struct mptcp_subflow_request_sock *subflow_req;
        u8 hmac[MPTCPOPT_HMAC_LEN];
@@ -340,13 +361,53 @@ static bool subflow_hmac_valid(const struct request_sock *req,
                              subflow_req->local_nonce, hmac);
 
        ret = true;
-       if (crypto_memneq(hmac, rx_opt->mptcp.hmac, sizeof(hmac)))
+       if (crypto_memneq(hmac, mp_opt->hmac, sizeof(hmac)))
                ret = false;
 
        sock_put((struct sock *)msk);
        return ret;
 }
 
+static void mptcp_sock_destruct(struct sock *sk)
+{
+       /* if new mptcp socket isn't accepted, it is free'd
+        * from the tcp listener sockets request queue, linked
+        * from req->sk.  The tcp socket is released.
+        * This calls the ULP release function which will
+        * also remove the mptcp socket, via
+        * sock_put(ctx->conn).
+        *
+        * Problem is that the mptcp socket will not be in
+        * SYN_RECV state and doesn't have SOCK_DEAD flag.
+        * Both result in warnings from inet_sock_destruct.
+        */
+
+       if (sk->sk_state == TCP_SYN_RECV) {
+               sk->sk_state = TCP_CLOSE;
+               WARN_ON_ONCE(sk->sk_socket);
+               sock_orphan(sk);
+       }
+
+       inet_sock_destruct(sk);
+}
+
+static void mptcp_force_close(struct sock *sk)
+{
+       inet_sk_state_store(sk, TCP_CLOSE);
+       sk_common_release(sk);
+}
+
+static void subflow_ulp_fallback(struct sock *sk,
+                                struct mptcp_subflow_context *old_ctx)
+{
+       struct inet_connection_sock *icsk = inet_csk(sk);
+
+       mptcp_subflow_tcp_fallback(sk, old_ctx);
+       icsk->icsk_ulp_ops = NULL;
+       rcu_assign_pointer(icsk->icsk_ulp_data, NULL);
+       tcp_sk(sk)->is_mptcp = 0;
+}
+
 static struct sock *subflow_syn_recv_sock(const struct sock *sk,
                                          struct sk_buff *skb,
                                          struct request_sock *req,
@@ -356,13 +417,18 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
 {
        struct mptcp_subflow_context *listener = mptcp_subflow_ctx(sk);
        struct mptcp_subflow_request_sock *subflow_req;
-       struct tcp_options_received opt_rx;
+       struct mptcp_options_received mp_opt;
        bool fallback_is_fatal = false;
        struct sock *new_msk = NULL;
+       bool fallback = false;
        struct sock *child;
 
        pr_debug("listener=%p, req=%p, conn=%p", listener, req, listener->conn);
 
+       /* we need later a valid 'mp_capable' value even when options are not
+        * parsed
+        */
+       mp_opt.mp_capable = 0;
        if (tcp_rsk(req)->is_mptcp == 0)
                goto create_child;
 
@@ -377,26 +443,21 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
                        goto create_msk;
                }
 
-               opt_rx.mptcp.mp_capable = 0;
-               mptcp_get_options(skb, &opt_rx);
-               if (opt_rx.mptcp.mp_capable) {
-                       subflow_req->remote_key = opt_rx.mptcp.sndr_key;
-                       subflow_req->remote_key_valid = 1;
-               } else {
-                       subflow_req->mp_capable = 0;
+               mptcp_get_options(skb, &mp_opt);
+               if (!mp_opt.mp_capable) {
+                       fallback = true;
                        goto create_child;
                }
 
 create_msk:
-               new_msk = mptcp_sk_clone(listener->conn, req);
+               new_msk = mptcp_sk_clone(listener->conn, &mp_opt, req);
                if (!new_msk)
-                       subflow_req->mp_capable = 0;
+                       fallback = true;
        } else if (subflow_req->mp_join) {
                fallback_is_fatal = true;
-               opt_rx.mptcp.mp_join = 0;
-               mptcp_get_options(skb, &opt_rx);
-               if (!opt_rx.mptcp.mp_join ||
-                   !subflow_hmac_valid(req, &opt_rx)) {
+               mptcp_get_options(skb, &mp_opt);
+               if (!mp_opt.mp_join ||
+                   !subflow_hmac_valid(req, &mp_opt)) {
                        SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINACKMAC);
                        return NULL;
                }
@@ -409,12 +470,18 @@ create_child:
        if (child && *own_req) {
                struct mptcp_subflow_context *ctx = mptcp_subflow_ctx(child);
 
-               /* we have null ctx on TCP fallback, which is fatal on
-                * MPJ handshake
+               /* we need to fallback on ctx allocation failure and on pre-reqs
+                * checking above. In the latter scenario we additionally need
+                * to reset the context to non MPTCP status.
                 */
-               if (!ctx) {
+               if (!ctx || fallback) {
                        if (fallback_is_fatal)
                                goto close_child;
+
+                       if (ctx) {
+                               subflow_ulp_fallback(child, ctx);
+                               kfree_rcu(ctx, rcu);
+                       }
                        goto out;
                }
 
@@ -422,10 +489,17 @@ create_child:
                        /* new mpc subflow takes ownership of the newly
                         * created mptcp socket
                         */
-                       inet_sk_state_store(new_msk, TCP_ESTABLISHED);
+                       new_msk->sk_destruct = mptcp_sock_destruct;
                        mptcp_pm_new_connection(mptcp_sk(new_msk), 1);
                        ctx->conn = new_msk;
                        new_msk = NULL;
+
+                       /* with OoO packets we can reach here without ingress
+                        * mpc option
+                        */
+                       ctx->remote_key = mp_opt.sndr_key;
+                       ctx->fully_established = mp_opt.mp_capable;
+                       ctx->can_ack = mp_opt.mp_capable;
                } else if (ctx->mp_join) {
                        struct mptcp_sock *owner;
 
@@ -444,7 +518,14 @@ create_child:
 out:
        /* dispose of the left over mptcp master, if any */
        if (unlikely(new_msk))
-               sock_put(new_msk);
+               mptcp_force_close(new_msk);
+
+       /* check for expected invariant - should never trigger, just help
+        * catching eariler subtle bugs
+        */
+       WARN_ON_ONCE(child && *own_req && tcp_sk(child)->is_mptcp &&
+                    (!mptcp_subflow_ctx(child) ||
+                     !mptcp_subflow_ctx(child)->conn));
        return child;
 
 close_child:
@@ -764,6 +845,24 @@ bool mptcp_subflow_data_available(struct sock *sk)
        return subflow->data_avail;
 }
 
+/* If ssk has an mptcp parent socket, use the mptcp rcvbuf occupancy,
+ * not the ssk one.
+ *
+ * In mptcp, rwin is about the mptcp-level connection data.
+ *
+ * Data that is still on the ssk rx queue can thus be ignored,
+ * as far as mptcp peer is concerened that data is still inflight.
+ * DSS ACK is updated when skb is moved to the mptcp rx queue.
+ */
+void mptcp_space(const struct sock *ssk, int *space, int *full_space)
+{
+       const struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
+       const struct sock *sk = subflow->conn;
+
+       *space = tcp_space(sk);
+       *full_space = tcp_full_space(sk);
+}
+
 static void subflow_data_ready(struct sock *sk)
 {
        struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
@@ -1047,17 +1146,6 @@ static void subflow_ulp_release(struct sock *sk)
        kfree_rcu(ctx, rcu);
 }
 
-static void subflow_ulp_fallback(struct sock *sk,
-                                struct mptcp_subflow_context *old_ctx)
-{
-       struct inet_connection_sock *icsk = inet_csk(sk);
-
-       mptcp_subflow_tcp_fallback(sk, old_ctx);
-       icsk->icsk_ulp_ops = NULL;
-       rcu_assign_pointer(icsk->icsk_ulp_data, NULL);
-       tcp_sk(sk)->is_mptcp = 0;
-}
-
 static void subflow_ulp_clone(const struct request_sock *req,
                              struct sock *newsk,
                              const gfp_t priority)
@@ -1091,9 +1179,6 @@ static void subflow_ulp_clone(const struct request_sock *req,
                 * is fully established only after we receive the remote key
                 */
                new_ctx->mp_capable = 1;
-               new_ctx->fully_established = subflow_req->remote_key_valid;
-               new_ctx->can_ack = subflow_req->remote_key_valid;
-               new_ctx->remote_key = subflow_req->remote_key;
                new_ctx->local_key = subflow_req->local_key;
                new_ctx->token = subflow_req->token;
                new_ctx->ssn_offset = subflow_req->ssn_offset;
index 468fea1..3a3915d 100644 (file)
@@ -1043,7 +1043,7 @@ config NETFILTER_XT_TARGET_TPROXY
          on Netfilter connection tracking and NAT, unlike REDIRECT.
          For it to work you will have to configure certain iptables rules
          and use policy routing. For more information on how to set it up
-         see Documentation/networking/tproxy.txt.
+         see Documentation/networking/tproxy.rst.
 
          To compile it as a module, choose M here.  If unsure, say N.
 
index 8dd1758..340cb95 100644 (file)
@@ -86,7 +86,8 @@ find_set_type(const char *name, u8 family, u8 revision)
 {
        struct ip_set_type *type;
 
-       list_for_each_entry_rcu(type, &ip_set_type_list, list)
+       list_for_each_entry_rcu(type, &ip_set_type_list, list,
+                               lockdep_is_held(&ip_set_type_mutex))
                if (STRNCMP(type->name, name) &&
                    (type->family == family ||
                     type->family == NFPROTO_UNSPEC) &&
index 8d14a1a..412656c 100644 (file)
@@ -1736,7 +1736,7 @@ static int three = 3;
 
 static int
 proc_do_defense_mode(struct ctl_table *table, int write,
-                    void __user *buffer, size_t *lenp, loff_t *ppos)
+                    void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct netns_ipvs *ipvs = table->extra2;
        int *valp = table->data;
@@ -1763,7 +1763,7 @@ proc_do_defense_mode(struct ctl_table *table, int write,
 
 static int
 proc_do_sync_threshold(struct ctl_table *table, int write,
-                      void __user *buffer, size_t *lenp, loff_t *ppos)
+                      void *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = table->data;
        int val[2];
@@ -1788,7 +1788,7 @@ proc_do_sync_threshold(struct ctl_table *table, int write,
 
 static int
 proc_do_sync_ports(struct ctl_table *table, int write,
-                  void __user *buffer, size_t *lenp, loff_t *ppos)
+                  void *buffer, size_t *lenp, loff_t *ppos)
 {
        int *valp = table->data;
        int val = *valp;
index 9b57330..6a26299 100644 (file)
@@ -348,7 +348,9 @@ static int ct_seq_show(struct seq_file *s, void *v)
        if (seq_print_acct(s, ct, IP_CT_DIR_REPLY))
                goto release;
 
-       if (test_bit(IPS_OFFLOAD_BIT, &ct->status))
+       if (test_bit(IPS_HW_OFFLOAD_BIT, &ct->status))
+               seq_puts(s, "[HW_OFFLOAD] ");
+       else if (test_bit(IPS_OFFLOAD_BIT, &ct->status))
                seq_puts(s, "[OFFLOAD] ");
        else if (test_bit(IPS_ASSURED_BIT, &ct->status))
                seq_puts(s, "[ASSURED] ");
@@ -517,7 +519,7 @@ static unsigned int nf_conntrack_htable_size_user __read_mostly;
 
 static int
 nf_conntrack_hash_sysctl(struct ctl_table *table, int write,
-                        void __user *buffer, size_t *lenp, loff_t *ppos)
+                        void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
 
index c0cb794..4344e57 100644 (file)
@@ -421,10 +421,12 @@ void nf_flow_table_offload_del_cb(struct nf_flowtable *flow_table,
 
        down_write(&flow_table->flow_block_lock);
        block_cb = flow_block_cb_lookup(block, cb, cb_priv);
-       if (block_cb)
+       if (block_cb) {
                list_del(&block_cb->list);
-       else
+               flow_block_cb_free(block_cb);
+       } else {
                WARN_ON(true);
+       }
        up_write(&flow_table->flow_block_lock);
 }
 EXPORT_SYMBOL_GPL(nf_flow_table_offload_del_cb);
index e3b099c..a2abb0f 100644 (file)
@@ -754,12 +754,15 @@ static void flow_offload_work_add(struct flow_offload_work *offload)
        err = flow_offload_rule_add(offload, flow_rule);
        if (err < 0)
                set_bit(NF_FLOW_HW_REFRESH, &offload->flow->flags);
+       else
+               set_bit(IPS_HW_OFFLOAD_BIT, &offload->flow->ct->status);
 
        nf_flow_offload_destroy(flow_rule);
 }
 
 static void flow_offload_work_del(struct flow_offload_work *offload)
 {
+       clear_bit(IPS_HW_OFFLOAD_BIT, &offload->flow->ct->status);
        flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_ORIGINAL);
        flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY);
        set_bit(NF_FLOW_HW_DEAD, &offload->flow->flags);
index bb25d4c..6cb9f94 100644 (file)
@@ -414,7 +414,7 @@ static struct ctl_table nf_log_sysctl_ftable[] = {
 };
 
 static int nf_log_proc_dostring(struct ctl_table *table, int write,
-                        void __user *buffer, size_t *lenp, loff_t *ppos)
+                        void *buffer, size_t *lenp, loff_t *ppos)
 {
        const struct nf_logger *logger;
        char buf[NFLOGGER_NAME_LEN];
index 64eedc1..59151dc 100644 (file)
@@ -68,15 +68,13 @@ static bool udp_manip_pkt(struct sk_buff *skb,
                          enum nf_nat_manip_type maniptype)
 {
        struct udphdr *hdr;
-       bool do_csum;
 
        if (skb_ensure_writable(skb, hdroff + sizeof(*hdr)))
                return false;
 
        hdr = (struct udphdr *)(skb->data + hdroff);
-       do_csum = hdr->check || skb->ip_summed == CHECKSUM_PARTIAL;
+       __udp_manip_pkt(skb, iphdroff, hdr, tuple, maniptype, !!hdr->check);
 
-       __udp_manip_pkt(skb, iphdroff, hdr, tuple, maniptype, do_csum);
        return true;
 }
 
@@ -1035,8 +1033,8 @@ int nf_nat_inet_register_fn(struct net *net, const struct nf_hook_ops *ops)
        ret = nf_nat_register_fn(net, NFPROTO_IPV4, ops, nf_nat_ipv4_ops,
                                 ARRAY_SIZE(nf_nat_ipv4_ops));
        if (ret)
-               nf_nat_ipv6_unregister_fn(net, ops);
-
+               nf_nat_unregister_fn(net, NFPROTO_IPV6, ops,
+                                       ARRAY_SIZE(nf_nat_ipv6_ops));
        return ret;
 }
 EXPORT_SYMBOL_GPL(nf_nat_inet_register_fn);
index 4471393..3558e76 100644 (file)
@@ -3542,6 +3542,7 @@ cont:
                        continue;
                if (!strcmp(set->name, i->name)) {
                        kfree(set->name);
+                       set->name = NULL;
                        return -ENFILE;
                }
        }
@@ -3961,8 +3962,8 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
                if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
                              NFT_SET_INTERVAL | NFT_SET_TIMEOUT |
                              NFT_SET_MAP | NFT_SET_EVAL |
-                             NFT_SET_OBJECT))
-                       return -EINVAL;
+                             NFT_SET_OBJECT | NFT_SET_CONCAT))
+                       return -EOPNOTSUPP;
                /* Only one of these operations is supported */
                if ((flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ==
                             (NFT_SET_MAP | NFT_SET_OBJECT))
@@ -4000,7 +4001,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
                objtype = ntohl(nla_get_be32(nla[NFTA_SET_OBJ_TYPE]));
                if (objtype == NFT_OBJECT_UNSPEC ||
                    objtype > NFT_OBJECT_MAX)
-                       return -EINVAL;
+                       return -EOPNOTSUPP;
        } else if (flags & NFT_SET_OBJECT)
                return -EINVAL;
        else
@@ -4668,6 +4669,25 @@ static int nft_setelem_parse_key(struct nft_ctx *ctx, struct nft_set *set,
        return 0;
 }
 
+static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set,
+                                 struct nft_data_desc *desc,
+                                 struct nft_data *data,
+                                 struct nlattr *attr)
+{
+       int err;
+
+       err = nft_data_init(ctx, data, NFT_DATA_VALUE_MAXLEN, desc, attr);
+       if (err < 0)
+               return err;
+
+       if (desc->type != NFT_DATA_VERDICT && desc->len != set->dlen) {
+               nft_data_release(data, desc->type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                            const struct nlattr *attr)
 {
@@ -4945,7 +4965,6 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        struct nft_expr *expr = NULL;
        struct nft_userdata *udata;
        struct nft_data_desc desc;
-       struct nft_data data;
        enum nft_registers dreg;
        struct nft_trans *trans;
        u32 flags = 0;
@@ -5071,15 +5090,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        }
 
        if (nla[NFTA_SET_ELEM_DATA] != NULL) {
-               err = nft_data_init(ctx, &data, sizeof(data), &desc,
-                                   nla[NFTA_SET_ELEM_DATA]);
+               err = nft_setelem_parse_data(ctx, set, &desc, &elem.data.val,
+                                            nla[NFTA_SET_ELEM_DATA]);
                if (err < 0)
                        goto err_parse_key_end;
 
-               err = -EINVAL;
-               if (set->dtype != NFT_DATA_VERDICT && desc.len != set->dlen)
-                       goto err_parse_data;
-
                dreg = nft_type_to_reg(set->dtype);
                list_for_each_entry(binding, &set->bindings, list) {
                        struct nft_ctx bind_ctx = {
@@ -5093,14 +5108,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                                continue;
 
                        err = nft_validate_register_store(&bind_ctx, dreg,
-                                                         &data,
+                                                         &elem.data.val,
                                                          desc.type, desc.len);
                        if (err < 0)
                                goto err_parse_data;
 
                        if (desc.type == NFT_DATA_VERDICT &&
-                           (data.verdict.code == NFT_GOTO ||
-                            data.verdict.code == NFT_JUMP))
+                           (elem.data.val.verdict.code == NFT_GOTO ||
+                            elem.data.val.verdict.code == NFT_JUMP))
                                nft_validate_state_update(ctx->net,
                                                          NFT_VALIDATE_NEED);
                }
@@ -5122,7 +5137,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
 
        err = -ENOMEM;
        elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
-                                     elem.key_end.val.data, data.data,
+                                     elem.key_end.val.data, elem.data.val.data,
                                      timeout, expiration, GFP_KERNEL);
        if (elem.priv == NULL)
                goto err_parse_data;
@@ -5200,7 +5215,7 @@ err_trans:
        nf_tables_set_elem_destroy(ctx, set, elem.priv);
 err_parse_data:
        if (nla[NFTA_SET_ELEM_DATA] != NULL)
-               nft_data_release(&data, desc.type);
+               nft_data_release(&elem.data.val, desc.type);
 err_parse_key_end:
        nft_data_release(&elem.key_end.val, NFT_DATA_VALUE);
 err_parse_key:
index 9f5dea0..916a3c7 100644 (file)
@@ -165,12 +165,12 @@ static bool nf_osf_match_one(const struct sk_buff *skb,
 static const struct tcphdr *nf_osf_hdr_ctx_init(struct nf_osf_hdr_ctx *ctx,
                                                const struct sk_buff *skb,
                                                const struct iphdr *ip,
-                                               unsigned char *opts)
+                                               unsigned char *opts,
+                                               struct tcphdr *_tcph)
 {
        const struct tcphdr *tcp;
-       struct tcphdr _tcph;
 
-       tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph);
+       tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), _tcph);
        if (!tcp)
                return NULL;
 
@@ -205,10 +205,11 @@ nf_osf_match(const struct sk_buff *skb, u_int8_t family,
        int fmatch = FMATCH_WRONG;
        struct nf_osf_hdr_ctx ctx;
        const struct tcphdr *tcp;
+       struct tcphdr _tcph;
 
        memset(&ctx, 0, sizeof(ctx));
 
-       tcp = nf_osf_hdr_ctx_init(&ctx, skb, ip, opts);
+       tcp = nf_osf_hdr_ctx_init(&ctx, skb, ip, opts, &_tcph);
        if (!tcp)
                return false;
 
@@ -265,10 +266,11 @@ bool nf_osf_find(const struct sk_buff *skb,
        const struct nf_osf_finger *kf;
        struct nf_osf_hdr_ctx ctx;
        const struct tcphdr *tcp;
+       struct tcphdr _tcph;
 
        memset(&ctx, 0, sizeof(ctx));
 
-       tcp = nf_osf_hdr_ctx_init(&ctx, skb, ip, opts);
+       tcp = nf_osf_hdr_ctx_init(&ctx, skb, ip, opts, &_tcph);
        if (!tcp)
                return false;
 
index 1e70359..f1363b8 100644 (file)
@@ -29,7 +29,7 @@ void nft_lookup_eval(const struct nft_expr *expr,
 {
        const struct nft_lookup *priv = nft_expr_priv(expr);
        const struct nft_set *set = priv->set;
-       const struct nft_set_ext *ext;
+       const struct nft_set_ext *ext = NULL;
        bool found;
 
        found = set->ops->lookup(nft_net(pkt), set, &regs->data[priv->sreg],
@@ -39,11 +39,13 @@ void nft_lookup_eval(const struct nft_expr *expr,
                return;
        }
 
-       if (set->flags & NFT_SET_MAP)
-               nft_data_copy(&regs->data[priv->dreg],
-                             nft_set_ext_data(ext), set->dlen);
+       if (ext) {
+               if (set->flags & NFT_SET_MAP)
+                       nft_data_copy(&regs->data[priv->dreg],
+                                     nft_set_ext_data(ext), set->dlen);
 
-       nft_set_elem_update_expr(ext, regs, pkt);
+               nft_set_elem_update_expr(ext, regs, pkt);
+       }
 }
 
 static const struct nla_policy nft_lookup_policy[NFTA_LOOKUP_MAX + 1] = {
index 8b44a4d..23a7bfd 100644 (file)
@@ -30,6 +30,76 @@ struct nft_nat {
        u16                     flags;
 };
 
+static void nft_nat_setup_addr(struct nf_nat_range2 *range,
+                              const struct nft_regs *regs,
+                              const struct nft_nat *priv)
+{
+       switch (priv->family) {
+       case AF_INET:
+               range->min_addr.ip = (__force __be32)
+                               regs->data[priv->sreg_addr_min];
+               range->max_addr.ip = (__force __be32)
+                               regs->data[priv->sreg_addr_max];
+               break;
+       case AF_INET6:
+               memcpy(range->min_addr.ip6, &regs->data[priv->sreg_addr_min],
+                      sizeof(range->min_addr.ip6));
+               memcpy(range->max_addr.ip6, &regs->data[priv->sreg_addr_max],
+                      sizeof(range->max_addr.ip6));
+               break;
+       }
+}
+
+static void nft_nat_setup_proto(struct nf_nat_range2 *range,
+                               const struct nft_regs *regs,
+                               const struct nft_nat *priv)
+{
+       range->min_proto.all = (__force __be16)
+               nft_reg_load16(&regs->data[priv->sreg_proto_min]);
+       range->max_proto.all = (__force __be16)
+               nft_reg_load16(&regs->data[priv->sreg_proto_max]);
+}
+
+static void nft_nat_setup_netmap(struct nf_nat_range2 *range,
+                                const struct nft_pktinfo *pkt,
+                                const struct nft_nat *priv)
+{
+       struct sk_buff *skb = pkt->skb;
+       union nf_inet_addr new_addr;
+       __be32 netmask;
+       int i, len = 0;
+
+       switch (priv->type) {
+       case NFT_NAT_SNAT:
+               if (nft_pf(pkt) == NFPROTO_IPV4) {
+                       new_addr.ip = ip_hdr(skb)->saddr;
+                       len = sizeof(struct in_addr);
+               } else {
+                       new_addr.in6 = ipv6_hdr(skb)->saddr;
+                       len = sizeof(struct in6_addr);
+               }
+               break;
+       case NFT_NAT_DNAT:
+               if (nft_pf(pkt) == NFPROTO_IPV4) {
+                       new_addr.ip = ip_hdr(skb)->daddr;
+                       len = sizeof(struct in_addr);
+               } else {
+                       new_addr.in6 = ipv6_hdr(skb)->daddr;
+                       len = sizeof(struct in6_addr);
+               }
+               break;
+       }
+
+       for (i = 0; i < len / sizeof(__be32); i++) {
+               netmask = ~(range->min_addr.ip6[i] ^ range->max_addr.ip6[i]);
+               new_addr.ip6[i] &= ~netmask;
+               new_addr.ip6[i] |= range->min_addr.ip6[i] & netmask;
+       }
+
+       range->min_addr = new_addr;
+       range->max_addr = new_addr;
+}
+
 static void nft_nat_eval(const struct nft_expr *expr,
                         struct nft_regs *regs,
                         const struct nft_pktinfo *pkt)
@@ -40,33 +110,17 @@ static void nft_nat_eval(const struct nft_expr *expr,
        struct nf_nat_range2 range;
 
        memset(&range, 0, sizeof(range));
-       if (priv->sreg_addr_min) {
-               if (priv->family == AF_INET) {
-                       range.min_addr.ip = (__force __be32)
-                                       regs->data[priv->sreg_addr_min];
-                       range.max_addr.ip = (__force __be32)
-                                       regs->data[priv->sreg_addr_max];
 
-               } else {
-                       memcpy(range.min_addr.ip6,
-                              &regs->data[priv->sreg_addr_min],
-                              sizeof(range.min_addr.ip6));
-                       memcpy(range.max_addr.ip6,
-                              &regs->data[priv->sreg_addr_max],
-                              sizeof(range.max_addr.ip6));
-               }
-               range.flags |= NF_NAT_RANGE_MAP_IPS;
+       if (priv->sreg_addr_min) {
+               nft_nat_setup_addr(&range, regs, priv);
+               if (priv->flags & NF_NAT_RANGE_NETMAP)
+                       nft_nat_setup_netmap(&range, pkt, priv);
        }
 
-       if (priv->sreg_proto_min) {
-               range.min_proto.all = (__force __be16)nft_reg_load16(
-                       &regs->data[priv->sreg_proto_min]);
-               range.max_proto.all = (__force __be16)nft_reg_load16(
-                       &regs->data[priv->sreg_proto_max]);
-               range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
-       }
+       if (priv->sreg_proto_min)
+               nft_nat_setup_proto(&range, regs, priv);
 
-       range.flags |= priv->flags;
+       range.flags = priv->flags;
 
        regs->verdict.code = nf_nat_setup_info(ct, &range, priv->type);
 }
@@ -129,7 +183,7 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
                priv->type = NF_NAT_MANIP_DST;
                break;
        default:
-               return -EINVAL;
+               return -EOPNOTSUPP;
        }
 
        if (tb[NFTA_NAT_FAMILY] == NULL)
@@ -169,6 +223,8 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
                } else {
                        priv->sreg_addr_max = priv->sreg_addr_min;
                }
+
+               priv->flags |= NF_NAT_RANGE_MAP_IPS;
        }
 
        plen = sizeof_field(struct nf_nat_range, min_addr.all);
@@ -191,12 +247,14 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
                } else {
                        priv->sreg_proto_max = priv->sreg_proto_min;
                }
+
+               priv->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
        }
 
        if (tb[NFTA_NAT_FLAGS]) {
-               priv->flags = ntohl(nla_get_be32(tb[NFTA_NAT_FLAGS]));
+               priv->flags |= ntohl(nla_get_be32(tb[NFTA_NAT_FLAGS]));
                if (priv->flags & ~NF_NAT_RANGE_MASK)
-                       return -EINVAL;
+                       return -EOPNOTSUPP;
        }
 
        return nf_ct_netns_get(ctx->net, family);
index 32f0fc8..2a81ea4 100644 (file)
@@ -81,7 +81,6 @@ static bool nft_bitmap_lookup(const struct net *net, const struct nft_set *set,
        u32 idx, off;
 
        nft_bitmap_location(set, key, &idx, &off);
-       *ext = NULL;
 
        return nft_bitmap_active(priv->bitmap, idx, off, genmask);
 }
index 3a5552e..3ffef45 100644 (file)
@@ -218,27 +218,26 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
 
        /* Detect overlaps as we descend the tree. Set the flag in these cases:
         *
-        * a1. |__ _ _?  >|__ _ _  (insert start after existing start)
-        * a2. _ _ __>|  ?_ _ __|  (insert end before existing end)
-        * a3. _ _ ___|  ?_ _ _>|  (insert end after existing end)
-        * a4. >|__ _ _   _ _ __|  (insert start before existing end)
+        * a1. _ _ __>|  ?_ _ __|  (insert end before existing end)
+        * a2. _ _ ___|  ?_ _ _>|  (insert end after existing end)
+        * a3. _ _ ___? >|_ _ __|  (insert start before existing end)
         *
         * and clear it later on, as we eventually reach the points indicated by
         * '?' above, in the cases described below. We'll always meet these
         * later, locally, due to tree ordering, and overlaps for the intervals
         * that are the closest together are always evaluated last.
         *
-        * b1. |__ _ _!  >|__ _ _  (insert start after existing end)
-        * b2. _ _ __>|  !_ _ __|  (insert end before existing start)
-        * b3. !_____>|            (insert end after existing start)
+        * b1. _ _ __>|  !_ _ __|  (insert end before existing start)
+        * b2. _ _ ___|  !_ _ _>|  (insert end after existing start)
+        * b3. _ _ ___! >|_ _ __|  (insert start after existing end)
         *
-        * Case a4. resolves to b1.:
+        * Case a3. resolves to b3.:
         * - if the inserted start element is the leftmost, because the '0'
         *   element in the tree serves as end element
         * - otherwise, if an existing end is found. Note that end elements are
         *   always inserted after corresponding start elements.
         *
-        * For a new, rightmost pair of elements, we'll hit cases b1. and b3.,
+        * For a new, rightmost pair of elements, we'll hit cases b3. and b2.,
         * in that order.
         *
         * The flag is also cleared in two special cases:
@@ -262,9 +261,9 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
                        p = &parent->rb_left;
 
                        if (nft_rbtree_interval_start(new)) {
-                               overlap = nft_rbtree_interval_start(rbe) &&
-                                         nft_set_elem_active(&rbe->ext,
-                                                             genmask);
+                               if (nft_rbtree_interval_end(rbe) &&
+                                   nft_set_elem_active(&rbe->ext, genmask))
+                                       overlap = false;
                        } else {
                                overlap = nft_rbtree_interval_end(rbe) &&
                                          nft_set_elem_active(&rbe->ext,
index 75bd0e5..7b2f359 100644 (file)
@@ -346,6 +346,9 @@ static int idletimer_tg_checkentry_v1(const struct xt_tgchk_param *par)
 
        pr_debug("checkentry targinfo%s\n", info->label);
 
+       if (info->send_nl_msg)
+               return -EOPNOTSUPP;
+
        ret = idletimer_tg_helper((struct idletimer_tg_info *)info);
        if(ret < 0)
        {
index 64280a1..07b03c3 100644 (file)
@@ -14,6 +14,6 @@ config NETLABEL
          Documentation/netlabel as well as the NetLabel SourceForge project
          for configuration tools and additional documentation.
 
-          * http://netlabel.sf.net
+          * https://github.com/netlabel/netlabel_tools
 
          If you are unsure, say N.
index de42df7..e052027 100644 (file)
@@ -3,7 +3,7 @@
 # Makefile for the netlink driver.
 #
 
-obj-y                                  := af_netlink.o genetlink.o
+obj-y                                  := af_netlink.o genetlink.o policy.o
 
 obj-$(CONFIG_NETLINK_DIAG)     += netlink_diag.o
 netlink_diag-y                 := diag.o
index 9f357aa..2f04969 100644 (file)
@@ -1043,6 +1043,80 @@ static int genl_ctrl_event(int event, const struct genl_family *family,
        return 0;
 }
 
+static int ctrl_dumppolicy(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       const struct genl_family *rt;
+       unsigned int fam_id = cb->args[0];
+       int err;
+
+       if (!fam_id) {
+               struct nlattr *tb[CTRL_ATTR_MAX + 1];
+
+               err = genlmsg_parse(cb->nlh, &genl_ctrl, tb,
+                                   genl_ctrl.maxattr,
+                                   genl_ctrl.policy, cb->extack);
+               if (err)
+                       return err;
+
+               if (!tb[CTRL_ATTR_FAMILY_ID] && !tb[CTRL_ATTR_FAMILY_NAME])
+                       return -EINVAL;
+
+               if (tb[CTRL_ATTR_FAMILY_ID]) {
+                       fam_id = nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
+               } else {
+                       rt = genl_family_find_byname(
+                               nla_data(tb[CTRL_ATTR_FAMILY_NAME]));
+                       if (!rt)
+                               return -ENOENT;
+                       fam_id = rt->id;
+               }
+       }
+
+       rt = genl_family_find_byid(fam_id);
+       if (!rt)
+               return -ENOENT;
+
+       if (!rt->policy)
+               return -ENODATA;
+
+       err = netlink_policy_dump_start(rt->policy, rt->maxattr, &cb->args[1]);
+       if (err)
+               return err;
+
+       while (netlink_policy_dump_loop(&cb->args[1])) {
+               void *hdr;
+               struct nlattr *nest;
+
+               hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+                                 cb->nlh->nlmsg_seq, &genl_ctrl,
+                                 NLM_F_MULTI, CTRL_CMD_GETPOLICY);
+               if (!hdr)
+                       goto nla_put_failure;
+
+               if (nla_put_u16(skb, CTRL_ATTR_FAMILY_ID, rt->id))
+                       goto nla_put_failure;
+
+               nest = nla_nest_start(skb, CTRL_ATTR_POLICY);
+               if (!nest)
+                       goto nla_put_failure;
+
+               if (netlink_policy_dump_write(skb, cb->args[1]))
+                       goto nla_put_failure;
+
+               nla_nest_end(skb, nest);
+
+               genlmsg_end(skb, hdr);
+               continue;
+
+nla_put_failure:
+               genlmsg_cancel(skb, hdr);
+               break;
+       }
+
+       cb->args[0] = fam_id;
+       return skb->len;
+}
+
 static const struct genl_ops genl_ctrl_ops[] = {
        {
                .cmd            = CTRL_CMD_GETFAMILY,
@@ -1050,6 +1124,10 @@ static const struct genl_ops genl_ctrl_ops[] = {
                .doit           = ctrl_getfamily,
                .dumpit         = ctrl_dumpfamily,
        },
+       {
+               .cmd            = CTRL_CMD_GETPOLICY,
+               .dumpit         = ctrl_dumppolicy,
+       },
 };
 
 static const struct genl_multicast_group genl_ctrl_groups[] = {
diff --git a/net/netlink/policy.c b/net/netlink/policy.c
new file mode 100644 (file)
index 0000000..f649185
--- /dev/null
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NETLINK      Policy advertisement to userspace
+ *
+ *             Authors:        Johannes Berg <johannes@sipsolutions.net>
+ *
+ * Copyright 2019 Intel Corporation
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <net/netlink.h>
+
+#define INITIAL_POLICIES_ALLOC 10
+
+struct nl_policy_dump {
+       unsigned int policy_idx;
+       unsigned int attr_idx;
+       unsigned int n_alloc;
+       struct {
+               const struct nla_policy *policy;
+               unsigned int maxtype;
+       } policies[];
+};
+
+static int add_policy(struct nl_policy_dump **statep,
+                     const struct nla_policy *policy,
+                     unsigned int maxtype)
+{
+       struct nl_policy_dump *state = *statep;
+       unsigned int n_alloc, i;
+
+       if (!policy || !maxtype)
+               return 0;
+
+       for (i = 0; i < state->n_alloc; i++) {
+               if (state->policies[i].policy == policy)
+                       return 0;
+
+               if (!state->policies[i].policy) {
+                       state->policies[i].policy = policy;
+                       state->policies[i].maxtype = maxtype;
+                       return 0;
+               }
+       }
+
+       n_alloc = state->n_alloc + INITIAL_POLICIES_ALLOC;
+       state = krealloc(state, struct_size(state, policies, n_alloc),
+                        GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       state->policies[state->n_alloc].policy = policy;
+       state->policies[state->n_alloc].maxtype = maxtype;
+       state->n_alloc = n_alloc;
+       *statep = state;
+
+       return 0;
+}
+
+static unsigned int get_policy_idx(struct nl_policy_dump *state,
+                                  const struct nla_policy *policy)
+{
+       unsigned int i;
+
+       for (i = 0; i < state->n_alloc; i++) {
+               if (state->policies[i].policy == policy)
+                       return i;
+       }
+
+       WARN_ON_ONCE(1);
+       return -1;
+}
+
+int netlink_policy_dump_start(const struct nla_policy *policy,
+                             unsigned int maxtype,
+                              unsigned long *_state)
+{
+       struct nl_policy_dump *state;
+       unsigned int policy_idx;
+       int err;
+
+       /* also returns 0 if "*_state" is our ERR_PTR() end marker */
+       if (*_state)
+               return 0;
+
+       /*
+        * walk the policies and nested ones first, and build
+        * a linear list of them.
+        */
+
+       state = kzalloc(struct_size(state, policies, INITIAL_POLICIES_ALLOC),
+                       GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+       state->n_alloc = INITIAL_POLICIES_ALLOC;
+
+       err = add_policy(&state, policy, maxtype);
+       if (err)
+               return err;
+
+       for (policy_idx = 0;
+            policy_idx < state->n_alloc && state->policies[policy_idx].policy;
+            policy_idx++) {
+               const struct nla_policy *policy;
+               unsigned int type;
+
+               policy = state->policies[policy_idx].policy;
+
+               for (type = 0;
+                    type <= state->policies[policy_idx].maxtype;
+                    type++) {
+                       switch (policy[type].type) {
+                       case NLA_NESTED:
+                       case NLA_NESTED_ARRAY:
+                               err = add_policy(&state,
+                                                policy[type].nested_policy,
+                                                policy[type].len);
+                               if (err)
+                                       return err;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
+
+       *_state = (unsigned long)state;
+
+       return 0;
+}
+
+static bool netlink_policy_dump_finished(struct nl_policy_dump *state)
+{
+       return state->policy_idx >= state->n_alloc ||
+              !state->policies[state->policy_idx].policy;
+}
+
+bool netlink_policy_dump_loop(unsigned long *_state)
+{
+       struct nl_policy_dump *state = (void *)*_state;
+
+       if (IS_ERR(state))
+               return false;
+
+       if (netlink_policy_dump_finished(state)) {
+               kfree(state);
+               /* store end marker instead of freed state */
+               *_state = (unsigned long)ERR_PTR(-ENOENT);
+               return false;
+       }
+
+       return true;
+}
+
+int netlink_policy_dump_write(struct sk_buff *skb, unsigned long _state)
+{
+       struct nl_policy_dump *state = (void *)_state;
+       const struct nla_policy *pt;
+       struct nlattr *policy, *attr;
+       enum netlink_attribute_type type;
+       bool again;
+
+send_attribute:
+       again = false;
+
+       pt = &state->policies[state->policy_idx].policy[state->attr_idx];
+
+       policy = nla_nest_start(skb, state->policy_idx);
+       if (!policy)
+               return -ENOBUFS;
+
+       attr = nla_nest_start(skb, state->attr_idx);
+       if (!attr)
+               goto nla_put_failure;
+
+       switch (pt->type) {
+       default:
+       case NLA_UNSPEC:
+       case NLA_REJECT:
+               /* skip - use NLA_MIN_LEN to advertise such */
+               nla_nest_cancel(skb, policy);
+               again = true;
+               goto next;
+       case NLA_NESTED:
+               type = NL_ATTR_TYPE_NESTED;
+               /* fall through */
+       case NLA_NESTED_ARRAY:
+               if (pt->type == NLA_NESTED_ARRAY)
+                       type = NL_ATTR_TYPE_NESTED_ARRAY;
+               if (pt->nested_policy && pt->len &&
+                   (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_IDX,
+                                get_policy_idx(state, pt->nested_policy)) ||
+                    nla_put_u32(skb, NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
+                                pt->len)))
+                       goto nla_put_failure;
+               break;
+       case NLA_U8:
+       case NLA_U16:
+       case NLA_U32:
+       case NLA_U64:
+       case NLA_MSECS: {
+               struct netlink_range_validation range;
+
+               if (pt->type == NLA_U8)
+                       type = NL_ATTR_TYPE_U8;
+               else if (pt->type == NLA_U16)
+                       type = NL_ATTR_TYPE_U16;
+               else if (pt->type == NLA_U32)
+                       type = NL_ATTR_TYPE_U32;
+               else
+                       type = NL_ATTR_TYPE_U64;
+
+               nla_get_range_unsigned(pt, &range);
+
+               if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_U,
+                                     range.min, NL_POLICY_TYPE_ATTR_PAD) ||
+                   nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_U,
+                                     range.max, NL_POLICY_TYPE_ATTR_PAD))
+                       goto nla_put_failure;
+               break;
+       }
+       case NLA_S8:
+       case NLA_S16:
+       case NLA_S32:
+       case NLA_S64: {
+               struct netlink_range_validation_signed range;
+
+               if (pt->type == NLA_S8)
+                       type = NL_ATTR_TYPE_S8;
+               else if (pt->type == NLA_S16)
+                       type = NL_ATTR_TYPE_S16;
+               else if (pt->type == NLA_S32)
+                       type = NL_ATTR_TYPE_S32;
+               else
+                       type = NL_ATTR_TYPE_S64;
+
+               nla_get_range_signed(pt, &range);
+
+               if (nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MIN_VALUE_S,
+                               range.min, NL_POLICY_TYPE_ATTR_PAD) ||
+                   nla_put_s64(skb, NL_POLICY_TYPE_ATTR_MAX_VALUE_S,
+                               range.max, NL_POLICY_TYPE_ATTR_PAD))
+                       goto nla_put_failure;
+               break;
+       }
+       case NLA_BITFIELD32:
+               type = NL_ATTR_TYPE_BITFIELD32;
+               if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
+                               pt->bitfield32_valid))
+                       goto nla_put_failure;
+               break;
+       case NLA_EXACT_LEN:
+               type = NL_ATTR_TYPE_BINARY;
+               if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH, pt->len) ||
+                   nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH, pt->len))
+                       goto nla_put_failure;
+               break;
+       case NLA_STRING:
+       case NLA_NUL_STRING:
+       case NLA_BINARY:
+               if (pt->type == NLA_STRING)
+                       type = NL_ATTR_TYPE_STRING;
+               else if (pt->type == NLA_NUL_STRING)
+                       type = NL_ATTR_TYPE_NUL_STRING;
+               else
+                       type = NL_ATTR_TYPE_BINARY;
+               if (pt->len && nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MAX_LENGTH,
+                                          pt->len))
+                       goto nla_put_failure;
+               break;
+       case NLA_MIN_LEN:
+               type = NL_ATTR_TYPE_BINARY;
+               if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_MIN_LENGTH, pt->len))
+                       goto nla_put_failure;
+               break;
+       case NLA_FLAG:
+               type = NL_ATTR_TYPE_FLAG;
+               break;
+       }
+
+       if (nla_put_u32(skb, NL_POLICY_TYPE_ATTR_TYPE, type))
+               goto nla_put_failure;
+
+       /* finish and move state to next attribute */
+       nla_nest_end(skb, attr);
+       nla_nest_end(skb, policy);
+
+next:
+       state->attr_idx += 1;
+       if (state->attr_idx > state->policies[state->policy_idx].maxtype) {
+               state->attr_idx = 0;
+               state->policy_idx++;
+       }
+
+       if (again) {
+               if (netlink_policy_dump_finished(state))
+                       return -ENODATA;
+               goto send_attribute;
+       }
+
+       return 0;
+
+nla_put_failure:
+       nla_nest_cancel(skb, policy);
+       return -ENOBUFS;
+}
index 7b1a74f..eccc7d3 100644 (file)
@@ -63,6 +63,26 @@ static DEFINE_SPINLOCK(nr_list_lock);
 
 static const struct proto_ops nr_proto_ops;
 
+/*
+ * NETROM network devices are virtual network devices encapsulating NETROM
+ * frames into AX.25 which will be sent through an AX.25 device, so form a
+ * special "super class" of normal net devices; split their locks off into a
+ * separate class since they always nest.
+ */
+static struct lock_class_key nr_netdev_xmit_lock_key;
+
+static void nr_set_lockdep_one(struct net_device *dev,
+                              struct netdev_queue *txq,
+                              void *_unused)
+{
+       lockdep_set_class(&txq->_xmit_lock, &nr_netdev_xmit_lock_key);
+}
+
+static void nr_set_lockdep_key(struct net_device *dev)
+{
+       netdev_for_each_tx_queue(dev, nr_set_lockdep_one, NULL);
+}
+
 /*
  *     Socket removal during an interrupt is now safe.
  */
@@ -1394,6 +1414,7 @@ static int __init nr_proto_init(void)
                        free_netdev(dev);
                        goto fail;
                }
+               nr_set_lockdep_key(dev);
                dev_nr[i] = dev;
        }
 
index 79f12d8..0891ee0 100644 (file)
@@ -208,6 +208,7 @@ static int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
                /* refcount initialized at 1 */
                spin_unlock_bh(&nr_node_list_lock);
 
+               nr_neigh_put(nr_neigh);
                return 0;
        }
        nr_node_lock(nr_node);
index e726159..4340f25 100644 (file)
@@ -1895,7 +1895,8 @@ static void ovs_ct_limit_exit(struct net *net, struct ovs_net *ovs_net)
                struct hlist_head *head = &info->limits[i];
                struct ovs_ct_limit *ct_limit;
 
-               hlist_for_each_entry_rcu(ct_limit, head, hlist_node)
+               hlist_for_each_entry_rcu(ct_limit, head, hlist_node,
+                                        lockdep_ovsl_is_held())
                        kfree_rcu(ct_limit, rcu);
        }
        kfree(ovs_net->ct_limit_info->limits);
index d8ae541..94b0245 100644 (file)
@@ -2466,8 +2466,10 @@ static void __net_exit ovs_exit_net(struct net *dnet)
        struct net *net;
        LIST_HEAD(head);
 
-       ovs_ct_exit(dnet);
        ovs_lock();
+
+       ovs_ct_exit(dnet);
+
        list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node)
                __dp_destroy(dp);
 
index e239a46..2016dd1 100644 (file)
@@ -82,7 +82,7 @@ struct datapath {
        u32 max_headroom;
 
        /* Switch meters. */
-       struct hlist_head *meters;
+       struct dp_meter_table meter_tbl;
 };
 
 /**
index 5010d1d..3d3d8e0 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/openvswitch.h>
 #include <linux/netlink.h>
 #include <linux/rculist.h>
+#include <linux/swap.h>
 
 #include <net/netlink.h>
 #include <net/genetlink.h>
@@ -19,8 +20,6 @@
 #include "datapath.h"
 #include "meter.h"
 
-#define METER_HASH_BUCKETS 1024
-
 static const struct nla_policy meter_policy[OVS_METER_ATTR_MAX + 1] = {
        [OVS_METER_ATTR_ID] = { .type = NLA_U32, },
        [OVS_METER_ATTR_KBPS] = { .type = NLA_FLAG },
@@ -39,6 +38,11 @@ static const struct nla_policy band_policy[OVS_BAND_ATTR_MAX + 1] = {
        [OVS_BAND_ATTR_STATS] = { .len = sizeof(struct ovs_flow_stats) },
 };
 
+static u32 meter_hash(struct dp_meter_instance *ti, u32 id)
+{
+       return id % ti->n_meters;
+}
+
 static void ovs_meter_free(struct dp_meter *meter)
 {
        if (!meter)
@@ -47,40 +51,162 @@ static void ovs_meter_free(struct dp_meter *meter)
        kfree_rcu(meter, rcu);
 }
 
-static struct hlist_head *meter_hash_bucket(const struct datapath *dp,
-                                           u32 meter_id)
-{
-       return &dp->meters[meter_id & (METER_HASH_BUCKETS - 1)];
-}
-
 /* Call with ovs_mutex or RCU read lock. */
-static struct dp_meter *lookup_meter(const struct datapath *dp,
+static struct dp_meter *lookup_meter(const struct dp_meter_table *tbl,
                                     u32 meter_id)
 {
+       struct dp_meter_instance *ti = rcu_dereference_ovsl(tbl->ti);
+       u32 hash = meter_hash(ti, meter_id);
        struct dp_meter *meter;
-       struct hlist_head *head;
 
-       head = meter_hash_bucket(dp, meter_id);
-       hlist_for_each_entry_rcu(meter, head, dp_hash_node,
-                               lockdep_ovsl_is_held()) {
-               if (meter->id == meter_id)
-                       return meter;
-       }
+       meter = rcu_dereference_ovsl(ti->dp_meters[hash]);
+       if (meter && likely(meter->id == meter_id))
+               return meter;
+
        return NULL;
 }
 
-static void attach_meter(struct datapath *dp, struct dp_meter *meter)
+static struct dp_meter_instance *dp_meter_instance_alloc(const u32 size)
+{
+       struct dp_meter_instance *ti;
+
+       ti = kvzalloc(sizeof(*ti) +
+                     sizeof(struct dp_meter *) * size,
+                     GFP_KERNEL);
+       if (!ti)
+               return NULL;
+
+       ti->n_meters = size;
+
+       return ti;
+}
+
+static void dp_meter_instance_free(struct dp_meter_instance *ti)
+{
+       kvfree(ti);
+}
+
+static void dp_meter_instance_free_rcu(struct rcu_head *rcu)
 {
-       struct hlist_head *head = meter_hash_bucket(dp, meter->id);
+       struct dp_meter_instance *ti;
 
-       hlist_add_head_rcu(&meter->dp_hash_node, head);
+       ti = container_of(rcu, struct dp_meter_instance, rcu);
+       kvfree(ti);
 }
 
-static void detach_meter(struct dp_meter *meter)
+static int
+dp_meter_instance_realloc(struct dp_meter_table *tbl, u32 size)
+{
+       struct dp_meter_instance *ti = rcu_dereference_ovsl(tbl->ti);
+       int n_meters = min(size, ti->n_meters);
+       struct dp_meter_instance *new_ti;
+       int i;
+
+       new_ti = dp_meter_instance_alloc(size);
+       if (!new_ti)
+               return -ENOMEM;
+
+       for (i = 0; i < n_meters; i++)
+               if (rcu_dereference_ovsl(ti->dp_meters[i]))
+                       new_ti->dp_meters[i] = ti->dp_meters[i];
+
+       rcu_assign_pointer(tbl->ti, new_ti);
+       call_rcu(&ti->rcu, dp_meter_instance_free_rcu);
+
+       return 0;
+}
+
+static void dp_meter_instance_insert(struct dp_meter_instance *ti,
+                                    struct dp_meter *meter)
+{
+       u32 hash;
+
+       hash = meter_hash(ti, meter->id);
+       rcu_assign_pointer(ti->dp_meters[hash], meter);
+}
+
+static void dp_meter_instance_remove(struct dp_meter_instance *ti,
+                                    struct dp_meter *meter)
 {
+       u32 hash;
+
+       hash = meter_hash(ti, meter->id);
+       RCU_INIT_POINTER(ti->dp_meters[hash], NULL);
+}
+
+static int attach_meter(struct dp_meter_table *tbl, struct dp_meter *meter)
+{
+       struct dp_meter_instance *ti = rcu_dereference_ovsl(tbl->ti);
+       u32 hash = meter_hash(ti, meter->id);
+       int err;
+
+       /* In generally, slots selected should be empty, because
+        * OvS uses id-pool to fetch a available id.
+        */
+       if (unlikely(rcu_dereference_ovsl(ti->dp_meters[hash])))
+               return -EBUSY;
+
+       dp_meter_instance_insert(ti, meter);
+
+       /* That function is thread-safe. */
+       tbl->count++;
+       if (tbl->count >= tbl->max_meters_allowed) {
+               err = -EFBIG;
+               goto attach_err;
+       }
+
+       if (tbl->count >= ti->n_meters &&
+           dp_meter_instance_realloc(tbl, ti->n_meters * 2)) {
+               err = -ENOMEM;
+               goto attach_err;
+       }
+
+       return 0;
+
+attach_err:
+       dp_meter_instance_remove(ti, meter);
+       tbl->count--;
+       return err;
+}
+
+static int detach_meter(struct dp_meter_table *tbl, struct dp_meter *meter)
+{
+       struct dp_meter_instance *ti;
+
        ASSERT_OVSL();
-       if (meter)
-               hlist_del_rcu(&meter->dp_hash_node);
+       if (!meter)
+               return 0;
+
+       ti = rcu_dereference_ovsl(tbl->ti);
+       dp_meter_instance_remove(ti, meter);
+
+       tbl->count--;
+
+       /* Shrink the meter array if necessary. */
+       if (ti->n_meters > DP_METER_ARRAY_SIZE_MIN &&
+           tbl->count <= (ti->n_meters / 4)) {
+               int half_size = ti->n_meters / 2;
+               int i;
+
+               /* Avoid hash collision, don't move slots to other place.
+                * Make sure there are no references of meters in array
+                * which will be released.
+                */
+               for (i = half_size; i < ti->n_meters; i++)
+                       if (rcu_dereference_ovsl(ti->dp_meters[i]))
+                               goto out;
+
+               if (dp_meter_instance_realloc(tbl, half_size))
+                       goto shrink_err;
+       }
+
+out:
+       return 0;
+
+shrink_err:
+       dp_meter_instance_insert(ti, meter);
+       tbl->count++;
+       return -ENOMEM;
 }
 
 static struct sk_buff *
@@ -116,12 +242,11 @@ static int ovs_meter_cmd_reply_stats(struct sk_buff *reply, u32 meter_id,
        if (nla_put_u32(reply, OVS_METER_ATTR_ID, meter_id))
                goto error;
 
-       if (!meter)
-               return 0;
-
        if (nla_put(reply, OVS_METER_ATTR_STATS,
-                   sizeof(struct ovs_flow_stats), &meter->stats) ||
-           nla_put_u64_64bit(reply, OVS_METER_ATTR_USED, meter->used,
+                   sizeof(struct ovs_flow_stats), &meter->stats))
+               goto error;
+
+       if (nla_put_u64_64bit(reply, OVS_METER_ATTR_USED, meter->used,
                              OVS_METER_ATTR_PAD))
                goto error;
 
@@ -150,18 +275,32 @@ error:
 
 static int ovs_meter_cmd_features(struct sk_buff *skb, struct genl_info *info)
 {
-       struct sk_buff *reply;
+       struct ovs_header *ovs_header = info->userhdr;
        struct ovs_header *ovs_reply_header;
        struct nlattr *nla, *band_nla;
-       int err;
+       struct sk_buff *reply;
+       struct datapath *dp;
+       int err = -EMSGSIZE;
 
        reply = ovs_meter_cmd_reply_start(info, OVS_METER_CMD_FEATURES,
                                          &ovs_reply_header);
        if (IS_ERR(reply))
                return PTR_ERR(reply);
 
-       if (nla_put_u32(reply, OVS_METER_ATTR_MAX_METERS, U32_MAX) ||
-           nla_put_u32(reply, OVS_METER_ATTR_MAX_BANDS, DP_MAX_BANDS))
+       ovs_lock();
+       dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
+       if (!dp) {
+               err = -ENODEV;
+               goto exit_unlock;
+       }
+
+       if (nla_put_u32(reply, OVS_METER_ATTR_MAX_METERS,
+                       dp->meter_tbl.max_meters_allowed))
+               goto exit_unlock;
+
+       ovs_unlock();
+
+       if (nla_put_u32(reply, OVS_METER_ATTR_MAX_BANDS, DP_MAX_BANDS))
                goto nla_put_failure;
 
        nla = nla_nest_start_noflag(reply, OVS_METER_ATTR_BANDS);
@@ -180,9 +319,10 @@ static int ovs_meter_cmd_features(struct sk_buff *skb, struct genl_info *info)
        genlmsg_end(reply, ovs_reply_header);
        return genlmsg_reply(reply, info);
 
+exit_unlock:
+       ovs_unlock();
 nla_put_failure:
        nlmsg_free(reply);
-       err = -EMSGSIZE;
        return err;
 }
 
@@ -252,8 +392,8 @@ static struct dp_meter *dp_meter_create(struct nlattr **a)
                 *
                 * Start with a full bucket.
                 */
-               band->bucket = (band->burst_size + band->rate) * 1000;
-               band_max_delta_t = band->bucket / band->rate;
+               band->bucket = (band->burst_size + band->rate) * 1000ULL;
+               band_max_delta_t = div_u64(band->bucket, band->rate);
                if (band_max_delta_t > meter->max_delta_t)
                        meter->max_delta_t = band_max_delta_t;
                band++;
@@ -273,14 +413,14 @@ static int ovs_meter_cmd_set(struct sk_buff *skb, struct genl_info *info)
        struct sk_buff *reply;
        struct ovs_header *ovs_reply_header;
        struct ovs_header *ovs_header = info->userhdr;
+       struct dp_meter_table *meter_tbl;
        struct datapath *dp;
        int err;
        u32 meter_id;
        bool failed;
 
-       if (!a[OVS_METER_ATTR_ID]) {
-               return -ENODEV;
-       }
+       if (!a[OVS_METER_ATTR_ID])
+               return -EINVAL;
 
        meter = dp_meter_create(a);
        if (IS_ERR_OR_NULL(meter))
@@ -300,12 +440,18 @@ static int ovs_meter_cmd_set(struct sk_buff *skb, struct genl_info *info)
                goto exit_unlock;
        }
 
+       meter_tbl = &dp->meter_tbl;
        meter_id = nla_get_u32(a[OVS_METER_ATTR_ID]);
 
-       /* Cannot fail after this. */
-       old_meter = lookup_meter(dp, meter_id);
-       detach_meter(old_meter);
-       attach_meter(dp, meter);
+       old_meter = lookup_meter(meter_tbl, meter_id);
+       err = detach_meter(meter_tbl, old_meter);
+       if (err)
+               goto exit_unlock;
+
+       err = attach_meter(meter_tbl, meter);
+       if (err)
+               goto exit_unlock;
+
        ovs_unlock();
 
        /* Build response with the meter_id and stats from
@@ -337,14 +483,14 @@ exit_free_meter:
 
 static int ovs_meter_cmd_get(struct sk_buff *skb, struct genl_info *info)
 {
-       struct nlattr **a = info->attrs;
-       u32 meter_id;
        struct ovs_header *ovs_header = info->userhdr;
        struct ovs_header *ovs_reply_header;
+       struct nlattr **a = info->attrs;
+       struct dp_meter *meter;
+       struct sk_buff *reply;
        struct datapath *dp;
+       u32 meter_id;
        int err;
-       struct sk_buff *reply;
-       struct dp_meter *meter;
 
        if (!a[OVS_METER_ATTR_ID])
                return -EINVAL;
@@ -365,7 +511,7 @@ static int ovs_meter_cmd_get(struct sk_buff *skb, struct genl_info *info)
        }
 
        /* Locate meter, copy stats. */
-       meter = lookup_meter(dp, meter_id);
+       meter = lookup_meter(&dp->meter_tbl, meter_id);
        if (!meter) {
                err = -ENOENT;
                goto exit_unlock;
@@ -390,18 +536,17 @@ exit_unlock:
 
 static int ovs_meter_cmd_del(struct sk_buff *skb, struct genl_info *info)
 {
-       struct nlattr **a = info->attrs;
-       u32 meter_id;
        struct ovs_header *ovs_header = info->userhdr;
        struct ovs_header *ovs_reply_header;
+       struct nlattr **a = info->attrs;
+       struct dp_meter *old_meter;
+       struct sk_buff *reply;
        struct datapath *dp;
+       u32 meter_id;
        int err;
-       struct sk_buff *reply;
-       struct dp_meter *old_meter;
 
        if (!a[OVS_METER_ATTR_ID])
                return -EINVAL;
-       meter_id = nla_get_u32(a[OVS_METER_ATTR_ID]);
 
        reply = ovs_meter_cmd_reply_start(info, OVS_METER_CMD_DEL,
                                          &ovs_reply_header);
@@ -416,14 +561,19 @@ static int ovs_meter_cmd_del(struct sk_buff *skb, struct genl_info *info)
                goto exit_unlock;
        }
 
-       old_meter = lookup_meter(dp, meter_id);
+       meter_id = nla_get_u32(a[OVS_METER_ATTR_ID]);
+       old_meter = lookup_meter(&dp->meter_tbl, meter_id);
        if (old_meter) {
                spin_lock_bh(&old_meter->lock);
                err = ovs_meter_cmd_reply_stats(reply, meter_id, old_meter);
                WARN_ON(err);
                spin_unlock_bh(&old_meter->lock);
-               detach_meter(old_meter);
+
+               err = detach_meter(&dp->meter_tbl, old_meter);
+               if (err)
+                       goto exit_unlock;
        }
+
        ovs_unlock();
        ovs_meter_free(old_meter);
        genlmsg_end(reply, ovs_reply_header);
@@ -443,16 +593,16 @@ exit_unlock:
 bool ovs_meter_execute(struct datapath *dp, struct sk_buff *skb,
                       struct sw_flow_key *key, u32 meter_id)
 {
-       struct dp_meter *meter;
-       struct dp_meter_band *band;
        long long int now_ms = div_u64(ktime_get_ns(), 1000 * 1000);
        long long int long_delta_ms;
-       u32 delta_ms;
-       u32 cost;
+       struct dp_meter_band *band;
+       struct dp_meter *meter;
        int i, band_exceeded_max = -1;
        u32 band_exceeded_rate = 0;
+       u32 delta_ms;
+       u32 cost;
 
-       meter = lookup_meter(dp, meter_id);
+       meter = lookup_meter(&dp->meter_tbl, meter_id);
        /* Do not drop the packet when there is no meter. */
        if (!meter)
                return false;
@@ -570,32 +720,39 @@ struct genl_family dp_meter_genl_family __ro_after_init = {
 
 int ovs_meters_init(struct datapath *dp)
 {
-       int i;
-
-       dp->meters = kmalloc_array(METER_HASH_BUCKETS,
-                                  sizeof(struct hlist_head), GFP_KERNEL);
+       struct dp_meter_table *tbl = &dp->meter_tbl;
+       struct dp_meter_instance *ti;
+       unsigned long free_mem_bytes;
 
-       if (!dp->meters)
+       ti = dp_meter_instance_alloc(DP_METER_ARRAY_SIZE_MIN);
+       if (!ti)
                return -ENOMEM;
 
-       for (i = 0; i < METER_HASH_BUCKETS; i++)
-               INIT_HLIST_HEAD(&dp->meters[i]);
+       /* Allow meters in a datapath to use ~3.12% of physical memory. */
+       free_mem_bytes = nr_free_buffer_pages() * (PAGE_SIZE >> 5);
+       tbl->max_meters_allowed = min(free_mem_bytes / sizeof(struct dp_meter),
+                                     DP_METER_NUM_MAX);
+       if (!tbl->max_meters_allowed)
+               goto out_err;
+
+       rcu_assign_pointer(tbl->ti, ti);
+       tbl->count = 0;
 
        return 0;
+
+out_err:
+       dp_meter_instance_free(ti);
+       return -ENOMEM;
 }
 
 void ovs_meters_exit(struct datapath *dp)
 {
+       struct dp_meter_table *tbl = &dp->meter_tbl;
+       struct dp_meter_instance *ti = rcu_dereference_raw(tbl->ti);
        int i;
 
-       for (i = 0; i < METER_HASH_BUCKETS; i++) {
-               struct hlist_head *head = &dp->meters[i];
-               struct dp_meter *meter;
-               struct hlist_node *n;
-
-               hlist_for_each_entry_safe(meter, n, head, dp_hash_node)
-                       kfree(meter);
-       }
+       for (i = 0; i < ti->n_meters; i++)
+               ovs_meter_free(rcu_dereference_raw(ti->dp_meters[i]));
 
-       kfree(dp->meters);
+       dp_meter_instance_free(ti);
 }
index f645913..0c33889 100644 (file)
 #include <linux/openvswitch.h>
 #include <linux/genetlink.h>
 #include <linux/skbuff.h>
+#include <linux/bits.h>
 
 #include "flow.h"
 struct datapath;
 
 #define DP_MAX_BANDS           1
+#define DP_METER_ARRAY_SIZE_MIN        BIT_ULL(10)
+#define DP_METER_NUM_MAX       (200000UL)
 
 struct dp_meter_band {
        u32 type;
        u32 rate;
        u32 burst_size;
-       u32 bucket; /* 1/1000 packets, or in bits */
+       u64 bucket; /* 1/1000 packets, or in bits */
        struct ovs_flow_stats stats;
 };
 
 struct dp_meter {
        spinlock_t lock;    /* Per meter lock */
        struct rcu_head rcu;
-       struct hlist_node dp_hash_node; /*Element in datapath->meters
-                                        * hash table.
-                                        */
        u32 id;
        u16 kbps:1, keep_stats:1;
        u16 n_bands;
@@ -42,6 +42,18 @@ struct dp_meter {
        struct dp_meter_band bands[];
 };
 
+struct dp_meter_instance {
+       struct rcu_head rcu;
+       u32 n_meters;
+       struct dp_meter __rcu *dp_meters[];
+};
+
+struct dp_meter_table {
+       struct dp_meter_instance __rcu *ti;
+       u32 count;
+       u32 max_meters_allowed;
+};
+
 extern struct genl_family dp_meter_genl_family;
 int ovs_meters_init(struct datapath *dp);
 void ovs_meters_exit(struct datapath *dp);
index 251e750..0d0bf41 100644 (file)
@@ -49,8 +49,7 @@ void phonet_get_local_port_range(int *min, int *max)
 }
 
 static int proc_local_port_range(struct ctl_table *table, int write,
-                               void __user *buffer,
-                               size_t *lenp, loff_t *ppos)
+                                void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
        int range[2] = {local_port_range[0], local_port_range[1]};
index 63f89cc..f362ca3 100644 (file)
@@ -4,7 +4,6 @@
 
 config QRTR
        tristate "Qualcomm IPC Router support"
-       depends on ARCH_QCOM || COMPILE_TEST
        ---help---
          Say Y if you intend to use Qualcomm IPC router protocol.  The
          protocol is used to communicate with services provided by other
@@ -29,4 +28,11 @@ config QRTR_TUN
          implement endpoints of QRTR, for purpose of tunneling data to other
          hosts or testing purposes.
 
+config QRTR_MHI
+       tristate "MHI IPC Router channels"
+       depends on MHI_BUS
+       help
+         Say Y here to support MHI based ipcrouter channels. MHI is the
+         transport used for communicating to external modems.
+
 endif # QRTR
index 32d4e92..1b1411d 100644 (file)
@@ -5,3 +5,5 @@ obj-$(CONFIG_QRTR_SMD) += qrtr-smd.o
 qrtr-smd-y     := smd.o
 obj-$(CONFIG_QRTR_TUN) += qrtr-tun.o
 qrtr-tun-y     := tun.o
+obj-$(CONFIG_QRTR_MHI) += qrtr-mhi.o
+qrtr-mhi-y     := mhi.o
diff --git a/net/qrtr/mhi.c b/net/qrtr/mhi.c
new file mode 100644 (file)
index 0000000..ff0c414
--- /dev/null
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/mhi.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+
+#include "qrtr.h"
+
+struct qrtr_mhi_dev {
+       struct qrtr_endpoint ep;
+       struct mhi_device *mhi_dev;
+       struct device *dev;
+};
+
+/* From MHI to QRTR */
+static void qcom_mhi_qrtr_dl_callback(struct mhi_device *mhi_dev,
+                                     struct mhi_result *mhi_res)
+{
+       struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
+       int rc;
+
+       if (!qdev || mhi_res->transaction_status)
+               return;
+
+       rc = qrtr_endpoint_post(&qdev->ep, mhi_res->buf_addr,
+                               mhi_res->bytes_xferd);
+       if (rc == -EINVAL)
+               dev_err(qdev->dev, "invalid ipcrouter packet\n");
+}
+
+/* From QRTR to MHI */
+static void qcom_mhi_qrtr_ul_callback(struct mhi_device *mhi_dev,
+                                     struct mhi_result *mhi_res)
+{
+       struct sk_buff *skb = mhi_res->buf_addr;
+
+       if (skb->sk)
+               sock_put(skb->sk);
+       consume_skb(skb);
+}
+
+/* Send data over MHI */
+static int qcom_mhi_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb)
+{
+       struct qrtr_mhi_dev *qdev = container_of(ep, struct qrtr_mhi_dev, ep);
+       int rc;
+
+       rc = skb_linearize(skb);
+       if (rc)
+               goto free_skb;
+
+       rc = mhi_queue_skb(qdev->mhi_dev, DMA_TO_DEVICE, skb, skb->len,
+                          MHI_EOT);
+       if (rc)
+               goto free_skb;
+
+       if (skb->sk)
+               sock_hold(skb->sk);
+
+       return rc;
+
+free_skb:
+       kfree_skb(skb);
+
+       return rc;
+}
+
+static int qcom_mhi_qrtr_probe(struct mhi_device *mhi_dev,
+                              const struct mhi_device_id *id)
+{
+       struct qrtr_mhi_dev *qdev;
+       int rc;
+
+       qdev = devm_kzalloc(&mhi_dev->dev, sizeof(*qdev), GFP_KERNEL);
+       if (!qdev)
+               return -ENOMEM;
+
+       qdev->mhi_dev = mhi_dev;
+       qdev->dev = &mhi_dev->dev;
+       qdev->ep.xmit = qcom_mhi_qrtr_send;
+
+       dev_set_drvdata(&mhi_dev->dev, qdev);
+       rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO);
+       if (rc)
+               return rc;
+
+       dev_dbg(qdev->dev, "Qualcomm MHI QRTR driver probed\n");
+
+       return 0;
+}
+
+static void qcom_mhi_qrtr_remove(struct mhi_device *mhi_dev)
+{
+       struct qrtr_mhi_dev *qdev = dev_get_drvdata(&mhi_dev->dev);
+
+       qrtr_endpoint_unregister(&qdev->ep);
+       dev_set_drvdata(&mhi_dev->dev, NULL);
+}
+
+static const struct mhi_device_id qcom_mhi_qrtr_id_table[] = {
+       { .chan = "IPCR" },
+       {}
+};
+MODULE_DEVICE_TABLE(mhi, qcom_mhi_qrtr_id_table);
+
+static struct mhi_driver qcom_mhi_qrtr_driver = {
+       .probe = qcom_mhi_qrtr_probe,
+       .remove = qcom_mhi_qrtr_remove,
+       .dl_xfer_cb = qcom_mhi_qrtr_dl_callback,
+       .ul_xfer_cb = qcom_mhi_qrtr_ul_callback,
+       .id_table = qcom_mhi_qrtr_id_table,
+       .driver = {
+               .name = "qcom_mhi_qrtr",
+       },
+};
+
+module_mhi_driver(qcom_mhi_qrtr_driver);
+
+MODULE_AUTHOR("Chris Lew <clew@codeaurora.org>");
+MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
+MODULE_DESCRIPTION("Qualcomm IPC-Router MHI interface driver");
+MODULE_LICENSE("GPL v2");
index e7d0fe3..3ca196f 100644 (file)
@@ -12,6 +12,9 @@
 
 #include "qrtr.h"
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/qrtr.h>
+
 static RADIX_TREE(nodes, GFP_KERNEL);
 
 static struct {
@@ -105,8 +108,8 @@ static int service_announce_new(struct sockaddr_qrtr *dest,
        struct msghdr msg = { };
        struct kvec iv;
 
-       trace_printk("advertising new server [%d:%x]@[%d:%d]\n",
-                    srv->service, srv->instance, srv->node, srv->port);
+       trace_qrtr_ns_service_announce_new(srv->service, srv->instance,
+                                          srv->node, srv->port);
 
        iv.iov_base = &pkt;
        iv.iov_len = sizeof(pkt);
@@ -132,8 +135,8 @@ static int service_announce_del(struct sockaddr_qrtr *dest,
        struct kvec iv;
        int ret;
 
-       trace_printk("advertising removal of server [%d:%x]@[%d:%d]\n",
-                    srv->service, srv->instance, srv->node, srv->port);
+       trace_qrtr_ns_service_announce_del(srv->service, srv->instance,
+                                          srv->node, srv->port);
 
        iv.iov_base = &pkt;
        iv.iov_len = sizeof(pkt);
@@ -244,8 +247,8 @@ static struct qrtr_server *server_add(unsigned int service,
 
        radix_tree_insert(&node->servers, port, srv);
 
-       trace_printk("add server [%d:%x]@[%d:%d]\n", srv->service,
-                    srv->instance, srv->node, srv->port);
+       trace_qrtr_ns_server_add(srv->service, srv->instance,
+                                srv->node, srv->port);
 
        return srv;
 
@@ -633,9 +636,8 @@ static void qrtr_ns_worker(struct work_struct *work)
                cmd = le32_to_cpu(pkt->cmd);
                if (cmd < ARRAY_SIZE(qrtr_ctrl_pkt_strings) &&
                    qrtr_ctrl_pkt_strings[cmd])
-                       trace_printk("%s from %d:%d\n",
-                                    qrtr_ctrl_pkt_strings[cmd], sq.sq_node,
-                                    sq.sq_port);
+                       trace_qrtr_ns_message(qrtr_ctrl_pkt_strings[cmd],
+                                             sq.sq_node, sq.sq_port);
 
                ret = 0;
                switch (cmd) {
index e22092e..7ed31b5 100644 (file)
@@ -906,20 +906,21 @@ static int qrtr_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
 
        node = NULL;
        if (addr->sq_node == QRTR_NODE_BCAST) {
-               enqueue_fn = qrtr_bcast_enqueue;
-               if (addr->sq_port != QRTR_PORT_CTRL) {
+               if (addr->sq_port != QRTR_PORT_CTRL &&
+                   qrtr_local_nid != QRTR_NODE_BCAST) {
                        release_sock(sk);
                        return -ENOTCONN;
                }
+               enqueue_fn = qrtr_bcast_enqueue;
        } else if (addr->sq_node == ipc->us.sq_node) {
                enqueue_fn = qrtr_local_enqueue;
        } else {
-               enqueue_fn = qrtr_node_enqueue;
                node = qrtr_node_lookup(addr->sq_node);
                if (!node) {
                        release_sock(sk);
                        return -ECONNRESET;
                }
+               enqueue_fn = qrtr_node_enqueue;
        }
 
        plen = (len + 3) & ~3;
index 50f13f1..071a261 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006 Oracle.  All rights reserved.
+ * Copyright (c) 2006, 2020 Oracle and/or its affiliates.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -162,12 +162,12 @@ static void rds_message_purge(struct rds_message *rm)
        if (rm->rdma.op_active)
                rds_rdma_free_op(&rm->rdma);
        if (rm->rdma.op_rdma_mr)
-               rds_mr_put(rm->rdma.op_rdma_mr);
+               kref_put(&rm->rdma.op_rdma_mr->r_kref, __rds_put_mr_final);
 
        if (rm->atomic.op_active)
                rds_atomic_free_op(&rm->atomic);
        if (rm->atomic.op_rdma_mr)
-               rds_mr_put(rm->atomic.op_rdma_mr);
+               kref_put(&rm->atomic.op_rdma_mr->r_kref, __rds_put_mr_final);
 }
 
 void rds_message_put(struct rds_message *rm)
@@ -308,26 +308,20 @@ out:
 /*
  * RDS ops use this to grab SG entries from the rm's sg pool.
  */
-struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents,
-                                         int *ret)
+struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents)
 {
        struct scatterlist *sg_first = (struct scatterlist *) &rm[1];
        struct scatterlist *sg_ret;
 
-       if (WARN_ON(!ret))
-               return NULL;
-
        if (nents <= 0) {
                pr_warn("rds: alloc sgs failed! nents <= 0\n");
-               *ret = -EINVAL;
-               return NULL;
+               return ERR_PTR(-EINVAL);
        }
 
        if (rm->m_used_sgs + nents > rm->m_total_sgs) {
                pr_warn("rds: alloc sgs failed! total %d used %d nents %d\n",
                        rm->m_total_sgs, rm->m_used_sgs, nents);
-               *ret = -ENOMEM;
-               return NULL;
+               return ERR_PTR(-ENOMEM);
        }
 
        sg_ret = &sg_first[rm->m_used_sgs];
@@ -343,7 +337,6 @@ struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned in
        unsigned int i;
        int num_sgs = DIV_ROUND_UP(total_len, PAGE_SIZE);
        int extra_bytes = num_sgs * sizeof(struct scatterlist);
-       int ret;
 
        rm = rds_message_alloc(extra_bytes, GFP_NOWAIT);
        if (!rm)
@@ -352,10 +345,10 @@ struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned in
        set_bit(RDS_MSG_PAGEVEC, &rm->m_flags);
        rm->m_inc.i_hdr.h_len = cpu_to_be32(total_len);
        rm->data.op_nents = DIV_ROUND_UP(total_len, PAGE_SIZE);
-       rm->data.op_sg = rds_message_alloc_sgs(rm, num_sgs, &ret);
-       if (!rm->data.op_sg) {
+       rm->data.op_sg = rds_message_alloc_sgs(rm, num_sgs);
+       if (IS_ERR(rm->data.op_sg)) {
                rds_message_put(rm);
-               return ERR_PTR(ret);
+               return ERR_CAST(rm->data.op_sg);
        }
 
        for (i = 0; i < rm->data.op_nents; ++i) {
index 585e6b3..a7ae118 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2020 Oracle and/or its affiliates.
  *
  * This software is available to you under a choice of one of two
  * licenses.  You may choose to be licensed under the terms of the GNU
@@ -84,7 +84,7 @@ static struct rds_mr *rds_mr_tree_walk(struct rb_root *root, u64 key,
        if (insert) {
                rb_link_node(&insert->r_rb_node, parent, p);
                rb_insert_color(&insert->r_rb_node, root);
-               refcount_inc(&insert->r_refcount);
+               kref_get(&insert->r_kref);
        }
        return NULL;
 }
@@ -99,10 +99,7 @@ static void rds_destroy_mr(struct rds_mr *mr)
        unsigned long flags;
 
        rdsdebug("RDS: destroy mr key is %x refcnt %u\n",
-                       mr->r_key, refcount_read(&mr->r_refcount));
-
-       if (test_and_set_bit(RDS_MR_DEAD, &mr->r_state))
-               return;
+                mr->r_key, kref_read(&mr->r_kref));
 
        spin_lock_irqsave(&rs->rs_rdma_lock, flags);
        if (!RB_EMPTY_NODE(&mr->r_rb_node))
@@ -115,8 +112,10 @@ static void rds_destroy_mr(struct rds_mr *mr)
                mr->r_trans->free_mr(trans_private, mr->r_invalidate);
 }
 
-void __rds_put_mr_final(struct rds_mr *mr)
+void __rds_put_mr_final(struct kref *kref)
 {
+       struct rds_mr *mr = container_of(kref, struct rds_mr, r_kref);
+
        rds_destroy_mr(mr);
        kfree(mr);
 }
@@ -140,8 +139,7 @@ void rds_rdma_drop_keys(struct rds_sock *rs)
                rb_erase(&mr->r_rb_node, &rs->rs_rdma_keys);
                RB_CLEAR_NODE(&mr->r_rb_node);
                spin_unlock_irqrestore(&rs->rs_rdma_lock, flags);
-               rds_destroy_mr(mr);
-               rds_mr_put(mr);
+               kref_put(&mr->r_kref, __rds_put_mr_final);
                spin_lock_irqsave(&rs->rs_rdma_lock, flags);
        }
        spin_unlock_irqrestore(&rs->rs_rdma_lock, flags);
@@ -242,7 +240,7 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args,
                goto out;
        }
 
-       refcount_set(&mr->r_refcount, 1);
+       kref_init(&mr->r_kref);
        RB_CLEAR_NODE(&mr->r_rb_node);
        mr->r_trans = rs->rs_transport;
        mr->r_sock = rs;
@@ -343,7 +341,7 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args,
 
        rdsdebug("RDS: get_mr key is %x\n", mr->r_key);
        if (mr_ret) {
-               refcount_inc(&mr->r_refcount);
+               kref_get(&mr->r_kref);
                *mr_ret = mr;
        }
 
@@ -351,7 +349,7 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args,
 out:
        kfree(pages);
        if (mr)
-               rds_mr_put(mr);
+               kref_put(&mr->r_kref, __rds_put_mr_final);
        return ret;
 }
 
@@ -434,13 +432,7 @@ int rds_free_mr(struct rds_sock *rs, char __user *optval, int optlen)
        if (!mr)
                return -EINVAL;
 
-       /*
-        * call rds_destroy_mr() ourselves so that we're sure it's done by the time
-        * we return.  If we let rds_mr_put() do it it might not happen until
-        * someone else drops their ref.
-        */
-       rds_destroy_mr(mr);
-       rds_mr_put(mr);
+       kref_put(&mr->r_kref, __rds_put_mr_final);
        return 0;
 }
 
@@ -464,6 +456,14 @@ void rds_rdma_unuse(struct rds_sock *rs, u32 r_key, int force)
                return;
        }
 
+       /* Get a reference so that the MR won't go away before calling
+        * sync_mr() below.
+        */
+       kref_get(&mr->r_kref);
+
+       /* If it is going to be freed, remove it from the tree now so
+        * that no other thread can find it and free it.
+        */
        if (mr->r_use_once || force) {
                rb_erase(&mr->r_rb_node, &rs->rs_rdma_keys);
                RB_CLEAR_NODE(&mr->r_rb_node);
@@ -477,12 +477,13 @@ void rds_rdma_unuse(struct rds_sock *rs, u32 r_key, int force)
        if (mr->r_trans->sync_mr)
                mr->r_trans->sync_mr(mr->r_trans_private, DMA_FROM_DEVICE);
 
+       /* Release the reference held above. */
+       kref_put(&mr->r_kref, __rds_put_mr_final);
+
        /* If the MR was marked as invalidate, this will
         * trigger an async flush. */
-       if (zot_me) {
-               rds_destroy_mr(mr);
-               rds_mr_put(mr);
-       }
+       if (zot_me)
+               kref_put(&mr->r_kref, __rds_put_mr_final);
 }
 
 void rds_rdma_free_op(struct rm_rdma_op *ro)
@@ -490,7 +491,7 @@ void rds_rdma_free_op(struct rm_rdma_op *ro)
        unsigned int i;
 
        if (ro->op_odp_mr) {
-               rds_mr_put(ro->op_odp_mr);
+               kref_put(&ro->op_odp_mr->r_kref, __rds_put_mr_final);
        } else {
                for (i = 0; i < ro->op_nents; i++) {
                        struct page *page = sg_page(&ro->op_sg[i]);
@@ -664,9 +665,11 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
        op->op_odp_mr = NULL;
 
        WARN_ON(!nr_pages);
-       op->op_sg = rds_message_alloc_sgs(rm, nr_pages, &ret);
-       if (!op->op_sg)
+       op->op_sg = rds_message_alloc_sgs(rm, nr_pages);
+       if (IS_ERR(op->op_sg)) {
+               ret = PTR_ERR(op->op_sg);
                goto out_pages;
+       }
 
        if (op->op_notify || op->op_recverr) {
                /* We allocate an uninitialized notifier here, because
@@ -730,7 +733,7 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
                                goto out_pages;
                        }
                        RB_CLEAR_NODE(&local_odp_mr->r_rb_node);
-                       refcount_set(&local_odp_mr->r_refcount, 1);
+                       kref_init(&local_odp_mr->r_kref);
                        local_odp_mr->r_trans = rs->rs_transport;
                        local_odp_mr->r_sock = rs;
                        local_odp_mr->r_trans_private =
@@ -827,7 +830,7 @@ int rds_cmsg_rdma_dest(struct rds_sock *rs, struct rds_message *rm,
        if (!mr)
                err = -EINVAL;  /* invalid r_key */
        else
-               refcount_inc(&mr->r_refcount);
+               kref_get(&mr->r_kref);
        spin_unlock_irqrestore(&rs->rs_rdma_lock, flags);
 
        if (mr) {
@@ -905,9 +908,11 @@ int rds_cmsg_atomic(struct rds_sock *rs, struct rds_message *rm,
        rm->atomic.op_silent = !!(args->flags & RDS_RDMA_SILENT);
        rm->atomic.op_active = 1;
        rm->atomic.op_recverr = rs->rs_recverr;
-       rm->atomic.op_sg = rds_message_alloc_sgs(rm, 1, &ret);
-       if (!rm->atomic.op_sg)
+       rm->atomic.op_sg = rds_message_alloc_sgs(rm, 1);
+       if (IS_ERR(rm->atomic.op_sg)) {
+               ret = PTR_ERR(rm->atomic.op_sg);
                goto err;
+       }
 
        /* verify 8 byte-aligned */
        if (args->local_addr & 0x7) {
index e4a6035..6019b0c 100644 (file)
@@ -291,7 +291,7 @@ struct rds_incoming {
 
 struct rds_mr {
        struct rb_node          r_rb_node;
-       refcount_t              r_refcount;
+       struct kref             r_kref;
        u32                     r_key;
 
        /* A copy of the creation flags */
@@ -299,19 +299,11 @@ struct rds_mr {
        unsigned int            r_invalidate:1;
        unsigned int            r_write:1;
 
-       /* This is for RDS_MR_DEAD.
-        * It would be nice & consistent to make this part of the above
-        * bit field here, but we need to use test_and_set_bit.
-        */
-       unsigned long           r_state;
        struct rds_sock         *r_sock; /* back pointer to the socket that owns us */
        struct rds_transport    *r_trans;
        void                    *r_trans_private;
 };
 
-/* Flags for mr->r_state */
-#define RDS_MR_DEAD            0
-
 static inline rds_rdma_cookie_t rds_rdma_make_cookie(u32 r_key, u32 offset)
 {
        return r_key | (((u64) offset) << 32);
@@ -852,8 +844,7 @@ rds_conn_connecting(struct rds_connection *conn)
 
 /* message.c */
 struct rds_message *rds_message_alloc(unsigned int nents, gfp_t gfp);
-struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents,
-                                         int *ret);
+struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents);
 int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from,
                               bool zcopy);
 struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned int total_len);
@@ -946,12 +937,7 @@ void rds_atomic_send_complete(struct rds_message *rm, int wc_status);
 int rds_cmsg_atomic(struct rds_sock *rs, struct rds_message *rm,
                    struct cmsghdr *cmsg);
 
-void __rds_put_mr_final(struct rds_mr *mr);
-static inline void rds_mr_put(struct rds_mr *mr)
-{
-       if (refcount_dec_and_test(&mr->r_refcount))
-               __rds_put_mr_final(mr);
-}
+void __rds_put_mr_final(struct kref *kref);
 
 static inline bool rds_destroy_pending(struct rds_connection *conn)
 {
index 82dcd8b..68e2bdb 100644 (file)
@@ -1274,9 +1274,11 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len)
 
        /* Attach data to the rm */
        if (payload_len) {
-               rm->data.op_sg = rds_message_alloc_sgs(rm, num_sgs, &ret);
-               if (!rm->data.op_sg)
+               rm->data.op_sg = rds_message_alloc_sgs(rm, num_sgs);
+               if (IS_ERR(rm->data.op_sg)) {
+                       ret = PTR_ERR(rm->data.op_sg);
                        goto out;
+               }
                ret = rds_message_copy_from_user(rm, &msg->msg_iter, zcopy);
                if (ret)
                        goto out;
index 66121bc..46782fa 100644 (file)
@@ -62,8 +62,7 @@ static atomic_t rds_tcp_unloading = ATOMIC_INIT(0);
 static struct kmem_cache *rds_tcp_conn_slab;
 
 static int rds_tcp_skbuf_handler(struct ctl_table *ctl, int write,
-                                void __user *buffer, size_t *lenp,
-                                loff_t *fpos);
+                                void *buffer, size_t *lenp, loff_t *fpos);
 
 static int rds_tcp_min_sndbuf = SOCK_MIN_SNDBUF;
 static int rds_tcp_min_rcvbuf = SOCK_MIN_RCVBUF;
@@ -676,8 +675,7 @@ static void rds_tcp_sysctl_reset(struct net *net)
 }
 
 static int rds_tcp_skbuf_handler(struct ctl_table *ctl, int write,
-                                void __user *buffer, size_t *lenp,
-                                loff_t *fpos)
+                                void *buffer, size_t *lenp, loff_t *fpos)
 {
        struct net *net = current->nsproxy->net_ns;
        int err;
index 1e8eeb0..e7a8722 100644 (file)
@@ -64,6 +64,26 @@ static const struct proto_ops rose_proto_ops;
 
 ax25_address rose_callsign;
 
+/*
+ * ROSE network devices are virtual network devices encapsulating ROSE
+ * frames into AX.25 which will be sent through an AX.25 device, so form a
+ * special "super class" of normal net devices; split their locks off into a
+ * separate class since they always nest.
+ */
+static struct lock_class_key rose_netdev_xmit_lock_key;
+
+static void rose_set_lockdep_one(struct net_device *dev,
+                                struct netdev_queue *txq,
+                                void *_unused)
+{
+       lockdep_set_class(&txq->_xmit_lock, &rose_netdev_xmit_lock_key);
+}
+
+static void rose_set_lockdep_key(struct net_device *dev)
+{
+       netdev_for_each_tx_queue(dev, rose_set_lockdep_one, NULL);
+}
+
 /*
  *     Convert a ROSE address into text.
  */
@@ -1511,6 +1531,7 @@ static int __init rose_proto_init(void)
                        free_netdev(dev);
                        goto fail;
                }
+               rose_set_lockdep_key(dev);
                dev_rose[i] = dev;
        }
 
index 57ebb29..d706bb4 100644 (file)
@@ -18,7 +18,7 @@ config AF_RXRPC
          This module at the moment only supports client operations and is
          currently incomplete.
 
-         See Documentation/networking/rxrpc.txt.
+         See Documentation/networking/rxrpc.rst.
 
 config AF_RXRPC_IPV6
        bool "IPv6 support for RxRPC"
@@ -41,7 +41,7 @@ config AF_RXRPC_DEBUG
        help
          Say Y here to make runtime controllable debugging messages appear.
 
-         See Documentation/networking/rxrpc.txt.
+         See Documentation/networking/rxrpc.rst.
 
 
 config RXKAD
@@ -56,4 +56,4 @@ config RXKAD
          Provide kerberos 4 and AFS kaserver security handling for AF_RXRPC
          through the use of the key retention service.
 
-         See Documentation/networking/rxrpc.txt.
+         See Documentation/networking/rxrpc.rst.
index a6c1349..01135e5 100644 (file)
@@ -165,15 +165,6 @@ static int rxrpc_open_socket(struct rxrpc_local *local, struct net *net)
                        goto error;
                }
 
-               /* we want to set the don't fragment bit */
-               opt = IPV6_PMTUDISC_DO;
-               ret = kernel_setsockopt(local->socket, SOL_IPV6, IPV6_MTU_DISCOVER,
-                                       (char *) &opt, sizeof(opt));
-               if (ret < 0) {
-                       _debug("setsockopt failed");
-                       goto error;
-               }
-
                /* Fall through and set IPv4 options too otherwise we don't get
                 * errors from IPv4 packets sent through the IPv6 socket.
                 */
index bad3d24..90e263c 100644 (file)
@@ -474,41 +474,21 @@ send_fragmentable:
        skb->tstamp = ktime_get_real();
 
        switch (conn->params.local->srx.transport.family) {
+       case AF_INET6:
        case AF_INET:
                opt = IP_PMTUDISC_DONT;
-               ret = kernel_setsockopt(conn->params.local->socket,
-                                       SOL_IP, IP_MTU_DISCOVER,
-                                       (char *)&opt, sizeof(opt));
-               if (ret == 0) {
-                       ret = kernel_sendmsg(conn->params.local->socket, &msg,
-                                            iov, 2, len);
-                       conn->params.peer->last_tx_at = ktime_get_seconds();
-
-                       opt = IP_PMTUDISC_DO;
-                       kernel_setsockopt(conn->params.local->socket, SOL_IP,
-                                         IP_MTU_DISCOVER,
-                                         (char *)&opt, sizeof(opt));
-               }
-               break;
-
-#ifdef CONFIG_AF_RXRPC_IPV6
-       case AF_INET6:
-               opt = IPV6_PMTUDISC_DONT;
-               ret = kernel_setsockopt(conn->params.local->socket,
-                                       SOL_IPV6, IPV6_MTU_DISCOVER,
-                                       (char *)&opt, sizeof(opt));
-               if (ret == 0) {
-                       ret = kernel_sendmsg(conn->params.local->socket, &msg,
-                                            iov, 2, len);
-                       conn->params.peer->last_tx_at = ktime_get_seconds();
-
-                       opt = IPV6_PMTUDISC_DO;
-                       kernel_setsockopt(conn->params.local->socket,
-                                         SOL_IPV6, IPV6_MTU_DISCOVER,
-                                         (char *)&opt, sizeof(opt));
-               }
+               kernel_setsockopt(conn->params.local->socket,
+                                 SOL_IP, IP_MTU_DISCOVER,
+                                 (char *)&opt, sizeof(opt));
+               ret = kernel_sendmsg(conn->params.local->socket, &msg,
+                                    iov, 2, len);
+               conn->params.peer->last_tx_at = ktime_get_seconds();
+
+               opt = IP_PMTUDISC_DO;
+               kernel_setsockopt(conn->params.local->socket,
+                                 SOL_IP, IP_MTU_DISCOVER,
+                                 (char *)&opt, sizeof(opt));
                break;
-#endif
 
        default:
                BUG();
index 2bbb381..174e903 100644 (file)
@@ -21,7 +21,7 @@ static const unsigned long max_jiffies = MAX_JIFFY_OFFSET;
 /*
  * RxRPC operating parameters.
  *
- * See Documentation/networking/rxrpc.txt and the variable definitions for more
+ * See Documentation/networking/rxrpc.rst and the variable definitions for more
  * information on the individual parameters.
  */
 static struct ctl_table rxrpc_sysctl_table[] = {
index bfbefb7..2f20073 100644 (file)
@@ -981,6 +981,18 @@ config NET_ACT_CT
          To compile this code as a module, choose M here: the
          module will be called act_ct.
 
+config NET_ACT_GATE
+       tristate "Frame gate entry list control tc action"
+       depends on NET_CLS_ACT
+       help
+         Say Y here to allow to control the ingress flow to be passed at
+         specific time slot and be dropped at other specific time slot by
+         the gate entry list.
+
+         If unsure, say N.
+         To compile this code as a module, choose M here: the
+         module will be called act_gate.
+
 config NET_IFE_SKBMARK
        tristate "Support to encoding decoding skb mark on IFE action"
        depends on NET_ACT_IFE
index 31c367a..66bbf9a 100644 (file)
@@ -30,6 +30,7 @@ obj-$(CONFIG_NET_IFE_SKBPRIO) += act_meta_skbprio.o
 obj-$(CONFIG_NET_IFE_SKBTCINDEX)       += act_meta_skbtcindex.o
 obj-$(CONFIG_NET_ACT_TUNNEL_KEY)+= act_tunnel_key.o
 obj-$(CONFIG_NET_ACT_CT)       += act_ct.o
+obj-$(CONFIG_NET_ACT_GATE)     += act_gate.o
 obj-$(CONFIG_NET_SCH_FIFO)     += sch_fifo.o
 obj-$(CONFIG_NET_SCH_CBQ)      += sch_cbq.o
 obj-$(CONFIG_NET_SCH_HTB)      += sch_htb.o
index df45609..fbbec2e 100644 (file)
@@ -876,19 +876,14 @@ static u8 tcf_action_hw_stats_get(struct nlattr *hw_stats_attr)
        return hw_stats_bf.value;
 }
 
-static const u32 tca_act_flags_allowed = TCA_ACT_FLAGS_NO_PERCPU_STATS;
-static const u32 tca_act_hw_stats_allowed = TCA_ACT_HW_STATS_ANY;
-
 static const struct nla_policy tcf_action_policy[TCA_ACT_MAX + 1] = {
        [TCA_ACT_KIND]          = { .type = NLA_STRING },
        [TCA_ACT_INDEX]         = { .type = NLA_U32 },
        [TCA_ACT_COOKIE]        = { .type = NLA_BINARY,
                                    .len = TC_COOKIE_MAX_SIZE },
        [TCA_ACT_OPTIONS]       = { .type = NLA_NESTED },
-       [TCA_ACT_FLAGS]         = { .type = NLA_BITFIELD32,
-                                   .validation_data = &tca_act_flags_allowed },
-       [TCA_ACT_HW_STATS]      = { .type = NLA_BITFIELD32,
-                                   .validation_data = &tca_act_hw_stats_allowed },
+       [TCA_ACT_FLAGS]         = NLA_POLICY_BITFIELD32(TCA_ACT_FLAGS_NO_PERCPU_STATS),
+       [TCA_ACT_HW_STATS]      = NLA_POLICY_BITFIELD32(TCA_ACT_HW_STATS_ANY),
 };
 
 struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
@@ -1454,10 +1449,8 @@ static int tcf_action_add(struct net *net, struct nlattr *nla,
        return ret;
 }
 
-static u32 tcaa_root_flags_allowed = TCA_FLAG_LARGE_DUMP_ON;
 static const struct nla_policy tcaa_policy[TCA_ROOT_MAX + 1] = {
-       [TCA_ROOT_FLAGS] = { .type = NLA_BITFIELD32,
-                            .validation_data = &tcaa_root_flags_allowed },
+       [TCA_ROOT_FLAGS] = NLA_POLICY_BITFIELD32(TCA_FLAG_LARGE_DUMP_ON),
        [TCA_ROOT_TIME_DELTA]      = { .type = NLA_U32 },
 };
 
index 1a76639..9adff83 100644 (file)
@@ -30,6 +30,7 @@
 #include <net/netfilter/nf_conntrack_core.h>
 #include <net/netfilter/nf_conntrack_zones.h>
 #include <net/netfilter/nf_conntrack_helper.h>
+#include <net/netfilter/nf_conntrack_acct.h>
 #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
 #include <uapi/linux/netfilter/nf_nat.h>
 
@@ -536,6 +537,7 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p,
        flow_offload_refresh(nf_ft, flow);
        nf_conntrack_get(&ct->ct_general);
        nf_ct_set(skb, ct, ctinfo);
+       nf_ct_acct_update(ct, dir, skb->len);
 
        return true;
 }
diff --git a/net/sched/act_gate.c b/net/sched/act_gate.c
new file mode 100644 (file)
index 0000000..35fc487
--- /dev/null
@@ -0,0 +1,636 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright 2020 NXP */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <net/act_api.h>
+#include <net/netlink.h>
+#include <net/pkt_cls.h>
+#include <net/tc_act/tc_gate.h>
+
+static unsigned int gate_net_id;
+static struct tc_action_ops act_gate_ops;
+
+static ktime_t gate_get_time(struct tcf_gate *gact)
+{
+       ktime_t mono = ktime_get();
+
+       switch (gact->tk_offset) {
+       case TK_OFFS_MAX:
+               return mono;
+       default:
+               return ktime_mono_to_any(mono, gact->tk_offset);
+       }
+
+       return KTIME_MAX;
+}
+
+static int gate_get_start_time(struct tcf_gate *gact, ktime_t *start)
+{
+       struct tcf_gate_params *param = &gact->param;
+       ktime_t now, base, cycle;
+       u64 n;
+
+       base = ns_to_ktime(param->tcfg_basetime);
+       now = gate_get_time(gact);
+
+       if (ktime_after(base, now)) {
+               *start = base;
+               return 0;
+       }
+
+       cycle = param->tcfg_cycletime;
+
+       /* cycle time should not be zero */
+       if (!cycle)
+               return -EFAULT;
+
+       n = div64_u64(ktime_sub_ns(now, base), cycle);
+       *start = ktime_add_ns(base, (n + 1) * cycle);
+       return 0;
+}
+
+static void gate_start_timer(struct tcf_gate *gact, ktime_t start)
+{
+       ktime_t expires;
+
+       expires = hrtimer_get_expires(&gact->hitimer);
+       if (expires == 0)
+               expires = KTIME_MAX;
+
+       start = min_t(ktime_t, start, expires);
+
+       hrtimer_start(&gact->hitimer, start, HRTIMER_MODE_ABS_SOFT);
+}
+
+static enum hrtimer_restart gate_timer_func(struct hrtimer *timer)
+{
+       struct tcf_gate *gact = container_of(timer, struct tcf_gate,
+                                            hitimer);
+       struct tcf_gate_params *p = &gact->param;
+       struct tcfg_gate_entry *next;
+       ktime_t close_time, now;
+
+       spin_lock(&gact->tcf_lock);
+
+       next = gact->next_entry;
+
+       /* cycle start, clear pending bit, clear total octets */
+       gact->current_gate_status = next->gate_state ? GATE_ACT_GATE_OPEN : 0;
+       gact->current_entry_octets = 0;
+       gact->current_max_octets = next->maxoctets;
+
+       gact->current_close_time = ktime_add_ns(gact->current_close_time,
+                                               next->interval);
+
+       close_time = gact->current_close_time;
+
+       if (list_is_last(&next->list, &p->entries))
+               next = list_first_entry(&p->entries,
+                                       struct tcfg_gate_entry, list);
+       else
+               next = list_next_entry(next, list);
+
+       now = gate_get_time(gact);
+
+       if (ktime_after(now, close_time)) {
+               ktime_t cycle, base;
+               u64 n;
+
+               cycle = p->tcfg_cycletime;
+               base = ns_to_ktime(p->tcfg_basetime);
+               n = div64_u64(ktime_sub_ns(now, base), cycle);
+               close_time = ktime_add_ns(base, (n + 1) * cycle);
+       }
+
+       gact->next_entry = next;
+
+       hrtimer_set_expires(&gact->hitimer, close_time);
+
+       spin_unlock(&gact->tcf_lock);
+
+       return HRTIMER_RESTART;
+}
+
+static int tcf_gate_act(struct sk_buff *skb, const struct tc_action *a,
+                       struct tcf_result *res)
+{
+       struct tcf_gate *gact = to_gate(a);
+
+       spin_lock(&gact->tcf_lock);
+
+       tcf_lastuse_update(&gact->tcf_tm);
+       bstats_update(&gact->tcf_bstats, skb);
+
+       if (unlikely(gact->current_gate_status & GATE_ACT_PENDING)) {
+               spin_unlock(&gact->tcf_lock);
+               return gact->tcf_action;
+       }
+
+       if (!(gact->current_gate_status & GATE_ACT_GATE_OPEN))
+               goto drop;
+
+       if (gact->current_max_octets >= 0) {
+               gact->current_entry_octets += qdisc_pkt_len(skb);
+               if (gact->current_entry_octets > gact->current_max_octets) {
+                       gact->tcf_qstats.overlimits++;
+                       goto drop;
+               }
+       }
+
+       spin_unlock(&gact->tcf_lock);
+
+       return gact->tcf_action;
+drop:
+       gact->tcf_qstats.drops++;
+       spin_unlock(&gact->tcf_lock);
+
+       return TC_ACT_SHOT;
+}
+
+static const struct nla_policy entry_policy[TCA_GATE_ENTRY_MAX + 1] = {
+       [TCA_GATE_ENTRY_INDEX]          = { .type = NLA_U32 },
+       [TCA_GATE_ENTRY_GATE]           = { .type = NLA_FLAG },
+       [TCA_GATE_ENTRY_INTERVAL]       = { .type = NLA_U32 },
+       [TCA_GATE_ENTRY_IPV]            = { .type = NLA_S32 },
+       [TCA_GATE_ENTRY_MAX_OCTETS]     = { .type = NLA_S32 },
+};
+
+static const struct nla_policy gate_policy[TCA_GATE_MAX + 1] = {
+       [TCA_GATE_PARMS]                = { .len = sizeof(struct tc_gate),
+                                           .type = NLA_EXACT_LEN },
+       [TCA_GATE_PRIORITY]             = { .type = NLA_S32 },
+       [TCA_GATE_ENTRY_LIST]           = { .type = NLA_NESTED },
+       [TCA_GATE_BASE_TIME]            = { .type = NLA_U64 },
+       [TCA_GATE_CYCLE_TIME]           = { .type = NLA_U64 },
+       [TCA_GATE_CYCLE_TIME_EXT]       = { .type = NLA_U64 },
+       [TCA_GATE_FLAGS]                = { .type = NLA_U32 },
+       [TCA_GATE_CLOCKID]              = { .type = NLA_S32 },
+};
+
+static int fill_gate_entry(struct nlattr **tb, struct tcfg_gate_entry *entry,
+                          struct netlink_ext_ack *extack)
+{
+       u32 interval = 0;
+
+       entry->gate_state = nla_get_flag(tb[TCA_GATE_ENTRY_GATE]);
+
+       if (tb[TCA_GATE_ENTRY_INTERVAL])
+               interval = nla_get_u32(tb[TCA_GATE_ENTRY_INTERVAL]);
+
+       if (interval == 0) {
+               NL_SET_ERR_MSG(extack, "Invalid interval for schedule entry");
+               return -EINVAL;
+       }
+
+       entry->interval = interval;
+
+       if (tb[TCA_GATE_ENTRY_IPV])
+               entry->ipv = nla_get_s32(tb[TCA_GATE_ENTRY_IPV]);
+       else
+               entry->ipv = -1;
+
+       if (tb[TCA_GATE_ENTRY_MAX_OCTETS])
+               entry->maxoctets = nla_get_s32(tb[TCA_GATE_ENTRY_MAX_OCTETS]);
+       else
+               entry->maxoctets = -1;
+
+       return 0;
+}
+
+static int parse_gate_entry(struct nlattr *n, struct  tcfg_gate_entry *entry,
+                           int index, struct netlink_ext_ack *extack)
+{
+       struct nlattr *tb[TCA_GATE_ENTRY_MAX + 1] = { };
+       int err;
+
+       err = nla_parse_nested(tb, TCA_GATE_ENTRY_MAX, n, entry_policy, extack);
+       if (err < 0) {
+               NL_SET_ERR_MSG(extack, "Could not parse nested entry");
+               return -EINVAL;
+       }
+
+       entry->index = index;
+
+       return fill_gate_entry(tb, entry, extack);
+}
+
+static void release_entry_list(struct list_head *entries)
+{
+       struct tcfg_gate_entry *entry, *e;
+
+       list_for_each_entry_safe(entry, e, entries, list) {
+               list_del(&entry->list);
+               kfree(entry);
+       }
+}
+
+static int parse_gate_list(struct nlattr *list_attr,
+                          struct tcf_gate_params *sched,
+                          struct netlink_ext_ack *extack)
+{
+       struct tcfg_gate_entry *entry;
+       struct nlattr *n;
+       int err, rem;
+       int i = 0;
+
+       if (!list_attr)
+               return -EINVAL;
+
+       nla_for_each_nested(n, list_attr, rem) {
+               if (nla_type(n) != TCA_GATE_ONE_ENTRY) {
+                       NL_SET_ERR_MSG(extack, "Attribute isn't type 'entry'");
+                       continue;
+               }
+
+               entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+               if (!entry) {
+                       NL_SET_ERR_MSG(extack, "Not enough memory for entry");
+                       err = -ENOMEM;
+                       goto release_list;
+               }
+
+               err = parse_gate_entry(n, entry, i, extack);
+               if (err < 0) {
+                       kfree(entry);
+                       goto release_list;
+               }
+
+               list_add_tail(&entry->list, &sched->entries);
+               i++;
+       }
+
+       sched->num_entries = i;
+
+       return i;
+
+release_list:
+       release_entry_list(&sched->entries);
+
+       return err;
+}
+
+static int tcf_gate_init(struct net *net, struct nlattr *nla,
+                        struct nlattr *est, struct tc_action **a,
+                        int ovr, int bind, bool rtnl_held,
+                        struct tcf_proto *tp, u32 flags,
+                        struct netlink_ext_ack *extack)
+{
+       struct tc_action_net *tn = net_generic(net, gate_net_id);
+       enum tk_offsets tk_offset = TK_OFFS_TAI;
+       struct nlattr *tb[TCA_GATE_MAX + 1];
+       struct tcf_chain *goto_ch = NULL;
+       struct tcf_gate_params *p;
+       s32 clockid = CLOCK_TAI;
+       struct tcf_gate *gact;
+       struct tc_gate *parm;
+       int ret = 0, err;
+       u64 basetime = 0;
+       u32 gflags = 0;
+       s32 prio = -1;
+       ktime_t start;
+       u32 index;
+
+       if (!nla)
+               return -EINVAL;
+
+       err = nla_parse_nested(tb, TCA_GATE_MAX, nla, gate_policy, extack);
+       if (err < 0)
+               return err;
+
+       if (!tb[TCA_GATE_PARMS])
+               return -EINVAL;
+
+       parm = nla_data(tb[TCA_GATE_PARMS]);
+       index = parm->index;
+
+       err = tcf_idr_check_alloc(tn, &index, a, bind);
+       if (err < 0)
+               return err;
+
+       if (err && bind)
+               return 0;
+
+       if (!err) {
+               ret = tcf_idr_create(tn, index, est, a,
+                                    &act_gate_ops, bind, false, 0);
+               if (ret) {
+                       tcf_idr_cleanup(tn, index);
+                       return ret;
+               }
+
+               ret = ACT_P_CREATED;
+       } else if (!ovr) {
+               tcf_idr_release(*a, bind);
+               return -EEXIST;
+       }
+
+       if (tb[TCA_GATE_PRIORITY])
+               prio = nla_get_s32(tb[TCA_GATE_PRIORITY]);
+
+       if (tb[TCA_GATE_BASE_TIME])
+               basetime = nla_get_u64(tb[TCA_GATE_BASE_TIME]);
+
+       if (tb[TCA_GATE_FLAGS])
+               gflags = nla_get_u32(tb[TCA_GATE_FLAGS]);
+
+       if (tb[TCA_GATE_CLOCKID]) {
+               clockid = nla_get_s32(tb[TCA_GATE_CLOCKID]);
+               switch (clockid) {
+               case CLOCK_REALTIME:
+                       tk_offset = TK_OFFS_REAL;
+                       break;
+               case CLOCK_MONOTONIC:
+                       tk_offset = TK_OFFS_MAX;
+                       break;
+               case CLOCK_BOOTTIME:
+                       tk_offset = TK_OFFS_BOOT;
+                       break;
+               case CLOCK_TAI:
+                       tk_offset = TK_OFFS_TAI;
+                       break;
+               default:
+                       NL_SET_ERR_MSG(extack, "Invalid 'clockid'");
+                       goto release_idr;
+               }
+       }
+
+       err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
+       if (err < 0)
+               goto release_idr;
+
+       gact = to_gate(*a);
+
+       spin_lock_bh(&gact->tcf_lock);
+       p = &gact->param;
+
+       if (tb[TCA_GATE_CYCLE_TIME]) {
+               p->tcfg_cycletime = nla_get_u64(tb[TCA_GATE_CYCLE_TIME]);
+               if (!p->tcfg_cycletime_ext)
+                       goto chain_put;
+       }
+
+       INIT_LIST_HEAD(&p->entries);
+       if (tb[TCA_GATE_ENTRY_LIST]) {
+               err = parse_gate_list(tb[TCA_GATE_ENTRY_LIST], p, extack);
+               if (err < 0)
+                       goto chain_put;
+       }
+
+       if (!p->tcfg_cycletime) {
+               struct tcfg_gate_entry *entry;
+               ktime_t cycle = 0;
+
+               list_for_each_entry(entry, &p->entries, list)
+                       cycle = ktime_add_ns(cycle, entry->interval);
+               p->tcfg_cycletime = cycle;
+       }
+
+       if (tb[TCA_GATE_CYCLE_TIME_EXT])
+               p->tcfg_cycletime_ext =
+                       nla_get_u64(tb[TCA_GATE_CYCLE_TIME_EXT]);
+
+       p->tcfg_priority = prio;
+       p->tcfg_basetime = basetime;
+       p->tcfg_clockid = clockid;
+       p->tcfg_flags = gflags;
+
+       gact->tk_offset = tk_offset;
+       hrtimer_init(&gact->hitimer, clockid, HRTIMER_MODE_ABS_SOFT);
+       gact->hitimer.function = gate_timer_func;
+
+       err = gate_get_start_time(gact, &start);
+       if (err < 0) {
+               NL_SET_ERR_MSG(extack,
+                              "Internal error: failed get start time");
+               release_entry_list(&p->entries);
+               goto chain_put;
+       }
+
+       gact->current_close_time = start;
+       gact->current_gate_status = GATE_ACT_GATE_OPEN | GATE_ACT_PENDING;
+
+       gact->next_entry = list_first_entry(&p->entries,
+                                           struct tcfg_gate_entry, list);
+
+       goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
+
+       gate_start_timer(gact, start);
+
+       spin_unlock_bh(&gact->tcf_lock);
+
+       if (goto_ch)
+               tcf_chain_put_by_act(goto_ch);
+
+       if (ret == ACT_P_CREATED)
+               tcf_idr_insert(tn, *a);
+
+       return ret;
+
+chain_put:
+       spin_unlock_bh(&gact->tcf_lock);
+
+       if (goto_ch)
+               tcf_chain_put_by_act(goto_ch);
+release_idr:
+       tcf_idr_release(*a, bind);
+       return err;
+}
+
+static void tcf_gate_cleanup(struct tc_action *a)
+{
+       struct tcf_gate *gact = to_gate(a);
+       struct tcf_gate_params *p;
+
+       hrtimer_cancel(&gact->hitimer);
+
+       p = &gact->param;
+
+       release_entry_list(&p->entries);
+}
+
+static int dumping_entry(struct sk_buff *skb,
+                        struct tcfg_gate_entry *entry)
+{
+       struct nlattr *item;
+
+       item = nla_nest_start_noflag(skb, TCA_GATE_ONE_ENTRY);
+       if (!item)
+               return -ENOSPC;
+
+       if (nla_put_u32(skb, TCA_GATE_ENTRY_INDEX, entry->index))
+               goto nla_put_failure;
+
+       if (entry->gate_state && nla_put_flag(skb, TCA_GATE_ENTRY_GATE))
+               goto nla_put_failure;
+
+       if (nla_put_u32(skb, TCA_GATE_ENTRY_INTERVAL, entry->interval))
+               goto nla_put_failure;
+
+       if (nla_put_s32(skb, TCA_GATE_ENTRY_MAX_OCTETS, entry->maxoctets))
+               goto nla_put_failure;
+
+       if (nla_put_s32(skb, TCA_GATE_ENTRY_IPV, entry->ipv))
+               goto nla_put_failure;
+
+       return nla_nest_end(skb, item);
+
+nla_put_failure:
+       nla_nest_cancel(skb, item);
+       return -1;
+}
+
+static int tcf_gate_dump(struct sk_buff *skb, struct tc_action *a,
+                        int bind, int ref)
+{
+       unsigned char *b = skb_tail_pointer(skb);
+       struct tcf_gate *gact = to_gate(a);
+       struct tc_gate opt = {
+               .index    = gact->tcf_index,
+               .refcnt   = refcount_read(&gact->tcf_refcnt) - ref,
+               .bindcnt  = atomic_read(&gact->tcf_bindcnt) - bind,
+       };
+       struct tcfg_gate_entry *entry;
+       struct tcf_gate_params *p;
+       struct nlattr *entry_list;
+       struct tcf_t t;
+
+       spin_lock_bh(&gact->tcf_lock);
+       opt.action = gact->tcf_action;
+
+       p = &gact->param;
+
+       if (nla_put(skb, TCA_GATE_PARMS, sizeof(opt), &opt))
+               goto nla_put_failure;
+
+       if (nla_put_u64_64bit(skb, TCA_GATE_BASE_TIME,
+                             p->tcfg_basetime, TCA_GATE_PAD))
+               goto nla_put_failure;
+
+       if (nla_put_u64_64bit(skb, TCA_GATE_CYCLE_TIME,
+                             p->tcfg_cycletime, TCA_GATE_PAD))
+               goto nla_put_failure;
+
+       if (nla_put_u64_64bit(skb, TCA_GATE_CYCLE_TIME_EXT,
+                             p->tcfg_cycletime_ext, TCA_GATE_PAD))
+               goto nla_put_failure;
+
+       if (nla_put_s32(skb, TCA_GATE_CLOCKID, p->tcfg_clockid))
+               goto nla_put_failure;
+
+       if (nla_put_u32(skb, TCA_GATE_FLAGS, p->tcfg_flags))
+               goto nla_put_failure;
+
+       if (nla_put_s32(skb, TCA_GATE_PRIORITY, p->tcfg_priority))
+               goto nla_put_failure;
+
+       entry_list = nla_nest_start_noflag(skb, TCA_GATE_ENTRY_LIST);
+       if (!entry_list)
+               goto nla_put_failure;
+
+       list_for_each_entry(entry, &p->entries, list) {
+               if (dumping_entry(skb, entry) < 0)
+                       goto nla_put_failure;
+       }
+
+       nla_nest_end(skb, entry_list);
+
+       tcf_tm_dump(&t, &gact->tcf_tm);
+       if (nla_put_64bit(skb, TCA_GATE_TM, sizeof(t), &t, TCA_GATE_PAD))
+               goto nla_put_failure;
+       spin_unlock_bh(&gact->tcf_lock);
+
+       return skb->len;
+
+nla_put_failure:
+       spin_unlock_bh(&gact->tcf_lock);
+       nlmsg_trim(skb, b);
+       return -1;
+}
+
+static int tcf_gate_walker(struct net *net, struct sk_buff *skb,
+                          struct netlink_callback *cb, int type,
+                          const struct tc_action_ops *ops,
+                          struct netlink_ext_ack *extack)
+{
+       struct tc_action_net *tn = net_generic(net, gate_net_id);
+
+       return tcf_generic_walker(tn, skb, cb, type, ops, extack);
+}
+
+static void tcf_gate_stats_update(struct tc_action *a, u64 bytes, u32 packets,
+                                 u64 lastuse, bool hw)
+{
+       struct tcf_gate *gact = to_gate(a);
+       struct tcf_t *tm = &gact->tcf_tm;
+
+       tcf_action_update_stats(a, bytes, packets, false, hw);
+       tm->lastuse = max_t(u64, tm->lastuse, lastuse);
+}
+
+static int tcf_gate_search(struct net *net, struct tc_action **a, u32 index)
+{
+       struct tc_action_net *tn = net_generic(net, gate_net_id);
+
+       return tcf_idr_search(tn, a, index);
+}
+
+static size_t tcf_gate_get_fill_size(const struct tc_action *act)
+{
+       return nla_total_size(sizeof(struct tc_gate));
+}
+
+static struct tc_action_ops act_gate_ops = {
+       .kind           =       "gate",
+       .id             =       TCA_ID_GATE,
+       .owner          =       THIS_MODULE,
+       .act            =       tcf_gate_act,
+       .dump           =       tcf_gate_dump,
+       .init           =       tcf_gate_init,
+       .cleanup        =       tcf_gate_cleanup,
+       .walk           =       tcf_gate_walker,
+       .stats_update   =       tcf_gate_stats_update,
+       .get_fill_size  =       tcf_gate_get_fill_size,
+       .lookup         =       tcf_gate_search,
+       .size           =       sizeof(struct tcf_gate),
+};
+
+static __net_init int gate_init_net(struct net *net)
+{
+       struct tc_action_net *tn = net_generic(net, gate_net_id);
+
+       return tc_action_net_init(net, tn, &act_gate_ops);
+}
+
+static void __net_exit gate_exit_net(struct list_head *net_list)
+{
+       tc_action_net_exit(net_list, gate_net_id);
+}
+
+static struct pernet_operations gate_net_ops = {
+       .init = gate_init_net,
+       .exit_batch = gate_exit_net,
+       .id   = &gate_net_id,
+       .size = sizeof(struct tc_action_net),
+};
+
+static int __init gate_init_module(void)
+{
+       return tcf_register_action(&act_gate_ops, &gate_net_ops);
+}
+
+static void __exit gate_cleanup_module(void)
+{
+       tcf_unregister_action(&act_gate_ops, &gate_net_ops);
+}
+
+module_init(gate_init_module);
+module_exit(gate_cleanup_module);
+MODULE_LICENSE("GPL v2");
index f6a3b96..299b963 100644 (file)
@@ -39,6 +39,7 @@
 #include <net/tc_act/tc_skbedit.h>
 #include <net/tc_act/tc_ct.h>
 #include <net/tc_act/tc_mpls.h>
+#include <net/tc_act/tc_gate.h>
 #include <net/flow_offload.h>
 
 extern const struct nla_policy rtm_tca_policy[TCA_MAX + 1];
@@ -735,8 +736,11 @@ static int tcf_block_offload_cmd(struct tcf_block *block,
        INIT_LIST_HEAD(&bo.cb_list);
 
        err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo);
-       if (err < 0)
+       if (err < 0) {
+               if (err != -EOPNOTSUPP)
+                       NL_SET_ERR_MSG(extack, "Driver ndo_setup_tc failed");
                return err;
+       }
 
        return tcf_block_setup(block, &bo);
 }
@@ -1667,6 +1671,7 @@ int tcf_classify_ingress(struct sk_buff *skb,
                skb_ext_del(skb, TC_SKB_EXT);
 
                tp = rcu_dereference_bh(fchain->filter_chain);
+               last_executed_chain = fchain->index;
        }
 
        ret = __tcf_classify(skb, tp, orig_tp, res, compat_mode,
@@ -2069,6 +2074,7 @@ replay:
                err = PTR_ERR(block);
                goto errout;
        }
+       block->classid = parent;
 
        chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
        if (chain_index > TC_ACT_EXT_VAL_MASK) {
@@ -2611,12 +2617,10 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
                        return skb->len;
 
                parent = tcm->tcm_parent;
-               if (!parent) {
+               if (!parent)
                        q = dev->qdisc;
-                       parent = q->handle;
-               } else {
+               else
                        q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
-               }
                if (!q)
                        goto out;
                cops = q->ops->cl_ops;
@@ -2632,6 +2636,7 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
                block = cops->tcf_block(q, cl, NULL);
                if (!block)
                        goto out;
+               parent = block->classid;
                if (tcf_block_shared(block))
                        q = NULL;
        }
@@ -3522,6 +3527,37 @@ static void tcf_sample_get_group(struct flow_action_entry *entry,
 #endif
 }
 
+static void tcf_gate_entry_destructor(void *priv)
+{
+       struct action_gate_entry *oe = priv;
+
+       kfree(oe);
+}
+
+static int tcf_gate_get_entries(struct flow_action_entry *entry,
+                               const struct tc_action *act)
+{
+       entry->gate.entries = tcf_gate_get_list(act);
+
+       if (!entry->gate.entries)
+               return -EINVAL;
+
+       entry->destructor = tcf_gate_entry_destructor;
+       entry->destructor_priv = entry->gate.entries;
+
+       return 0;
+}
+
+static enum flow_action_hw_stats tc_act_hw_stats(u8 hw_stats)
+{
+       if (WARN_ON_ONCE(hw_stats > TCA_ACT_HW_STATS_ANY))
+               return FLOW_ACTION_HW_STATS_DONT_CARE;
+       else if (!hw_stats)
+               return FLOW_ACTION_HW_STATS_DISABLED;
+
+       return hw_stats;
+}
+
 int tc_setup_flow_action(struct flow_action *flow_action,
                         const struct tcf_exts *exts)
 {
@@ -3545,7 +3581,7 @@ int tc_setup_flow_action(struct flow_action *flow_action,
                if (err)
                        goto err_out_locked;
 
-               entry->hw_stats = act->hw_stats;
+               entry->hw_stats = tc_act_hw_stats(act->hw_stats);
 
                if (is_tcf_gact_ok(act)) {
                        entry->id = FLOW_ACTION_ACCEPT;
@@ -3613,7 +3649,7 @@ int tc_setup_flow_action(struct flow_action *flow_action,
                                entry->mangle.mask = tcf_pedit_mask(act, k);
                                entry->mangle.val = tcf_pedit_val(act, k);
                                entry->mangle.offset = tcf_pedit_offset(act, k);
-                               entry->hw_stats = act->hw_stats;
+                               entry->hw_stats = tc_act_hw_stats(act->hw_stats);
                                entry = &flow_action->entries[++j];
                        }
                } else if (is_tcf_csum(act)) {
@@ -3668,6 +3704,17 @@ int tc_setup_flow_action(struct flow_action *flow_action,
                } else if (is_tcf_skbedit_priority(act)) {
                        entry->id = FLOW_ACTION_PRIORITY;
                        entry->priority = tcf_skbedit_priority(act);
+               } else if (is_tcf_gate(act)) {
+                       entry->id = FLOW_ACTION_GATE;
+                       entry->gate.index = tcf_gate_index(act);
+                       entry->gate.prio = tcf_gate_prio(act);
+                       entry->gate.basetime = tcf_gate_basetime(act);
+                       entry->gate.cycletime = tcf_gate_cycletime(act);
+                       entry->gate.cycletimeext = tcf_gate_cycletimeext(act);
+                       entry->gate.num_entries = tcf_gate_num_entries(act);
+                       err = tcf_gate_get_entries(entry, act);
+                       if (err)
+                               goto err_out;
                } else {
                        err = -EOPNOTSUPP;
                        goto err_out_locked;
index eecfe07..18755d2 100644 (file)
@@ -199,7 +199,7 @@ static void em_ipt_destroy(struct tcf_ematch *em)
                im->match->destroy(&par);
        }
        module_put(im->match->me);
-       kfree((void *)im);
+       kfree(im);
 }
 
 static int em_ipt_match(struct sk_buff *skb, struct tcf_ematch *em,
index a36974e..bd618b0 100644 (file)
@@ -131,7 +131,6 @@ static void choke_drop_by_idx(struct Qdisc *sch, unsigned int idx,
 }
 
 struct choke_skb_cb {
-       u16                     classid;
        u8                      keys_valid;
        struct                  flow_keys_digest keys;
 };
@@ -142,11 +141,6 @@ static inline struct choke_skb_cb *choke_skb_cb(const struct sk_buff *skb)
        return (struct choke_skb_cb *)qdisc_skb_cb(skb)->data;
 }
 
-static inline void choke_set_classid(struct sk_buff *skb, u16 classid)
-{
-       choke_skb_cb(skb)->classid = classid;
-}
-
 /*
  * Compare flow of two packets
  *  Returns true only if source and destination address and port match.
@@ -323,7 +317,8 @@ static void choke_reset(struct Qdisc *sch)
 
        sch->q.qlen = 0;
        sch->qstats.backlog = 0;
-       memset(q->tab, 0, (q->tab_mask + 1) * sizeof(struct sk_buff *));
+       if (q->tab)
+               memset(q->tab, 0, (q->tab_mask + 1) * sizeof(struct sk_buff *));
        q->head = q->tail = 0;
        red_restart(&q->vars);
 }
index b1da558..c48f910 100644 (file)
@@ -82,7 +82,7 @@ static bool is_packet_valid(struct Qdisc *sch, struct sk_buff *nskb)
        if (q->skip_sock_check)
                goto skip;
 
-       if (!sk)
+       if (!sk || !sk_fullsock(sk))
                return false;
 
        if (!sock_flag(sk, SOCK_TXTIME))
@@ -137,8 +137,9 @@ static void report_sock_error(struct sk_buff *skb, u32 err, u8 code)
        struct sock_exterr_skb *serr;
        struct sk_buff *clone;
        ktime_t txtime = skb->tstamp;
+       struct sock *sk = skb->sk;
 
-       if (!skb->sk || !(skb->sk->sk_txtime_report_errors))
+       if (!sk || !sk_fullsock(sk) || !(sk->sk_txtime_report_errors))
                return;
 
        clone = skb_clone(skb, GFP_ATOMIC);
@@ -154,7 +155,7 @@ static void report_sock_error(struct sk_buff *skb, u32 err, u8 code)
        serr->ee.ee_data = (txtime >> 32); /* high part of tstamp */
        serr->ee.ee_info = txtime; /* low part of tstamp */
 
-       if (sock_queue_err_skb(skb->sk, clone))
+       if (sock_queue_err_skb(sk, clone))
                kfree_skb(clone);
 }
 
index 4c06013..8f06a80 100644 (file)
@@ -66,22 +66,27 @@ static inline struct fq_skb_cb *fq_skb_cb(struct sk_buff *skb)
  * in linear list (head,tail), otherwise are placed in a rbtree (t_root).
  */
 struct fq_flow {
+/* First cache line : used in fq_gc(), fq_enqueue(), fq_dequeue() */
        struct rb_root  t_root;
        struct sk_buff  *head;          /* list of skbs for this flow : first skb */
        union {
                struct sk_buff *tail;   /* last skb in the list */
-               unsigned long  age;     /* jiffies when flow was emptied, for gc */
+               unsigned long  age;     /* (jiffies | 1UL) when flow was emptied, for gc */
        };
        struct rb_node  fq_node;        /* anchor in fq_root[] trees */
        struct sock     *sk;
+       u32             socket_hash;    /* sk_hash */
        int             qlen;           /* number of packets in flow queue */
+
+/* Second cache line, used in fq_dequeue() */
        int             credit;
-       u32             socket_hash;    /* sk_hash */
-       struct fq_flow *next;           /* next pointer in RR lists, or &detached */
+       /* 32bit hole on 64bit arches */
+
+       struct fq_flow *next;           /* next pointer in RR lists */
 
        struct rb_node  rate_node;      /* anchor in q->delayed tree */
        u64             time_next_packet;
-};
+} ____cacheline_aligned_in_smp;
 
 struct fq_flow_head {
        struct fq_flow *first;
@@ -95,6 +100,7 @@ struct fq_sched_data {
 
        struct rb_root  delayed;        /* for rate limited flows */
        u64             time_next_delayed_flow;
+       u64             ktime_cache;    /* copy of last ktime_get_ns() */
        unsigned long   unthrottle_latency_ns;
 
        struct fq_flow  internal;       /* for non classified or high prio packets */
@@ -104,12 +110,13 @@ struct fq_sched_data {
        u32             flow_plimit;    /* max packets per flow */
        unsigned long   flow_max_rate;  /* optional max rate per flow */
        u64             ce_threshold;
+       u64             horizon;        /* horizon in ns */
        u32             orphan_mask;    /* mask for orphaned skb */
        u32             low_rate_threshold;
        struct rb_root  *fq_root;
        u8              rate_enable;
        u8              fq_trees_log;
-
+       u8              horizon_drop;
        u32             flows;
        u32             inactive_flows;
        u32             throttled_flows;
@@ -118,6 +125,8 @@ struct fq_sched_data {
        u64             stat_internal_packets;
        u64             stat_throttled;
        u64             stat_ce_mark;
+       u64             stat_horizon_drops;
+       u64             stat_horizon_caps;
        u64             stat_flows_plimit;
        u64             stat_pkts_too_long;
        u64             stat_allocation_errors;
@@ -126,20 +135,25 @@ struct fq_sched_data {
        struct qdisc_watchdog watchdog;
 };
 
-/* special value to mark a detached flow (not on old/new list) */
-static struct fq_flow detached, throttled;
-
+/*
+ * f->tail and f->age share the same location.
+ * We can use the low order bit to differentiate if this location points
+ * to a sk_buff or contains a jiffies value, if we force this value to be odd.
+ * This assumes f->tail low order bit must be 0 since alignof(struct sk_buff) >= 2
+ */
 static void fq_flow_set_detached(struct fq_flow *f)
 {
-       f->next = &detached;
-       f->age = jiffies;
+       f->age = jiffies | 1UL;
 }
 
 static bool fq_flow_is_detached(const struct fq_flow *f)
 {
-       return f->next == &detached;
+       return !!(f->age & 1UL);
 }
 
+/* special value to mark a throttled flow (not on old/new list) */
+static struct fq_flow throttled;
+
 static bool fq_flow_is_throttled(const struct fq_flow *f)
 {
        return f->next == &throttled;
@@ -204,9 +218,10 @@ static void fq_gc(struct fq_sched_data *q,
                  struct rb_root *root,
                  struct sock *sk)
 {
-       struct fq_flow *f, *tofree[FQ_GC_MAX];
        struct rb_node **p, *parent;
-       int fcnt = 0;
+       void *tofree[FQ_GC_MAX];
+       struct fq_flow *f;
+       int i, fcnt = 0;
 
        p = &root->rb_node;
        parent = NULL;
@@ -229,15 +244,18 @@ static void fq_gc(struct fq_sched_data *q,
                        p = &parent->rb_left;
        }
 
+       if (!fcnt)
+               return;
+
+       for (i = fcnt; i > 0; ) {
+               f = tofree[--i];
+               rb_erase(&f->fq_node, root);
+       }
        q->flows -= fcnt;
        q->inactive_flows -= fcnt;
        q->stat_gc_flows += fcnt;
-       while (fcnt) {
-               struct fq_flow *f = tofree[--fcnt];
 
-               rb_erase(&f->fq_node, root);
-               kmem_cache_free(fq_flow_cachep, f);
-       }
+       kmem_cache_free_bulk(fq_flow_cachep, fcnt, tofree);
 }
 
 static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q)
@@ -370,19 +388,17 @@ static void fq_erase_head(struct Qdisc *sch, struct fq_flow *flow,
        }
 }
 
-/* remove one skb from head of flow queue */
-static struct sk_buff *fq_dequeue_head(struct Qdisc *sch, struct fq_flow *flow)
+/* Remove one skb from flow queue.
+ * This skb must be the return value of prior fq_peek().
+ */
+static void fq_dequeue_skb(struct Qdisc *sch, struct fq_flow *flow,
+                          struct sk_buff *skb)
 {
-       struct sk_buff *skb = fq_peek(flow);
-
-       if (skb) {
-               fq_erase_head(sch, flow, skb);
-               skb_mark_not_on_list(skb);
-               flow->qlen--;
-               qdisc_qstats_backlog_dec(sch, skb);
-               sch->q.qlen--;
-       }
-       return skb;
+       fq_erase_head(sch, flow, skb);
+       skb_mark_not_on_list(skb);
+       flow->qlen--;
+       qdisc_qstats_backlog_dec(sch, skb);
+       sch->q.qlen--;
 }
 
 static void flow_queue_add(struct fq_flow *flow, struct sk_buff *skb)
@@ -390,8 +406,6 @@ static void flow_queue_add(struct fq_flow *flow, struct sk_buff *skb)
        struct rb_node **p, *parent;
        struct sk_buff *head, *aux;
 
-       fq_skb_cb(skb)->time_to_send = skb->tstamp ?: ktime_get_ns();
-
        head = flow->head;
        if (!head ||
            fq_skb_cb(skb)->time_to_send >= fq_skb_cb(flow->tail)->time_to_send) {
@@ -419,6 +433,12 @@ static void flow_queue_add(struct fq_flow *flow, struct sk_buff *skb)
        rb_insert_color(&skb->rbnode, &flow->t_root);
 }
 
+static bool fq_packet_beyond_horizon(const struct sk_buff *skb,
+                                   const struct fq_sched_data *q)
+{
+       return unlikely((s64)skb->tstamp > (s64)(q->ktime_cache + q->horizon));
+}
+
 static int fq_enqueue(struct sk_buff *skb, struct Qdisc *sch,
                      struct sk_buff **to_free)
 {
@@ -428,6 +448,28 @@ static int fq_enqueue(struct sk_buff *skb, struct Qdisc *sch,
        if (unlikely(sch->q.qlen >= sch->limit))
                return qdisc_drop(skb, sch, to_free);
 
+       if (!skb->tstamp) {
+               fq_skb_cb(skb)->time_to_send = q->ktime_cache = ktime_get_ns();
+       } else {
+               /* Check if packet timestamp is too far in the future.
+                * Try first if our cached value, to avoid ktime_get_ns()
+                * cost in most cases.
+                */
+               if (fq_packet_beyond_horizon(skb, q)) {
+                       /* Refresh our cache and check another time */
+                       q->ktime_cache = ktime_get_ns();
+                       if (fq_packet_beyond_horizon(skb, q)) {
+                               if (q->horizon_drop) {
+                                       q->stat_horizon_drops++;
+                                       return qdisc_drop(skb, sch, to_free);
+                               }
+                               q->stat_horizon_caps++;
+                               skb->tstamp = q->ktime_cache + q->horizon;
+                       }
+               }
+               fq_skb_cb(skb)->time_to_send = skb->tstamp;
+       }
+
        f = fq_classify(skb, q);
        if (unlikely(f->qlen >= q->flow_plimit && f != &q->internal)) {
                q->stat_flows_plimit++;
@@ -494,11 +536,13 @@ static struct sk_buff *fq_dequeue(struct Qdisc *sch)
        if (!sch->q.qlen)
                return NULL;
 
-       skb = fq_dequeue_head(sch, &q->internal);
-       if (skb)
+       skb = fq_peek(&q->internal);
+       if (unlikely(skb)) {
+               fq_dequeue_skb(sch, &q->internal, skb);
                goto out;
+       }
 
-       now = ktime_get_ns();
+       q->ktime_cache = now = ktime_get_ns();
        fq_check_throttled(q, now);
 begin:
        head = &q->new_flows;
@@ -532,14 +576,13 @@ begin:
                        fq_flow_set_throttled(q, f);
                        goto begin;
                }
+               prefetch(&skb->end);
                if ((s64)(now - time_next_packet - q->ce_threshold) > 0) {
                        INET_ECN_set_ce(skb);
                        q->stat_ce_mark++;
                }
-       }
-
-       skb = fq_dequeue_head(sch, f);
-       if (!skb) {
+               fq_dequeue_skb(sch, f, skb);
+       } else {
                head->first = f->next;
                /* force a pass through old_flows to prevent starvation */
                if ((head == &q->new_flows) && q->old_flows.first) {
@@ -550,7 +593,6 @@ begin:
                }
                goto begin;
        }
-       prefetch(&skb->end);
        plen = qdisc_pkt_len(skb);
        f->credit -= plen;
 
@@ -753,6 +795,8 @@ static const struct nla_policy fq_policy[TCA_FQ_MAX + 1] = {
        [TCA_FQ_LOW_RATE_THRESHOLD]     = { .type = NLA_U32 },
        [TCA_FQ_CE_THRESHOLD]           = { .type = NLA_U32 },
        [TCA_FQ_TIMER_SLACK]            = { .type = NLA_U32 },
+       [TCA_FQ_HORIZON]                = { .type = NLA_U32 },
+       [TCA_FQ_HORIZON_DROP]           = { .type = NLA_U8 },
 };
 
 static int fq_change(struct Qdisc *sch, struct nlattr *opt,
@@ -842,7 +886,15 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt,
        if (tb[TCA_FQ_TIMER_SLACK])
                q->timer_slack = nla_get_u32(tb[TCA_FQ_TIMER_SLACK]);
 
+       if (tb[TCA_FQ_HORIZON])
+               q->horizon = (u64)NSEC_PER_USEC *
+                                 nla_get_u32(tb[TCA_FQ_HORIZON]);
+
+       if (tb[TCA_FQ_HORIZON_DROP])
+               q->horizon_drop = nla_get_u8(tb[TCA_FQ_HORIZON_DROP]);
+
        if (!err) {
+
                sch_tree_unlock(sch);
                err = fq_resize(sch, fq_log);
                sch_tree_lock(sch);
@@ -895,6 +947,9 @@ static int fq_init(struct Qdisc *sch, struct nlattr *opt,
 
        q->timer_slack = 10 * NSEC_PER_USEC; /* 10 usec of hrtimer slack */
 
+       q->horizon = 10ULL * NSEC_PER_SEC; /* 10 seconds */
+       q->horizon_drop = 1; /* by default, drop packets beyond horizon */
+
        /* Default ce_threshold of 4294 seconds */
        q->ce_threshold         = (u64)NSEC_PER_USEC * ~0U;
 
@@ -912,6 +967,7 @@ static int fq_dump(struct Qdisc *sch, struct sk_buff *skb)
 {
        struct fq_sched_data *q = qdisc_priv(sch);
        u64 ce_threshold = q->ce_threshold;
+       u64 horizon = q->horizon;
        struct nlattr *opts;
 
        opts = nla_nest_start_noflag(skb, TCA_OPTIONS);
@@ -921,6 +977,7 @@ static int fq_dump(struct Qdisc *sch, struct sk_buff *skb)
        /* TCA_FQ_FLOW_DEFAULT_RATE is not used anymore */
 
        do_div(ce_threshold, NSEC_PER_USEC);
+       do_div(horizon, NSEC_PER_USEC);
 
        if (nla_put_u32(skb, TCA_FQ_PLIMIT, sch->limit) ||
            nla_put_u32(skb, TCA_FQ_FLOW_PLIMIT, q->flow_plimit) ||
@@ -936,7 +993,9 @@ static int fq_dump(struct Qdisc *sch, struct sk_buff *skb)
                        q->low_rate_threshold) ||
            nla_put_u32(skb, TCA_FQ_CE_THRESHOLD, (u32)ce_threshold) ||
            nla_put_u32(skb, TCA_FQ_BUCKETS_LOG, q->fq_trees_log) ||
-           nla_put_u32(skb, TCA_FQ_TIMER_SLACK, q->timer_slack))
+           nla_put_u32(skb, TCA_FQ_TIMER_SLACK, q->timer_slack) ||
+           nla_put_u32(skb, TCA_FQ_HORIZON, (u32)horizon) ||
+           nla_put_u8(skb, TCA_FQ_HORIZON_DROP, q->horizon_drop))
                goto nla_put_failure;
 
        return nla_nest_end(skb, opts);
@@ -967,6 +1026,8 @@ static int fq_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
        st.unthrottle_latency_ns  = min_t(unsigned long,
                                          q->unthrottle_latency_ns, ~0U);
        st.ce_mark                = q->stat_ce_mark;
+       st.horizon_drops          = q->stat_horizon_drops;
+       st.horizon_caps           = q->stat_horizon_caps;
        sch_tree_unlock(sch);
 
        return gnet_stats_copy_app(d, &st, sizeof(st));
index 968519f..436160b 100644 (file)
@@ -416,7 +416,7 @@ static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt,
                q->quantum = max(256U, nla_get_u32(tb[TCA_FQ_CODEL_QUANTUM]));
 
        if (tb[TCA_FQ_CODEL_DROP_BATCH_SIZE])
-               q->drop_batch_size = min(1U, nla_get_u32(tb[TCA_FQ_CODEL_DROP_BATCH_SIZE]));
+               q->drop_batch_size = max(1U, nla_get_u32(tb[TCA_FQ_CODEL_DROP_BATCH_SIZE]));
 
        if (tb[TCA_FQ_CODEL_MEMORY_LIMIT])
                q->memory_limit = min(1U << 31, nla_get_u32(tb[TCA_FQ_CODEL_MEMORY_LIMIT]));
index 2efd5b6..ebc55d8 100644 (file)
@@ -794,6 +794,9 @@ struct Qdisc_ops pfifo_fast_ops __read_mostly = {
 };
 EXPORT_SYMBOL(pfifo_fast_ops);
 
+static struct lock_class_key qdisc_tx_busylock;
+static struct lock_class_key qdisc_running_key;
+
 struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
                          const struct Qdisc_ops *ops,
                          struct netlink_ext_ack *extack)
@@ -846,9 +849,17 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
        }
 
        spin_lock_init(&sch->busylock);
+       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);
 
        sch->ops = ops;
        sch->flags = ops->static_flags;
@@ -859,12 +870,6 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
        dev_hold(dev);
        refcount_set(&sch->refcnt, 1);
 
-       if (sch != &noop_qdisc) {
-               lockdep_set_class(&sch->busylock, &dev->qdisc_tx_busylock_key);
-               lockdep_set_class(&sch->seqlock, &dev->qdisc_tx_busylock_key);
-               lockdep_set_class(&sch->running, &dev->qdisc_running_key);
-       }
-
        return sch;
 errout1:
        kfree(p);
@@ -1037,10 +1042,9 @@ static void attach_one_default_qdisc(struct net_device *dev,
                ops = &pfifo_fast_ops;
 
        qdisc = qdisc_create_dflt(dev_queue, ops, TC_H_ROOT, NULL);
-       if (!qdisc) {
-               netdev_info(dev, "activation failed\n");
+       if (!qdisc)
                return;
-       }
+
        if (!netif_is_multiqueue(dev))
                qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
        dev_queue->qdisc_sleeping = qdisc;
@@ -1065,6 +1069,18 @@ static void attach_default_qdiscs(struct net_device *dev)
                        qdisc->ops->attach(qdisc);
                }
        }
+
+       /* Detect default qdisc setup/init failed and fallback to "noqueue" */
+       if (dev->qdisc == &noop_qdisc) {
+               netdev_warn(dev, "default qdisc (%s) fail, fallback to %s\n",
+                           default_qdisc_ops->id, noqueue_qdisc_ops.id);
+               dev->priv_flags |= IFF_NO_QUEUE;
+               netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL);
+               dev->qdisc = txq->qdisc_sleeping;
+               qdisc_refcount_inc(dev->qdisc);
+               dev->priv_flags ^= IFF_NO_QUEUE;
+       }
+
 #ifdef CONFIG_NET_SCHED
        if (dev->qdisc != &noop_qdisc)
                qdisc_hash_add(dev->qdisc, false);
index c7de47c..555a1b9 100644 (file)
@@ -48,7 +48,7 @@ struct red_sched_data {
        struct Qdisc            *qdisc;
 };
 
-static const u32 red_supported_flags = TC_RED_HISTORIC_FLAGS | TC_RED_NODROP;
+#define TC_RED_SUPPORTED_FLAGS (TC_RED_HISTORIC_FLAGS | TC_RED_NODROP)
 
 static inline int red_use_ecn(struct red_sched_data *q)
 {
@@ -212,8 +212,7 @@ static const struct nla_policy red_policy[TCA_RED_MAX + 1] = {
        [TCA_RED_PARMS] = { .len = sizeof(struct tc_red_qopt) },
        [TCA_RED_STAB]  = { .len = RED_STAB_SIZE },
        [TCA_RED_MAX_P] = { .type = NLA_U32 },
-       [TCA_RED_FLAGS] = { .type = NLA_BITFIELD32,
-                           .validation_data = &red_supported_flags },
+       [TCA_RED_FLAGS] = NLA_POLICY_BITFIELD32(TC_RED_SUPPORTED_FLAGS),
 };
 
 static int red_change(struct Qdisc *sch, struct nlattr *opt,
@@ -248,7 +247,7 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt,
                return -EINVAL;
 
        err = red_get_flags(ctl->flags, TC_RED_HISTORIC_FLAGS,
-                           tb[TCA_RED_FLAGS], red_supported_flags,
+                           tb[TCA_RED_FLAGS], TC_RED_SUPPORTED_FLAGS,
                            &flags_bf, &userbits, extack);
        if (err)
                return err;
@@ -372,7 +371,7 @@ static int red_dump(struct Qdisc *sch, struct sk_buff *skb)
        if (nla_put(skb, TCA_RED_PARMS, sizeof(opt), &opt) ||
            nla_put_u32(skb, TCA_RED_MAX_P, q->parms.max_P) ||
            nla_put_bitfield32(skb, TCA_RED_FLAGS,
-                              q->flags, red_supported_flags))
+                              q->flags, TC_RED_SUPPORTED_FLAGS))
                goto nla_put_failure;
        return nla_nest_end(skb, opts);
 
index c787d4d..5a6def5 100644 (file)
@@ -637,6 +637,15 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt)
        if (ctl->divisor &&
            (!is_power_of_2(ctl->divisor) || ctl->divisor > 65536))
                return -EINVAL;
+
+       /* slot->allot is a short, make sure quantum is not too big. */
+       if (ctl->quantum) {
+               unsigned int scaled = SFQ_ALLOT_SIZE(ctl->quantum);
+
+               if (scaled <= 0 || scaled > SHRT_MAX)
+                       return -EINVAL;
+       }
+
        if (ctl_v1 && !red_check_params(ctl_v1->qth_min, ctl_v1->qth_max,
                                        ctl_v1->Wlog))
                return -EINVAL;
index 0fb10ab..7a5e4c4 100644 (file)
@@ -169,6 +169,9 @@ static int skbprio_change(struct Qdisc *sch, struct nlattr *opt,
 {
        struct tc_skbprio_qopt *ctl = nla_data(opt);
 
+       if (opt->nla_len != nla_attr_size(sizeof(*ctl)))
+               return -EINVAL;
+
        sch->limit = ctl->limit;
        return 0;
 }
index 09050c1..f7cb0b7 100644 (file)
@@ -858,7 +858,11 @@ struct sctp_chunk *sctp_make_shutdown(const struct sctp_association *asoc,
        struct sctp_chunk *retval;
        __u32 ctsn;
 
-       ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
+       if (chunk && chunk->asoc)
+               ctsn = sctp_tsnmap_get_ctsn(&chunk->asoc->peer.tsn_map);
+       else
+               ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
+
        shut.cum_tsn_ack = htonl(ctsn);
 
        retval = sctp_make_control(asoc, SCTP_CID_SHUTDOWN, 0,
index 6a16af4..26788f4 100644 (file)
@@ -1865,7 +1865,7 @@ static enum sctp_disposition sctp_sf_do_dupcook_a(
                 */
                sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
                return sctp_sf_do_9_2_start_shutdown(net, ep, asoc,
-                                                    SCTP_ST_CHUNK(0), NULL,
+                                                    SCTP_ST_CHUNK(0), repl,
                                                     commands);
        } else {
                sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
@@ -5470,7 +5470,7 @@ enum sctp_disposition sctp_sf_do_9_2_start_shutdown(
         * in the Cumulative TSN Ack field the last sequential TSN it
         * has received from the peer.
         */
-       reply = sctp_make_shutdown(asoc, NULL);
+       reply = sctp_make_shutdown(asoc, arg);
        if (!reply)
                goto nomem;
 
@@ -6068,7 +6068,7 @@ enum sctp_disposition sctp_sf_autoclose_timer_expire(
        disposition = SCTP_DISPOSITION_CONSUME;
        if (sctp_outq_is_empty(&asoc->outqueue)) {
                disposition = sctp_sf_do_9_2_start_shutdown(net, ep, asoc, type,
-                                                           arg, commands);
+                                                           NULL, commands);
        }
 
        return disposition;
index 4740aa7..c16c809 100644 (file)
@@ -43,20 +43,15 @@ static unsigned long max_autoclose_max =
        ? UINT_MAX : MAX_SCHEDULE_TIMEOUT / HZ;
 
 static int proc_sctp_do_hmac_alg(struct ctl_table *ctl, int write,
-                               void __user *buffer, size_t *lenp,
-                               loff_t *ppos);
+                                void *buffer, size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_rto_min(struct ctl_table *ctl, int write,
-                               void __user *buffer, size_t *lenp,
-                               loff_t *ppos);
-static int proc_sctp_do_rto_max(struct ctl_table *ctl, int write,
-                               void __user *buffer, size_t *lenp,
-                               loff_t *ppos);
+                               void *buffer, size_t *lenp, loff_t *ppos);
+static int proc_sctp_do_rto_max(struct ctl_table *ctl, int write, void *buffer,
+                               size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_alpha_beta(struct ctl_table *ctl, int write,
-                                  void __user *buffer, size_t *lenp,
-                                  loff_t *ppos);
+                                  void *buffer, size_t *lenp, loff_t *ppos);
 static int proc_sctp_do_auth(struct ctl_table *ctl, int write,
-                            void __user *buffer, size_t *lenp,
-                            loff_t *ppos);
+                            void *buffer, size_t *lenp, loff_t *ppos);
 
 static struct ctl_table sctp_table[] = {
        {
@@ -343,8 +338,7 @@ static struct ctl_table sctp_net_table[] = {
 };
 
 static int proc_sctp_do_hmac_alg(struct ctl_table *ctl, int write,
-                               void __user *buffer, size_t *lenp,
-                               loff_t *ppos)
+                                void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net = current->nsproxy->net_ns;
        struct ctl_table tbl;
@@ -389,8 +383,7 @@ static int proc_sctp_do_hmac_alg(struct ctl_table *ctl, int write,
 }
 
 static int proc_sctp_do_rto_min(struct ctl_table *ctl, int write,
-                               void __user *buffer, size_t *lenp,
-                               loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net = current->nsproxy->net_ns;
        unsigned int min = *(unsigned int *) ctl->extra1;
@@ -418,8 +411,7 @@ static int proc_sctp_do_rto_min(struct ctl_table *ctl, int write,
 }
 
 static int proc_sctp_do_rto_max(struct ctl_table *ctl, int write,
-                               void __user *buffer, size_t *lenp,
-                               loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net = current->nsproxy->net_ns;
        unsigned int min = *(unsigned int *) ctl->extra1;
@@ -447,8 +439,7 @@ static int proc_sctp_do_rto_max(struct ctl_table *ctl, int write,
 }
 
 static int proc_sctp_do_alpha_beta(struct ctl_table *ctl, int write,
-                                  void __user *buffer, size_t *lenp,
-                                  loff_t *ppos)
+                                  void *buffer, size_t *lenp, loff_t *ppos)
 {
        if (write)
                pr_warn_once("Changing rto_alpha or rto_beta may lead to "
@@ -458,8 +449,7 @@ static int proc_sctp_do_alpha_beta(struct ctl_table *ctl, int write,
 }
 
 static int proc_sctp_do_auth(struct ctl_table *ctl, int write,
-                            void __user *buffer, size_t *lenp,
-                            loff_t *ppos)
+                            void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct net *net = current->nsproxy->net_ns;
        struct ctl_table tbl;
index 6fd44bd..9033215 100644 (file)
@@ -337,50 +337,61 @@ 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, 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)
-{
-       if (!rmb_desc->wr_reg) {
-               /* register memory region for new rmb */
-               if (smc_wr_reg_send(link, rmb_desc->mr_rx[SMC_SINGLE_LINK])) {
-                       rmb_desc->regerr = 1;
-                       return -EFAULT;
-               }
-               rmb_desc->wr_reg = 1;
+/* register the new rmb on all links */
+static int smcr_lgr_reg_rmbs(struct smc_link *link,
+                            struct smc_buf_desc *rmb_desc)
+{
+       struct smc_link_group *lgr = link->lgr;
+       int i, rc = 0;
+
+       rc = smc_llc_flow_initiate(lgr, SMC_LLC_FLOW_RKEY);
+       if (rc)
+               return rc;
+       /* protect against parallel smc_llc_cli_rkey_exchange() and
+        * parallel smcr_link_reg_rmb()
+        */
+       mutex_lock(&lgr->llc_conf_mutex);
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               if (lgr->lnk[i].state != SMC_LNK_ACTIVE)
+                       continue;
+               rc = smcr_link_reg_rmb(&lgr->lnk[i], rmb_desc);
+               if (rc)
+                       goto out;
        }
-       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;
+       rc = smc_llc_do_confirm_rkey(link, rmb_desc);
+       if (rc) {
+               rc = -EFAULT;
+               goto out;
        }
-       return 0;
+       rmb_desc->is_conf_rkey = true;
+out:
+       mutex_unlock(&lgr->llc_conf_mutex);
+       smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
+       return rc;
 }
 
-static int smc_clnt_conf_first_link(struct smc_sock *smc)
+static int smcr_clnt_conf_first_link(struct smc_sock *smc)
 {
-       struct net *net = sock_net(smc->clcsock->sk);
-       struct smc_link_group *lgr = smc->conn.lgr;
-       struct smc_link *link;
-       int rest;
+       struct smc_link *link = smc->conn.lnk;
+       struct smc_llc_qentry *qentry;
        int rc;
 
-       link = &lgr->lnk[SMC_SINGLE_LINK];
        /* receive CONFIRM LINK request from server over RoCE fabric */
-       rest = wait_for_completion_interruptible_timeout(
-               &link->llc_confirm,
-               SMC_LLC_WAIT_FIRST_TIME);
-       if (rest <= 0) {
+       qentry = smc_llc_wait(link->lgr, NULL, SMC_LLC_WAIT_TIME,
+                             SMC_LLC_CONFIRM_LINK);
+       if (!qentry) {
                struct smc_clc_msg_decline dclc;
 
                rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
                                      SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
                return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_CL : rc;
        }
-
-       if (link->llc_confirm_rc)
+       smc_llc_save_peer_uid(qentry);
+       rc = smc_llc_eval_conf_link(qentry, SMC_LLC_REQ);
+       smc_llc_flow_qentry_del(&link->lgr->llc_flow_lcl);
+       if (rc)
                return SMC_CLC_DECL_RMBE_EC;
 
        rc = smc_ib_modify_qp_rts(link);
@@ -389,34 +400,34 @@ 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, false))
+       if (smcr_link_reg_rmb(link, smc->conn.rmb_desc))
                return SMC_CLC_DECL_ERR_REGRMB;
 
+       /* confirm_rkey is implicit on 1st contact */
+       smc->conn.rmb_desc->is_conf_rkey = true;
+
        /* send CONFIRM LINK response over RoCE fabric */
        rc = smc_llc_send_confirm_link(link, SMC_LLC_RESP);
        if (rc < 0)
                return SMC_CLC_DECL_TIMEOUT_CL;
 
-       /* receive ADD LINK request from server over RoCE fabric */
-       rest = wait_for_completion_interruptible_timeout(&link->llc_add,
-                                                        SMC_LLC_WAIT_TIME);
-       if (rest <= 0) {
+       smc_llc_link_active(link);
+       smcr_lgr_set_type(link->lgr, SMC_LGR_SINGLE);
+
+       /* optional 2nd link, receive ADD LINK request from server */
+       qentry = smc_llc_wait(link->lgr, NULL, SMC_LLC_WAIT_TIME,
+                             SMC_LLC_ADD_LINK);
+       if (!qentry) {
                struct smc_clc_msg_decline dclc;
 
                rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
                                      SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
-               return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_AL : rc;
+               if (rc == -EAGAIN)
+                       rc = 0; /* no DECLINE received, go with one link */
+               return rc;
        }
-
-       /* send add link reject message, only one link supported for now */
-       rc = smc_llc_send_add_link(link,
-                                  link->smcibdev->mac[link->ibport - 1],
-                                  link->gid, SMC_LLC_RESP);
-       if (rc < 0)
-               return SMC_CLC_DECL_TIMEOUT_AL;
-
-       smc_llc_link_active(link, net->ipv4.sysctl_tcp_keepalive_time);
-
+       smc_llc_flow_qentry_clr(&link->lgr->llc_flow_lcl);
+       smc_llc_cli_add_link(link, qentry);
        return 0;
 }
 
@@ -596,8 +607,8 @@ static int smc_connect_rdma(struct smc_sock *smc,
                            struct smc_clc_msg_accept_confirm *aclc,
                            struct smc_init_info *ini)
 {
+       int i, reason_code = 0;
        struct smc_link *link;
-       int reason_code = 0;
 
        ini->is_smcd = false;
        ini->ib_lcl = &aclc->lcl;
@@ -610,10 +621,28 @@ static int smc_connect_rdma(struct smc_sock *smc,
                mutex_unlock(&smc_client_lgr_pending);
                return reason_code;
        }
-       link = &smc->conn.lgr->lnk[SMC_SINGLE_LINK];
 
        smc_conn_save_peer_info(smc, aclc);
 
+       if (ini->cln_first_contact == SMC_FIRST_CONTACT) {
+               link = smc->conn.lnk;
+       } else {
+               /* set link that was assigned by server */
+               link = NULL;
+               for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+                       struct smc_link *l = &smc->conn.lgr->lnk[i];
+
+                       if (l->peer_qpn == ntoh24(aclc->qpn)) {
+                               link = l;
+                               break;
+                       }
+               }
+               if (!link)
+                       return smc_connect_abort(smc, SMC_CLC_DECL_NOSRVLINK,
+                                                ini->cln_first_contact);
+               smc->conn.lnk = link;
+       }
+
        /* create send buffer and rmb */
        if (smc_buf_create(smc, false))
                return smc_connect_abort(smc, SMC_CLC_DECL_MEM,
@@ -622,7 +651,7 @@ static int smc_connect_rdma(struct smc_sock *smc,
        if (ini->cln_first_contact == SMC_FIRST_CONTACT)
                smc_link_save_peer_info(link, aclc);
 
-       if (smc_rmb_rtoken_handling(&smc->conn, aclc))
+       if (smc_rmb_rtoken_handling(&smc->conn, link, aclc))
                return smc_connect_abort(smc, SMC_CLC_DECL_ERR_RTOK,
                                         ini->cln_first_contact);
 
@@ -634,7 +663,7 @@ static int smc_connect_rdma(struct smc_sock *smc,
                        return smc_connect_abort(smc, SMC_CLC_DECL_ERR_RDYLNK,
                                                 ini->cln_first_contact);
        } else {
-               if (smc_reg_rmb(link, smc->conn.rmb_desc, true))
+               if (smcr_lgr_reg_rmbs(link, smc->conn.rmb_desc))
                        return smc_connect_abort(smc, SMC_CLC_DECL_ERR_REGRMB,
                                                 ini->cln_first_contact);
        }
@@ -649,7 +678,9 @@ static int smc_connect_rdma(struct smc_sock *smc,
 
        if (ini->cln_first_contact == SMC_FIRST_CONTACT) {
                /* QP confirmation over RoCE fabric */
-               reason_code = smc_clnt_conf_first_link(smc);
+               smc_llc_flow_initiate(link->lgr, SMC_LLC_FLOW_ADD_LINK);
+               reason_code = smcr_clnt_conf_first_link(smc);
+               smc_llc_flow_stop(link->lgr, &link->lgr->llc_flow_lcl);
                if (reason_code)
                        return smc_connect_abort(smc, reason_code,
                                                 ini->cln_first_contact);
@@ -999,17 +1030,13 @@ void smc_close_non_accepted(struct sock *sk)
        sock_put(sk); /* final sock_put */
 }
 
-static int smc_serv_conf_first_link(struct smc_sock *smc)
+static int smcr_serv_conf_first_link(struct smc_sock *smc)
 {
-       struct net *net = sock_net(smc->clcsock->sk);
-       struct smc_link_group *lgr = smc->conn.lgr;
-       struct smc_link *link;
-       int rest;
+       struct smc_link *link = smc->conn.lnk;
+       struct smc_llc_qentry *qentry;
        int rc;
 
-       link = &lgr->lnk[SMC_SINGLE_LINK];
-
-       if (smc_reg_rmb(link, smc->conn.rmb_desc, false))
+       if (smcr_link_reg_rmb(link, smc->conn.rmb_desc))
                return SMC_CLC_DECL_ERR_REGRMB;
 
        /* send CONFIRM LINK request to client over the RoCE fabric */
@@ -1018,40 +1045,29 @@ static int smc_serv_conf_first_link(struct smc_sock *smc)
                return SMC_CLC_DECL_TIMEOUT_CL;
 
        /* receive CONFIRM LINK response from client over the RoCE fabric */
-       rest = wait_for_completion_interruptible_timeout(
-               &link->llc_confirm_resp,
-               SMC_LLC_WAIT_FIRST_TIME);
-       if (rest <= 0) {
+       qentry = smc_llc_wait(link->lgr, link, SMC_LLC_WAIT_TIME,
+                             SMC_LLC_CONFIRM_LINK);
+       if (!qentry) {
                struct smc_clc_msg_decline dclc;
 
                rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
                                      SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
                return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_CL : rc;
        }
-
-       if (link->llc_confirm_resp_rc)
+       smc_llc_save_peer_uid(qentry);
+       rc = smc_llc_eval_conf_link(qentry, SMC_LLC_RESP);
+       smc_llc_flow_qentry_del(&link->lgr->llc_flow_lcl);
+       if (rc)
                return SMC_CLC_DECL_RMBE_EC;
 
-       /* send ADD LINK request to client over the RoCE fabric */
-       rc = smc_llc_send_add_link(link,
-                                  link->smcibdev->mac[link->ibport - 1],
-                                  link->gid, SMC_LLC_REQ);
-       if (rc < 0)
-               return SMC_CLC_DECL_TIMEOUT_AL;
-
-       /* receive ADD LINK response from client over the RoCE fabric */
-       rest = wait_for_completion_interruptible_timeout(&link->llc_add_resp,
-                                                        SMC_LLC_WAIT_TIME);
-       if (rest <= 0) {
-               struct smc_clc_msg_decline dclc;
-
-               rc = smc_clc_wait_msg(smc, &dclc, sizeof(dclc),
-                                     SMC_CLC_DECLINE, CLC_WAIT_TIME_SHORT);
-               return rc == -EAGAIN ? SMC_CLC_DECL_TIMEOUT_AL : rc;
-       }
+       /* confirm_rkey is implicit on 1st contact */
+       smc->conn.rmb_desc->is_conf_rkey = true;
 
-       smc_llc_link_active(link, net->ipv4.sysctl_tcp_keepalive_time);
+       smc_llc_link_active(link);
+       smcr_lgr_set_type(link->lgr, SMC_LGR_SINGLE);
 
+       /* initial contact - try to establish second link */
+       smc_llc_srv_add_link(link);
        return 0;
 }
 
@@ -1194,10 +1210,10 @@ static int smc_listen_ism_init(struct smc_sock *new_smc,
 /* 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];
+       struct smc_connection *conn = &new_smc->conn;
 
        if (local_contact != SMC_FIRST_CONTACT) {
-               if (smc_reg_rmb(link, new_smc->conn.rmb_desc, true))
+               if (smcr_lgr_reg_rmbs(conn->lnk, conn->rmb_desc))
                        return SMC_CLC_DECL_ERR_REGRMB;
        }
        smc_rmb_sync_sg_for_device(&new_smc->conn);
@@ -1210,13 +1226,13 @@ static int 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];
+       struct smc_link *link = new_smc->conn.lnk;
        int reason_code = 0;
 
        if (local_contact == SMC_FIRST_CONTACT)
                smc_link_save_peer_info(link, cclc);
 
-       if (smc_rmb_rtoken_handling(&new_smc->conn, cclc)) {
+       if (smc_rmb_rtoken_handling(&new_smc->conn, link, cclc)) {
                reason_code = SMC_CLC_DECL_ERR_RTOK;
                goto decline;
        }
@@ -1227,7 +1243,9 @@ static int smc_listen_rdma_finish(struct smc_sock *new_smc,
                        goto decline;
                }
                /* QP confirmation over RoCE fabric */
-               reason_code = smc_serv_conf_first_link(new_smc);
+               smc_llc_flow_initiate(link->lgr, SMC_LLC_FLOW_ADD_LINK);
+               reason_code = smcr_serv_conf_first_link(new_smc);
+               smc_llc_flow_stop(link->lgr, &link->lgr->llc_flow_lcl);
                if (reason_code)
                        goto decline;
        }
index be11ba4..6f1c42d 100644 (file)
@@ -121,6 +121,7 @@ enum smc_urg_state {
 struct smc_connection {
        struct rb_node          alert_node;
        struct smc_link_group   *lgr;           /* link group of connection */
+       struct smc_link         *lnk;           /* assigned SMC-R link */
        u32                     alert_token_local; /* unique conn. id */
        u8                      peer_rmbe_idx;  /* from tcp handshake */
        int                     peer_rmbe_size; /* size of peer rx buffer */
@@ -142,6 +143,9 @@ struct smc_connection {
                                                 * .prod cf. TCP snd_nxt
                                                 * .cons cf. TCP sends ack
                                                 */
+       union smc_host_cursor   local_tx_ctrl_fin;
+                                               /* prod crsr - confirmed by peer
+                                                */
        union smc_host_cursor   tx_curs_prep;   /* tx - prepared data
                                                 * snd_max..wmem_alloc
                                                 */
@@ -153,6 +157,7 @@ struct smc_connection {
                                                 */
        atomic_t                sndbuf_space;   /* remaining space in sndbuf */
        u16                     tx_cdc_seq;     /* sequence # for CDC send */
+       u16                     tx_cdc_seq_fin; /* sequence # - tx completed */
        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 */
@@ -183,12 +188,14 @@ struct smc_connection {
        spinlock_t              acurs_lock;     /* protect cursors */
 #endif
        struct work_struct      close_work;     /* peer sent some closing */
+       struct work_struct      abort_work;     /* abort the connection */
        struct tasklet_struct   rx_tsklet;      /* Receiver tasklet for SMC-D */
        u8                      rx_off;         /* receive offset:
                                                 * 0 for SMC-R, 32 for SMC-D
                                                 */
        u64                     peer_token;     /* SMC-D token of peer */
        u8                      killed : 1;     /* abnormal termination */
+       u8                      out_of_sync : 1; /* out of sync with peer */
 };
 
 struct smc_sock {                              /* smc sock container */
index 164f158..b2b85e1 100644 (file)
@@ -47,17 +47,20 @@ static void smc_cdc_tx_handler(struct smc_wr_tx_pend_priv *pnd_snd,
                /* guarantee 0 <= sndbuf_space <= sndbuf_desc->len */
                smp_mb__after_atomic();
                smc_curs_copy(&conn->tx_curs_fin, &cdcpend->cursor, conn);
+               smc_curs_copy(&conn->local_tx_ctrl_fin, &cdcpend->p_cursor,
+                             conn);
+               conn->tx_cdc_seq_fin = cdcpend->ctrl_seq;
        }
        smc_tx_sndbuf_nonfull(smc);
        bh_unlock_sock(&smc->sk);
 }
 
 int smc_cdc_get_free_slot(struct smc_connection *conn,
+                         struct smc_link *link,
                          struct smc_wr_buf **wr_buf,
                          struct smc_rdma_wr **wr_rdma_buf,
                          struct smc_cdc_tx_pend **pend)
 {
-       struct smc_link *link = &conn->lgr->lnk[SMC_SINGLE_LINK];
        int rc;
 
        rc = smc_wr_tx_get_free_slot(link, smc_cdc_tx_handler, wr_buf,
@@ -91,12 +94,10 @@ int smc_cdc_msg_send(struct smc_connection *conn,
                     struct smc_wr_buf *wr_buf,
                     struct smc_cdc_tx_pend *pend)
 {
+       struct smc_link *link = conn->lnk;
        union smc_host_cursor cfed;
-       struct smc_link *link;
        int rc;
 
-       link = &conn->lgr->lnk[SMC_SINGLE_LINK];
-
        smc_cdc_add_pending_send(conn, pend);
 
        conn->tx_cdc_seq++;
@@ -106,22 +107,64 @@ int smc_cdc_msg_send(struct smc_connection *conn,
        if (!rc) {
                smc_curs_copy(&conn->rx_curs_confirmed, &cfed, conn);
                conn->local_rx_ctrl.prod_flags.cons_curs_upd_req = 0;
+       } else {
+               conn->tx_cdc_seq--;
+               conn->local_tx_ctrl.seqno = conn->tx_cdc_seq;
        }
 
        return rc;
 }
 
+/* send a validation msg indicating the move of a conn to an other QP link */
+int smcr_cdc_msg_send_validation(struct smc_connection *conn)
+{
+       struct smc_host_cdc_msg *local = &conn->local_tx_ctrl;
+       struct smc_link *link = conn->lnk;
+       struct smc_cdc_tx_pend *pend;
+       struct smc_wr_buf *wr_buf;
+       struct smc_cdc_msg *peer;
+       int rc;
+
+       rc = smc_cdc_get_free_slot(conn, link, &wr_buf, NULL, &pend);
+       if (rc)
+               return rc;
+
+       peer = (struct smc_cdc_msg *)wr_buf;
+       peer->common.type = local->common.type;
+       peer->len = local->len;
+       peer->seqno = htons(conn->tx_cdc_seq_fin); /* seqno last compl. tx */
+       peer->token = htonl(local->token);
+       peer->prod_flags.failover_validation = 1;
+
+       rc = smc_wr_tx_send(link, (struct smc_wr_tx_pend_priv *)pend);
+       return rc;
+}
+
 static int smcr_cdc_get_slot_and_msg_send(struct smc_connection *conn)
 {
        struct smc_cdc_tx_pend *pend;
        struct smc_wr_buf *wr_buf;
+       struct smc_link *link;
+       bool again = false;
        int rc;
 
-       rc = smc_cdc_get_free_slot(conn, &wr_buf, NULL, &pend);
+again:
+       link = conn->lnk;
+       rc = smc_cdc_get_free_slot(conn, link, &wr_buf, NULL, &pend);
        if (rc)
                return rc;
 
        spin_lock_bh(&conn->send_lock);
+       if (link != conn->lnk) {
+               /* link of connection changed, try again one time*/
+               spin_unlock_bh(&conn->send_lock);
+               smc_wr_tx_put_slot(link,
+                                  (struct smc_wr_tx_pend_priv *)pend);
+               if (again)
+                       return -ENOLINK;
+               again = true;
+               goto again;
+       }
        rc = smc_cdc_msg_send(conn, wr_buf, pend);
        spin_unlock_bh(&conn->send_lock);
        return rc;
@@ -165,7 +208,7 @@ static void smc_cdc_tx_dismisser(struct smc_wr_tx_pend_priv *tx_pend)
 
 void smc_cdc_tx_dismiss_slots(struct smc_connection *conn)
 {
-       struct smc_link *link = &conn->lgr->lnk[SMC_SINGLE_LINK];
+       struct smc_link *link = conn->lnk;
 
        smc_wr_tx_dismiss_slots(link, SMC_CDC_MSG_TYPE,
                                smc_cdc_tx_filter, smc_cdc_tx_dismisser,
@@ -239,6 +282,28 @@ static void smc_cdc_handle_urg_data_arrival(struct smc_sock *smc,
        sk_send_sigurg(&smc->sk);
 }
 
+static void smc_cdc_msg_validate(struct smc_sock *smc, struct smc_cdc_msg *cdc,
+                                struct smc_link *link)
+{
+       struct smc_connection *conn = &smc->conn;
+       u16 recv_seq = ntohs(cdc->seqno);
+       s16 diff;
+
+       /* check that seqnum was seen before */
+       diff = conn->local_rx_ctrl.seqno - recv_seq;
+       if (diff < 0) { /* diff larger than 0x7fff */
+               /* drop connection */
+               conn->out_of_sync = 1;  /* prevent any further receives */
+               spin_lock_bh(&conn->send_lock);
+               conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1;
+               conn->lnk = link;
+               spin_unlock_bh(&conn->send_lock);
+               sock_hold(&smc->sk); /* sock_put in abort_work */
+               if (!schedule_work(&conn->abort_work))
+                       sock_put(&smc->sk);
+       }
+}
+
 static void smc_cdc_msg_recv_action(struct smc_sock *smc,
                                    struct smc_cdc_msg *cdc)
 {
@@ -369,16 +434,19 @@ static void smc_cdc_rx_handler(struct ib_wc *wc, void *buf)
        read_lock_bh(&lgr->conns_lock);
        conn = smc_lgr_find_conn(ntohl(cdc->token), lgr);
        read_unlock_bh(&lgr->conns_lock);
-       if (!conn)
+       if (!conn || conn->out_of_sync)
                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;
+       if (cdc->prod_flags.failover_validation) {
+               smc_cdc_msg_validate(smc, cdc, link);
+               return;
        }
+       if (smc_cdc_before(ntohs(cdc->seqno),
+                          conn->local_rx_ctrl.seqno))
+               /* received seqno is old */
+               return;
+
        smc_cdc_msg_recv(smc, cdc);
 }
 
index 861dc24..2ddcc5f 100644 (file)
@@ -97,23 +97,6 @@ static inline void smc_curs_add(int size, union smc_host_cursor *curs,
        }
 }
 
-/* SMC cursors are 8 bytes long and require atomic reading and writing */
-static inline u64 smc_curs_read(union smc_host_cursor *curs,
-                               struct smc_connection *conn)
-{
-#ifndef KERNEL_HAS_ATOMIC64
-       unsigned long flags;
-       u64 ret;
-
-       spin_lock_irqsave(&conn->acurs_lock, flags);
-       ret = curs->acurs;
-       spin_unlock_irqrestore(&conn->acurs_lock, flags);
-       return ret;
-#else
-       return atomic64_read(&curs->acurs);
-#endif
-}
-
 /* Copy cursor src into tgt */
 static inline void smc_curs_copy(union smc_host_cursor *tgt,
                                 union smc_host_cursor *src,
@@ -304,6 +287,7 @@ struct smc_cdc_tx_pend {
 };
 
 int smc_cdc_get_free_slot(struct smc_connection *conn,
+                         struct smc_link *link,
                          struct smc_wr_buf **wr_buf,
                          struct smc_rdma_wr **wr_rdma_buf,
                          struct smc_cdc_tx_pend **pend);
@@ -312,6 +296,7 @@ int smc_cdc_msg_send(struct smc_connection *conn, struct smc_wr_buf *wr_buf,
                     struct smc_cdc_tx_pend *pend);
 int smc_cdc_get_slot_and_msg_send(struct smc_connection *conn);
 int smcd_cdc_msg_send(struct smc_connection *conn);
+int smcr_cdc_msg_send_validation(struct smc_connection *conn);
 int smc_cdc_init(void) __init;
 void smcd_cdc_rx_init(struct smc_connection *conn);
 
index ea0068f..d5627df 100644 (file)
@@ -496,7 +496,7 @@ int smc_clc_send_confirm(struct smc_sock *smc)
                       sizeof(SMCD_EYECATCHER));
        } else {
                /* SMC-R specific settings */
-               link = &conn->lgr->lnk[SMC_SINGLE_LINK];
+               link = conn->lnk;
                memcpy(cclc.hdr.eyecatcher, SMC_EYECATCHER,
                       sizeof(SMC_EYECATCHER));
                cclc.hdr.path = SMC_TYPE_R;
@@ -508,13 +508,13 @@ int smc_clc_send_confirm(struct smc_sock *smc)
                       ETH_ALEN);
                hton24(cclc.qpn, link->roce_qp->qp_num);
                cclc.rmb_rkey =
-                       htonl(conn->rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey);
+                       htonl(conn->rmb_desc->mr_rx[link->link_idx]->rkey);
                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;
                cclc.rmb_dma_addr = cpu_to_be64((u64)sg_dma_address
-                               (conn->rmb_desc->sgt[SMC_SINGLE_LINK].sgl));
+                               (conn->rmb_desc->sgt[link->link_idx].sgl));
                hton24(cclc.psn, link->psn_initial);
                memcpy(cclc.smcr_trl.eyecatcher, SMC_EYECATCHER,
                       sizeof(SMC_EYECATCHER));
@@ -572,7 +572,7 @@ int smc_clc_send_accept(struct smc_sock *new_smc, int srv_first_contact)
                memcpy(aclc.hdr.eyecatcher, SMC_EYECATCHER,
                       sizeof(SMC_EYECATCHER));
                aclc.hdr.path = SMC_TYPE_R;
-               link = &conn->lgr->lnk[SMC_SINGLE_LINK];
+               link = conn->lnk;
                memcpy(aclc.lcl.id_for_peer, local_systemid,
                       sizeof(local_systemid));
                memcpy(&aclc.lcl.gid, link->gid, SMC_GID_SIZE);
@@ -580,13 +580,13 @@ int smc_clc_send_accept(struct smc_sock *new_smc, int srv_first_contact)
                       ETH_ALEN);
                hton24(aclc.qpn, link->roce_qp->qp_num);
                aclc.rmb_rkey =
-                       htonl(conn->rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey);
+                       htonl(conn->rmb_desc->mr_rx[link->link_idx]->rkey);
                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,
                aclc.rmb_dma_addr = cpu_to_be64((u64)sg_dma_address
-                               (conn->rmb_desc->sgt[SMC_SINGLE_LINK].sgl));
+                               (conn->rmb_desc->sgt[link->link_idx].sgl));
                hton24(aclc.psn, link->psn_initial);
                memcpy(aclc.smcr_trl.eyecatcher, SMC_EYECATCHER,
                       sizeof(SMC_EYECATCHER));
index ca20927..4658767 100644 (file)
@@ -44,6 +44,8 @@
 #define SMC_CLC_DECL_DIFFPREFIX        0x03070000  /* IP prefix / subnet mismatch    */
 #define SMC_CLC_DECL_GETVLANERR        0x03080000  /* err to get vlan id of ip device*/
 #define SMC_CLC_DECL_ISMVLANERR        0x03090000  /* err to reg vlan id on ism dev  */
+#define SMC_CLC_DECL_NOACTLINK 0x030a0000  /* no active smc-r link in lgr    */
+#define SMC_CLC_DECL_NOSRVLINK 0x030b0000  /* SMC-R link from srv not found  */
 #define SMC_CLC_DECL_SYNCERR   0x04000000  /* synchronization error          */
 #define SMC_CLC_DECL_PEERDECL  0x05000000  /* peer declined during handshake */
 #define SMC_CLC_DECL_INTERR    0x09990000  /* internal error                 */
index 824c521..65de700 100644 (file)
@@ -44,10 +44,20 @@ static struct smc_lgr_list smc_lgr_list = { /* established link groups */
 static atomic_t lgr_cnt = ATOMIC_INIT(0); /* number of existing link groups */
 static DECLARE_WAIT_QUEUE_HEAD(lgrs_deleted);
 
+struct smc_ib_up_work {
+       struct work_struct      work;
+       struct smc_link_group   *lgr;
+       struct smc_ib_device    *smcibdev;
+       u8                      ibport;
+};
+
 static void smc_buf_free(struct smc_link_group *lgr, bool is_rmb,
                         struct smc_buf_desc *buf_desc);
 static void __smc_lgr_terminate(struct smc_link_group *lgr, bool soft);
 
+static void smc_link_up_work(struct work_struct *work);
+static void smc_link_down_work(struct work_struct *work);
+
 /* return head of link group list and its lock for a given link group */
 static inline struct list_head *smc_lgr_list_head(struct smc_link_group *lgr,
                                                  spinlock_t **lgr_lock)
@@ -111,16 +121,60 @@ static void smc_lgr_add_alert_token(struct smc_connection *conn)
        rb_insert_color(&conn->alert_node, &conn->lgr->conns_all);
 }
 
+/* assign an SMC-R link to the connection */
+static int smcr_lgr_conn_assign_link(struct smc_connection *conn, bool first)
+{
+       enum smc_link_state expected = first ? SMC_LNK_ACTIVATING :
+                                      SMC_LNK_ACTIVE;
+       int i, j;
+
+       /* do link balancing */
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               struct smc_link *lnk = &conn->lgr->lnk[i];
+
+               if (lnk->state != expected || lnk->link_is_asym)
+                       continue;
+               if (conn->lgr->role == SMC_CLNT) {
+                       conn->lnk = lnk; /* temporary, SMC server assigns link*/
+                       break;
+               }
+               if (conn->lgr->conns_num % 2) {
+                       for (j = i + 1; j < SMC_LINKS_PER_LGR_MAX; j++) {
+                               struct smc_link *lnk2;
+
+                               lnk2 = &conn->lgr->lnk[j];
+                               if (lnk2->state == expected &&
+                                   !lnk2->link_is_asym) {
+                                       conn->lnk = lnk2;
+                                       break;
+                               }
+                       }
+               }
+               if (!conn->lnk)
+                       conn->lnk = lnk;
+               break;
+       }
+       if (!conn->lnk)
+               return SMC_CLC_DECL_NOACTLINK;
+       return 0;
+}
+
 /* Register connection in link group by assigning an alert token
  * registered in a search tree.
  * Requires @conns_lock
  * Note that '0' is a reserved value and not assigned.
  */
-static void smc_lgr_register_conn(struct smc_connection *conn)
+static int smc_lgr_register_conn(struct smc_connection *conn, bool first)
 {
        struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
        static atomic_t nexttoken = ATOMIC_INIT(0);
+       int rc;
 
+       if (!conn->lgr->is_smcd) {
+               rc = smcr_lgr_conn_assign_link(conn, first);
+               if (rc)
+                       return rc;
+       }
        /* find a new alert_token_local value not yet used by some connection
         * in this link group
         */
@@ -132,6 +186,7 @@ static void smc_lgr_register_conn(struct smc_connection *conn)
        }
        smc_lgr_add_alert_token(conn);
        conn->lgr->conns_num++;
+       return 0;
 }
 
 /* Unregister connection and reset the alert token of the given connection<
@@ -166,27 +221,33 @@ static void smc_lgr_unregister_conn(struct smc_connection *conn)
 void smc_lgr_cleanup_early(struct smc_connection *conn)
 {
        struct smc_link_group *lgr = conn->lgr;
+       struct list_head *lgr_list;
+       spinlock_t *lgr_lock;
 
        if (!lgr)
                return;
 
        smc_conn_free(conn);
-       smc_lgr_forget(lgr);
+       lgr_list = smc_lgr_list_head(lgr, &lgr_lock);
+       spin_lock_bh(lgr_lock);
+       /* do not use this link group for new connections */
+       if (!list_empty(lgr_list))
+               list_del_init(lgr_list);
+       spin_unlock_bh(lgr_lock);
        smc_lgr_schedule_free_work_fast(lgr);
 }
 
-/* Send delete link, either as client to request the initiation
- * of the DELETE LINK sequence from server; or as server to
- * initiate the delete processing. See smc_llc_rx_delete_link().
- */
-static int smc_link_send_delete(struct smc_link *lnk, bool orderly)
+static void smcr_lgr_link_deactivate_all(struct smc_link_group *lgr)
 {
-       if (lnk->state == SMC_LNK_ACTIVE &&
-           !smc_llc_send_delete_link(lnk, SMC_LLC_REQ, orderly)) {
-               smc_llc_link_deleting(lnk);
-               return 0;
+       int i;
+
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               struct smc_link *lnk = &lgr->lnk[i];
+
+               if (smc_link_usable(lnk))
+                       lnk->state = SMC_LNK_INACTIVE;
        }
-       return -ENOTCONN;
+       wake_up_interruptible_all(&lgr->llc_waiter);
 }
 
 static void smc_lgr_free(struct smc_link_group *lgr);
@@ -197,7 +258,6 @@ static void smc_lgr_free_work(struct work_struct *work)
                                                  struct smc_link_group,
                                                  free_work);
        spinlock_t *lgr_lock;
-       struct smc_link *lnk;
        bool conns;
 
        smc_lgr_list_head(lgr, &lgr_lock);
@@ -214,26 +274,17 @@ static void smc_lgr_free_work(struct work_struct *work)
                return;
        }
        list_del_init(&lgr->list); /* remove from smc_lgr_list */
-
-       lnk = &lgr->lnk[SMC_SINGLE_LINK];
-       if (!lgr->is_smcd && !lgr->terminating) {
-               /* try to send del link msg, on error free lgr immediately */
-               if (lnk->state == SMC_LNK_ACTIVE &&
-                   !smc_link_send_delete(lnk, true)) {
-                       /* reschedule in case we never receive a response */
-                       smc_lgr_schedule_free_work(lgr);
-                       spin_unlock_bh(lgr_lock);
-                       return;
-               }
-       }
        lgr->freeing = 1; /* this instance does the freeing, no new schedule */
        spin_unlock_bh(lgr_lock);
        cancel_delayed_work(&lgr->free_work);
 
-       if (!lgr->is_smcd && lnk->state != SMC_LNK_INACTIVE)
-               smc_llc_link_inactive(lnk);
+       if (!lgr->is_smcd && !lgr->terminating)
+               smc_llc_send_link_delete_all(lgr, true,
+                                            SMC_LLC_DEL_PROG_INIT_TERM);
        if (lgr->is_smcd && !lgr->terminating)
                smc_ism_signal_shutdown(lgr);
+       if (!lgr->is_smcd)
+               smcr_lgr_link_deactivate_all(lgr);
        smc_lgr_free(lgr);
 }
 
@@ -245,6 +296,89 @@ static void smc_lgr_terminate_work(struct work_struct *work)
        __smc_lgr_terminate(lgr, true);
 }
 
+/* return next unique link id for the lgr */
+static u8 smcr_next_link_id(struct smc_link_group *lgr)
+{
+       u8 link_id;
+       int i;
+
+       while (1) {
+               link_id = ++lgr->next_link_id;
+               if (!link_id)   /* skip zero as link_id */
+                       link_id = ++lgr->next_link_id;
+               for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+                       if (smc_link_usable(&lgr->lnk[i]) &&
+                           lgr->lnk[i].link_id == link_id)
+                               continue;
+               }
+               break;
+       }
+       return link_id;
+}
+
+int smcr_link_init(struct smc_link_group *lgr, struct smc_link *lnk,
+                  u8 link_idx, struct smc_init_info *ini)
+{
+       u8 rndvec[3];
+       int rc;
+
+       get_device(&ini->ib_dev->ibdev->dev);
+       atomic_inc(&ini->ib_dev->lnk_cnt);
+       lnk->state = SMC_LNK_ACTIVATING;
+       lnk->link_id = smcr_next_link_id(lgr);
+       lnk->lgr = lgr;
+       lnk->link_idx = link_idx;
+       lnk->smcibdev = ini->ib_dev;
+       lnk->ibport = ini->ib_port;
+       lnk->path_mtu = ini->ib_dev->pattr[ini->ib_port - 1].active_mtu;
+       smc_llc_link_set_uid(lnk);
+       INIT_WORK(&lnk->link_down_wrk, smc_link_down_work);
+       if (!ini->ib_dev->initialized) {
+               rc = (int)smc_ib_setup_per_ibdev(ini->ib_dev);
+               if (rc)
+                       goto out;
+       }
+       get_random_bytes(rndvec, sizeof(rndvec));
+       lnk->psn_initial = rndvec[0] + (rndvec[1] << 8) +
+               (rndvec[2] << 16);
+       rc = smc_ib_determine_gid(lnk->smcibdev, lnk->ibport,
+                                 ini->vlan_id, lnk->gid, &lnk->sgid_index);
+       if (rc)
+               goto out;
+       rc = smc_llc_link_init(lnk);
+       if (rc)
+               goto out;
+       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;
+       rc = smc_ib_create_queue_pair(lnk);
+       if (rc)
+               goto dealloc_pd;
+       rc = smc_wr_create_link(lnk);
+       if (rc)
+               goto destroy_qp;
+       return 0;
+
+destroy_qp:
+       smc_ib_destroy_queue_pair(lnk);
+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, false);
+out:
+       put_device(&ini->ib_dev->ibdev->dev);
+       memset(lnk, 0, sizeof(struct smc_link));
+       lnk->state = SMC_LNK_UNUSED;
+       if (!atomic_dec_return(&ini->ib_dev->lnk_cnt))
+               wake_up(&ini->ib_dev->lnks_deleted);
+       return rc;
+}
+
 /* create a new SMC link group */
 static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
 {
@@ -252,7 +386,7 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
        struct list_head *lgr_list;
        struct smc_link *lnk;
        spinlock_t *lgr_lock;
-       u8 rndvec[3];
+       u8 link_idx;
        int rc = 0;
        int i;
 
@@ -274,13 +408,14 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
        lgr->freefast = 0;
        lgr->freeing = 0;
        lgr->vlan_id = ini->vlan_id;
-       rwlock_init(&lgr->sndbufs_lock);
-       rwlock_init(&lgr->rmbs_lock);
+       mutex_init(&lgr->sndbufs_lock);
+       mutex_init(&lgr->rmbs_lock);
        rwlock_init(&lgr->conns_lock);
        for (i = 0; i < SMC_RMBE_SIZES; i++) {
                INIT_LIST_HEAD(&lgr->sndbufs[i]);
                INIT_LIST_HEAD(&lgr->rmbs[i]);
        }
+       lgr->next_link_id = 0;
        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);
@@ -297,48 +432,21 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
                atomic_inc(&ini->ism_dev->lgr_cnt);
        } else {
                /* SMC-R specific settings */
-               get_device(&ini->ib_dev->ibdev->dev);
                lgr->role = smc->listen_smc ? SMC_SERV : SMC_CLNT;
                memcpy(lgr->peer_systemid, ini->ib_lcl->id_for_peer,
                       SMC_SYSTEMID_LEN);
+               memcpy(lgr->pnet_id, ini->ib_dev->pnetid[ini->ib_port - 1],
+                      SMC_MAX_PNETID_LEN);
+               smc_llc_lgr_init(lgr, smc);
 
-               lnk = &lgr->lnk[SMC_SINGLE_LINK];
-               /* initialize link */
-               lnk->state = SMC_LNK_ACTIVATING;
-               lnk->link_id = SMC_SINGLE_LINK;
-               lnk->smcibdev = ini->ib_dev;
-               lnk->ibport = ini->ib_port;
-               lgr_list = &smc_lgr_list.list;
-               lgr_lock = &smc_lgr_list.lock;
-               lnk->path_mtu =
-                       ini->ib_dev->pattr[ini->ib_port - 1].active_mtu;
-               if (!ini->ib_dev->initialized)
-                       smc_ib_setup_per_ibdev(ini->ib_dev);
-               get_random_bytes(rndvec, sizeof(rndvec));
-               lnk->psn_initial = rndvec[0] + (rndvec[1] << 8) +
-                       (rndvec[2] << 16);
-               rc = smc_ib_determine_gid(lnk->smcibdev, lnk->ibport,
-                                         ini->vlan_id, lnk->gid,
-                                         &lnk->sgid_index);
+               link_idx = SMC_SINGLE_LINK;
+               lnk = &lgr->lnk[link_idx];
+               rc = smcr_link_init(lgr, lnk, link_idx, ini);
                if (rc)
                        goto free_lgr;
-               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;
-               rc = smc_ib_create_queue_pair(lnk);
-               if (rc)
-                       goto dealloc_pd;
-               rc = smc_wr_create_link(lnk);
-               if (rc)
-                       goto destroy_qp;
+               lgr_list = &smc_lgr_list.list;
+               lgr_lock = &smc_lgr_list.lock;
                atomic_inc(&lgr_cnt);
-               atomic_inc(&ini->ib_dev->lnk_cnt);
        }
        smc->conn.lgr = lgr;
        spin_lock_bh(lgr_lock);
@@ -346,14 +454,6 @@ static int smc_lgr_create(struct smc_sock *smc, struct smc_init_info *ini)
        spin_unlock_bh(lgr_lock);
        return 0;
 
-destroy_qp:
-       smc_ib_destroy_queue_pair(lnk);
-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);
 ism_put_vlan:
@@ -369,29 +469,174 @@ out:
        return rc;
 }
 
+static int smc_write_space(struct smc_connection *conn)
+{
+       int buffer_len = conn->peer_rmbe_size;
+       union smc_host_cursor prod;
+       union smc_host_cursor cons;
+       int space;
+
+       smc_curs_copy(&prod, &conn->local_tx_ctrl.prod, conn);
+       smc_curs_copy(&cons, &conn->local_rx_ctrl.cons, conn);
+       /* determine rx_buf space */
+       space = buffer_len - smc_curs_diff(buffer_len, &cons, &prod);
+       return space;
+}
+
+static int smc_switch_cursor(struct smc_sock *smc)
+{
+       struct smc_connection *conn = &smc->conn;
+       union smc_host_cursor cons, fin;
+       int rc = 0;
+       int diff;
+
+       smc_curs_copy(&conn->tx_curs_sent, &conn->tx_curs_fin, conn);
+       smc_curs_copy(&fin, &conn->local_tx_ctrl_fin, conn);
+       /* set prod cursor to old state, enforce tx_rdma_writes() */
+       smc_curs_copy(&conn->local_tx_ctrl.prod, &fin, conn);
+       smc_curs_copy(&cons, &conn->local_rx_ctrl.cons, conn);
+
+       if (smc_curs_comp(conn->peer_rmbe_size, &cons, &fin) < 0) {
+               /* cons cursor advanced more than fin, and prod was set
+                * fin above, so now prod is smaller than cons. Fix that.
+                */
+               diff = smc_curs_diff(conn->peer_rmbe_size, &fin, &cons);
+               smc_curs_add(conn->sndbuf_desc->len,
+                            &conn->tx_curs_sent, diff);
+               smc_curs_add(conn->sndbuf_desc->len,
+                            &conn->tx_curs_fin, diff);
+
+               smp_mb__before_atomic();
+               atomic_add(diff, &conn->sndbuf_space);
+               smp_mb__after_atomic();
+
+               smc_curs_add(conn->peer_rmbe_size,
+                            &conn->local_tx_ctrl.prod, diff);
+               smc_curs_add(conn->peer_rmbe_size,
+                            &conn->local_tx_ctrl_fin, diff);
+       }
+       /* recalculate, value is used by tx_rdma_writes() */
+       atomic_set(&smc->conn.peer_rmbe_space, smc_write_space(conn));
+
+       if (smc->sk.sk_state != SMC_INIT &&
+           smc->sk.sk_state != SMC_CLOSED) {
+               rc = smcr_cdc_msg_send_validation(conn);
+               if (!rc) {
+                       schedule_delayed_work(&conn->tx_work, 0);
+                       smc->sk.sk_data_ready(&smc->sk);
+               }
+       }
+       return rc;
+}
+
+struct smc_link *smc_switch_conns(struct smc_link_group *lgr,
+                                 struct smc_link *from_lnk, bool is_dev_err)
+{
+       struct smc_link *to_lnk = NULL;
+       struct smc_connection *conn;
+       struct smc_sock *smc;
+       struct rb_node *node;
+       int i, rc = 0;
+
+       /* link is inactive, wake up tx waiters */
+       smc_wr_wakeup_tx_wait(from_lnk);
+
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               if (lgr->lnk[i].state != SMC_LNK_ACTIVE ||
+                   i == from_lnk->link_idx)
+                       continue;
+               if (is_dev_err && from_lnk->smcibdev == lgr->lnk[i].smcibdev &&
+                   from_lnk->ibport == lgr->lnk[i].ibport) {
+                       continue;
+               }
+               to_lnk = &lgr->lnk[i];
+               break;
+       }
+       if (!to_lnk) {
+               smc_lgr_terminate_sched(lgr);
+               return NULL;
+       }
+again:
+       read_lock_bh(&lgr->conns_lock);
+       for (node = rb_first(&lgr->conns_all); node; node = rb_next(node)) {
+               conn = rb_entry(node, struct smc_connection, alert_node);
+               if (conn->lnk != from_lnk)
+                       continue;
+               smc = container_of(conn, struct smc_sock, conn);
+               /* conn->lnk not yet set in SMC_INIT state */
+               if (smc->sk.sk_state == SMC_INIT)
+                       continue;
+               if (smc->sk.sk_state == SMC_CLOSED ||
+                   smc->sk.sk_state == SMC_PEERCLOSEWAIT1 ||
+                   smc->sk.sk_state == SMC_PEERCLOSEWAIT2 ||
+                   smc->sk.sk_state == SMC_APPFINCLOSEWAIT ||
+                   smc->sk.sk_state == SMC_APPCLOSEWAIT1 ||
+                   smc->sk.sk_state == SMC_APPCLOSEWAIT2 ||
+                   smc->sk.sk_state == SMC_PEERFINCLOSEWAIT ||
+                   smc->sk.sk_state == SMC_PEERABORTWAIT ||
+                   smc->sk.sk_state == SMC_PROCESSABORT) {
+                       spin_lock_bh(&conn->send_lock);
+                       conn->lnk = to_lnk;
+                       spin_unlock_bh(&conn->send_lock);
+                       continue;
+               }
+               sock_hold(&smc->sk);
+               read_unlock_bh(&lgr->conns_lock);
+               /* avoid race with smcr_tx_sndbuf_nonempty() */
+               spin_lock_bh(&conn->send_lock);
+               conn->lnk = to_lnk;
+               rc = smc_switch_cursor(smc);
+               spin_unlock_bh(&conn->send_lock);
+               sock_put(&smc->sk);
+               if (rc) {
+                       smcr_link_down_cond_sched(to_lnk);
+                       return NULL;
+               }
+               goto again;
+       }
+       read_unlock_bh(&lgr->conns_lock);
+       return to_lnk;
+}
+
+static void smcr_buf_unuse(struct smc_buf_desc *rmb_desc,
+                          struct smc_link_group *lgr)
+{
+       int rc;
+
+       if (rmb_desc->is_conf_rkey && !list_empty(&lgr->list)) {
+               /* unregister rmb with peer */
+               rc = smc_llc_flow_initiate(lgr, SMC_LLC_FLOW_RKEY);
+               if (!rc) {
+                       /* protect against smc_llc_cli_rkey_exchange() */
+                       mutex_lock(&lgr->llc_conf_mutex);
+                       smc_llc_do_delete_rkey(lgr, rmb_desc);
+                       rmb_desc->is_conf_rkey = false;
+                       mutex_unlock(&lgr->llc_conf_mutex);
+                       smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
+               }
+       }
+
+       if (rmb_desc->is_reg_err) {
+               /* buf registration failed, reuse not possible */
+               mutex_lock(&lgr->rmbs_lock);
+               list_del(&rmb_desc->list);
+               mutex_unlock(&lgr->rmbs_lock);
+
+               smc_buf_free(lgr, true, rmb_desc);
+       } else {
+               rmb_desc->used = 0;
+       }
+}
+
 static void smc_buf_unuse(struct smc_connection *conn,
                          struct smc_link_group *lgr)
 {
        if (conn->sndbuf_desc)
                conn->sndbuf_desc->used = 0;
-       if (conn->rmb_desc) {
-               if (!conn->rmb_desc->regerr) {
-                       if (!lgr->is_smcd && !list_empty(&lgr->list)) {
-                               /* unregister rmb with peer */
-                               smc_llc_do_delete_rkey(
-                                               &lgr->lnk[SMC_SINGLE_LINK],
-                                               conn->rmb_desc);
-                       }
-                       conn->rmb_desc->used = 0;
-               } else {
-                       /* buf registration failed, reuse not possible */
-                       write_lock_bh(&lgr->rmbs_lock);
-                       list_del(&conn->rmb_desc->list);
-                       write_unlock_bh(&lgr->rmbs_lock);
-
-                       smc_buf_free(lgr, true, conn->rmb_desc);
-               }
-       }
+       if (conn->rmb_desc && lgr->is_smcd)
+               conn->rmb_desc->used = 0;
+       else if (conn->rmb_desc)
+               smcr_buf_unuse(conn->rmb_desc, lgr);
 }
 
 /* remove a finished connection from its link group */
@@ -407,6 +652,8 @@ void smc_conn_free(struct smc_connection *conn)
                tasklet_kill(&conn->rx_tsklet);
        } else {
                smc_cdc_tx_dismiss_slots(conn);
+               if (current_work() != &conn->abort_work)
+                       cancel_work_sync(&conn->abort_work);
        }
        if (!list_empty(&lgr->list)) {
                smc_lgr_unregister_conn(conn);
@@ -417,35 +664,91 @@ void smc_conn_free(struct smc_connection *conn)
                smc_lgr_schedule_free_work(lgr);
 }
 
-static void smc_link_clear(struct smc_link *lnk)
+/* unregister a link from a buf_desc */
+static void smcr_buf_unmap_link(struct smc_buf_desc *buf_desc, bool is_rmb,
+                               struct smc_link *lnk)
 {
+       if (is_rmb)
+               buf_desc->is_reg_mr[lnk->link_idx] = false;
+       if (!buf_desc->is_map_ib[lnk->link_idx])
+               return;
+       if (is_rmb) {
+               if (buf_desc->mr_rx[lnk->link_idx]) {
+                       smc_ib_put_memory_region(
+                                       buf_desc->mr_rx[lnk->link_idx]);
+                       buf_desc->mr_rx[lnk->link_idx] = NULL;
+               }
+               smc_ib_buf_unmap_sg(lnk, buf_desc, DMA_FROM_DEVICE);
+       } else {
+               smc_ib_buf_unmap_sg(lnk, buf_desc, DMA_TO_DEVICE);
+       }
+       sg_free_table(&buf_desc->sgt[lnk->link_idx]);
+       buf_desc->is_map_ib[lnk->link_idx] = false;
+}
+
+/* unmap all buffers of lgr for a deleted link */
+static void smcr_buf_unmap_lgr(struct smc_link *lnk)
+{
+       struct smc_link_group *lgr = lnk->lgr;
+       struct smc_buf_desc *buf_desc, *bf;
+       int i;
+
+       for (i = 0; i < SMC_RMBE_SIZES; i++) {
+               mutex_lock(&lgr->rmbs_lock);
+               list_for_each_entry_safe(buf_desc, bf, &lgr->rmbs[i], list)
+                       smcr_buf_unmap_link(buf_desc, true, lnk);
+               mutex_unlock(&lgr->rmbs_lock);
+               mutex_lock(&lgr->sndbufs_lock);
+               list_for_each_entry_safe(buf_desc, bf, &lgr->sndbufs[i],
+                                        list)
+                       smcr_buf_unmap_link(buf_desc, false, lnk);
+               mutex_unlock(&lgr->sndbufs_lock);
+       }
+}
+
+static void smcr_rtoken_clear_link(struct smc_link *lnk)
+{
+       struct smc_link_group *lgr = lnk->lgr;
+       int i;
+
+       for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
+               lgr->rtokens[i][lnk->link_idx].rkey = 0;
+               lgr->rtokens[i][lnk->link_idx].dma_addr = 0;
+       }
+}
+
+/* must be called under lgr->llc_conf_mutex lock */
+void smcr_link_clear(struct smc_link *lnk, bool log)
+{
+       struct smc_ib_device *smcibdev;
+
+       if (!lnk->lgr || lnk->state == SMC_LNK_UNUSED)
+               return;
        lnk->peer_qpn = 0;
-       smc_llc_link_clear(lnk);
+       smc_llc_link_clear(lnk, log);
+       smcr_buf_unmap_lgr(lnk);
+       smcr_rtoken_clear_link(lnk);
        smc_ib_modify_qp_reset(lnk);
        smc_wr_free_link(lnk);
        smc_ib_destroy_queue_pair(lnk);
        smc_ib_dealloc_protection_domain(lnk);
        smc_wr_free_link_mem(lnk);
-       if (!atomic_dec_return(&lnk->smcibdev->lnk_cnt))
-               wake_up(&lnk->smcibdev->lnks_deleted);
+       put_device(&lnk->smcibdev->ibdev->dev);
+       smcibdev = lnk->smcibdev;
+       memset(lnk, 0, sizeof(struct smc_link));
+       lnk->state = SMC_LNK_UNUSED;
+       if (!atomic_dec_return(&smcibdev->lnk_cnt))
+               wake_up(&smcibdev->lnks_deleted);
 }
 
 static void smcr_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];
+       int i;
+
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++)
+               smcr_buf_unmap_link(buf_desc, is_rmb, &lgr->lnk[i]);
 
-       if (is_rmb) {
-               if (buf_desc->mr_rx[SMC_SINGLE_LINK])
-                       smc_ib_put_memory_region(
-                                       buf_desc->mr_rx[SMC_SINGLE_LINK]);
-               smc_ib_buf_unmap_sg(lnk->smcibdev, buf_desc,
-                                   DMA_FROM_DEVICE);
-       } else {
-               smc_ib_buf_unmap_sg(lnk->smcibdev, buf_desc,
-                                   DMA_TO_DEVICE);
-       }
-       sg_free_table(&buf_desc->sgt[SMC_SINGLE_LINK]);
        if (buf_desc->pages)
                __free_pages(buf_desc->pages, buf_desc->order);
        kfree(buf_desc);
@@ -503,6 +806,18 @@ static void smc_lgr_free_bufs(struct smc_link_group *lgr)
 /* remove a link group */
 static void smc_lgr_free(struct smc_link_group *lgr)
 {
+       int i;
+
+       if (!lgr->is_smcd) {
+               mutex_lock(&lgr->llc_conf_mutex);
+               for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+                       if (lgr->lnk[i].state != SMC_LNK_UNUSED)
+                               smcr_link_clear(&lgr->lnk[i], false);
+               }
+               mutex_unlock(&lgr->llc_conf_mutex);
+               smc_llc_lgr_clear(lgr);
+       }
+
        smc_lgr_free_bufs(lgr);
        if (lgr->is_smcd) {
                if (!lgr->terminating) {
@@ -512,27 +827,12 @@ static void smc_lgr_free(struct smc_link_group *lgr)
                if (!atomic_dec_return(&lgr->smcd->lgr_cnt))
                        wake_up(&lgr->smcd->lgrs_deleted);
        } else {
-               smc_link_clear(&lgr->lnk[SMC_SINGLE_LINK]);
-               put_device(&lgr->lnk[SMC_SINGLE_LINK].smcibdev->ibdev->dev);
                if (!atomic_dec_return(&lgr_cnt))
                        wake_up(&lgrs_deleted);
        }
        kfree(lgr);
 }
 
-void smc_lgr_forget(struct smc_link_group *lgr)
-{
-       struct list_head *lgr_list;
-       spinlock_t *lgr_lock;
-
-       lgr_list = smc_lgr_list_head(lgr, &lgr_lock);
-       spin_lock_bh(lgr_lock);
-       /* do not use this link group for new connections */
-       if (!list_empty(lgr_list))
-               list_del_init(lgr_list);
-       spin_unlock_bh(lgr_lock);
-}
-
 static void smcd_unregister_all_dmbs(struct smc_link_group *lgr)
 {
        int i;
@@ -587,10 +887,12 @@ static void smc_lgr_cleanup(struct smc_link_group *lgr)
                smc_ism_put_vlan(lgr->smcd, lgr->vlan_id);
                put_device(&lgr->smcd->dev);
        } else {
-               struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK];
+               u32 rsn = lgr->llc_termination_rsn;
 
-               if (lnk->state != SMC_LNK_INACTIVE)
-                       smc_llc_link_inactive(lnk);
+               if (!rsn)
+                       rsn = SMC_LLC_DEL_PROG_INIT_TERM;
+               smc_llc_send_link_delete_all(lgr, false, rsn);
+               smcr_lgr_link_deactivate_all(lgr);
        }
 }
 
@@ -606,11 +908,9 @@ static void __smc_lgr_terminate(struct smc_link_group *lgr, bool soft)
 
        if (lgr->terminating)
                return; /* lgr already terminating */
-       if (!soft)
-               cancel_delayed_work_sync(&lgr->free_work);
+       /* cancel free_work sync, will terminate when lgr->freeing is set */
+       cancel_delayed_work_sync(&lgr->free_work);
        lgr->terminating = 1;
-       if (!lgr->is_smcd)
-               smc_llc_link_inactive(&lgr->lnk[SMC_SINGLE_LINK]);
 
        /* kill remaining link group connections */
        read_lock_bh(&lgr->conns_lock);
@@ -629,10 +929,7 @@ static void __smc_lgr_terminate(struct smc_link_group *lgr, bool soft)
        }
        read_unlock_bh(&lgr->conns_lock);
        smc_lgr_cleanup(lgr);
-       if (soft)
-               smc_lgr_schedule_free_work_fast(lgr);
-       else
-               smc_lgr_free(lgr);
+       smc_lgr_free(lgr);
 }
 
 /* unlink link group and schedule termination */
@@ -647,33 +944,11 @@ void smc_lgr_terminate_sched(struct smc_link_group *lgr)
                return; /* lgr already terminating */
        }
        list_del_init(&lgr->list);
+       lgr->freeing = 1;
        spin_unlock_bh(lgr_lock);
        schedule_work(&lgr->terminate_work);
 }
 
-/* Called when IB port is terminated */
-void smc_port_terminate(struct smc_ib_device *smcibdev, u8 ibport)
-{
-       struct smc_link_group *lgr, *l;
-       LIST_HEAD(lgr_free_list);
-
-       spin_lock_bh(&smc_lgr_list.lock);
-       list_for_each_entry_safe(lgr, l, &smc_lgr_list.list, list) {
-               if (!lgr->is_smcd &&
-                   lgr->lnk[SMC_SINGLE_LINK].smcibdev == smcibdev &&
-                   lgr->lnk[SMC_SINGLE_LINK].ibport == ibport) {
-                       list_move(&lgr->list, &lgr_free_list);
-                       lgr->freeing = 1;
-               }
-       }
-       spin_unlock_bh(&smc_lgr_list.lock);
-
-       list_for_each_entry_safe(lgr, l, &lgr_free_list, list) {
-               list_del_init(&lgr->list);
-               __smc_lgr_terminate(lgr, false);
-       }
-}
-
 /* Called when peer lgr shutdown (regularly or abnormally) is received */
 void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid, unsigned short vlan)
 {
@@ -688,6 +963,7 @@ void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid, unsigned short vlan)
                        if (peer_gid) /* peer triggered termination */
                                lgr->peer_shutdown = 1;
                        list_move(&lgr->list, &lgr_free_list);
+                       lgr->freeing = 1;
                }
        }
        spin_unlock_bh(&dev->lgr_lock);
@@ -728,6 +1004,7 @@ void smc_smcr_terminate_all(struct smc_ib_device *smcibdev)
 {
        struct smc_link_group *lgr, *lg;
        LIST_HEAD(lgr_free_list);
+       int i;
 
        spin_lock_bh(&smc_lgr_list.lock);
        if (!smcibdev) {
@@ -736,9 +1013,9 @@ void smc_smcr_terminate_all(struct smc_ib_device *smcibdev)
                        lgr->freeing = 1;
        } else {
                list_for_each_entry_safe(lgr, lg, &smc_lgr_list.list, list) {
-                       if (lgr->lnk[SMC_SINGLE_LINK].smcibdev == smcibdev) {
-                               list_move(&lgr->list, &lgr_free_list);
-                               lgr->freeing = 1;
+                       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+                               if (lgr->lnk[i].smcibdev == smcibdev)
+                                       smcr_link_down_cond_sched(&lgr->lnk[i]);
                        }
                }
        }
@@ -746,6 +1023,7 @@ void smc_smcr_terminate_all(struct smc_ib_device *smcibdev)
 
        list_for_each_entry_safe(lgr, lg, &lgr_free_list, list) {
                list_del_init(&lgr->list);
+               smc_llc_set_termination_rsn(lgr, SMC_LLC_DEL_OP_INIT_TERM);
                __smc_lgr_terminate(lgr, false);
        }
 
@@ -759,6 +1037,225 @@ void smc_smcr_terminate_all(struct smc_ib_device *smcibdev)
        }
 }
 
+/* set new lgr type and clear all asymmetric link tagging */
+void smcr_lgr_set_type(struct smc_link_group *lgr, enum smc_lgr_type new_type)
+{
+       char *lgr_type = "";
+       int i;
+
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++)
+               if (smc_link_usable(&lgr->lnk[i]))
+                       lgr->lnk[i].link_is_asym = false;
+       if (lgr->type == new_type)
+               return;
+       lgr->type = new_type;
+
+       switch (lgr->type) {
+       case SMC_LGR_NONE:
+               lgr_type = "NONE";
+               break;
+       case SMC_LGR_SINGLE:
+               lgr_type = "SINGLE";
+               break;
+       case SMC_LGR_SYMMETRIC:
+               lgr_type = "SYMMETRIC";
+               break;
+       case SMC_LGR_ASYMMETRIC_PEER:
+               lgr_type = "ASYMMETRIC_PEER";
+               break;
+       case SMC_LGR_ASYMMETRIC_LOCAL:
+               lgr_type = "ASYMMETRIC_LOCAL";
+               break;
+       }
+       pr_warn_ratelimited("smc: SMC-R lg %*phN state changed: "
+                           "%s, pnetid %.16s\n", SMC_LGR_ID_SIZE, &lgr->id,
+                           lgr_type, lgr->pnet_id);
+}
+
+/* set new lgr type and tag a link as asymmetric */
+void smcr_lgr_set_type_asym(struct smc_link_group *lgr,
+                           enum smc_lgr_type new_type, int asym_lnk_idx)
+{
+       smcr_lgr_set_type(lgr, new_type);
+       lgr->lnk[asym_lnk_idx].link_is_asym = true;
+}
+
+/* abort connection, abort_work scheduled from tasklet context */
+static void smc_conn_abort_work(struct work_struct *work)
+{
+       struct smc_connection *conn = container_of(work,
+                                                  struct smc_connection,
+                                                  abort_work);
+       struct smc_sock *smc = container_of(conn, struct smc_sock, conn);
+
+       smc_conn_kill(conn, true);
+       sock_put(&smc->sk); /* sock_hold done by schedulers of abort_work */
+}
+
+/* link is up - establish alternate link if applicable */
+static void smcr_link_up(struct smc_link_group *lgr,
+                        struct smc_ib_device *smcibdev, u8 ibport)
+{
+       struct smc_link *link = NULL;
+
+       if (list_empty(&lgr->list) ||
+           lgr->type == SMC_LGR_SYMMETRIC ||
+           lgr->type == SMC_LGR_ASYMMETRIC_PEER)
+               return;
+
+       if (lgr->role == SMC_SERV) {
+               /* trigger local add link processing */
+               link = smc_llc_usable_link(lgr);
+               if (!link)
+                       return;
+               smc_llc_srv_add_link_local(link);
+       } else {
+               /* invite server to start add link processing */
+               u8 gid[SMC_GID_SIZE];
+
+               if (smc_ib_determine_gid(smcibdev, ibport, lgr->vlan_id, gid,
+                                        NULL))
+                       return;
+               if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
+                       /* some other llc task is ongoing */
+                       wait_event_interruptible_timeout(lgr->llc_waiter,
+                               (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE),
+                               SMC_LLC_WAIT_TIME);
+               }
+               if (list_empty(&lgr->list) ||
+                   !smc_ib_port_active(smcibdev, ibport))
+                       return; /* lgr or device no longer active */
+               link = smc_llc_usable_link(lgr);
+               if (!link)
+                       return;
+               smc_llc_send_add_link(link, smcibdev->mac[ibport - 1], gid,
+                                     NULL, SMC_LLC_REQ);
+       }
+}
+
+void smcr_port_add(struct smc_ib_device *smcibdev, u8 ibport)
+{
+       struct smc_ib_up_work *ib_work;
+       struct smc_link_group *lgr, *n;
+
+       list_for_each_entry_safe(lgr, n, &smc_lgr_list.list, list) {
+               if (strncmp(smcibdev->pnetid[ibport - 1], lgr->pnet_id,
+                           SMC_MAX_PNETID_LEN) ||
+                   lgr->type == SMC_LGR_SYMMETRIC ||
+                   lgr->type == SMC_LGR_ASYMMETRIC_PEER)
+                       continue;
+               ib_work = kmalloc(sizeof(*ib_work), GFP_KERNEL);
+               if (!ib_work)
+                       continue;
+               INIT_WORK(&ib_work->work, smc_link_up_work);
+               ib_work->lgr = lgr;
+               ib_work->smcibdev = smcibdev;
+               ib_work->ibport = ibport;
+               schedule_work(&ib_work->work);
+       }
+}
+
+/* link is down - switch connections to alternate link,
+ * must be called under lgr->llc_conf_mutex lock
+ */
+static void smcr_link_down(struct smc_link *lnk)
+{
+       struct smc_link_group *lgr = lnk->lgr;
+       struct smc_link *to_lnk;
+       int del_link_id;
+
+       if (!lgr || lnk->state == SMC_LNK_UNUSED || list_empty(&lgr->list))
+               return;
+
+       smc_ib_modify_qp_reset(lnk);
+       to_lnk = smc_switch_conns(lgr, lnk, true);
+       if (!to_lnk) { /* no backup link available */
+               smcr_link_clear(lnk, true);
+               return;
+       }
+       smcr_lgr_set_type(lgr, SMC_LGR_SINGLE);
+       del_link_id = lnk->link_id;
+
+       if (lgr->role == SMC_SERV) {
+               /* trigger local delete link processing */
+               smc_llc_srv_delete_link_local(to_lnk, del_link_id);
+       } else {
+               if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
+                       /* another llc task is ongoing */
+                       mutex_unlock(&lgr->llc_conf_mutex);
+                       wait_event_interruptible_timeout(lgr->llc_waiter,
+                               (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE),
+                               SMC_LLC_WAIT_TIME);
+                       mutex_lock(&lgr->llc_conf_mutex);
+               }
+               smc_llc_send_delete_link(to_lnk, del_link_id, SMC_LLC_REQ, true,
+                                        SMC_LLC_DEL_LOST_PATH);
+       }
+}
+
+/* must be called under lgr->llc_conf_mutex lock */
+void smcr_link_down_cond(struct smc_link *lnk)
+{
+       if (smc_link_downing(&lnk->state))
+               smcr_link_down(lnk);
+}
+
+/* will get the lgr->llc_conf_mutex lock */
+void smcr_link_down_cond_sched(struct smc_link *lnk)
+{
+       if (smc_link_downing(&lnk->state))
+               schedule_work(&lnk->link_down_wrk);
+}
+
+void smcr_port_err(struct smc_ib_device *smcibdev, u8 ibport)
+{
+       struct smc_link_group *lgr, *n;
+       int i;
+
+       list_for_each_entry_safe(lgr, n, &smc_lgr_list.list, list) {
+               if (strncmp(smcibdev->pnetid[ibport - 1], lgr->pnet_id,
+                           SMC_MAX_PNETID_LEN))
+                       continue; /* lgr is not affected */
+               if (list_empty(&lgr->list))
+                       continue;
+               for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+                       struct smc_link *lnk = &lgr->lnk[i];
+
+                       if (smc_link_usable(lnk) &&
+                           lnk->smcibdev == smcibdev && lnk->ibport == ibport)
+                               smcr_link_down_cond_sched(lnk);
+               }
+       }
+}
+
+static void smc_link_up_work(struct work_struct *work)
+{
+       struct smc_ib_up_work *ib_work = container_of(work,
+                                                     struct smc_ib_up_work,
+                                                     work);
+       struct smc_link_group *lgr = ib_work->lgr;
+
+       if (list_empty(&lgr->list))
+               goto out;
+       smcr_link_up(lgr, ib_work->smcibdev, ib_work->ibport);
+out:
+       kfree(ib_work);
+}
+
+static void smc_link_down_work(struct work_struct *work)
+{
+       struct smc_link *link = container_of(work, struct smc_link,
+                                            link_down_wrk);
+       struct smc_link_group *lgr = link->lgr;
+
+       if (list_empty(&lgr->list))
+               return;
+       wake_up_interruptible_all(&lgr->llc_waiter);
+       mutex_lock(&lgr->llc_conf_mutex);
+       smcr_link_down(link);
+       mutex_unlock(&lgr->llc_conf_mutex);
+}
+
 /* Determine vlan of internal TCP socket.
  * @vlan_id: address to store the determined vlan id into
  */
@@ -810,15 +1307,21 @@ static bool smcr_lgr_match(struct smc_link_group *lgr,
                           struct smc_clc_msg_local *lcl,
                           enum smc_lgr_role role, u32 clcqpn)
 {
-       return !memcmp(lgr->peer_systemid, lcl->id_for_peer,
-                      SMC_SYSTEMID_LEN) &&
-               !memcmp(lgr->lnk[SMC_SINGLE_LINK].peer_gid, &lcl->gid,
-                       SMC_GID_SIZE) &&
-               !memcmp(lgr->lnk[SMC_SINGLE_LINK].peer_mac, lcl->mac,
-                       sizeof(lcl->mac)) &&
-               lgr->role == role &&
-               (lgr->role == SMC_SERV ||
-                lgr->lnk[SMC_SINGLE_LINK].peer_qpn == clcqpn);
+       int i;
+
+       if (memcmp(lgr->peer_systemid, lcl->id_for_peer, SMC_SYSTEMID_LEN) ||
+           lgr->role != role)
+               return false;
+
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               if (lgr->lnk[i].state != SMC_LNK_ACTIVE)
+                       continue;
+               if ((lgr->role == SMC_SERV || lgr->lnk[i].peer_qpn == clcqpn) &&
+                   !memcmp(lgr->lnk[i].peer_gid, &lcl->gid, SMC_GID_SIZE) &&
+                   !memcmp(lgr->lnk[i].peer_mac, lcl->mac, sizeof(lcl->mac)))
+                       return true;
+       }
+       return false;
 }
 
 static bool smcd_lgr_match(struct smc_link_group *lgr,
@@ -859,15 +1362,17 @@ int smc_conn_create(struct smc_sock *smc, struct smc_init_info *ini)
                        /* link group found */
                        ini->cln_first_contact = SMC_REUSE_CONTACT;
                        conn->lgr = lgr;
-                       smc_lgr_register_conn(conn); /* add smc conn to lgr */
-                       if (delayed_work_pending(&lgr->free_work))
-                               cancel_delayed_work(&lgr->free_work);
+                       rc = smc_lgr_register_conn(conn, false);
                        write_unlock_bh(&lgr->conns_lock);
+                       if (!rc && delayed_work_pending(&lgr->free_work))
+                               cancel_delayed_work(&lgr->free_work);
                        break;
                }
                write_unlock_bh(&lgr->conns_lock);
        }
        spin_unlock_bh(lgr_lock);
+       if (rc)
+               return rc;
 
        if (role == SMC_CLNT && !ini->srv_first_contact &&
            ini->cln_first_contact == SMC_FIRST_CONTACT) {
@@ -885,12 +1390,15 @@ create:
                        goto out;
                lgr = conn->lgr;
                write_lock_bh(&lgr->conns_lock);
-               smc_lgr_register_conn(conn); /* add smc conn to lgr */
+               rc = smc_lgr_register_conn(conn, true);
                write_unlock_bh(&lgr->conns_lock);
+               if (rc)
+                       goto out;
        }
        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;
+       INIT_WORK(&smc->conn.abort_work, smc_conn_abort_work);
        if (ini->is_smcd) {
                conn->rx_off = sizeof(struct smcd_cdc_msg);
                smcd_cdc_rx_init(conn); /* init tasklet for this conn */
@@ -934,19 +1442,19 @@ int smc_uncompress_bufsize(u8 compressed)
  * buffer size; if not available, return NULL
  */
 static struct smc_buf_desc *smc_buf_get_slot(int compressed_bufsize,
-                                            rwlock_t *lock,
+                                            struct mutex *lock,
                                             struct list_head *buf_list)
 {
        struct smc_buf_desc *buf_slot;
 
-       read_lock_bh(lock);
+       mutex_lock(lock);
        list_for_each_entry(buf_slot, buf_list, list) {
                if (cmpxchg(&buf_slot->used, 0, 1) == 0) {
-                       read_unlock_bh(lock);
+                       mutex_unlock(lock);
                        return buf_slot;
                }
        }
-       read_unlock_bh(lock);
+       mutex_unlock(lock);
        return NULL;
 }
 
@@ -959,12 +1467,135 @@ static inline int smc_rmb_wnd_update_limit(int rmbe_size)
        return min_t(int, rmbe_size / 10, SOCK_MIN_SNDBUF / 2);
 }
 
+/* map an rmb buf to a link */
+static int smcr_buf_map_link(struct smc_buf_desc *buf_desc, bool is_rmb,
+                            struct smc_link *lnk)
+{
+       int rc;
+
+       if (buf_desc->is_map_ib[lnk->link_idx])
+               return 0;
+
+       rc = sg_alloc_table(&buf_desc->sgt[lnk->link_idx], 1, GFP_KERNEL);
+       if (rc)
+               return rc;
+       sg_set_buf(buf_desc->sgt[lnk->link_idx].sgl,
+                  buf_desc->cpu_addr, buf_desc->len);
+
+       /* map sg table to DMA address */
+       rc = smc_ib_buf_map_sg(lnk, buf_desc,
+                              is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       /* SMC protocol depends on mapping to one DMA address only */
+       if (rc != 1) {
+               rc = -EAGAIN;
+               goto free_table;
+       }
+
+       /* create a new memory region for the RMB */
+       if (is_rmb) {
+               rc = smc_ib_get_memory_region(lnk->roce_pd,
+                                             IB_ACCESS_REMOTE_WRITE |
+                                             IB_ACCESS_LOCAL_WRITE,
+                                             buf_desc, lnk->link_idx);
+               if (rc)
+                       goto buf_unmap;
+               smc_ib_sync_sg_for_device(lnk, buf_desc, DMA_FROM_DEVICE);
+       }
+       buf_desc->is_map_ib[lnk->link_idx] = true;
+       return 0;
+
+buf_unmap:
+       smc_ib_buf_unmap_sg(lnk, buf_desc,
+                           is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+free_table:
+       sg_free_table(&buf_desc->sgt[lnk->link_idx]);
+       return rc;
+}
+
+/* register a new rmb on IB device,
+ * must be called under lgr->llc_conf_mutex lock
+ */
+int smcr_link_reg_rmb(struct smc_link *link, struct smc_buf_desc *rmb_desc)
+{
+       if (list_empty(&link->lgr->list))
+               return -ENOLINK;
+       if (!rmb_desc->is_reg_mr[link->link_idx]) {
+               /* register memory region for new rmb */
+               if (smc_wr_reg_send(link, rmb_desc->mr_rx[link->link_idx])) {
+                       rmb_desc->is_reg_err = true;
+                       return -EFAULT;
+               }
+               rmb_desc->is_reg_mr[link->link_idx] = true;
+       }
+       return 0;
+}
+
+static int _smcr_buf_map_lgr(struct smc_link *lnk, struct mutex *lock,
+                            struct list_head *lst, bool is_rmb)
+{
+       struct smc_buf_desc *buf_desc, *bf;
+       int rc = 0;
+
+       mutex_lock(lock);
+       list_for_each_entry_safe(buf_desc, bf, lst, list) {
+               if (!buf_desc->used)
+                       continue;
+               rc = smcr_buf_map_link(buf_desc, is_rmb, lnk);
+               if (rc)
+                       goto out;
+       }
+out:
+       mutex_unlock(lock);
+       return rc;
+}
+
+/* map all used buffers of lgr for a new link */
+int smcr_buf_map_lgr(struct smc_link *lnk)
+{
+       struct smc_link_group *lgr = lnk->lgr;
+       int i, rc = 0;
+
+       for (i = 0; i < SMC_RMBE_SIZES; i++) {
+               rc = _smcr_buf_map_lgr(lnk, &lgr->rmbs_lock,
+                                      &lgr->rmbs[i], true);
+               if (rc)
+                       return rc;
+               rc = _smcr_buf_map_lgr(lnk, &lgr->sndbufs_lock,
+                                      &lgr->sndbufs[i], false);
+               if (rc)
+                       return rc;
+       }
+       return 0;
+}
+
+/* register all used buffers of lgr for a new link,
+ * must be called under lgr->llc_conf_mutex lock
+ */
+int smcr_buf_reg_lgr(struct smc_link *lnk)
+{
+       struct smc_link_group *lgr = lnk->lgr;
+       struct smc_buf_desc *buf_desc, *bf;
+       int i, rc = 0;
+
+       mutex_lock(&lgr->rmbs_lock);
+       for (i = 0; i < SMC_RMBE_SIZES; i++) {
+               list_for_each_entry_safe(buf_desc, bf, &lgr->rmbs[i], list) {
+                       if (!buf_desc->used)
+                               continue;
+                       rc = smcr_link_reg_rmb(lnk, buf_desc);
+                       if (rc)
+                               goto out;
+               }
+       }
+out:
+       mutex_unlock(&lgr->rmbs_lock);
+       return rc;
+}
+
 static struct smc_buf_desc *smcr_new_buf_create(struct smc_link_group *lgr,
                                                bool is_rmb, int bufsize)
 {
        struct smc_buf_desc *buf_desc;
-       struct smc_link *lnk;
-       int rc;
 
        /* try to alloc a new buffer */
        buf_desc = kzalloc(sizeof(*buf_desc), GFP_KERNEL);
@@ -981,41 +1612,33 @@ static struct smc_buf_desc *smcr_new_buf_create(struct smc_link_group *lgr,
                return ERR_PTR(-EAGAIN);
        }
        buf_desc->cpu_addr = (void *)page_address(buf_desc->pages);
+       buf_desc->len = bufsize;
+       return buf_desc;
+}
 
-       /* build the sg table from the pages */
-       lnk = &lgr->lnk[SMC_SINGLE_LINK];
-       rc = sg_alloc_table(&buf_desc->sgt[SMC_SINGLE_LINK], 1,
-                           GFP_KERNEL);
-       if (rc) {
-               smc_buf_free(lgr, is_rmb, buf_desc);
-               return ERR_PTR(rc);
-       }
-       sg_set_buf(buf_desc->sgt[SMC_SINGLE_LINK].sgl,
-                  buf_desc->cpu_addr, bufsize);
+/* map buf_desc on all usable links,
+ * unused buffers stay mapped as long as the link is up
+ */
+static int smcr_buf_map_usable_links(struct smc_link_group *lgr,
+                                    struct smc_buf_desc *buf_desc, bool is_rmb)
+{
+       int i, rc = 0;
 
-       /* map sg table to DMA address */
-       rc = smc_ib_buf_map_sg(lnk->smcibdev, buf_desc,
-                              is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
-       /* SMC protocol depends on mapping to one DMA address only */
-       if (rc != 1)  {
-               smc_buf_free(lgr, is_rmb, buf_desc);
-               return ERR_PTR(-EAGAIN);
-       }
+       /* protect against parallel link reconfiguration */
+       mutex_lock(&lgr->llc_conf_mutex);
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               struct smc_link *lnk = &lgr->lnk[i];
 
-       /* create a new memory region for the RMB */
-       if (is_rmb) {
-               rc = smc_ib_get_memory_region(lnk->roce_pd,
-                                             IB_ACCESS_REMOTE_WRITE |
-                                             IB_ACCESS_LOCAL_WRITE,
-                                             buf_desc);
-               if (rc) {
-                       smc_buf_free(lgr, is_rmb, buf_desc);
-                       return ERR_PTR(rc);
+               if (!smc_link_usable(lnk))
+                       continue;
+               if (smcr_buf_map_link(buf_desc, is_rmb, lnk)) {
+                       rc = -ENOMEM;
+                       goto out;
                }
        }
-
-       buf_desc->len = bufsize;
-       return buf_desc;
+out:
+       mutex_unlock(&lgr->llc_conf_mutex);
+       return rc;
 }
 
 #define SMCD_DMBE_SIZES                7 /* 0 -> 16KB, 1 -> 32KB, .. 6 -> 1MB */
@@ -1062,8 +1685,8 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb)
        struct smc_link_group *lgr = conn->lgr;
        struct list_head *buf_list;
        int bufsize, bufsize_short;
+       struct mutex *lock;     /* lock buffer list */
        int sk_buf_size;
-       rwlock_t *lock;
 
        if (is_rmb)
                /* use socket recv buffer size (w/o overhead) as start value */
@@ -1104,15 +1727,22 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb)
                        continue;
 
                buf_desc->used = 1;
-               write_lock_bh(lock);
+               mutex_lock(lock);
                list_add(&buf_desc->list, buf_list);
-               write_unlock_bh(lock);
+               mutex_unlock(lock);
                break; /* found */
        }
 
        if (IS_ERR(buf_desc))
                return -ENOMEM;
 
+       if (!is_smcd) {
+               if (smcr_buf_map_usable_links(lgr, buf_desc, is_rmb)) {
+                       smcr_buf_unuse(buf_desc, lgr);
+                       return -ENOMEM;
+               }
+       }
+
        if (is_rmb) {
                conn->rmb_desc = buf_desc;
                conn->rmbe_size_short = bufsize_short;
@@ -1132,42 +1762,44 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_smcd, bool is_rmb)
 
 void smc_sndbuf_sync_sg_for_cpu(struct smc_connection *conn)
 {
-       struct smc_link_group *lgr = conn->lgr;
-
-       if (!conn->lgr || conn->lgr->is_smcd)
+       if (!conn->lgr || conn->lgr->is_smcd || !smc_link_usable(conn->lnk))
                return;
-       smc_ib_sync_sg_for_cpu(lgr->lnk[SMC_SINGLE_LINK].smcibdev,
-                              conn->sndbuf_desc, DMA_TO_DEVICE);
+       smc_ib_sync_sg_for_cpu(conn->lnk, conn->sndbuf_desc, DMA_TO_DEVICE);
 }
 
 void smc_sndbuf_sync_sg_for_device(struct smc_connection *conn)
 {
-       struct smc_link_group *lgr = conn->lgr;
-
-       if (!conn->lgr || conn->lgr->is_smcd)
+       if (!conn->lgr || conn->lgr->is_smcd || !smc_link_usable(conn->lnk))
                return;
-       smc_ib_sync_sg_for_device(lgr->lnk[SMC_SINGLE_LINK].smcibdev,
-                                 conn->sndbuf_desc, DMA_TO_DEVICE);
+       smc_ib_sync_sg_for_device(conn->lnk, conn->sndbuf_desc, DMA_TO_DEVICE);
 }
 
 void smc_rmb_sync_sg_for_cpu(struct smc_connection *conn)
 {
-       struct smc_link_group *lgr = conn->lgr;
+       int i;
 
        if (!conn->lgr || conn->lgr->is_smcd)
                return;
-       smc_ib_sync_sg_for_cpu(lgr->lnk[SMC_SINGLE_LINK].smcibdev,
-                              conn->rmb_desc, DMA_FROM_DEVICE);
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               if (!smc_link_usable(&conn->lgr->lnk[i]))
+                       continue;
+               smc_ib_sync_sg_for_cpu(&conn->lgr->lnk[i], conn->rmb_desc,
+                                      DMA_FROM_DEVICE);
+       }
 }
 
 void smc_rmb_sync_sg_for_device(struct smc_connection *conn)
 {
-       struct smc_link_group *lgr = conn->lgr;
+       int i;
 
        if (!conn->lgr || conn->lgr->is_smcd)
                return;
-       smc_ib_sync_sg_for_device(lgr->lnk[SMC_SINGLE_LINK].smcibdev,
-                                 conn->rmb_desc, DMA_FROM_DEVICE);
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               if (!smc_link_usable(&conn->lgr->lnk[i]))
+                       continue;
+               smc_ib_sync_sg_for_device(&conn->lgr->lnk[i], conn->rmb_desc,
+                                         DMA_FROM_DEVICE);
+       }
 }
 
 /* create the send and receive buffer for an SMC socket;
@@ -1202,16 +1834,64 @@ static inline int smc_rmb_reserve_rtoken_idx(struct smc_link_group *lgr)
        return -ENOSPC;
 }
 
+static int smc_rtoken_find_by_link(struct smc_link_group *lgr, int lnk_idx,
+                                  u32 rkey)
+{
+       int i;
+
+       for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
+               if (test_bit(i, lgr->rtokens_used_mask) &&
+                   lgr->rtokens[i][lnk_idx].rkey == rkey)
+                       return i;
+       }
+       return -ENOENT;
+}
+
+/* set rtoken for a new link to an existing rmb */
+void smc_rtoken_set(struct smc_link_group *lgr, int link_idx, int link_idx_new,
+                   __be32 nw_rkey_known, __be64 nw_vaddr, __be32 nw_rkey)
+{
+       int rtok_idx;
+
+       rtok_idx = smc_rtoken_find_by_link(lgr, link_idx, ntohl(nw_rkey_known));
+       if (rtok_idx == -ENOENT)
+               return;
+       lgr->rtokens[rtok_idx][link_idx_new].rkey = ntohl(nw_rkey);
+       lgr->rtokens[rtok_idx][link_idx_new].dma_addr = be64_to_cpu(nw_vaddr);
+}
+
+/* set rtoken for a new link whose link_id is given */
+void smc_rtoken_set2(struct smc_link_group *lgr, int rtok_idx, int link_id,
+                    __be64 nw_vaddr, __be32 nw_rkey)
+{
+       u64 dma_addr = be64_to_cpu(nw_vaddr);
+       u32 rkey = ntohl(nw_rkey);
+       bool found = false;
+       int link_idx;
+
+       for (link_idx = 0; link_idx < SMC_LINKS_PER_LGR_MAX; link_idx++) {
+               if (lgr->lnk[link_idx].link_id == link_id) {
+                       found = true;
+                       break;
+               }
+       }
+       if (!found)
+               return;
+       lgr->rtokens[rtok_idx][link_idx].rkey = rkey;
+       lgr->rtokens[rtok_idx][link_idx].dma_addr = dma_addr;
+}
+
 /* add a new rtoken from peer */
-int smc_rtoken_add(struct smc_link_group *lgr, __be64 nw_vaddr, __be32 nw_rkey)
+int smc_rtoken_add(struct smc_link *lnk, __be64 nw_vaddr, __be32 nw_rkey)
 {
+       struct smc_link_group *lgr = smc_get_lgr(lnk);
        u64 dma_addr = be64_to_cpu(nw_vaddr);
        u32 rkey = ntohl(nw_rkey);
        int i;
 
        for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
-               if ((lgr->rtokens[i][SMC_SINGLE_LINK].rkey == rkey) &&
-                   (lgr->rtokens[i][SMC_SINGLE_LINK].dma_addr == dma_addr) &&
+               if (lgr->rtokens[i][lnk->link_idx].rkey == rkey &&
+                   lgr->rtokens[i][lnk->link_idx].dma_addr == dma_addr &&
                    test_bit(i, lgr->rtokens_used_mask)) {
                        /* already in list */
                        return i;
@@ -1220,23 +1900,25 @@ int smc_rtoken_add(struct smc_link_group *lgr, __be64 nw_vaddr, __be32 nw_rkey)
        i = smc_rmb_reserve_rtoken_idx(lgr);
        if (i < 0)
                return i;
-       lgr->rtokens[i][SMC_SINGLE_LINK].rkey = rkey;
-       lgr->rtokens[i][SMC_SINGLE_LINK].dma_addr = dma_addr;
+       lgr->rtokens[i][lnk->link_idx].rkey = rkey;
+       lgr->rtokens[i][lnk->link_idx].dma_addr = dma_addr;
        return i;
 }
 
-/* delete an rtoken */
-int smc_rtoken_delete(struct smc_link_group *lgr, __be32 nw_rkey)
+/* delete an rtoken from all links */
+int smc_rtoken_delete(struct smc_link *lnk, __be32 nw_rkey)
 {
+       struct smc_link_group *lgr = smc_get_lgr(lnk);
        u32 rkey = ntohl(nw_rkey);
-       int i;
+       int i, j;
 
        for (i = 0; i < SMC_RMBS_PER_LGR_MAX; i++) {
-               if (lgr->rtokens[i][SMC_SINGLE_LINK].rkey == rkey &&
+               if (lgr->rtokens[i][lnk->link_idx].rkey == rkey &&
                    test_bit(i, lgr->rtokens_used_mask)) {
-                       lgr->rtokens[i][SMC_SINGLE_LINK].rkey = 0;
-                       lgr->rtokens[i][SMC_SINGLE_LINK].dma_addr = 0;
-
+                       for (j = 0; j < SMC_LINKS_PER_LGR_MAX; j++) {
+                               lgr->rtokens[i][j].rkey = 0;
+                               lgr->rtokens[i][j].dma_addr = 0;
+                       }
                        clear_bit(i, lgr->rtokens_used_mask);
                        return 0;
                }
@@ -1246,9 +1928,10 @@ int smc_rtoken_delete(struct smc_link_group *lgr, __be32 nw_rkey)
 
 /* save rkey and dma_addr received from peer during clc handshake */
 int smc_rmb_rtoken_handling(struct smc_connection *conn,
+                           struct smc_link *lnk,
                            struct smc_clc_msg_accept_confirm *clc)
 {
-       conn->rtoken_idx = smc_rtoken_add(conn->lgr, clc->rmb_dma_addr,
+       conn->rtoken_idx = smc_rtoken_add(lnk, clc->rmb_dma_addr,
                                          clc->rmb_rkey);
        if (conn->rtoken_idx < 0)
                return conn->rtoken_idx;
index 8041db2..86d160f 100644 (file)
@@ -32,10 +32,10 @@ enum smc_lgr_role {         /* possible roles of a link group */
 };
 
 enum smc_link_state {                  /* possible states of a link */
+       SMC_LNK_UNUSED,         /* link is unused */
        SMC_LNK_INACTIVE,       /* link is inactive */
        SMC_LNK_ACTIVATING,     /* link is being activated */
        SMC_LNK_ACTIVE,         /* link is active */
-       SMC_LNK_DELETING,       /* link is being deleted */
 };
 
 #define SMC_WR_BUF_SIZE                48      /* size of work request buffer */
@@ -70,6 +70,8 @@ struct smc_rdma_wr {                          /* work requests per message
        struct ib_rdma_wr       wr_tx_rdma[SMC_MAX_RDMA_WRITES];
 };
 
+#define SMC_LGR_ID_SIZE                4
+
 struct smc_link {
        struct smc_ib_device    *smcibdev;      /* ib-device */
        u8                      ibport;         /* port - values 1 | 2 */
@@ -85,6 +87,7 @@ struct smc_link {
        struct smc_rdma_sges    *wr_tx_rdma_sges;/*RDMA WRITE gather meta data*/
        struct smc_rdma_wr      *wr_tx_rdmas;   /* WR RDMA WRITE */
        struct smc_wr_tx_pend   *wr_tx_pends;   /* WR send waiting for CQE */
+       struct completion       *wr_tx_compl;   /* WR send CQE completion */
        /* above four vectors have wr_tx_cnt elements and use the same index */
        dma_addr_t              wr_tx_dma_addr; /* DMA address of wr_tx_bufs */
        atomic_long_t           wr_tx_id;       /* seq # of last sent WR */
@@ -115,29 +118,23 @@ struct smc_link {
        u8                      peer_mac[ETH_ALEN];     /* = gid[8:10||13:15] */
        u8                      peer_gid[SMC_GID_SIZE]; /* gid of peer*/
        u8                      link_id;        /* unique # within link group */
+       u8                      link_uid[SMC_LGR_ID_SIZE]; /* unique lnk id */
+       u8                      peer_link_uid[SMC_LGR_ID_SIZE]; /* peer uid */
+       u8                      link_idx;       /* index in lgr link array */
+       u8                      link_is_asym;   /* is link asymmetric? */
+       struct smc_link_group   *lgr;           /* parent link group */
+       struct work_struct      link_down_wrk;  /* wrk to bring link down */
 
        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 */
-       int                     llc_confirm_resp_rc; /* rc from conf_resp msg */
-       struct completion       llc_add;        /* wait for rx of add link */
-       struct completion       llc_add_resp;   /* wait for rx of add link rsp*/
        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 */
-       struct completion       llc_delete_rkey; /* wait 4 rx of del rkey */
-       int                     llc_delete_rkey_rc; /* rc from del rkey msg */
-       struct mutex            llc_delete_rkey_mutex; /* serialize usage */
 };
 
 /* For now we just allow one parallel link per link group. The SMC protocol
  * allows more (up to 8).
  */
-#define SMC_LINKS_PER_LGR_MAX  1
+#define SMC_LINKS_PER_LGR_MAX  3
 #define SMC_SINGLE_LINK                0
 
 #define SMC_FIRST_CONTACT      1               /* first contact to a peer */
@@ -150,25 +147,32 @@ struct smc_buf_desc {
        struct page             *pages;
        int                     len;            /* length of buffer */
        u32                     used;           /* currently used / unused */
-       u8                      wr_reg  : 1;    /* mem region registered */
-       u8                      regerr  : 1;    /* err during registration */
        union {
                struct { /* SMC-R */
-                       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
-                                                * incl. rkey provided to peer
-                                                */
-                       u32                     order;  /* allocation order */
+                       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
+                                        * incl. rkey provided to peer
+                                        */
+                       u32             order;  /* allocation order */
+
+                       u8              is_conf_rkey;
+                                       /* confirm_rkey done */
+                       u8              is_reg_mr[SMC_LINKS_PER_LGR_MAX];
+                                       /* mem region registered */
+                       u8              is_map_ib[SMC_LINKS_PER_LGR_MAX];
+                                       /* mem region mapped to lnk */
+                       u8              is_reg_err;
+                                       /* buffer registration err */
                };
                struct { /* SMC-D */
-                       unsigned short          sba_idx;
-                                               /* SBA index number */
-                       u64                     token;
-                                               /* DMB token number */
-                       dma_addr_t              dma_addr;
-                                               /* DMA address */
+                       unsigned short  sba_idx;
+                                       /* SBA index number */
+                       u64             token;
+                                       /* DMB token number */
+                       dma_addr_t      dma_addr;
+                                       /* DMA address */
                };
        };
 };
@@ -178,7 +182,6 @@ struct smc_rtoken {                         /* address/key of remote RMB */
        u32                     rkey;
 };
 
-#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,
@@ -188,6 +191,28 @@ struct smc_rtoken {                                /* address/key of remote RMB */
 
 struct smcd_dev;
 
+enum smc_lgr_type {                            /* redundancy state of lgr */
+       SMC_LGR_NONE,                   /* no active links, lgr to be deleted */
+       SMC_LGR_SINGLE,                 /* 1 active RNIC on each peer */
+       SMC_LGR_SYMMETRIC,              /* 2 active RNICs on each peer */
+       SMC_LGR_ASYMMETRIC_PEER,        /* local has 2, peer 1 active RNICs */
+       SMC_LGR_ASYMMETRIC_LOCAL,       /* local has 1, peer 2 active RNICs */
+};
+
+enum smc_llc_flowtype {
+       SMC_LLC_FLOW_NONE       = 0,
+       SMC_LLC_FLOW_ADD_LINK   = 2,
+       SMC_LLC_FLOW_DEL_LINK   = 4,
+       SMC_LLC_FLOW_RKEY       = 6,
+};
+
+struct smc_llc_qentry;
+
+struct smc_llc_flow {
+       enum smc_llc_flowtype type;
+       struct smc_llc_qentry *qentry;
+};
+
 struct smc_link_group {
        struct list_head        list;
        struct rb_root          conns_all;      /* connection tree */
@@ -196,9 +221,9 @@ struct smc_link_group {
        unsigned short          vlan_id;        /* vlan id of link group */
 
        struct list_head        sndbufs[SMC_RMBE_SIZES];/* tx buffers */
-       rwlock_t                sndbufs_lock;   /* protects tx buffers */
+       struct mutex            sndbufs_lock;   /* protects tx buffers */
        struct list_head        rmbs[SMC_RMBE_SIZES];   /* rx buffers */
-       rwlock_t                rmbs_lock;      /* protects rx buffers */
+       struct mutex            rmbs_lock;      /* protects rx buffers */
 
        u8                      id[SMC_LGR_ID_SIZE];    /* unique lgr id */
        struct delayed_work     free_work;      /* delayed freeing of an lgr */
@@ -222,6 +247,35 @@ struct smc_link_group {
                                                /* remote addr/key pairs */
                        DECLARE_BITMAP(rtokens_used_mask, SMC_RMBS_PER_LGR_MAX);
                                                /* used rtoken elements */
+                       u8                      next_link_id;
+                       enum smc_lgr_type       type;
+                                               /* redundancy state */
+                       u8                      pnet_id[SMC_MAX_PNETID_LEN + 1];
+                                               /* pnet id of this lgr */
+                       struct list_head        llc_event_q;
+                                               /* queue for llc events */
+                       spinlock_t              llc_event_q_lock;
+                                               /* protects llc_event_q */
+                       struct mutex            llc_conf_mutex;
+                                               /* protects lgr reconfig. */
+                       struct work_struct      llc_add_link_work;
+                       struct work_struct      llc_del_link_work;
+                       struct work_struct      llc_event_work;
+                                               /* llc event worker */
+                       wait_queue_head_t       llc_waiter;
+                                               /* w4 next llc event */
+                       struct smc_llc_flow     llc_flow_lcl;
+                                               /* llc local control field */
+                       struct smc_llc_flow     llc_flow_rmt;
+                                               /* llc remote control field */
+                       struct smc_llc_qentry   *delayed_event;
+                                               /* arrived when flow active */
+                       spinlock_t              llc_flow_lock;
+                                               /* protects llc flow */
+                       int                     llc_testlink_time;
+                                               /* link keep alive time */
+                       u32                     llc_termination_rsn;
+                                               /* rsn code for termination */
                };
                struct { /* SMC-D */
                        u64                     peer_gid;
@@ -285,24 +339,36 @@ static inline struct smc_connection *smc_lgr_find_conn(
        return res;
 }
 
+/* returns true if the specified link is usable */
+static inline bool smc_link_usable(struct smc_link *lnk)
+{
+       if (lnk->state == SMC_LNK_UNUSED || lnk->state == SMC_LNK_INACTIVE)
+               return false;
+       return true;
+}
+
 struct smc_sock;
 struct smc_clc_msg_accept_confirm;
 struct smc_clc_msg_local;
 
-void smc_lgr_forget(struct smc_link_group *lgr);
 void smc_lgr_cleanup_early(struct smc_connection *conn);
 void smc_lgr_terminate_sched(struct smc_link_group *lgr);
-void smc_port_terminate(struct smc_ib_device *smcibdev, u8 ibport);
+void smcr_port_add(struct smc_ib_device *smcibdev, u8 ibport);
+void smcr_port_err(struct smc_ib_device *smcibdev, u8 ibport);
 void smc_smcd_terminate(struct smcd_dev *dev, u64 peer_gid,
                        unsigned short vlan);
 void smc_smcd_terminate_all(struct smcd_dev *dev);
 void smc_smcr_terminate_all(struct smc_ib_device *smcibdev);
 int smc_buf_create(struct smc_sock *smc, bool is_smcd);
 int smc_uncompress_bufsize(u8 compressed);
-int smc_rmb_rtoken_handling(struct smc_connection *conn,
+int smc_rmb_rtoken_handling(struct smc_connection *conn, struct smc_link *link,
                            struct smc_clc_msg_accept_confirm *clc);
-int smc_rtoken_add(struct smc_link_group *lgr, __be64 nw_vaddr, __be32 nw_rkey);
-int smc_rtoken_delete(struct smc_link_group *lgr, __be32 nw_rkey);
+int smc_rtoken_add(struct smc_link *lnk, __be64 nw_vaddr, __be32 nw_rkey);
+int smc_rtoken_delete(struct smc_link *lnk, __be32 nw_rkey);
+void smc_rtoken_set(struct smc_link_group *lgr, int link_idx, int link_idx_new,
+                   __be32 nw_rkey_known, __be64 nw_vaddr, __be32 nw_rkey);
+void smc_rtoken_set2(struct smc_link_group *lgr, int rtok_idx, int link_id,
+                    __be64 nw_vaddr, __be32 nw_rkey);
 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);
@@ -315,8 +381,22 @@ void smc_lgr_schedule_free_work_fast(struct smc_link_group *lgr);
 int smc_core_init(void);
 void smc_core_exit(void);
 
+int smcr_link_init(struct smc_link_group *lgr, struct smc_link *lnk,
+                  u8 link_idx, struct smc_init_info *ini);
+void smcr_link_clear(struct smc_link *lnk, bool log);
+int smcr_buf_map_lgr(struct smc_link *lnk);
+int smcr_buf_reg_lgr(struct smc_link *lnk);
+void smcr_lgr_set_type(struct smc_link_group *lgr, enum smc_lgr_type new_type);
+void smcr_lgr_set_type_asym(struct smc_link_group *lgr,
+                           enum smc_lgr_type new_type, int asym_lnk_idx);
+int smcr_link_reg_rmb(struct smc_link *link, struct smc_buf_desc *rmb_desc);
+struct smc_link *smc_switch_conns(struct smc_link_group *lgr,
+                                 struct smc_link *from_lnk, bool is_dev_err);
+void smcr_link_down_cond(struct smc_link *lnk);
+void smcr_link_down_cond_sched(struct smc_link *lnk);
+
 static inline struct smc_link_group *smc_get_lgr(struct smc_link *link)
 {
-       return container_of(link, struct smc_link_group, lnk[SMC_SINGLE_LINK]);
+       return link->lgr;
 }
 #endif
index 04b6fef..f0a5064 100644 (file)
@@ -249,9 +249,10 @@ static void smc_ib_port_event_work(struct work_struct *work)
                clear_bit(port_idx, &smcibdev->port_event_mask);
                if (!smc_ib_port_active(smcibdev, port_idx + 1)) {
                        set_bit(port_idx, smcibdev->ports_going_away);
-                       smc_port_terminate(smcibdev, port_idx + 1);
+                       smcr_port_err(smcibdev, port_idx + 1);
                } else {
                        clear_bit(port_idx, smcibdev->ports_going_away);
+                       smcr_port_add(smcibdev, port_idx + 1);
                }
        }
 }
@@ -389,15 +390,15 @@ void smc_ib_put_memory_region(struct ib_mr *mr)
        ib_dereg_mr(mr);
 }
 
-static int smc_ib_map_mr_sg(struct smc_buf_desc *buf_slot)
+static int smc_ib_map_mr_sg(struct smc_buf_desc *buf_slot, u8 link_idx)
 {
        unsigned int offset = 0;
        int sg_num;
 
        /* map the largest prefix of a dma mapped SG list */
-       sg_num = ib_map_mr_sg(buf_slot->mr_rx[SMC_SINGLE_LINK],
-                             buf_slot->sgt[SMC_SINGLE_LINK].sgl,
-                             buf_slot->sgt[SMC_SINGLE_LINK].orig_nents,
+       sg_num = ib_map_mr_sg(buf_slot->mr_rx[link_idx],
+                             buf_slot->sgt[link_idx].sgl,
+                             buf_slot->sgt[link_idx].orig_nents,
                              &offset, PAGE_SIZE);
 
        return sg_num;
@@ -405,29 +406,29 @@ static int smc_ib_map_mr_sg(struct smc_buf_desc *buf_slot)
 
 /* Allocate a memory region and map the dma mapped SG list of buf_slot */
 int smc_ib_get_memory_region(struct ib_pd *pd, int access_flags,
-                            struct smc_buf_desc *buf_slot)
+                            struct smc_buf_desc *buf_slot, u8 link_idx)
 {
-       if (buf_slot->mr_rx[SMC_SINGLE_LINK])
+       if (buf_slot->mr_rx[link_idx])
                return 0; /* already done */
 
-       buf_slot->mr_rx[SMC_SINGLE_LINK] =
+       buf_slot->mr_rx[link_idx] =
                ib_alloc_mr(pd, IB_MR_TYPE_MEM_REG, 1 << buf_slot->order);
-       if (IS_ERR(buf_slot->mr_rx[SMC_SINGLE_LINK])) {
+       if (IS_ERR(buf_slot->mr_rx[link_idx])) {
                int rc;
 
-               rc = PTR_ERR(buf_slot->mr_rx[SMC_SINGLE_LINK]);
-               buf_slot->mr_rx[SMC_SINGLE_LINK] = NULL;
+               rc = PTR_ERR(buf_slot->mr_rx[link_idx]);
+               buf_slot->mr_rx[link_idx] = NULL;
                return rc;
        }
 
-       if (smc_ib_map_mr_sg(buf_slot) != 1)
+       if (smc_ib_map_mr_sg(buf_slot, link_idx) != 1)
                return -EINVAL;
 
        return 0;
 }
 
 /* synchronize buffer usage for cpu access */
-void smc_ib_sync_sg_for_cpu(struct smc_ib_device *smcibdev,
+void smc_ib_sync_sg_for_cpu(struct smc_link *lnk,
                            struct smc_buf_desc *buf_slot,
                            enum dma_data_direction data_direction)
 {
@@ -435,11 +436,11 @@ void smc_ib_sync_sg_for_cpu(struct smc_ib_device *smcibdev,
        unsigned int i;
 
        /* for now there is just one DMA address */
-       for_each_sg(buf_slot->sgt[SMC_SINGLE_LINK].sgl, sg,
-                   buf_slot->sgt[SMC_SINGLE_LINK].nents, i) {
+       for_each_sg(buf_slot->sgt[lnk->link_idx].sgl, sg,
+                   buf_slot->sgt[lnk->link_idx].nents, i) {
                if (!sg_dma_len(sg))
                        break;
-               ib_dma_sync_single_for_cpu(smcibdev->ibdev,
+               ib_dma_sync_single_for_cpu(lnk->smcibdev->ibdev,
                                           sg_dma_address(sg),
                                           sg_dma_len(sg),
                                           data_direction);
@@ -447,7 +448,7 @@ void smc_ib_sync_sg_for_cpu(struct smc_ib_device *smcibdev,
 }
 
 /* synchronize buffer usage for device access */
-void smc_ib_sync_sg_for_device(struct smc_ib_device *smcibdev,
+void smc_ib_sync_sg_for_device(struct smc_link *lnk,
                               struct smc_buf_desc *buf_slot,
                               enum dma_data_direction data_direction)
 {
@@ -455,11 +456,11 @@ void smc_ib_sync_sg_for_device(struct smc_ib_device *smcibdev,
        unsigned int i;
 
        /* for now there is just one DMA address */
-       for_each_sg(buf_slot->sgt[SMC_SINGLE_LINK].sgl, sg,
-                   buf_slot->sgt[SMC_SINGLE_LINK].nents, i) {
+       for_each_sg(buf_slot->sgt[lnk->link_idx].sgl, sg,
+                   buf_slot->sgt[lnk->link_idx].nents, i) {
                if (!sg_dma_len(sg))
                        break;
-               ib_dma_sync_single_for_device(smcibdev->ibdev,
+               ib_dma_sync_single_for_device(lnk->smcibdev->ibdev,
                                              sg_dma_address(sg),
                                              sg_dma_len(sg),
                                              data_direction);
@@ -467,15 +468,15 @@ void smc_ib_sync_sg_for_device(struct smc_ib_device *smcibdev,
 }
 
 /* Map a new TX or RX buffer SG-table to DMA */
-int smc_ib_buf_map_sg(struct smc_ib_device *smcibdev,
+int smc_ib_buf_map_sg(struct smc_link *lnk,
                      struct smc_buf_desc *buf_slot,
                      enum dma_data_direction data_direction)
 {
        int mapped_nents;
 
-       mapped_nents = ib_dma_map_sg(smcibdev->ibdev,
-                                    buf_slot->sgt[SMC_SINGLE_LINK].sgl,
-                                    buf_slot->sgt[SMC_SINGLE_LINK].orig_nents,
+       mapped_nents = ib_dma_map_sg(lnk->smcibdev->ibdev,
+                                    buf_slot->sgt[lnk->link_idx].sgl,
+                                    buf_slot->sgt[lnk->link_idx].orig_nents,
                                     data_direction);
        if (!mapped_nents)
                return -ENOMEM;
@@ -483,18 +484,18 @@ int smc_ib_buf_map_sg(struct smc_ib_device *smcibdev,
        return mapped_nents;
 }
 
-void smc_ib_buf_unmap_sg(struct smc_ib_device *smcibdev,
+void smc_ib_buf_unmap_sg(struct smc_link *lnk,
                         struct smc_buf_desc *buf_slot,
                         enum dma_data_direction data_direction)
 {
-       if (!buf_slot->sgt[SMC_SINGLE_LINK].sgl->dma_address)
+       if (!buf_slot->sgt[lnk->link_idx].sgl->dma_address)
                return; /* already unmapped */
 
-       ib_dma_unmap_sg(smcibdev->ibdev,
-                       buf_slot->sgt[SMC_SINGLE_LINK].sgl,
-                       buf_slot->sgt[SMC_SINGLE_LINK].orig_nents,
+       ib_dma_unmap_sg(lnk->smcibdev->ibdev,
+                       buf_slot->sgt[lnk->link_idx].sgl,
+                       buf_slot->sgt[lnk->link_idx].orig_nents,
                        data_direction);
-       buf_slot->sgt[SMC_SINGLE_LINK].sgl->dma_address = 0;
+       buf_slot->sgt[lnk->link_idx].sgl->dma_address = 0;
 }
 
 long smc_ib_setup_per_ibdev(struct smc_ib_device *smcibdev)
@@ -574,13 +575,23 @@ static void smc_ib_add_dev(struct ib_device *ibdev)
 
        /* trigger reading of the port attributes */
        port_cnt = smcibdev->ibdev->phys_port_cnt;
+       pr_warn_ratelimited("smc: adding ib device %s with port count %d\n",
+                           smcibdev->ibdev->name, port_cnt);
        for (i = 0;
             i < min_t(size_t, port_cnt, SMC_MAX_PORTS);
             i++) {
                set_bit(i, &smcibdev->port_event_mask);
                /* determine pnetids of the port */
-               smc_pnetid_by_dev_port(ibdev->dev.parent, i,
-                                      smcibdev->pnetid[i]);
+               if (smc_pnetid_by_dev_port(ibdev->dev.parent, i,
+                                          smcibdev->pnetid[i]))
+                       smc_pnetid_by_table_ib(smcibdev, i + 1);
+               pr_warn_ratelimited("smc:    ib device %s port %d has pnetid "
+                                   "%.16s%s\n",
+                                   smcibdev->ibdev->name, i + 1,
+                                   smcibdev->pnetid[i],
+                                   smcibdev->pnetid_by_user[i] ?
+                                    " (user defined)" :
+                                    "");
        }
        schedule_work(&smcibdev->port_event_work);
 }
@@ -597,6 +608,8 @@ static void smc_ib_remove_dev(struct ib_device *ibdev, void *client_data)
        spin_lock(&smc_ib_devices.lock);
        list_del_init(&smcibdev->list); /* remove from smc_ib_devices */
        spin_unlock(&smc_ib_devices.lock);
+       pr_warn_ratelimited("smc: removing ib device %s\n",
+                           smcibdev->ibdev->name);
        smc_smcr_terminate_all(smcibdev);
        smc_ib_cleanup_per_ibdev(smcibdev);
        ib_unregister_event_handler(&smcibdev->event_handler);
index 5c2b115..e6a696a 100644 (file)
@@ -59,10 +59,10 @@ struct smc_link;
 int smc_ib_register_client(void) __init;
 void smc_ib_unregister_client(void);
 bool smc_ib_port_active(struct smc_ib_device *smcibdev, u8 ibport);
-int smc_ib_buf_map_sg(struct smc_ib_device *smcibdev,
+int smc_ib_buf_map_sg(struct smc_link *lnk,
                      struct smc_buf_desc *buf_slot,
                      enum dma_data_direction data_direction);
-void smc_ib_buf_unmap_sg(struct smc_ib_device *smcibdev,
+void smc_ib_buf_unmap_sg(struct smc_link *lnk,
                         struct smc_buf_desc *buf_slot,
                         enum dma_data_direction data_direction);
 void smc_ib_dealloc_protection_domain(struct smc_link *lnk);
@@ -74,12 +74,12 @@ int smc_ib_modify_qp_rts(struct smc_link *lnk);
 int smc_ib_modify_qp_reset(struct smc_link *lnk);
 long smc_ib_setup_per_ibdev(struct smc_ib_device *smcibdev);
 int smc_ib_get_memory_region(struct ib_pd *pd, int access_flags,
-                            struct smc_buf_desc *buf_slot);
+                            struct smc_buf_desc *buf_slot, u8 link_idx);
 void smc_ib_put_memory_region(struct ib_mr *mr);
-void smc_ib_sync_sg_for_cpu(struct smc_ib_device *smcibdev,
+void smc_ib_sync_sg_for_cpu(struct smc_link *lnk,
                            struct smc_buf_desc *buf_slot,
                            enum dma_data_direction data_direction);
-void smc_ib_sync_sg_for_device(struct smc_ib_device *smcibdev,
+void smc_ib_sync_sg_for_device(struct smc_link *lnk,
                               struct smc_buf_desc *buf_slot,
                               enum dma_data_direction data_direction);
 int smc_ib_determine_gid(struct smc_ib_device *smcibdev, u8 ibport,
index 5c4727d..91f85fc 100644 (file)
@@ -296,7 +296,8 @@ struct smcd_dev *smcd_alloc_dev(struct device *parent, const char *name,
        device_initialize(&smcd->dev);
        dev_set_name(&smcd->dev, name);
        smcd->ops = ops;
-       smc_pnetid_by_dev_port(parent, 0, smcd->pnetid);
+       if (smc_pnetid_by_dev_port(parent, 0, smcd->pnetid))
+               smc_pnetid_by_table_smcd(smcd);
 
        spin_lock_init(&smcd->lock);
        spin_lock_init(&smcd->lgr_lock);
@@ -320,12 +321,18 @@ int smcd_register_dev(struct smcd_dev *smcd)
        list_add_tail(&smcd->list, &smcd_dev_list.list);
        spin_unlock(&smcd_dev_list.lock);
 
+       pr_warn_ratelimited("smc: adding smcd device %s with pnetid %.16s%s\n",
+                           dev_name(&smcd->dev), smcd->pnetid,
+                           smcd->pnetid_by_user ? " (user defined)" : "");
+
        return device_add(&smcd->dev);
 }
 EXPORT_SYMBOL_GPL(smcd_register_dev);
 
 void smcd_unregister_dev(struct smcd_dev *smcd)
 {
+       pr_warn_ratelimited("smc: removing smcd device %s\n",
+                           dev_name(&smcd->dev));
        spin_lock(&smcd_dev_list.lock);
        list_del_init(&smcd->list);
        spin_unlock(&smcd_dev_list.lock);
index 0e52aab..4cc5836 100644 (file)
@@ -17,6 +17,7 @@
 #include "smc_core.h"
 #include "smc_clc.h"
 #include "smc_llc.h"
+#include "smc_pnet.h"
 
 #define SMC_LLC_DATA_LEN               40
 
@@ -58,11 +59,34 @@ struct smc_llc_msg_add_link {               /* type 0x02 */
        u8 sender_gid[SMC_GID_SIZE];
        u8 sender_qp_num[3];
        u8 link_num;
-       u8 flags2;      /* QP mtu */
+#if defined(__BIG_ENDIAN_BITFIELD)
+       u8 reserved3 : 4,
+          qp_mtu   : 4;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       u8 qp_mtu   : 4,
+          reserved3 : 4;
+#endif
        u8 initial_psn[3];
        u8 reserved[8];
 };
 
+struct smc_llc_msg_add_link_cont_rt {
+       __be32 rmb_key;
+       __be32 rmb_key_new;
+       __be64 rmb_vaddr_new;
+};
+
+#define SMC_LLC_RKEYS_PER_CONT_MSG     2
+
+struct smc_llc_msg_add_link_cont {     /* type 0x03 */
+       struct smc_llc_hdr hd;
+       u8 link_num;
+       u8 num_rkeys;
+       u8 reserved2[2];
+       struct smc_llc_msg_add_link_cont_rt rt[SMC_LLC_RKEYS_PER_CONT_MSG];
+       u8 reserved[4];
+} __packed;                    /* format defined in RFC7609 */
+
 #define SMC_LLC_FLAG_DEL_LINK_ALL      0x40
 #define SMC_LLC_FLAG_DEL_LINK_ORDERLY  0x20
 
@@ -98,13 +122,8 @@ struct smc_llc_msg_confirm_rkey {   /* type 0x06 */
        u8 reserved;
 };
 
-struct smc_llc_msg_confirm_rkey_cont { /* type 0x08 */
-       struct smc_llc_hdr hd;
-       u8 num_rkeys;
-       struct smc_rmb_rtoken rtoken[SMC_LLC_RKEYS_PER_MSG];
-};
-
 #define SMC_LLC_DEL_RKEY_MAX   8
+#define SMC_LLC_FLAG_RKEY_RETRY        0x10
 #define SMC_LLC_FLAG_RKEY_NEG  0x20
 
 struct smc_llc_msg_delete_rkey {       /* type 0x09 */
@@ -119,10 +138,10 @@ struct smc_llc_msg_delete_rkey {  /* type 0x09 */
 union smc_llc_msg {
        struct smc_llc_msg_confirm_link confirm_link;
        struct smc_llc_msg_add_link add_link;
+       struct smc_llc_msg_add_link_cont add_link_cont;
        struct smc_llc_msg_del_link delete_link;
 
        struct smc_llc_msg_confirm_rkey confirm_rkey;
-       struct smc_llc_msg_confirm_rkey_cont confirm_rkey_cont;
        struct smc_llc_msg_delete_rkey delete_rkey;
 
        struct smc_llc_msg_test_link test_link;
@@ -134,6 +153,162 @@ union smc_llc_msg {
 
 #define SMC_LLC_FLAG_RESP              0x80
 
+struct smc_llc_qentry {
+       struct list_head list;
+       struct smc_link *link;
+       union smc_llc_msg msg;
+};
+
+static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc);
+
+struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow)
+{
+       struct smc_llc_qentry *qentry = flow->qentry;
+
+       flow->qentry = NULL;
+       return qentry;
+}
+
+void smc_llc_flow_qentry_del(struct smc_llc_flow *flow)
+{
+       struct smc_llc_qentry *qentry;
+
+       if (flow->qentry) {
+               qentry = flow->qentry;
+               flow->qentry = NULL;
+               kfree(qentry);
+       }
+}
+
+static inline void smc_llc_flow_qentry_set(struct smc_llc_flow *flow,
+                                          struct smc_llc_qentry *qentry)
+{
+       flow->qentry = qentry;
+}
+
+/* try to start a new llc flow, initiated by an incoming llc msg */
+static bool smc_llc_flow_start(struct smc_llc_flow *flow,
+                              struct smc_llc_qentry *qentry)
+{
+       struct smc_link_group *lgr = qentry->link->lgr;
+
+       spin_lock_bh(&lgr->llc_flow_lock);
+       if (flow->type) {
+               /* a flow is already active */
+               if ((qentry->msg.raw.hdr.common.type == SMC_LLC_ADD_LINK ||
+                    qentry->msg.raw.hdr.common.type == SMC_LLC_DELETE_LINK) &&
+                   !lgr->delayed_event) {
+                       lgr->delayed_event = qentry;
+               } else {
+                       /* forget this llc request */
+                       kfree(qentry);
+               }
+               spin_unlock_bh(&lgr->llc_flow_lock);
+               return false;
+       }
+       switch (qentry->msg.raw.hdr.common.type) {
+       case SMC_LLC_ADD_LINK:
+               flow->type = SMC_LLC_FLOW_ADD_LINK;
+               break;
+       case SMC_LLC_DELETE_LINK:
+               flow->type = SMC_LLC_FLOW_DEL_LINK;
+               break;
+       case SMC_LLC_CONFIRM_RKEY:
+       case SMC_LLC_DELETE_RKEY:
+               flow->type = SMC_LLC_FLOW_RKEY;
+               break;
+       default:
+               flow->type = SMC_LLC_FLOW_NONE;
+       }
+       if (qentry == lgr->delayed_event)
+               lgr->delayed_event = NULL;
+       spin_unlock_bh(&lgr->llc_flow_lock);
+       smc_llc_flow_qentry_set(flow, qentry);
+       return true;
+}
+
+/* start a new local llc flow, wait till current flow finished */
+int smc_llc_flow_initiate(struct smc_link_group *lgr,
+                         enum smc_llc_flowtype type)
+{
+       enum smc_llc_flowtype allowed_remote = SMC_LLC_FLOW_NONE;
+       int rc;
+
+       /* all flows except confirm_rkey and delete_rkey are exclusive,
+        * confirm/delete rkey flows can run concurrently (local and remote)
+        */
+       if (type == SMC_LLC_FLOW_RKEY)
+               allowed_remote = SMC_LLC_FLOW_RKEY;
+again:
+       if (list_empty(&lgr->list))
+               return -ENODEV;
+       spin_lock_bh(&lgr->llc_flow_lock);
+       if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE &&
+           (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE ||
+            lgr->llc_flow_rmt.type == allowed_remote)) {
+               lgr->llc_flow_lcl.type = type;
+               spin_unlock_bh(&lgr->llc_flow_lock);
+               return 0;
+       }
+       spin_unlock_bh(&lgr->llc_flow_lock);
+       rc = wait_event_interruptible_timeout(lgr->llc_waiter,
+                       (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_NONE &&
+                        (lgr->llc_flow_rmt.type == SMC_LLC_FLOW_NONE ||
+                         lgr->llc_flow_rmt.type == allowed_remote)),
+                       SMC_LLC_WAIT_TIME);
+       if (!rc)
+               return -ETIMEDOUT;
+       goto again;
+}
+
+/* finish the current llc flow */
+void smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow)
+{
+       spin_lock_bh(&lgr->llc_flow_lock);
+       memset(flow, 0, sizeof(*flow));
+       flow->type = SMC_LLC_FLOW_NONE;
+       spin_unlock_bh(&lgr->llc_flow_lock);
+       if (!list_empty(&lgr->list) && lgr->delayed_event &&
+           flow == &lgr->llc_flow_lcl)
+               schedule_work(&lgr->llc_event_work);
+       else
+               wake_up_interruptible(&lgr->llc_waiter);
+}
+
+/* lnk is optional and used for early wakeup when link goes down, useful in
+ * cases where we wait for a response on the link after we sent a request
+ */
+struct smc_llc_qentry *smc_llc_wait(struct smc_link_group *lgr,
+                                   struct smc_link *lnk,
+                                   int time_out, u8 exp_msg)
+{
+       struct smc_llc_flow *flow = &lgr->llc_flow_lcl;
+
+       wait_event_interruptible_timeout(lgr->llc_waiter,
+                                        (flow->qentry ||
+                                         (lnk && !smc_link_usable(lnk)) ||
+                                         list_empty(&lgr->list)),
+                                        time_out);
+       if (!flow->qentry ||
+           (lnk && !smc_link_usable(lnk)) || list_empty(&lgr->list)) {
+               smc_llc_flow_qentry_del(flow);
+               goto out;
+       }
+       if (exp_msg && flow->qentry->msg.raw.hdr.common.type != exp_msg) {
+               if (exp_msg == SMC_LLC_ADD_LINK &&
+                   flow->qentry->msg.raw.hdr.common.type ==
+                   SMC_LLC_DELETE_LINK) {
+                       /* flow_start will delay the unexpected msg */
+                       smc_llc_flow_start(&lgr->llc_flow_lcl,
+                                          smc_llc_flow_qentry_clr(flow));
+                       return NULL;
+               }
+               smc_llc_flow_qentry_del(flow);
+       }
+out:
+       return flow->qentry;
+}
+
 /********************************** send *************************************/
 
 struct smc_llc_tx_pend {
@@ -186,7 +361,6 @@ static int smc_llc_add_pending_send(struct smc_link *link,
 int smc_llc_send_confirm_link(struct smc_link *link,
                              enum smc_llc_reqresp reqresp)
 {
-       struct smc_link_group *lgr = smc_get_lgr(link);
        struct smc_llc_msg_confirm_link *confllc;
        struct smc_wr_tx_pend_priv *pend;
        struct smc_wr_buf *wr_buf;
@@ -207,35 +381,52 @@ int smc_llc_send_confirm_link(struct smc_link *link,
        memcpy(confllc->sender_gid, link->gid, SMC_GID_SIZE);
        hton24(confllc->sender_qp_num, link->roce_qp->qp_num);
        confllc->link_num = link->link_id;
-       memcpy(confllc->link_uid, lgr->id, SMC_LGR_ID_SIZE);
-       confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS; /* enforce peer resp. */
+       memcpy(confllc->link_uid, link->link_uid, SMC_LGR_ID_SIZE);
+       confllc->max_links = SMC_LLC_ADD_LNK_MAX_LINKS;
        /* send llc message */
        rc = smc_wr_tx_send(link, pend);
        return rc;
 }
 
 /* send LLC confirm rkey request */
-static int smc_llc_send_confirm_rkey(struct smc_link *link,
+static int smc_llc_send_confirm_rkey(struct smc_link *send_link,
                                     struct smc_buf_desc *rmb_desc)
 {
        struct smc_llc_msg_confirm_rkey *rkeyllc;
        struct smc_wr_tx_pend_priv *pend;
        struct smc_wr_buf *wr_buf;
-       int rc;
+       struct smc_link *link;
+       int i, rc, rtok_ix;
 
-       rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
+       rc = smc_llc_add_pending_send(send_link, &wr_buf, &pend);
        if (rc)
                return rc;
        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);
+
+       rtok_ix = 1;
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               link = &send_link->lgr->lnk[i];
+               if (link->state == SMC_LNK_ACTIVE && link != send_link) {
+                       rkeyllc->rtoken[rtok_ix].link_id = link->link_id;
+                       rkeyllc->rtoken[rtok_ix].rmb_key =
+                               htonl(rmb_desc->mr_rx[link->link_idx]->rkey);
+                       rkeyllc->rtoken[rtok_ix].rmb_vaddr = cpu_to_be64(
+                               (u64)sg_dma_address(
+                                       rmb_desc->sgt[link->link_idx].sgl));
+                       rtok_ix++;
+               }
+       }
+       /* rkey of send_link is in rtoken[0] */
+       rkeyllc->rtoken[0].num_rkeys = rtok_ix - 1;
        rkeyllc->rtoken[0].rmb_key =
-               htonl(rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey);
+               htonl(rmb_desc->mr_rx[send_link->link_idx]->rkey);
        rkeyllc->rtoken[0].rmb_vaddr = cpu_to_be64(
-               (u64)sg_dma_address(rmb_desc->sgt[SMC_SINGLE_LINK].sgl));
+               (u64)sg_dma_address(rmb_desc->sgt[send_link->link_idx].sgl));
        /* send llc message */
-       rc = smc_wr_tx_send(link, pend);
+       rc = smc_wr_tx_send(send_link, pend);
        return rc;
 }
 
@@ -256,32 +447,15 @@ static int smc_llc_send_delete_rkey(struct smc_link *link,
        rkeyllc->hd.common.type = SMC_LLC_DELETE_RKEY;
        rkeyllc->hd.length = sizeof(struct smc_llc_msg_delete_rkey);
        rkeyllc->num_rkeys = 1;
-       rkeyllc->rkey[0] = htonl(rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey);
+       rkeyllc->rkey[0] = htonl(rmb_desc->mr_rx[link->link_idx]->rkey);
        /* send llc message */
        rc = smc_wr_tx_send(link, pend);
        return rc;
 }
 
-/* prepare an add link message */
-static void smc_llc_prep_add_link(struct smc_llc_msg_add_link *addllc,
-                                 struct smc_link *link, u8 mac[], u8 gid[],
-                                 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);
-       if (reqresp == SMC_LLC_RESP) {
-               addllc->hd.flags |= SMC_LLC_FLAG_RESP;
-               /* always reject more links for now */
-               addllc->hd.flags |= SMC_LLC_FLAG_ADD_LNK_REJ;
-               addllc->hd.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH;
-       }
-       memcpy(addllc->sender_mac, mac, ETH_ALEN);
-       memcpy(addllc->sender_gid, gid, SMC_GID_SIZE);
-}
-
 /* send ADD LINK request or response */
 int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[],
+                         struct smc_link *link_new,
                          enum smc_llc_reqresp reqresp)
 {
        struct smc_llc_msg_add_link *addllc;
@@ -293,32 +467,33 @@ int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[],
        if (rc)
                return rc;
        addllc = (struct smc_llc_msg_add_link *)wr_buf;
-       smc_llc_prep_add_link(addllc, link, mac, gid, reqresp);
+
+       memset(addllc, 0, sizeof(*addllc));
+       addllc->hd.common.type = SMC_LLC_ADD_LINK;
+       addllc->hd.length = sizeof(struct smc_llc_msg_add_link);
+       if (reqresp == SMC_LLC_RESP)
+               addllc->hd.flags |= SMC_LLC_FLAG_RESP;
+       memcpy(addllc->sender_mac, mac, ETH_ALEN);
+       memcpy(addllc->sender_gid, gid, SMC_GID_SIZE);
+       if (link_new) {
+               addllc->link_num = link_new->link_id;
+               hton24(addllc->sender_qp_num, link_new->roce_qp->qp_num);
+               hton24(addllc->initial_psn, link_new->psn_initial);
+               if (reqresp == SMC_LLC_REQ)
+                       addllc->qp_mtu = link_new->path_mtu;
+               else
+                       addllc->qp_mtu = min(link_new->path_mtu,
+                                            link_new->peer_mtu);
+       }
        /* 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, bool orderly)
-{
-       memset(delllc, 0, sizeof(*delllc));
-       delllc->hd.common.type = SMC_LLC_DELETE_LINK;
-       delllc->hd.length = sizeof(struct smc_llc_msg_add_link);
-       if (reqresp == SMC_LLC_RESP)
-               delllc->hd.flags |= SMC_LLC_FLAG_RESP;
-       /* DEL_LINK_ALL because only 1 link supported */
-       delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
-       if (orderly)
-               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, bool orderly)
+int smc_llc_send_delete_link(struct smc_link *link, u8 link_del_id,
+                            enum smc_llc_reqresp reqresp, bool orderly,
+                            u32 reason)
 {
        struct smc_llc_msg_del_link *delllc;
        struct smc_wr_tx_pend_priv *pend;
@@ -329,7 +504,19 @@ int smc_llc_send_delete_link(struct smc_link *link,
        if (rc)
                return rc;
        delllc = (struct smc_llc_msg_del_link *)wr_buf;
-       smc_llc_prep_delete_link(delllc, link, reqresp, orderly);
+
+       memset(delllc, 0, sizeof(*delllc));
+       delllc->hd.common.type = SMC_LLC_DELETE_LINK;
+       delllc->hd.length = sizeof(struct smc_llc_msg_del_link);
+       if (reqresp == SMC_LLC_RESP)
+               delllc->hd.flags |= SMC_LLC_FLAG_RESP;
+       if (orderly)
+               delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
+       if (link_del_id)
+               delllc->link_num = link_del_id;
+       else
+               delllc->hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
+       delllc->reason = htonl(reason);
        /* send llc message */
        rc = smc_wr_tx_send(link, pend);
        return rc;
@@ -356,238 +543,1100 @@ static int smc_llc_send_test_link(struct smc_link *link, u8 user_data[16])
        return rc;
 }
 
-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)
+/* schedule an llc send on link, may wait for buffers */
+static int smc_llc_send_message(struct smc_link *link, void *llcbuf)
 {
-       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;
 
-       if (llcwrk->link->state == SMC_LNK_INACTIVE)
-               goto out;
-       rc = smc_llc_add_pending_send(llcwrk->link, &wr_buf, &pend);
+       if (!smc_link_usable(link))
+               return -ENOLINK;
+       rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
        if (rc)
-               goto out;
-       memcpy(wr_buf, &llcwrk->llcbuf, llcwrk->llclen);
-       smc_wr_tx_send(llcwrk->link, pend);
-out:
-       kfree(llcwrk);
+               return rc;
+       memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg));
+       return smc_wr_tx_send(link, pend);
 }
 
-/* copy llcbuf and schedule an llc send on link */
-static int smc_llc_send_message(struct smc_link *link, void *llcbuf, int llclen)
+/* schedule an llc send on link, may wait for buffers,
+ * and wait for send completion notification.
+ * @return 0 on success
+ */
+static int smc_llc_send_message_wait(struct smc_link *link, void *llcbuf)
 {
-       struct smc_llc_send_work *wrk = kmalloc(sizeof(*wrk), GFP_ATOMIC);
+       struct smc_wr_tx_pend_priv *pend;
+       struct smc_wr_buf *wr_buf;
+       int rc;
 
-       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;
+       if (!smc_link_usable(link))
+               return -ENOLINK;
+       rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
+       if (rc)
+               return rc;
+       memcpy(wr_buf, llcbuf, sizeof(union smc_llc_msg));
+       return smc_wr_tx_send_wait(link, pend, SMC_LLC_WAIT_TIME);
 }
 
 /********************************* receive ***********************************/
 
-static void smc_llc_rx_confirm_link(struct smc_link *link,
-                                   struct smc_llc_msg_confirm_link *llc)
+static int smc_llc_alloc_alt_link(struct smc_link_group *lgr,
+                                 enum smc_lgr_type lgr_new_t)
 {
-       struct smc_link_group *lgr = smc_get_lgr(link);
-       int conf_rc;
+       int i;
+
+       if (lgr->type == SMC_LGR_SYMMETRIC ||
+           (lgr->type != SMC_LGR_SINGLE &&
+            (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
+             lgr_new_t == SMC_LGR_ASYMMETRIC_PEER)))
+               return -EMLINK;
+
+       if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
+           lgr_new_t == SMC_LGR_ASYMMETRIC_PEER) {
+               for (i = SMC_LINKS_PER_LGR_MAX - 1; i >= 0; i--)
+                       if (lgr->lnk[i].state == SMC_LNK_UNUSED)
+                               return i;
+       } else {
+               for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++)
+                       if (lgr->lnk[i].state == SMC_LNK_UNUSED)
+                               return i;
+       }
+       return -EMLINK;
+}
 
-       /* RMBE eyecatchers are not supported */
-       if (llc->hd.flags & SMC_LLC_FLAG_NO_RMBE_EYEC)
-               conf_rc = 0;
-       else
-               conf_rc = ENOTSUPP;
+/* return first buffer from any of the next buf lists */
+static struct smc_buf_desc *_smc_llc_get_next_rmb(struct smc_link_group *lgr,
+                                                 int *buf_lst)
+{
+       struct smc_buf_desc *buf_pos;
+
+       while (*buf_lst < SMC_RMBE_SIZES) {
+               buf_pos = list_first_entry_or_null(&lgr->rmbs[*buf_lst],
+                                                  struct smc_buf_desc, list);
+               if (buf_pos)
+                       return buf_pos;
+               (*buf_lst)++;
+       }
+       return NULL;
+}
+
+/* return next rmb from buffer lists */
+static struct smc_buf_desc *smc_llc_get_next_rmb(struct smc_link_group *lgr,
+                                                int *buf_lst,
+                                                struct smc_buf_desc *buf_pos)
+{
+       struct smc_buf_desc *buf_next;
+
+       if (!buf_pos || list_is_last(&buf_pos->list, &lgr->rmbs[*buf_lst])) {
+               (*buf_lst)++;
+               return _smc_llc_get_next_rmb(lgr, buf_lst);
+       }
+       buf_next = list_next_entry(buf_pos, list);
+       return buf_next;
+}
+
+static struct smc_buf_desc *smc_llc_get_first_rmb(struct smc_link_group *lgr,
+                                                 int *buf_lst)
+{
+       *buf_lst = 0;
+       return smc_llc_get_next_rmb(lgr, buf_lst, NULL);
+}
+
+/* send one add_link_continue msg */
+static int smc_llc_add_link_cont(struct smc_link *link,
+                                struct smc_link *link_new, u8 *num_rkeys_todo,
+                                int *buf_lst, struct smc_buf_desc **buf_pos)
+{
+       struct smc_llc_msg_add_link_cont *addc_llc;
+       struct smc_link_group *lgr = link->lgr;
+       int prim_lnk_idx, lnk_idx, i, rc;
+       struct smc_wr_tx_pend_priv *pend;
+       struct smc_wr_buf *wr_buf;
+       struct smc_buf_desc *rmb;
+       u8 n;
 
-       if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
-               if (lgr->role == SMC_SERV &&
-                   link->state == SMC_LNK_ACTIVATING) {
-                       link->llc_confirm_resp_rc = conf_rc;
-                       complete(&link->llc_confirm_resp);
+       rc = smc_llc_add_pending_send(link, &wr_buf, &pend);
+       if (rc)
+               return rc;
+       addc_llc = (struct smc_llc_msg_add_link_cont *)wr_buf;
+       memset(addc_llc, 0, sizeof(*addc_llc));
+
+       prim_lnk_idx = link->link_idx;
+       lnk_idx = link_new->link_idx;
+       addc_llc->link_num = link_new->link_id;
+       addc_llc->num_rkeys = *num_rkeys_todo;
+       n = *num_rkeys_todo;
+       for (i = 0; i < min_t(u8, n, SMC_LLC_RKEYS_PER_CONT_MSG); i++) {
+               if (!*buf_pos) {
+                       addc_llc->num_rkeys = addc_llc->num_rkeys -
+                                             *num_rkeys_todo;
+                       *num_rkeys_todo = 0;
+                       break;
                }
-       } else {
-               if (lgr->role == SMC_CLNT &&
-                   link->state == SMC_LNK_ACTIVATING) {
-                       link->llc_confirm_rc = conf_rc;
-                       link->link_id = llc->link_num;
-                       complete(&link->llc_confirm);
+               rmb = *buf_pos;
+
+               addc_llc->rt[i].rmb_key = htonl(rmb->mr_rx[prim_lnk_idx]->rkey);
+               addc_llc->rt[i].rmb_key_new = htonl(rmb->mr_rx[lnk_idx]->rkey);
+               addc_llc->rt[i].rmb_vaddr_new =
+                       cpu_to_be64((u64)sg_dma_address(rmb->sgt[lnk_idx].sgl));
+
+               (*num_rkeys_todo)--;
+               *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos);
+               while (*buf_pos && !(*buf_pos)->used)
+                       *buf_pos = smc_llc_get_next_rmb(lgr, buf_lst, *buf_pos);
+       }
+       addc_llc->hd.common.type = SMC_LLC_ADD_LINK_CONT;
+       addc_llc->hd.length = sizeof(struct smc_llc_msg_add_link_cont);
+       if (lgr->role == SMC_CLNT)
+               addc_llc->hd.flags |= SMC_LLC_FLAG_RESP;
+       return smc_wr_tx_send(link, pend);
+}
+
+static int smc_llc_cli_rkey_exchange(struct smc_link *link,
+                                    struct smc_link *link_new)
+{
+       struct smc_llc_msg_add_link_cont *addc_llc;
+       struct smc_link_group *lgr = link->lgr;
+       u8 max, num_rkeys_send, num_rkeys_recv;
+       struct smc_llc_qentry *qentry;
+       struct smc_buf_desc *buf_pos;
+       int buf_lst;
+       int rc = 0;
+       int i;
+
+       mutex_lock(&lgr->rmbs_lock);
+       num_rkeys_send = lgr->conns_num;
+       buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst);
+       do {
+               qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_TIME,
+                                     SMC_LLC_ADD_LINK_CONT);
+               if (!qentry) {
+                       rc = -ETIMEDOUT;
+                       break;
+               }
+               addc_llc = &qentry->msg.add_link_cont;
+               num_rkeys_recv = addc_llc->num_rkeys;
+               max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG);
+               for (i = 0; i < max; i++) {
+                       smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
+                                      addc_llc->rt[i].rmb_key,
+                                      addc_llc->rt[i].rmb_vaddr_new,
+                                      addc_llc->rt[i].rmb_key_new);
+                       num_rkeys_recv--;
                }
+               smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+               rc = smc_llc_add_link_cont(link, link_new, &num_rkeys_send,
+                                          &buf_lst, &buf_pos);
+               if (rc)
+                       break;
+       } while (num_rkeys_send || num_rkeys_recv);
+
+       mutex_unlock(&lgr->rmbs_lock);
+       return rc;
+}
+
+/* prepare and send an add link reject response */
+static int smc_llc_cli_add_link_reject(struct smc_llc_qentry *qentry)
+{
+       qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP;
+       qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_ADD_LNK_REJ;
+       qentry->msg.raw.hdr.add_link_rej_rsn = SMC_LLC_REJ_RSN_NO_ALT_PATH;
+       return smc_llc_send_message(qentry->link, &qentry->msg);
+}
+
+static int smc_llc_cli_conf_link(struct smc_link *link,
+                                struct smc_init_info *ini,
+                                struct smc_link *link_new,
+                                enum smc_lgr_type lgr_new_t)
+{
+       struct smc_link_group *lgr = link->lgr;
+       struct smc_llc_msg_del_link *del_llc;
+       struct smc_llc_qentry *qentry = NULL;
+       int rc = 0;
+
+       /* receive CONFIRM LINK request over RoCE fabric */
+       qentry = smc_llc_wait(lgr, NULL, SMC_LLC_WAIT_FIRST_TIME, 0);
+       if (!qentry) {
+               rc = smc_llc_send_delete_link(link, link_new->link_id,
+                                             SMC_LLC_REQ, false,
+                                             SMC_LLC_DEL_LOST_PATH);
+               return -ENOLINK;
+       }
+       if (qentry->msg.raw.hdr.common.type != SMC_LLC_CONFIRM_LINK) {
+               /* received DELETE_LINK instead */
+               del_llc = &qentry->msg.delete_link;
+               qentry->msg.raw.hdr.flags |= SMC_LLC_FLAG_RESP;
+               smc_llc_send_message(link, &qentry->msg);
+               smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+               return -ENOLINK;
+       }
+       smc_llc_save_peer_uid(qentry);
+       smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+
+       rc = smc_ib_modify_qp_rts(link_new);
+       if (rc) {
+               smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
+                                        false, SMC_LLC_DEL_LOST_PATH);
+               return -ENOLINK;
        }
+       smc_wr_remember_qp_attr(link_new);
+
+       rc = smcr_buf_reg_lgr(link_new);
+       if (rc) {
+               smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
+                                        false, SMC_LLC_DEL_LOST_PATH);
+               return -ENOLINK;
+       }
+
+       /* send CONFIRM LINK response over RoCE fabric */
+       rc = smc_llc_send_confirm_link(link_new, SMC_LLC_RESP);
+       if (rc) {
+               smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
+                                        false, SMC_LLC_DEL_LOST_PATH);
+               return -ENOLINK;
+       }
+       smc_llc_link_active(link_new);
+       if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
+           lgr_new_t == SMC_LGR_ASYMMETRIC_PEER)
+               smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx);
+       else
+               smcr_lgr_set_type(lgr, lgr_new_t);
+       return 0;
 }
 
-static void smc_llc_rx_add_link(struct smc_link *link,
-                               struct smc_llc_msg_add_link *llc)
+static void smc_llc_save_add_link_info(struct smc_link *link,
+                                      struct smc_llc_msg_add_link *add_llc)
 {
+       link->peer_qpn = ntoh24(add_llc->sender_qp_num);
+       memcpy(link->peer_gid, add_llc->sender_gid, SMC_GID_SIZE);
+       memcpy(link->peer_mac, add_llc->sender_mac, ETH_ALEN);
+       link->peer_psn = ntoh24(add_llc->initial_psn);
+       link->peer_mtu = add_llc->qp_mtu;
+}
+
+/* as an SMC client, process an add link request */
+int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry)
+{
+       struct smc_llc_msg_add_link *llc = &qentry->msg.add_link;
+       enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC;
        struct smc_link_group *lgr = smc_get_lgr(link);
+       struct smc_link *lnk_new = NULL;
+       struct smc_init_info ini;
+       int lnk_idx, rc = 0;
+
+       ini.vlan_id = lgr->vlan_id;
+       smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev);
+       if (!memcmp(llc->sender_gid, link->peer_gid, SMC_GID_SIZE) &&
+           !memcmp(llc->sender_mac, link->peer_mac, ETH_ALEN)) {
+               if (!ini.ib_dev)
+                       goto out_reject;
+               lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
+       }
+       if (!ini.ib_dev) {
+               lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL;
+               ini.ib_dev = link->smcibdev;
+               ini.ib_port = link->ibport;
+       }
+       lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t);
+       if (lnk_idx < 0)
+               goto out_reject;
+       lnk_new = &lgr->lnk[lnk_idx];
+       rc = smcr_link_init(lgr, lnk_new, lnk_idx, &ini);
+       if (rc)
+               goto out_reject;
+       smc_llc_save_add_link_info(lnk_new, llc);
+       lnk_new->link_id = llc->link_num;       /* SMC server assigns link id */
+       smc_llc_link_set_uid(lnk_new);
 
-       if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
-               if (link->state == SMC_LNK_ACTIVATING)
-                       complete(&link->llc_add_resp);
-       } else {
-               if (link->state == SMC_LNK_ACTIVATING) {
-                       complete(&link->llc_add);
-                       return;
-               }
+       rc = smc_ib_ready_link(lnk_new);
+       if (rc)
+               goto out_clear_lnk;
 
-               if (lgr->role == SMC_SERV) {
-                       smc_llc_prep_add_link(llc, link,
-                                       link->smcibdev->mac[link->ibport - 1],
-                                       link->gid, SMC_LLC_REQ);
+       rc = smcr_buf_map_lgr(lnk_new);
+       if (rc)
+               goto out_clear_lnk;
 
-               } else {
-                       smc_llc_prep_add_link(llc, link,
-                                       link->smcibdev->mac[link->ibport - 1],
-                                       link->gid, SMC_LLC_RESP);
+       rc = smc_llc_send_add_link(link,
+                                  lnk_new->smcibdev->mac[ini.ib_port - 1],
+                                  lnk_new->gid, lnk_new, SMC_LLC_RESP);
+       if (rc)
+               goto out_clear_lnk;
+       rc = smc_llc_cli_rkey_exchange(link, lnk_new);
+       if (rc) {
+               rc = 0;
+               goto out_clear_lnk;
+       }
+       rc = smc_llc_cli_conf_link(link, &ini, lnk_new, lgr_new_t);
+       if (!rc)
+               goto out;
+out_clear_lnk:
+       smcr_link_clear(lnk_new, false);
+out_reject:
+       smc_llc_cli_add_link_reject(qentry);
+out:
+       kfree(qentry);
+       return rc;
+}
+
+static void smc_llc_process_cli_add_link(struct smc_link_group *lgr)
+{
+       struct smc_llc_qentry *qentry;
+
+       qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
+
+       mutex_lock(&lgr->llc_conf_mutex);
+       smc_llc_cli_add_link(qentry->link, qentry);
+       mutex_unlock(&lgr->llc_conf_mutex);
+}
+
+static int smc_llc_active_link_count(struct smc_link_group *lgr)
+{
+       int i, link_count = 0;
+
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               if (!smc_link_usable(&lgr->lnk[i]))
+                       continue;
+               link_count++;
+       }
+       return link_count;
+}
+
+/* find the asymmetric link when 3 links are established  */
+static struct smc_link *smc_llc_find_asym_link(struct smc_link_group *lgr)
+{
+       int asym_idx = -ENOENT;
+       int i, j, k;
+       bool found;
+
+       /* determine asymmetric link */
+       found = false;
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               for (j = i + 1; j < SMC_LINKS_PER_LGR_MAX; j++) {
+                       if (!smc_link_usable(&lgr->lnk[i]) ||
+                           !smc_link_usable(&lgr->lnk[j]))
+                               continue;
+                       if (!memcmp(lgr->lnk[i].gid, lgr->lnk[j].gid,
+                                   SMC_GID_SIZE)) {
+                               found = true;   /* asym_lnk is i or j */
+                               break;
+                       }
                }
-               smc_llc_send_message(link, llc, sizeof(*llc));
+               if (found)
+                       break;
        }
+       if (!found)
+               goto out; /* no asymmetric link */
+       for (k = 0; k < SMC_LINKS_PER_LGR_MAX; k++) {
+               if (!smc_link_usable(&lgr->lnk[k]))
+                       continue;
+               if (k != i &&
+                   !memcmp(lgr->lnk[i].peer_gid, lgr->lnk[k].peer_gid,
+                           SMC_GID_SIZE)) {
+                       asym_idx = i;
+                       break;
+               }
+               if (k != j &&
+                   !memcmp(lgr->lnk[j].peer_gid, lgr->lnk[k].peer_gid,
+                           SMC_GID_SIZE)) {
+                       asym_idx = j;
+                       break;
+               }
+       }
+out:
+       return (asym_idx < 0) ? NULL : &lgr->lnk[asym_idx];
 }
 
-static void smc_llc_rx_delete_link(struct smc_link *link,
-                                  struct smc_llc_msg_del_link *llc)
+static void smc_llc_delete_asym_link(struct smc_link_group *lgr)
 {
-       struct smc_link_group *lgr = smc_get_lgr(link);
+       struct smc_link *lnk_new = NULL, *lnk_asym;
+       struct smc_llc_qentry *qentry;
+       int rc;
 
-       if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
-               if (lgr->role == SMC_SERV)
-                       smc_lgr_schedule_free_work_fast(lgr);
-       } else {
-               smc_lgr_forget(lgr);
-               smc_llc_link_deleting(link);
-               if (lgr->role == SMC_SERV) {
-                       /* client asks to delete this link, send request */
-                       smc_llc_prep_delete_link(llc, link, SMC_LLC_REQ, true);
-               } else {
-                       /* server requests to delete this link, send response */
-                       smc_llc_prep_delete_link(llc, link, SMC_LLC_RESP, true);
+       lnk_asym = smc_llc_find_asym_link(lgr);
+       if (!lnk_asym)
+               return; /* no asymmetric link */
+       if (!smc_link_downing(&lnk_asym->state))
+               return;
+       lnk_new = smc_switch_conns(lgr, lnk_asym, false);
+       smc_wr_tx_wait_no_pending_sends(lnk_asym);
+       if (!lnk_new)
+               goto out_free;
+       /* change flow type from ADD_LINK into DEL_LINK */
+       lgr->llc_flow_lcl.type = SMC_LLC_FLOW_DEL_LINK;
+       rc = smc_llc_send_delete_link(lnk_new, lnk_asym->link_id, SMC_LLC_REQ,
+                                     true, SMC_LLC_DEL_NO_ASYM_NEEDED);
+       if (rc) {
+               smcr_link_down_cond(lnk_new);
+               goto out_free;
+       }
+       qentry = smc_llc_wait(lgr, lnk_new, SMC_LLC_WAIT_TIME,
+                             SMC_LLC_DELETE_LINK);
+       if (!qentry) {
+               smcr_link_down_cond(lnk_new);
+               goto out_free;
+       }
+       smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+out_free:
+       smcr_link_clear(lnk_asym, true);
+}
+
+static int smc_llc_srv_rkey_exchange(struct smc_link *link,
+                                    struct smc_link *link_new)
+{
+       struct smc_llc_msg_add_link_cont *addc_llc;
+       struct smc_link_group *lgr = link->lgr;
+       u8 max, num_rkeys_send, num_rkeys_recv;
+       struct smc_llc_qentry *qentry = NULL;
+       struct smc_buf_desc *buf_pos;
+       int buf_lst;
+       int rc = 0;
+       int i;
+
+       mutex_lock(&lgr->rmbs_lock);
+       num_rkeys_send = lgr->conns_num;
+       buf_pos = smc_llc_get_first_rmb(lgr, &buf_lst);
+       do {
+               smc_llc_add_link_cont(link, link_new, &num_rkeys_send,
+                                     &buf_lst, &buf_pos);
+               qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME,
+                                     SMC_LLC_ADD_LINK_CONT);
+               if (!qentry) {
+                       rc = -ETIMEDOUT;
+                       goto out;
                }
-               smc_llc_send_message(link, llc, sizeof(*llc));
-               smc_lgr_terminate_sched(lgr);
+               addc_llc = &qentry->msg.add_link_cont;
+               num_rkeys_recv = addc_llc->num_rkeys;
+               max = min_t(u8, num_rkeys_recv, SMC_LLC_RKEYS_PER_CONT_MSG);
+               for (i = 0; i < max; i++) {
+                       smc_rtoken_set(lgr, link->link_idx, link_new->link_idx,
+                                      addc_llc->rt[i].rmb_key,
+                                      addc_llc->rt[i].rmb_vaddr_new,
+                                      addc_llc->rt[i].rmb_key_new);
+                       num_rkeys_recv--;
+               }
+               smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+       } while (num_rkeys_send || num_rkeys_recv);
+out:
+       mutex_unlock(&lgr->rmbs_lock);
+       return rc;
+}
+
+static int smc_llc_srv_conf_link(struct smc_link *link,
+                                struct smc_link *link_new,
+                                enum smc_lgr_type lgr_new_t)
+{
+       struct smc_link_group *lgr = link->lgr;
+       struct smc_llc_qentry *qentry = NULL;
+       int rc;
+
+       /* send CONFIRM LINK request over the RoCE fabric */
+       rc = smc_llc_send_confirm_link(link_new, SMC_LLC_REQ);
+       if (rc)
+               return -ENOLINK;
+       /* receive CONFIRM LINK response over the RoCE fabric */
+       qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_FIRST_TIME,
+                             SMC_LLC_CONFIRM_LINK);
+       if (!qentry) {
+               /* send DELETE LINK */
+               smc_llc_send_delete_link(link, link_new->link_id, SMC_LLC_REQ,
+                                        false, SMC_LLC_DEL_LOST_PATH);
+               return -ENOLINK;
        }
+       smc_llc_save_peer_uid(qentry);
+       smc_llc_link_active(link_new);
+       if (lgr_new_t == SMC_LGR_ASYMMETRIC_LOCAL ||
+           lgr_new_t == SMC_LGR_ASYMMETRIC_PEER)
+               smcr_lgr_set_type_asym(lgr, lgr_new_t, link_new->link_idx);
+       else
+               smcr_lgr_set_type(lgr, lgr_new_t);
+       smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+       return 0;
 }
 
-static void smc_llc_rx_test_link(struct smc_link *link,
-                                struct smc_llc_msg_test_link *llc)
+int smc_llc_srv_add_link(struct smc_link *link)
 {
-       if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
-               if (link->state == SMC_LNK_ACTIVE)
-                       complete(&link->llc_testlink_resp);
-       } else {
-               llc->hd.flags |= SMC_LLC_FLAG_RESP;
-               smc_llc_send_message(link, llc, sizeof(*llc));
+       enum smc_lgr_type lgr_new_t = SMC_LGR_SYMMETRIC;
+       struct smc_link_group *lgr = link->lgr;
+       struct smc_llc_msg_add_link *add_llc;
+       struct smc_llc_qentry *qentry = NULL;
+       struct smc_link *link_new;
+       struct smc_init_info ini;
+       int lnk_idx, rc = 0;
+
+       /* ignore client add link recommendation, start new flow */
+       ini.vlan_id = lgr->vlan_id;
+       smc_pnet_find_alt_roce(lgr, &ini, link->smcibdev);
+       if (!ini.ib_dev) {
+               lgr_new_t = SMC_LGR_ASYMMETRIC_LOCAL;
+               ini.ib_dev = link->smcibdev;
+               ini.ib_port = link->ibport;
+       }
+       lnk_idx = smc_llc_alloc_alt_link(lgr, lgr_new_t);
+       if (lnk_idx < 0)
+               return 0;
+
+       rc = smcr_link_init(lgr, &lgr->lnk[lnk_idx], lnk_idx, &ini);
+       if (rc)
+               return rc;
+       link_new = &lgr->lnk[lnk_idx];
+       rc = smc_llc_send_add_link(link,
+                                  link_new->smcibdev->mac[ini.ib_port - 1],
+                                  link_new->gid, link_new, SMC_LLC_REQ);
+       if (rc)
+               goto out_err;
+       /* receive ADD LINK response over the RoCE fabric */
+       qentry = smc_llc_wait(lgr, link, SMC_LLC_WAIT_TIME, SMC_LLC_ADD_LINK);
+       if (!qentry) {
+               rc = -ETIMEDOUT;
+               goto out_err;
        }
+       add_llc = &qentry->msg.add_link;
+       if (add_llc->hd.flags & SMC_LLC_FLAG_ADD_LNK_REJ) {
+               smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+               rc = -ENOLINK;
+               goto out_err;
+       }
+       if (lgr->type == SMC_LGR_SINGLE &&
+           (!memcmp(add_llc->sender_gid, link->peer_gid, SMC_GID_SIZE) &&
+            !memcmp(add_llc->sender_mac, link->peer_mac, ETH_ALEN))) {
+               lgr_new_t = SMC_LGR_ASYMMETRIC_PEER;
+       }
+       smc_llc_save_add_link_info(link_new, add_llc);
+       smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+
+       rc = smc_ib_ready_link(link_new);
+       if (rc)
+               goto out_err;
+       rc = smcr_buf_map_lgr(link_new);
+       if (rc)
+               goto out_err;
+       rc = smcr_buf_reg_lgr(link_new);
+       if (rc)
+               goto out_err;
+       rc = smc_llc_srv_rkey_exchange(link, link_new);
+       if (rc)
+               goto out_err;
+       rc = smc_llc_srv_conf_link(link, link_new, lgr_new_t);
+       if (rc)
+               goto out_err;
+       return 0;
+out_err:
+       smcr_link_clear(link_new, false);
+       return rc;
 }
 
-static void smc_llc_rx_confirm_rkey(struct smc_link *link,
-                                   struct smc_llc_msg_confirm_rkey *llc)
+static void smc_llc_process_srv_add_link(struct smc_link_group *lgr)
 {
+       struct smc_link *link = lgr->llc_flow_lcl.qentry->link;
        int rc;
 
-       if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
-               link->llc_confirm_rkey_rc = llc->hd.flags &
-                                           SMC_LLC_FLAG_RKEY_NEG;
-               complete(&link->llc_confirm_rkey);
-       } else {
-               rc = smc_rtoken_add(smc_get_lgr(link),
-                                   llc->rtoken[0].rmb_vaddr,
-                                   llc->rtoken[0].rmb_key);
+       smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
 
-               /* ignore rtokens for other links, we have only one link */
+       mutex_lock(&lgr->llc_conf_mutex);
+       rc = smc_llc_srv_add_link(link);
+       if (!rc && lgr->type == SMC_LGR_SYMMETRIC) {
+               /* delete any asymmetric link */
+               smc_llc_delete_asym_link(lgr);
+       }
+       mutex_unlock(&lgr->llc_conf_mutex);
+}
+
+/* enqueue a local add_link req to trigger a new add_link flow, only as SERV */
+void smc_llc_srv_add_link_local(struct smc_link *link)
+{
+       struct smc_llc_msg_add_link add_llc = {0};
+
+       add_llc.hd.length = sizeof(add_llc);
+       add_llc.hd.common.type = SMC_LLC_ADD_LINK;
+       /* no dev and port needed, we as server ignore client data anyway */
+       smc_llc_enqueue(link, (union smc_llc_msg *)&add_llc);
+}
+
+/* worker to process an add link message */
+static void smc_llc_add_link_work(struct work_struct *work)
+{
+       struct smc_link_group *lgr = container_of(work, struct smc_link_group,
+                                                 llc_add_link_work);
 
-               llc->hd.flags |= SMC_LLC_FLAG_RESP;
-               if (rc < 0)
-                       llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
-               smc_llc_send_message(link, llc, sizeof(*llc));
+       if (list_empty(&lgr->list)) {
+               /* link group is terminating */
+               smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+               goto out;
        }
+
+       if (lgr->role == SMC_CLNT)
+               smc_llc_process_cli_add_link(lgr);
+       else
+               smc_llc_process_srv_add_link(lgr);
+out:
+       smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
 }
 
-static void smc_llc_rx_confirm_rkey_cont(struct smc_link *link,
-                                     struct smc_llc_msg_confirm_rkey_cont *llc)
+/* enqueue a local del_link msg to trigger a new del_link flow,
+ * called only for role SMC_SERV
+ */
+void smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id)
 {
-       if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
-               /* unused as long as we don't send this type of msg */
-       } else {
-               /* ignore rtokens for other links, we have only one link */
-               llc->hd.flags |= SMC_LLC_FLAG_RESP;
-               smc_llc_send_message(link, llc, sizeof(*llc));
+       struct smc_llc_msg_del_link del_llc = {0};
+
+       del_llc.hd.length = sizeof(del_llc);
+       del_llc.hd.common.type = SMC_LLC_DELETE_LINK;
+       del_llc.link_num = del_link_id;
+       del_llc.reason = htonl(SMC_LLC_DEL_LOST_PATH);
+       del_llc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
+       smc_llc_enqueue(link, (union smc_llc_msg *)&del_llc);
+}
+
+static void smc_llc_process_cli_delete_link(struct smc_link_group *lgr)
+{
+       struct smc_link *lnk_del = NULL, *lnk_asym, *lnk;
+       struct smc_llc_msg_del_link *del_llc;
+       struct smc_llc_qentry *qentry;
+       int active_links;
+       int lnk_idx;
+
+       qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
+       lnk = qentry->link;
+       del_llc = &qentry->msg.delete_link;
+
+       if (del_llc->hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) {
+               smc_lgr_terminate_sched(lgr);
+               goto out;
+       }
+       mutex_lock(&lgr->llc_conf_mutex);
+       /* delete single link */
+       for (lnk_idx = 0; lnk_idx < SMC_LINKS_PER_LGR_MAX; lnk_idx++) {
+               if (lgr->lnk[lnk_idx].link_id != del_llc->link_num)
+                       continue;
+               lnk_del = &lgr->lnk[lnk_idx];
+               break;
+       }
+       del_llc->hd.flags |= SMC_LLC_FLAG_RESP;
+       if (!lnk_del) {
+               /* link was not found */
+               del_llc->reason = htonl(SMC_LLC_DEL_NOLNK);
+               smc_llc_send_message(lnk, &qentry->msg);
+               goto out_unlock;
+       }
+       lnk_asym = smc_llc_find_asym_link(lgr);
+
+       del_llc->reason = 0;
+       smc_llc_send_message(lnk, &qentry->msg); /* response */
+
+       if (smc_link_downing(&lnk_del->state)) {
+               smc_switch_conns(lgr, lnk_del, false);
+               smc_wr_tx_wait_no_pending_sends(lnk_del);
+       }
+       smcr_link_clear(lnk_del, true);
+
+       active_links = smc_llc_active_link_count(lgr);
+       if (lnk_del == lnk_asym) {
+               /* expected deletion of asym link, don't change lgr state */
+       } else if (active_links == 1) {
+               smcr_lgr_set_type(lgr, SMC_LGR_SINGLE);
+       } else if (!active_links) {
+               smcr_lgr_set_type(lgr, SMC_LGR_NONE);
+               smc_lgr_terminate_sched(lgr);
        }
+out_unlock:
+       mutex_unlock(&lgr->llc_conf_mutex);
+out:
+       kfree(qentry);
 }
 
-static void smc_llc_rx_delete_rkey(struct smc_link *link,
-                                  struct smc_llc_msg_delete_rkey *llc)
+/* try to send a DELETE LINK ALL request on any active link,
+ * waiting for send completion
+ */
+void smc_llc_send_link_delete_all(struct smc_link_group *lgr, bool ord, u32 rsn)
 {
-       u8 err_mask = 0;
-       int i, max;
+       struct smc_llc_msg_del_link delllc = {0};
+       int i;
+
+       delllc.hd.common.type = SMC_LLC_DELETE_LINK;
+       delllc.hd.length = sizeof(delllc);
+       if (ord)
+               delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ORDERLY;
+       delllc.hd.flags |= SMC_LLC_FLAG_DEL_LINK_ALL;
+       delllc.reason = htonl(rsn);
+
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               if (!smc_link_usable(&lgr->lnk[i]))
+                       continue;
+               if (!smc_llc_send_message_wait(&lgr->lnk[i], &delllc))
+                       break;
+       }
+}
 
-       if (llc->hd.flags & SMC_LLC_FLAG_RESP) {
-               link->llc_delete_rkey_rc = llc->hd.flags &
-                                           SMC_LLC_FLAG_RKEY_NEG;
-               complete(&link->llc_delete_rkey);
-       } else {
-               max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX);
-               for (i = 0; i < max; i++) {
-                       if (smc_rtoken_delete(smc_get_lgr(link), llc->rkey[i]))
-                               err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i);
+static void smc_llc_process_srv_delete_link(struct smc_link_group *lgr)
+{
+       struct smc_llc_msg_del_link *del_llc;
+       struct smc_link *lnk, *lnk_del;
+       struct smc_llc_qentry *qentry;
+       int active_links;
+       int i;
+
+       mutex_lock(&lgr->llc_conf_mutex);
+       qentry = smc_llc_flow_qentry_clr(&lgr->llc_flow_lcl);
+       lnk = qentry->link;
+       del_llc = &qentry->msg.delete_link;
+
+       if (qentry->msg.delete_link.hd.flags & SMC_LLC_FLAG_DEL_LINK_ALL) {
+               /* delete entire lgr */
+               smc_llc_send_link_delete_all(lgr, true, ntohl(
+                                             qentry->msg.delete_link.reason));
+               smc_lgr_terminate_sched(lgr);
+               goto out;
+       }
+       /* delete single link */
+       lnk_del = NULL;
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++) {
+               if (lgr->lnk[i].link_id == del_llc->link_num) {
+                       lnk_del = &lgr->lnk[i];
+                       break;
                }
+       }
+       if (!lnk_del)
+               goto out; /* asymmetric link already deleted */
 
-               if (err_mask) {
-                       llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
-                       llc->err_mask = err_mask;
+       if (smc_link_downing(&lnk_del->state)) {
+               smc_switch_conns(lgr, lnk_del, false);
+               smc_wr_tx_wait_no_pending_sends(lnk_del);
+       }
+       if (!list_empty(&lgr->list)) {
+               /* qentry is either a request from peer (send it back to
+                * initiate the DELETE_LINK processing), or a locally
+                * enqueued DELETE_LINK request (forward it)
+                */
+               if (!smc_llc_send_message(lnk, &qentry->msg)) {
+                       struct smc_llc_msg_del_link *del_llc_resp;
+                       struct smc_llc_qentry *qentry2;
+
+                       qentry2 = smc_llc_wait(lgr, lnk, SMC_LLC_WAIT_TIME,
+                                              SMC_LLC_DELETE_LINK);
+                       if (!qentry2) {
+                       } else {
+                               del_llc_resp = &qentry2->msg.delete_link;
+                               smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+                       }
                }
+       }
+       smcr_link_clear(lnk_del, true);
 
-               llc->hd.flags |= SMC_LLC_FLAG_RESP;
-               smc_llc_send_message(link, llc, sizeof(*llc));
+       active_links = smc_llc_active_link_count(lgr);
+       if (active_links == 1) {
+               smcr_lgr_set_type(lgr, SMC_LGR_SINGLE);
+       } else if (!active_links) {
+               smcr_lgr_set_type(lgr, SMC_LGR_NONE);
+               smc_lgr_terminate_sched(lgr);
        }
+
+       if (lgr->type == SMC_LGR_SINGLE && !list_empty(&lgr->list)) {
+               /* trigger setup of asymm alt link */
+               smc_llc_srv_add_link_local(lnk);
+       }
+out:
+       mutex_unlock(&lgr->llc_conf_mutex);
+       kfree(qentry);
 }
 
-static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
+static void smc_llc_delete_link_work(struct work_struct *work)
 {
-       struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
-       union smc_llc_msg *llc = buf;
+       struct smc_link_group *lgr = container_of(work, struct smc_link_group,
+                                                 llc_del_link_work);
 
-       if (wc->byte_len < sizeof(*llc))
-               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 */
+       if (list_empty(&lgr->list)) {
+               /* link group is terminating */
+               smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+               goto out;
+       }
+
+       if (lgr->role == SMC_CLNT)
+               smc_llc_process_cli_delete_link(lgr);
+       else
+               smc_llc_process_srv_delete_link(lgr);
+out:
+       smc_llc_flow_stop(lgr, &lgr->llc_flow_lcl);
+}
+
+/* process a confirm_rkey request from peer, remote flow */
+static void smc_llc_rmt_conf_rkey(struct smc_link_group *lgr)
+{
+       struct smc_llc_msg_confirm_rkey *llc;
+       struct smc_llc_qentry *qentry;
+       struct smc_link *link;
+       int num_entries;
+       int rk_idx;
+       int i;
+
+       qentry = lgr->llc_flow_rmt.qentry;
+       llc = &qentry->msg.confirm_rkey;
+       link = qentry->link;
+
+       num_entries = llc->rtoken[0].num_rkeys;
+       /* first rkey entry is for receiving link */
+       rk_idx = smc_rtoken_add(link,
+                               llc->rtoken[0].rmb_vaddr,
+                               llc->rtoken[0].rmb_key);
+       if (rk_idx < 0)
+               goto out_err;
+
+       for (i = 1; i <= min_t(u8, num_entries, SMC_LLC_RKEYS_PER_MSG - 1); i++)
+               smc_rtoken_set2(lgr, rk_idx, llc->rtoken[i].link_id,
+                               llc->rtoken[i].rmb_vaddr,
+                               llc->rtoken[i].rmb_key);
+       /* max links is 3 so there is no need to support conf_rkey_cont msgs */
+       goto out;
+out_err:
+       llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
+       llc->hd.flags |= SMC_LLC_FLAG_RKEY_RETRY;
+out:
+       llc->hd.flags |= SMC_LLC_FLAG_RESP;
+       smc_llc_send_message(link, &qentry->msg);
+       smc_llc_flow_qentry_del(&lgr->llc_flow_rmt);
+}
+
+/* process a delete_rkey request from peer, remote flow */
+static void smc_llc_rmt_delete_rkey(struct smc_link_group *lgr)
+{
+       struct smc_llc_msg_delete_rkey *llc;
+       struct smc_llc_qentry *qentry;
+       struct smc_link *link;
+       u8 err_mask = 0;
+       int i, max;
+
+       qentry = lgr->llc_flow_rmt.qentry;
+       llc = &qentry->msg.delete_rkey;
+       link = qentry->link;
+
+       max = min_t(u8, llc->num_rkeys, SMC_LLC_DEL_RKEY_MAX);
+       for (i = 0; i < max; i++) {
+               if (smc_rtoken_delete(link, llc->rkey[i]))
+                       err_mask |= 1 << (SMC_LLC_DEL_RKEY_MAX - 1 - i);
+       }
+       if (err_mask) {
+               llc->hd.flags |= SMC_LLC_FLAG_RKEY_NEG;
+               llc->err_mask = err_mask;
+       }
+       llc->hd.flags |= SMC_LLC_FLAG_RESP;
+       smc_llc_send_message(link, &qentry->msg);
+       smc_llc_flow_qentry_del(&lgr->llc_flow_rmt);
+}
+
+static void smc_llc_protocol_violation(struct smc_link_group *lgr, u8 type)
+{
+       pr_warn_ratelimited("smc: SMC-R lg %*phN LLC protocol violation: "
+                           "llc_type %d\n", SMC_LGR_ID_SIZE, &lgr->id, type);
+       smc_llc_set_termination_rsn(lgr, SMC_LLC_DEL_PROT_VIOL);
+       smc_lgr_terminate_sched(lgr);
+}
+
+/* flush the llc event queue */
+static void smc_llc_event_flush(struct smc_link_group *lgr)
+{
+       struct smc_llc_qentry *qentry, *q;
+
+       spin_lock_bh(&lgr->llc_event_q_lock);
+       list_for_each_entry_safe(qentry, q, &lgr->llc_event_q, list) {
+               list_del_init(&qentry->list);
+               kfree(qentry);
+       }
+       spin_unlock_bh(&lgr->llc_event_q_lock);
+}
+
+static void smc_llc_event_handler(struct smc_llc_qentry *qentry)
+{
+       union smc_llc_msg *llc = &qentry->msg;
+       struct smc_link *link = qentry->link;
+       struct smc_link_group *lgr = link->lgr;
+
+       if (!smc_link_usable(link))
+               goto out;
 
        switch (llc->raw.hdr.common.type) {
        case SMC_LLC_TEST_LINK:
-               smc_llc_rx_test_link(link, &llc->test_link);
-               break;
-       case SMC_LLC_CONFIRM_LINK:
-               smc_llc_rx_confirm_link(link, &llc->confirm_link);
+               llc->test_link.hd.flags |= SMC_LLC_FLAG_RESP;
+               smc_llc_send_message(link, llc);
                break;
        case SMC_LLC_ADD_LINK:
-               smc_llc_rx_add_link(link, &llc->add_link);
+               if (list_empty(&lgr->list))
+                       goto out;       /* lgr is terminating */
+               if (lgr->role == SMC_CLNT) {
+                       if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK) {
+                               /* a flow is waiting for this message */
+                               smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
+                                                       qentry);
+                               wake_up_interruptible(&lgr->llc_waiter);
+                       } else if (smc_llc_flow_start(&lgr->llc_flow_lcl,
+                                                     qentry)) {
+                               schedule_work(&lgr->llc_add_link_work);
+                       }
+               } else if (smc_llc_flow_start(&lgr->llc_flow_lcl, qentry)) {
+                       /* as smc server, handle client suggestion */
+                       schedule_work(&lgr->llc_add_link_work);
+               }
+               return;
+       case SMC_LLC_CONFIRM_LINK:
+       case SMC_LLC_ADD_LINK_CONT:
+               if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
+                       /* a flow is waiting for this message */
+                       smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry);
+                       wake_up_interruptible(&lgr->llc_waiter);
+                       return;
+               }
                break;
        case SMC_LLC_DELETE_LINK:
-               smc_llc_rx_delete_link(link, &llc->delete_link);
-               break;
+               if (lgr->role == SMC_CLNT) {
+                       /* server requests to delete this link, send response */
+                       if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) {
+                               /* DEL LINK REQ during ADD LINK SEQ */
+                               smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
+                                                       qentry);
+                               wake_up_interruptible(&lgr->llc_waiter);
+                       } else if (smc_llc_flow_start(&lgr->llc_flow_lcl,
+                                                     qentry)) {
+                               schedule_work(&lgr->llc_del_link_work);
+                       }
+               } else {
+                       if (lgr->llc_flow_lcl.type == SMC_LLC_FLOW_ADD_LINK &&
+                           !lgr->llc_flow_lcl.qentry) {
+                               /* DEL LINK REQ during ADD LINK SEQ */
+                               smc_llc_flow_qentry_set(&lgr->llc_flow_lcl,
+                                                       qentry);
+                               wake_up_interruptible(&lgr->llc_waiter);
+                       } else if (smc_llc_flow_start(&lgr->llc_flow_lcl,
+                                                     qentry)) {
+                               schedule_work(&lgr->llc_del_link_work);
+                       }
+               }
+               return;
        case SMC_LLC_CONFIRM_RKEY:
-               smc_llc_rx_confirm_rkey(link, &llc->confirm_rkey);
-               break;
+               /* new request from remote, assign to remote flow */
+               if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) {
+                       /* process here, does not wait for more llc msgs */
+                       smc_llc_rmt_conf_rkey(lgr);
+                       smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt);
+               }
+               return;
        case SMC_LLC_CONFIRM_RKEY_CONT:
-               smc_llc_rx_confirm_rkey_cont(link, &llc->confirm_rkey_cont);
+               /* not used because max links is 3, and 3 rkeys fit into
+                * one CONFIRM_RKEY message
+                */
                break;
        case SMC_LLC_DELETE_RKEY:
-               smc_llc_rx_delete_rkey(link, &llc->delete_rkey);
+               /* new request from remote, assign to remote flow */
+               if (smc_llc_flow_start(&lgr->llc_flow_rmt, qentry)) {
+                       /* process here, does not wait for more llc msgs */
+                       smc_llc_rmt_delete_rkey(lgr);
+                       smc_llc_flow_stop(lgr, &lgr->llc_flow_rmt);
+               }
+               return;
+       default:
+               smc_llc_protocol_violation(lgr, llc->raw.hdr.common.type);
                break;
        }
+out:
+       kfree(qentry);
+}
+
+/* worker to process llc messages on the event queue */
+static void smc_llc_event_work(struct work_struct *work)
+{
+       struct smc_link_group *lgr = container_of(work, struct smc_link_group,
+                                                 llc_event_work);
+       struct smc_llc_qentry *qentry;
+
+       if (!lgr->llc_flow_lcl.type && lgr->delayed_event) {
+               if (smc_link_usable(lgr->delayed_event->link)) {
+                       smc_llc_event_handler(lgr->delayed_event);
+               } else {
+                       qentry = lgr->delayed_event;
+                       lgr->delayed_event = NULL;
+                       kfree(qentry);
+               }
+       }
+
+again:
+       spin_lock_bh(&lgr->llc_event_q_lock);
+       if (!list_empty(&lgr->llc_event_q)) {
+               qentry = list_first_entry(&lgr->llc_event_q,
+                                         struct smc_llc_qentry, list);
+               list_del_init(&qentry->list);
+               spin_unlock_bh(&lgr->llc_event_q_lock);
+               smc_llc_event_handler(qentry);
+               goto again;
+       }
+       spin_unlock_bh(&lgr->llc_event_q_lock);
+}
+
+/* process llc responses in tasklet context */
+static void smc_llc_rx_response(struct smc_link *link,
+                               struct smc_llc_qentry *qentry)
+{
+       u8 llc_type = qentry->msg.raw.hdr.common.type;
+
+       switch (llc_type) {
+       case SMC_LLC_TEST_LINK:
+               if (link->state == SMC_LNK_ACTIVE)
+                       complete(&link->llc_testlink_resp);
+               break;
+       case SMC_LLC_ADD_LINK:
+       case SMC_LLC_DELETE_LINK:
+       case SMC_LLC_CONFIRM_LINK:
+       case SMC_LLC_ADD_LINK_CONT:
+       case SMC_LLC_CONFIRM_RKEY:
+       case SMC_LLC_DELETE_RKEY:
+               /* assign responses to the local flow, we requested them */
+               smc_llc_flow_qentry_set(&link->lgr->llc_flow_lcl, qentry);
+               wake_up_interruptible(&link->lgr->llc_waiter);
+               return;
+       case SMC_LLC_CONFIRM_RKEY_CONT:
+               /* not used because max links is 3 */
+               break;
+       default:
+               smc_llc_protocol_violation(link->lgr, llc_type);
+               break;
+       }
+       kfree(qentry);
+}
+
+static void smc_llc_enqueue(struct smc_link *link, union smc_llc_msg *llc)
+{
+       struct smc_link_group *lgr = link->lgr;
+       struct smc_llc_qentry *qentry;
+       unsigned long flags;
+
+       qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC);
+       if (!qentry)
+               return;
+       qentry->link = link;
+       INIT_LIST_HEAD(&qentry->list);
+       memcpy(&qentry->msg, llc, sizeof(union smc_llc_msg));
+
+       /* process responses immediately */
+       if (llc->raw.hdr.flags & SMC_LLC_FLAG_RESP) {
+               smc_llc_rx_response(link, qentry);
+               return;
+       }
+
+       /* add requests to event queue */
+       spin_lock_irqsave(&lgr->llc_event_q_lock, flags);
+       list_add_tail(&qentry->list, &lgr->llc_event_q);
+       spin_unlock_irqrestore(&lgr->llc_event_q_lock, flags);
+       schedule_work(&link->lgr->llc_event_work);
+}
+
+/* copy received msg and add it to the event queue */
+static void smc_llc_rx_handler(struct ib_wc *wc, void *buf)
+{
+       struct smc_link *link = (struct smc_link *)wc->qp->qp_context;
+       union smc_llc_msg *llc = buf;
+
+       if (wc->byte_len < sizeof(*llc))
+               return; /* short message */
+       if (llc->raw.hdr.length != sizeof(*llc))
+               return; /* invalid message */
+
+       smc_llc_enqueue(link, llc);
 }
 
 /***************************** worker, utils *********************************/
@@ -613,112 +1662,162 @@ static void smc_llc_testlink_work(struct work_struct *work)
        /* receive TEST LINK response over RoCE fabric */
        rc = wait_for_completion_interruptible_timeout(&link->llc_testlink_resp,
                                                       SMC_LLC_WAIT_TIME);
+       if (link->state != SMC_LNK_ACTIVE)
+               return;         /* link state changed */
        if (rc <= 0) {
-               smc_lgr_terminate_sched(smc_get_lgr(link));
+               smcr_link_down_cond_sched(link);
                return;
        }
        next_interval = link->llc_testlink_time;
 out:
-       queue_delayed_work(link->llc_wq, &link->llc_testlink_wrk,
-                          next_interval);
+       schedule_delayed_work(&link->llc_testlink_wrk, next_interval);
 }
 
-int smc_llc_link_init(struct smc_link *link)
+void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc)
 {
-       struct smc_link_group *lgr = smc_get_lgr(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_delete_rkey);
-       mutex_init(&link->llc_delete_rkey_mutex);
-       init_completion(&link->llc_testlink_resp);
-       INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
-       return 0;
+       struct net *net = sock_net(smc->clcsock->sk);
+
+       INIT_WORK(&lgr->llc_event_work, smc_llc_event_work);
+       INIT_WORK(&lgr->llc_add_link_work, smc_llc_add_link_work);
+       INIT_WORK(&lgr->llc_del_link_work, smc_llc_delete_link_work);
+       INIT_LIST_HEAD(&lgr->llc_event_q);
+       spin_lock_init(&lgr->llc_event_q_lock);
+       spin_lock_init(&lgr->llc_flow_lock);
+       init_waitqueue_head(&lgr->llc_waiter);
+       mutex_init(&lgr->llc_conf_mutex);
+       lgr->llc_testlink_time = net->ipv4.sysctl_tcp_keepalive_time;
 }
 
-void smc_llc_link_active(struct smc_link *link, int testlink_time)
+/* called after lgr was removed from lgr_list */
+void smc_llc_lgr_clear(struct smc_link_group *lgr)
 {
-       link->state = SMC_LNK_ACTIVE;
-       if (testlink_time) {
-               link->llc_testlink_time = testlink_time * HZ;
-               queue_delayed_work(link->llc_wq, &link->llc_testlink_wrk,
-                                  link->llc_testlink_time);
+       smc_llc_event_flush(lgr);
+       wake_up_interruptible_all(&lgr->llc_waiter);
+       cancel_work_sync(&lgr->llc_event_work);
+       cancel_work_sync(&lgr->llc_add_link_work);
+       cancel_work_sync(&lgr->llc_del_link_work);
+       if (lgr->delayed_event) {
+               kfree(lgr->delayed_event);
+               lgr->delayed_event = NULL;
        }
 }
 
-void smc_llc_link_deleting(struct smc_link *link)
+int smc_llc_link_init(struct smc_link *link)
 {
-       link->state = SMC_LNK_DELETING;
-       smc_wr_wakeup_tx_wait(link);
+       init_completion(&link->llc_testlink_resp);
+       INIT_DELAYED_WORK(&link->llc_testlink_wrk, smc_llc_testlink_work);
+       return 0;
 }
 
-/* called in tasklet context */
-void smc_llc_link_inactive(struct smc_link *link)
+void smc_llc_link_active(struct smc_link *link)
 {
-       link->state = SMC_LNK_INACTIVE;
-       cancel_delayed_work(&link->llc_testlink_wrk);
-       smc_wr_wakeup_reg_wait(link);
-       smc_wr_wakeup_tx_wait(link);
+       pr_warn_ratelimited("smc: SMC-R lg %*phN link added: id %*phN, "
+                           "peerid %*phN, ibdev %s, ibport %d\n",
+                           SMC_LGR_ID_SIZE, &link->lgr->id,
+                           SMC_LGR_ID_SIZE, &link->link_uid,
+                           SMC_LGR_ID_SIZE, &link->peer_link_uid,
+                           link->smcibdev->ibdev->name, link->ibport);
+       link->state = SMC_LNK_ACTIVE;
+       if (link->lgr->llc_testlink_time) {
+               link->llc_testlink_time = link->lgr->llc_testlink_time * HZ;
+               schedule_delayed_work(&link->llc_testlink_wrk,
+                                     link->llc_testlink_time);
+       }
 }
 
 /* called in worker context */
-void smc_llc_link_clear(struct smc_link *link)
+void smc_llc_link_clear(struct smc_link *link, bool log)
 {
-       flush_workqueue(link->llc_wq);
-       destroy_workqueue(link->llc_wq);
+       if (log)
+               pr_warn_ratelimited("smc: SMC-R lg %*phN link removed: id %*phN"
+                                   ", peerid %*phN, ibdev %s, ibport %d\n",
+                                   SMC_LGR_ID_SIZE, &link->lgr->id,
+                                   SMC_LGR_ID_SIZE, &link->link_uid,
+                                   SMC_LGR_ID_SIZE, &link->peer_link_uid,
+                                   link->smcibdev->ibdev->name, link->ibport);
+       complete(&link->llc_testlink_resp);
+       cancel_delayed_work_sync(&link->llc_testlink_wrk);
+       smc_wr_wakeup_reg_wait(link);
+       smc_wr_wakeup_tx_wait(link);
 }
 
-/* register a new rtoken at the remote peer */
-int smc_llc_do_confirm_rkey(struct smc_link *link,
+/* register a new rtoken at the remote peer (for all links) */
+int smc_llc_do_confirm_rkey(struct smc_link *send_link,
                            struct smc_buf_desc *rmb_desc)
 {
-       int rc;
+       struct smc_link_group *lgr = send_link->lgr;
+       struct smc_llc_qentry *qentry = NULL;
+       int rc = 0;
 
-       /* protected by mutex smc_create_lgr_pending */
-       reinit_completion(&link->llc_confirm_rkey);
-       rc = smc_llc_send_confirm_rkey(link, rmb_desc);
+       rc = smc_llc_send_confirm_rkey(send_link, rmb_desc);
        if (rc)
-               return rc;
+               goto out;
        /* 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;
+       qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME,
+                             SMC_LLC_CONFIRM_RKEY);
+       if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG))
+               rc = -EFAULT;
+out:
+       if (qentry)
+               smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
+       return rc;
 }
 
 /* unregister an rtoken at the remote peer */
-int smc_llc_do_delete_rkey(struct smc_link *link,
+int smc_llc_do_delete_rkey(struct smc_link_group *lgr,
                           struct smc_buf_desc *rmb_desc)
 {
+       struct smc_llc_qentry *qentry = NULL;
+       struct smc_link *send_link;
        int rc = 0;
 
-       mutex_lock(&link->llc_delete_rkey_mutex);
-       if (link->state != SMC_LNK_ACTIVE)
-               goto out;
-       reinit_completion(&link->llc_delete_rkey);
-       rc = smc_llc_send_delete_rkey(link, rmb_desc);
+       send_link = smc_llc_usable_link(lgr);
+       if (!send_link)
+               return -ENOLINK;
+
+       /* protected by llc_flow control */
+       rc = smc_llc_send_delete_rkey(send_link, rmb_desc);
        if (rc)
                goto out;
        /* receive DELETE RKEY response from server over RoCE fabric */
-       rc = wait_for_completion_interruptible_timeout(&link->llc_delete_rkey,
-                                                      SMC_LLC_WAIT_TIME);
-       if (rc <= 0 || link->llc_delete_rkey_rc)
+       qentry = smc_llc_wait(lgr, send_link, SMC_LLC_WAIT_TIME,
+                             SMC_LLC_DELETE_RKEY);
+       if (!qentry || (qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_RKEY_NEG))
                rc = -EFAULT;
-       else
-               rc = 0;
 out:
-       mutex_unlock(&link->llc_delete_rkey_mutex);
+       if (qentry)
+               smc_llc_flow_qentry_del(&lgr->llc_flow_lcl);
        return rc;
 }
 
+void smc_llc_link_set_uid(struct smc_link *link)
+{
+       __be32 link_uid;
+
+       link_uid = htonl(*((u32 *)link->lgr->id) + link->link_id);
+       memcpy(link->link_uid, &link_uid, SMC_LGR_ID_SIZE);
+}
+
+/* save peers link user id, used for debug purposes */
+void smc_llc_save_peer_uid(struct smc_llc_qentry *qentry)
+{
+       memcpy(qentry->link->peer_link_uid, qentry->msg.confirm_link.link_uid,
+              SMC_LGR_ID_SIZE);
+}
+
+/* evaluate confirm link request or response */
+int smc_llc_eval_conf_link(struct smc_llc_qentry *qentry,
+                          enum smc_llc_reqresp type)
+{
+       if (type == SMC_LLC_REQ) {      /* SMC server assigns link_id */
+               qentry->link->link_id = qentry->msg.confirm_link.link_num;
+               smc_llc_link_set_uid(qentry->link);
+       }
+       if (!(qentry->msg.raw.hdr.flags & SMC_LLC_FLAG_NO_RMBE_EYEC))
+               return -ENOTSUPP;
+       return 0;
+}
+
 /***************************** init, exit, misc ******************************/
 
 static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
@@ -734,6 +1833,10 @@ static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
                .handler        = smc_llc_rx_handler,
                .type           = SMC_LLC_ADD_LINK
        },
+       {
+               .handler        = smc_llc_rx_handler,
+               .type           = SMC_LLC_ADD_LINK_CONT
+       },
        {
                .handler        = smc_llc_rx_handler,
                .type           = SMC_LLC_DELETE_LINK
index 461c0c3..a5d2fe3 100644 (file)
@@ -28,6 +28,7 @@ enum smc_llc_reqresp {
 enum smc_llc_msg_type {
        SMC_LLC_CONFIRM_LINK            = 0x01,
        SMC_LLC_ADD_LINK                = 0x02,
+       SMC_LLC_ADD_LINK_CONT           = 0x03,
        SMC_LLC_DELETE_LINK             = 0x04,
        SMC_LLC_CONFIRM_RKEY            = 0x06,
        SMC_LLC_TEST_LINK               = 0x07,
@@ -35,22 +36,74 @@ enum smc_llc_msg_type {
        SMC_LLC_DELETE_RKEY             = 0x09,
 };
 
+#define smc_link_downing(state) \
+       (cmpxchg(state, SMC_LNK_ACTIVE, SMC_LNK_INACTIVE) == SMC_LNK_ACTIVE)
+
+/* LLC DELETE LINK Request Reason Codes */
+#define SMC_LLC_DEL_LOST_PATH          0x00010000
+#define SMC_LLC_DEL_OP_INIT_TERM       0x00020000
+#define SMC_LLC_DEL_PROG_INIT_TERM     0x00030000
+#define SMC_LLC_DEL_PROT_VIOL          0x00040000
+#define SMC_LLC_DEL_NO_ASYM_NEEDED     0x00050000
+/* LLC DELETE LINK Response Reason Codes */
+#define SMC_LLC_DEL_NOLNK      0x00100000  /* Unknown Link ID (no link) */
+#define SMC_LLC_DEL_NOLGR      0x00200000  /* Unknown Link Group */
+
+/* returns a usable link of the link group, or NULL */
+static inline struct smc_link *smc_llc_usable_link(struct smc_link_group *lgr)
+{
+       int i;
+
+       for (i = 0; i < SMC_LINKS_PER_LGR_MAX; i++)
+               if (smc_link_usable(&lgr->lnk[i]))
+                       return &lgr->lnk[i];
+       return NULL;
+}
+
+/* set the termination reason code for the link group */
+static inline void smc_llc_set_termination_rsn(struct smc_link_group *lgr,
+                                              u32 rsn)
+{
+       if (!lgr->llc_termination_rsn)
+               lgr->llc_termination_rsn = rsn;
+}
+
 /* transmit */
 int smc_llc_send_confirm_link(struct smc_link *lnk,
                              enum smc_llc_reqresp reqresp);
 int smc_llc_send_add_link(struct smc_link *link, u8 mac[], u8 gid[],
+                         struct smc_link *link_new,
                          enum smc_llc_reqresp reqresp);
-int smc_llc_send_delete_link(struct smc_link *link,
-                            enum smc_llc_reqresp reqresp, bool orderly);
+int smc_llc_send_delete_link(struct smc_link *link, u8 link_del_id,
+                            enum smc_llc_reqresp reqresp, bool orderly,
+                            u32 reason);
+void smc_llc_srv_delete_link_local(struct smc_link *link, u8 del_link_id);
+void smc_llc_lgr_init(struct smc_link_group *lgr, struct smc_sock *smc);
+void smc_llc_lgr_clear(struct smc_link_group *lgr);
 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_deleting(struct smc_link *link);
-void smc_llc_link_inactive(struct smc_link *link);
-void smc_llc_link_clear(struct smc_link *link);
-int smc_llc_do_confirm_rkey(struct smc_link *link,
+void smc_llc_link_active(struct smc_link *link);
+void smc_llc_link_clear(struct smc_link *link, bool log);
+int smc_llc_do_confirm_rkey(struct smc_link *send_link,
                            struct smc_buf_desc *rmb_desc);
-int smc_llc_do_delete_rkey(struct smc_link *link,
+int smc_llc_do_delete_rkey(struct smc_link_group *lgr,
                           struct smc_buf_desc *rmb_desc);
+int smc_llc_flow_initiate(struct smc_link_group *lgr,
+                         enum smc_llc_flowtype type);
+void smc_llc_flow_stop(struct smc_link_group *lgr, struct smc_llc_flow *flow);
+int smc_llc_eval_conf_link(struct smc_llc_qentry *qentry,
+                          enum smc_llc_reqresp type);
+void smc_llc_link_set_uid(struct smc_link *link);
+void smc_llc_save_peer_uid(struct smc_llc_qentry *qentry);
+struct smc_llc_qentry *smc_llc_wait(struct smc_link_group *lgr,
+                                   struct smc_link *lnk,
+                                   int time_out, u8 exp_msg);
+struct smc_llc_qentry *smc_llc_flow_qentry_clr(struct smc_llc_flow *flow);
+void smc_llc_flow_qentry_del(struct smc_llc_flow *flow);
+void smc_llc_send_link_delete_all(struct smc_link_group *lgr, bool ord,
+                                 u32 rsn);
+int smc_llc_cli_add_link(struct smc_link *link, struct smc_llc_qentry *qentry);
+int smc_llc_srv_add_link(struct smc_link *link);
+void smc_llc_srv_add_link_local(struct smc_link *link);
 int smc_llc_init(void) __init;
 
 #endif /* SMC_LLC_H */
index 2a5ed47..be03f12 100644 (file)
@@ -50,29 +50,26 @@ static struct nla_policy smc_pnet_policy[SMC_PNETID_MAX + 1] = {
 
 static struct genl_family smc_pnet_nl_family;
 
-/**
- * struct smc_user_pnetentry - pnet identifier name entry for/from user
- * @list: List node.
- * @pnet_name: Pnet identifier name
- * @ndev: pointer to network device.
- * @smcibdev: Pointer to IB device.
- * @ib_port: Port of IB device.
- * @smcd_dev: Pointer to smcd device.
- */
-struct smc_user_pnetentry {
-       struct list_head list;
-       char pnet_name[SMC_MAX_PNETID_LEN + 1];
-       struct net_device *ndev;
-       struct smc_ib_device *smcibdev;
-       u8 ib_port;
-       struct smcd_dev *smcd_dev;
+enum smc_pnet_nametype {
+       SMC_PNET_ETH    = 1,
+       SMC_PNET_IB     = 2,
 };
 
 /* pnet entry stored in pnet table */
 struct smc_pnetentry {
        struct list_head list;
        char pnet_name[SMC_MAX_PNETID_LEN + 1];
-       struct net_device *ndev;
+       enum smc_pnet_nametype type;
+       union {
+               struct {
+                       char eth_name[IFNAMSIZ + 1];
+                       struct net_device *ndev;
+               };
+               struct {
+                       char ib_name[IB_DEVICE_NAME_MAX + 1];
+                       u8 ib_port;
+               };
+       };
 };
 
 /* Check if two given pnetids match */
@@ -106,14 +103,21 @@ static int smc_pnet_remove_by_pnetid(struct net *net, char *pnet_name)
        sn = net_generic(net, smc_net_id);
        pnettable = &sn->pnettable;
 
-       /* remove netdevices */
+       /* remove table entry */
        write_lock(&pnettable->lock);
        list_for_each_entry_safe(pnetelem, tmp_pe, &pnettable->pnetlist,
                                 list) {
                if (!pnet_name ||
                    smc_pnet_match(pnetelem->pnet_name, pnet_name)) {
                        list_del(&pnetelem->list);
-                       dev_put(pnetelem->ndev);
+                       if (pnetelem->type == SMC_PNET_ETH && pnetelem->ndev) {
+                               dev_put(pnetelem->ndev);
+                               pr_warn_ratelimited("smc: net device %s "
+                                                   "erased user defined "
+                                                   "pnetid %.16s\n",
+                                                   pnetelem->eth_name,
+                                                   pnetelem->pnet_name);
+                       }
                        kfree(pnetelem);
                        rc = 0;
                }
@@ -132,6 +136,12 @@ static int smc_pnet_remove_by_pnetid(struct net *net, char *pnet_name)
                            (!pnet_name ||
                             smc_pnet_match(pnet_name,
                                            ibdev->pnetid[ibport]))) {
+                               pr_warn_ratelimited("smc: ib device %s ibport "
+                                                   "%d erased user defined "
+                                                   "pnetid %.16s\n",
+                                                   ibdev->ibdev->name,
+                                                   ibport + 1,
+                                                   ibdev->pnetid[ibport]);
                                memset(ibdev->pnetid[ibport], 0,
                                       SMC_MAX_PNETID_LEN);
                                ibdev->pnetid_by_user[ibport] = false;
@@ -146,6 +156,10 @@ static int smc_pnet_remove_by_pnetid(struct net *net, char *pnet_name)
                if (smcd_dev->pnetid_by_user &&
                    (!pnet_name ||
                     smc_pnet_match(pnet_name, smcd_dev->pnetid))) {
+                       pr_warn_ratelimited("smc: smcd device %s "
+                                           "erased user defined pnetid "
+                                           "%.16s\n", dev_name(&smcd_dev->dev),
+                                           smcd_dev->pnetid);
                        memset(smcd_dev->pnetid, 0, SMC_MAX_PNETID_LEN);
                        smcd_dev->pnetid_by_user = false;
                        rc = 0;
@@ -155,9 +169,9 @@ static int smc_pnet_remove_by_pnetid(struct net *net, char *pnet_name)
        return rc;
 }
 
-/* Remove a pnet entry mentioning a given network device from the pnet table.
+/* Add the reference to a given network device to the pnet table.
  */
-static int smc_pnet_remove_by_ndev(struct net_device *ndev)
+static int smc_pnet_add_by_ndev(struct net_device *ndev)
 {
        struct smc_pnetentry *pnetelem, *tmp_pe;
        struct smc_pnettable *pnettable;
@@ -171,11 +185,15 @@ static int smc_pnet_remove_by_ndev(struct net_device *ndev)
 
        write_lock(&pnettable->lock);
        list_for_each_entry_safe(pnetelem, tmp_pe, &pnettable->pnetlist, list) {
-               if (pnetelem->ndev == ndev) {
-                       list_del(&pnetelem->list);
-                       dev_put(pnetelem->ndev);
-                       kfree(pnetelem);
+               if (pnetelem->type == SMC_PNET_ETH && !pnetelem->ndev &&
+                   !strncmp(pnetelem->eth_name, ndev->name, IFNAMSIZ)) {
+                       dev_hold(ndev);
+                       pnetelem->ndev = ndev;
                        rc = 0;
+                       pr_warn_ratelimited("smc: adding net device %s with "
+                                           "user defined pnetid %.16s\n",
+                                           pnetelem->eth_name,
+                                           pnetelem->pnet_name);
                        break;
                }
        }
@@ -183,80 +201,71 @@ static int smc_pnet_remove_by_ndev(struct net_device *ndev)
        return rc;
 }
 
-/* Append a pnetid to the end of the pnet table if not already on this list.
+/* Remove the reference to a given network device from the pnet table.
  */
-static int smc_pnet_enter(struct smc_pnettable *pnettable,
-                         struct smc_user_pnetentry *new_pnetelem)
+static int smc_pnet_remove_by_ndev(struct net_device *ndev)
 {
-       u8 pnet_null[SMC_MAX_PNETID_LEN] = {0};
-       u8 ndev_pnetid[SMC_MAX_PNETID_LEN];
-       struct smc_pnetentry *tmp_pnetelem;
-       struct smc_pnetentry *pnetelem;
-       bool new_smcddev = false;
-       struct net_device *ndev;
-       bool new_netdev = true;
-       bool new_ibdev = false;
-
-       if (new_pnetelem->smcibdev) {
-               struct smc_ib_device *ib_dev = new_pnetelem->smcibdev;
-               int ib_port = new_pnetelem->ib_port;
+       struct smc_pnetentry *pnetelem, *tmp_pe;
+       struct smc_pnettable *pnettable;
+       struct net *net = dev_net(ndev);
+       struct smc_net *sn;
+       int rc = -ENOENT;
 
-               spin_lock(&smc_ib_devices.lock);
-               if (smc_pnet_match(ib_dev->pnetid[ib_port - 1], pnet_null)) {
-                       memcpy(ib_dev->pnetid[ib_port - 1],
-                              new_pnetelem->pnet_name, SMC_MAX_PNETID_LEN);
-                       ib_dev->pnetid_by_user[ib_port - 1] = true;
-                       new_ibdev = true;
-               }
-               spin_unlock(&smc_ib_devices.lock);
-       }
-       if (new_pnetelem->smcd_dev) {
-               struct smcd_dev *smcd_dev = new_pnetelem->smcd_dev;
+       /* get pnettable for namespace */
+       sn = net_generic(net, smc_net_id);
+       pnettable = &sn->pnettable;
 
-               spin_lock(&smcd_dev_list.lock);
-               if (smc_pnet_match(smcd_dev->pnetid, pnet_null)) {
-                       memcpy(smcd_dev->pnetid, new_pnetelem->pnet_name,
-                              SMC_MAX_PNETID_LEN);
-                       smcd_dev->pnetid_by_user = true;
-                       new_smcddev = true;
+       write_lock(&pnettable->lock);
+       list_for_each_entry_safe(pnetelem, tmp_pe, &pnettable->pnetlist, list) {
+               if (pnetelem->type == SMC_PNET_ETH && pnetelem->ndev == ndev) {
+                       dev_put(pnetelem->ndev);
+                       pnetelem->ndev = NULL;
+                       rc = 0;
+                       pr_warn_ratelimited("smc: removing net device %s with "
+                                           "user defined pnetid %.16s\n",
+                                           pnetelem->eth_name,
+                                           pnetelem->pnet_name);
+                       break;
                }
-               spin_unlock(&smcd_dev_list.lock);
        }
+       write_unlock(&pnettable->lock);
+       return rc;
+}
 
-       if (!new_pnetelem->ndev)
-               return (new_ibdev || new_smcddev) ? 0 : -EEXIST;
+/* Apply pnetid to ib device when no pnetid is set.
+ */
+static bool smc_pnet_apply_ib(struct smc_ib_device *ib_dev, u8 ib_port,
+                             char *pnet_name)
+{
+       u8 pnet_null[SMC_MAX_PNETID_LEN] = {0};
+       bool applied = false;
 
-       /* check if (base) netdev already has a pnetid. If there is one, we do
-        * not want to add a pnet table entry
-        */
-       ndev = pnet_find_base_ndev(new_pnetelem->ndev);
-       if (!smc_pnetid_by_dev_port(ndev->dev.parent, ndev->dev_port,
-                                   ndev_pnetid))
-               return (new_ibdev || new_smcddev) ? 0 : -EEXIST;
+       spin_lock(&smc_ib_devices.lock);
+       if (smc_pnet_match(ib_dev->pnetid[ib_port - 1], pnet_null)) {
+               memcpy(ib_dev->pnetid[ib_port - 1], pnet_name,
+                      SMC_MAX_PNETID_LEN);
+               ib_dev->pnetid_by_user[ib_port - 1] = true;
+               applied = true;
+       }
+       spin_unlock(&smc_ib_devices.lock);
+       return applied;
+}
 
-       /* add a new netdev entry to the pnet table if there isn't one */
-       tmp_pnetelem = kzalloc(sizeof(*pnetelem), GFP_KERNEL);
-       if (!tmp_pnetelem)
-               return -ENOMEM;
-       memcpy(tmp_pnetelem->pnet_name, new_pnetelem->pnet_name,
-              SMC_MAX_PNETID_LEN);
-       tmp_pnetelem->ndev = new_pnetelem->ndev;
+/* Apply pnetid to smcd device when no pnetid is set.
+ */
+static bool smc_pnet_apply_smcd(struct smcd_dev *smcd_dev, char *pnet_name)
+{
+       u8 pnet_null[SMC_MAX_PNETID_LEN] = {0};
+       bool applied = false;
 
-       write_lock(&pnettable->lock);
-       list_for_each_entry(pnetelem, &pnettable->pnetlist, list) {
-               if (pnetelem->ndev == new_pnetelem->ndev)
-                       new_netdev = false;
-       }
-       if (new_netdev) {
-               dev_hold(tmp_pnetelem->ndev);
-               list_add_tail(&tmp_pnetelem->list, &pnettable->pnetlist);
-               write_unlock(&pnettable->lock);
-       } else {
-               write_unlock(&pnettable->lock);
-               kfree(tmp_pnetelem);
+       spin_lock(&smcd_dev_list.lock);
+       if (smc_pnet_match(smcd_dev->pnetid, pnet_null)) {
+               memcpy(smcd_dev->pnetid, pnet_name, SMC_MAX_PNETID_LEN);
+               smcd_dev->pnetid_by_user = true;
+               applied = true;
        }
-
-       return (new_netdev || new_ibdev || new_smcddev) ? 0 : -EEXIST;
+       spin_unlock(&smcd_dev_list.lock);
+       return applied;
 }
 
 /* The limit for pnetid is 16 characters.
@@ -323,57 +332,184 @@ out:
        return smcd_dev;
 }
 
-/* Parse the supplied netlink attributes and fill a pnetentry structure.
- * For ethernet and infiniband device names verify that the devices exist.
+static int smc_pnet_add_eth(struct smc_pnettable *pnettable, struct net *net,
+                           char *eth_name, char *pnet_name)
+{
+       struct smc_pnetentry *tmp_pe, *new_pe;
+       struct net_device *ndev, *base_ndev;
+       u8 ndev_pnetid[SMC_MAX_PNETID_LEN];
+       bool new_netdev;
+       int rc;
+
+       /* check if (base) netdev already has a pnetid. If there is one, we do
+        * not want to add a pnet table entry
+        */
+       rc = -EEXIST;
+       ndev = dev_get_by_name(net, eth_name);  /* dev_hold() */
+       if (ndev) {
+               base_ndev = pnet_find_base_ndev(ndev);
+               if (!smc_pnetid_by_dev_port(base_ndev->dev.parent,
+                                           base_ndev->dev_port, ndev_pnetid))
+                       goto out_put;
+       }
+
+       /* add a new netdev entry to the pnet table if there isn't one */
+       rc = -ENOMEM;
+       new_pe = kzalloc(sizeof(*new_pe), GFP_KERNEL);
+       if (!new_pe)
+               goto out_put;
+       new_pe->type = SMC_PNET_ETH;
+       memcpy(new_pe->pnet_name, pnet_name, SMC_MAX_PNETID_LEN);
+       strncpy(new_pe->eth_name, eth_name, IFNAMSIZ);
+       new_pe->ndev = ndev;
+
+       rc = -EEXIST;
+       new_netdev = true;
+       write_lock(&pnettable->lock);
+       list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) {
+               if (tmp_pe->type == SMC_PNET_ETH &&
+                   !strncmp(tmp_pe->eth_name, eth_name, IFNAMSIZ)) {
+                       new_netdev = false;
+                       break;
+               }
+       }
+       if (new_netdev) {
+               list_add_tail(&new_pe->list, &pnettable->pnetlist);
+               write_unlock(&pnettable->lock);
+       } else {
+               write_unlock(&pnettable->lock);
+               kfree(new_pe);
+               goto out_put;
+       }
+       if (ndev)
+               pr_warn_ratelimited("smc: net device %s "
+                                   "applied user defined pnetid %.16s\n",
+                                   new_pe->eth_name, new_pe->pnet_name);
+       return 0;
+
+out_put:
+       if (ndev)
+               dev_put(ndev);
+       return rc;
+}
+
+static int smc_pnet_add_ib(struct smc_pnettable *pnettable, char *ib_name,
+                          u8 ib_port, char *pnet_name)
+{
+       struct smc_pnetentry *tmp_pe, *new_pe;
+       struct smc_ib_device *ib_dev;
+       bool smcddev_applied = true;
+       bool ibdev_applied = true;
+       struct smcd_dev *smcd_dev;
+       bool new_ibdev;
+
+       /* try to apply the pnetid to active devices */
+       ib_dev = smc_pnet_find_ib(ib_name);
+       if (ib_dev) {
+               ibdev_applied = smc_pnet_apply_ib(ib_dev, ib_port, pnet_name);
+               if (ibdev_applied)
+                       pr_warn_ratelimited("smc: ib device %s ibport %d "
+                                           "applied user defined pnetid "
+                                           "%.16s\n", ib_dev->ibdev->name,
+                                           ib_port,
+                                           ib_dev->pnetid[ib_port - 1]);
+       }
+       smcd_dev = smc_pnet_find_smcd(ib_name);
+       if (smcd_dev) {
+               smcddev_applied = smc_pnet_apply_smcd(smcd_dev, pnet_name);
+               if (smcddev_applied)
+                       pr_warn_ratelimited("smc: smcd device %s "
+                                           "applied user defined pnetid "
+                                           "%.16s\n", dev_name(&smcd_dev->dev),
+                                           smcd_dev->pnetid);
+       }
+       /* Apply fails when a device has a hardware-defined pnetid set, do not
+        * add a pnet table entry in that case.
+        */
+       if (!ibdev_applied || !smcddev_applied)
+               return -EEXIST;
+
+       /* add a new ib entry to the pnet table if there isn't one */
+       new_pe = kzalloc(sizeof(*new_pe), GFP_KERNEL);
+       if (!new_pe)
+               return -ENOMEM;
+       new_pe->type = SMC_PNET_IB;
+       memcpy(new_pe->pnet_name, pnet_name, SMC_MAX_PNETID_LEN);
+       strncpy(new_pe->ib_name, ib_name, IB_DEVICE_NAME_MAX);
+       new_pe->ib_port = ib_port;
+
+       new_ibdev = true;
+       write_lock(&pnettable->lock);
+       list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) {
+               if (tmp_pe->type == SMC_PNET_IB &&
+                   !strncmp(tmp_pe->ib_name, ib_name, IB_DEVICE_NAME_MAX)) {
+                       new_ibdev = false;
+                       break;
+               }
+       }
+       if (new_ibdev) {
+               list_add_tail(&new_pe->list, &pnettable->pnetlist);
+               write_unlock(&pnettable->lock);
+       } else {
+               write_unlock(&pnettable->lock);
+               kfree(new_pe);
+       }
+       return (new_ibdev) ? 0 : -EEXIST;
+}
+
+/* Append a pnetid to the end of the pnet table if not already on this list.
  */
-static int smc_pnet_fill_entry(struct net *net,
-                              struct smc_user_pnetentry *pnetelem,
-                              struct nlattr *tb[])
+static int smc_pnet_enter(struct net *net, struct nlattr *tb[])
 {
-       char *string, *ibname;
+       char pnet_name[SMC_MAX_PNETID_LEN + 1];
+       struct smc_pnettable *pnettable;
+       bool new_netdev = false;
+       bool new_ibdev = false;
+       struct smc_net *sn;
+       u8 ibport = 1;
+       char *string;
        int rc;
 
-       memset(pnetelem, 0, sizeof(*pnetelem));
-       INIT_LIST_HEAD(&pnetelem->list);
+       /* get pnettable for namespace */
+       sn = net_generic(net, smc_net_id);
+       pnettable = &sn->pnettable;
 
        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))
+       if (!smc_pnetid_valid(string, pnet_name))
                goto error;
 
-       rc = -EINVAL;
        if (tb[SMC_PNETID_ETHNAME]) {
                string = (char *)nla_data(tb[SMC_PNETID_ETHNAME]);
-               pnetelem->ndev = dev_get_by_name(net, string);
-               if (!pnetelem->ndev)
+               rc = smc_pnet_add_eth(pnettable, net, string, pnet_name);
+               if (!rc)
+                       new_netdev = true;
+               else if (rc != -EEXIST)
                        goto error;
        }
 
        /* if this is not the initial namespace, stop here */
        if (net != &init_net)
-               return 0;
+               return new_netdev ? 0 : -EEXIST;
 
        rc = -EINVAL;
        if (tb[SMC_PNETID_IBNAME]) {
-               ibname = (char *)nla_data(tb[SMC_PNETID_IBNAME]);
-               ibname = strim(ibname);
-               pnetelem->smcibdev = smc_pnet_find_ib(ibname);
-               pnetelem->smcd_dev = smc_pnet_find_smcd(ibname);
-               if (!pnetelem->smcibdev && !pnetelem->smcd_dev)
-                       goto error;
-               if (pnetelem->smcibdev) {
-                       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)
+               string = (char *)nla_data(tb[SMC_PNETID_IBNAME]);
+               string = strim(string);
+               if (tb[SMC_PNETID_IBPORT]) {
+                       ibport = nla_get_u8(tb[SMC_PNETID_IBPORT]);
+                       if (ibport < 1 || ibport > SMC_MAX_PORTS)
                                goto error;
                }
+               rc = smc_pnet_add_ib(pnettable, string, ibport, pnet_name);
+               if (!rc)
+                       new_ibdev = true;
+               else if (rc != -EEXIST)
+                       goto error;
        }
-
-       return 0;
+       return (new_netdev || new_ibdev) ? 0 : -EEXIST;
 
 error:
        return rc;
@@ -381,28 +517,22 @@ error:
 
 /* Convert an smc_pnetentry to a netlink attribute sequence */
 static int smc_pnet_set_nla(struct sk_buff *msg,
-                           struct smc_user_pnetentry *pnetelem)
+                           struct smc_pnetentry *pnetelem)
 {
        if (nla_put_string(msg, SMC_PNETID_NAME, pnetelem->pnet_name))
                return -1;
-       if (pnetelem->ndev) {
+       if (pnetelem->type == SMC_PNET_ETH) {
                if (nla_put_string(msg, SMC_PNETID_ETHNAME,
-                                  pnetelem->ndev->name))
+                                  pnetelem->eth_name))
                        return -1;
        } else {
                if (nla_put_string(msg, SMC_PNETID_ETHNAME, "n/a"))
                        return -1;
        }
-       if (pnetelem->smcibdev) {
-               if (nla_put_string(msg, SMC_PNETID_IBNAME,
-                       dev_name(pnetelem->smcibdev->ibdev->dev.parent)) ||
+       if (pnetelem->type == SMC_PNET_IB) {
+               if (nla_put_string(msg, SMC_PNETID_IBNAME, pnetelem->ib_name) ||
                    nla_put_u8(msg, SMC_PNETID_IBPORT, pnetelem->ib_port))
                        return -1;
-       } else if (pnetelem->smcd_dev) {
-               if (nla_put_string(msg, SMC_PNETID_IBNAME,
-                                  dev_name(&pnetelem->smcd_dev->dev)) ||
-                   nla_put_u8(msg, SMC_PNETID_IBPORT, 1))
-                       return -1;
        } else {
                if (nla_put_string(msg, SMC_PNETID_IBNAME, "n/a") ||
                    nla_put_u8(msg, SMC_PNETID_IBPORT, 0xff))
@@ -415,21 +545,8 @@ static int smc_pnet_set_nla(struct sk_buff *msg,
 static int smc_pnet_add(struct sk_buff *skb, struct genl_info *info)
 {
        struct net *net = genl_info_net(info);
-       struct smc_user_pnetentry pnetelem;
-       struct smc_pnettable *pnettable;
-       struct smc_net *sn;
-       int rc;
-
-       /* get pnettable for namespace */
-       sn = net_generic(net, smc_net_id);
-       pnettable = &sn->pnettable;
 
-       rc = smc_pnet_fill_entry(net, &pnetelem, info->attrs);
-       if (!rc)
-               rc = smc_pnet_enter(pnettable, &pnetelem);
-       if (pnetelem.ndev)
-               dev_put(pnetelem.ndev);
-       return rc;
+       return smc_pnet_enter(net, info->attrs);
 }
 
 static int smc_pnet_del(struct sk_buff *skb, struct genl_info *info)
@@ -450,7 +567,7 @@ static int smc_pnet_dump_start(struct netlink_callback *cb)
 
 static int smc_pnet_dumpinfo(struct sk_buff *skb,
                             u32 portid, u32 seq, u32 flags,
-                            struct smc_user_pnetentry *pnetelem)
+                            struct smc_pnetentry *pnetelem)
 {
        void *hdr;
 
@@ -469,91 +586,32 @@ static int smc_pnet_dumpinfo(struct sk_buff *skb,
 static int _smc_pnet_dump(struct net *net, struct sk_buff *skb, u32 portid,
                          u32 seq, u8 *pnetid, int start_idx)
 {
-       struct smc_user_pnetentry tmp_entry;
        struct smc_pnettable *pnettable;
        struct smc_pnetentry *pnetelem;
-       struct smc_ib_device *ibdev;
-       struct smcd_dev *smcd_dev;
        struct smc_net *sn;
        int idx = 0;
-       int ibport;
 
        /* get pnettable for namespace */
        sn = net_generic(net, smc_net_id);
        pnettable = &sn->pnettable;
 
-       /* dump netdevices */
+       /* dump pnettable entries */
        read_lock(&pnettable->lock);
        list_for_each_entry(pnetelem, &pnettable->pnetlist, list) {
                if (pnetid && !smc_pnet_match(pnetelem->pnet_name, pnetid))
                        continue;
                if (idx++ < start_idx)
                        continue;
-               memset(&tmp_entry, 0, sizeof(tmp_entry));
-               memcpy(&tmp_entry.pnet_name, pnetelem->pnet_name,
-                      SMC_MAX_PNETID_LEN);
-               tmp_entry.ndev = pnetelem->ndev;
+               /* if this is not the initial namespace, dump only netdev */
+               if (net != &init_net && pnetelem->type != SMC_PNET_ETH)
+                       continue;
                if (smc_pnet_dumpinfo(skb, portid, seq, NLM_F_MULTI,
-                                     &tmp_entry)) {
+                                     pnetelem)) {
                        --idx;
                        break;
                }
        }
        read_unlock(&pnettable->lock);
-
-       /* if this is not the initial namespace, stop here */
-       if (net != &init_net)
-               return idx;
-
-       /* dump ib devices */
-       spin_lock(&smc_ib_devices.lock);
-       list_for_each_entry(ibdev, &smc_ib_devices.list, list) {
-               for (ibport = 0; ibport < SMC_MAX_PORTS; ibport++) {
-                       if (ibdev->pnetid_by_user[ibport]) {
-                               if (pnetid &&
-                                   !smc_pnet_match(ibdev->pnetid[ibport],
-                                                   pnetid))
-                                       continue;
-                               if (idx++ < start_idx)
-                                       continue;
-                               memset(&tmp_entry, 0, sizeof(tmp_entry));
-                               memcpy(&tmp_entry.pnet_name,
-                                      ibdev->pnetid[ibport],
-                                      SMC_MAX_PNETID_LEN);
-                               tmp_entry.smcibdev = ibdev;
-                               tmp_entry.ib_port = ibport + 1;
-                               if (smc_pnet_dumpinfo(skb, portid, seq,
-                                                     NLM_F_MULTI,
-                                                     &tmp_entry)) {
-                                       --idx;
-                                       break;
-                               }
-                       }
-               }
-       }
-       spin_unlock(&smc_ib_devices.lock);
-
-       /* dump smcd devices */
-       spin_lock(&smcd_dev_list.lock);
-       list_for_each_entry(smcd_dev, &smcd_dev_list.list, list) {
-               if (smcd_dev->pnetid_by_user) {
-                       if (pnetid && !smc_pnet_match(smcd_dev->pnetid, pnetid))
-                               continue;
-                       if (idx++ < start_idx)
-                               continue;
-                       memset(&tmp_entry, 0, sizeof(tmp_entry));
-                       memcpy(&tmp_entry.pnet_name, smcd_dev->pnetid,
-                              SMC_MAX_PNETID_LEN);
-                       tmp_entry.smcd_dev = smcd_dev;
-                       if (smc_pnet_dumpinfo(skb, portid, seq, NLM_F_MULTI,
-                                             &tmp_entry)) {
-                               --idx;
-                               break;
-                       }
-               }
-       }
-       spin_unlock(&smcd_dev_list.lock);
-
        return idx;
 }
 
@@ -659,6 +717,9 @@ static int smc_pnet_netdev_event(struct notifier_block *this,
        case NETDEV_UNREGISTER:
                smc_pnet_remove_by_ndev(event_dev);
                return NOTIFY_OK;
+       case NETDEV_REGISTER:
+               smc_pnet_add_by_ndev(event_dev);
+               return NOTIFY_OK;
        default:
                return NOTIFY_DONE;
        }
@@ -744,7 +805,7 @@ static int smc_pnet_find_ndev_pnetid_by_table(struct net_device *ndev,
 
        read_lock(&pnettable->lock);
        list_for_each_entry(pnetelem, &pnettable->pnetlist, list) {
-               if (ndev == pnetelem->ndev) {
+               if (pnetelem->type == SMC_PNET_ETH && ndev == pnetelem->ndev) {
                        /* get pnetid of netdev device */
                        memcpy(pnetid, pnetelem->pnet_name, SMC_MAX_PNETID_LEN);
                        rc = 0;
@@ -755,6 +816,45 @@ static int smc_pnet_find_ndev_pnetid_by_table(struct net_device *ndev,
        return rc;
 }
 
+/* find a roce device for the given pnetid */
+static void _smc_pnet_find_roce_by_pnetid(u8 *pnet_id,
+                                         struct smc_init_info *ini,
+                                         struct smc_ib_device *known_dev)
+{
+       struct smc_ib_device *ibdev;
+       int i;
+
+       ini->ib_dev = NULL;
+       spin_lock(&smc_ib_devices.lock);
+       list_for_each_entry(ibdev, &smc_ib_devices.list, list) {
+               if (ibdev == known_dev)
+                       continue;
+               for (i = 1; i <= SMC_MAX_PORTS; i++) {
+                       if (!rdma_is_port_valid(ibdev->ibdev, i))
+                               continue;
+                       if (smc_pnet_match(ibdev->pnetid[i - 1], pnet_id) &&
+                           smc_ib_port_active(ibdev, i) &&
+                           !test_bit(i - 1, ibdev->ports_going_away) &&
+                           !smc_ib_determine_gid(ibdev, i, ini->vlan_id,
+                                                 ini->ib_gid, NULL)) {
+                               ini->ib_dev = ibdev;
+                               ini->ib_port = i;
+                               goto out;
+                       }
+               }
+       }
+out:
+       spin_unlock(&smc_ib_devices.lock);
+}
+
+/* find alternate roce device with same pnet_id and vlan_id */
+void smc_pnet_find_alt_roce(struct smc_link_group *lgr,
+                           struct smc_init_info *ini,
+                           struct smc_ib_device *known_dev)
+{
+       _smc_pnet_find_roce_by_pnetid(lgr->pnet_id, ini, known_dev);
+}
+
 /* if handshake network device belongs to a roce device, return its
  * IB device and port
  */
@@ -801,8 +901,6 @@ static void smc_pnet_find_roce_by_pnetid(struct net_device *ndev,
                                         struct smc_init_info *ini)
 {
        u8 ndev_pnetid[SMC_MAX_PNETID_LEN];
-       struct smc_ib_device *ibdev;
-       int i;
 
        ndev = pnet_find_base_ndev(ndev);
        if (smc_pnetid_by_dev_port(ndev->dev.parent, ndev->dev_port,
@@ -811,25 +909,7 @@ static void smc_pnet_find_roce_by_pnetid(struct net_device *ndev,
                smc_pnet_find_rdma_dev(ndev, ini);
                return; /* pnetid could not be determined */
        }
-
-       spin_lock(&smc_ib_devices.lock);
-       list_for_each_entry(ibdev, &smc_ib_devices.list, list) {
-               for (i = 1; i <= SMC_MAX_PORTS; i++) {
-                       if (!rdma_is_port_valid(ibdev->ibdev, i))
-                               continue;
-                       if (smc_pnet_match(ibdev->pnetid[i - 1], ndev_pnetid) &&
-                           smc_ib_port_active(ibdev, i) &&
-                           !test_bit(i - 1, ibdev->ports_going_away) &&
-                           !smc_ib_determine_gid(ibdev, i, ini->vlan_id,
-                                                 ini->ib_gid, NULL)) {
-                               ini->ib_dev = ibdev;
-                               ini->ib_port = i;
-                               goto out;
-                       }
-               }
-       }
-out:
-       spin_unlock(&smc_ib_devices.lock);
+       _smc_pnet_find_roce_by_pnetid(ndev_pnetid, ini, NULL);
 }
 
 static void smc_pnet_find_ism_by_pnetid(struct net_device *ndev,
@@ -895,3 +975,60 @@ out_rel:
 out:
        return;
 }
+
+/* Lookup and apply a pnet table entry to the given ib device.
+ */
+int smc_pnetid_by_table_ib(struct smc_ib_device *smcibdev, u8 ib_port)
+{
+       char *ib_name = smcibdev->ibdev->name;
+       struct smc_pnettable *pnettable;
+       struct smc_pnetentry *tmp_pe;
+       struct smc_net *sn;
+       int rc = -ENOENT;
+
+       /* get pnettable for init namespace */
+       sn = net_generic(&init_net, smc_net_id);
+       pnettable = &sn->pnettable;
+
+       read_lock(&pnettable->lock);
+       list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) {
+               if (tmp_pe->type == SMC_PNET_IB &&
+                   !strncmp(tmp_pe->ib_name, ib_name, IB_DEVICE_NAME_MAX) &&
+                   tmp_pe->ib_port == ib_port) {
+                       smc_pnet_apply_ib(smcibdev, ib_port, tmp_pe->pnet_name);
+                       rc = 0;
+                       break;
+               }
+       }
+       read_unlock(&pnettable->lock);
+
+       return rc;
+}
+
+/* Lookup and apply a pnet table entry to the given smcd device.
+ */
+int smc_pnetid_by_table_smcd(struct smcd_dev *smcddev)
+{
+       const char *ib_name = dev_name(&smcddev->dev);
+       struct smc_pnettable *pnettable;
+       struct smc_pnetentry *tmp_pe;
+       struct smc_net *sn;
+       int rc = -ENOENT;
+
+       /* get pnettable for init namespace */
+       sn = net_generic(&init_net, smc_net_id);
+       pnettable = &sn->pnettable;
+
+       read_lock(&pnettable->lock);
+       list_for_each_entry(tmp_pe, &pnettable->pnetlist, list) {
+               if (tmp_pe->type == SMC_PNET_IB &&
+                   !strncmp(tmp_pe->ib_name, ib_name, IB_DEVICE_NAME_MAX)) {
+                       smc_pnet_apply_smcd(smcddev, tmp_pe->pnet_name);
+                       rc = 0;
+                       break;
+               }
+       }
+       read_unlock(&pnettable->lock);
+
+       return rc;
+}
index 4564e4d..811a659 100644 (file)
@@ -19,6 +19,7 @@
 struct smc_ib_device;
 struct smcd_dev;
 struct smc_init_info;
+struct smc_link_group;
 
 /**
  * struct smc_pnettable - SMC PNET table anchor
@@ -46,5 +47,9 @@ void smc_pnet_exit(void);
 void smc_pnet_net_exit(struct net *net);
 void smc_pnet_find_roce_resource(struct sock *sk, struct smc_init_info *ini);
 void smc_pnet_find_ism_resource(struct sock *sk, struct smc_init_info *ini);
-
+int smc_pnetid_by_table_ib(struct smc_ib_device *smcibdev, u8 ib_port);
+int smc_pnetid_by_table_smcd(struct smcd_dev *smcd);
+void smc_pnet_find_alt_roce(struct smc_link_group *lgr,
+                           struct smc_init_info *ini,
+                           struct smc_ib_device *known_dev);
 #endif
index 9f1ade8..54ba044 100644 (file)
@@ -269,22 +269,21 @@ static int smc_tx_rdma_write(struct smc_connection *conn, int peer_rmbe_offset,
                             int num_sges, struct ib_rdma_wr *rdma_wr)
 {
        struct smc_link_group *lgr = conn->lgr;
-       struct smc_link *link;
+       struct smc_link *link = conn->lnk;
        int rc;
 
-       link = &lgr->lnk[SMC_SINGLE_LINK];
        rdma_wr->wr.wr_id = smc_wr_tx_get_next_wr_id(link);
        rdma_wr->wr.num_sge = num_sges;
        rdma_wr->remote_addr =
-               lgr->rtokens[conn->rtoken_idx][SMC_SINGLE_LINK].dma_addr +
+               lgr->rtokens[conn->rtoken_idx][link->link_idx].dma_addr +
                /* RMBE within RMB */
                conn->tx_off +
                /* offset within RMBE */
                peer_rmbe_offset;
-       rdma_wr->rkey = lgr->rtokens[conn->rtoken_idx][SMC_SINGLE_LINK].rkey;
+       rdma_wr->rkey = lgr->rtokens[conn->rtoken_idx][link->link_idx].rkey;
        rc = ib_post_send(link->roce_qp, &rdma_wr->wr, NULL);
        if (rc)
-               smc_lgr_terminate_sched(lgr);
+               smcr_link_down_cond_sched(link);
        return rc;
 }
 
@@ -310,8 +309,10 @@ static int smcr_tx_rdma_writes(struct smc_connection *conn, size_t len,
                               size_t dst_off, size_t dst_len,
                               struct smc_rdma_wr *wr_rdma_buf)
 {
+       struct smc_link *link = conn->lnk;
+
        dma_addr_t dma_addr =
-               sg_dma_address(conn->sndbuf_desc->sgt[SMC_SINGLE_LINK].sgl);
+               sg_dma_address(conn->sndbuf_desc->sgt[link->link_idx].sgl);
        int src_len_sum = src_len, dst_len_sum = dst_len;
        int sent_count = src_off;
        int srcchunk, dstchunk;
@@ -481,12 +482,13 @@ static int smc_tx_rdma_writes(struct smc_connection *conn,
 static int smcr_tx_sndbuf_nonempty(struct smc_connection *conn)
 {
        struct smc_cdc_producer_flags *pflags = &conn->local_tx_ctrl.prod_flags;
+       struct smc_link *link = conn->lnk;
        struct smc_rdma_wr *wr_rdma_buf;
        struct smc_cdc_tx_pend *pend;
        struct smc_wr_buf *wr_buf;
        int rc;
 
-       rc = smc_cdc_get_free_slot(conn, &wr_buf, &wr_rdma_buf, &pend);
+       rc = smc_cdc_get_free_slot(conn, link, &wr_buf, &wr_rdma_buf, &pend);
        if (rc < 0) {
                if (rc == -EBUSY) {
                        struct smc_sock *smc =
@@ -504,10 +506,17 @@ static int smcr_tx_sndbuf_nonempty(struct smc_connection *conn)
        }
 
        spin_lock_bh(&conn->send_lock);
+       if (link != conn->lnk) {
+               /* link of connection changed, tx_work will restart */
+               smc_wr_tx_put_slot(link,
+                                  (struct smc_wr_tx_pend_priv *)pend);
+               rc = -ENOLINK;
+               goto out_unlock;
+       }
        if (!pflags->urg_data_present) {
                rc = smc_tx_rdma_writes(conn, wr_rdma_buf);
                if (rc) {
-                       smc_wr_tx_put_slot(&conn->lgr->lnk[SMC_SINGLE_LINK],
+                       smc_wr_tx_put_slot(link,
                                           (struct smc_wr_tx_pend_priv *)pend);
                        goto out_unlock;
                }
index 337ee52..7239ba9 100644 (file)
@@ -44,6 +44,7 @@ struct smc_wr_tx_pend {       /* control data for a pending send request */
        struct smc_link         *link;
        u32                     idx;
        struct smc_wr_tx_pend_priv priv;
+       u8                      compl_requested;
 };
 
 /******************************** send queue *********************************/
@@ -61,7 +62,7 @@ static inline bool smc_wr_is_tx_pend(struct smc_link *link)
 }
 
 /* wait till all pending tx work requests on the given link are completed */
-static inline int smc_wr_tx_wait_no_pending_sends(struct smc_link *link)
+int smc_wr_tx_wait_no_pending_sends(struct smc_link *link)
 {
        if (wait_event_timeout(link->wr_tx_wait, !smc_wr_is_tx_pend(link),
                               SMC_WR_TX_WAIT_PENDING_TIME))
@@ -103,6 +104,8 @@ static inline void smc_wr_tx_process_cqe(struct ib_wc *wc)
        if (pnd_snd_idx == link->wr_tx_cnt)
                return;
        link->wr_tx_pends[pnd_snd_idx].wc_status = wc->status;
+       if (link->wr_tx_pends[pnd_snd_idx].compl_requested)
+               complete(&link->wr_tx_compl[pnd_snd_idx]);
        memcpy(&pnd_snd, &link->wr_tx_pends[pnd_snd_idx], sizeof(pnd_snd));
        /* clear the full struct smc_wr_tx_pend including .priv */
        memset(&link->wr_tx_pends[pnd_snd_idx], 0,
@@ -120,8 +123,8 @@ static inline void smc_wr_tx_process_cqe(struct ib_wc *wc)
                               sizeof(link->wr_tx_bufs[i]));
                        clear_bit(i, link->wr_tx_mask);
                }
-               /* terminate connections of this link group abnormally */
-               smc_lgr_terminate_sched(smc_get_lgr(link));
+               /* terminate link */
+               smcr_link_down_cond_sched(link);
        }
        if (pnd_snd.handler)
                pnd_snd.handler(&pnd_snd.priv, link, wc->status);
@@ -207,13 +210,13 @@ int smc_wr_tx_get_free_slot(struct smc_link *link,
        } else {
                rc = wait_event_interruptible_timeout(
                        link->wr_tx_wait,
-                       link->state == SMC_LNK_INACTIVE ||
+                       !smc_link_usable(link) ||
                        lgr->terminating ||
                        (smc_wr_tx_get_free_slot_index(link, &idx) != -EBUSY),
                        SMC_WR_TX_WAIT_FREE_SLOT_TIME);
                if (!rc) {
-                       /* timeout - terminate connections */
-                       smc_lgr_terminate_sched(lgr);
+                       /* timeout - terminate link */
+                       smcr_link_down_cond_sched(link);
                        return -EPIPE;
                }
                if (idx == link->wr_tx_cnt)
@@ -270,11 +273,38 @@ int smc_wr_tx_send(struct smc_link *link, struct smc_wr_tx_pend_priv *priv)
        rc = ib_post_send(link->roce_qp, &link->wr_tx_ibs[pend->idx], NULL);
        if (rc) {
                smc_wr_tx_put_slot(link, priv);
-               smc_lgr_terminate_sched(smc_get_lgr(link));
+               smcr_link_down_cond_sched(link);
        }
        return rc;
 }
 
+/* Send prepared WR slot via ib_post_send and wait for send completion
+ * notification.
+ * @priv: pointer to smc_wr_tx_pend_priv identifying prepared message buffer
+ */
+int smc_wr_tx_send_wait(struct smc_link *link, struct smc_wr_tx_pend_priv *priv,
+                       unsigned long timeout)
+{
+       struct smc_wr_tx_pend *pend;
+       int rc;
+
+       pend = container_of(priv, struct smc_wr_tx_pend, priv);
+       pend->compl_requested = 1;
+       init_completion(&link->wr_tx_compl[pend->idx]);
+
+       rc = smc_wr_tx_send(link, priv);
+       if (rc)
+               return rc;
+       /* wait for completion by smc_wr_tx_process_cqe() */
+       rc = wait_for_completion_interruptible_timeout(
+                                       &link->wr_tx_compl[pend->idx], timeout);
+       if (rc <= 0)
+               rc = -ENODATA;
+       if (rc > 0)
+               rc = 0;
+       return rc;
+}
+
 /* Register a memory region and wait for result. */
 int smc_wr_reg_send(struct smc_link *link, struct ib_mr *mr)
 {
@@ -294,8 +324,8 @@ int smc_wr_reg_send(struct smc_link *link, struct ib_mr *mr)
                                              (link->wr_reg_state != POSTED),
                                              SMC_WR_REG_MR_WAIT_TIME);
        if (!rc) {
-               /* timeout - terminate connections */
-               smc_lgr_terminate_sched(smc_get_lgr(link));
+               /* timeout - terminate link */
+               smcr_link_down_cond_sched(link);
                return -EPIPE;
        }
        if (rc == -ERESTARTSYS)
@@ -393,10 +423,7 @@ static inline void smc_wr_rx_process_cqes(struct ib_wc wc[], int num)
                        case IB_WC_RETRY_EXC_ERR:
                        case IB_WC_RNR_RETRY_EXC_ERR:
                        case IB_WC_WR_FLUSH_ERR:
-                               /* terminate connections of this link group
-                                * abnormally
-                                */
-                               smc_lgr_terminate_sched(smc_get_lgr(link));
+                               smcr_link_down_cond_sched(link);
                                break;
                        default:
                                smc_wr_rx_post(link); /* refill WR RX */
@@ -558,6 +585,8 @@ void smc_wr_free_link(struct smc_link *lnk)
 
 void smc_wr_free_link_mem(struct smc_link *lnk)
 {
+       kfree(lnk->wr_tx_compl);
+       lnk->wr_tx_compl = NULL;
        kfree(lnk->wr_tx_pends);
        lnk->wr_tx_pends = NULL;
        kfree(lnk->wr_tx_mask);
@@ -628,8 +657,15 @@ int smc_wr_alloc_link_mem(struct smc_link *link)
                                    GFP_KERNEL);
        if (!link->wr_tx_pends)
                goto no_mem_wr_tx_mask;
+       link->wr_tx_compl = kcalloc(SMC_WR_BUF_CNT,
+                                   sizeof(link->wr_tx_compl[0]),
+                                   GFP_KERNEL);
+       if (!link->wr_tx_compl)
+               goto no_mem_wr_tx_pends;
        return 0;
 
+no_mem_wr_tx_pends:
+       kfree(link->wr_tx_pends);
 no_mem_wr_tx_mask:
        kfree(link->wr_tx_mask);
 no_mem_wr_rx_sges:
index 3ac99c8..423b870 100644 (file)
@@ -101,11 +101,14 @@ int smc_wr_tx_put_slot(struct smc_link *link,
                       struct smc_wr_tx_pend_priv *wr_pend_priv);
 int smc_wr_tx_send(struct smc_link *link,
                   struct smc_wr_tx_pend_priv *wr_pend_priv);
+int smc_wr_tx_send_wait(struct smc_link *link, struct smc_wr_tx_pend_priv *priv,
+                       unsigned long timeout);
 void smc_wr_tx_cq_handler(struct ib_cq *ib_cq, void *cq_context);
 void smc_wr_tx_dismiss_slots(struct smc_link *lnk, u8 wr_rx_hdr_type,
                             smc_wr_tx_filter filter,
                             smc_wr_tx_dismisser dismisser,
                             unsigned long data);
+int smc_wr_tx_wait_no_pending_sends(struct smc_link *link);
 
 int smc_wr_rx_register_handler(struct smc_wr_rx_handler *handler);
 int smc_wr_rx_post_init(struct smc_link *link);
index af0ddd2..baef5ee 100644 (file)
@@ -529,7 +529,6 @@ void cache_purge(struct cache_detail *detail)
 {
        struct cache_head *ch = NULL;
        struct hlist_head *head = NULL;
-       struct hlist_node *tmp = NULL;
        int i = 0;
 
        spin_lock(&detail->hash_lock);
@@ -541,7 +540,9 @@ void cache_purge(struct cache_detail *detail)
        dprintk("RPC: %d entries in %s cache\n", detail->entries, detail->name);
        for (i = 0; i < detail->hash_size; i++) {
                head = &detail->hash_table[i];
-               hlist_for_each_entry_safe(ch, tmp, head, cache_list) {
+               while (!hlist_empty(head)) {
+                       ch = hlist_entry(head->first, struct cache_head,
+                                        cache_list);
                        sunrpc_begin_cache_remove_entry(ch, detail);
                        spin_unlock(&detail->hash_lock);
                        sunrpc_end_cache_remove_entry(ch, detail);
index 325a085..8350d3a 100644 (file)
@@ -880,6 +880,20 @@ EXPORT_SYMBOL_GPL(rpc_shutdown_client);
 /*
  * Free an RPC client
  */
+static void rpc_free_client_work(struct work_struct *work)
+{
+       struct rpc_clnt *clnt = container_of(work, struct rpc_clnt, cl_work);
+
+       /* These might block on processes that might allocate memory,
+        * so they cannot be called in rpciod, so they are handled separately
+        * here.
+        */
+       rpc_clnt_debugfs_unregister(clnt);
+       rpc_clnt_remove_pipedir(clnt);
+
+       kfree(clnt);
+       rpciod_down();
+}
 static struct rpc_clnt *
 rpc_free_client(struct rpc_clnt *clnt)
 {
@@ -890,17 +904,16 @@ rpc_free_client(struct rpc_clnt *clnt)
                        rcu_dereference(clnt->cl_xprt)->servername);
        if (clnt->cl_parent != clnt)
                parent = clnt->cl_parent;
-       rpc_clnt_debugfs_unregister(clnt);
-       rpc_clnt_remove_pipedir(clnt);
        rpc_unregister_client(clnt);
        rpc_free_iostats(clnt->cl_metrics);
        clnt->cl_metrics = NULL;
        xprt_put(rcu_dereference_raw(clnt->cl_xprt));
        xprt_iter_destroy(&clnt->cl_xpi);
-       rpciod_down();
        put_cred(clnt->cl_cred);
        rpc_free_clid(clnt);
-       kfree(clnt);
+
+       INIT_WORK(&clnt->cl_work, rpc_free_client_work);
+       schedule_work(&clnt->cl_work);
        return parent;
 }
 
@@ -2808,8 +2821,7 @@ int rpc_clnt_test_and_add_xprt(struct rpc_clnt *clnt,
        task = rpc_call_null_helper(clnt, xprt, NULL,
                        RPC_TASK_SOFT|RPC_TASK_SOFTCONN|RPC_TASK_ASYNC|RPC_TASK_NULLCREDS,
                        &rpc_cb_add_xprt_call_ops, data);
-       if (IS_ERR(task))
-               return PTR_ERR(task);
+
        rpc_put_task(task);
 success:
        return 1;
index e27e353..2284ff0 100644 (file)
@@ -908,9 +908,6 @@ int svc_send(struct svc_rqst *rqstp)
        if (!xprt)
                goto out;
 
-       /* release the receive skb before sending the reply */
-       xprt->xpt_ops->xpo_release_rqst(rqstp);
-
        /* calculate over-all length */
        xb = &rqstp->rq_res;
        xb->len = xb->head[0].iov_len +
@@ -1040,6 +1037,8 @@ static void svc_delete_xprt(struct svc_xprt *xprt)
 
        dprintk("svc: svc_delete_xprt(%p)\n", xprt);
        xprt->xpt_ops->xpo_detach(xprt);
+       if (xprt->xpt_bc_xprt)
+               xprt->xpt_bc_xprt->ops->close(xprt->xpt_bc_xprt);
 
        spin_lock_bh(&serv->sv_lock);
        list_del_init(&xprt->xpt_list);
index 519cf9c..023514e 100644 (file)
@@ -527,6 +527,8 @@ static int svc_udp_sendto(struct svc_rqst *rqstp)
        unsigned int uninitialized_var(sent);
        int err;
 
+       svc_release_udp_skb(rqstp);
+
        svc_set_cmsg_data(rqstp, cmh);
 
        err = xprt_sock_sendmsg(svsk->sk_sock, &msg, xdr, 0, 0, &sent);
@@ -1076,6 +1078,8 @@ static int svc_tcp_sendto(struct svc_rqst *rqstp)
        unsigned int uninitialized_var(sent);
        int err;
 
+       svc_release_skb(rqstp);
+
        err = xprt_sock_sendmsg(svsk->sk_sock, &msg, xdr, 0, marker, &sent);
        xdr_free_bvec(xdr);
        if (err < 0 || sent != (xdr->len + sizeof(marker)))
index d75f17b..999eee1 100644 (file)
@@ -60,7 +60,7 @@ rpc_unregister_sysctl(void)
 }
 
 static int proc_do_xprt(struct ctl_table *table, int write,
-                       void __user *buffer, size_t *lenp, loff_t *ppos)
+                       void *buffer, size_t *lenp, loff_t *ppos)
 {
        char tmpbuf[256];
        size_t len;
@@ -70,15 +70,15 @@ static int proc_do_xprt(struct ctl_table *table, int write,
                return 0;
        }
        len = svc_print_xprts(tmpbuf, sizeof(tmpbuf));
-       return simple_read_from_buffer(buffer, *lenp, ppos, tmpbuf, len);
+       return memory_read_from_buffer(buffer, *lenp, ppos, tmpbuf, len);
 }
 
 static int
-proc_dodebug(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+proc_dodebug(struct ctl_table *table, int write, void *buffer, size_t *lenp,
+            loff_t *ppos)
 {
-       char            tmpbuf[20], c, *s = NULL;
-       char __user *p;
+       char            tmpbuf[20], *s = NULL;
+       char *p;
        unsigned int    value;
        size_t          left, len;
 
@@ -90,18 +90,17 @@ proc_dodebug(struct ctl_table *table, int write,
        left = *lenp;
 
        if (write) {
-               if (!access_ok(buffer, left))
-                       return -EFAULT;
                p = buffer;
-               while (left && __get_user(c, p) >= 0 && isspace(c))
-                       left--, p++;
+               while (left && isspace(*p)) {
+                       left--;
+                       p++;
+               }
                if (!left)
                        goto done;
 
                if (left > sizeof(tmpbuf) - 1)
                        return -EINVAL;
-               if (copy_from_user(tmpbuf, p, left))
-                       return -EFAULT;
+               memcpy(tmpbuf, p, left);
                tmpbuf[left] = '\0';
 
                value = simple_strtol(tmpbuf, &s, 0);
@@ -121,11 +120,9 @@ proc_dodebug(struct ctl_table *table, int write,
                len = sprintf(tmpbuf, "0x%04x", *(unsigned int *) table->data);
                if (len > left)
                        len = left;
-               if (copy_to_user(buffer, tmpbuf, len))
-                       return -EFAULT;
+               memcpy(buffer, tmpbuf, len);
                if ((left -= len) > 0) {
-                       if (put_user('\n', (char __user *)buffer + len))
-                               return -EFAULT;
+                       *((char *)buffer + len) = '\n';
                        left--;
                }
        }
index 4a81e69..3c627dc 100644 (file)
@@ -388,7 +388,9 @@ static int rpcrdma_encode_read_list(struct rpcrdma_xprt *r_xprt,
        } while (nsegs);
 
 done:
-       return xdr_stream_encode_item_absent(xdr);
+       if (xdr_stream_encode_item_absent(xdr) < 0)
+               return -EMSGSIZE;
+       return 0;
 }
 
 /* Register and XDR encode the Write list. Supports encoding a list
@@ -454,7 +456,9 @@ static int rpcrdma_encode_write_list(struct rpcrdma_xprt *r_xprt,
        *segcount = cpu_to_be32(nchunks);
 
 done:
-       return xdr_stream_encode_item_absent(xdr);
+       if (xdr_stream_encode_item_absent(xdr) < 0)
+               return -EMSGSIZE;
+       return 0;
 }
 
 /* Register and XDR encode the Reply chunk. Supports encoding an array
@@ -480,8 +484,11 @@ static int rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt,
        int nsegs, nchunks;
        __be32 *segcount;
 
-       if (wtype != rpcrdma_replych)
-               return xdr_stream_encode_item_absent(xdr);
+       if (wtype != rpcrdma_replych) {
+               if (xdr_stream_encode_item_absent(xdr) < 0)
+                       return -EMSGSIZE;
+               return 0;
+       }
 
        seg = req->rl_segments;
        nsegs = rpcrdma_convert_iovs(r_xprt, &rqst->rq_rcv_buf, 0, wtype, seg);
index 97bca50..526da5d 100644 (file)
@@ -80,8 +80,7 @@ atomic_t rdma_stat_sq_prod;
  * current value.
  */
 static int read_reset_stat(struct ctl_table *table, int write,
-                          void __user *buffer, size_t *lenp,
-                          loff_t *ppos)
+                          void *buffer, size_t *lenp, loff_t *ppos)
 {
        atomic_t *stat = (atomic_t *)table->data;
 
@@ -103,8 +102,8 @@ static int read_reset_stat(struct ctl_table *table, int write,
                len -= *ppos;
                if (len > *lenp)
                        len = *lenp;
-               if (len && copy_to_user(buffer, str_buf, len))
-                       return -EFAULT;
+               if (len)
+                       memcpy(buffer, str_buf, len);
                *lenp = len;
                *ppos += len;
        }
index d510a3a..af7eb8d 100644 (file)
@@ -244,6 +244,8 @@ static void
 xprt_rdma_bc_close(struct rpc_xprt *xprt)
 {
        dprintk("svcrdma: %s: xprt %p\n", __func__, xprt);
+
+       xprt_disconnect_done(xprt);
        xprt->cwnd = RPC_CWNDSHIFT;
 }
 
index 54469b7..efa5fcb 100644 (file)
@@ -223,6 +223,26 @@ void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma,
                svc_rdma_recv_ctxt_destroy(rdma, ctxt);
 }
 
+/**
+ * svc_rdma_release_rqst - Release transport-specific per-rqst resources
+ * @rqstp: svc_rqst being released
+ *
+ * Ensure that the recv_ctxt is released whether or not a Reply
+ * was sent. For example, the client could close the connection,
+ * or svc_process could drop an RPC, before the Reply is sent.
+ */
+void svc_rdma_release_rqst(struct svc_rqst *rqstp)
+{
+       struct svc_rdma_recv_ctxt *ctxt = rqstp->rq_xprt_ctxt;
+       struct svc_xprt *xprt = rqstp->rq_xprt;
+       struct svcxprt_rdma *rdma =
+               container_of(xprt, struct svcxprt_rdma, sc_xprt);
+
+       rqstp->rq_xprt_ctxt = NULL;
+       if (ctxt)
+               svc_rdma_recv_ctxt_put(rdma, ctxt);
+}
+
 static int __svc_rdma_post_recv(struct svcxprt_rdma *rdma,
                                struct svc_rdma_recv_ctxt *ctxt)
 {
@@ -820,6 +840,8 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
        __be32 *p;
        int ret;
 
+       rqstp->rq_xprt_ctxt = NULL;
+
        spin_lock(&rdma_xprt->sc_rq_dto_lock);
        ctxt = svc_rdma_next_recv_ctxt(&rdma_xprt->sc_read_complete_q);
        if (ctxt) {
index bd7c195..23c2d3c 100644 (file)
@@ -323,8 +323,6 @@ static int svc_rdma_post_chunk_ctxt(struct svc_rdma_chunk_ctxt *cc)
                if (atomic_sub_return(cc->cc_sqecount,
                                      &rdma->sc_sq_avail) > 0) {
                        ret = ib_post_send(rdma->sc_qp, first_wr, &bad_wr);
-                       trace_svcrdma_post_rw(&cc->cc_cqe,
-                                             cc->cc_sqecount, ret);
                        if (ret)
                                break;
                        return 0;
@@ -337,6 +335,7 @@ static int svc_rdma_post_chunk_ctxt(struct svc_rdma_chunk_ctxt *cc)
                trace_svcrdma_sq_retry(rdma);
        } while (1);
 
+       trace_svcrdma_sq_post_err(rdma, ret);
        set_bit(XPT_CLOSE, &xprt->xpt_flags);
 
        /* If even one was posted, there will be a completion. */
index 90cba30..b6c8643 100644 (file)
@@ -322,15 +322,17 @@ int svc_rdma_send(struct svcxprt_rdma *rdma, struct ib_send_wr *wr)
                }
 
                svc_xprt_get(&rdma->sc_xprt);
+               trace_svcrdma_post_send(wr);
                ret = ib_post_send(rdma->sc_qp, wr, NULL);
-               trace_svcrdma_post_send(wr, ret);
-               if (ret) {
-                       set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags);
-                       svc_xprt_put(&rdma->sc_xprt);
-                       wake_up(&rdma->sc_send_wait);
-               }
-               break;
+               if (ret)
+                       break;
+               return 0;
        }
+
+       trace_svcrdma_sq_post_err(rdma, ret);
+       set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags);
+       svc_xprt_put(&rdma->sc_xprt);
+       wake_up(&rdma->sc_send_wait);
        return ret;
 }
 
@@ -924,12 +926,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
        ret = svc_rdma_send_reply_msg(rdma, sctxt, rctxt, rqstp);
        if (ret < 0)
                goto err1;
-       ret = 0;
-
-out:
-       rqstp->rq_xprt_ctxt = NULL;
-       svc_rdma_recv_ctxt_put(rdma, rctxt);
-       return ret;
+       return 0;
 
  err2:
        if (ret != -E2BIG && ret != -EINVAL)
@@ -938,16 +935,14 @@ out:
        ret = svc_rdma_send_error_msg(rdma, sctxt, rqstp);
        if (ret < 0)
                goto err1;
-       ret = 0;
-       goto out;
+       return 0;
 
  err1:
        svc_rdma_send_ctxt_put(rdma, sctxt);
  err0:
        trace_svcrdma_send_failed(rqstp, ret);
        set_bit(XPT_CLOSE, &xprt->xpt_flags);
-       ret = -ENOTCONN;
-       goto out;
+       return -ENOTCONN;
 }
 
 /**
index 8bb9998..ea54785 100644 (file)
@@ -71,7 +71,6 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
                                        struct sockaddr *sa, int salen,
                                        int flags);
 static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt);
-static void svc_rdma_release_rqst(struct svc_rqst *);
 static void svc_rdma_detach(struct svc_xprt *xprt);
 static void svc_rdma_free(struct svc_xprt *xprt);
 static int svc_rdma_has_wspace(struct svc_xprt *xprt);
@@ -552,10 +551,6 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
        return NULL;
 }
 
-static void svc_rdma_release_rqst(struct svc_rqst *rqstp)
-{
-}
-
 /*
  * When connected, an svc_xprt has at least two references:
  *
index cdd84c0..05c4d3a 100644 (file)
@@ -289,6 +289,7 @@ rpcrdma_cm_event_handler(struct rdma_cm_id *id, struct rdma_cm_event *event)
        case RDMA_CM_EVENT_DISCONNECTED:
                ep->re_connect_status = -ECONNABORTED;
 disconnected:
+               xprt_force_disconnect(xprt);
                return rpcrdma_ep_destroy(ep);
        default:
                break;
@@ -1355,8 +1356,8 @@ int rpcrdma_post_sends(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
                --ep->re_send_count;
        }
 
+       trace_xprtrdma_post_send(req);
        rc = frwr_send(r_xprt, req);
-       trace_xprtrdma_post_send(req, rc);
        if (rc)
                return -ENOTCONN;
        return 0;
index 0bda8a7..845d0be 100644 (file)
@@ -2584,6 +2584,7 @@ static int bc_send_request(struct rpc_rqst *req)
 
 static void bc_close(struct rpc_xprt *xprt)
 {
+       xprt_disconnect_done(xprt);
 }
 
 /*
index c8c47fc..8c47ded 100644 (file)
@@ -1712,6 +1712,7 @@ exit:
        case -EBUSY:
                this_cpu_inc(stats->stat[STAT_ASYNC]);
                *skb = NULL;
+               tipc_aead_put(aead);
                return rc;
        default:
                this_cpu_inc(stats->stat[STAT_NOK]);
index 467c53a..d4675e9 100644 (file)
@@ -1065,7 +1065,7 @@ static void tipc_link_update_cwin(struct tipc_link *l, int released,
        /* Enter fast recovery */
        if (unlikely(retransmitted)) {
                l->ssthresh = max_t(u16, l->window / 2, 300);
-               l->window = l->ssthresh;
+               l->window = min_t(u16, l->ssthresh, l->window);
                return;
        }
        /* Enter slow start */
index 10292c9..803a3a6 100644 (file)
@@ -2038,6 +2038,7 @@ void tipc_rcv(struct net *net, struct sk_buff *skb, struct tipc_bearer *b)
                n = tipc_node_find_by_id(net, ehdr->id);
        }
        tipc_crypto_rcv(net, (n) ? n->crypto_rx : NULL, &skb, b);
+       tipc_node_put(n);
        if (!skb)
                return;
 
@@ -2090,7 +2091,7 @@ rcv:
        /* Check/update node state before receiving */
        if (unlikely(skb)) {
                if (unlikely(skb_linearize(skb)))
-                       goto discard;
+                       goto out_node_put;
                tipc_node_write_lock(n);
                if (tipc_node_check_state(n, skb, bearer_id, &xmitq)) {
                        if (le->link) {
@@ -2119,6 +2120,7 @@ rcv:
        if (!skb_queue_empty(&xmitq))
                tipc_bearer_xmit(net, bearer_id, &xmitq, &le->maddr, n);
 
+out_node_put:
        tipc_node_put(n);
 discard:
        kfree_skb(skb);
index 3a12fc1..73dbed0 100644 (file)
@@ -402,10 +402,11 @@ static int tipc_conn_rcv_from_sock(struct tipc_conn *con)
                read_lock_bh(&sk->sk_callback_lock);
                ret = tipc_conn_rcv_sub(srv, con, &s);
                read_unlock_bh(&sk->sk_callback_lock);
+               if (!ret)
+                       return 0;
        }
-       if (ret < 0)
-               tipc_conn_close(con);
 
+       tipc_conn_close(con);
        return ret;
 }
 
index 156efce..0e98900 100644 (file)
@@ -56,9 +56,9 @@ enum {
        TLS_NUM_PROTS,
 };
 
-static struct proto *saved_tcpv6_prot;
+static const struct proto *saved_tcpv6_prot;
 static DEFINE_MUTEX(tcpv6_prot_mutex);
-static struct proto *saved_tcpv4_prot;
+static const struct proto *saved_tcpv4_prot;
 static DEFINE_MUTEX(tcpv4_prot_mutex);
 static struct proto tls_prots[TLS_NUM_PROTS][TLS_NUM_CONFIG][TLS_NUM_CONFIG];
 static struct proto_ops tls_sw_proto_ops;
index c98e602..e23f94a 100644 (file)
@@ -800,6 +800,8 @@ static int bpf_exec_tx_verdict(struct sk_msg *msg, struct sock *sk,
                        *copied -= sk_msg_free(sk, msg);
                        tls_free_open_rec(sk);
                }
+               if (psock)
+                       sk_psock_put(sk, psock);
                return err;
        }
 more_data:
@@ -2081,8 +2083,9 @@ static void tls_data_ready(struct sock *sk)
        strp_data_ready(&ctx->strp);
 
        psock = sk_psock_get(sk);
-       if (psock && !list_empty(&psock->ingress_msg)) {
-               ctx->saved_data_ready(sk);
+       if (psock) {
+               if (!list_empty(&psock->ingress_msg))
+                       ctx->saved_data_ready(sk);
                sk_psock_put(sk, psock);
        }
 }
index 709038a..69efc89 100644 (file)
@@ -157,7 +157,11 @@ static struct sk_buff *virtio_transport_build_skb(void *opaque)
 
 void virtio_transport_deliver_tap_pkt(struct virtio_vsock_pkt *pkt)
 {
+       if (pkt->tap_delivered)
+               return;
+
        vsock_deliver_tap(virtio_transport_build_skb, pkt);
+       pkt->tap_delivered = true;
 }
 EXPORT_SYMBOL_GPL(virtio_transport_deliver_tap_pkt);
 
index 5fa4021..5194144 100644 (file)
@@ -253,6 +253,8 @@ static int validate_ie_attr(const struct nlattr *attr,
 }
 
 /* policy for the attributes */
+static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR];
+
 static const struct nla_policy
 nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = {
        [NL80211_FTM_RESP_ATTR_ENABLED] = { .type = NLA_FLAG, },
@@ -296,11 +298,7 @@ nl80211_pmsr_req_attr_policy[NL80211_PMSR_REQ_ATTR_MAX + 1] = {
 static const struct nla_policy
 nl80211_psmr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = {
        [NL80211_PMSR_PEER_ATTR_ADDR] = NLA_POLICY_ETH_ADDR,
-       /*
-        * we could specify this again to be the top-level policy,
-        * but that would open us up to recursion problems ...
-        */
-       [NL80211_PMSR_PEER_ATTR_CHAN] = { .type = NLA_NESTED },
+       [NL80211_PMSR_PEER_ATTR_CHAN] = NLA_POLICY_NESTED(nl80211_policy),
        [NL80211_PMSR_PEER_ATTR_REQ] =
                NLA_POLICY_NESTED(nl80211_pmsr_req_attr_policy),
        [NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT },
@@ -347,7 +345,7 @@ nl80211_tid_config_attr_policy[NL80211_TID_CONFIG_ATTR_MAX + 1] = {
                        NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE),
 };
 
-const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD },
        [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
        [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
@@ -378,11 +376,8 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
        [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
 
-       [NL80211_ATTR_MAC] = { .type = NLA_EXACT_LEN_WARN, .len = ETH_ALEN },
-       [NL80211_ATTR_PREV_BSSID] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = ETH_ALEN
-       },
+       [NL80211_ATTR_MAC] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
+       [NL80211_ATTR_PREV_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
 
        [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
        [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
@@ -434,10 +429,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
        [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
 
-       [NL80211_ATTR_HT_CAPABILITY] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = NL80211_HT_CAPABILITY_LEN
-       },
+       [NL80211_ATTR_HT_CAPABILITY] = NLA_POLICY_EXACT_LEN_WARN(NL80211_HT_CAPABILITY_LEN),
 
        [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
        [NL80211_ATTR_IE] = NLA_POLICY_VALIDATE_FN(NLA_BINARY,
@@ -468,10 +460,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
        [NL80211_ATTR_PID] = { .type = NLA_U32 },
        [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
-       [NL80211_ATTR_PMKID] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = WLAN_PMKID_LEN
-       },
+       [NL80211_ATTR_PMKID] = NLA_POLICY_EXACT_LEN_WARN(WLAN_PMKID_LEN),
        [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
        [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
        [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
@@ -535,10 +524,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_WDEV] = { .type = NLA_U64 },
        [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
        [NL80211_ATTR_AUTH_DATA] = { .type = NLA_BINARY, },
-       [NL80211_ATTR_VHT_CAPABILITY] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = NL80211_VHT_CAPABILITY_LEN
-       },
+       [NL80211_ATTR_VHT_CAPABILITY] = NLA_POLICY_EXACT_LEN_WARN(NL80211_VHT_CAPABILITY_LEN),
        [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
        [NL80211_ATTR_P2P_CTWINDOW] = NLA_POLICY_MAX(NLA_U8, 127),
        [NL80211_ATTR_P2P_OPPPS] = NLA_POLICY_MAX(NLA_U8, 1),
@@ -576,10 +562,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
        [NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY,
                                   .len = IEEE80211_QOS_MAP_LEN_MAX },
-       [NL80211_ATTR_MAC_HINT] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = ETH_ALEN
-       },
+       [NL80211_ATTR_MAC_HINT] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
        [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
        [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
        [NL80211_ATTR_SOCKET_OWNER] = { .type = NLA_FLAG },
@@ -591,10 +574,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
        [NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
        [NL80211_ATTR_OPER_CLASS] = { .type = NLA_U8 },
-       [NL80211_ATTR_MAC_MASK] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = ETH_ALEN
-       },
+       [NL80211_ATTR_MAC_MASK] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
        [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
        [NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 },
        [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
@@ -606,21 +586,15 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_MU_MIMO_GROUP_DATA] = {
                .len = VHT_MUMIMO_GROUPS_DATA_LEN
        },
-       [NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = ETH_ALEN
-       },
+       [NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
        [NL80211_ATTR_NAN_MASTER_PREF] = NLA_POLICY_MIN(NLA_U8, 1),
        [NL80211_ATTR_BANDS] = { .type = NLA_U32 },
        [NL80211_ATTR_NAN_FUNC] = { .type = NLA_NESTED },
        [NL80211_ATTR_FILS_KEK] = { .type = NLA_BINARY,
                                    .len = FILS_MAX_KEK_LEN },
-       [NL80211_ATTR_FILS_NONCES] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = 2 * FILS_NONCE_LEN
-       },
+       [NL80211_ATTR_FILS_NONCES] = NLA_POLICY_EXACT_LEN_WARN(2 * FILS_NONCE_LEN),
        [NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED] = { .type = NLA_FLAG, },
-       [NL80211_ATTR_BSSID] = { .type = NLA_EXACT_LEN_WARN, .len = ETH_ALEN },
+       [NL80211_ATTR_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
        [NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] = { .type = NLA_S8 },
        [NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST] = {
                .len = sizeof(struct nl80211_bss_select_rssi_adjust)
@@ -633,7 +607,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] = { .type = NLA_U16 },
        [NL80211_ATTR_FILS_ERP_RRK] = { .type = NLA_BINARY,
                                        .len = FILS_ERP_MAX_RRK_LEN },
-       [NL80211_ATTR_FILS_CACHE_ID] = { .type = NLA_EXACT_LEN_WARN, .len = 2 },
+       [NL80211_ATTR_FILS_CACHE_ID] = NLA_POLICY_EXACT_LEN_WARN(2),
        [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 },
@@ -644,10 +618,8 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_HE_CAPABILITY] = { .type = NLA_BINARY,
                                         .len = NL80211_HE_MAX_CAPABILITY_LEN },
 
-       [NL80211_ATTR_FTM_RESPONDER] = {
-               .type = NLA_NESTED,
-               .validation_data = nl80211_ftm_responder_policy,
-       },
+       [NL80211_ATTR_FTM_RESPONDER] =
+               NLA_POLICY_NESTED(nl80211_ftm_responder_policy),
        [NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1),
        [NL80211_ATTR_PEER_MEASUREMENTS] =
                NLA_POLICY_NESTED(nl80211_pmsr_attr_policy),
@@ -705,10 +677,7 @@ static const struct nla_policy
 nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = {
        [NL80211_WOWLAN_TCP_SRC_IPV4] = { .type = NLA_U32 },
        [NL80211_WOWLAN_TCP_DST_IPV4] = { .type = NLA_U32 },
-       [NL80211_WOWLAN_TCP_DST_MAC] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = ETH_ALEN
-       },
+       [NL80211_WOWLAN_TCP_DST_MAC] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
        [NL80211_WOWLAN_TCP_SRC_PORT] = { .type = NLA_U16 },
        [NL80211_WOWLAN_TCP_DST_PORT] = { .type = NLA_U16 },
        [NL80211_WOWLAN_TCP_DATA_PAYLOAD] = { .type = NLA_MIN_LEN, .len = 1 },
@@ -738,18 +707,9 @@ nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = {
 /* policy for GTK rekey offload attributes */
 static const struct nla_policy
 nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
-       [NL80211_REKEY_DATA_KEK] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = NL80211_KEK_LEN,
-       },
-       [NL80211_REKEY_DATA_KCK] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = NL80211_KCK_LEN,
-       },
-       [NL80211_REKEY_DATA_REPLAY_CTR] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = NL80211_REPLAY_CTR_LEN
-       },
+       [NL80211_REKEY_DATA_KEK] = NLA_POLICY_EXACT_LEN_WARN(NL80211_KEK_LEN),
+       [NL80211_REKEY_DATA_KCK] = NLA_POLICY_EXACT_LEN_WARN(NL80211_KCK_LEN),
+       [NL80211_REKEY_DATA_REPLAY_CTR] = NLA_POLICY_EXACT_LEN_WARN(NL80211_REPLAY_CTR_LEN),
 };
 
 static const struct nla_policy
@@ -764,10 +724,7 @@ static const struct nla_policy
 nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
        [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY,
                                                 .len = IEEE80211_MAX_SSID_LEN },
-       [NL80211_SCHED_SCAN_MATCH_ATTR_BSSID] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = ETH_ALEN
-       },
+       [NL80211_SCHED_SCAN_MATCH_ATTR_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
        [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 },
        [NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI] =
                NLA_POLICY_NESTED(nl80211_match_band_rssi_policy),
@@ -799,10 +756,7 @@ nl80211_nan_func_policy[NL80211_NAN_FUNC_ATTR_MAX + 1] = {
        [NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE] = { .type = NLA_FLAG },
        [NL80211_NAN_FUNC_FOLLOW_UP_ID] = { .type = NLA_U8 },
        [NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] = { .type = NLA_U8 },
-       [NL80211_NAN_FUNC_FOLLOW_UP_DEST] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = ETH_ALEN
-       },
+       [NL80211_NAN_FUNC_FOLLOW_UP_DEST] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN),
        [NL80211_NAN_FUNC_CLOSE_RANGE] = { .type = NLA_FLAG },
        [NL80211_NAN_FUNC_TTL] = { .type = NLA_U32 },
        [NL80211_NAN_FUNC_SERVICE_INFO] = { .type = NLA_BINARY,
@@ -4408,10 +4362,7 @@ static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
                                    .len = NL80211_MAX_SUPP_RATES },
        [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
                                .len = NL80211_MAX_SUPP_HT_RATES },
-       [NL80211_TXRATE_VHT] = {
-               .type = NLA_EXACT_LEN_WARN,
-               .len = sizeof(struct nl80211_txrate_vht),
-       },
+       [NL80211_TXRATE_VHT] = NLA_POLICY_EXACT_LEN_WARN(sizeof(struct nl80211_txrate_vht)),
        [NL80211_TXRATE_GI] = { .type = NLA_U8 },
 };
 
index a41e94a..d3e8e42 100644 (file)
@@ -11,8 +11,6 @@
 int nl80211_init(void);
 void nl80211_exit(void);
 
-extern const struct nla_policy nl80211_policy[NUM_NL80211_ATTR];
-
 void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
                     int flags, u8 cmd);
 bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
index 63dc802..a95c79d 100644 (file)
@@ -187,10 +187,9 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
 
        /* reuse info->attrs */
        memset(info->attrs, 0, sizeof(*info->attrs) * (NL80211_ATTR_MAX + 1));
-       /* need to validate here, we don't want to have validation recursion */
        err = nla_parse_nested_deprecated(info->attrs, NL80211_ATTR_MAX,
                                          tb[NL80211_PMSR_PEER_ATTR_CHAN],
-                                         nl80211_policy, info->extack);
+                                         NULL, info->extack);
        if (err)
                return err;
 
index 6582d15..d5e2823 100644 (file)
@@ -90,7 +90,7 @@ static const struct ieee80211_radiotap_namespace radiotap_ns = {
  * iterator.this_arg for type "type" safely on all arches.
  *
  * Example code:
- * See Documentation/networking/radiotap-headers.txt
+ * See Documentation/networking/radiotap-headers.rst
  */
 
 int ieee80211_radiotap_iterator_init(
index 2ecb2e5..9f0d58b 100644 (file)
@@ -20,8 +20,8 @@ config X25
          You can read more about X.25 at <http://www.sangoma.com/tutorials/x25/> and
          <http://docwiki.cisco.com/wiki/X.25>.
          Information about X.25 for Linux is contained in the files
-         <file:Documentation/networking/x25.txt> and
-         <file:Documentation/networking/x25-iface.txt>.
+         <file:Documentation/networking/x25.rst> and
+         <file:Documentation/networking/x25-iface.rst>.
 
          One connects to an X.25 network either with a dedicated network card
          using the X.21 protocol (not yet supported by Linux) or one can do
index 00e7823..25bf72e 100644 (file)
@@ -115,8 +115,10 @@ int x25_lapb_receive_frame(struct sk_buff *skb, struct net_device *dev,
                goto drop;
        }
 
-       if (!pskb_may_pull(skb, 1))
+       if (!pskb_may_pull(skb, 1)) {
+               x25_neigh_put(nb);
                return 0;
+       }
 
        switch (skb->data[0]) {
 
index 8aa415a..0285aaa 100644 (file)
@@ -357,6 +357,12 @@ void x25_disconnect(struct sock *sk, int reason, unsigned char cause,
                sk->sk_state_change(sk);
                sock_set_flag(sk, SOCK_DEAD);
        }
+       if (x25->neighbour) {
+               read_lock_bh(&x25_list_lock);
+               x25_neigh_put(x25->neighbour);
+               x25->neighbour = NULL;
+               read_unlock_bh(&x25_list_lock);
+       }
 }
 
 /*
index fa7bb5e..ed7a606 100644 (file)
@@ -343,7 +343,7 @@ static int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr)
        u32 chunk_size = mr->chunk_size, headroom = mr->headroom;
        unsigned int chunks, chunks_per_page;
        u64 addr = mr->addr, size = mr->len;
-       int size_chk, err;
+       int err;
 
        if (chunk_size < XDP_UMEM_MIN_CHUNK_SIZE || chunk_size > PAGE_SIZE) {
                /* Strictly speaking we could support this, if:
@@ -382,8 +382,7 @@ static int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr)
                        return -EINVAL;
        }
 
-       size_chk = chunk_size - headroom - XDP_PACKET_HEADROOM;
-       if (size_chk < 0)
+       if (headroom >= chunk_size - XDP_PACKET_HEADROOM)
                return -EINVAL;
 
        umem->address = (unsigned long)addr;
index 356f90e..f6e6609 100644 (file)
@@ -131,8 +131,9 @@ static void __xsk_rcv_memcpy(struct xdp_umem *umem, u64 addr, void *from_buf,
                u64 page_start = addr & ~(PAGE_SIZE - 1);
                u64 first_len = PAGE_SIZE - (addr - page_start);
 
-               memcpy(to_buf, from_buf, first_len + metalen);
-               memcpy(next_pg_addr, from_buf + first_len, len - first_len);
+               memcpy(to_buf, from_buf, first_len);
+               memcpy(next_pg_addr, from_buf + first_len,
+                      len + metalen - first_len);
 
                return;
        }
@@ -321,7 +322,7 @@ bool xsk_umem_consume_tx(struct xdp_umem *umem, struct xdp_desc *desc)
                if (!xskq_cons_peek_desc(xs->tx, desc, umem))
                        continue;
 
-               /* This is the backpreassure mechanism for the Tx path.
+               /* This is the backpressure mechanism for the Tx path.
                 * Reserve space in the completion queue and only proceed
                 * if there is space in it. This avoids having to implement
                 * any buffering in the Tx path.
@@ -405,7 +406,7 @@ static int xsk_generic_xmit(struct sock *sk)
                addr = desc.addr;
                buffer = xdp_umem_get_data(xs->umem, addr);
                err = skb_store_bits(skb, 0, buffer, len);
-               /* This is the backpreassure mechanism for the Tx path.
+               /* This is the backpressure mechanism for the Tx path.
                 * Reserve space in the completion queue and only proceed
                 * if there is space in it. This avoids having to implement
                 * any buffering in the Tx path.
index 3f6483e..f9c53ca 100644 (file)
@@ -3,7 +3,7 @@ Sample and benchmark scripts for pktgen (packet generator)
 This directory contains some pktgen sample and benchmark scripts, that
 can easily be copied and adjusted for your own use-case.
 
-General doc is located in kernel: Documentation/networking/pktgen.txt
+General doc is located in kernel: Documentation/networking/pktgen.rst
 
 Helper include files
 ====================
index cc86bf6..9894693 100644 (file)
@@ -418,7 +418,7 @@ static int mdpy_mmap(struct mdev_device *mdev, struct vm_area_struct *vma)
                return -EINVAL;
 
        return remap_vmalloc_range_partial(vma, vma->vm_start,
-                                          mdev_state->memblk,
+                                          mdev_state->memblk, 0,
                                           vma->vm_end - vma->vm_start);
 }
 
index 9754710..4b79973 100644 (file)
@@ -309,7 +309,7 @@ define rule_dtc
 endef
 
 $(obj)/%.dt.yaml: $(src)/%.dts $(DTC) $(DT_TMP_SCHEMA) FORCE
-       $(call if_changed_rule,dtc)
+       $(call if_changed_rule,dtc,yaml)
 
 dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
 
index d64c67b..eac40f0 100755 (executable)
@@ -479,7 +479,7 @@ our $allocFunctions = qr{(?x:
                (?:kv|k|v)[czm]alloc(?:_node|_array)? |
                kstrdup(?:_const)? |
                kmemdup(?:_nul)?) |
-       (?:\w+)?alloc_skb(?:ip_align)? |
+       (?:\w+)?alloc_skb(?:_ip_align)? |
                                # dev_alloc_skb/netdev_alloc_skb, et al
        dma_alloc_coherent
 )};
index e0e3982..eee5b7f 100755 (executable)
@@ -7,6 +7,9 @@ myname=${0##*/}
 # If no prefix forced, use the default CONFIG_
 CONFIG_="${CONFIG_-CONFIG_}"
 
+# We use an uncommon delimiter for sed substitutions
+SED_DELIM=$(echo -en "\001")
+
 usage() {
        cat >&2 <<EOL
 Manipulate options in a .config file from the command line.
@@ -83,7 +86,7 @@ txt_subst() {
        local infile="$3"
        local tmpfile="$infile.swp"
 
-       sed -e "s:$before:$after:" "$infile" >"$tmpfile"
+       sed -e "s$SED_DELIM$before$SED_DELIM$after$SED_DELIM" "$infile" >"$tmpfile"
        # replace original file with the edited one
        mv "$tmpfile" "$infile"
 }
index 9a8cc10..c71832b 100755 (executable)
@@ -25,7 +25,7 @@ my $fix = 0;
 my $warn = 0;
 
 if (! -d ".git") {
-       printf "Warning: can't check if file exists, as this is not a git tree";
+       printf "Warning: can't check if file exists, as this is not a git tree\n";
        exit 0;
 }
 
index 2f3c3a7..ef85f8b 100644 (file)
@@ -13,7 +13,7 @@ dtc-objs      += dtc-lexer.lex.o dtc-parser.tab.o
 HOST_EXTRACFLAGS := -I $(srctree)/$(src)/libfdt
 
 ifeq ($(shell pkg-config --exists yaml-0.1 2>/dev/null && echo yes),)
-ifneq ($(CHECK_DTBS),)
+ifneq ($(CHECK_DT_BINDING)$(CHECK_DTBS),)
 $(error dtc needs libyaml for DT schema validation support. \
        Install the necessary libyaml development package.)
 endif
index f22858b..80f3542 100644 (file)
@@ -4,6 +4,7 @@ GCC_PLUGINS_DIR := $(shell $(CC) -print-file-name=plugin)
 HOST_EXTRACXXFLAGS += -I$(GCC_PLUGINS_DIR)/include -I$(src) -std=gnu++98 -fno-rtti
 HOST_EXTRACXXFLAGS += -fno-exceptions -fasynchronous-unwind-tables -ggdb
 HOST_EXTRACXXFLAGS += -Wno-narrowing -Wno-unused-variable -Wno-c++11-compat
+HOST_EXTRACXXFLAGS += -Wno-format-diag
 
 $(obj)/randomize_layout_plugin.o: $(objtree)/$(obj)/randomize_layout_seed.h
 quiet_cmd_create_randomize_layout_seed = GENSEED $@
index 17f0607..9ad76b7 100644 (file)
@@ -35,7 +35,9 @@
 #include "ggc.h"
 #include "timevar.h"
 
+#if BUILDING_GCC_VERSION < 10000
 #include "params.h"
+#endif
 
 #if BUILDING_GCC_VERSION <= 4009
 #include "pointer-set.h"
@@ -847,6 +849,7 @@ static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree l
        return gimple_build_assign(lhs, subcode, op1, op2 PASS_MEM_STAT);
 }
 
+#if BUILDING_GCC_VERSION < 10000
 template <>
 template <>
 inline bool is_a_helper<const ggoto *>::test(const_gimple gs)
@@ -860,6 +863,7 @@ inline bool is_a_helper<const greturn *>::test(const_gimple gs)
 {
        return gs->code == GIMPLE_RETURN;
 }
+#endif
 
 static inline gasm *as_a_gasm(gimple stmt)
 {
index dbd3746..cc75eeb 100644 (file)
@@ -51,7 +51,6 @@ static void stackleak_add_track_stack(gimple_stmt_iterator *gsi, bool after)
        gimple stmt;
        gcall *stackleak_track_stack;
        cgraph_node_ptr node;
-       int frequency;
        basic_block bb;
 
        /* Insert call to void stackleak_track_stack(void) */
@@ -68,9 +67,9 @@ static void stackleak_add_track_stack(gimple_stmt_iterator *gsi, bool after)
        bb = gimple_bb(stackleak_track_stack);
        node = cgraph_get_create_node(track_function_decl);
        gcc_assert(node);
-       frequency = compute_call_stmt_bb_frequency(current_function_decl, bb);
        cgraph_create_edge(cgraph_get_node(current_function_decl), node,
-                       stackleak_track_stack, bb->count, frequency);
+                       stackleak_track_stack, bb->count,
+                       compute_call_stmt_bb_frequency(current_function_decl, bb));
 }
 
 static bool is_alloca(gimple stmt)
index 3e8dea6..6dc3078 100644 (file)
@@ -34,7 +34,7 @@ struct sym_entry {
        unsigned int len;
        unsigned int start_pos;
        unsigned int percpu_absolute;
-       unsigned char sym[0];
+       unsigned char sym[];
 };
 
 struct addr_range {
index f2d73f0..f746ca8 100755 (executable)
@@ -853,7 +853,7 @@ sub output_function_rst(%) {
 
        if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
            # pointer-to-function
-           print $1 . $parameter . ") (" . $2;
+           print $1 . $parameter . ") (" . $2 . ")";
        } else {
            print $type . " " . $parameter;
        }
index 5c3c50c..7f7d4ee 100644 (file)
@@ -2251,6 +2251,7 @@ static void add_header(struct buffer *b, struct module *mod)
         * Include build-salt.h after module.h in order to
         * inherit the definitions.
         */
+       buf_printf(b, "#define INCLUDE_VERMAGIC\n");
        buf_printf(b, "#include <linux/build-salt.h>\n");
        buf_printf(b, "#include <linux/vermagic.h>\n");
        buf_printf(b, "#include <linux/compiler.h>\n");
index b621ad7..27e371b 100644 (file)
@@ -1696,7 +1696,7 @@ static int __init alloc_buffers(void)
 
 #ifdef CONFIG_SYSCTL
 static int apparmor_dointvec(struct ctl_table *table, int write,
-                            void __user *buffer, size_t *lenp, loff_t *ppos)
+                            void *buffer, size_t *lenp, loff_t *ppos)
 {
        if (!policy_admin_capable(NULL))
                return -EPERM;
index 415f3f1..d0cde66 100644 (file)
@@ -139,6 +139,8 @@ static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
        n = key_serial_next(p, v);
        if (n)
                *_pos = key_node_serial(n);
+       else
+               (*_pos)++;
        return n;
 }
 
index 94d2b0c..88c9a6a 100644 (file)
@@ -30,7 +30,7 @@ static void update_mmap_min_addr(void)
  * calls update_mmap_min_addr() so non MAP_FIXED hints get rounded properly
  */
 int mmap_min_addr_handler(struct ctl_table *table, int write,
-                         void __user *buffer, size_t *lenp, loff_t *ppos)
+                         void *buffer, size_t *lenp, loff_t *ppos)
 {
        int ret;
 
index 0b4e321..4c037c2 100644 (file)
@@ -5842,40 +5842,60 @@ static unsigned int selinux_ipv6_postroute(void *priv,
 
 static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
 {
-       int err = 0;
-       u32 perm;
+       int rc = 0;
+       unsigned int msg_len;
+       unsigned int data_len = skb->len;
+       unsigned char *data = skb->data;
        struct nlmsghdr *nlh;
        struct sk_security_struct *sksec = sk->sk_security;
+       u16 sclass = sksec->sclass;
+       u32 perm;
 
-       if (skb->len < NLMSG_HDRLEN) {
-               err = -EINVAL;
-               goto out;
-       }
-       nlh = nlmsg_hdr(skb);
+       while (data_len >= nlmsg_total_size(0)) {
+               nlh = (struct nlmsghdr *)data;
+
+               /* NOTE: the nlmsg_len field isn't reliably set by some netlink
+                *       users which means we can't reject skb's with bogus
+                *       length fields; our solution is to follow what
+                *       netlink_rcv_skb() does and simply skip processing at
+                *       messages with length fields that are clearly junk
+                */
+               if (nlh->nlmsg_len < NLMSG_HDRLEN || nlh->nlmsg_len > data_len)
+                       return 0;
 
-       err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm);
-       if (err) {
-               if (err == -EINVAL) {
+               rc = selinux_nlmsg_lookup(sclass, nlh->nlmsg_type, &perm);
+               if (rc == 0) {
+                       rc = sock_has_perm(sk, perm);
+                       if (rc)
+                               return rc;
+               } else if (rc == -EINVAL) {
+                       /* -EINVAL is a missing msg/perm mapping */
                        pr_warn_ratelimited("SELinux: unrecognized netlink"
-                              " message: protocol=%hu nlmsg_type=%hu sclass=%s"
-                              " pid=%d comm=%s\n",
-                              sk->sk_protocol, nlh->nlmsg_type,
-                              secclass_map[sksec->sclass - 1].name,
-                              task_pid_nr(current), current->comm);
-                       if (!enforcing_enabled(&selinux_state) ||
-                           security_get_allow_unknown(&selinux_state))
-                               err = 0;
+                               " message: protocol=%hu nlmsg_type=%hu sclass=%s"
+                               " pid=%d comm=%s\n",
+                               sk->sk_protocol, nlh->nlmsg_type,
+                               secclass_map[sclass - 1].name,
+                               task_pid_nr(current), current->comm);
+                       if (enforcing_enabled(&selinux_state) &&
+                           !security_get_allow_unknown(&selinux_state))
+                               return rc;
+                       rc = 0;
+               } else if (rc == -ENOENT) {
+                       /* -ENOENT is a missing socket/class mapping, ignore */
+                       rc = 0;
+               } else {
+                       return rc;
                }
 
-               /* Ignore */
-               if (err == -ENOENT)
-                       err = 0;
-               goto out;
+               /* move to the next message after applying netlink padding */
+               msg_len = NLMSG_ALIGN(nlh->nlmsg_len);
+               if (msg_len >= data_len)
+                       return 0;
+               data_len -= msg_len;
+               data += msg_len;
        }
 
-       err = sock_has_perm(sk, perm);
-out:
-       return err;
+       return rc;
 }
 
 static void ipc_init_security(struct ipc_security_struct *isec, u16 sclass)
index 939a74f..da94a1b 100644 (file)
@@ -429,7 +429,7 @@ int cond_read_list(struct policydb *p, void *fp)
 
        p->cond_list = kcalloc(len, sizeof(*p->cond_list), GFP_KERNEL);
        if (!p->cond_list)
-               return rc;
+               return -ENOMEM;
 
        rc = avtab_alloc(&(p->te_cond_avtab), p->te_avtab.nel);
        if (rc)
index 70ecdc7..c21b922 100644 (file)
@@ -1035,14 +1035,14 @@ static int str_read(char **strp, gfp_t flags, void *fp, u32 len)
        if (!str)
                return -ENOMEM;
 
-       /* it's expected the caller should free the str */
-       *strp = str;
-
        rc = next_entry(str, fp, len);
-       if (rc)
+       if (rc) {
+               kfree(str);
                return rc;
+       }
 
        str[len] = '\0';
+       *strp = str;
        return 0;
 }
 
index 94dc346..536c996 100644 (file)
@@ -430,7 +430,7 @@ static struct security_hook_list yama_hooks[] __lsm_ro_after_init = {
 
 #ifdef CONFIG_SYSCTL
 static int yama_dointvec_minmax(struct ctl_table *table, int write,
-                               void __user *buffer, size_t *lenp, loff_t *ppos)
+                               void *buffer, size_t *lenp, loff_t *ppos)
 {
        struct ctl_table table_copy;
 
index 59d62f0..1545f8f 100644 (file)
@@ -205,13 +205,14 @@ static snd_pcm_sframes_t calc_dst_frames(struct snd_pcm_substream *plug,
        plugin = snd_pcm_plug_first(plug);
        while (plugin && frames > 0) {
                plugin_next = plugin->next;
+               if (check_size && plugin->buf_frames &&
+                   frames > plugin->buf_frames)
+                       frames = plugin->buf_frames;
                if (plugin->dst_frames) {
                        frames = plugin->dst_frames(plugin, frames);
                        if (frames < 0)
                                return frames;
                }
-               if (check_size && frames > plugin->buf_frames)
-                       frames = plugin->buf_frames;
                plugin = plugin_next;
        }
        return frames;
@@ -225,14 +226,15 @@ static snd_pcm_sframes_t calc_src_frames(struct snd_pcm_substream *plug,
 
        plugin = snd_pcm_plug_last(plug);
        while (plugin && frames > 0) {
-               if (check_size && frames > plugin->buf_frames)
-                       frames = plugin->buf_frames;
                plugin_prev = plugin->prev;
                if (plugin->src_frames) {
                        frames = plugin->src_frames(plugin, frames);
                        if (frames < 0)
                                return frames;
                }
+               if (check_size && plugin->buf_frames &&
+                   frames > plugin->buf_frames)
+                       frames = plugin->buf_frames;
                plugin = plugin_prev;
        }
        return frames;
index 4ca6b09..3bc9224 100644 (file)
@@ -21,16 +21,17 @@ config SND_HDA_EXT_CORE
        select SND_HDA_CORE
 
 config SND_HDA_PREALLOC_SIZE
-       int "Pre-allocated buffer size for HD-audio driver" if !SND_DMA_SGBUF
+       int "Pre-allocated buffer size for HD-audio driver"
        range 0 32768
-       default 0 if SND_DMA_SGBUF
+       default 2048 if SND_DMA_SGBUF
        default 64 if !SND_DMA_SGBUF
        help
          Specifies the default pre-allocated buffer-size in kB for the
          HD-audio driver.  A larger buffer (e.g. 2048) is preferred
          for systems using PulseAudio.  The default 64 is chosen just
          for compatibility reasons.
-         On x86 systems, the default is zero as we need no preallocation.
+         On x86 systems, the default is 2048 as a reasonable value for
+         most of modern systems.
 
          Note that the pre-allocation size can be changed dynamically
          via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
index e764816..b039429 100644 (file)
@@ -867,10 +867,13 @@ static void snd_miro_write(struct snd_miro *chip, unsigned char reg,
        spin_unlock_irqrestore(&chip->lock, flags);
 }
 
+static inline void snd_miro_write_mask(struct snd_miro *chip,
+               unsigned char reg, unsigned char value, unsigned char mask)
+{
+       unsigned char oldval = snd_miro_read(chip, reg);
 
-#define snd_miro_write_mask(chip, reg, value, mask)    \
-       snd_miro_write(chip, reg,                       \
-               (snd_miro_read(chip, reg) & ~(mask)) | ((value) & (mask)))
+       snd_miro_write(chip, reg, (oldval & ~mask) | (value & mask));
+}
 
 /*
  *  Proc Interface
index d06b296..0e6d20e 100644 (file)
@@ -317,10 +317,13 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
 }
 
 
-#define snd_opti9xx_write_mask(chip, reg, value, mask) \
-       snd_opti9xx_write(chip, reg,                    \
-               (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
+static inline void snd_opti9xx_write_mask(struct snd_opti9xx *chip,
+               unsigned char reg, unsigned char value, unsigned char mask)
+{
+       unsigned char oldval = snd_opti9xx_read(chip, reg);
 
+       snd_opti9xx_write(chip, reg, (oldval & ~mask) | (value & mask));
+}
 
 static int snd_opti9xx_configure(struct snd_opti9xx *chip,
                                           long port,
index 6e3177b..015c0d6 100644 (file)
@@ -168,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk)
 
 static int src_put_rsc_ctrl_blk(void *blk)
 {
-       kfree((struct src_rsc_ctrl_blk *)blk);
+       kfree(blk);
 
        return 0;
 }
@@ -494,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk)
 
 static int src_mgr_put_ctrl_blk(void *blk)
 {
-       kfree((struct src_mgr_ctrl_blk *)blk);
+       kfree(blk);
 
        return 0;
 }
@@ -515,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk)
 
 static int srcimp_mgr_put_ctrl_blk(void *blk)
 {
-       kfree((struct srcimp_mgr_ctrl_blk *)blk);
+       kfree(blk);
 
        return 0;
 }
@@ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk)
 
 static int amixer_rsc_put_ctrl_blk(void *blk)
 {
-       kfree((struct amixer_rsc_ctrl_blk *)blk);
+       kfree(blk);
 
        return 0;
 }
@@ -909,7 +909,7 @@ static int dai_get_ctrl_blk(void **rblk)
 
 static int dai_put_ctrl_blk(void *blk)
 {
-       kfree((struct dai_ctrl_blk *)blk);
+       kfree(blk);
 
        return 0;
 }
@@ -958,7 +958,7 @@ static int dao_get_ctrl_blk(void **rblk)
 
 static int dao_put_ctrl_blk(void *blk)
 {
-       kfree((struct dao_ctrl_blk *)blk);
+       kfree(blk);
 
        return 0;
 }
@@ -1156,7 +1156,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
 
 static int daio_mgr_put_ctrl_blk(void *blk)
 {
-       kfree((struct daio_mgr_ctrl_blk *)blk);
+       kfree(blk);
 
        return 0;
 }
index a34a2c9..7e3ae45 100644 (file)
@@ -641,8 +641,18 @@ static void hda_jackpoll_work(struct work_struct *work)
        struct hda_codec *codec =
                container_of(work, struct hda_codec, jackpoll_work.work);
 
-       snd_hda_jack_set_dirty_all(codec);
-       snd_hda_jack_poll_all(codec);
+       /* for non-polling trigger: we need nothing if already powered on */
+       if (!codec->jackpoll_interval && snd_hdac_is_power_on(&codec->core))
+               return;
+
+       /* the power-up/down sequence triggers the runtime resume */
+       snd_hda_power_up_pm(codec);
+       /* update jacks manually if polling is required, too */
+       if (codec->jackpoll_interval) {
+               snd_hda_jack_set_dirty_all(codec);
+               snd_hda_jack_poll_all(codec);
+       }
+       snd_hda_power_down_pm(codec);
 
        if (!codec->jackpoll_interval)
                return;
@@ -2951,18 +2961,14 @@ static int hda_codec_runtime_resume(struct device *dev)
 static int hda_codec_force_resume(struct device *dev)
 {
        struct hda_codec *codec = dev_to_hda_codec(dev);
-       bool forced_resume = !codec->relaxed_resume && codec->jacktbl.used;
        int ret;
 
-       /* The get/put pair below enforces the runtime resume even if the
-        * device hasn't been used at suspend time.  This trick is needed to
-        * update the jack state change during the sleep.
-        */
-       if (forced_resume)
-               pm_runtime_get_noresume(dev);
        ret = pm_runtime_force_resume(dev);
-       if (forced_resume)
-               pm_runtime_put(dev);
+       /* schedule jackpoll work for jack detection update */
+       if (codec->jackpoll_interval ||
+           (pm_runtime_suspended(dev) && hda_codec_need_resume(codec)))
+               schedule_delayed_work(&codec->jackpoll_work,
+                                     codec->jackpoll_interval);
        return ret;
 }
 
index bd09359..0310193 100644 (file)
@@ -1004,7 +1004,8 @@ static void __azx_runtime_resume(struct azx *chip, bool from_rt)
 
        if (status && from_rt) {
                list_for_each_codec(codec, &chip->bus)
-                       if (status & (1 << codec->addr))
+                       if (!codec->relaxed_resume &&
+                           (status & (1 << codec->addr)))
                                schedule_delayed_work(&codec->jackpoll_work,
                                                      codec->jackpoll_interval);
        }
@@ -1027,7 +1028,7 @@ static int azx_suspend(struct device *dev)
        chip = card->private_data;
        bus = azx_bus(chip);
        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-       __azx_runtime_suspend(chip);
+       pm_runtime_force_suspend(dev);
        if (bus->irq >= 0) {
                free_irq(bus->irq, chip);
                bus->irq = -1;
@@ -1055,7 +1056,8 @@ static int azx_resume(struct device *dev)
                        chip->msi = 0;
        if (azx_acquire_irq(chip, 1) < 0)
                return -EIO;
-       __azx_runtime_resume(chip, false);
+
+       pm_runtime_force_resume(dev);
        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 
        trace_azx_resume(chip);
@@ -1071,6 +1073,8 @@ static int azx_freeze_noirq(struct device *dev)
        struct azx *chip = card->private_data;
        struct pci_dev *pci = to_pci_dev(dev);
 
+       if (!azx_is_pm_ready(card))
+               return 0;
        if (chip->driver_type == AZX_DRIVER_SKL)
                pci_set_power_state(pci, PCI_D3hot);
 
@@ -1083,6 +1087,8 @@ static int azx_thaw_noirq(struct device *dev)
        struct azx *chip = card->private_data;
        struct pci_dev *pci = to_pci_dev(dev);
 
+       if (!azx_is_pm_ready(card))
+               return 0;
        if (chip->driver_type == AZX_DRIVER_SKL)
                pci_set_power_state(pci, PCI_D0);
 
@@ -1098,12 +1104,12 @@ static int azx_runtime_suspend(struct device *dev)
        if (!azx_is_pm_ready(card))
                return 0;
        chip = card->private_data;
-       if (!azx_has_pm_runtime(chip))
-               return 0;
 
        /* enable controller wake up event */
-       azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
-                 STATESTS_INT_MASK);
+       if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) {
+               azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
+                          STATESTS_INT_MASK);
+       }
 
        __azx_runtime_suspend(chip);
        trace_azx_runtime_suspend(chip);
@@ -1114,17 +1120,18 @@ static int azx_runtime_resume(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
        struct azx *chip;
+       bool from_rt = snd_power_get_state(card) == SNDRV_CTL_POWER_D0;
 
        if (!azx_is_pm_ready(card))
                return 0;
        chip = card->private_data;
-       if (!azx_has_pm_runtime(chip))
-               return 0;
-       __azx_runtime_resume(chip, true);
+       __azx_runtime_resume(chip, from_rt);
 
        /* disable controller Wake Up event*/
-       azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
-                       ~STATESTS_INT_MASK);
+       if (from_rt) {
+               azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
+                          ~STATESTS_INT_MASK);
+       }
 
        trace_azx_runtime_resume(chip);
        return 0;
@@ -1199,10 +1206,8 @@ static void azx_vs_set_state(struct pci_dev *pci,
                if (!disabled) {
                        dev_info(chip->card->dev,
                                 "Start delayed initialization\n");
-                       if (azx_probe_continue(chip) < 0) {
+                       if (azx_probe_continue(chip) < 0)
                                dev_err(chip->card->dev, "initialization error\n");
-                               hda->init_failed = true;
-                       }
                }
        } else {
                dev_info(chip->card->dev, "%s via vga_switcheroo\n",
@@ -1335,12 +1340,15 @@ static int register_vga_switcheroo(struct azx *chip)
 /*
  * destructor
  */
-static int azx_free(struct azx *chip)
+static void azx_free(struct azx *chip)
 {
        struct pci_dev *pci = chip->pci;
        struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
        struct hdac_bus *bus = azx_bus(chip);
 
+       if (hda->freed)
+               return;
+
        if (azx_has_pm_runtime(chip) && chip->running)
                pm_runtime_get_noresume(&pci->dev);
        chip->running = 0;
@@ -1384,9 +1392,8 @@ static int azx_free(struct azx *chip)
 
        if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT)
                snd_hdac_i915_exit(bus);
-       kfree(hda);
 
-       return 0;
+       hda->freed = 1;
 }
 
 static int azx_dev_disconnect(struct snd_device *device)
@@ -1402,7 +1409,8 @@ static int azx_dev_disconnect(struct snd_device *device)
 
 static int azx_dev_free(struct snd_device *device)
 {
-       return azx_free(device->device_data);
+       azx_free(device->device_data);
+       return 0;
 }
 
 #ifdef SUPPORT_VGA_SWITCHEROO
@@ -1769,7 +1777,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
        if (err < 0)
                return err;
 
-       hda = kzalloc(sizeof(*hda), GFP_KERNEL);
+       hda = devm_kzalloc(&pci->dev, sizeof(*hda), GFP_KERNEL);
        if (!hda) {
                pci_disable_device(pci);
                return -ENOMEM;
@@ -1810,7 +1818,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
 
        err = azx_bus_init(chip, model[dev]);
        if (err < 0) {
-               kfree(hda);
                pci_disable_device(pci);
                return err;
        }
@@ -2005,7 +2012,7 @@ static int azx_first_init(struct azx *chip)
        /* codec detection */
        if (!azx_bus(chip)->codec_mask) {
                dev_err(card->dev, "no codecs found!\n");
-               return -ENODEV;
+               /* keep running the rest for the runtime PM */
        }
 
        if (azx_acquire_irq(chip, 0) < 0)
@@ -2027,24 +2034,15 @@ static void azx_firmware_cb(const struct firmware *fw, void *context)
 {
        struct snd_card *card = context;
        struct azx *chip = card->private_data;
-       struct pci_dev *pci = chip->pci;
-
-       if (!fw) {
-               dev_err(card->dev, "Cannot load firmware, aborting\n");
-               goto error;
-       }
 
-       chip->fw = fw;
+       if (fw)
+               chip->fw = fw;
+       else
+               dev_err(card->dev, "Cannot load firmware, continue without patching\n");
        if (!chip->disabled) {
                /* continue probing */
-               if (azx_probe_continue(chip))
-                       goto error;
+               azx_probe_continue(chip);
        }
-       return; /* OK */
-
- error:
-       snd_card_free(card);
-       pci_set_drvdata(pci, NULL);
 }
 #endif
 
@@ -2080,10 +2078,10 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
  * some HD-audio PCI entries are exposed without any codecs, and such devices
  * should be ignored from the beginning.
  */
-static const struct snd_pci_quirk driver_blacklist[] = {
-       SND_PCI_QUIRK(0x1043, 0x874f, "ASUS ROG Zenith II / Strix", 0),
-       SND_PCI_QUIRK(0x1462, 0xcb59, "MSI TRX40 Creator", 0),
-       SND_PCI_QUIRK(0x1462, 0xcb60, "MSI TRX40", 0),
+static const struct pci_device_id driver_blacklist[] = {
+       { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1043, 0x874f) }, /* ASUS ROG Zenith II / Strix */
+       { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb59) }, /* MSI TRX40 Creator */
+       { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb60) }, /* MSI TRX40 */
        {}
 };
 
@@ -2103,7 +2101,7 @@ static int azx_probe(struct pci_dev *pci,
        bool schedule_probe;
        int err;
 
-       if (snd_pci_quirk_lookup(pci, driver_blacklist)) {
+       if (pci_match_id(driver_blacklist, pci)) {
                dev_info(&pci->dev, "Skipping the blacklisted device\n");
                return -ENODEV;
        }
@@ -2308,9 +2306,11 @@ static int azx_probe_continue(struct azx *chip)
 #endif
 
        /* create codec instances */
-       err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);
-       if (err < 0)
-               goto out_free;
+       if (bus->codec_mask) {
+               err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);
+               if (err < 0)
+                       goto out_free;
+       }
 
 #ifdef CONFIG_SND_HDA_PATCH_LOADER
        if (chip->fw) {
@@ -2324,7 +2324,7 @@ static int azx_probe_continue(struct azx *chip)
 #endif
        }
 #endif
-       if ((probe_only[dev] & 1) == 0) {
+       if (bus->codec_mask && !(probe_only[dev] & 1)) {
                err = azx_codec_configure(chip);
                if (err < 0)
                        goto out_free;
@@ -2341,17 +2341,23 @@ static int azx_probe_continue(struct azx *chip)
 
        set_default_power_save(chip);
 
-       if (azx_has_pm_runtime(chip))
+       if (azx_has_pm_runtime(chip)) {
+               pm_runtime_use_autosuspend(&pci->dev);
+               pm_runtime_allow(&pci->dev);
                pm_runtime_put_autosuspend(&pci->dev);
+       }
 
 out_free:
-       if (err < 0 || !hda->need_i915_power)
+       if (err < 0) {
+               azx_free(chip);
+               return err;
+       }
+
+       if (!hda->need_i915_power)
                display_power(chip, false);
-       if (err < 0)
-               hda->init_failed = 1;
        complete_all(&hda->probe_wait);
        to_hda_bus(bus)->bus_probing = 0;
-       return err;
+       return 0;
 }
 
 static void azx_remove(struct pci_dev *pci)
index 2acfff3..3fb119f 100644 (file)
@@ -27,6 +27,7 @@ struct hda_intel {
        unsigned int use_vga_switcheroo:1;
        unsigned int vga_switcheroo_registered:1;
        unsigned int init_failed:1; /* delayed init failed */
+       unsigned int freed:1; /* resources already released */
 
        bool need_i915_power:1; /* the hda controller needs i915 power */
 };
index bb287a9..93760a3 100644 (file)
@@ -38,6 +38,10 @@ static bool static_hdmi_pcm;
 module_param(static_hdmi_pcm, bool, 0644);
 MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
 
+static bool enable_acomp = true;
+module_param(enable_acomp, bool, 0444);
+MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)");
+
 struct hdmi_spec_per_cvt {
        hda_nid_t cvt_nid;
        int assigned;
@@ -1844,8 +1848,10 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
        /* Add sanity check to pass klockwork check.
         * This should never happen.
         */
-       if (WARN_ON(spdif == NULL))
+       if (WARN_ON(spdif == NULL)) {
+               mutex_unlock(&codec->spdif_mutex);
                return true;
+       }
        non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO);
        mutex_unlock(&codec->spdif_mutex);
        return non_pcm;
@@ -2194,7 +2200,9 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
 
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
                struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+               struct hdmi_eld *pin_eld = &per_pin->sink_eld;
 
+               pin_eld->eld_valid = false;
                hdmi_present_sense(per_pin, 0);
        }
 
@@ -2505,6 +2513,11 @@ static void generic_acomp_init(struct hda_codec *codec,
 {
        struct hdmi_spec *spec = codec->spec;
 
+       if (!enable_acomp) {
+               codec_info(codec, "audio component disabled by module option\n");
+               return;
+       }
+
        spec->port2pin = port2pin;
        setup_drm_audio_ops(codec, ops);
        if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops,
index de2826f..c16f639 100644 (file)
@@ -377,6 +377,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
        case 0x10ec0233:
        case 0x10ec0235:
        case 0x10ec0236:
+       case 0x10ec0245:
        case 0x10ec0255:
        case 0x10ec0256:
        case 0x10ec0257:
@@ -797,9 +798,11 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
 {
        if (!alc_subsystem_id(codec, ports)) {
                struct alc_spec *spec = codec->spec;
-               codec_dbg(codec,
-                         "realtek: Enable default setup for auto mode as fallback\n");
-               spec->init_amp = ALC_INIT_DEFAULT;
+               if (spec->init_amp == ALC_INIT_UNDEFINED) {
+                       codec_dbg(codec,
+                                 "realtek: Enable default setup for auto mode as fallback\n");
+                       spec->init_amp = ALC_INIT_DEFAULT;
+               }
        }
 }
 
@@ -7378,6 +7381,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK),
        SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC),
+       SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC),
        SND_PCI_QUIRK(0x1043, 0x19ce, "ASUS B9450FA", ALC294_FIXUP_ASUS_HPE),
        SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
        SND_PCI_QUIRK(0x1043, 0x1a30, "ASUS X705UD", ALC256_FIXUP_ASUS_MIC),
@@ -7416,6 +7420,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1558, 0x8560, "System76 Gazelle (gaze14)", ALC269_FIXUP_HEADSET_MIC),
        SND_PCI_QUIRK(0x1558, 0x8561, "System76 Gazelle (gaze14)", ALC269_FIXUP_HEADSET_MIC),
        SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS),
+       SND_PCI_QUIRK(0x17aa, 0x1048, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
        SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
        SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE),
@@ -8195,6 +8200,7 @@ static int patch_alc269(struct hda_codec *codec)
                spec->gen.mixer_nid = 0;
                break;
        case 0x10ec0215:
+       case 0x10ec0245:
        case 0x10ec0285:
        case 0x10ec0289:
                spec->codec_variant = ALC269_TYPE_ALC215;
@@ -9456,6 +9462,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
        HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0236, "ALC236", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0245, "ALC245", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0257, "ALC257", patch_alc269),
index 024a7ee..e499c00 100644 (file)
@@ -89,9 +89,9 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
-       snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
-       snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
-       snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+       snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+       snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+       snd_jack_set_key(pco_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
 
        ret = snd_soc_component_set_jack(component, &pco_jack, NULL);
        if (ret) {
index e6a0c5d..e60e0b6 100644 (file)
@@ -1525,6 +1525,7 @@ config SND_SOC_WM8804_SPI
 
 config SND_SOC_WM8900
        tristate
+       depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8903
        tristate "Wolfson Microelectronics WM8903 CODEC"
@@ -1576,6 +1577,7 @@ config SND_SOC_WM8985
 
 config SND_SOC_WM8988
        tristate
+       depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8990
        tristate
@@ -1594,6 +1596,7 @@ config SND_SOC_WM8994
 
 config SND_SOC_WM8995
        tristate
+       depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8996
        tristate
index fba9b74..f26b77f 100644 (file)
@@ -142,14 +142,14 @@ static struct hdac_hdmi_pcm *
 hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi,
                           struct hdac_hdmi_cvt *cvt)
 {
-       struct hdac_hdmi_pcm *pcm = NULL;
+       struct hdac_hdmi_pcm *pcm;
 
        list_for_each_entry(pcm, &hdmi->pcm_list, head) {
                if (pcm->cvt == cvt)
-                       break;
+                       return pcm;
        }
 
-       return pcm;
+       return NULL;
 }
 
 static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
index 40de9d7..a448d2a 100644 (file)
@@ -1903,7 +1903,6 @@ const struct soc_enum madera_isrc_fsh[] = {
                              MADERA_ISRC4_FSH_SHIFT, 0xf,
                              MADERA_RATE_ENUM_SIZE,
                              madera_rate_text, madera_rate_val),
-
 };
 EXPORT_SYMBOL_GPL(madera_isrc_fsh);
 
@@ -1924,7 +1923,6 @@ const struct soc_enum madera_isrc_fsl[] = {
                              MADERA_ISRC4_FSL_SHIFT, 0xf,
                              MADERA_RATE_ENUM_SIZE,
                              madera_rate_text, madera_rate_val),
-
 };
 EXPORT_SYMBOL_GPL(madera_isrc_fsl);
 
@@ -1938,7 +1936,6 @@ const struct soc_enum madera_asrc1_rate[] = {
                              MADERA_ASYNC_RATE_ENUM_SIZE,
                              madera_rate_text + MADERA_SYNC_RATE_ENUM_SIZE,
                              madera_rate_val + MADERA_SYNC_RATE_ENUM_SIZE),
-
 };
 EXPORT_SYMBOL_GPL(madera_asrc1_rate);
 
@@ -1964,7 +1961,6 @@ const struct soc_enum madera_asrc2_rate[] = {
                              MADERA_ASYNC_RATE_ENUM_SIZE,
                              madera_rate_text + MADERA_SYNC_RATE_ENUM_SIZE,
                              madera_rate_val + MADERA_SYNC_RATE_ENUM_SIZE),
-
 };
 EXPORT_SYMBOL_GPL(madera_asrc2_rate);
 
index d513019..e8a8bf7 100644 (file)
@@ -1653,6 +1653,40 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
                dev_err(&client->dev,
                        "Error %d initializing CHIP_CLK_CTRL\n", ret);
 
+       /* Mute everything to avoid pop from the following power-up */
+       ret = regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_CTRL,
+                          SGTL5000_CHIP_ANA_CTRL_DEFAULT);
+       if (ret) {
+               dev_err(&client->dev,
+                       "Error %d muting outputs via CHIP_ANA_CTRL\n", ret);
+               goto disable_clk;
+       }
+
+       /*
+        * If VAG is powered-on (e.g. from previous boot), it would be disabled
+        * by the write to ANA_POWER in later steps of the probe code. This
+        * may create a loud pop even with all outputs muted. The proper way
+        * to circumvent this is disabling the bit first and waiting the proper
+        * cool-down time.
+        */
+       ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, &value);
+       if (ret) {
+               dev_err(&client->dev, "Failed to read ANA_POWER: %d\n", ret);
+               goto disable_clk;
+       }
+       if (value & SGTL5000_VAG_POWERUP) {
+               ret = regmap_update_bits(sgtl5000->regmap,
+                                        SGTL5000_CHIP_ANA_POWER,
+                                        SGTL5000_VAG_POWERUP,
+                                        0);
+               if (ret) {
+                       dev_err(&client->dev, "Error %d disabling VAG\n", ret);
+                       goto disable_clk;
+               }
+
+               msleep(SGTL5000_VAG_POWERDOWN_DELAY);
+       }
+
        /* Follow section 2.2.1.1 of AN3663 */
        ana_pwr = SGTL5000_ANA_POWER_DEFAULT;
        if (sgtl5000->num_supplies <= VDDD) {
index a4bf4bc..56ec586 100644 (file)
 /*
  * SGTL5000_CHIP_ANA_CTRL
  */
+#define SGTL5000_CHIP_ANA_CTRL_DEFAULT         0x0133
 #define SGTL5000_LINE_OUT_MUTE                 0x0100
 #define SGTL5000_HP_SEL_MASK                   0x0040
 #define SGTL5000_HP_SEL_SHIFT                  6
index 1554631..5b7f9fc 100644 (file)
@@ -820,8 +820,10 @@ static int tas571x_i2c_probe(struct i2c_client *client,
 
        priv->regmap = devm_regmap_init(dev, NULL, client,
                                        priv->chip->regmap_config);
-       if (IS_ERR(priv->regmap))
-               return PTR_ERR(priv->regmap);
+       if (IS_ERR(priv->regmap)) {
+               ret = PTR_ERR(priv->regmap);
+               goto disable_regs;
+       }
 
        priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW);
        if (IS_ERR(priv->pdn_gpio)) {
@@ -845,7 +847,7 @@ static int tas571x_i2c_probe(struct i2c_client *client,
 
        ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0);
        if (ret)
-               return ret;
+               goto disable_regs;
 
        usleep_range(50000, 60000);
 
@@ -861,12 +863,20 @@ static int tas571x_i2c_probe(struct i2c_client *client,
                 */
                ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0);
                if (ret)
-                       return ret;
+                       goto disable_regs;
        }
 
-       return devm_snd_soc_register_component(&client->dev,
+       ret = devm_snd_soc_register_component(&client->dev,
                                      &priv->component_driver,
                                      &tas571x_dai, 1);
+       if (ret)
+               goto disable_regs;
+
+       return ret;
+
+disable_regs:
+       regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies);
+       return ret;
 }
 
 static int tas571x_i2c_remove(struct i2c_client *client)
index 55112c1..6cf0f66 100644 (file)
@@ -860,8 +860,7 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
 
        wm8960->is_stream_in_use[tx] = true;
 
-       if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON &&
-           !wm8960->is_stream_in_use[!tx])
+       if (!wm8960->is_stream_in_use[!tx])
                return wm8960_configure_clocking(component);
 
        return 0;
index f2d6f2f..d39d479 100644 (file)
@@ -394,6 +394,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = {
                .min_ch = 1,
                .max_ch = 1,
                .simple_ch_prep_sm = true,
+               .read_only_wordlength = true,
        }, {
                /* COMP */
                .num = 2,
@@ -401,6 +402,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = {
                .min_ch = 1,
                .max_ch = 1,
                .simple_ch_prep_sm = true,
+               .read_only_wordlength = true,
        }, {
                /* BOOST */
                .num = 3,
@@ -408,6 +410,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = {
                .min_ch = 1,
                .max_ch = 1,
                .simple_ch_prep_sm = true,
+               .read_only_wordlength = true,
        }, {
                /* VISENSE */
                .num = 4,
@@ -415,6 +418,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = {
                .min_ch = 1,
                .max_ch = 1,
                .simple_ch_prep_sm = true,
+               .read_only_wordlength = true,
        }
 };
 
index bcedec6..7d85bd5 100644 (file)
@@ -113,14 +113,6 @@ static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = {
        }
 };
 
-static const struct snd_soc_acpi_adr_device rt1308_2_adr[] = {
-       {
-               .adr = 0x000210025D130800,
-               .num_endpoints = 1,
-               .endpoints = &single_endpoint,
-       }
-};
-
 static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = {
        {
                .adr = 0x000110025D130800,
index ef85003..16ec9f3 100644 (file)
@@ -87,14 +87,6 @@ static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = {
        }
 };
 
-static const struct snd_soc_acpi_adr_device rt1308_2_adr[] = {
-       {
-               .adr = 0x000210025D130800,
-               .num_endpoints = 1,
-               .endpoints = &single_endpoint,
-       }
-};
-
 static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = {
        {
                .adr = 0x000110025D130800,
index af46845..89f7f64 100644 (file)
@@ -338,8 +338,10 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
 
        if (axg_card_cpu_is_tdm_iface(dai_link->cpus->of_node))
                ret = axg_card_parse_tdm(card, np, index);
-       else if (axg_card_cpu_is_codec(dai_link->cpus->of_node))
+       else if (axg_card_cpu_is_codec(dai_link->cpus->of_node)) {
                dai_link->params = &codec_params;
+               dai_link->no_pcm = 0; /* link is not a DPCM BE */
+       }
 
        return ret;
 }
index 7b01dcb..4abf7ef 100644 (file)
@@ -108,8 +108,10 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np,
                ret = gx_card_parse_i2s(card, np, index);
 
        /* Or apply codec to codec params if necessary */
-       else if (gx_card_cpu_identify(dai_link->cpus, "CODEC CTRL"))
+       else if (gx_card_cpu_identify(dai_link->cpus, "CODEC CTRL")) {
                dai_link->params = &codec_params;
+               dai_link->no_pcm = 0; /* link is not a DPCM BE */
+       }
 
        return ret;
 }
index d55e3ad..287ad2a 100644 (file)
@@ -116,10 +116,8 @@ static int apq8096_platform_probe(struct platform_device *pdev)
        card->dev = dev;
        dev_set_drvdata(dev, card);
        ret = qcom_snd_parse_of(card);
-       if (ret) {
-               dev_err(dev, "Error parsing OF data\n");
+       if (ret)
                goto err;
-       }
 
        apq8096_add_be_ops(card);
        ret = snd_soc_register_card(card);
index c1a7624..2a5302f 100644 (file)
@@ -902,6 +902,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
                                 SNDRV_PCM_RATE_16000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE |
                                   SNDRV_PCM_FMTBIT_S24_LE,
+                       .channels_min = 1,
+                       .channels_max = 8,
                        .rate_min =     8000,
                        .rate_max =     48000,
                },
@@ -917,6 +919,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
                                 SNDRV_PCM_RATE_16000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE |
                                   SNDRV_PCM_FMTBIT_S24_LE,
+                       .channels_min = 1,
+                       .channels_max = 8,
                        .rate_min =     8000,
                        .rate_max =     48000,
                },
@@ -931,6 +935,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
                        .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
                                 SNDRV_PCM_RATE_16000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
+                       .channels_min = 1,
+                       .channels_max = 8,
                        .rate_min =     8000,
                        .rate_max =     48000,
                },
@@ -946,6 +952,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
                                 SNDRV_PCM_RATE_16000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE |
                                   SNDRV_PCM_FMTBIT_S24_LE,
+                       .channels_min = 1,
+                       .channels_max = 8,
                        .rate_min =     8000,
                        .rate_max =     48000,
                },
@@ -960,6 +968,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
                        .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
                                 SNDRV_PCM_RATE_16000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
+                       .channels_min = 1,
+                       .channels_max = 8,
                        .rate_min =     8000,
                        .rate_max =     48000,
                },
@@ -975,6 +985,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
                                 SNDRV_PCM_RATE_16000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE |
                                   SNDRV_PCM_FMTBIT_S24_LE,
+                       .channels_min = 1,
+                       .channels_max = 8,
                        .rate_min =     8000,
                        .rate_max =     48000,
                },
@@ -989,6 +1001,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
                        .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
                                 SNDRV_PCM_RATE_16000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
+                       .channels_min = 1,
+                       .channels_max = 8,
                        .rate_min =     8000,
                        .rate_max =     48000,
                },
@@ -1004,6 +1018,8 @@ static struct snd_soc_dai_driver q6afe_dais[] = {
                                 SNDRV_PCM_RATE_16000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE |
                                   SNDRV_PCM_FMTBIT_S24_LE,
+                       .channels_min = 1,
+                       .channels_max = 8,
                        .rate_min =     8000,
                        .rate_max =     48000,
                },
index b2de65c..68e9388 100644 (file)
@@ -559,10 +559,8 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev)
        card->dev = dev;
        dev_set_drvdata(dev, card);
        ret = qcom_snd_parse_of(card);
-       if (ret) {
-               dev_err(dev, "Error parsing OF data\n");
+       if (ret)
                goto parse_dt_fail;
-       }
 
        data->card = card;
        snd_soc_card_set_drvdata(card, data);
index 3588878..5e95c30 100644 (file)
@@ -656,60 +656,6 @@ void s3c_i2sv2_cleanup(struct snd_soc_dai *dai,
 }
 EXPORT_SYMBOL_GPL(s3c_i2sv2_cleanup);
 
-#ifdef CONFIG_PM
-static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
-{
-       struct s3c_i2sv2_info *i2s = to_info(dai);
-       u32 iismod;
-
-       if (dai->active) {
-               i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
-               i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
-               i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
-
-               /* some basic suspend checks */
-
-               iismod = readl(i2s->regs + S3C2412_IISMOD);
-
-               if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
-                       pr_warn("%s: RXDMA active?\n", __func__);
-
-               if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
-                       pr_warn("%s: TXDMA active?\n", __func__);
-
-               if (iismod & S3C2412_IISCON_IIS_ACTIVE)
-                       pr_warn("%s: IIS active\n", __func__);
-       }
-
-       return 0;
-}
-
-static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
-{
-       struct s3c_i2sv2_info *i2s = to_info(dai);
-
-       pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
-               dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
-
-       if (dai->active) {
-               writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
-               writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
-               writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
-
-               writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
-                      i2s->regs + S3C2412_IISFIC);
-
-               ndelay(250);
-               writel(0x0, i2s->regs + S3C2412_IISFIC);
-       }
-
-       return 0;
-}
-#else
-#define s3c2412_i2s_suspend NULL
-#define s3c2412_i2s_resume  NULL
-#endif
-
 int s3c_i2sv2_register_component(struct device *dev, int id,
                           const struct snd_soc_component_driver *cmp_drv,
                           struct snd_soc_dai_driver *dai_drv)
@@ -727,9 +673,6 @@ int s3c_i2sv2_register_component(struct device *dev, int id,
        if (!ops->delay)
                ops->delay = s3c2412_i2s_delay;
 
-       dai_drv->suspend = s3c2412_i2s_suspend;
-       dai_drv->resume = s3c2412_i2s_resume;
-
        return devm_snd_soc_register_component(dev, cmp_drv, dai_drv, 1);
 }
 EXPORT_SYMBOL_GPL(s3c_i2sv2_register_component);
index 787a3f6..b35d828 100644 (file)
@@ -117,6 +117,60 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int s3c2412_i2s_suspend(struct snd_soc_component *component)
+{
+       struct s3c_i2sv2_info *i2s = snd_soc_component_get_drvdata(component);
+       u32 iismod;
+
+       if (component->active) {
+               i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
+               i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
+               i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
+
+               /* some basic suspend checks */
+
+               iismod = readl(i2s->regs + S3C2412_IISMOD);
+
+               if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
+                       pr_warn("%s: RXDMA active?\n", __func__);
+
+               if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
+                       pr_warn("%s: TXDMA active?\n", __func__);
+
+               if (iismod & S3C2412_IISCON_IIS_ACTIVE)
+                       pr_warn("%s: IIS active\n", __func__);
+       }
+
+       return 0;
+}
+
+static int s3c2412_i2s_resume(struct snd_soc_component *component)
+{
+       struct s3c_i2sv2_info *i2s = snd_soc_component_get_drvdata(component);
+
+       pr_info("component_active %d, IISMOD %08x, IISCON %08x\n",
+               component->active, i2s->suspend_iismod, i2s->suspend_iiscon);
+
+       if (component->active) {
+               writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
+               writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
+               writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
+
+               writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
+                      i2s->regs + S3C2412_IISFIC);
+
+               ndelay(250);
+               writel(0x0, i2s->regs + S3C2412_IISFIC);
+       }
+
+       return 0;
+}
+#else
+#define s3c2412_i2s_suspend NULL
+#define s3c2412_i2s_resume  NULL
+#endif
+
 #define S3C2412_I2S_RATES \
        (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
        SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
@@ -146,6 +200,8 @@ static struct snd_soc_dai_driver s3c2412_i2s_dai = {
 
 static const struct snd_soc_component_driver s3c2412_i2s_component = {
        .name           = "s3c2412-i2s",
+       .suspend        = s3c2412_i2s_suspend,
+       .resume         = s3c2412_i2s_resume,
 };
 
 static int s3c2412_iis_dev_probe(struct platform_device *pdev)
index fc5d089..4a7d341 100644 (file)
@@ -594,10 +594,16 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod,
         * Capture:  It might not receave data. Do nothing
         */
        if (rsnd_io_is_play(io)) {
-               rsnd_mod_write(mod, SSICR, cr | EN);
+               rsnd_mod_write(mod, SSICR, cr | ssi->cr_en);
                rsnd_ssi_status_check(mod, DIRQ);
        }
 
+       /* In multi-SSI mode, stop is performed by setting ssi0129 in
+        * SSI_CONTROL to 0 (in rsnd_ssio_stop_gen2). Do nothing here.
+        */
+       if (rsnd_ssi_multi_slaves_runtime(io))
+               return 0;
+
        /*
         * disable SSI,
         * and, wait idle state
@@ -737,6 +743,9 @@ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
        if (!rsnd_rdai_is_clk_master(rdai))
                return;
 
+       if (rsnd_ssi_is_multi_slave(mod, io))
+               return;
+
        switch (rsnd_mod_id(mod)) {
        case 1:
        case 2:
index f35d882..9c7c3e7 100644 (file)
@@ -221,7 +221,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
                        i;
 
                for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) {
-                       shift   = (i * 4) + 16;
+                       shift   = (i * 4) + 20;
                        val     = (val & ~(0xF << shift)) |
                                rsnd_mod_id(pos) << shift;
                }
index 8f3cad8..31c4155 100644 (file)
@@ -295,24 +295,17 @@ int snd_soc_dai_startup(struct snd_soc_dai *dai,
 {
        int ret = 0;
 
-       if (!dai->started[substream->stream] &&
-           dai->driver->ops->startup)
+       if (dai->driver->ops->startup)
                ret = dai->driver->ops->startup(substream, dai);
 
-       if (ret == 0)
-               dai->started[substream->stream] = 1;
-
        return ret;
 }
 
 void snd_soc_dai_shutdown(struct snd_soc_dai *dai,
                         struct snd_pcm_substream *substream)
 {
-       if (dai->started[substream->stream] &&
-           dai->driver->ops->shutdown)
+       if (dai->driver->ops->shutdown)
                dai->driver->ops->shutdown(substream, dai);
-
-       dai->started[substream->stream] = 0;
 }
 
 int snd_soc_dai_prepare(struct snd_soc_dai *dai,
index 679ed60..e263284 100644 (file)
@@ -423,7 +423,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
 
                        memset(&template, 0, sizeof(template));
                        template.reg = e->reg;
-                       template.mask = e->mask << e->shift_l;
+                       template.mask = e->mask;
                        template.shift = e->shift_l;
                        template.off_val = snd_soc_enum_item_to_val(e, 0);
                        template.on_val = template.off_val;
@@ -546,8 +546,22 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
        if (data->value == value)
                return false;
 
-       if (data->widget)
-               data->widget->on_val = value;
+       if (data->widget) {
+               switch (dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->id) {
+               case snd_soc_dapm_switch:
+               case snd_soc_dapm_mixer:
+               case snd_soc_dapm_mixer_named_ctl:
+                       data->widget->on_val = value & data->widget->mask;
+                       break;
+               case snd_soc_dapm_demux:
+               case snd_soc_dapm_mux:
+                       data->widget->on_val = value >> data->widget->shift;
+                       break;
+               default:
+                       data->widget->on_val = value;
+                       break;
+               }
+       }
 
        data->value = value;
 
@@ -4165,6 +4179,8 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card,
        w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template);
        if (IS_ERR(w)) {
                ret = PTR_ERR(w);
+               dev_err(rtd->dev, "ASoC: Failed to create %s widget: %d\n",
+                       link_name, ret);
                goto outfree_kcontrol_news;
        }
 
@@ -4283,52 +4299,58 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
        return 0;
 }
 
-static void dapm_add_valid_dai_widget(struct snd_soc_card *card,
-                                     struct snd_soc_pcm_runtime *rtd,
-                                     struct snd_soc_dai *codec_dai,
-                                     struct snd_soc_dai *cpu_dai)
+static void dapm_connect_dai_routes(struct snd_soc_dapm_context *dapm,
+                                   struct snd_soc_dai *src_dai,
+                                   struct snd_soc_dapm_widget *src,
+                                   struct snd_soc_dapm_widget *dai,
+                                   struct snd_soc_dai *sink_dai,
+                                   struct snd_soc_dapm_widget *sink)
 {
-       struct snd_soc_dapm_widget *playback = NULL, *capture = NULL;
-       struct snd_soc_dapm_widget *codec, *playback_cpu, *capture_cpu;
+       dev_dbg(dapm->dev, "connected DAI link %s:%s -> %s:%s\n",
+               src_dai->component->name, src->name,
+               sink_dai->component->name, sink->name);
+
+       if (dai) {
+               snd_soc_dapm_add_path(dapm, src, dai, NULL, NULL);
+               src = dai;
+       }
+
+       snd_soc_dapm_add_path(dapm, src, sink, NULL, NULL);
+}
+
+static void dapm_connect_dai_pair(struct snd_soc_card *card,
+                                 struct snd_soc_pcm_runtime *rtd,
+                                 struct snd_soc_dai *codec_dai,
+                                 struct snd_soc_dai *cpu_dai)
+{
+       struct snd_soc_dai_link *dai_link = rtd->dai_link;
+       struct snd_soc_dapm_widget *dai, *codec, *playback_cpu, *capture_cpu;
        struct snd_pcm_substream *substream;
        struct snd_pcm_str *streams = rtd->pcm->streams;
 
-       if (rtd->dai_link->params) {
+       if (dai_link->params) {
                playback_cpu = cpu_dai->capture_widget;
                capture_cpu = cpu_dai->playback_widget;
        } else {
-               playback = cpu_dai->playback_widget;
-               capture = cpu_dai->capture_widget;
-               playback_cpu = playback;
-               capture_cpu = capture;
+               playback_cpu = cpu_dai->playback_widget;
+               capture_cpu = cpu_dai->capture_widget;
        }
 
        /* connect BE DAI playback if widgets are valid */
        codec = codec_dai->playback_widget;
 
        if (playback_cpu && codec) {
-               if (!playback) {
+               if (dai_link->params && !dai_link->playback_widget) {
                        substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
-                       playback = snd_soc_dapm_new_dai(card, substream,
-                                                       "playback");
-                       if (IS_ERR(playback)) {
-                               dev_err(rtd->dev,
-                                       "ASoC: Failed to create DAI %s: %ld\n",
-                                       codec_dai->name,
-                                       PTR_ERR(playback));
+                       dai = snd_soc_dapm_new_dai(card, substream, "playback");
+                       if (IS_ERR(dai))
                                goto capture;
-                       }
-
-                       snd_soc_dapm_add_path(&card->dapm, playback_cpu,
-                                             playback, NULL, NULL);
+                       dai_link->playback_widget = dai;
                }
 
-               dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
-                       cpu_dai->component->name, playback_cpu->name,
-                       codec_dai->component->name, codec->name);
-
-               snd_soc_dapm_add_path(&card->dapm, playback, codec,
-                                     NULL, NULL);
+               dapm_connect_dai_routes(&card->dapm, cpu_dai, playback_cpu,
+                                       dai_link->playback_widget,
+                                       codec_dai, codec);
        }
 
 capture:
@@ -4336,50 +4358,18 @@ capture:
        codec = codec_dai->capture_widget;
 
        if (codec && capture_cpu) {
-               if (!capture) {
+               if (dai_link->params && !dai_link->capture_widget) {
                        substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream;
-                       capture = snd_soc_dapm_new_dai(card, substream,
-                                                      "capture");
-                       if (IS_ERR(capture)) {
-                               dev_err(rtd->dev,
-                                       "ASoC: Failed to create DAI %s: %ld\n",
-                                       codec_dai->name,
-                                       PTR_ERR(capture));
+                       dai = snd_soc_dapm_new_dai(card, substream, "capture");
+                       if (IS_ERR(dai))
                                return;
-                       }
-
-                       snd_soc_dapm_add_path(&card->dapm, capture,
-                                             capture_cpu, NULL, NULL);
+                       dai_link->capture_widget = dai;
                }
 
-               dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
-                       codec_dai->component->name, codec->name,
-                       cpu_dai->component->name, capture_cpu->name);
-
-               snd_soc_dapm_add_path(&card->dapm, codec, capture,
-                                     NULL, NULL);
-       }
-}
-
-static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
-                                         struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_dai *codec_dai;
-       int i;
-
-       if (rtd->num_cpus == 1) {
-               for_each_rtd_codec_dais(rtd, i, codec_dai)
-                       dapm_add_valid_dai_widget(card, rtd, codec_dai,
-                                                 rtd->cpu_dais[0]);
-       } else if (rtd->num_codecs == rtd->num_cpus) {
-               for_each_rtd_codec_dais(rtd, i, codec_dai)
-                       dapm_add_valid_dai_widget(card, rtd, codec_dai,
-                                                 rtd->cpu_dais[i]);
-       } else {
-               dev_err(card->dev,
-                       "N cpus to M codecs link is not supported yet\n");
+               dapm_connect_dai_routes(&card->dapm, codec_dai, codec,
+                                       dai_link->capture_widget,
+                                       cpu_dai, capture_cpu);
        }
-
 }
 
 static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
@@ -4422,6 +4412,8 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
 void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
 {
        struct snd_soc_pcm_runtime *rtd;
+       struct snd_soc_dai *codec_dai;
+       int i;
 
        /* for each BE DAI link... */
        for_each_card_rtds(card, rtd)  {
@@ -4432,7 +4424,18 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
                if (rtd->dai_link->dynamic)
                        continue;
 
-               dapm_connect_dai_link_widgets(card, rtd);
+               if (rtd->num_cpus == 1) {
+                       for_each_rtd_codec_dais(rtd, i, codec_dai)
+                               dapm_connect_dai_pair(card, rtd, codec_dai,
+                                                     rtd->cpu_dais[0]);
+               } else if (rtd->num_codecs == rtd->num_cpus) {
+                       for_each_rtd_codec_dais(rtd, i, codec_dai)
+                               dapm_connect_dai_pair(card, rtd, codec_dai,
+                                                     rtd->cpu_dais[i]);
+               } else {
+                       dev_err(card->dev,
+                               "N cpus to M codecs link is not supported yet\n");
+               }
        }
 }
 
index 289aebc..1f302de 100644 (file)
@@ -2911,8 +2911,17 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
        int i;
 
        if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
-               playback = rtd->dai_link->dpcm_playback;
-               capture = rtd->dai_link->dpcm_capture;
+               cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+               if (rtd->num_cpus > 1) {
+                       dev_err(rtd->dev,
+                               "DPCM doesn't support Multi CPU yet\n");
+                       return -EINVAL;
+               }
+
+               playback = rtd->dai_link->dpcm_playback &&
+                          snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK);
+               capture = rtd->dai_link->dpcm_capture &&
+                         snd_soc_dai_stream_valid(cpu_dai, SNDRV_PCM_STREAM_CAPTURE);
        } else {
                /* Adapt stream for codec2codec links */
                int cpu_capture = rtd->dai_link->params ?
index 87f75ed..6df3b0d 100644 (file)
@@ -894,7 +894,13 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count,
                }
 
                /* create any TLV data */
-               soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
+               err = soc_tplg_create_tlv(tplg, &kc, &mc->hdr);
+               if (err < 0) {
+                       dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
+                               mc->hdr.name);
+                       kfree(sm);
+                       continue;
+               }
 
                /* pass control to driver for optional further init */
                err = soc_tplg_init_kcontrol(tplg, &kc,
@@ -1118,6 +1124,7 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
        struct snd_soc_tplg_hdr *hdr)
 {
        struct snd_soc_tplg_ctl_hdr *control_hdr;
+       int ret;
        int i;
 
        if (tplg->pass != SOC_TPLG_PASS_MIXER) {
@@ -1146,25 +1153,30 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
                case SND_SOC_TPLG_CTL_RANGE:
                case SND_SOC_TPLG_DAPM_CTL_VOLSW:
                case SND_SOC_TPLG_DAPM_CTL_PIN:
-                       soc_tplg_dmixer_create(tplg, 1,
-                                              le32_to_cpu(hdr->payload_size));
+                       ret = soc_tplg_dmixer_create(tplg, 1,
+                                       le32_to_cpu(hdr->payload_size));
                        break;
                case SND_SOC_TPLG_CTL_ENUM:
                case SND_SOC_TPLG_CTL_ENUM_VALUE:
                case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
                case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
                case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
-                       soc_tplg_denum_create(tplg, 1,
-                                             le32_to_cpu(hdr->payload_size));
+                       ret = soc_tplg_denum_create(tplg, 1,
+                                       le32_to_cpu(hdr->payload_size));
                        break;
                case SND_SOC_TPLG_CTL_BYTES:
-                       soc_tplg_dbytes_create(tplg, 1,
-                                              le32_to_cpu(hdr->payload_size));
+                       ret = soc_tplg_dbytes_create(tplg, 1,
+                                       le32_to_cpu(hdr->payload_size));
                        break;
                default:
                        soc_bind_err(tplg, control_hdr, i);
                        return -EINVAL;
                }
+               if (ret < 0) {
+                       dev_err(tplg->dev, "ASoC: invalid control\n");
+                       return ret;
+               }
+
        }
 
        return 0;
@@ -1272,7 +1284,9 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
                routes[i]->dobj.index = tplg->index;
                list_add(&routes[i]->dobj.list, &tplg->comp->dobj_list);
 
-               soc_tplg_add_route(tplg, routes[i]);
+               ret = soc_tplg_add_route(tplg, routes[i]);
+               if (ret < 0)
+                       break;
 
                /* add route, but keep going if some fail */
                snd_soc_dapm_add_routes(dapm, routes[i], 1);
@@ -1355,7 +1369,13 @@ static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create(
                }
 
                /* create any TLV data */
-               soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
+               err = soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr);
+               if (err < 0) {
+                       dev_err(tplg->dev, "ASoC: failed to create TLV %s\n",
+                               mc->hdr.name);
+                       kfree(sm);
+                       continue;
+               }
 
                /* pass control to driver for optional further init */
                err = soc_tplg_init_kcontrol(tplg, &kc[i],
@@ -1766,10 +1786,13 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg)
        return 0;
 }
 
-static void set_stream_info(struct snd_soc_pcm_stream *stream,
+static int set_stream_info(struct snd_soc_pcm_stream *stream,
        struct snd_soc_tplg_stream_caps *caps)
 {
        stream->stream_name = kstrdup(caps->name, GFP_KERNEL);
+       if (!stream->stream_name)
+               return -ENOMEM;
+
        stream->channels_min = le32_to_cpu(caps->channels_min);
        stream->channels_max = le32_to_cpu(caps->channels_max);
        stream->rates = le32_to_cpu(caps->rates);
@@ -1777,6 +1800,8 @@ static void set_stream_info(struct snd_soc_pcm_stream *stream,
        stream->rate_max = le32_to_cpu(caps->rate_max);
        stream->formats = le64_to_cpu(caps->formats);
        stream->sig_bits = le32_to_cpu(caps->sig_bits);
+
+       return 0;
 }
 
 static void set_dai_flags(struct snd_soc_dai_driver *dai_drv,
@@ -1812,20 +1837,29 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
        if (dai_drv == NULL)
                return -ENOMEM;
 
-       if (strlen(pcm->dai_name))
+       if (strlen(pcm->dai_name)) {
                dai_drv->name = kstrdup(pcm->dai_name, GFP_KERNEL);
+               if (!dai_drv->name) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+       }
        dai_drv->id = le32_to_cpu(pcm->dai_id);
 
        if (pcm->playback) {
                stream = &dai_drv->playback;
                caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
-               set_stream_info(stream, caps);
+               ret = set_stream_info(stream, caps);
+               if (ret < 0)
+                       goto err;
        }
 
        if (pcm->capture) {
                stream = &dai_drv->capture;
                caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE];
-               set_stream_info(stream, caps);
+               ret = set_stream_info(stream, caps);
+               if (ret < 0)
+                       goto err;
        }
 
        if (pcm->compress)
@@ -1835,11 +1869,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
        ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL);
        if (ret < 0) {
                dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n");
-               kfree(dai_drv->playback.stream_name);
-               kfree(dai_drv->capture.stream_name);
-               kfree(dai_drv->name);
-               kfree(dai_drv);
-               return ret;
+               goto err;
        }
 
        dai_drv->dobj.index = tplg->index;
@@ -1860,6 +1890,14 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
                return ret;
        }
 
+       return 0;
+
+err:
+       kfree(dai_drv->playback.stream_name);
+       kfree(dai_drv->capture.stream_name);
+       kfree(dai_drv->name);
+       kfree(dai_drv);
+
        return ret;
 }
 
@@ -1916,11 +1954,20 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
        if (strlen(pcm->pcm_name)) {
                link->name = kstrdup(pcm->pcm_name, GFP_KERNEL);
                link->stream_name = kstrdup(pcm->pcm_name, GFP_KERNEL);
+               if (!link->name || !link->stream_name) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
        }
        link->id = le32_to_cpu(pcm->pcm_id);
 
-       if (strlen(pcm->dai_name))
+       if (strlen(pcm->dai_name)) {
                link->cpus->dai_name = kstrdup(pcm->dai_name, GFP_KERNEL);
+               if (!link->cpus->dai_name) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+       }
 
        link->codecs->name = "snd-soc-dummy";
        link->codecs->dai_name = "snd-soc-dummy-dai";
@@ -2088,7 +2135,9 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
                        _pcm = pcm;
                } else {
                        abi_match = false;
-                       pcm_new_ver(tplg, pcm, &_pcm);
+                       ret = pcm_new_ver(tplg, pcm, &_pcm);
+                       if (ret < 0)
+                               return ret;
                }
 
                /* create the FE DAIs and DAI links */
@@ -2436,13 +2485,17 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg,
        if (d->playback) {
                stream = &dai_drv->playback;
                caps = &d->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
-               set_stream_info(stream, caps);
+               ret = set_stream_info(stream, caps);
+               if (ret < 0)
+                       goto err;
        }
 
        if (d->capture) {
                stream = &dai_drv->capture;
                caps = &d->caps[SND_SOC_TPLG_STREAM_CAPTURE];
-               set_stream_info(stream, caps);
+               ret = set_stream_info(stream, caps);
+               if (ret < 0)
+                       goto err;
        }
 
        if (d->flag_mask)
@@ -2454,10 +2507,15 @@ static int soc_tplg_dai_config(struct soc_tplg *tplg,
        ret = soc_tplg_dai_load(tplg, dai_drv, NULL, dai);
        if (ret < 0) {
                dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n");
-               return ret;
+               goto err;
        }
 
        return 0;
+
+err:
+       kfree(dai_drv->playback.stream_name);
+       kfree(dai_drv->capture.stream_name);
+       return ret;
 }
 
 /* load physical DAI elements */
@@ -2466,7 +2524,7 @@ static int soc_tplg_dai_elems_load(struct soc_tplg *tplg,
 {
        struct snd_soc_tplg_dai *dai;
        int count;
-       int i;
+       int i, ret;
 
        count = le32_to_cpu(hdr->count);
 
@@ -2481,7 +2539,12 @@ static int soc_tplg_dai_elems_load(struct soc_tplg *tplg,
                        return -EINVAL;
                }
 
-               soc_tplg_dai_config(tplg, dai);
+               ret = soc_tplg_dai_config(tplg, dai);
+               if (ret < 0) {
+                       dev_err(tplg->dev, "ASoC: failed to configure DAI\n");
+                       return ret;
+               }
+
                tplg->pos += (sizeof(*dai) + le32_to_cpu(dai->priv.size));
        }
 
@@ -2589,7 +2652,7 @@ static int soc_valid_header(struct soc_tplg *tplg,
        }
 
        /* big endian firmware objects not supported atm */
-       if (hdr->magic == SOC_TPLG_MAGIC_BIG_ENDIAN) {
+       if (le32_to_cpu(hdr->magic) == SOC_TPLG_MAGIC_BIG_ENDIAN) {
                dev_err(tplg->dev,
                        "ASoC: pass %d big endian not supported header got %x at offset 0x%lx size 0x%zx.\n",
                        tplg->pass, hdr->magic,
index 6c23c57..a32a3ef 100644 (file)
@@ -567,9 +567,25 @@ static void bdw_set_mach_params(const struct snd_soc_acpi_mach *mach,
 static struct snd_soc_dai_driver bdw_dai[] = {
 {
        .name = "ssp0-port",
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
 },
 {
        .name = "ssp1-port",
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
 },
 };
 
index f843912..29fd1d8 100644 (file)
@@ -459,21 +459,69 @@ static void byt_set_mach_params(const struct snd_soc_acpi_mach *mach,
 static struct snd_soc_dai_driver byt_dai[] = {
 {
        .name = "ssp0-port",
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
 },
 {
        .name = "ssp1-port",
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
 },
 {
        .name = "ssp2-port",
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 8,
+       }
 },
 {
        .name = "ssp3-port",
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
 },
 {
        .name = "ssp4-port",
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
 },
 {
        .name = "ssp5-port",
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 8,
+       },
 },
 };
 
index 0d0c9af..41f01c3 100644 (file)
@@ -837,7 +837,7 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai,
                cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_32);
                break;
        default:
-               dev_err(cpu_dai->dev, "Data format not supported");
+               dev_err(cpu_dai->dev, "Data format not supported\n");
                return -EINVAL;
        }
 
@@ -1547,6 +1547,9 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)
                return ret;
        }
 
+       if (STM_SAI_PROTOCOL_IS_SPDIF(sai))
+               conf = &stm32_sai_pcm_config_spdif;
+
        ret = snd_dmaengine_pcm_register(&pdev->dev, conf, 0);
        if (ret) {
                if (ret != -EPROBE_DEFER)
@@ -1556,15 +1559,10 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)
 
        ret = snd_soc_register_component(&pdev->dev, &stm32_component,
                                         &sai->cpu_dai_drv, 1);
-       if (ret) {
+       if (ret)
                snd_dmaengine_pcm_unregister(&pdev->dev);
-               return ret;
-       }
-
-       if (STM_SAI_PROTOCOL_IS_SPDIF(sai))
-               conf = &stm32_sai_pcm_config_spdif;
 
-       return 0;
+       return ret;
 }
 
 static int stm32_sai_sub_remove(struct platform_device *pdev)
index 50e1874..5ffb457 100644 (file)
@@ -277,6 +277,52 @@ static bool s1810c_valid_sample_rate(struct audioformat *fp,
        return false;
 }
 
+/*
+ * Many Focusrite devices supports a limited set of sampling rates per
+ * altsetting. Maximum rate is exposed in the last 4 bytes of Format Type
+ * descriptor which has a non-standard bLength = 10.
+ */
+static bool focusrite_valid_sample_rate(struct snd_usb_audio *chip,
+                                       struct audioformat *fp,
+                                       unsigned int rate)
+{
+       struct usb_interface *iface;
+       struct usb_host_interface *alts;
+       unsigned char *fmt;
+       unsigned int max_rate;
+
+       iface = usb_ifnum_to_if(chip->dev, fp->iface);
+       if (!iface)
+               return true;
+
+       alts = &iface->altsetting[fp->altset_idx];
+       fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen,
+                                     NULL, UAC_FORMAT_TYPE);
+       if (!fmt)
+               return true;
+
+       if (fmt[0] == 10) { /* bLength */
+               max_rate = combine_quad(&fmt[6]);
+
+               /* Validate max rate */
+               if (max_rate != 48000 &&
+                   max_rate != 96000 &&
+                   max_rate != 192000 &&
+                   max_rate != 384000) {
+
+                       usb_audio_info(chip,
+                               "%u:%d : unexpected max rate: %u\n",
+                               fp->iface, fp->altsetting, max_rate);
+
+                       return true;
+               }
+
+               return rate <= max_rate;
+       }
+
+       return true;
+}
+
 /*
  * Helper function to walk the array of sample rate triplets reported by
  * the device. The problem is that we need to parse whole array first to
@@ -319,6 +365,11 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
                            !s1810c_valid_sample_rate(fp, rate))
                                goto skip_rate;
 
+                       /* Filter out invalid rates on Focusrite devices */
+                       if (USB_ID_VENDOR(chip->usb_id) == 0x1235 &&
+                           !focusrite_valid_sample_rate(chip, fp, rate))
+                               goto skip_rate;
+
                        if (fp->rate_table)
                                fp->rate_table[nr_rates] = rate;
                        if (!fp->rate_min || rate < fp->rate_min)
index d37db32..e39dc85 100644 (file)
@@ -21,8 +21,7 @@
 enum {
        LINE6_PODHD300,
        LINE6_PODHD400,
-       LINE6_PODHD500_0,
-       LINE6_PODHD500_1,
+       LINE6_PODHD500,
        LINE6_PODX3,
        LINE6_PODX3LIVE,
        LINE6_PODHD500X,
@@ -318,8 +317,7 @@ static const struct usb_device_id podhd_id_table[] = {
        /* TODO: no need to alloc data interfaces when only audio is used */
        { LINE6_DEVICE(0x5057),    .driver_info = LINE6_PODHD300 },
        { LINE6_DEVICE(0x5058),    .driver_info = LINE6_PODHD400 },
-       { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
-       { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
+       { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500 },
        { LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
        { LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE },
        { LINE6_IF_NUM(0x4159, 0), .driver_info = LINE6_PODHD500X },
@@ -352,23 +350,13 @@ static const struct line6_properties podhd_properties_table[] = {
                .ep_audio_r = 0x82,
                .ep_audio_w = 0x01,
        },
-       [LINE6_PODHD500_0] = {
+       [LINE6_PODHD500] = {
                .id = "PODHD500",
                .name = "POD HD500",
-               .capabilities   = LINE6_CAP_PCM
+               .capabilities   = LINE6_CAP_PCM | LINE6_CAP_CONTROL
                                | LINE6_CAP_HWMON,
                .altsetting = 1,
-               .ep_ctrl_r = 0x81,
-               .ep_ctrl_w = 0x01,
-               .ep_audio_r = 0x86,
-               .ep_audio_w = 0x02,
-       },
-       [LINE6_PODHD500_1] = {
-               .id = "PODHD500",
-               .name = "POD HD500",
-               .capabilities   = LINE6_CAP_PCM
-                               | LINE6_CAP_HWMON,
-               .altsetting = 0,
+               .ctrl_if = 1,
                .ep_ctrl_r = 0x81,
                .ep_ctrl_w = 0x01,
                .ep_audio_r = 0x86,
index 721d121..a88d785 100644 (file)
@@ -1457,7 +1457,7 @@ error:
                usb_audio_err(chip,
                        "cannot get connectors status: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
                        UAC_GET_CUR, validx, idx, cval->val_type);
-               return ret;
+               return filter_error(cval, ret);
        }
 
        ucontrol->value.integer.value[0] = val;
@@ -1771,10 +1771,16 @@ static void get_connector_control_name(struct usb_mixer_interface *mixer,
 
 /* Build a mixer control for a UAC connector control (jack-detect) */
 static void build_connector_control(struct usb_mixer_interface *mixer,
+                                   const struct usbmix_name_map *imap,
                                    struct usb_audio_term *term, bool is_input)
 {
        struct snd_kcontrol *kctl;
        struct usb_mixer_elem_info *cval;
+       const struct usbmix_name_map *map;
+
+       map = find_map(imap, term->id, 0);
+       if (check_ignored_ctl(map))
+               return;
 
        cval = kzalloc(sizeof(*cval), GFP_KERNEL);
        if (!cval)
@@ -1805,8 +1811,12 @@ static void build_connector_control(struct usb_mixer_interface *mixer,
                usb_mixer_elem_info_free(cval);
                return;
        }
-       get_connector_control_name(mixer, term, is_input, kctl->id.name,
-                                  sizeof(kctl->id.name));
+
+       if (check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)))
+               strlcat(kctl->id.name, " Jack", sizeof(kctl->id.name));
+       else
+               get_connector_control_name(mixer, term, is_input, kctl->id.name,
+                                          sizeof(kctl->id.name));
        kctl->private_free = snd_usb_mixer_elem_free;
        snd_usb_mixer_add_control(&cval->head, kctl);
 }
@@ -2109,8 +2119,9 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid,
        check_input_term(state, term_id, &iterm);
 
        /* Check for jack detection. */
-       if (uac_v2v3_control_is_readable(bmctls, control))
-               build_connector_control(state->mixer, &iterm, true);
+       if ((iterm.type & 0xff00) != 0x0100 &&
+           uac_v2v3_control_is_readable(bmctls, control))
+               build_connector_control(state->mixer, state->map, &iterm, true);
 
        return 0;
 }
@@ -3071,13 +3082,13 @@ static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer,
                memset(&iterm, 0, sizeof(iterm));
                iterm.id = UAC3_BADD_IT_ID4;
                iterm.type = UAC_BIDIR_TERMINAL_HEADSET;
-               build_connector_control(mixer, &iterm, true);
+               build_connector_control(mixer, map->map, &iterm, true);
 
                /* Output Term - Insertion control */
                memset(&oterm, 0, sizeof(oterm));
                oterm.id = UAC3_BADD_OT_ID3;
                oterm.type = UAC_BIDIR_TERMINAL_HEADSET;
-               build_connector_control(mixer, &oterm, false);
+               build_connector_control(mixer, map->map, &oterm, false);
        }
 
        return 0;
@@ -3106,7 +3117,8 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
                if (map->id == state.chip->usb_id) {
                        state.map = map->map;
                        state.selector_map = map->selector_map;
-                       mixer->ignore_ctl_error = map->ignore_ctl_error;
+                       mixer->connector_map = map->connector_map;
+                       mixer->ignore_ctl_error |= map->ignore_ctl_error;
                        break;
                }
        }
@@ -3149,10 +3161,11 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
                        if (err < 0 && err != -EINVAL)
                                return err;
 
-                       if (uac_v2v3_control_is_readable(le16_to_cpu(desc->bmControls),
+                       if ((state.oterm.type & 0xff00) != 0x0100 &&
+                           uac_v2v3_control_is_readable(le16_to_cpu(desc->bmControls),
                                                         UAC2_TE_CONNECTOR)) {
-                               build_connector_control(state.mixer, &state.oterm,
-                                                       false);
+                               build_connector_control(state.mixer, state.map,
+                                                       &state.oterm, false);
                        }
                } else {  /* UAC_VERSION_3 */
                        struct uac3_output_terminal_descriptor *desc = p;
@@ -3174,10 +3187,11 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
                        if (err < 0 && err != -EINVAL)
                                return err;
 
-                       if (uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls),
+                       if ((state.oterm.type & 0xff00) != 0x0100 &&
+                           uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls),
                                                         UAC3_TE_INSERTION)) {
-                               build_connector_control(state.mixer, &state.oterm,
-                                                       false);
+                               build_connector_control(state.mixer, state.map,
+                                                       &state.oterm, false);
                        }
                }
        }
@@ -3185,10 +3199,32 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
        return 0;
 }
 
+static int delegate_notify(struct usb_mixer_interface *mixer, int unitid,
+                          u8 *control, u8 *channel)
+{
+       const struct usbmix_connector_map *map = mixer->connector_map;
+
+       if (!map)
+               return unitid;
+
+       for (; map->id; map++) {
+               if (map->id == unitid) {
+                       if (control && map->control)
+                               *control = map->control;
+                       if (channel && map->channel)
+                               *channel = map->channel;
+                       return map->delegated_id;
+               }
+       }
+       return unitid;
+}
+
 void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
 {
        struct usb_mixer_elem_list *list;
 
+       unitid = delegate_notify(mixer, unitid, NULL, NULL);
+
        for_each_mixer_elem(list, mixer, unitid) {
                struct usb_mixer_elem_info *info =
                        mixer_elem_list_to_info(list);
@@ -3258,6 +3294,8 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
                return;
        }
 
+       unitid = delegate_notify(mixer, unitid, &control, &channel);
+
        for_each_mixer_elem(list, mixer, unitid)
                count++;
 
index 65d6d08..41ec9dc 100644 (file)
@@ -6,6 +6,13 @@
 
 struct media_mixer_ctl;
 
+struct usbmix_connector_map {
+       u8 id;
+       u8 delegated_id;
+       u8 control;
+       u8 channel;
+};
+
 struct usb_mixer_interface {
        struct snd_usb_audio *chip;
        struct usb_host_interface *hostif;
@@ -18,6 +25,9 @@ struct usb_mixer_interface {
        /* the usb audio specification version this interface complies to */
        int protocol;
 
+       /* optional connector delegation map */
+       const struct usbmix_connector_map *connector_map;
+
        /* Sound Blaster remote control stuff */
        const struct rc_config *rc_cfg;
        u32 rc_code;
index 72b575c..0260c75 100644 (file)
@@ -27,6 +27,7 @@ struct usbmix_ctl_map {
        u32 id;
        const struct usbmix_name_map *map;
        const struct usbmix_selector_map *selector_map;
+       const struct usbmix_connector_map *connector_map;
        int ignore_ctl_error;
 };
 
@@ -360,13 +361,42 @@ static const struct usbmix_name_map corsair_virtuoso_map[] = {
 };
 
 /* Some mobos shipped with a dummy HD-audio show the invalid GET_MIN/GET_MAX
- * response for Input Gain Pad (id=19, control=12).  Skip it.
+ * response for Input Gain Pad (id=19, control=12) and the connector status
+ * for SPDIF terminal (id=18).  Skip them.
  */
 static const struct usbmix_name_map asus_rog_map[] = {
+       { 18, NULL }, /* OT, connector control */
        { 19, NULL, 12 }, /* FU, Input Gain Pad */
        {}
 };
 
+/* TRX40 mobos with Realtek ALC1220-VB */
+static const struct usbmix_name_map trx40_mobo_map[] = {
+       { 18, NULL }, /* OT, IEC958 - broken response, disabled */
+       { 19, NULL, 12 }, /* FU, Input Gain Pad - broken response, disabled */
+       { 16, "Speaker" },              /* OT */
+       { 22, "Speaker Playback" },     /* FU */
+       { 7, "Line" },                  /* IT */
+       { 19, "Line Capture" },         /* FU */
+       { 17, "Front Headphone" },      /* OT */
+       { 23, "Front Headphone Playback" },     /* FU */
+       { 8, "Mic" },                   /* IT */
+       { 20, "Mic Capture" },          /* FU */
+       { 9, "Front Mic" },             /* IT */
+       { 21, "Front Mic Capture" },    /* FU */
+       { 24, "IEC958 Playback" },      /* FU */
+       {}
+};
+
+static const struct usbmix_connector_map trx40_mobo_connector_map[] = {
+       { 10, 16 },     /* (Back) Speaker */
+       { 11, 17 },     /* Front Headphone */
+       { 13, 7 },      /* Line */
+       { 14, 8 },      /* Mic */
+       { 15, 9 },      /* Front Mic */
+       {}
+};
+
 /*
  * Control map entries
  */
@@ -498,7 +528,8 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
        },
        {       /* Gigabyte TRX40 Aorus Pro WiFi */
                .id = USB_ID(0x0414, 0xa002),
-               .map = asus_rog_map,
+               .map = trx40_mobo_map,
+               .connector_map = trx40_mobo_connector_map,
        },
        {       /* ASUS ROG Zenith II */
                .id = USB_ID(0x0b05, 0x1916),
@@ -510,11 +541,13 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
        },
        {       /* MSI TRX40 Creator */
                .id = USB_ID(0x0db0, 0x0d64),
-               .map = asus_rog_map,
+               .map = trx40_mobo_map,
+               .connector_map = trx40_mobo_connector_map,
        },
        {       /* MSI TRX40 */
                .id = USB_ID(0x0db0, 0x543d),
-               .map = asus_rog_map,
+               .map = trx40_mobo_map,
+               .connector_map = trx40_mobo_connector_map,
        },
        { 0 } /* terminator */
 };
index 02b036b..a5f65a9 100644 (file)
@@ -1509,11 +1509,15 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
 
        /* use known values for that card: interface#1 altsetting#1 */
        iface = usb_ifnum_to_if(chip->dev, 1);
-       if (!iface || iface->num_altsetting < 2)
-               return -EINVAL;
+       if (!iface || iface->num_altsetting < 2) {
+               err = -EINVAL;
+               goto end;
+       }
        alts = &iface->altsetting[1];
-       if (get_iface_desc(alts)->bNumEndpoints < 1)
-               return -EINVAL;
+       if (get_iface_desc(alts)->bNumEndpoints < 1) {
+               err = -EINVAL;
+               goto end;
+       }
        ep = get_endpoint(alts, 0)->bEndpointAddress;
 
        err = snd_usb_ctl_msg(chip->dev,
index e009d58..a1df4c5 100644 (file)
@@ -2756,90 +2756,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                .type = QUIRK_MIDI_NOVATION
        }
 },
-{
-       /*
-        * Focusrite Scarlett Solo 2nd generation
-        * Reports that playback should use Synch: Synchronous
-        * while still providing a feedback endpoint. Synchronous causes
-        * snapping on some sample rates.
-        * Force it to use Synch: Asynchronous.
-        */
-       USB_DEVICE(0x1235, 0x8205),
-       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
-               .ifnum = QUIRK_ANY_INTERFACE,
-               .type = QUIRK_COMPOSITE,
-               .data = (const struct snd_usb_audio_quirk[]) {
-                       {
-                               .ifnum = 1,
-                               .type = QUIRK_AUDIO_FIXED_ENDPOINT,
-                               .data = & (const struct audioformat) {
-                                       .formats = SNDRV_PCM_FMTBIT_S32_LE,
-                                       .channels = 2,
-                                       .iface = 1,
-                                       .altsetting = 1,
-                                       .altset_idx = 1,
-                                       .attributes = 0,
-                                       .endpoint = 0x01,
-                                       .ep_attr = USB_ENDPOINT_XFER_ISOC |
-                                                  USB_ENDPOINT_SYNC_ASYNC,
-                                       .protocol = UAC_VERSION_2,
-                                       .rates = SNDRV_PCM_RATE_44100 |
-                                                SNDRV_PCM_RATE_48000 |
-                                                SNDRV_PCM_RATE_88200 |
-                                                SNDRV_PCM_RATE_96000 |
-                                                SNDRV_PCM_RATE_176400 |
-                                                SNDRV_PCM_RATE_192000,
-                                       .rate_min = 44100,
-                                       .rate_max = 192000,
-                                       .nr_rates = 6,
-                                       .rate_table = (unsigned int[]) {
-                                               44100, 48000, 88200,
-                                               96000, 176400, 192000
-                                       },
-                                       .clock = 41
-                               }
-                       },
-                       {
-                               .ifnum = 2,
-                               .type = QUIRK_AUDIO_FIXED_ENDPOINT,
-                               .data = & (const struct audioformat) {
-                                       .formats = SNDRV_PCM_FMTBIT_S32_LE,
-                                       .channels = 2,
-                                       .iface = 2,
-                                       .altsetting = 1,
-                                       .altset_idx = 1,
-                                       .attributes = 0,
-                                       .endpoint = 0x82,
-                                       .ep_attr = USB_ENDPOINT_XFER_ISOC |
-                                                  USB_ENDPOINT_SYNC_ASYNC |
-                                                  USB_ENDPOINT_USAGE_IMPLICIT_FB,
-                                       .protocol = UAC_VERSION_2,
-                                       .rates = SNDRV_PCM_RATE_44100 |
-                                                SNDRV_PCM_RATE_48000 |
-                                                SNDRV_PCM_RATE_88200 |
-                                                SNDRV_PCM_RATE_96000 |
-                                                SNDRV_PCM_RATE_176400 |
-                                                SNDRV_PCM_RATE_192000,
-                                       .rate_min = 44100,
-                                       .rate_max = 192000,
-                                       .nr_rates = 6,
-                                       .rate_table = (unsigned int[]) {
-                                               44100, 48000, 88200,
-                                               96000, 176400, 192000
-                                       },
-                                       .clock = 41
-                               }
-                       },
-                       {
-                               .ifnum = 3,
-                               .type = QUIRK_IGNORE_INTERFACE
-                       },
-                       {
-                               .ifnum = -1
-                       }
-               }
-       }
-},
 
 /* Access Music devices */
 {
@@ -3635,4 +3551,18 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
        }
 },
 
+#define ALC1220_VB_DESKTOP(vend, prod) { \
+       USB_DEVICE(vend, prod), \
+       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \
+               .vendor_name = "Realtek", \
+               .product_name = "ALC1220-VB-DT", \
+               .profile_name = "Realtek-ALC1220-VB-Desktop", \
+               .ifnum = QUIRK_NO_INTERFACE \
+       } \
+}
+ALC1220_VB_DESKTOP(0x0414, 0xa002), /* Gigabyte TRX40 Aorus Pro WiFi */
+ALC1220_VB_DESKTOP(0x0db0, 0x0d64), /* MSI TRX40 Creator */
+ALC1220_VB_DESKTOP(0x0db0, 0x543d), /* MSI TRX40 */
+#undef ALC1220_VB_DESKTOP
+
 #undef USB_DEVICE_VENDOR_SPEC
index a8ece17..848a4cc 100644 (file)
@@ -1687,7 +1687,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
 
        case USB_ID(0x0d8c, 0x0316): /* Hegel HD12 DSD */
        case USB_ID(0x10cb, 0x0103): /* The Bit Opus #3; with fp->dsd_raw */
-       case USB_ID(0x16b0, 0x06b2): /* NuPrime DAC-10 */
+       case USB_ID(0x16d0, 0x06b2): /* NuPrime DAC-10 */
        case USB_ID(0x16d0, 0x09dd): /* Encore mDSD */
        case USB_ID(0x16d0, 0x0733): /* Furutech ADL Stratos */
        case USB_ID(0x16d0, 0x09db): /* NuPrime Audio DAC-9 */
@@ -1806,6 +1806,20 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
                 */
                fp->attributes &= ~UAC_EP_CS_ATTR_FILL_MAX;
                break;
+       case USB_ID(0x1235, 0x8200):  /* Focusrite Scarlett 2i4 2nd gen */
+       case USB_ID(0x1235, 0x8202):  /* Focusrite Scarlett 2i2 2nd gen */
+       case USB_ID(0x1235, 0x8205):  /* Focusrite Scarlett Solo 2nd gen */
+               /*
+                * Reports that playback should use Synch: Synchronous
+                * while still providing a feedback endpoint.
+                * Synchronous causes snapping on some sample rates.
+                * Force it to use Synch: Asynchronous.
+                */
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
+                       fp->ep_attr |= USB_ENDPOINT_SYNC_ASYNC;
+               }
+               break;
        }
 }
 
index 37d290f..ecaf412 100644 (file)
@@ -681,6 +681,8 @@ static int usX2Y_rate_set(struct usX2Ydev *usX2Y, int rate)
                        us->submitted = 2*NOOF_SETRATE_URBS;
                        for (i = 0; i < NOOF_SETRATE_URBS; ++i) {
                                struct urb *urb = us->urb[i];
+                               if (!urb)
+                                       continue;
                                if (urb->status) {
                                        if (!err)
                                                err = -ENODEV;
index f3327cb..db18994 100644 (file)
 #define X86_FEATURE_IBRS               ( 7*32+25) /* Indirect Branch Restricted Speculation */
 #define X86_FEATURE_IBPB               ( 7*32+26) /* Indirect Branch Prediction Barrier */
 #define X86_FEATURE_STIBP              ( 7*32+27) /* Single Thread Indirect Branch Predictors */
-#define X86_FEATURE_ZEN                        ( 7*32+28) /* "" CPU is AMD family 0x17 (Zen) */
+#define X86_FEATURE_ZEN                        ( 7*32+28) /* "" CPU is AMD family 0x17 or above (Zen) */
 #define X86_FEATURE_L1TF_PTEINV                ( 7*32+29) /* "" L1TF workaround PTE inversion */
 #define X86_FEATURE_IBRS_ENHANCED      ( 7*32+30) /* Enhanced IBRS */
 #define X86_FEATURE_MSR_IA32_FEAT_CTL  ( 7*32+31) /* "" MSR IA32_FEAT_CTL configured */
 #define X86_FEATURE_CQM_MBM_LOCAL      (11*32+ 3) /* LLC Local MBM monitoring */
 #define X86_FEATURE_FENCE_SWAPGS_USER  (11*32+ 4) /* "" LFENCE in user entry SWAPGS path */
 #define X86_FEATURE_FENCE_SWAPGS_KERNEL        (11*32+ 5) /* "" LFENCE in kernel entry SWAPGS path */
+#define X86_FEATURE_SPLIT_LOCK_DETECT  (11*32+ 6) /* #AC for split lock */
 
 /* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */
 #define X86_FEATURE_AVX512_BF16                (12*32+ 5) /* AVX512 BFLOAT16 instructions */
 #define X86_FEATURE_AMD_IBRS           (13*32+14) /* "" Indirect Branch Restricted Speculation */
 #define X86_FEATURE_AMD_STIBP          (13*32+15) /* "" Single Thread Indirect Branch Predictors */
 #define X86_FEATURE_AMD_STIBP_ALWAYS_ON        (13*32+17) /* "" Single Thread Indirect Branch Predictors always-on preferred */
+#define X86_FEATURE_AMD_PPIN           (13*32+23) /* Protected Processor Inventory Number */
 #define X86_FEATURE_AMD_SSBD           (13*32+24) /* "" Speculative Store Bypass Disable */
 #define X86_FEATURE_VIRT_SSBD          (13*32+25) /* Virtualized Speculative Store Bypass Disable */
 #define X86_FEATURE_AMD_SSB_NO         (13*32+26) /* "" Speculative Store Bypass is fixed in hardware. */
 #define X86_FEATURE_INTEL_STIBP                (18*32+27) /* "" Single Thread Indirect Branch Predictors */
 #define X86_FEATURE_FLUSH_L1D          (18*32+28) /* Flush L1D cache */
 #define X86_FEATURE_ARCH_CAPABILITIES  (18*32+29) /* IA32_ARCH_CAPABILITIES MSR (Intel) */
+#define X86_FEATURE_CORE_CAPABILITIES  (18*32+30) /* "" IA32_CORE_CAPABILITIES MSR */
 #define X86_FEATURE_SPEC_CTRL_SSBD     (18*32+31) /* "" Speculative Store Bypass Disable */
 
 /*
index d5e517d..12c9684 100644 (file)
 
 /* Intel MSRs. Some also available on other CPUs */
 
+#define MSR_TEST_CTRL                          0x00000033
+#define MSR_TEST_CTRL_SPLIT_LOCK_DETECT_BIT    29
+#define MSR_TEST_CTRL_SPLIT_LOCK_DETECT                BIT(MSR_TEST_CTRL_SPLIT_LOCK_DETECT_BIT)
+
 #define MSR_IA32_SPEC_CTRL             0x00000048 /* Speculation Control */
 #define SPEC_CTRL_IBRS                 BIT(0)     /* Indirect Branch Restricted Speculation */
 #define SPEC_CTRL_STIBP_SHIFT          1          /* Single Thread Indirect Branch Predictor (STIBP) bit */
  */
 #define MSR_IA32_UMWAIT_CONTROL_TIME_MASK      (~0x03U)
 
+/* Abbreviated from Intel SDM name IA32_CORE_CAPABILITIES */
+#define MSR_IA32_CORE_CAPS                       0x000000cf
+#define MSR_IA32_CORE_CAPS_SPLIT_LOCK_DETECT_BIT  5
+#define MSR_IA32_CORE_CAPS_SPLIT_LOCK_DETECT     BIT(MSR_IA32_CORE_CAPS_SPLIT_LOCK_DETECT_BIT)
+
 #define MSR_PKG_CST_CONFIG_CONTROL     0x000000e2
 #define NHM_C3_AUTO_DEMOTE             (1UL << 25)
 #define NHM_C1_AUTO_DEMOTE             (1UL << 26)
index e5f95e3..0063c3c 100644 (file)
@@ -11,7 +11,7 @@
  *
  * How to get into it:
  *
- * 1) read Documentation/networking/filter.txt
+ * 1) read Documentation/networking/filter.rst
  * 2) Run `bpf_asm [-c] <filter-prog file>` to translate into binary
  *    blob that is loadable with xt_bpf, cls_bpf et al. Note: -c will
  *    pretty print a C-like construct.
index 9d3766e..a0ebcdf 100644 (file)
@@ -13,7 +13,7 @@
  * for making a verdict when multiple simple BPF programs are combined
  * into one in order to prevent parsing same headers multiple times.
  *
- * More on how to debug BPF opcodes see Documentation/networking/filter.txt
+ * More on how to debug BPF opcodes see Documentation/networking/filter.rst
  * which is the main document on BPF. Mini howto for getting started:
  *
  *  1) `./bpf_dbg` to enter the shell (shell cmds denoted with '>'):
index b04156c..1fa755f 100644 (file)
@@ -19,7 +19,7 @@ SYNOPSIS
 FEATURE COMMANDS
 ================
 
-|      **bpftool** **feature probe** [*COMPONENT*] [**full**] [**macros** [**prefix** *PREFIX*]]
+|      **bpftool** **feature probe** [*COMPONENT*] [**full**] [**unprivileged**] [**macros** [**prefix** *PREFIX*]]
 |      **bpftool** **feature help**
 |
 |      *COMPONENT* := { **kernel** | **dev** *NAME* }
@@ -49,6 +49,16 @@ DESCRIPTION
                  Keyword **kernel** can be omitted. If no probe target is
                  specified, probing the kernel is the default behaviour.
 
+                 When the **unprivileged** keyword is used, bpftool will dump
+                 only the features available to a user who does not have the
+                 **CAP_SYS_ADMIN** capability set. The features available in
+                 that case usually represent a small subset of the parameters
+                 supported by the system. Unprivileged users MUST use the
+                 **unprivileged** keyword: This is to avoid misdetection if
+                 bpftool is inadvertently run as non-root, for example. This
+                 keyword is unavailable if bpftool was compiled without
+                 libcap.
+
        **bpftool feature probe dev** *NAME* [**full**] [**macros** [**prefix** *PREFIX*]]
                  Probe network device for supported eBPF features and dump
                  results to the console.
diff --git a/tools/bpf/bpftool/Documentation/bpftool-link.rst b/tools/bpf/bpftool/Documentation/bpftool-link.rst
new file mode 100644 (file)
index 0000000..ee6500d
--- /dev/null
@@ -0,0 +1,118 @@
+================
+bpftool-link
+================
+-------------------------------------------------------------------------------
+tool for inspection and simple manipulation of eBPF links
+-------------------------------------------------------------------------------
+
+:Manual section: 8
+
+SYNOPSIS
+========
+
+       **bpftool** [*OPTIONS*] **link** *COMMAND*
+
+       *OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }
+
+       *COMMANDS* := { **show** | **list** | **pin** | **help** }
+
+LINK COMMANDS
+=============
+
+|      **bpftool** **link { show | list }** [*LINK*]
+|      **bpftool** **link pin** *LINK* *FILE*
+|      **bpftool** **link help**
+|
+|      *LINK* := { **id** *LINK_ID* | **pinned** *FILE* }
+
+
+DESCRIPTION
+===========
+       **bpftool link { show | list }** [*LINK*]
+                 Show information about active links. If *LINK* is
+                 specified show information only about given link,
+                 otherwise list all links currently active on the system.
+
+                 Output will start with link ID followed by link type and
+                 zero or more named attributes, some of which depend on type
+                 of link.
+
+       **bpftool link pin** *LINK* *FILE*
+                 Pin link *LINK* as *FILE*.
+
+                 Note: *FILE* must be located in *bpffs* mount. It must not
+                 contain a dot character ('.'), which is reserved for future
+                 extensions of *bpffs*.
+
+       **bpftool link help**
+                 Print short help message.
+
+OPTIONS
+=======
+       -h, --help
+                 Print short generic help message (similar to **bpftool help**).
+
+       -V, --version
+                 Print version number (similar to **bpftool version**).
+
+       -j, --json
+                 Generate JSON output. For commands that cannot produce JSON, this
+                 option has no effect.
+
+       -p, --pretty
+                 Generate human-readable JSON output. Implies **-j**.
+
+       -f, --bpffs
+                 When showing BPF links, show file names of pinned
+                 links.
+
+       -n, --nomount
+                 Do not automatically attempt to mount any virtual file system
+                 (such as tracefs or BPF virtual file system) when necessary.
+
+       -d, --debug
+                 Print all logs available, even debug-level information. This
+                 includes logs from libbpf.
+
+EXAMPLES
+========
+**# bpftool link show**
+
+::
+
+    10: cgroup  prog 25
+            cgroup_id 614  attach_type egress
+
+**# bpftool --json --pretty link show**
+
+::
+
+    [{
+            "type": "cgroup",
+            "prog_id": 25,
+            "cgroup_id": 614,
+            "attach_type": "egress"
+        }
+    ]
+
+|
+| **# bpftool link pin id 10 /sys/fs/bpf/link**
+| **# ls -l /sys/fs/bpf/**
+
+::
+
+    -rw------- 1 root root 0 Apr 23 21:39 link
+
+
+SEE ALSO
+========
+       **bpf**\ (2),
+       **bpf-helpers**\ (7),
+       **bpftool**\ (8),
+       **bpftool-prog\ (8),
+       **bpftool-map**\ (8),
+       **bpftool-cgroup**\ (8),
+       **bpftool-feature**\ (8),
+       **bpftool-net**\ (8),
+       **bpftool-perf**\ (8),
+       **bpftool-btf**\ (8)
index f584d1f..2759f9c 100644 (file)
@@ -55,16 +55,15 @@ ifneq ($(EXTRA_LDFLAGS),)
 LDFLAGS += $(EXTRA_LDFLAGS)
 endif
 
-LIBS = $(LIBBPF) -lelf -lz
-
 INSTALL ?= install
 RM ?= rm -f
 CLANG ?= clang
 
 FEATURE_USER = .bpftool
-FEATURE_TESTS = libbfd disassembler-four-args reallocarray zlib \
+FEATURE_TESTS = libbfd disassembler-four-args reallocarray zlib libcap \
+       clang-bpf-global-var
+FEATURE_DISPLAY = libbfd disassembler-four-args zlib libcap \
        clang-bpf-global-var
-FEATURE_DISPLAY = libbfd disassembler-four-args zlib clang-bpf-global-var
 
 check_feat := 1
 NON_CHECK_FEAT_TARGETS := clean uninstall doc doc-clean doc-install doc-uninstall
@@ -90,6 +89,12 @@ ifeq ($(feature-reallocarray), 0)
 CFLAGS += -DCOMPAT_NEED_REALLOCARRAY
 endif
 
+LIBS = $(LIBBPF) -lelf -lz
+ifeq ($(feature-libcap), 1)
+CFLAGS += -DUSE_LIBCAP
+LIBS += -lcap
+endif
+
 include $(wildcard $(OUTPUT)*.d)
 
 all: $(OUTPUT)bpftool
index 45ee99b..fc989ea 100644 (file)
@@ -98,6 +98,12 @@ _bpftool_get_btf_ids()
         command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
 }
 
+_bpftool_get_link_ids()
+{
+    COMPREPLY+=( $( compgen -W "$( bpftool -jp link 2>&1 | \
+        command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )
+}
+
 _bpftool_get_obj_map_names()
 {
     local obj
@@ -1073,7 +1079,7 @@ _bpftool()
                         COMPREPLY+=( $( compgen -W 'macros' -- "$cur" ) )
                     fi
                     _bpftool_one_of_list 'kernel dev'
-                    _bpftool_once_attr 'full'
+                    _bpftool_once_attr 'full unprivileged'
                     return 0
                     ;;
                 *)
@@ -1082,6 +1088,39 @@ _bpftool()
                     ;;
             esac
             ;;
+        link)
+            case $command in
+                show|list|pin)
+                    case $prev in
+                        id)
+                            _bpftool_get_link_ids
+                            return 0
+                            ;;
+                    esac
+                    ;;
+            esac
+
+            local LINK_TYPE='id pinned'
+            case $command in
+                show|list)
+                    [[ $prev != "$command" ]] && return 0
+                    COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )
+                    return 0
+                    ;;
+                pin)
+                    if [[ $prev == "$command" ]]; then
+                        COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )
+                    else
+                        _filedir
+                    fi
+                    return 0
+                    ;;
+                *)
+                    [[ $prev == $object ]] && \
+                        COMPREPLY=( $( compgen -W 'help pin show list' -- "$cur" ) )
+                    ;;
+            esac
+            ;;
     esac
 } &&
 complete -F _bpftool bpftool
index bcaf55b..41a1346 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/hashtable.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <unistd.h>
 
 #include "json_writer.h"
 #include "main.h"
index 62c6a1d..1693c80 100644 (file)
 
 static unsigned int query_flags;
 
-static const char * const attach_type_strings[] = {
-       [BPF_CGROUP_INET_INGRESS] = "ingress",
-       [BPF_CGROUP_INET_EGRESS] = "egress",
-       [BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
-       [BPF_CGROUP_SOCK_OPS] = "sock_ops",
-       [BPF_CGROUP_DEVICE] = "device",
-       [BPF_CGROUP_INET4_BIND] = "bind4",
-       [BPF_CGROUP_INET6_BIND] = "bind6",
-       [BPF_CGROUP_INET4_CONNECT] = "connect4",
-       [BPF_CGROUP_INET6_CONNECT] = "connect6",
-       [BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
-       [BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
-       [BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
-       [BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
-       [BPF_CGROUP_SYSCTL] = "sysctl",
-       [BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4",
-       [BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6",
-       [BPF_CGROUP_GETSOCKOPT] = "getsockopt",
-       [BPF_CGROUP_SETSOCKOPT] = "setsockopt",
-       [__MAX_BPF_ATTACH_TYPE] = NULL,
-};
-
 static enum bpf_attach_type parse_attach_type(const char *str)
 {
        enum bpf_attach_type type;
 
        for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
-               if (attach_type_strings[type] &&
-                   is_prefix(str, attach_type_strings[type]))
+               if (attach_type_name[type] &&
+                   is_prefix(str, attach_type_name[type]))
                        return type;
        }
 
        return __MAX_BPF_ATTACH_TYPE;
 }
 
-static int show_bpf_prog(int id, const char *attach_type_str,
+static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
                         const char *attach_flags_str,
                         int level)
 {
@@ -86,18 +64,22 @@ static int show_bpf_prog(int id, const char *attach_type_str,
        if (json_output) {
                jsonw_start_object(json_wtr);
                jsonw_uint_field(json_wtr, "id", info.id);
-               jsonw_string_field(json_wtr, "attach_type",
-                                  attach_type_str);
+               if (attach_type < ARRAY_SIZE(attach_type_name))
+                       jsonw_string_field(json_wtr, "attach_type",
+                                          attach_type_name[attach_type]);
+               else
+                       jsonw_uint_field(json_wtr, "attach_type", attach_type);
                jsonw_string_field(json_wtr, "attach_flags",
                                   attach_flags_str);
                jsonw_string_field(json_wtr, "name", info.name);
                jsonw_end_object(json_wtr);
        } else {
-               printf("%s%-8u %-15s %-15s %-15s\n", level ? "    " : "",
-                      info.id,
-                      attach_type_str,
-                      attach_flags_str,
-                      info.name);
+               printf("%s%-8u ", level ? "    " : "", info.id);
+               if (attach_type < ARRAY_SIZE(attach_type_name))
+                       printf("%-15s", attach_type_name[attach_type]);
+               else
+                       printf("type %-10u", attach_type);
+               printf(" %-15s %-15s\n", attach_flags_str, info.name);
        }
 
        close(prog_fd);
@@ -171,7 +153,7 @@ static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
        }
 
        for (iter = 0; iter < prog_cnt; iter++)
-               show_bpf_prog(prog_ids[iter], attach_type_strings[type],
+               show_bpf_prog(prog_ids[iter], type,
                              attach_flags_str, level);
 
        return 0;
index f2223db..c47bdc6 100644 (file)
@@ -262,6 +262,8 @@ int get_fd_type(int fd)
                return BPF_OBJ_MAP;
        else if (strstr(buf, "bpf-prog"))
                return BPF_OBJ_PROG;
+       else if (strstr(buf, "bpf-link"))
+               return BPF_OBJ_LINK;
 
        return BPF_OBJ_UNKNOWN;
 }
index 88718ee..f54347f 100644 (file)
@@ -6,6 +6,9 @@
 #include <string.h>
 #include <unistd.h>
 #include <net/if.h>
+#ifdef USE_LIBCAP
+#include <sys/capability.h>
+#endif
 #include <sys/utsname.h>
 #include <sys/vfs.h>
 
@@ -35,6 +38,11 @@ static const char * const helper_name[] = {
 
 #undef BPF_HELPER_MAKE_ENTRY
 
+static bool full_mode;
+#ifdef USE_LIBCAP
+static bool run_as_unprivileged;
+#endif
+
 /* Miscellaneous utility functions */
 
 static bool check_procfs(void)
@@ -471,6 +479,13 @@ probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
                }
 
        res = bpf_probe_prog_type(prog_type, ifindex);
+#ifdef USE_LIBCAP
+       /* Probe may succeed even if program load fails, for unprivileged users
+        * check that we did not fail because of insufficient permissions
+        */
+       if (run_as_unprivileged && errno == EPERM)
+               res = false;
+#endif
 
        supported_types[prog_type] |= res;
 
@@ -499,6 +514,10 @@ probe_map_type(enum bpf_map_type map_type, const char *define_prefix,
 
        res = bpf_probe_map_type(map_type, ifindex);
 
+       /* Probe result depends on the success of map creation, no additional
+        * check required for unprivileged users
+        */
+
        maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
        if (strlen(map_type_name[map_type]) > maxlen) {
                p_info("map type name too long");
@@ -518,12 +537,19 @@ probe_helper_for_progtype(enum bpf_prog_type prog_type, bool supported_type,
                          const char *define_prefix, unsigned int id,
                          const char *ptype_name, __u32 ifindex)
 {
-       bool res;
+       bool res = false;
 
-       if (!supported_type)
-               res = false;
-       else
+       if (supported_type) {
                res = bpf_probe_helper(id, prog_type, ifindex);
+#ifdef USE_LIBCAP
+               /* Probe may succeed even if program load fails, for
+                * unprivileged users check that we did not fail because of
+                * insufficient permissions
+                */
+               if (run_as_unprivileged && errno == EPERM)
+                       res = false;
+#endif
+       }
 
        if (json_output) {
                if (res)
@@ -540,8 +566,7 @@ probe_helper_for_progtype(enum bpf_prog_type prog_type, bool supported_type,
 
 static void
 probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type,
-                          const char *define_prefix, bool full_mode,
-                          __u32 ifindex)
+                          const char *define_prefix, __u32 ifindex)
 {
        const char *ptype_name = prog_type_name[prog_type];
        char feat_name[128];
@@ -678,8 +703,7 @@ static void section_map_types(const char *define_prefix, __u32 ifindex)
 }
 
 static void
-section_helpers(bool *supported_types, const char *define_prefix,
-               bool full_mode, __u32 ifindex)
+section_helpers(bool *supported_types, const char *define_prefix, __u32 ifindex)
 {
        unsigned int i;
 
@@ -704,8 +728,8 @@ section_helpers(bool *supported_types, const char *define_prefix,
                       define_prefix, define_prefix, define_prefix,
                       define_prefix);
        for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
-               probe_helpers_for_progtype(i, supported_types[i],
-                                          define_prefix, full_mode, ifindex);
+               probe_helpers_for_progtype(i, supported_types[i], define_prefix,
+                                          ifindex);
 
        print_end_section();
 }
@@ -720,23 +744,86 @@ static void section_misc(const char *define_prefix, __u32 ifindex)
        print_end_section();
 }
 
-static int do_probe(int argc, char **argv)
+static int handle_perms(void)
 {
-       enum probe_component target = COMPONENT_UNSPEC;
-       const char *define_prefix = NULL;
-       bool supported_types[128] = {};
-       bool full_mode = false;
-       __u32 ifindex = 0;
-       char *ifname;
+#ifdef USE_LIBCAP
+       cap_value_t cap_list[1] = { CAP_SYS_ADMIN };
+       bool has_sys_admin_cap = false;
+       cap_flag_value_t val;
+       int res = -1;
+       cap_t caps;
+
+       caps = cap_get_proc();
+       if (!caps) {
+               p_err("failed to get capabilities for process: %s",
+                     strerror(errno));
+               return -1;
+       }
+
+       if (cap_get_flag(caps, CAP_SYS_ADMIN, CAP_EFFECTIVE, &val)) {
+               p_err("bug: failed to retrieve CAP_SYS_ADMIN status");
+               goto exit_free;
+       }
+       if (val == CAP_SET)
+               has_sys_admin_cap = true;
+
+       if (!run_as_unprivileged && !has_sys_admin_cap) {
+               p_err("full feature probing requires CAP_SYS_ADMIN, run as root or use 'unprivileged'");
+               goto exit_free;
+       }
+
+       if ((run_as_unprivileged && !has_sys_admin_cap) ||
+           (!run_as_unprivileged && has_sys_admin_cap)) {
+               /* We are all good, exit now */
+               res = 0;
+               goto exit_free;
+       }
 
+       /* if (run_as_unprivileged && has_sys_admin_cap), drop CAP_SYS_ADMIN */
+
+       if (cap_set_flag(caps, CAP_EFFECTIVE, ARRAY_SIZE(cap_list), cap_list,
+                        CAP_CLEAR)) {
+               p_err("bug: failed to clear CAP_SYS_ADMIN from capabilities");
+               goto exit_free;
+       }
+
+       if (cap_set_proc(caps)) {
+               p_err("failed to drop CAP_SYS_ADMIN: %s", strerror(errno));
+               goto exit_free;
+       }
+
+       res = 0;
+
+exit_free:
+       if (cap_free(caps) && !res) {
+               p_err("failed to clear storage object for capabilities: %s",
+                     strerror(errno));
+               res = -1;
+       }
+
+       return res;
+#else
        /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
-        * Let's approximate, and restrict usage to root user only.
+        * We do not use libpcap so let's approximate, and restrict usage to
+        * root user only.
         */
        if (geteuid()) {
-               p_err("please run this command as root user");
+               p_err("full feature probing requires root privileges");
                return -1;
        }
 
+       return 0;
+#endif /* USE_LIBCAP */
+}
+
+static int do_probe(int argc, char **argv)
+{
+       enum probe_component target = COMPONENT_UNSPEC;
+       const char *define_prefix = NULL;
+       bool supported_types[128] = {};
+       __u32 ifindex = 0;
+       char *ifname;
+
        set_max_rlimit();
 
        while (argc) {
@@ -785,6 +872,14 @@ static int do_probe(int argc, char **argv)
                        if (!REQ_ARGS(1))
                                return -1;
                        define_prefix = GET_ARG();
+               } else if (is_prefix(*argv, "unprivileged")) {
+#ifdef USE_LIBCAP
+                       run_as_unprivileged = true;
+                       NEXT_ARG();
+#else
+                       p_err("unprivileged run not supported, recompile bpftool with libcap");
+                       return -1;
+#endif
                } else {
                        p_err("expected no more arguments, 'kernel', 'dev', 'macros' or 'prefix', got: '%s'?",
                              *argv);
@@ -792,6 +887,12 @@ static int do_probe(int argc, char **argv)
                }
        }
 
+       /* Full feature detection requires CAP_SYS_ADMIN privilege.
+        * Let's approximate, and warn if user is not root.
+        */
+       if (handle_perms())
+               return -1;
+
        if (json_output) {
                define_prefix = NULL;
                jsonw_start_object(json_wtr);
@@ -803,7 +904,7 @@ static int do_probe(int argc, char **argv)
                goto exit_close_json;
        section_program_types(supported_types, define_prefix, ifindex);
        section_map_types(define_prefix, ifindex);
-       section_helpers(supported_types, define_prefix, full_mode, ifindex);
+       section_helpers(supported_types, define_prefix, ifindex);
        section_misc(define_prefix, ifindex);
 
 exit_close_json:
@@ -822,7 +923,7 @@ static int do_help(int argc, char **argv)
        }
 
        fprintf(stderr,
-               "Usage: %s %s probe [COMPONENT] [full] [macros [prefix PREFIX]]\n"
+               "Usage: %s %s probe [COMPONENT] [full] [unprivileged] [macros [prefix PREFIX]]\n"
                "       %s %s help\n"
                "\n"
                "       COMPONENT := { kernel | dev NAME }\n"
index f8113b3..0e5f023 100644 (file)
@@ -17,7 +17,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
-#include <unistd.h>
 #include <bpf/btf.h>
 
 #include "bpf/libbpf_internal.h"
index f7f5885..e7e7eee 100644 (file)
@@ -15,7 +15,6 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <stdint.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
 #include <unistd.h>
diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
new file mode 100644 (file)
index 0000000..adc7dc4
--- /dev/null
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2020 Facebook */
+
+#include <errno.h>
+#include <net/if.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <bpf/bpf.h>
+
+#include "json_writer.h"
+#include "main.h"
+
+static const char * const link_type_name[] = {
+       [BPF_LINK_TYPE_UNSPEC]                  = "unspec",
+       [BPF_LINK_TYPE_RAW_TRACEPOINT]          = "raw_tracepoint",
+       [BPF_LINK_TYPE_TRACING]                 = "tracing",
+       [BPF_LINK_TYPE_CGROUP]                  = "cgroup",
+};
+
+static int link_parse_fd(int *argc, char ***argv)
+{
+       if (is_prefix(**argv, "id")) {
+               unsigned int id;
+               char *endptr;
+
+               NEXT_ARGP();
+
+               id = strtoul(**argv, &endptr, 0);
+               if (*endptr) {
+                       p_err("can't parse %s as ID", **argv);
+                       return -1;
+               }
+               NEXT_ARGP();
+
+               return bpf_link_get_fd_by_id(id);
+       } else if (is_prefix(**argv, "pinned")) {
+               char *path;
+
+               NEXT_ARGP();
+
+               path = **argv;
+               NEXT_ARGP();
+
+               return open_obj_pinned_any(path, BPF_OBJ_LINK);
+       }
+
+       p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
+       return -1;
+}
+
+static void
+show_link_header_json(struct bpf_link_info *info, json_writer_t *wtr)
+{
+       jsonw_uint_field(wtr, "id", info->id);
+       if (info->type < ARRAY_SIZE(link_type_name))
+               jsonw_string_field(wtr, "type", link_type_name[info->type]);
+       else
+               jsonw_uint_field(wtr, "type", info->type);
+
+       jsonw_uint_field(json_wtr, "prog_id", info->prog_id);
+}
+
+static int get_prog_info(int prog_id, struct bpf_prog_info *info)
+{
+       __u32 len = sizeof(*info);
+       int err, prog_fd;
+
+       prog_fd = bpf_prog_get_fd_by_id(prog_id);
+       if (prog_fd < 0)
+               return prog_fd;
+
+       memset(info, 0, sizeof(*info));
+       err = bpf_obj_get_info_by_fd(prog_fd, info, &len);
+       if (err)
+               p_err("can't get prog info: %s", strerror(errno));
+       close(prog_fd);
+       return err;
+}
+
+static int show_link_close_json(int fd, struct bpf_link_info *info)
+{
+       struct bpf_prog_info prog_info;
+       int err;
+
+       jsonw_start_object(json_wtr);
+
+       show_link_header_json(info, json_wtr);
+
+       switch (info->type) {
+       case BPF_LINK_TYPE_RAW_TRACEPOINT:
+               jsonw_string_field(json_wtr, "tp_name",
+                                  (const char *)info->raw_tracepoint.tp_name);
+               break;
+       case BPF_LINK_TYPE_TRACING:
+               err = get_prog_info(info->prog_id, &prog_info);
+               if (err)
+                       return err;
+
+               if (prog_info.type < ARRAY_SIZE(prog_type_name))
+                       jsonw_string_field(json_wtr, "prog_type",
+                                          prog_type_name[prog_info.type]);
+               else
+                       jsonw_uint_field(json_wtr, "prog_type",
+                                        prog_info.type);
+
+               if (info->tracing.attach_type < ARRAY_SIZE(attach_type_name))
+                       jsonw_string_field(json_wtr, "attach_type",
+                              attach_type_name[info->tracing.attach_type]);
+               else
+                       jsonw_uint_field(json_wtr, "attach_type",
+                                        info->tracing.attach_type);
+               break;
+       case BPF_LINK_TYPE_CGROUP:
+               jsonw_lluint_field(json_wtr, "cgroup_id",
+                                  info->cgroup.cgroup_id);
+               if (info->cgroup.attach_type < ARRAY_SIZE(attach_type_name))
+                       jsonw_string_field(json_wtr, "attach_type",
+                              attach_type_name[info->cgroup.attach_type]);
+               else
+                       jsonw_uint_field(json_wtr, "attach_type",
+                                        info->cgroup.attach_type);
+               break;
+       default:
+               break;
+       }
+
+       if (!hash_empty(link_table.table)) {
+               struct pinned_obj *obj;
+
+               jsonw_name(json_wtr, "pinned");
+               jsonw_start_array(json_wtr);
+               hash_for_each_possible(link_table.table, obj, hash, info->id) {
+                       if (obj->id == info->id)
+                               jsonw_string(json_wtr, obj->path);
+               }
+               jsonw_end_array(json_wtr);
+       }
+       jsonw_end_object(json_wtr);
+
+       return 0;
+}
+
+static void show_link_header_plain(struct bpf_link_info *info)
+{
+       printf("%u: ", info->id);
+       if (info->type < ARRAY_SIZE(link_type_name))
+               printf("%s  ", link_type_name[info->type]);
+       else
+               printf("type %u  ", info->type);
+
+       printf("prog %u  ", info->prog_id);
+}
+
+static int show_link_close_plain(int fd, struct bpf_link_info *info)
+{
+       struct bpf_prog_info prog_info;
+       int err;
+
+       show_link_header_plain(info);
+
+       switch (info->type) {
+       case BPF_LINK_TYPE_RAW_TRACEPOINT:
+               printf("\n\ttp '%s'  ",
+                      (const char *)info->raw_tracepoint.tp_name);
+               break;
+       case BPF_LINK_TYPE_TRACING:
+               err = get_prog_info(info->prog_id, &prog_info);
+               if (err)
+                       return err;
+
+               if (prog_info.type < ARRAY_SIZE(prog_type_name))
+                       printf("\n\tprog_type %s  ",
+                              prog_type_name[prog_info.type]);
+               else
+                       printf("\n\tprog_type %u  ", prog_info.type);
+
+               if (info->tracing.attach_type < ARRAY_SIZE(attach_type_name))
+                       printf("attach_type %s  ",
+                              attach_type_name[info->tracing.attach_type]);
+               else
+                       printf("attach_type %u  ", info->tracing.attach_type);
+               break;
+       case BPF_LINK_TYPE_CGROUP:
+               printf("\n\tcgroup_id %zu  ", (size_t)info->cgroup.cgroup_id);
+               if (info->cgroup.attach_type < ARRAY_SIZE(attach_type_name))
+                       printf("attach_type %s  ",
+                              attach_type_name[info->cgroup.attach_type]);
+               else
+                       printf("attach_type %u  ", info->cgroup.attach_type);
+               break;
+       default:
+               break;
+       }
+
+       if (!hash_empty(link_table.table)) {
+               struct pinned_obj *obj;
+
+               hash_for_each_possible(link_table.table, obj, hash, info->id) {
+                       if (obj->id == info->id)
+                               printf("\n\tpinned %s", obj->path);
+               }
+       }
+
+       printf("\n");
+
+       return 0;
+}
+
+static int do_show_link(int fd)
+{
+       struct bpf_link_info info;
+       __u32 len = sizeof(info);
+       char raw_tp_name[256];
+       int err;
+
+       memset(&info, 0, sizeof(info));
+again:
+       err = bpf_obj_get_info_by_fd(fd, &info, &len);
+       if (err) {
+               p_err("can't get link info: %s",
+                     strerror(errno));
+               close(fd);
+               return err;
+       }
+       if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT &&
+           !info.raw_tracepoint.tp_name) {
+               info.raw_tracepoint.tp_name = (unsigned long)&raw_tp_name;
+               info.raw_tracepoint.tp_name_len = sizeof(raw_tp_name);
+               goto again;
+       }
+
+       if (json_output)
+               show_link_close_json(fd, &info);
+       else
+               show_link_close_plain(fd, &info);
+
+       close(fd);
+       return 0;
+}
+
+static int do_show(int argc, char **argv)
+{
+       __u32 id = 0;
+       int err, fd;
+
+       if (show_pinned)
+               build_pinned_obj_table(&link_table, BPF_OBJ_LINK);
+
+       if (argc == 2) {
+               fd = link_parse_fd(&argc, &argv);
+               if (fd < 0)
+                       return fd;
+               return do_show_link(fd);
+       }
+
+       if (argc)
+               return BAD_ARG();
+
+       if (json_output)
+               jsonw_start_array(json_wtr);
+       while (true) {
+               err = bpf_link_get_next_id(id, &id);
+               if (err) {
+                       if (errno == ENOENT)
+                               break;
+                       p_err("can't get next link: %s%s", strerror(errno),
+                             errno == EINVAL ? " -- kernel too old?" : "");
+                       break;
+               }
+
+               fd = bpf_link_get_fd_by_id(id);
+               if (fd < 0) {
+                       if (errno == ENOENT)
+                               continue;
+                       p_err("can't get link by id (%u): %s",
+                             id, strerror(errno));
+                       break;
+               }
+
+               err = do_show_link(fd);
+               if (err)
+                       break;
+       }
+       if (json_output)
+               jsonw_end_array(json_wtr);
+
+       return errno == ENOENT ? 0 : -1;
+}
+
+static int do_pin(int argc, char **argv)
+{
+       int err;
+
+       err = do_pin_any(argc, argv, link_parse_fd);
+       if (!err && json_output)
+               jsonw_null(json_wtr);
+       return err;
+}
+
+static int do_help(int argc, char **argv)
+{
+       if (json_output) {
+               jsonw_null(json_wtr);
+               return 0;
+       }
+
+       fprintf(stderr,
+               "Usage: %1$s %2$s { show | list }   [LINK]\n"
+               "       %1$s %2$s pin        LINK  FILE\n"
+               "       %1$s %2$s help\n"
+               "\n"
+               "       " HELP_SPEC_LINK "\n"
+               "       " HELP_SPEC_PROGRAM "\n"
+               "       " HELP_SPEC_OPTIONS "\n"
+               "",
+               bin_name, argv[-2]);
+
+       return 0;
+}
+
+static const struct cmd cmds[] = {
+       { "show",       do_show },
+       { "list",       do_show },
+       { "help",       do_help },
+       { "pin",        do_pin },
+       { 0 }
+};
+
+int do_link(int argc, char **argv)
+{
+       return cmd_select(cmds, argc, argv, do_help);
+}
index 466c269..1413a15 100644 (file)
@@ -30,6 +30,7 @@ bool verifier_logs;
 bool relaxed_maps;
 struct pinned_obj_table prog_table;
 struct pinned_obj_table map_table;
+struct pinned_obj_table link_table;
 
 static void __noreturn clean_and_exit(int i)
 {
@@ -58,7 +59,7 @@ static int do_help(int argc, char **argv)
                "       %s batch file FILE\n"
                "       %s version\n"
                "\n"
-               "       OBJECT := { prog | map | cgroup | perf | net | feature | btf | gen | struct_ops }\n"
+               "       OBJECT := { prog | map | link | cgroup | perf | net | feature | btf | gen | struct_ops }\n"
                "       " HELP_SPEC_OPTIONS "\n"
                "",
                bin_name, bin_name, bin_name);
@@ -215,6 +216,7 @@ static const struct cmd cmds[] = {
        { "batch",      do_batch },
        { "prog",       do_prog },
        { "map",        do_map },
+       { "link",       do_link },
        { "cgroup",     do_cgroup },
        { "perf",       do_perf },
        { "net",        do_net },
@@ -364,6 +366,7 @@ int main(int argc, char **argv)
 
        hash_init(prog_table.table);
        hash_init(map_table.table);
+       hash_init(link_table.table);
 
        opterr = 0;
        while ((opt = getopt_long(argc, argv, "Vhpjfmnd",
@@ -422,6 +425,7 @@ int main(int argc, char **argv)
        if (show_pinned) {
                delete_pinned_obj_table(&prog_table);
                delete_pinned_obj_table(&map_table);
+               delete_pinned_obj_table(&link_table);
        }
 
        return ret;
index 86f14ce..9b1fb81 100644 (file)
@@ -50,6 +50,8 @@
        "\t            {-m|--mapcompat} | {-n|--nomount} }"
 #define HELP_SPEC_MAP                                                  \
        "MAP := { id MAP_ID | pinned FILE | name MAP_NAME }"
+#define HELP_SPEC_LINK                                                 \
+       "LINK := { id LINK_ID | pinned FILE }"
 
 static const char * const prog_type_name[] = {
        [BPF_PROG_TYPE_UNSPEC]                  = "unspec",
@@ -83,6 +85,38 @@ static const char * const prog_type_name[] = {
        [BPF_PROG_TYPE_EXT]                     = "ext",
 };
 
+static const char * const attach_type_name[__MAX_BPF_ATTACH_TYPE] = {
+       [BPF_CGROUP_INET_INGRESS] = "ingress",
+       [BPF_CGROUP_INET_EGRESS] = "egress",
+       [BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
+       [BPF_CGROUP_SOCK_OPS] = "sock_ops",
+       [BPF_CGROUP_DEVICE] = "device",
+       [BPF_CGROUP_INET4_BIND] = "bind4",
+       [BPF_CGROUP_INET6_BIND] = "bind6",
+       [BPF_CGROUP_INET4_CONNECT] = "connect4",
+       [BPF_CGROUP_INET6_CONNECT] = "connect6",
+       [BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
+       [BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
+       [BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
+       [BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
+       [BPF_CGROUP_SYSCTL] = "sysctl",
+       [BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4",
+       [BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6",
+       [BPF_CGROUP_GETSOCKOPT] = "getsockopt",
+       [BPF_CGROUP_SETSOCKOPT] = "setsockopt",
+
+       [BPF_SK_SKB_STREAM_PARSER] = "sk_skb_stream_parser",
+       [BPF_SK_SKB_STREAM_VERDICT] = "sk_skb_stream_verdict",
+       [BPF_SK_MSG_VERDICT] = "sk_msg_verdict",
+       [BPF_LIRC_MODE2] = "lirc_mode2",
+       [BPF_FLOW_DISSECTOR] = "flow_dissector",
+       [BPF_TRACE_RAW_TP] = "raw_tp",
+       [BPF_TRACE_FENTRY] = "fentry",
+       [BPF_TRACE_FEXIT] = "fexit",
+       [BPF_MODIFY_RETURN] = "mod_ret",
+       [BPF_LSM_MAC] = "lsm_mac",
+};
+
 extern const char * const map_type_name[];
 extern const size_t map_type_name_size;
 
@@ -90,6 +124,7 @@ enum bpf_obj_type {
        BPF_OBJ_UNKNOWN,
        BPF_OBJ_PROG,
        BPF_OBJ_MAP,
+       BPF_OBJ_LINK,
 };
 
 extern const char *bin_name;
@@ -102,6 +137,7 @@ extern bool verifier_logs;
 extern bool relaxed_maps;
 extern struct pinned_obj_table prog_table;
 extern struct pinned_obj_table map_table;
+extern struct pinned_obj_table link_table;
 
 void __printf(1, 2) p_err(const char *fmt, ...);
 void __printf(1, 2) p_info(const char *fmt, ...);
@@ -153,6 +189,7 @@ int do_pin_fd(int fd, const char *name);
 
 int do_prog(int argc, char **arg);
 int do_map(int argc, char **arg);
+int do_link(int argc, char **arg);
 int do_event_pipe(int argc, char **argv);
 int do_cgroup(int argc, char **arg);
 int do_perf(int argc, char **arg);
index 2a7befb..e177384 100644 (file)
@@ -479,6 +479,7 @@ static int do_unregister(int argc, char **argv)
 
 static int do_register(int argc, char **argv)
 {
+       struct bpf_object_load_attr load_attr = {};
        const struct bpf_map_def *def;
        struct bpf_map_info info = {};
        __u32 info_len = sizeof(info);
@@ -499,7 +500,12 @@ static int do_register(int argc, char **argv)
 
        set_max_rlimit();
 
-       if (bpf_object__load(obj)) {
+       load_attr.obj = obj;
+       if (verifier_logs)
+               /* log_level1 + log_level2 + stats, but not stable UAPI */
+               load_attr.log_level = 1 + 2 + 4;
+
+       if (bpf_object__load_xattr(&load_attr)) {
                bpf_object__close(obj);
                return -1;
        }
@@ -591,6 +597,8 @@ int do_struct_ops(int argc, char **argv)
 
        err = cmd_select(cmds, argc, argv, do_help);
 
-       btf__free(btf_vmlinux);
+       if (!IS_ERR(btf_vmlinux))
+               btf__free(btf_vmlinux);
+
        return err;
 }
index 39edd68..8a6f82e 100644 (file)
@@ -8,7 +8,7 @@ BPFTOOL ?= $(DEFAULT_BPFTOOL)
 LIBBPF_SRC := $(abspath ../../lib/bpf)
 BPFOBJ := $(OUTPUT)/libbpf.a
 BPF_INCLUDE := $(OUTPUT)
-INCLUDES := -I$(BPF_INCLUDE) -I$(OUTPUT) -I$(abspath ../../lib)
+INCLUDES := -I$(OUTPUT) -I$(BPF_INCLUDE) -I$(abspath ../../lib)
 CFLAGS := -g -Wall
 
 # Try to detect best kernel BTF source
index 1e38d19..3bc6b07 100644 (file)
@@ -7,7 +7,7 @@ int main(int argc, char *argv[])
 {
        uint64_t old, new = argc;
 
-       argv = argv;
+       (void)argv;
        do {
                old = __sync_val_compare_and_swap(&x, 0, 0);
        } while (!__sync_bool_compare_and_swap(&x, old, new));
index 669d694..4671fbf 100644 (file)
@@ -3,9 +3,9 @@
 #define __LINUX_BITS_H
 
 #include <linux/const.h>
+#include <vdso/bits.h>
 #include <asm/bitsperlong.h>
 
-#define BIT(nr)                        (UL(1) << (nr))
 #define BIT_ULL(nr)            (ULL(1) << (nr))
 #define BIT_MASK(nr)           (UL(1) << ((nr) % BITS_PER_LONG))
 #define BIT_WORD(nr)           ((nr) / BITS_PER_LONG)
  * position @h. For example
  * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000.
  */
-#define GENMASK(h, l) \
+#if !defined(__ASSEMBLY__) && \
+       (!defined(CONFIG_CC_IS_GCC) || CONFIG_GCC_VERSION >= 49000)
+#include <linux/build_bug.h>
+#define GENMASK_INPUT_CHECK(h, l) \
+       (BUILD_BUG_ON_ZERO(__builtin_choose_expr( \
+               __builtin_constant_p((l) > (h)), (l) > (h), 0)))
+#else
+/*
+ * BUILD_BUG_ON_ZERO is not available in h files included from asm files,
+ * disable the input check if that is the case.
+ */
+#define GENMASK_INPUT_CHECK(h, l) 0
+#endif
+
+#define __GENMASK(h, l) \
        (((~UL(0)) - (UL(1) << (l)) + 1) & \
         (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
+#define GENMASK(h, l) \
+       (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
 
-#define GENMASK_ULL(h, l) \
+#define __GENMASK_ULL(h, l) \
        (((~ULL(0)) - (ULL(1) << (l)) + 1) & \
         (~ULL(0) >> (BITS_PER_LONG_LONG - 1 - (h))))
+#define GENMASK_ULL(h, l) \
+       (GENMASK_INPUT_CHECK(h, l) + __GENMASK_ULL(h, l))
 
 #endif /* __LINUX_BITS_H */
diff --git a/tools/include/linux/build_bug.h b/tools/include/linux/build_bug.h
new file mode 100644 (file)
index 0000000..cc7070c
--- /dev/null
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_BUILD_BUG_H
+#define _LINUX_BUILD_BUG_H
+
+#include <linux/compiler.h>
+
+#ifdef __CHECKER__
+#define BUILD_BUG_ON_ZERO(e) (0)
+#else /* __CHECKER__ */
+/*
+ * Force a compilation error if condition is true, but also produce a
+ * result (of value 0 and type int), so the expression can be used
+ * e.g. in a structure initializer (or where-ever else comma expressions
+ * aren't permitted).
+ */
+#define BUILD_BUG_ON_ZERO(e) ((int)(sizeof(struct { int:(-!!(e)); })))
+#endif /* __CHECKER__ */
+
+/* Force a compilation error if a constant expression is not a power of 2 */
+#define __BUILD_BUG_ON_NOT_POWER_OF_2(n)       \
+       BUILD_BUG_ON(((n) & ((n) - 1)) != 0)
+#define BUILD_BUG_ON_NOT_POWER_OF_2(n)                 \
+       BUILD_BUG_ON((n) == 0 || (((n) & ((n) - 1)) != 0))
+
+/*
+ * BUILD_BUG_ON_INVALID() permits the compiler to check the validity of the
+ * expression but avoids the generation of any code, even if that expression
+ * has side-effects.
+ */
+#define BUILD_BUG_ON_INVALID(e) ((void)(sizeof((__force long)(e))))
+
+/**
+ * BUILD_BUG_ON_MSG - break compile if a condition is true & emit supplied
+ *                   error message.
+ * @condition: the condition which the compiler should know is false.
+ *
+ * See BUILD_BUG_ON for description.
+ */
+#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)
+
+/**
+ * BUILD_BUG_ON - break compile if a condition is true.
+ * @condition: the condition which the compiler should know is false.
+ *
+ * If you have some code which relies on certain constants being equal, or
+ * some other compile-time-evaluated condition, you should use BUILD_BUG_ON to
+ * detect if someone changes it.
+ */
+#define BUILD_BUG_ON(condition) \
+       BUILD_BUG_ON_MSG(condition, "BUILD_BUG_ON failed: " #condition)
+
+/**
+ * BUILD_BUG - break compile if used.
+ *
+ * If you have some code that you expect the compiler to eliminate at
+ * build time, you should use BUILD_BUG to detect if it is
+ * unexpectedly used.
+ */
+#define BUILD_BUG() BUILD_BUG_ON_MSG(1, "BUILD_BUG failed")
+
+/**
+ * static_assert - check integer constant expression at build time
+ *
+ * static_assert() is a wrapper for the C11 _Static_assert, with a
+ * little macro magic to make the message optional (defaulting to the
+ * stringification of the tested expression).
+ *
+ * Contrary to BUILD_BUG_ON(), static_assert() can be used at global
+ * scope, but requires the expression to be an integer constant
+ * expression (i.e., it is not enough that __builtin_constant_p() is
+ * true for expr).
+ *
+ * Also note that BUILD_BUG_ON() fails the build if the condition is
+ * true, while static_assert() fails the build if the expression is
+ * false.
+ */
+#ifndef static_assert
+#define static_assert(expr, ...) __static_assert(expr, ##__VA_ARGS__, #expr)
+#define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
+#endif // static_assert
+
+#endif /* _LINUX_BUILD_BUG_H */
index 1827c2f..180f771 100644 (file)
 # define __compiletime_error(message)
 #endif
 
+#ifdef __OPTIMIZE__
+# define __compiletime_assert(condition, msg, prefix, suffix)          \
+       do {                                                            \
+               extern void prefix ## suffix(void) __compiletime_error(msg); \
+               if (!(condition))                                       \
+                       prefix ## suffix();                             \
+       } while (0)
+#else
+# define __compiletime_assert(condition, msg, prefix, suffix) do { } while (0)
+#endif
+
+#define _compiletime_assert(condition, msg, prefix, suffix) \
+       __compiletime_assert(condition, msg, prefix, suffix)
+
+/**
+ * compiletime_assert - break build and emit msg if condition is false
+ * @condition: a compile-time constant condition to check
+ * @msg:       a message to emit if condition is false
+ *
+ * In tradition of POSIX assert, this macro will break the build if the
+ * supplied condition is *false*, emitting the supplied error message if the
+ * compiler has support to do so.
+ */
+#define compiletime_assert(condition, msg) \
+       _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
+
 /* Optimization barrier */
 /* The "volatile" is due to gcc bugs */
 #define barrier() __asm__ __volatile__("": : :"memory")
index 7b55a55..81b8aae 100644 (file)
@@ -1,9 +1,6 @@
 #ifndef _LINUX_CONST_H
 #define _LINUX_CONST_H
 
-#include <uapi/linux/const.h>
-
-#define UL(x)          (_UL(x))
-#define ULL(x)         (_ULL(x))
+#include <vdso/const.h>
 
 #endif /* _LINUX_CONST_H */
index cba2269..a7e54a0 100644 (file)
@@ -5,6 +5,7 @@
 #include <stdarg.h>
 #include <stddef.h>
 #include <assert.h>
+#include <linux/build_bug.h>
 #include <linux/compiler.h>
 #include <endian.h>
 #include <byteswap.h>
@@ -35,9 +36,6 @@
        (type *)((char *)__mptr - offsetof(type, member)); })
 #endif
 
-#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
-#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
-
 #ifndef max
 #define max(x, y) ({                           \
        typeof(x) _max1 = (x);                  \
index 868bf79..808b48a 100644 (file)
@@ -948,6 +948,8 @@ extern "C" {
 #define DRM_IOCTL_SYNCOBJ_TRANSFER     DRM_IOWR(0xCC, struct drm_syncobj_transfer)
 #define DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL      DRM_IOWR(0xCD, struct drm_syncobj_timeline_array)
 
+#define DRM_IOCTL_MODE_GETFB2          DRM_IOWR(0xCE, struct drm_mode_fb_cmd2)
+
 /**
  * Device specific ioctls should only be in their respective headers
  * The device specific ioctl range is from 0x40 to 0x9f.
index 829c0a4..2813e57 100644 (file)
@@ -1619,6 +1619,27 @@ struct drm_i915_gem_context_param {
  * By default, new contexts allow persistence.
  */
 #define I915_CONTEXT_PARAM_PERSISTENCE 0xb
+
+/*
+ * I915_CONTEXT_PARAM_RINGSIZE:
+ *
+ * Sets the size of the CS ringbuffer to use for logical ring contexts. This
+ * applies a limit of how many batches can be queued to HW before the caller
+ * is blocked due to lack of space for more commands.
+ *
+ * Only reliably possible to be set prior to first use, i.e. during
+ * construction. At any later point, the current execution must be flushed as
+ * the ring can only be changed while the context is idle. Note, the ringsize
+ * can be specified as a constructor property, see
+ * I915_CONTEXT_CREATE_EXT_SETPARAM, but can also be set later if required.
+ *
+ * Only applies to the current set of engine and lost when those engines
+ * are replaced by a new mapping (see I915_CONTEXT_PARAM_ENGINES).
+ *
+ * Must be between 4 - 512 KiB, in intervals of page size [4 KiB].
+ * Default is 16 KiB.
+ */
+#define I915_CONTEXT_PARAM_RINGSIZE    0xc
 /* Must be kept compact -- no holes and well documented */
 
        __u64 value;
index 2e29a67..b3643e2 100644 (file)
@@ -113,6 +113,9 @@ enum bpf_cmd {
        BPF_MAP_DELETE_BATCH,
        BPF_LINK_CREATE,
        BPF_LINK_UPDATE,
+       BPF_LINK_GET_FD_BY_ID,
+       BPF_LINK_GET_NEXT_ID,
+       BPF_ENABLE_STATS,
 };
 
 enum bpf_map_type {
@@ -220,6 +223,15 @@ enum bpf_attach_type {
 
 #define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
 
+enum bpf_link_type {
+       BPF_LINK_TYPE_UNSPEC = 0,
+       BPF_LINK_TYPE_RAW_TRACEPOINT = 1,
+       BPF_LINK_TYPE_TRACING = 2,
+       BPF_LINK_TYPE_CGROUP = 3,
+
+       MAX_BPF_LINK_TYPE,
+};
+
 /* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
  *
  * NONE(default): No further bpf programs allowed in the subtree.
@@ -379,6 +391,12 @@ enum {
  */
 #define BPF_F_QUERY_EFFECTIVE  (1U << 0)
 
+/* type for BPF_ENABLE_STATS */
+enum bpf_stats_type {
+       /* enabled run_time_ns and run_cnt */
+       BPF_STATS_RUN_TIME = 0,
+};
+
 enum bpf_stack_build_id_status {
        /* user space need an empty entry to identify end of a trace */
        BPF_STACK_BUILD_ID_EMPTY = 0,
@@ -523,6 +541,7 @@ union bpf_attr {
                        __u32           prog_id;
                        __u32           map_id;
                        __u32           btf_id;
+                       __u32           link_id;
                };
                __u32           next_id;
                __u32           open_flags;
@@ -589,6 +608,10 @@ union bpf_attr {
                __u32           old_prog_fd;
        } link_update;
 
+       struct { /* struct used by BPF_ENABLE_STATS command */
+               __u32           type;
+       } enable_stats;
+
 } __attribute__((aligned(8)));
 
 /* The description below is an attempt at providing documentation to eBPF
@@ -652,6 +675,8 @@ union bpf_attr {
  * u64 bpf_ktime_get_ns(void)
  *     Description
  *             Return the time elapsed since system boot, in nanoseconds.
+ *             Does not include time the system was suspended.
+ *             See: clock_gettime(CLOCK_MONOTONIC)
  *     Return
  *             Current *ktime*.
  *
@@ -1562,7 +1587,7 @@ union bpf_attr {
  *     Return
  *             0
  *
- * int bpf_setsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen)
+ * int bpf_setsockopt(void *bpf_socket, int level, int optname, void *optval, int optlen)
  *     Description
  *             Emulate a call to **setsockopt()** on the socket associated to
  *             *bpf_socket*, which must be a full socket. The *level* at
@@ -1570,6 +1595,11 @@ union bpf_attr {
  *             must be specified, see **setsockopt(2)** for more information.
  *             The option value of length *optlen* is pointed by *optval*.
  *
+ *             *bpf_socket* should be one of the following:
+ *             * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.
+ *             * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**
+ *               and **BPF_CGROUP_INET6_CONNECT**.
+ *
  *             This helper actually implements a subset of **setsockopt()**.
  *             It supports the following *level*\ s:
  *
@@ -1642,7 +1672,7 @@ union bpf_attr {
  *             ifindex, but doesn't require a map to do so.
  *     Return
  *             **XDP_REDIRECT** on success, or the value of the two lower bits
- *             of the **flags* argument on error.
+ *             of the *flags* argument on error.
  *
  * int bpf_sk_redirect_map(struct sk_buff *skb, struct bpf_map *map, u32 key, u64 flags)
  *     Description
@@ -1764,7 +1794,7 @@ union bpf_attr {
  *     Return
  *             0 on success, or a negative error in case of failure.
  *
- * int bpf_getsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen)
+ * int bpf_getsockopt(void *bpf_socket, int level, int optname, void *optval, int optlen)
  *     Description
  *             Emulate a call to **getsockopt()** on the socket associated to
  *             *bpf_socket*, which must be a full socket. The *level* at
@@ -1773,6 +1803,11 @@ union bpf_attr {
  *             The retrieved value is stored in the structure pointed by
  *             *opval* and of length *optlen*.
  *
+ *             *bpf_socket* should be one of the following:
+ *             * **struct bpf_sock_ops** for **BPF_PROG_TYPE_SOCK_OPS**.
+ *             * **struct bpf_sock_addr** for **BPF_CGROUP_INET4_CONNECT**
+ *               and **BPF_CGROUP_INET6_CONNECT**.
+ *
  *             This helper actually implements a subset of **getsockopt()**.
  *             It supports the following *level*\ s:
  *
@@ -3025,6 +3060,14 @@ union bpf_attr {
  *             * **-EOPNOTSUPP**       Unsupported operation, for example a
  *                                     call from outside of TC ingress.
  *             * **-ESOCKTNOSUPPORT**  Socket type not supported (reuseport).
+ *
+ * u64 bpf_ktime_get_boot_ns(void)
+ *     Description
+ *             Return the time elapsed since system boot, in nanoseconds.
+ *             Does include the time the system was suspended.
+ *             See: clock_gettime(CLOCK_BOOTTIME)
+ *     Return
+ *             Current *ktime*.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -3151,7 +3194,8 @@ union bpf_attr {
        FN(xdp_output),                 \
        FN(get_netns_cookie),           \
        FN(get_current_ancestor_cgroup_id),     \
-       FN(sk_assign),
+       FN(sk_assign),                  \
+       FN(ktime_get_boot_ns),
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
  * function eBPF program intends to call
@@ -3598,6 +3642,25 @@ struct bpf_btf_info {
        __u32 id;
 } __attribute__((aligned(8)));
 
+struct bpf_link_info {
+       __u32 type;
+       __u32 id;
+       __u32 prog_id;
+       union {
+               struct {
+                       __aligned_u64 tp_name; /* in/out: tp_name buffer ptr */
+                       __u32 tp_name_len;     /* in/out: tp_name buffer len */
+               } raw_tracepoint;
+               struct {
+                       __u32 attach_type;
+               } tracing;
+               struct {
+                       __u64 cgroup_id;
+                       __u32 attach_type;
+               } cgroup;
+       };
+} __attribute__((aligned(8)));
+
 /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
  * by user and intended to be used by socket (e.g. to bind to, depends on
  * attach attach type).
index 0d8a6f4..a10e3cd 100644 (file)
@@ -163,6 +163,7 @@ struct fscrypt_get_key_status_arg {
 #define FS_IOC_REMOVE_ENCRYPTION_KEY           _IOWR('f', 24, struct fscrypt_remove_key_arg)
 #define FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS _IOWR('f', 25, struct fscrypt_remove_key_arg)
 #define FS_IOC_GET_ENCRYPTION_KEY_STATUS       _IOWR('f', 26, struct fscrypt_get_key_status_arg)
+#define FS_IOC_GET_ENCRYPTION_NONCE            _IOR('f', 27, __u8[16])
 
 /**********************************************************************/
 
index ca6665e..cafedbb 100644 (file)
@@ -343,6 +343,7 @@ enum {
        IFLA_BRPORT_NEIGH_SUPPRESS,
        IFLA_BRPORT_ISOLATED,
        IFLA_BRPORT_BACKUP_PORT,
+       IFLA_BRPORT_MRP_RING_OPEN,
        __IFLA_BRPORT_MAX
 };
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
index 4b95f9a..428c7dd 100644 (file)
@@ -474,12 +474,17 @@ struct kvm_s390_mem_op {
        __u32 size;             /* amount of bytes */
        __u32 op;               /* type of operation */
        __u64 buf;              /* buffer in userspace */
-       __u8 ar;                /* the access register number */
-       __u8 reserved[31];      /* should be set to 0 */
+       union {
+               __u8 ar;        /* the access register number */
+               __u32 sida_offset; /* offset into the sida */
+               __u8 reserved[32]; /* should be set to 0 */
+       };
 };
 /* types for kvm_s390_mem_op->op */
 #define KVM_S390_MEMOP_LOGICAL_READ    0
 #define KVM_S390_MEMOP_LOGICAL_WRITE   1
+#define KVM_S390_MEMOP_SIDA_READ       2
+#define KVM_S390_MEMOP_SIDA_WRITE      3
 /* flags for kvm_s390_mem_op->flags */
 #define KVM_S390_MEMOP_F_CHECK_ONLY            (1ULL << 0)
 #define KVM_S390_MEMOP_F_INJECT_EXCEPTION      (1ULL << 1)
@@ -1010,6 +1015,8 @@ struct kvm_ppc_resize_hpt {
 #define KVM_CAP_ARM_NISV_TO_USER 177
 #define KVM_CAP_ARM_INJECT_EXT_DABT 178
 #define KVM_CAP_S390_VCPU_RESETS 179
+#define KVM_CAP_S390_PROTECTED 180
+#define KVM_CAP_PPC_SECURE_GUEST 181
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -1478,6 +1485,39 @@ struct kvm_enc_region {
 #define KVM_S390_NORMAL_RESET  _IO(KVMIO,   0xc3)
 #define KVM_S390_CLEAR_RESET   _IO(KVMIO,   0xc4)
 
+struct kvm_s390_pv_sec_parm {
+       __u64 origin;
+       __u64 length;
+};
+
+struct kvm_s390_pv_unp {
+       __u64 addr;
+       __u64 size;
+       __u64 tweak;
+};
+
+enum pv_cmd_id {
+       KVM_PV_ENABLE,
+       KVM_PV_DISABLE,
+       KVM_PV_SET_SEC_PARMS,
+       KVM_PV_UNPACK,
+       KVM_PV_VERIFY,
+       KVM_PV_PREP_RESET,
+       KVM_PV_UNSHARE_ALL,
+};
+
+struct kvm_pv_cmd {
+       __u32 cmd;      /* Command to be executed */
+       __u16 rc;       /* Ultravisor return code */
+       __u16 rrc;      /* Ultravisor return reason code */
+       __u64 data;     /* Data or address */
+       __u32 flags;    /* flags for future extensions. Must be 0 for now */
+       __u32 reserved[3];
+};
+
+/* Available with KVM_CAP_S390_PROTECTED */
+#define KVM_S390_PV_COMMAND            _IOWR(KVMIO, 0xc5, struct kvm_pv_cmd)
+
 /* Secure Encrypted Virtualization command */
 enum sev_cmd_id {
        /* Guest initialization commands */
@@ -1628,4 +1668,7 @@ struct kvm_hyperv_eventfd {
 #define KVM_HYPERV_CONN_ID_MASK                0x00ffffff
 #define KVM_HYPERV_EVENTFD_DEASSIGN    (1 << 0)
 
+#define KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE    (1 << 0)
+#define KVM_DIRTY_LOG_INITIALLY_SET            (1 << 1)
+
 #endif /* __LINUX_KVM_H */
index fc1a64c..923cc16 100644 (file)
@@ -5,8 +5,9 @@
 #include <asm/mman.h>
 #include <asm-generic/hugetlb_encode.h>
 
-#define MREMAP_MAYMOVE 1
-#define MREMAP_FIXED   2
+#define MREMAP_MAYMOVE         1
+#define MREMAP_FIXED           2
+#define MREMAP_DONTUNMAP       4
 
 #define OVERCOMMIT_GUESS               0
 #define OVERCOMMIT_ALWAYS              1
index 2e3bc22..3bac0a8 100644 (file)
@@ -35,6 +35,7 @@
 
 /* Flags for the clone3() syscall. */
 #define CLONE_CLEAR_SIGHAND 0x100000000ULL /* Clear any signal handler and reset to SIG_DFL. */
+#define CLONE_INTO_CGROUP 0x200000000ULL /* Clone into a specific cgroup given the right permissions. */
 
 /*
  * cloning flags intersect with CSIGNAL so can be used with unshare and clone3
@@ -81,6 +82,8 @@
  * @set_tid_size: This defines the size of the array referenced
  *                in @set_tid. This cannot be larger than the
  *                kernel's limit of nested PID namespaces.
+ * @cgroup:       If CLONE_INTO_CGROUP is specified set this to
+ *                a file descriptor for the cgroup.
  *
  * The structure is versioned by size and thus extensible.
  * New struct members must go at the end of the struct and
@@ -97,11 +100,13 @@ struct clone_args {
        __aligned_u64 tls;
        __aligned_u64 set_tid;
        __aligned_u64 set_tid_size;
+       __aligned_u64 cgroup;
 };
 #endif
 
 #define CLONE_ARGS_SIZE_VER0 64 /* sizeof first published struct */
 #define CLONE_ARGS_SIZE_VER1 80 /* sizeof second published struct */
+#define CLONE_ARGS_SIZE_VER2 88 /* sizeof third published struct */
 
 /*
  * Scheduling policies
index 40d028e..9fe72e4 100644 (file)
 #define VHOST_VSOCK_SET_GUEST_CID      _IOW(VHOST_VIRTIO, 0x60, __u64)
 #define VHOST_VSOCK_SET_RUNNING                _IOW(VHOST_VIRTIO, 0x61, int)
 
+/* VHOST_VDPA specific defines */
+
+/* Get the device id. The device ids follow the same definition of
+ * the device id defined in virtio-spec.
+ */
+#define VHOST_VDPA_GET_DEVICE_ID       _IOR(VHOST_VIRTIO, 0x70, __u32)
+/* Get and set the status. The status bits follow the same definition
+ * of the device status defined in virtio-spec.
+ */
+#define VHOST_VDPA_GET_STATUS          _IOR(VHOST_VIRTIO, 0x71, __u8)
+#define VHOST_VDPA_SET_STATUS          _IOW(VHOST_VIRTIO, 0x72, __u8)
+/* Get and set the device config. The device config follows the same
+ * definition of the device config defined in virtio-spec.
+ */
+#define VHOST_VDPA_GET_CONFIG          _IOR(VHOST_VIRTIO, 0x73, \
+                                            struct vhost_vdpa_config)
+#define VHOST_VDPA_SET_CONFIG          _IOW(VHOST_VIRTIO, 0x74, \
+                                            struct vhost_vdpa_config)
+/* Enable/disable the ring. */
+#define VHOST_VDPA_SET_VRING_ENABLE    _IOW(VHOST_VIRTIO, 0x75, \
+                                            struct vhost_vring_state)
+/* Get the max ring size. */
+#define VHOST_VDPA_GET_VRING_NUM       _IOR(VHOST_VIRTIO, 0x76, __u16)
+
 #endif
diff --git a/tools/include/vdso/bits.h b/tools/include/vdso/bits.h
new file mode 100644 (file)
index 0000000..6d005a1
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __VDSO_BITS_H
+#define __VDSO_BITS_H
+
+#include <vdso/const.h>
+
+#define BIT(nr)                        (UL(1) << (nr))
+
+#endif /* __VDSO_BITS_H */
diff --git a/tools/include/vdso/const.h b/tools/include/vdso/const.h
new file mode 100644 (file)
index 0000000..94b385a
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __VDSO_CONST_H
+#define __VDSO_CONST_H
+
+#include <uapi/linux/const.h>
+
+#define UL(x)          (_UL(x))
+#define ULL(x)         (_ULL(x))
+
+#endif /* __VDSO_CONST_H */
index 5cc1b07..43322f0 100644 (file)
@@ -721,6 +721,11 @@ int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id)
        return bpf_obj_get_next_id(start_id, next_id, BPF_BTF_GET_NEXT_ID);
 }
 
+int bpf_link_get_next_id(__u32 start_id, __u32 *next_id)
+{
+       return bpf_obj_get_next_id(start_id, next_id, BPF_LINK_GET_NEXT_ID);
+}
+
 int bpf_prog_get_fd_by_id(__u32 id)
 {
        union bpf_attr attr;
@@ -751,13 +756,23 @@ int bpf_btf_get_fd_by_id(__u32 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)
+int bpf_link_get_fd_by_id(__u32 id)
+{
+       union bpf_attr attr;
+
+       memset(&attr, 0, sizeof(attr));
+       attr.link_id = id;
+
+       return sys_bpf(BPF_LINK_GET_FD_BY_ID, &attr, sizeof(attr));
+}
+
+int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len)
 {
        union bpf_attr attr;
        int err;
 
        memset(&attr, 0, sizeof(attr));
-       attr.info.bpf_fd = prog_fd;
+       attr.info.bpf_fd = bpf_fd;
        attr.info.info_len = *info_len;
        attr.info.info = ptr_to_u64(info);
 
@@ -826,3 +841,13 @@ int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf, __u32 *buf_len,
 
        return err;
 }
+
+int bpf_enable_stats(enum bpf_stats_type type)
+{
+       union bpf_attr attr;
+
+       memset(&attr, 0, sizeof(attr));
+       attr.enable_stats.type = type;
+
+       return sys_bpf(BPF_ENABLE_STATS, &attr, sizeof(attr));
+}
index 46d47af..1901b27 100644 (file)
@@ -216,10 +216,12 @@ LIBBPF_API int bpf_prog_test_run(int prog_fd, int repeat, void *data,
 LIBBPF_API int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id);
 LIBBPF_API int bpf_map_get_next_id(__u32 start_id, __u32 *next_id);
 LIBBPF_API int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id);
+LIBBPF_API int bpf_link_get_next_id(__u32 start_id, __u32 *next_id);
 LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id);
 LIBBPF_API int bpf_map_get_fd_by_id(__u32 id);
 LIBBPF_API int bpf_btf_get_fd_by_id(__u32 id);
-LIBBPF_API int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len);
+LIBBPF_API int bpf_link_get_fd_by_id(__u32 id);
+LIBBPF_API int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len);
 LIBBPF_API int bpf_prog_query(int target_fd, enum bpf_attach_type type,
                              __u32 query_flags, __u32 *attach_flags,
                              __u32 *prog_ids, __u32 *prog_cnt);
@@ -229,6 +231,7 @@ LIBBPF_API int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf,
 LIBBPF_API int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf,
                                 __u32 *buf_len, __u32 *prog_id, __u32 *fd_type,
                                 __u64 *probe_offset, __u64 *probe_addr);
+LIBBPF_API int bpf_enable_stats(enum bpf_stats_type type);
 
 #ifdef __cplusplus
 } /* extern "C" */
index f69cc20..da00b87 100644 (file)
@@ -2,10 +2,17 @@
 #ifndef __BPF_HELPERS__
 #define __BPF_HELPERS__
 
+/*
+ * Note that bpf programs need to include either
+ * vmlinux.h (auto-generated from BTF) or linux/types.h
+ * in advance since bpf_helper_defs.h uses such types
+ * as __u64.
+ */
 #include "bpf_helper_defs.h"
 
 #define __uint(name, val) int (*name)[val]
 #define __type(name, val) typeof(val) *name
+#define __array(name, val) typeof(val) *name[]
 
 /* Helper macro to print out debug messages */
 #define bpf_printk(fmt, ...)                           \
index 0c28ee8..de07e55 100644 (file)
@@ -658,7 +658,7 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
                        if (!btf_dump_is_blacklisted(d, id)) {
                                btf_dump_emit_typedef_def(d, id, t, 0);
                                btf_dump_printf(d, ";\n\n");
-                       };
+                       }
                        tstate->fwd_emitted = 1;
                        break;
                default:
index 54c30c8..cffb962 100644 (file)
@@ -59,7 +59,14 @@ struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
 
 void hashmap__clear(struct hashmap *map)
 {
+       struct hashmap_entry *cur, *tmp;
+       int bkt;
+
+       hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
+               free(cur);
+       }
        free(map->buckets);
+       map->buckets = NULL;
        map->cap = map->cap_bits = map->sz = 0;
 }
 
index ff91742..977add1 100644 (file)
@@ -178,6 +178,8 @@ struct bpf_capabilities {
        __u32 array_mmap:1;
        /* BTF_FUNC_GLOBAL is supported */
        __u32 btf_func_global:1;
+       /* kernel support for expected_attach_type in BPF_PROG_LOAD */
+       __u32 exp_attach_type:1;
 };
 
 enum reloc_type {
@@ -194,6 +196,22 @@ struct reloc_desc {
        int sym_off;
 };
 
+struct bpf_sec_def;
+
+typedef struct bpf_link *(*attach_fn_t)(const struct bpf_sec_def *sec,
+                                       struct bpf_program *prog);
+
+struct bpf_sec_def {
+       const char *sec;
+       size_t len;
+       enum bpf_prog_type prog_type;
+       enum bpf_attach_type expected_attach_type;
+       bool is_exp_attach_type_optional;
+       bool is_attachable;
+       bool is_attach_btf;
+       attach_fn_t attach_fn;
+};
+
 /*
  * bpf_prog should be a better name but it has been used in
  * linux/filter.h.
@@ -204,6 +222,7 @@ struct bpf_program {
        char *name;
        int prog_ifindex;
        char *section_name;
+       const struct bpf_sec_def *sec_def;
        /* section_name with / replaced by _; makes recursive pinning
         * in bpf_object__pin_programs easier
         */
@@ -291,6 +310,7 @@ struct bpf_map {
        int map_ifindex;
        int inner_map_fd;
        struct bpf_map_def def;
+       __u32 btf_var_idx;
        __u32 btf_key_type_id;
        __u32 btf_value_type_id;
        __u32 btf_vmlinux_value_type_id;
@@ -299,6 +319,9 @@ struct bpf_map {
        enum libbpf_map_type libbpf_type;
        void *mmaped;
        struct bpf_struct_ops *st_ops;
+       struct bpf_map *inner_map;
+       void **init_slots;
+       int init_slots_sz;
        char *pin_path;
        bool pinned;
        bool reused;
@@ -370,6 +393,7 @@ struct bpf_object {
                int nr_reloc_sects;
                int maps_shndx;
                int btf_maps_shndx;
+               __u32 btf_maps_sec_btf_id;
                int text_shndx;
                int symbols_shndx;
                int data_shndx;
@@ -1895,109 +1919,54 @@ static int build_map_pin_path(struct bpf_map *map, const char *path)
        return 0;
 }
 
-static int bpf_object__init_user_btf_map(struct bpf_object *obj,
-                                        const struct btf_type *sec,
-                                        int var_idx, int sec_idx,
-                                        const Elf_Data *data, bool strict,
-                                        const char *pin_root_path)
+
+static int parse_btf_map_def(struct bpf_object *obj,
+                            struct bpf_map *map,
+                            const struct btf_type *def,
+                            bool strict, bool is_inner,
+                            const char *pin_root_path)
 {
-       const struct btf_type *var, *def, *t;
-       const struct btf_var_secinfo *vi;
-       const struct btf_var *var_extra;
+       const struct btf_type *t;
        const struct btf_member *m;
-       const char *map_name;
-       struct bpf_map *map;
        int vlen, i;
 
-       vi = btf_var_secinfos(sec) + var_idx;
-       var = btf__type_by_id(obj->btf, vi->type);
-       var_extra = btf_var(var);
-       map_name = btf__name_by_offset(obj->btf, var->name_off);
-       vlen = btf_vlen(var);
-
-       if (map_name == NULL || map_name[0] == '\0') {
-               pr_warn("map #%d: empty name.\n", var_idx);
-               return -EINVAL;
-       }
-       if ((__u64)vi->offset + vi->size > data->d_size) {
-               pr_warn("map '%s' BTF data is corrupted.\n", map_name);
-               return -EINVAL;
-       }
-       if (!btf_is_var(var)) {
-               pr_warn("map '%s': unexpected var kind %u.\n",
-                       map_name, btf_kind(var));
-               return -EINVAL;
-       }
-       if (var_extra->linkage != BTF_VAR_GLOBAL_ALLOCATED &&
-           var_extra->linkage != BTF_VAR_STATIC) {
-               pr_warn("map '%s': unsupported var linkage %u.\n",
-                       map_name, var_extra->linkage);
-               return -EOPNOTSUPP;
-       }
-
-       def = skip_mods_and_typedefs(obj->btf, var->type, NULL);
-       if (!btf_is_struct(def)) {
-               pr_warn("map '%s': unexpected def kind %u.\n",
-                       map_name, btf_kind(var));
-               return -EINVAL;
-       }
-       if (def->size > vi->size) {
-               pr_warn("map '%s': invalid def size.\n", map_name);
-               return -EINVAL;
-       }
-
-       map = bpf_object__add_map(obj);
-       if (IS_ERR(map))
-               return PTR_ERR(map);
-       map->name = strdup(map_name);
-       if (!map->name) {
-               pr_warn("map '%s': failed to alloc map name.\n", map_name);
-               return -ENOMEM;
-       }
-       map->libbpf_type = LIBBPF_MAP_UNSPEC;
-       map->def.type = BPF_MAP_TYPE_UNSPEC;
-       map->sec_idx = sec_idx;
-       map->sec_offset = vi->offset;
-       pr_debug("map '%s': at sec_idx %d, offset %zu.\n",
-                map_name, map->sec_idx, map->sec_offset);
-
        vlen = btf_vlen(def);
        m = btf_members(def);
        for (i = 0; i < vlen; i++, m++) {
                const char *name = btf__name_by_offset(obj->btf, m->name_off);
 
                if (!name) {
-                       pr_warn("map '%s': invalid field #%d.\n", map_name, i);
+                       pr_warn("map '%s': invalid field #%d.\n", map->name, i);
                        return -EINVAL;
                }
                if (strcmp(name, "type") == 0) {
-                       if (!get_map_field_int(map_name, obj->btf, m,
+                       if (!get_map_field_int(map->name, obj->btf, m,
                                               &map->def.type))
                                return -EINVAL;
                        pr_debug("map '%s': found type = %u.\n",
-                                map_name, map->def.type);
+                                map->name, map->def.type);
                } else if (strcmp(name, "max_entries") == 0) {
-                       if (!get_map_field_int(map_name, obj->btf, m,
+                       if (!get_map_field_int(map->name, obj->btf, m,
                                               &map->def.max_entries))
                                return -EINVAL;
                        pr_debug("map '%s': found max_entries = %u.\n",
-                                map_name, map->def.max_entries);
+                                map->name, map->def.max_entries);
                } else if (strcmp(name, "map_flags") == 0) {
-                       if (!get_map_field_int(map_name, obj->btf, m,
+                       if (!get_map_field_int(map->name, obj->btf, m,
                                               &map->def.map_flags))
                                return -EINVAL;
                        pr_debug("map '%s': found map_flags = %u.\n",
-                                map_name, map->def.map_flags);
+                                map->name, map->def.map_flags);
                } else if (strcmp(name, "key_size") == 0) {
                        __u32 sz;
 
-                       if (!get_map_field_int(map_name, obj->btf, m, &sz))
+                       if (!get_map_field_int(map->name, obj->btf, m, &sz))
                                return -EINVAL;
                        pr_debug("map '%s': found key_size = %u.\n",
-                                map_name, sz);
+                                map->name, sz);
                        if (map->def.key_size && map->def.key_size != sz) {
                                pr_warn("map '%s': conflicting key size %u != %u.\n",
-                                       map_name, map->def.key_size, sz);
+                                       map->name, map->def.key_size, sz);
                                return -EINVAL;
                        }
                        map->def.key_size = sz;
@@ -2007,25 +1976,25 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
                        t = btf__type_by_id(obj->btf, m->type);
                        if (!t) {
                                pr_warn("map '%s': key type [%d] not found.\n",
-                                       map_name, m->type);
+                                       map->name, m->type);
                                return -EINVAL;
                        }
                        if (!btf_is_ptr(t)) {
                                pr_warn("map '%s': key spec is not PTR: %u.\n",
-                                       map_name, btf_kind(t));
+                                       map->name, btf_kind(t));
                                return -EINVAL;
                        }
                        sz = btf__resolve_size(obj->btf, t->type);
                        if (sz < 0) {
                                pr_warn("map '%s': can't determine key size for type [%u]: %zd.\n",
-                                       map_name, t->type, (ssize_t)sz);
+                                       map->name, t->type, (ssize_t)sz);
                                return sz;
                        }
                        pr_debug("map '%s': found key [%u], sz = %zd.\n",
-                                map_name, t->type, (ssize_t)sz);
+                                map->name, t->type, (ssize_t)sz);
                        if (map->def.key_size && map->def.key_size != sz) {
                                pr_warn("map '%s': conflicting key size %u != %zd.\n",
-                                       map_name, map->def.key_size, (ssize_t)sz);
+                                       map->name, map->def.key_size, (ssize_t)sz);
                                return -EINVAL;
                        }
                        map->def.key_size = sz;
@@ -2033,13 +2002,13 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
                } else if (strcmp(name, "value_size") == 0) {
                        __u32 sz;
 
-                       if (!get_map_field_int(map_name, obj->btf, m, &sz))
+                       if (!get_map_field_int(map->name, obj->btf, m, &sz))
                                return -EINVAL;
                        pr_debug("map '%s': found value_size = %u.\n",
-                                map_name, sz);
+                                map->name, sz);
                        if (map->def.value_size && map->def.value_size != sz) {
                                pr_warn("map '%s': conflicting value size %u != %u.\n",
-                                       map_name, map->def.value_size, sz);
+                                       map->name, map->def.value_size, sz);
                                return -EINVAL;
                        }
                        map->def.value_size = sz;
@@ -2049,71 +2018,207 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
                        t = btf__type_by_id(obj->btf, m->type);
                        if (!t) {
                                pr_warn("map '%s': value type [%d] not found.\n",
-                                       map_name, m->type);
+                                       map->name, m->type);
                                return -EINVAL;
                        }
                        if (!btf_is_ptr(t)) {
                                pr_warn("map '%s': value spec is not PTR: %u.\n",
-                                       map_name, btf_kind(t));
+                                       map->name, btf_kind(t));
                                return -EINVAL;
                        }
                        sz = btf__resolve_size(obj->btf, t->type);
                        if (sz < 0) {
                                pr_warn("map '%s': can't determine value size for type [%u]: %zd.\n",
-                                       map_name, t->type, (ssize_t)sz);
+                                       map->name, t->type, (ssize_t)sz);
                                return sz;
                        }
                        pr_debug("map '%s': found value [%u], sz = %zd.\n",
-                                map_name, t->type, (ssize_t)sz);
+                                map->name, t->type, (ssize_t)sz);
                        if (map->def.value_size && map->def.value_size != sz) {
                                pr_warn("map '%s': conflicting value size %u != %zd.\n",
-                                       map_name, map->def.value_size, (ssize_t)sz);
+                                       map->name, map->def.value_size, (ssize_t)sz);
                                return -EINVAL;
                        }
                        map->def.value_size = sz;
                        map->btf_value_type_id = t->type;
+               }
+               else if (strcmp(name, "values") == 0) {
+                       int err;
+
+                       if (is_inner) {
+                               pr_warn("map '%s': multi-level inner maps not supported.\n",
+                                       map->name);
+                               return -ENOTSUP;
+                       }
+                       if (i != vlen - 1) {
+                               pr_warn("map '%s': '%s' member should be last.\n",
+                                       map->name, name);
+                               return -EINVAL;
+                       }
+                       if (!bpf_map_type__is_map_in_map(map->def.type)) {
+                               pr_warn("map '%s': should be map-in-map.\n",
+                                       map->name);
+                               return -ENOTSUP;
+                       }
+                       if (map->def.value_size && map->def.value_size != 4) {
+                               pr_warn("map '%s': conflicting value size %u != 4.\n",
+                                       map->name, map->def.value_size);
+                               return -EINVAL;
+                       }
+                       map->def.value_size = 4;
+                       t = btf__type_by_id(obj->btf, m->type);
+                       if (!t) {
+                               pr_warn("map '%s': map-in-map inner type [%d] not found.\n",
+                                       map->name, m->type);
+                               return -EINVAL;
+                       }
+                       if (!btf_is_array(t) || btf_array(t)->nelems) {
+                               pr_warn("map '%s': map-in-map inner spec is not a zero-sized array.\n",
+                                       map->name);
+                               return -EINVAL;
+                       }
+                       t = skip_mods_and_typedefs(obj->btf, btf_array(t)->type,
+                                                  NULL);
+                       if (!btf_is_ptr(t)) {
+                               pr_warn("map '%s': map-in-map inner def is of unexpected kind %u.\n",
+                                       map->name, btf_kind(t));
+                               return -EINVAL;
+                       }
+                       t = skip_mods_and_typedefs(obj->btf, t->type, NULL);
+                       if (!btf_is_struct(t)) {
+                               pr_warn("map '%s': map-in-map inner def is of unexpected kind %u.\n",
+                                       map->name, btf_kind(t));
+                               return -EINVAL;
+                       }
+
+                       map->inner_map = calloc(1, sizeof(*map->inner_map));
+                       if (!map->inner_map)
+                               return -ENOMEM;
+                       map->inner_map->sec_idx = obj->efile.btf_maps_shndx;
+                       map->inner_map->name = malloc(strlen(map->name) +
+                                                     sizeof(".inner") + 1);
+                       if (!map->inner_map->name)
+                               return -ENOMEM;
+                       sprintf(map->inner_map->name, "%s.inner", map->name);
+
+                       err = parse_btf_map_def(obj, map->inner_map, t, strict,
+                                               true /* is_inner */, NULL);
+                       if (err)
+                               return err;
                } else if (strcmp(name, "pinning") == 0) {
                        __u32 val;
                        int err;
 
-                       if (!get_map_field_int(map_name, obj->btf, m, &val))
+                       if (is_inner) {
+                               pr_debug("map '%s': inner def can't be pinned.\n",
+                                        map->name);
+                               return -EINVAL;
+                       }
+                       if (!get_map_field_int(map->name, obj->btf, m, &val))
                                return -EINVAL;
                        pr_debug("map '%s': found pinning = %u.\n",
-                                map_name, val);
+                                map->name, val);
 
                        if (val != LIBBPF_PIN_NONE &&
                            val != LIBBPF_PIN_BY_NAME) {
                                pr_warn("map '%s': invalid pinning value %u.\n",
-                                       map_name, val);
+                                       map->name, val);
                                return -EINVAL;
                        }
                        if (val == LIBBPF_PIN_BY_NAME) {
                                err = build_map_pin_path(map, pin_root_path);
                                if (err) {
                                        pr_warn("map '%s': couldn't build pin path.\n",
-                                               map_name);
+                                               map->name);
                                        return err;
                                }
                        }
                } else {
                        if (strict) {
                                pr_warn("map '%s': unknown field '%s'.\n",
-                                       map_name, name);
+                                       map->name, name);
                                return -ENOTSUP;
                        }
                        pr_debug("map '%s': ignoring unknown field '%s'.\n",
-                                map_name, name);
+                                map->name, name);
                }
        }
 
        if (map->def.type == BPF_MAP_TYPE_UNSPEC) {
-               pr_warn("map '%s': map type isn't specified.\n", map_name);
+               pr_warn("map '%s': map type isn't specified.\n", map->name);
                return -EINVAL;
        }
 
        return 0;
 }
 
+static int bpf_object__init_user_btf_map(struct bpf_object *obj,
+                                        const struct btf_type *sec,
+                                        int var_idx, int sec_idx,
+                                        const Elf_Data *data, bool strict,
+                                        const char *pin_root_path)
+{
+       const struct btf_type *var, *def;
+       const struct btf_var_secinfo *vi;
+       const struct btf_var *var_extra;
+       const char *map_name;
+       struct bpf_map *map;
+
+       vi = btf_var_secinfos(sec) + var_idx;
+       var = btf__type_by_id(obj->btf, vi->type);
+       var_extra = btf_var(var);
+       map_name = btf__name_by_offset(obj->btf, var->name_off);
+
+       if (map_name == NULL || map_name[0] == '\0') {
+               pr_warn("map #%d: empty name.\n", var_idx);
+               return -EINVAL;
+       }
+       if ((__u64)vi->offset + vi->size > data->d_size) {
+               pr_warn("map '%s' BTF data is corrupted.\n", map_name);
+               return -EINVAL;
+       }
+       if (!btf_is_var(var)) {
+               pr_warn("map '%s': unexpected var kind %u.\n",
+                       map_name, btf_kind(var));
+               return -EINVAL;
+       }
+       if (var_extra->linkage != BTF_VAR_GLOBAL_ALLOCATED &&
+           var_extra->linkage != BTF_VAR_STATIC) {
+               pr_warn("map '%s': unsupported var linkage %u.\n",
+                       map_name, var_extra->linkage);
+               return -EOPNOTSUPP;
+       }
+
+       def = skip_mods_and_typedefs(obj->btf, var->type, NULL);
+       if (!btf_is_struct(def)) {
+               pr_warn("map '%s': unexpected def kind %u.\n",
+                       map_name, btf_kind(var));
+               return -EINVAL;
+       }
+       if (def->size > vi->size) {
+               pr_warn("map '%s': invalid def size.\n", map_name);
+               return -EINVAL;
+       }
+
+       map = bpf_object__add_map(obj);
+       if (IS_ERR(map))
+               return PTR_ERR(map);
+       map->name = strdup(map_name);
+       if (!map->name) {
+               pr_warn("map '%s': failed to alloc map name.\n", map_name);
+               return -ENOMEM;
+       }
+       map->libbpf_type = LIBBPF_MAP_UNSPEC;
+       map->def.type = BPF_MAP_TYPE_UNSPEC;
+       map->sec_idx = sec_idx;
+       map->sec_offset = vi->offset;
+       map->btf_var_idx = var_idx;
+       pr_debug("map '%s': at sec_idx %d, offset %zu.\n",
+                map_name, map->sec_idx, map->sec_offset);
+
+       return parse_btf_map_def(obj, map, def, strict, false, pin_root_path);
+}
+
 static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
                                          const char *pin_root_path)
 {
@@ -2144,6 +2249,7 @@ static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
                name = btf__name_by_offset(obj->btf, t->name_off);
                if (strcmp(name, MAPS_ELF_SEC) == 0) {
                        sec = t;
+                       obj->efile.btf_maps_sec_btf_id = i;
                        break;
                }
        }
@@ -2530,7 +2636,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 
                        /* Only do relo for section with exec instructions */
                        if (!section_have_execinstr(obj, sec) &&
-                           strcmp(name, ".rel" STRUCT_OPS_SEC)) {
+                           strcmp(name, ".rel" STRUCT_OPS_SEC) &&
+                           strcmp(name, ".rel" MAPS_ELF_SEC)) {
                                pr_debug("skip relo %s(%d) for section(%d)\n",
                                         name, idx, sec);
                                continue;
@@ -3315,6 +3422,37 @@ static int bpf_object__probe_array_mmap(struct bpf_object *obj)
        return 0;
 }
 
+static int
+bpf_object__probe_exp_attach_type(struct bpf_object *obj)
+{
+       struct bpf_load_program_attr attr;
+       struct bpf_insn insns[] = {
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_EXIT_INSN(),
+       };
+       int fd;
+
+       memset(&attr, 0, sizeof(attr));
+       /* use any valid combination of program type and (optional)
+        * non-zero expected attach type (i.e., not a BPF_CGROUP_INET_INGRESS)
+        * to see if kernel supports expected_attach_type field for
+        * BPF_PROG_LOAD command
+        */
+       attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK;
+       attr.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE;
+       attr.insns = insns;
+       attr.insns_cnt = ARRAY_SIZE(insns);
+       attr.license = "GPL";
+
+       fd = bpf_load_program_xattr(&attr, NULL, 0);
+       if (fd >= 0) {
+               obj->caps.exp_attach_type = 1;
+               close(fd);
+               return 1;
+       }
+       return 0;
+}
+
 static int
 bpf_object__probe_caps(struct bpf_object *obj)
 {
@@ -3325,6 +3463,7 @@ bpf_object__probe_caps(struct bpf_object *obj)
                bpf_object__probe_btf_func_global,
                bpf_object__probe_btf_datasec,
                bpf_object__probe_array_mmap,
+               bpf_object__probe_exp_attach_type,
        };
        int i, ret;
 
@@ -3431,124 +3570,181 @@ bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map)
        return 0;
 }
 
+static void bpf_map__destroy(struct bpf_map *map);
+
+static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map)
+{
+       struct bpf_create_map_attr create_attr;
+       struct bpf_map_def *def = &map->def;
+
+       memset(&create_attr, 0, sizeof(create_attr));
+
+       if (obj->caps.name)
+               create_attr.name = map->name;
+       create_attr.map_ifindex = map->map_ifindex;
+       create_attr.map_type = def->type;
+       create_attr.map_flags = def->map_flags;
+       create_attr.key_size = def->key_size;
+       create_attr.value_size = def->value_size;
+
+       if (def->type == BPF_MAP_TYPE_PERF_EVENT_ARRAY && !def->max_entries) {
+               int nr_cpus;
+
+               nr_cpus = libbpf_num_possible_cpus();
+               if (nr_cpus < 0) {
+                       pr_warn("map '%s': failed to determine number of system CPUs: %d\n",
+                               map->name, nr_cpus);
+                       return nr_cpus;
+               }
+               pr_debug("map '%s': setting size to %d\n", map->name, nr_cpus);
+               create_attr.max_entries = nr_cpus;
+       } else {
+               create_attr.max_entries = def->max_entries;
+       }
+
+       if (bpf_map__is_struct_ops(map))
+               create_attr.btf_vmlinux_value_type_id =
+                       map->btf_vmlinux_value_type_id;
+
+       create_attr.btf_fd = 0;
+       create_attr.btf_key_type_id = 0;
+       create_attr.btf_value_type_id = 0;
+       if (obj->btf && !bpf_map_find_btf_info(obj, map)) {
+               create_attr.btf_fd = btf__fd(obj->btf);
+               create_attr.btf_key_type_id = map->btf_key_type_id;
+               create_attr.btf_value_type_id = map->btf_value_type_id;
+       }
+
+       if (bpf_map_type__is_map_in_map(def->type)) {
+               if (map->inner_map) {
+                       int err;
+
+                       err = bpf_object__create_map(obj, map->inner_map);
+                       if (err) {
+                               pr_warn("map '%s': failed to create inner map: %d\n",
+                                       map->name, err);
+                               return err;
+                       }
+                       map->inner_map_fd = bpf_map__fd(map->inner_map);
+               }
+               if (map->inner_map_fd >= 0)
+                       create_attr.inner_map_fd = map->inner_map_fd;
+       }
+
+       map->fd = bpf_create_map_xattr(&create_attr);
+       if (map->fd < 0 && (create_attr.btf_key_type_id ||
+                           create_attr.btf_value_type_id)) {
+               char *cp, errmsg[STRERR_BUFSIZE];
+               int err = -errno;
+
+               cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
+               pr_warn("Error in bpf_create_map_xattr(%s):%s(%d). Retrying without BTF.\n",
+                       map->name, cp, err);
+               create_attr.btf_fd = 0;
+               create_attr.btf_key_type_id = 0;
+               create_attr.btf_value_type_id = 0;
+               map->btf_key_type_id = 0;
+               map->btf_value_type_id = 0;
+               map->fd = bpf_create_map_xattr(&create_attr);
+       }
+
+       if (map->fd < 0)
+               return -errno;
+
+       if (bpf_map_type__is_map_in_map(def->type) && map->inner_map) {
+               bpf_map__destroy(map->inner_map);
+               zfree(&map->inner_map);
+       }
+
+       return 0;
+}
+
 static int
 bpf_object__create_maps(struct bpf_object *obj)
 {
-       struct bpf_create_map_attr create_attr = {};
-       int nr_cpus = 0;
-       unsigned int i;
+       struct bpf_map *map;
+       char *cp, errmsg[STRERR_BUFSIZE];
+       unsigned int i, j;
        int err;
 
        for (i = 0; i < obj->nr_maps; i++) {
-               struct bpf_map *map = &obj->maps[i];
-               struct bpf_map_def *def = &map->def;
-               char *cp, errmsg[STRERR_BUFSIZE];
-               int *pfd = &map->fd;
+               map = &obj->maps[i];
 
                if (map->pin_path) {
                        err = bpf_object__reuse_map(map);
                        if (err) {
-                               pr_warn("error reusing pinned map %s\n",
+                               pr_warn("map '%s': error reusing pinned map\n",
                                        map->name);
-                               return err;
+                               goto err_out;
                        }
                }
 
                if (map->fd >= 0) {
-                       pr_debug("skip map create (preset) %s: fd=%d\n",
+                       pr_debug("map '%s': skipping creation (preset fd=%d)\n",
                                 map->name, map->fd);
                        continue;
                }
 
-               if (obj->caps.name)
-                       create_attr.name = map->name;
-               create_attr.map_ifindex = map->map_ifindex;
-               create_attr.map_type = def->type;
-               create_attr.map_flags = def->map_flags;
-               create_attr.key_size = def->key_size;
-               create_attr.value_size = def->value_size;
-               if (def->type == BPF_MAP_TYPE_PERF_EVENT_ARRAY &&
-                   !def->max_entries) {
-                       if (!nr_cpus)
-                               nr_cpus = libbpf_num_possible_cpus();
-                       if (nr_cpus < 0) {
-                               pr_warn("failed to determine number of system CPUs: %d\n",
-                                       nr_cpus);
-                               err = nr_cpus;
-                               goto err_out;
-                       }
-                       pr_debug("map '%s': setting size to %d\n",
-                                map->name, nr_cpus);
-                       create_attr.max_entries = nr_cpus;
-               } else {
-                       create_attr.max_entries = def->max_entries;
-               }
-               create_attr.btf_fd = 0;
-               create_attr.btf_key_type_id = 0;
-               create_attr.btf_value_type_id = 0;
-               if (bpf_map_type__is_map_in_map(def->type) &&
-                   map->inner_map_fd >= 0)
-                       create_attr.inner_map_fd = map->inner_map_fd;
-               if (bpf_map__is_struct_ops(map))
-                       create_attr.btf_vmlinux_value_type_id =
-                               map->btf_vmlinux_value_type_id;
-
-               if (obj->btf && !bpf_map_find_btf_info(obj, map)) {
-                       create_attr.btf_fd = btf__fd(obj->btf);
-                       create_attr.btf_key_type_id = map->btf_key_type_id;
-                       create_attr.btf_value_type_id = map->btf_value_type_id;
-               }
-
-               *pfd = bpf_create_map_xattr(&create_attr);
-               if (*pfd < 0 && (create_attr.btf_key_type_id ||
-                                create_attr.btf_value_type_id)) {
-                       err = -errno;
-                       cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
-                       pr_warn("Error in bpf_create_map_xattr(%s):%s(%d). Retrying without BTF.\n",
-                               map->name, cp, err);
-                       create_attr.btf_fd = 0;
-                       create_attr.btf_key_type_id = 0;
-                       create_attr.btf_value_type_id = 0;
-                       map->btf_key_type_id = 0;
-                       map->btf_value_type_id = 0;
-                       *pfd = bpf_create_map_xattr(&create_attr);
-               }
-
-               if (*pfd < 0) {
-                       size_t j;
+               err = bpf_object__create_map(obj, map);
+               if (err)
+                       goto err_out;
 
-                       err = -errno;
-err_out:
-                       cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
-                       pr_warn("failed to create map (name: '%s'): %s(%d)\n",
-                               map->name, cp, err);
-                       pr_perm_msg(err);
-                       for (j = 0; j < i; j++)
-                               zclose(obj->maps[j].fd);
-                       return err;
-               }
+               pr_debug("map '%s': created successfully, fd=%d\n", map->name,
+                        map->fd);
 
                if (bpf_map__is_internal(map)) {
                        err = bpf_object__populate_internal_map(obj, map);
                        if (err < 0) {
-                               zclose(*pfd);
+                               zclose(map->fd);
                                goto err_out;
                        }
                }
 
+               if (map->init_slots_sz) {
+                       for (j = 0; j < map->init_slots_sz; j++) {
+                               const struct bpf_map *targ_map;
+                               int fd;
+
+                               if (!map->init_slots[j])
+                                       continue;
+
+                               targ_map = map->init_slots[j];
+                               fd = bpf_map__fd(targ_map);
+                               err = bpf_map_update_elem(map->fd, &j, &fd, 0);
+                               if (err) {
+                                       err = -errno;
+                                       pr_warn("map '%s': failed to initialize slot [%d] to map '%s' fd=%d: %d\n",
+                                               map->name, j, targ_map->name,
+                                               fd, err);
+                                       goto err_out;
+                               }
+                               pr_debug("map '%s': slot [%d] set to map '%s' fd=%d\n",
+                                        map->name, j, targ_map->name, fd);
+                       }
+                       zfree(&map->init_slots);
+                       map->init_slots_sz = 0;
+               }
+
                if (map->pin_path && !map->pinned) {
                        err = bpf_map__pin(map, NULL);
                        if (err) {
-                               pr_warn("failed to auto-pin map name '%s' at '%s'\n",
-                                       map->name, map->pin_path);
-                               return err;
+                               pr_warn("map '%s': failed to auto-pin at '%s': %d\n",
+                                       map->name, map->pin_path, err);
+                               zclose(map->fd);
+                               goto err_out;
                        }
                }
-
-               pr_debug("created map %s: fd=%d\n", map->name, *pfd);
        }
 
        return 0;
+
+err_out:
+       cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
+       pr_warn("map '%s': failed to create: %s(%d)\n", map->name, cp, err);
+       pr_perm_msg(err);
+       for (j = 0; j < i; j++)
+               zclose(obj->maps[j].fd);
+       return err;
 }
 
 static int
@@ -4800,9 +4996,118 @@ bpf_object__relocate(struct bpf_object *obj, const char *targ_btf_path)
        return 0;
 }
 
-static int bpf_object__collect_struct_ops_map_reloc(struct bpf_object *obj,
-                                                   GElf_Shdr *shdr,
-                                                   Elf_Data *data);
+static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
+                                           GElf_Shdr *shdr, Elf_Data *data);
+
+static int bpf_object__collect_map_relos(struct bpf_object *obj,
+                                        GElf_Shdr *shdr, Elf_Data *data)
+{
+       int i, j, nrels, new_sz, ptr_sz = sizeof(void *);
+       const struct btf_var_secinfo *vi = NULL;
+       const struct btf_type *sec, *var, *def;
+       const struct btf_member *member;
+       struct bpf_map *map, *targ_map;
+       const char *name, *mname;
+       Elf_Data *symbols;
+       unsigned int moff;
+       GElf_Sym sym;
+       GElf_Rel rel;
+       void *tmp;
+
+       if (!obj->efile.btf_maps_sec_btf_id || !obj->btf)
+               return -EINVAL;
+       sec = btf__type_by_id(obj->btf, obj->efile.btf_maps_sec_btf_id);
+       if (!sec)
+               return -EINVAL;
+
+       symbols = obj->efile.symbols;
+       nrels = shdr->sh_size / shdr->sh_entsize;
+       for (i = 0; i < nrels; i++) {
+               if (!gelf_getrel(data, i, &rel)) {
+                       pr_warn(".maps relo #%d: failed to get ELF relo\n", i);
+                       return -LIBBPF_ERRNO__FORMAT;
+               }
+               if (!gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym)) {
+                       pr_warn(".maps relo #%d: symbol %zx not found\n",
+                               i, (size_t)GELF_R_SYM(rel.r_info));
+                       return -LIBBPF_ERRNO__FORMAT;
+               }
+               name = elf_strptr(obj->efile.elf, obj->efile.strtabidx,
+                                 sym.st_name) ? : "<?>";
+               if (sym.st_shndx != obj->efile.btf_maps_shndx) {
+                       pr_warn(".maps relo #%d: '%s' isn't a BTF-defined map\n",
+                               i, name);
+                       return -LIBBPF_ERRNO__RELOC;
+               }
+
+               pr_debug(".maps relo #%d: for %zd value %zd rel.r_offset %zu name %d ('%s')\n",
+                        i, (ssize_t)(rel.r_info >> 32), (size_t)sym.st_value,
+                        (size_t)rel.r_offset, sym.st_name, name);
+
+               for (j = 0; j < obj->nr_maps; j++) {
+                       map = &obj->maps[j];
+                       if (map->sec_idx != obj->efile.btf_maps_shndx)
+                               continue;
+
+                       vi = btf_var_secinfos(sec) + map->btf_var_idx;
+                       if (vi->offset <= rel.r_offset &&
+                           rel.r_offset + sizeof(void *) <= vi->offset + vi->size)
+                               break;
+               }
+               if (j == obj->nr_maps) {
+                       pr_warn(".maps relo #%d: cannot find map '%s' at rel.r_offset %zu\n",
+                               i, name, (size_t)rel.r_offset);
+                       return -EINVAL;
+               }
+
+               if (!bpf_map_type__is_map_in_map(map->def.type))
+                       return -EINVAL;
+               if (map->def.type == BPF_MAP_TYPE_HASH_OF_MAPS &&
+                   map->def.key_size != sizeof(int)) {
+                       pr_warn(".maps relo #%d: hash-of-maps '%s' should have key size %zu.\n",
+                               i, map->name, sizeof(int));
+                       return -EINVAL;
+               }
+
+               targ_map = bpf_object__find_map_by_name(obj, name);
+               if (!targ_map)
+                       return -ESRCH;
+
+               var = btf__type_by_id(obj->btf, vi->type);
+               def = skip_mods_and_typedefs(obj->btf, var->type, NULL);
+               if (btf_vlen(def) == 0)
+                       return -EINVAL;
+               member = btf_members(def) + btf_vlen(def) - 1;
+               mname = btf__name_by_offset(obj->btf, member->name_off);
+               if (strcmp(mname, "values"))
+                       return -EINVAL;
+
+               moff = btf_member_bit_offset(def, btf_vlen(def) - 1) / 8;
+               if (rel.r_offset - vi->offset < moff)
+                       return -EINVAL;
+
+               moff = rel.r_offset - vi->offset - moff;
+               if (moff % ptr_sz)
+                       return -EINVAL;
+               moff /= ptr_sz;
+               if (moff >= map->init_slots_sz) {
+                       new_sz = moff + 1;
+                       tmp = realloc(map->init_slots, new_sz * ptr_sz);
+                       if (!tmp)
+                               return -ENOMEM;
+                       map->init_slots = tmp;
+                       memset(map->init_slots + map->init_slots_sz, 0,
+                              (new_sz - map->init_slots_sz) * ptr_sz);
+                       map->init_slots_sz = new_sz;
+               }
+               map->init_slots[moff] = targ_map;
+
+               pr_debug(".maps relo #%d: map '%s' slot [%d] points to map '%s'\n",
+                        i, map->name, moff, name);
+       }
+
+       return 0;
+}
 
 static int bpf_object__collect_reloc(struct bpf_object *obj)
 {
@@ -4825,21 +5130,17 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
                }
 
                if (idx == obj->efile.st_ops_shndx) {
-                       err = bpf_object__collect_struct_ops_map_reloc(obj,
-                                                                      shdr,
-                                                                      data);
-                       if (err)
-                               return err;
-                       continue;
-               }
-
-               prog = bpf_object__find_prog_by_idx(obj, idx);
-               if (!prog) {
-                       pr_warn("relocation failed: no section(%d)\n", idx);
-                       return -LIBBPF_ERRNO__RELOC;
+                       err = bpf_object__collect_st_ops_relos(obj, shdr, data);
+               } else if (idx == obj->efile.btf_maps_shndx) {
+                       err = bpf_object__collect_map_relos(obj, shdr, data);
+               } else {
+                       prog = bpf_object__find_prog_by_idx(obj, idx);
+                       if (!prog) {
+                               pr_warn("relocation failed: no prog in section(%d)\n", idx);
+                               return -LIBBPF_ERRNO__RELOC;
+                       }
+                       err = bpf_program__collect_reloc(prog, shdr, data, obj);
                }
-
-               err = bpf_program__collect_reloc(prog, shdr, data, obj);
                if (err)
                        return err;
        }
@@ -4861,7 +5162,12 @@ load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
 
        memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
        load_attr.prog_type = prog->type;
-       load_attr.expected_attach_type = prog->expected_attach_type;
+       /* old kernels might not support specifying expected_attach_type */
+       if (!prog->caps->exp_attach_type && prog->sec_def &&
+           prog->sec_def->is_exp_attach_type_optional)
+               load_attr.expected_attach_type = 0;
+       else
+               load_attr.expected_attach_type = prog->expected_attach_type;
        if (prog->caps->name)
                load_attr.name = prog->name;
        load_attr.insns = insns;
@@ -5062,6 +5368,8 @@ bpf_object__load_progs(struct bpf_object *obj, int log_level)
        return 0;
 }
 
+static const struct bpf_sec_def *find_sec_def(const char *sec_name);
+
 static struct bpf_object *
 __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
                   const struct bpf_object_open_opts *opts)
@@ -5117,24 +5425,17 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
        bpf_object__elf_finish(obj);
 
        bpf_object__for_each_program(prog, obj) {
-               enum bpf_prog_type prog_type;
-               enum bpf_attach_type attach_type;
-
-               if (prog->type != BPF_PROG_TYPE_UNSPEC)
-                       continue;
-
-               err = libbpf_prog_type_by_name(prog->section_name, &prog_type,
-                                              &attach_type);
-               if (err == -ESRCH)
+               prog->sec_def = find_sec_def(prog->section_name);
+               if (!prog->sec_def)
                        /* couldn't guess, but user might manually specify */
                        continue;
-               if (err)
-                       goto out;
 
-               bpf_program__set_type(prog, prog_type);
-               bpf_program__set_expected_attach_type(prog, attach_type);
-               if (prog_type == BPF_PROG_TYPE_TRACING ||
-                   prog_type == BPF_PROG_TYPE_EXT)
+               bpf_program__set_type(prog, prog->sec_def->prog_type);
+               bpf_program__set_expected_attach_type(prog,
+                               prog->sec_def->expected_attach_type);
+
+               if (prog->sec_def->prog_type == BPF_PROG_TYPE_TRACING ||
+                   prog->sec_def->prog_type == BPF_PROG_TYPE_EXT)
                        prog->attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0);
        }
 
@@ -5904,6 +6205,40 @@ int bpf_object__pin(struct bpf_object *obj, const char *path)
        return 0;
 }
 
+static void bpf_map__destroy(struct bpf_map *map)
+{
+       if (map->clear_priv)
+               map->clear_priv(map, map->priv);
+       map->priv = NULL;
+       map->clear_priv = NULL;
+
+       if (map->inner_map) {
+               bpf_map__destroy(map->inner_map);
+               zfree(&map->inner_map);
+       }
+
+       zfree(&map->init_slots);
+       map->init_slots_sz = 0;
+
+       if (map->mmaped) {
+               munmap(map->mmaped, bpf_map_mmap_sz(map));
+               map->mmaped = NULL;
+       }
+
+       if (map->st_ops) {
+               zfree(&map->st_ops->data);
+               zfree(&map->st_ops->progs);
+               zfree(&map->st_ops->kern_func_off);
+               zfree(&map->st_ops);
+       }
+
+       zfree(&map->name);
+       zfree(&map->pin_path);
+
+       if (map->fd >= 0)
+               zclose(map->fd);
+}
+
 void bpf_object__close(struct bpf_object *obj)
 {
        size_t i;
@@ -5919,29 +6254,8 @@ void bpf_object__close(struct bpf_object *obj)
        btf__free(obj->btf);
        btf_ext__free(obj->btf_ext);
 
-       for (i = 0; i < obj->nr_maps; i++) {
-               struct bpf_map *map = &obj->maps[i];
-
-               if (map->clear_priv)
-                       map->clear_priv(map, map->priv);
-               map->priv = NULL;
-               map->clear_priv = NULL;
-
-               if (map->mmaped) {
-                       munmap(map->mmaped, bpf_map_mmap_sz(map));
-                       map->mmaped = NULL;
-               }
-
-               if (map->st_ops) {
-                       zfree(&map->st_ops->data);
-                       zfree(&map->st_ops->progs);
-                       zfree(&map->st_ops->kern_func_off);
-                       zfree(&map->st_ops);
-               }
-
-               zfree(&map->name);
-               zfree(&map->pin_path);
-       }
+       for (i = 0; i < obj->nr_maps; i++)
+               bpf_map__destroy(&obj->maps[i]);
 
        zfree(&obj->kconfig);
        zfree(&obj->externs);
@@ -6223,23 +6537,32 @@ void bpf_program__set_expected_attach_type(struct bpf_program *prog,
        prog->expected_attach_type = type;
 }
 
-#define BPF_PROG_SEC_IMPL(string, ptype, eatype, is_attachable, btf, atype) \
-       { string, sizeof(string) - 1, ptype, eatype, is_attachable, btf, atype }
+#define BPF_PROG_SEC_IMPL(string, ptype, eatype, eatype_optional,          \
+                         attachable, attach_btf)                           \
+       {                                                                   \
+               .sec = string,                                              \
+               .len = sizeof(string) - 1,                                  \
+               .prog_type = ptype,                                         \
+               .expected_attach_type = eatype,                             \
+               .is_exp_attach_type_optional = eatype_optional,             \
+               .is_attachable = attachable,                                \
+               .is_attach_btf = attach_btf,                                \
+       }
 
 /* Programs that can NOT be attached. */
 #define BPF_PROG_SEC(string, ptype) BPF_PROG_SEC_IMPL(string, ptype, 0, 0, 0, 0)
 
 /* Programs that can be attached. */
 #define BPF_APROG_SEC(string, ptype, atype) \
-       BPF_PROG_SEC_IMPL(string, ptype, 0, 1, 0, atype)
+       BPF_PROG_SEC_IMPL(string, ptype, atype, true, 1, 0)
 
 /* Programs that must specify expected attach type at load time. */
 #define BPF_EAPROG_SEC(string, ptype, eatype) \
-       BPF_PROG_SEC_IMPL(string, ptype, eatype, 1, 0, eatype)
+       BPF_PROG_SEC_IMPL(string, ptype, eatype, false, 1, 0)
 
 /* Programs that use BTF to identify attach point */
 #define BPF_PROG_BTF(string, ptype, eatype) \
-       BPF_PROG_SEC_IMPL(string, ptype, eatype, 0, 1, 0)
+       BPF_PROG_SEC_IMPL(string, ptype, eatype, false, 0, 1)
 
 /* Programs that can be attached but attach type can't be identified by section
  * name. Kept for backward compatibility.
@@ -6253,11 +6576,6 @@ void bpf_program__set_expected_attach_type(struct bpf_program *prog,
        __VA_ARGS__                                                         \
 }
 
-struct bpf_sec_def;
-
-typedef struct bpf_link *(*attach_fn_t)(const struct bpf_sec_def *sec,
-                                       struct bpf_program *prog);
-
 static struct bpf_link *attach_kprobe(const struct bpf_sec_def *sec,
                                      struct bpf_program *prog);
 static struct bpf_link *attach_tp(const struct bpf_sec_def *sec,
@@ -6269,17 +6587,6 @@ static struct bpf_link *attach_trace(const struct bpf_sec_def *sec,
 static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
                                   struct bpf_program *prog);
 
-struct bpf_sec_def {
-       const char *sec;
-       size_t len;
-       enum bpf_prog_type prog_type;
-       enum bpf_attach_type expected_attach_type;
-       bool is_attachable;
-       bool is_attach_btf;
-       enum bpf_attach_type attach_type;
-       attach_fn_t attach_fn;
-};
-
 static const struct bpf_sec_def section_defs[] = {
        BPF_PROG_SEC("socket",                  BPF_PROG_TYPE_SOCKET_FILTER),
        BPF_PROG_SEC("sk_reuseport",            BPF_PROG_TYPE_SK_REUSEPORT),
@@ -6472,9 +6779,8 @@ static struct bpf_map *find_struct_ops_map_by_offset(struct bpf_object *obj,
 }
 
 /* Collect the reloc from ELF and populate the st_ops->progs[] */
-static int bpf_object__collect_struct_ops_map_reloc(struct bpf_object *obj,
-                                                   GElf_Shdr *shdr,
-                                                   Elf_Data *data)
+static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
+                                           GElf_Shdr *shdr, Elf_Data *data)
 {
        const struct btf_member *member;
        struct bpf_struct_ops *st_ops;
@@ -6628,6 +6934,7 @@ int libbpf_find_vmlinux_btf_id(const char *name,
                               enum bpf_attach_type attach_type)
 {
        struct btf *btf;
+       int err;
 
        btf = libbpf_find_kernel_btf();
        if (IS_ERR(btf)) {
@@ -6635,7 +6942,9 @@ int libbpf_find_vmlinux_btf_id(const char *name,
                return -EINVAL;
        }
 
-       return __find_vmlinux_btf_id(btf, name, attach_type);
+       err = __find_vmlinux_btf_id(btf, name, attach_type);
+       btf__free(btf);
+       return err;
 }
 
 static int libbpf_find_prog_btf_id(const char *name, __u32 attach_prog_fd)
@@ -6713,7 +7022,7 @@ int libbpf_attach_type_by_name(const char *name,
                        continue;
                if (!section_defs[i].is_attachable)
                        return -EINVAL;
-               *attach_type = section_defs[i].attach_type;
+               *attach_type = section_defs[i].expected_attach_type;
                return 0;
        }
        pr_debug("failed to guess attach type based on ELF section name '%s'\n", name);
@@ -6962,7 +7271,7 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
        err = bpf_object__load(obj);
        if (err) {
                bpf_object__close(obj);
-               return -EINVAL;
+               return err;
        }
 
        *pobj = obj;
@@ -7542,7 +7851,6 @@ static struct bpf_link *attach_lsm(const struct bpf_sec_def *sec,
 struct bpf_link *
 bpf_program__attach_cgroup(struct bpf_program *prog, int cgroup_fd)
 {
-       const struct bpf_sec_def *sec_def;
        enum bpf_attach_type attach_type;
        char errmsg[STRERR_BUFSIZE];
        struct bpf_link *link;
@@ -7561,11 +7869,6 @@ bpf_program__attach_cgroup(struct bpf_program *prog, int cgroup_fd)
        link->detach = &bpf_link__detach_fd;
 
        attach_type = bpf_program__get_expected_attach_type(prog);
-       if (!attach_type) {
-               sec_def = find_sec_def(bpf_program__title(prog, false));
-               if (sec_def)
-                       attach_type = sec_def->attach_type;
-       }
        link_fd = bpf_link_create(prog_fd, cgroup_fd, attach_type, NULL);
        if (link_fd < 0) {
                link_fd = -errno;
index 44df1d3..f1dacec 100644 (file)
@@ -458,7 +458,7 @@ struct xdp_link_info {
 
 struct bpf_xdp_set_link_opts {
        size_t sz;
-       __u32 old_fd;
+       int old_fd;
 };
 #define bpf_xdp_set_link_opts__last_field old_fd
 
index bb88316..e03bd4d 100644 (file)
@@ -254,3 +254,10 @@ LIBBPF_0.0.8 {
                bpf_program__set_lsm;
                bpf_set_link_xdp_fd_opts;
 } LIBBPF_0.0.7;
+
+LIBBPF_0.0.9 {
+       global:
+               bpf_enable_stats;
+               bpf_link_get_fd_by_id;
+               bpf_link_get_next_id;
+} LIBBPF_0.0.8;
index 18b5319..312f887 100644 (file)
@@ -142,7 +142,7 @@ static int __bpf_set_link_xdp_fd_replace(int ifindex, int fd, int old_fd,
                struct ifinfomsg ifinfo;
                char             attrbuf[64];
        } req;
-       __u32 nl_pid;
+       __u32 nl_pid = 0;
 
        sock = libbpf_netlink_open(&nl_pid);
        if (sock < 0)
@@ -288,7 +288,7 @@ int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
 {
        struct xdp_id_md xdp_id = {};
        int sock, ret;
-       __u32 nl_pid;
+       __u32 nl_pid = 0;
        __u32 mask;
 
        if (flags & ~XDP_FLAGS_MASK || !info_size)
@@ -321,7 +321,9 @@ int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
 
 static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags)
 {
-       if (info->attach_mode != XDP_ATTACHED_MULTI)
+       flags &= XDP_FLAGS_MODES;
+
+       if (info->attach_mode != XDP_ATTACHED_MULTI && !flags)
                return info->prog_id;
        if (flags & XDP_FLAGS_DRV_MODE)
                return info->drv_prog_id;
index 8dd01f9..4b170fd 100644 (file)
@@ -1050,10 +1050,7 @@ static struct rela *find_jump_table(struct objtool_file *file,
         * it.
         */
        for (;
-            &insn->list != &file->insn_list &&
-            insn->sec == func->sec &&
-            insn->offset >= func->offset;
-
+            &insn->list != &file->insn_list && insn->func && insn->func->pfunc == func;
             insn = insn->first_jump_src ?: list_prev_entry(insn, list)) {
 
                if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
@@ -2008,8 +2005,8 @@ static int validate_return(struct symbol *func, struct instruction *insn, struct
        }
 
        if (state->bp_scratch) {
-               WARN("%s uses BP as a scratch register",
-                    func->name);
+               WARN_FUNC("BP used as a scratch register",
+                         insn->sec, insn->offset);
                return 1;
        }
 
@@ -2364,14 +2361,27 @@ static bool ignore_unreachable_insn(struct instruction *insn)
            !strcmp(insn->sec->name, ".altinstr_aux"))
                return true;
 
+       if (!insn->func)
+               return false;
+
+       /*
+        * CONFIG_UBSAN_TRAP inserts a UD2 when it sees
+        * __builtin_unreachable().  The BUG() macro has an unreachable() after
+        * the UD2, which causes GCC's undefined trap logic to emit another UD2
+        * (or occasionally a JMP to UD2).
+        */
+       if (list_prev_entry(insn, list)->dead_end &&
+           (insn->type == INSN_BUG ||
+            (insn->type == INSN_JUMP_UNCONDITIONAL &&
+             insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
+               return true;
+
        /*
         * Check if this (or a subsequent) instruction is related to
         * CONFIG_UBSAN or CONFIG_KASAN.
         *
         * End the search at 5 instructions to avoid going into the weeds.
         */
-       if (!insn->func)
-               return false;
        for (i = 0; i < 5; i++) {
 
                if (is_kasan_insn(insn) || is_ubsan_insn(insn))
index 09ddc8f..c4857fa 100644 (file)
@@ -105,7 +105,7 @@ static int symbol_by_offset(const void *key, const struct rb_node *node)
 
        if (*o < s->offset)
                return -1;
-       if (*o > s->offset + s->len)
+       if (*o >= s->offset + s->len)
                return 1;
 
        return 0;
index ebbb10c..0b79c23 100644 (file)
@@ -99,7 +99,7 @@ static inline u32 sec_offset_hash(struct section *sec, unsigned long offset)
        offset &= OFFSET_STRIDE_MASK;
 
        ol = offset;
-       oh = offset >> 32;
+       oh = (offset >> 16) >> 16;
 
        __jhash_mix(ol, oh, idx);
 
index 13ccf77..ba4cbb1 100644 (file)
@@ -66,7 +66,7 @@ int orc_dump(const char *_objname)
        char *name;
        size_t nr_sections;
        Elf64_Addr orc_ip_addr = 0;
-       size_t shstrtab_idx;
+       size_t shstrtab_idx, strtab_idx = 0;
        Elf *elf;
        Elf_Scn *scn;
        GElf_Shdr sh;
@@ -127,6 +127,8 @@ int orc_dump(const char *_objname)
 
                if (!strcmp(name, ".symtab")) {
                        symtab = data;
+               } else if (!strcmp(name, ".strtab")) {
+                       strtab_idx = i;
                } else if (!strcmp(name, ".orc_unwind")) {
                        orc = data->d_buf;
                        orc_size = sh.sh_size;
@@ -138,7 +140,7 @@ int orc_dump(const char *_objname)
                }
        }
 
-       if (!symtab || !orc || !orc_ip)
+       if (!symtab || !strtab_idx || !orc || !orc_ip)
                return 0;
 
        if (orc_size % sizeof(*orc) != 0) {
@@ -159,21 +161,29 @@ int orc_dump(const char *_objname)
                                return -1;
                        }
 
-                       scn = elf_getscn(elf, sym.st_shndx);
-                       if (!scn) {
-                               WARN_ELF("elf_getscn");
-                               return -1;
-                       }
-
-                       if (!gelf_getshdr(scn, &sh)) {
-                               WARN_ELF("gelf_getshdr");
-                               return -1;
-                       }
-
-                       name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
-                       if (!name || !*name) {
-                               WARN_ELF("elf_strptr");
-                               return -1;
+                       if (GELF_ST_TYPE(sym.st_info) == STT_SECTION) {
+                               scn = elf_getscn(elf, sym.st_shndx);
+                               if (!scn) {
+                                       WARN_ELF("elf_getscn");
+                                       return -1;
+                               }
+
+                               if (!gelf_getshdr(scn, &sh)) {
+                                       WARN_ELF("gelf_getshdr");
+                                       return -1;
+                               }
+
+                               name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
+                               if (!name) {
+                                       WARN_ELF("elf_strptr");
+                                       return -1;
+                               }
+                       } else {
+                               name = elf_strptr(elf, strtab_idx, sym.st_name);
+                               if (!name) {
+                                       WARN_ELF("elf_strptr");
+                                       return -1;
+                               }
                        }
 
                        printf("%s+%llx:", name, (unsigned long long)rela.r_addend);
index 41e4a27..4c0dabd 100644 (file)
@@ -88,11 +88,6 @@ static int create_orc_entry(struct elf *elf, struct section *u_sec, struct secti
        struct orc_entry *orc;
        struct rela *rela;
 
-       if (!insn_sec->sym) {
-               WARN("missing symbol for section %s", insn_sec->name);
-               return -1;
-       }
-
        /* populate ORC data */
        orc = (struct orc_entry *)u_sec->data->d_buf + idx;
        memcpy(orc, o, sizeof(*orc));
@@ -105,8 +100,32 @@ static int create_orc_entry(struct elf *elf, struct section *u_sec, struct secti
        }
        memset(rela, 0, sizeof(*rela));
 
-       rela->sym = insn_sec->sym;
-       rela->addend = insn_off;
+       if (insn_sec->sym) {
+               rela->sym = insn_sec->sym;
+               rela->addend = insn_off;
+       } else {
+               /*
+                * The Clang assembler doesn't produce section symbols, so we
+                * have to reference the function symbol instead:
+                */
+               rela->sym = find_symbol_containing(insn_sec, insn_off);
+               if (!rela->sym) {
+                       /*
+                        * Hack alert.  This happens when we need to reference
+                        * the NOP pad insn immediately after the function.
+                        */
+                       rela->sym = find_symbol_containing(insn_sec,
+                                                          insn_off - 1);
+               }
+               if (!rela->sym) {
+                       WARN("missing symbol for insn at offset 0x%lx\n",
+                            insn_off);
+                       return -1;
+               }
+
+               rela->addend = insn_off - rela->sym->offset;
+       }
+
        rela->type = R_X86_64_PC32;
        rela->offset = idx * sizeof(int);
        rela->sec = ip_relasec;
index 44d510b..37b844f 100644 (file)
 #
 # The abi is "common", "64" or "x32" for this file.
 #
-0      common  read                    __x64_sys_read
-1      common  write                   __x64_sys_write
-2      common  open                    __x64_sys_open
-3      common  close                   __x64_sys_close
-4      common  stat                    __x64_sys_newstat
-5      common  fstat                   __x64_sys_newfstat
-6      common  lstat                   __x64_sys_newlstat
-7      common  poll                    __x64_sys_poll
-8      common  lseek                   __x64_sys_lseek
-9      common  mmap                    __x64_sys_mmap
-10     common  mprotect                __x64_sys_mprotect
-11     common  munmap                  __x64_sys_munmap
-12     common  brk                     __x64_sys_brk
-13     64      rt_sigaction            __x64_sys_rt_sigaction
-14     common  rt_sigprocmask          __x64_sys_rt_sigprocmask
-15     64      rt_sigreturn            __x64_sys_rt_sigreturn/ptregs
-16     64      ioctl                   __x64_sys_ioctl
-17     common  pread64                 __x64_sys_pread64
-18     common  pwrite64                __x64_sys_pwrite64
-19     64      readv                   __x64_sys_readv
-20     64      writev                  __x64_sys_writev
-21     common  access                  __x64_sys_access
-22     common  pipe                    __x64_sys_pipe
-23     common  select                  __x64_sys_select
-24     common  sched_yield             __x64_sys_sched_yield
-25     common  mremap                  __x64_sys_mremap
-26     common  msync                   __x64_sys_msync
-27     common  mincore                 __x64_sys_mincore
-28     common  madvise                 __x64_sys_madvise
-29     common  shmget                  __x64_sys_shmget
-30     common  shmat                   __x64_sys_shmat
-31     common  shmctl                  __x64_sys_shmctl
-32     common  dup                     __x64_sys_dup
-33     common  dup2                    __x64_sys_dup2
-34     common  pause                   __x64_sys_pause
-35     common  nanosleep               __x64_sys_nanosleep
-36     common  getitimer               __x64_sys_getitimer
-37     common  alarm                   __x64_sys_alarm
-38     common  setitimer               __x64_sys_setitimer
-39     common  getpid                  __x64_sys_getpid
-40     common  sendfile                __x64_sys_sendfile64
-41     common  socket                  __x64_sys_socket
-42     common  connect                 __x64_sys_connect
-43     common  accept                  __x64_sys_accept
-44     common  sendto                  __x64_sys_sendto
-45     64      recvfrom                __x64_sys_recvfrom
-46     64      sendmsg                 __x64_sys_sendmsg
-47     64      recvmsg                 __x64_sys_recvmsg
-48     common  shutdown                __x64_sys_shutdown
-49     common  bind                    __x64_sys_bind
-50     common  listen                  __x64_sys_listen
-51     common  getsockname             __x64_sys_getsockname
-52     common  getpeername             __x64_sys_getpeername
-53     common  socketpair              __x64_sys_socketpair
-54     64      setsockopt              __x64_sys_setsockopt
-55     64      getsockopt              __x64_sys_getsockopt
-56     common  clone                   __x64_sys_clone/ptregs
-57     common  fork                    __x64_sys_fork/ptregs
-58     common  vfork                   __x64_sys_vfork/ptregs
-59     64      execve                  __x64_sys_execve/ptregs
-60     common  exit                    __x64_sys_exit
-61     common  wait4                   __x64_sys_wait4
-62     common  kill                    __x64_sys_kill
-63     common  uname                   __x64_sys_newuname
-64     common  semget                  __x64_sys_semget
-65     common  semop                   __x64_sys_semop
-66     common  semctl                  __x64_sys_semctl
-67     common  shmdt                   __x64_sys_shmdt
-68     common  msgget                  __x64_sys_msgget
-69     common  msgsnd                  __x64_sys_msgsnd
-70     common  msgrcv                  __x64_sys_msgrcv
-71     common  msgctl                  __x64_sys_msgctl
-72     common  fcntl                   __x64_sys_fcntl
-73     common  flock                   __x64_sys_flock
-74     common  fsync                   __x64_sys_fsync
-75     common  fdatasync               __x64_sys_fdatasync
-76     common  truncate                __x64_sys_truncate
-77     common  ftruncate               __x64_sys_ftruncate
-78     common  getdents                __x64_sys_getdents
-79     common  getcwd                  __x64_sys_getcwd
-80     common  chdir                   __x64_sys_chdir
-81     common  fchdir                  __x64_sys_fchdir
-82     common  rename                  __x64_sys_rename
-83     common  mkdir                   __x64_sys_mkdir
-84     common  rmdir                   __x64_sys_rmdir
-85     common  creat                   __x64_sys_creat
-86     common  link                    __x64_sys_link
-87     common  unlink                  __x64_sys_unlink
-88     common  symlink                 __x64_sys_symlink
-89     common  readlink                __x64_sys_readlink
-90     common  chmod                   __x64_sys_chmod
-91     common  fchmod                  __x64_sys_fchmod
-92     common  chown                   __x64_sys_chown
-93     common  fchown                  __x64_sys_fchown
-94     common  lchown                  __x64_sys_lchown
-95     common  umask                   __x64_sys_umask
-96     common  gettimeofday            __x64_sys_gettimeofday
-97     common  getrlimit               __x64_sys_getrlimit
-98     common  getrusage               __x64_sys_getrusage
-99     common  sysinfo                 __x64_sys_sysinfo
-100    common  times                   __x64_sys_times
-101    64      ptrace                  __x64_sys_ptrace
-102    common  getuid                  __x64_sys_getuid
-103    common  syslog                  __x64_sys_syslog
-104    common  getgid                  __x64_sys_getgid
-105    common  setuid                  __x64_sys_setuid
-106    common  setgid                  __x64_sys_setgid
-107    common  geteuid                 __x64_sys_geteuid
-108    common  getegid                 __x64_sys_getegid
-109    common  setpgid                 __x64_sys_setpgid
-110    common  getppid                 __x64_sys_getppid
-111    common  getpgrp                 __x64_sys_getpgrp
-112    common  setsid                  __x64_sys_setsid
-113    common  setreuid                __x64_sys_setreuid
-114    common  setregid                __x64_sys_setregid
-115    common  getgroups               __x64_sys_getgroups
-116    common  setgroups               __x64_sys_setgroups
-117    common  setresuid               __x64_sys_setresuid
-118    common  getresuid               __x64_sys_getresuid
-119    common  setresgid               __x64_sys_setresgid
-120    common  getresgid               __x64_sys_getresgid
-121    common  getpgid                 __x64_sys_getpgid
-122    common  setfsuid                __x64_sys_setfsuid
-123    common  setfsgid                __x64_sys_setfsgid
-124    common  getsid                  __x64_sys_getsid
-125    common  capget                  __x64_sys_capget
-126    common  capset                  __x64_sys_capset
-127    64      rt_sigpending           __x64_sys_rt_sigpending
-128    64      rt_sigtimedwait         __x64_sys_rt_sigtimedwait
-129    64      rt_sigqueueinfo         __x64_sys_rt_sigqueueinfo
-130    common  rt_sigsuspend           __x64_sys_rt_sigsuspend
-131    64      sigaltstack             __x64_sys_sigaltstack
-132    common  utime                   __x64_sys_utime
-133    common  mknod                   __x64_sys_mknod
+0      common  read                    sys_read
+1      common  write                   sys_write
+2      common  open                    sys_open
+3      common  close                   sys_close
+4      common  stat                    sys_newstat
+5      common  fstat                   sys_newfstat
+6      common  lstat                   sys_newlstat
+7      common  poll                    sys_poll
+8      common  lseek                   sys_lseek
+9      common  mmap                    sys_mmap
+10     common  mprotect                sys_mprotect
+11     common  munmap                  sys_munmap
+12     common  brk                     sys_brk
+13     64      rt_sigaction            sys_rt_sigaction
+14     common  rt_sigprocmask          sys_rt_sigprocmask
+15     64      rt_sigreturn            sys_rt_sigreturn
+16     64      ioctl                   sys_ioctl
+17     common  pread64                 sys_pread64
+18     common  pwrite64                sys_pwrite64
+19     64      readv                   sys_readv
+20     64      writev                  sys_writev
+21     common  access                  sys_access
+22     common  pipe                    sys_pipe
+23     common  select                  sys_select
+24     common  sched_yield             sys_sched_yield
+25     common  mremap                  sys_mremap
+26     common  msync                   sys_msync
+27     common  mincore                 sys_mincore
+28     common  madvise                 sys_madvise
+29     common  shmget                  sys_shmget
+30     common  shmat                   sys_shmat
+31     common  shmctl                  sys_shmctl
+32     common  dup                     sys_dup
+33     common  dup2                    sys_dup2
+34     common  pause                   sys_pause
+35     common  nanosleep               sys_nanosleep
+36     common  getitimer               sys_getitimer
+37     common  alarm                   sys_alarm
+38     common  setitimer               sys_setitimer
+39     common  getpid                  sys_getpid
+40     common  sendfile                sys_sendfile64
+41     common  socket                  sys_socket
+42     common  connect                 sys_connect
+43     common  accept                  sys_accept
+44     common  sendto                  sys_sendto
+45     64      recvfrom                sys_recvfrom
+46     64      sendmsg                 sys_sendmsg
+47     64      recvmsg                 sys_recvmsg
+48     common  shutdown                sys_shutdown
+49     common  bind                    sys_bind
+50     common  listen                  sys_listen
+51     common  getsockname             sys_getsockname
+52     common  getpeername             sys_getpeername
+53     common  socketpair              sys_socketpair
+54     64      setsockopt              sys_setsockopt
+55     64      getsockopt              sys_getsockopt
+56     common  clone                   sys_clone
+57     common  fork                    sys_fork
+58     common  vfork                   sys_vfork
+59     64      execve                  sys_execve
+60     common  exit                    sys_exit
+61     common  wait4                   sys_wait4
+62     common  kill                    sys_kill
+63     common  uname                   sys_newuname
+64     common  semget                  sys_semget
+65     common  semop                   sys_semop
+66     common  semctl                  sys_semctl
+67     common  shmdt                   sys_shmdt
+68     common  msgget                  sys_msgget
+69     common  msgsnd                  sys_msgsnd
+70     common  msgrcv                  sys_msgrcv
+71     common  msgctl                  sys_msgctl
+72     common  fcntl                   sys_fcntl
+73     common  flock                   sys_flock
+74     common  fsync                   sys_fsync
+75     common  fdatasync               sys_fdatasync
+76     common  truncate                sys_truncate
+77     common  ftruncate               sys_ftruncate
+78     common  getdents                sys_getdents
+79     common  getcwd                  sys_getcwd
+80     common  chdir                   sys_chdir
+81     common  fchdir                  sys_fchdir
+82     common  rename                  sys_rename
+83     common  mkdir                   sys_mkdir
+84     common  rmdir                   sys_rmdir
+85     common  creat                   sys_creat
+86     common  link                    sys_link
+87     common  unlink                  sys_unlink
+88     common  symlink                 sys_symlink
+89     common  readlink                sys_readlink
+90     common  chmod                   sys_chmod
+91     common  fchmod                  sys_fchmod
+92     common  chown                   sys_chown
+93     common  fchown                  sys_fchown
+94     common  lchown                  sys_lchown
+95     common  umask                   sys_umask
+96     common  gettimeofday            sys_gettimeofday
+97     common  getrlimit               sys_getrlimit
+98     common  getrusage               sys_getrusage
+99     common  sysinfo                 sys_sysinfo
+100    common  times                   sys_times
+101    64      ptrace                  sys_ptrace
+102    common  getuid                  sys_getuid
+103    common  syslog                  sys_syslog
+104    common  getgid                  sys_getgid
+105    common  setuid                  sys_setuid
+106    common  setgid                  sys_setgid
+107    common  geteuid                 sys_geteuid
+108    common  getegid                 sys_getegid
+109    common  setpgid                 sys_setpgid
+110    common  getppid                 sys_getppid
+111    common  getpgrp                 sys_getpgrp
+112    common  setsid                  sys_setsid
+113    common  setreuid                sys_setreuid
+114    common  setregid                sys_setregid
+115    common  getgroups               sys_getgroups
+116    common  setgroups               sys_setgroups
+117    common  setresuid               sys_setresuid
+118    common  getresuid               sys_getresuid
+119    common  setresgid               sys_setresgid
+120    common  getresgid               sys_getresgid
+121    common  getpgid                 sys_getpgid
+122    common  setfsuid                sys_setfsuid
+123    common  setfsgid                sys_setfsgid
+124    common  getsid                  sys_getsid
+125    common  capget                  sys_capget
+126    common  capset                  sys_capset
+127    64      rt_sigpending           sys_rt_sigpending
+128    64      rt_sigtimedwait         sys_rt_sigtimedwait
+129    64      rt_sigqueueinfo         sys_rt_sigqueueinfo
+130    common  rt_sigsuspend           sys_rt_sigsuspend
+131    64      sigaltstack             sys_sigaltstack
+132    common  utime                   sys_utime
+133    common  mknod                   sys_mknod
 134    64      uselib
-135    common  personality             __x64_sys_personality
-136    common  ustat                   __x64_sys_ustat
-137    common  statfs                  __x64_sys_statfs
-138    common  fstatfs                 __x64_sys_fstatfs
-139    common  sysfs                   __x64_sys_sysfs
-140    common  getpriority             __x64_sys_getpriority
-141    common  setpriority             __x64_sys_setpriority
-142    common  sched_setparam          __x64_sys_sched_setparam
-143    common  sched_getparam          __x64_sys_sched_getparam
-144    common  sched_setscheduler      __x64_sys_sched_setscheduler
-145    common  sched_getscheduler      __x64_sys_sched_getscheduler
-146    common  sched_get_priority_max  __x64_sys_sched_get_priority_max
-147    common  sched_get_priority_min  __x64_sys_sched_get_priority_min
-148    common  sched_rr_get_interval   __x64_sys_sched_rr_get_interval
-149    common  mlock                   __x64_sys_mlock
-150    common  munlock                 __x64_sys_munlock
-151    common  mlockall                __x64_sys_mlockall
-152    common  munlockall              __x64_sys_munlockall
-153    common  vhangup                 __x64_sys_vhangup
-154    common  modify_ldt              __x64_sys_modify_ldt
-155    common  pivot_root              __x64_sys_pivot_root
-156    64      _sysctl                 __x64_sys_sysctl
-157    common  prctl                   __x64_sys_prctl
-158    common  arch_prctl              __x64_sys_arch_prctl
-159    common  adjtimex                __x64_sys_adjtimex
-160    common  setrlimit               __x64_sys_setrlimit
-161    common  chroot                  __x64_sys_chroot
-162    common  sync                    __x64_sys_sync
-163    common  acct                    __x64_sys_acct
-164    common  settimeofday            __x64_sys_settimeofday
-165    common  mount                   __x64_sys_mount
-166    common  umount2                 __x64_sys_umount
-167    common  swapon                  __x64_sys_swapon
-168    common  swapoff                 __x64_sys_swapoff
-169    common  reboot                  __x64_sys_reboot
-170    common  sethostname             __x64_sys_sethostname
-171    common  setdomainname           __x64_sys_setdomainname
-172    common  iopl                    __x64_sys_iopl/ptregs
-173    common  ioperm                  __x64_sys_ioperm
+135    common  personality             sys_personality
+136    common  ustat                   sys_ustat
+137    common  statfs                  sys_statfs
+138    common  fstatfs                 sys_fstatfs
+139    common  sysfs                   sys_sysfs
+140    common  getpriority             sys_getpriority
+141    common  setpriority             sys_setpriority
+142    common  sched_setparam          sys_sched_setparam
+143    common  sched_getparam          sys_sched_getparam
+144    common  sched_setscheduler      sys_sched_setscheduler
+145    common  sched_getscheduler      sys_sched_getscheduler
+146    common  sched_get_priority_max  sys_sched_get_priority_max
+147    common  sched_get_priority_min  sys_sched_get_priority_min
+148    common  sched_rr_get_interval   sys_sched_rr_get_interval
+149    common  mlock                   sys_mlock
+150    common  munlock                 sys_munlock
+151    common  mlockall                sys_mlockall
+152    common  munlockall              sys_munlockall
+153    common  vhangup                 sys_vhangup
+154    common  modify_ldt              sys_modify_ldt
+155    common  pivot_root              sys_pivot_root
+156    64      _sysctl                 sys_sysctl
+157    common  prctl                   sys_prctl
+158    common  arch_prctl              sys_arch_prctl
+159    common  adjtimex                sys_adjtimex
+160    common  setrlimit               sys_setrlimit
+161    common  chroot                  sys_chroot
+162    common  sync                    sys_sync
+163    common  acct                    sys_acct
+164    common  settimeofday            sys_settimeofday
+165    common  mount                   sys_mount
+166    common  umount2                 sys_umount
+167    common  swapon                  sys_swapon
+168    common  swapoff                 sys_swapoff
+169    common  reboot                  sys_reboot
+170    common  sethostname             sys_sethostname
+171    common  setdomainname           sys_setdomainname
+172    common  iopl                    sys_iopl
+173    common  ioperm                  sys_ioperm
 174    64      create_module
-175    common  init_module             __x64_sys_init_module
-176    common  delete_module           __x64_sys_delete_module
+175    common  init_module             sys_init_module
+176    common  delete_module           sys_delete_module
 177    64      get_kernel_syms
 178    64      query_module
-179    common  quotactl                __x64_sys_quotactl
+179    common  quotactl                sys_quotactl
 180    64      nfsservctl
 181    common  getpmsg
 182    common  putpmsg
 183    common  afs_syscall
 184    common  tuxcall
 185    common  security
-186    common  gettid                  __x64_sys_gettid
-187    common  readahead               __x64_sys_readahead
-188    common  setxattr                __x64_sys_setxattr
-189    common  lsetxattr               __x64_sys_lsetxattr
-190    common  fsetxattr               __x64_sys_fsetxattr
-191    common  getxattr                __x64_sys_getxattr
-192    common  lgetxattr               __x64_sys_lgetxattr
-193    common  fgetxattr               __x64_sys_fgetxattr
-194    common  listxattr               __x64_sys_listxattr
-195    common  llistxattr              __x64_sys_llistxattr
-196    common  flistxattr              __x64_sys_flistxattr
-197    common  removexattr             __x64_sys_removexattr
-198    common  lremovexattr            __x64_sys_lremovexattr
-199    common  fremovexattr            __x64_sys_fremovexattr
-200    common  tkill                   __x64_sys_tkill
-201    common  time                    __x64_sys_time
-202    common  futex                   __x64_sys_futex
-203    common  sched_setaffinity       __x64_sys_sched_setaffinity
-204    common  sched_getaffinity       __x64_sys_sched_getaffinity
+186    common  gettid                  sys_gettid
+187    common  readahead               sys_readahead
+188    common  setxattr                sys_setxattr
+189    common  lsetxattr               sys_lsetxattr
+190    common  fsetxattr               sys_fsetxattr
+191    common  getxattr                sys_getxattr
+192    common  lgetxattr               sys_lgetxattr
+193    common  fgetxattr               sys_fgetxattr
+194    common  listxattr               sys_listxattr
+195    common  llistxattr              sys_llistxattr
+196    common  flistxattr              sys_flistxattr
+197    common  removexattr             sys_removexattr
+198    common  lremovexattr            sys_lremovexattr
+199    common  fremovexattr            sys_fremovexattr
+200    common  tkill                   sys_tkill
+201    common  time                    sys_time
+202    common  futex                   sys_futex
+203    common  sched_setaffinity       sys_sched_setaffinity
+204    common  sched_getaffinity       sys_sched_getaffinity
 205    64      set_thread_area
-206    64      io_setup                __x64_sys_io_setup
-207    common  io_destroy              __x64_sys_io_destroy
-208    common  io_getevents            __x64_sys_io_getevents
-209    64      io_submit               __x64_sys_io_submit
-210    common  io_cancel               __x64_sys_io_cancel
+206    64      io_setup                sys_io_setup
+207    common  io_destroy              sys_io_destroy
+208    common  io_getevents            sys_io_getevents
+209    64      io_submit               sys_io_submit
+210    common  io_cancel               sys_io_cancel
 211    64      get_thread_area
-212    common  lookup_dcookie          __x64_sys_lookup_dcookie
-213    common  epoll_create            __x64_sys_epoll_create
+212    common  lookup_dcookie          sys_lookup_dcookie
+213    common  epoll_create            sys_epoll_create
 214    64      epoll_ctl_old
 215    64      epoll_wait_old
-216    common  remap_file_pages        __x64_sys_remap_file_pages
-217    common  getdents64              __x64_sys_getdents64
-218    common  set_tid_address         __x64_sys_set_tid_address
-219    common  restart_syscall         __x64_sys_restart_syscall
-220    common  semtimedop              __x64_sys_semtimedop
-221    common  fadvise64               __x64_sys_fadvise64
-222    64      timer_create            __x64_sys_timer_create
-223    common  timer_settime           __x64_sys_timer_settime
-224    common  timer_gettime           __x64_sys_timer_gettime
-225    common  timer_getoverrun        __x64_sys_timer_getoverrun
-226    common  timer_delete            __x64_sys_timer_delete
-227    common  clock_settime           __x64_sys_clock_settime
-228    common  clock_gettime           __x64_sys_clock_gettime
-229    common  clock_getres            __x64_sys_clock_getres
-230    common  clock_nanosleep         __x64_sys_clock_nanosleep
-231    common  exit_group              __x64_sys_exit_group
-232    common  epoll_wait              __x64_sys_epoll_wait
-233    common  epoll_ctl               __x64_sys_epoll_ctl
-234    common  tgkill                  __x64_sys_tgkill
-235    common  utimes                  __x64_sys_utimes
+216    common  remap_file_pages        sys_remap_file_pages
+217    common  getdents64              sys_getdents64
+218    common  set_tid_address         sys_set_tid_address
+219    common  restart_syscall         sys_restart_syscall
+220    common  semtimedop              sys_semtimedop
+221    common  fadvise64               sys_fadvise64
+222    64      timer_create            sys_timer_create
+223    common  timer_settime           sys_timer_settime
+224    common  timer_gettime           sys_timer_gettime
+225    common  timer_getoverrun        sys_timer_getoverrun
+226    common  timer_delete            sys_timer_delete
+227    common  clock_settime           sys_clock_settime
+228    common  clock_gettime           sys_clock_gettime
+229    common  clock_getres            sys_clock_getres
+230    common  clock_nanosleep         sys_clock_nanosleep
+231    common  exit_group              sys_exit_group
+232    common  epoll_wait              sys_epoll_wait
+233    common  epoll_ctl               sys_epoll_ctl
+234    common  tgkill                  sys_tgkill
+235    common  utimes                  sys_utimes
 236    64      vserver
-237    common  mbind                   __x64_sys_mbind
-238    common  set_mempolicy           __x64_sys_set_mempolicy
-239    common  get_mempolicy           __x64_sys_get_mempolicy
-240    common  mq_open                 __x64_sys_mq_open
-241    common  mq_unlink               __x64_sys_mq_unlink
-242    common  mq_timedsend            __x64_sys_mq_timedsend
-243    common  mq_timedreceive         __x64_sys_mq_timedreceive
-244    64      mq_notify               __x64_sys_mq_notify
-245    common  mq_getsetattr           __x64_sys_mq_getsetattr
-246    64      kexec_load              __x64_sys_kexec_load
-247    64      waitid                  __x64_sys_waitid
-248    common  add_key                 __x64_sys_add_key
-249    common  request_key             __x64_sys_request_key
-250    common  keyctl                  __x64_sys_keyctl
-251    common  ioprio_set              __x64_sys_ioprio_set
-252    common  ioprio_get              __x64_sys_ioprio_get
-253    common  inotify_init            __x64_sys_inotify_init
-254    common  inotify_add_watch       __x64_sys_inotify_add_watch
-255    common  inotify_rm_watch        __x64_sys_inotify_rm_watch
-256    common  migrate_pages           __x64_sys_migrate_pages
-257    common  openat                  __x64_sys_openat
-258    common  mkdirat                 __x64_sys_mkdirat
-259    common  mknodat                 __x64_sys_mknodat
-260    common  fchownat                __x64_sys_fchownat
-261    common  futimesat               __x64_sys_futimesat
-262    common  newfstatat              __x64_sys_newfstatat
-263    common  unlinkat                __x64_sys_unlinkat
-264    common  renameat                __x64_sys_renameat
-265    common  linkat                  __x64_sys_linkat
-266    common  symlinkat               __x64_sys_symlinkat
-267    common  readlinkat              __x64_sys_readlinkat
-268    common  fchmodat                __x64_sys_fchmodat
-269    common  faccessat               __x64_sys_faccessat
-270    common  pselect6                __x64_sys_pselect6
-271    common  ppoll                   __x64_sys_ppoll
-272    common  unshare                 __x64_sys_unshare
-273    64      set_robust_list         __x64_sys_set_robust_list
-274    64      get_robust_list         __x64_sys_get_robust_list
-275    common  splice                  __x64_sys_splice
-276    common  tee                     __x64_sys_tee
-277    common  sync_file_range         __x64_sys_sync_file_range
-278    64      vmsplice                __x64_sys_vmsplice
-279    64      move_pages              __x64_sys_move_pages
-280    common  utimensat               __x64_sys_utimensat
-281    common  epoll_pwait             __x64_sys_epoll_pwait
-282    common  signalfd                __x64_sys_signalfd
-283    common  timerfd_create          __x64_sys_timerfd_create
-284    common  eventfd                 __x64_sys_eventfd
-285    common  fallocate               __x64_sys_fallocate
-286    common  timerfd_settime         __x64_sys_timerfd_settime
-287    common  timerfd_gettime         __x64_sys_timerfd_gettime
-288    common  accept4                 __x64_sys_accept4
-289    common  signalfd4               __x64_sys_signalfd4
-290    common  eventfd2                __x64_sys_eventfd2
-291    common  epoll_create1           __x64_sys_epoll_create1
-292    common  dup3                    __x64_sys_dup3
-293    common  pipe2                   __x64_sys_pipe2
-294    common  inotify_init1           __x64_sys_inotify_init1
-295    64      preadv                  __x64_sys_preadv
-296    64      pwritev                 __x64_sys_pwritev
-297    64      rt_tgsigqueueinfo       __x64_sys_rt_tgsigqueueinfo
-298    common  perf_event_open         __x64_sys_perf_event_open
-299    64      recvmmsg                __x64_sys_recvmmsg
-300    common  fanotify_init           __x64_sys_fanotify_init
-301    common  fanotify_mark           __x64_sys_fanotify_mark
-302    common  prlimit64               __x64_sys_prlimit64
-303    common  name_to_handle_at       __x64_sys_name_to_handle_at
-304    common  open_by_handle_at       __x64_sys_open_by_handle_at
-305    common  clock_adjtime           __x64_sys_clock_adjtime
-306    common  syncfs                  __x64_sys_syncfs
-307    64      sendmmsg                __x64_sys_sendmmsg
-308    common  setns                   __x64_sys_setns
-309    common  getcpu                  __x64_sys_getcpu
-310    64      process_vm_readv        __x64_sys_process_vm_readv
-311    64      process_vm_writev       __x64_sys_process_vm_writev
-312    common  kcmp                    __x64_sys_kcmp
-313    common  finit_module            __x64_sys_finit_module
-314    common  sched_setattr           __x64_sys_sched_setattr
-315    common  sched_getattr           __x64_sys_sched_getattr
-316    common  renameat2               __x64_sys_renameat2
-317    common  seccomp                 __x64_sys_seccomp
-318    common  getrandom               __x64_sys_getrandom
-319    common  memfd_create            __x64_sys_memfd_create
-320    common  kexec_file_load         __x64_sys_kexec_file_load
-321    common  bpf                     __x64_sys_bpf
-322    64      execveat                __x64_sys_execveat/ptregs
-323    common  userfaultfd             __x64_sys_userfaultfd
-324    common  membarrier              __x64_sys_membarrier
-325    common  mlock2                  __x64_sys_mlock2
-326    common  copy_file_range         __x64_sys_copy_file_range
-327    64      preadv2                 __x64_sys_preadv2
-328    64      pwritev2                __x64_sys_pwritev2
-329    common  pkey_mprotect           __x64_sys_pkey_mprotect
-330    common  pkey_alloc              __x64_sys_pkey_alloc
-331    common  pkey_free               __x64_sys_pkey_free
-332    common  statx                   __x64_sys_statx
-333    common  io_pgetevents           __x64_sys_io_pgetevents
-334    common  rseq                    __x64_sys_rseq
+237    common  mbind                   sys_mbind
+238    common  set_mempolicy           sys_set_mempolicy
+239    common  get_mempolicy           sys_get_mempolicy
+240    common  mq_open                 sys_mq_open
+241    common  mq_unlink               sys_mq_unlink
+242    common  mq_timedsend            sys_mq_timedsend
+243    common  mq_timedreceive         sys_mq_timedreceive
+244    64      mq_notify               sys_mq_notify
+245    common  mq_getsetattr           sys_mq_getsetattr
+246    64      kexec_load              sys_kexec_load
+247    64      waitid                  sys_waitid
+248    common  add_key                 sys_add_key
+249    common  request_key             sys_request_key
+250    common  keyctl                  sys_keyctl
+251    common  ioprio_set              sys_ioprio_set
+252    common  ioprio_get              sys_ioprio_get
+253    common  inotify_init            sys_inotify_init
+254    common  inotify_add_watch       sys_inotify_add_watch
+255    common  inotify_rm_watch        sys_inotify_rm_watch
+256    common  migrate_pages           sys_migrate_pages
+257    common  openat                  sys_openat
+258    common  mkdirat                 sys_mkdirat
+259    common  mknodat                 sys_mknodat
+260    common  fchownat                sys_fchownat
+261    common  futimesat               sys_futimesat
+262    common  newfstatat              sys_newfstatat
+263    common  unlinkat                sys_unlinkat
+264    common  renameat                sys_renameat
+265    common  linkat                  sys_linkat
+266    common  symlinkat               sys_symlinkat
+267    common  readlinkat              sys_readlinkat
+268    common  fchmodat                sys_fchmodat
+269    common  faccessat               sys_faccessat
+270    common  pselect6                sys_pselect6
+271    common  ppoll                   sys_ppoll
+272    common  unshare                 sys_unshare
+273    64      set_robust_list         sys_set_robust_list
+274    64      get_robust_list         sys_get_robust_list
+275    common  splice                  sys_splice
+276    common  tee                     sys_tee
+277    common  sync_file_range         sys_sync_file_range
+278    64      vmsplice                sys_vmsplice
+279    64      move_pages              sys_move_pages
+280    common  utimensat               sys_utimensat
+281    common  epoll_pwait             sys_epoll_pwait
+282    common  signalfd                sys_signalfd
+283    common  timerfd_create          sys_timerfd_create
+284    common  eventfd                 sys_eventfd
+285    common  fallocate               sys_fallocate
+286    common  timerfd_settime         sys_timerfd_settime
+287    common  timerfd_gettime         sys_timerfd_gettime
+288    common  accept4                 sys_accept4
+289    common  signalfd4               sys_signalfd4
+290    common  eventfd2                sys_eventfd2
+291    common  epoll_create1           sys_epoll_create1
+292    common  dup3                    sys_dup3
+293    common  pipe2                   sys_pipe2
+294    common  inotify_init1           sys_inotify_init1
+295    64      preadv                  sys_preadv
+296    64      pwritev                 sys_pwritev
+297    64      rt_tgsigqueueinfo       sys_rt_tgsigqueueinfo
+298    common  perf_event_open         sys_perf_event_open
+299    64      recvmmsg                sys_recvmmsg
+300    common  fanotify_init           sys_fanotify_init
+301    common  fanotify_mark           sys_fanotify_mark
+302    common  prlimit64               sys_prlimit64
+303    common  name_to_handle_at       sys_name_to_handle_at
+304    common  open_by_handle_at       sys_open_by_handle_at
+305    common  clock_adjtime           sys_clock_adjtime
+306    common  syncfs                  sys_syncfs
+307    64      sendmmsg                sys_sendmmsg
+308    common  setns                   sys_setns
+309    common  getcpu                  sys_getcpu
+310    64      process_vm_readv        sys_process_vm_readv
+311    64      process_vm_writev       sys_process_vm_writev
+312    common  kcmp                    sys_kcmp
+313    common  finit_module            sys_finit_module
+314    common  sched_setattr           sys_sched_setattr
+315    common  sched_getattr           sys_sched_getattr
+316    common  renameat2               sys_renameat2
+317    common  seccomp                 sys_seccomp
+318    common  getrandom               sys_getrandom
+319    common  memfd_create            sys_memfd_create
+320    common  kexec_file_load         sys_kexec_file_load
+321    common  bpf                     sys_bpf
+322    64      execveat                sys_execveat
+323    common  userfaultfd             sys_userfaultfd
+324    common  membarrier              sys_membarrier
+325    common  mlock2                  sys_mlock2
+326    common  copy_file_range         sys_copy_file_range
+327    64      preadv2                 sys_preadv2
+328    64      pwritev2                sys_pwritev2
+329    common  pkey_mprotect           sys_pkey_mprotect
+330    common  pkey_alloc              sys_pkey_alloc
+331    common  pkey_free               sys_pkey_free
+332    common  statx                   sys_statx
+333    common  io_pgetevents           sys_io_pgetevents
+334    common  rseq                    sys_rseq
 # don't use numbers 387 through 423, add new calls after the last
 # 'common' entry
-424    common  pidfd_send_signal       __x64_sys_pidfd_send_signal
-425    common  io_uring_setup          __x64_sys_io_uring_setup
-426    common  io_uring_enter          __x64_sys_io_uring_enter
-427    common  io_uring_register       __x64_sys_io_uring_register
-428    common  open_tree               __x64_sys_open_tree
-429    common  move_mount              __x64_sys_move_mount
-430    common  fsopen                  __x64_sys_fsopen
-431    common  fsconfig                __x64_sys_fsconfig
-432    common  fsmount                 __x64_sys_fsmount
-433    common  fspick                  __x64_sys_fspick
-434    common  pidfd_open              __x64_sys_pidfd_open
-435    common  clone3                  __x64_sys_clone3/ptregs
-437    common  openat2                 __x64_sys_openat2
-438    common  pidfd_getfd             __x64_sys_pidfd_getfd
+424    common  pidfd_send_signal       sys_pidfd_send_signal
+425    common  io_uring_setup          sys_io_uring_setup
+426    common  io_uring_enter          sys_io_uring_enter
+427    common  io_uring_register       sys_io_uring_register
+428    common  open_tree               sys_open_tree
+429    common  move_mount              sys_move_mount
+430    common  fsopen                  sys_fsopen
+431    common  fsconfig                sys_fsconfig
+432    common  fsmount                 sys_fsmount
+433    common  fspick                  sys_fspick
+434    common  pidfd_open              sys_pidfd_open
+435    common  clone3                  sys_clone3
+437    common  openat2                 sys_openat2
+438    common  pidfd_getfd             sys_pidfd_getfd
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
 # on-the-fly for compat_sys_*() compatibility system calls if X86_X32
 # is defined.
 #
-512    x32     rt_sigaction            __x32_compat_sys_rt_sigaction
-513    x32     rt_sigreturn            sys32_x32_rt_sigreturn
-514    x32     ioctl                   __x32_compat_sys_ioctl
-515    x32     readv                   __x32_compat_sys_readv
-516    x32     writev                  __x32_compat_sys_writev
-517    x32     recvfrom                __x32_compat_sys_recvfrom
-518    x32     sendmsg                 __x32_compat_sys_sendmsg
-519    x32     recvmsg                 __x32_compat_sys_recvmsg
-520    x32     execve                  __x32_compat_sys_execve/ptregs
-521    x32     ptrace                  __x32_compat_sys_ptrace
-522    x32     rt_sigpending           __x32_compat_sys_rt_sigpending
-523    x32     rt_sigtimedwait         __x32_compat_sys_rt_sigtimedwait_time64
-524    x32     rt_sigqueueinfo         __x32_compat_sys_rt_sigqueueinfo
-525    x32     sigaltstack             __x32_compat_sys_sigaltstack
-526    x32     timer_create            __x32_compat_sys_timer_create
-527    x32     mq_notify               __x32_compat_sys_mq_notify
-528    x32     kexec_load              __x32_compat_sys_kexec_load
-529    x32     waitid                  __x32_compat_sys_waitid
-530    x32     set_robust_list         __x32_compat_sys_set_robust_list
-531    x32     get_robust_list         __x32_compat_sys_get_robust_list
-532    x32     vmsplice                __x32_compat_sys_vmsplice
-533    x32     move_pages              __x32_compat_sys_move_pages
-534    x32     preadv                  __x32_compat_sys_preadv64
-535    x32     pwritev                 __x32_compat_sys_pwritev64
-536    x32     rt_tgsigqueueinfo       __x32_compat_sys_rt_tgsigqueueinfo
-537    x32     recvmmsg                __x32_compat_sys_recvmmsg_time64
-538    x32     sendmmsg                __x32_compat_sys_sendmmsg
-539    x32     process_vm_readv        __x32_compat_sys_process_vm_readv
-540    x32     process_vm_writev       __x32_compat_sys_process_vm_writev
-541    x32     setsockopt              __x32_compat_sys_setsockopt
-542    x32     getsockopt              __x32_compat_sys_getsockopt
-543    x32     io_setup                __x32_compat_sys_io_setup
-544    x32     io_submit               __x32_compat_sys_io_submit
-545    x32     execveat                __x32_compat_sys_execveat/ptregs
-546    x32     preadv2                 __x32_compat_sys_preadv64v2
-547    x32     pwritev2                __x32_compat_sys_pwritev64v2
+512    x32     rt_sigaction            compat_sys_rt_sigaction
+513    x32     rt_sigreturn            compat_sys_x32_rt_sigreturn
+514    x32     ioctl                   compat_sys_ioctl
+515    x32     readv                   compat_sys_readv
+516    x32     writev                  compat_sys_writev
+517    x32     recvfrom                compat_sys_recvfrom
+518    x32     sendmsg                 compat_sys_sendmsg
+519    x32     recvmsg                 compat_sys_recvmsg
+520    x32     execve                  compat_sys_execve
+521    x32     ptrace                  compat_sys_ptrace
+522    x32     rt_sigpending           compat_sys_rt_sigpending
+523    x32     rt_sigtimedwait         compat_sys_rt_sigtimedwait_time64
+524    x32     rt_sigqueueinfo         compat_sys_rt_sigqueueinfo
+525    x32     sigaltstack             compat_sys_sigaltstack
+526    x32     timer_create            compat_sys_timer_create
+527    x32     mq_notify               compat_sys_mq_notify
+528    x32     kexec_load              compat_sys_kexec_load
+529    x32     waitid                  compat_sys_waitid
+530    x32     set_robust_list         compat_sys_set_robust_list
+531    x32     get_robust_list         compat_sys_get_robust_list
+532    x32     vmsplice                compat_sys_vmsplice
+533    x32     move_pages              compat_sys_move_pages
+534    x32     preadv                  compat_sys_preadv64
+535    x32     pwritev                 compat_sys_pwritev64
+536    x32     rt_tgsigqueueinfo       compat_sys_rt_tgsigqueueinfo
+537    x32     recvmmsg                compat_sys_recvmmsg_time64
+538    x32     sendmmsg                compat_sys_sendmmsg
+539    x32     process_vm_readv        compat_sys_process_vm_readv
+540    x32     process_vm_writev       compat_sys_process_vm_writev
+541    x32     setsockopt              compat_sys_setsockopt
+542    x32     getsockopt              compat_sys_getsockopt
+543    x32     io_setup                compat_sys_io_setup
+544    x32     io_submit               compat_sys_io_submit
+545    x32     execveat                compat_sys_execveat
+546    x32     preadv2                 compat_sys_preadv64v2
+547    x32     pwritev2                compat_sys_pwritev64v2
index bfb21d0..cf147db 100755 (executable)
@@ -22,7 +22,9 @@ include/uapi/linux/usbdevice_fs.h
 include/uapi/linux/vhost.h
 include/uapi/sound/asound.h
 include/linux/bits.h
+include/vdso/bits.h
 include/linux/const.h
+include/vdso/const.h
 include/linux/hash.h
 include/uapi/linux/hw_breakpoint.h
 arch/x86/include/asm/disabled-features.h
@@ -115,6 +117,7 @@ check arch/x86/lib/memcpy_64.S        '-I "^EXPORT_SYMBOL" -I "^#include <asm/ex
 check arch/x86/lib/memset_64.S        '-I "^EXPORT_SYMBOL" -I "^#include <asm/export.h>" -I"^SYM_FUNC_START\(_LOCAL\)*(memset_\(erms\|orig\))"'
 check include/uapi/asm-generic/mman.h '-I "^#include <\(uapi/\)*asm-generic/mman-common\(-tools\)*.h>"'
 check include/uapi/linux/mman.h       '-I "^#include <\(uapi/\)*asm/mman.h>"'
+check include/linux/build_bug.h       '-I "^#\(ifndef\|endif\)\( \/\/\)* static_assert$"'
 check include/linux/ctype.h          '-I "isdigit("'
 check lib/ctype.c                    '-I "^EXPORT_SYMBOL" -I "^#include <linux/export.h>" -B'
 check arch/x86/include/asm/inat.h     '-I "^#include [\"<]\(asm/\)*inat_types.h[\">]"'
index 062ca84..f4db894 100644 (file)
@@ -46,6 +46,7 @@ static size_t clone__scnprintf_flags(unsigned long flags, char *bf, size_t size,
        P_FLAG(NEWNET);
        P_FLAG(IO);
        P_FLAG(CLEAR_SIGHAND);
+       P_FLAG(INTO_CGROUP);
 #undef P_FLAG
 
        if (flags)
index 9fa771a..862c833 100644 (file)
@@ -69,6 +69,7 @@ static size_t syscall_arg__scnprintf_mremap_flags(char *bf, size_t size,
 
        P_MREMAP_FLAG(MAYMOVE);
        P_MREMAP_FLAG(FIXED);
+       P_MREMAP_FLAG(DONTUNMAP);
 #undef P_MREMAP_FLAG
 
        if (flags)
index 347b2c0..c5e3e9a 100644 (file)
@@ -21,6 +21,8 @@ if cc_is_clang:
             vars[var] = sub("-fstack-clash-protection", "", vars[var])
         if not clang_has_option("-fstack-protector-strong"):
             vars[var] = sub("-fstack-protector-strong", "", vars[var])
+        if not clang_has_option("-fno-semantic-interposition"):
+            vars[var] = sub("-fno-semantic-interposition", "", vars[var])
 
 from distutils.core import setup, Extension
 
index 0fd713d..03ecb8c 100644 (file)
@@ -803,8 +803,11 @@ static void generic_metric(struct perf_stat_config *config,
                                     out->force_header ?
                                     (metric_name ? metric_name : name) : "", 0);
                }
-       } else
-               print_metric(config, ctxp, NULL, NULL, "", 0);
+       } else {
+               print_metric(config, ctxp, NULL, NULL,
+                            out->force_header ?
+                            (metric_name ? metric_name : name) : "", 0);
+       }
 
        for (i = 1; i < pctx.num_ids; i++)
                zfree(&pctx.ids[i].name);
index 8455415..b531083 100644 (file)
@@ -41,6 +41,10 @@ uninstall :
        if [ -d $(DESTDIR)$(PREFIX)/lib/pm-graph/config ] ; then \
                rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph/config; \
        fi;
+       rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/__pycache__/*
+       if [ -d $(DESTDIR)$(PREFIX)/lib/pm-graph/__pycache__ ] ; then \
+               rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph/__pycache__; \
+       fi;
        rm -f $(DESTDIR)$(PREFIX)/lib/pm-graph/*
        if [ -d $(DESTDIR)$(PREFIX)/lib/pm-graph ] ; then \
                rmdir $(DESTDIR)$(PREFIX)/lib/pm-graph; \
index 96259f6..afe6beb 100644 (file)
@@ -1,7 +1,12 @@
-                     p m - g r a p h
+                                                _
+    _ __  _ __ ___         __ _ _ __ __ _ _ __ | |__
+   | '_ \| '_ ` _ \ _____ / _` | '__/ _` | '_ \| '_ \
+   | |_) | | | | | |_____| (_| | | | (_| | |_) | | | |
+   | .__/|_| |_| |_|      \__, |_|  \__,_| .__/|_| |_|
+   |_|                    |___/          |_|
 
    pm-graph: suspend/resume/boot timing analysis tools
-    Version: 5.5
+    Version: 5.6
      Author: Todd Brandt <todd.e.brandt@intel.com>
   Home Page: https://01.org/pm-graph
 
        - upstream version in git:
          https://github.com/intel/pm-graph/
 
- Requirements:
-       - runs with python2 or python3, choice is made by /usr/bin/python link
-       - python2 now requires python-configparser be installed
-
  Table of Contents
        - Overview
        - Setup
@@ -29,6 +30,8 @@
                - Basic Usage
                - Dev Mode Usage
                - Proc Mode Usage
+       - Endurance Testing
+               - Usage Examples
        - Configuration Files
                - Usage Examples
                - Config File Options
 |                            SETUP                               |
 ------------------------------------------------------------------
 
-    These packages are required to execute the scripts
+    Package Requirements
+       - runs with python2 or python3, choice is made by /usr/bin/python link
        - python
-       - python-requests
+       - python-configparser (for python2 sleepgraph)
+       - python-requests (for googlesheet.py)
+       - linux-tools-common (for turbostat usage in sleepgraph)
 
        Ubuntu:
-          sudo apt-get install python python-requests
+          sudo apt-get install python python-configparser python-requests linux-tools-common
 
        Fedora:
-          sudo dnf install python python-requests
+          sudo dnf install python python-configparser python-requests linux-tools-common
 
     The tools can most easily be installed via git clone and make install
 
@@ -190,6 +196,104 @@ _______________
 
  %> sudo ./sleepgraph.py -config config/suspend-proc.cfg
 
+------------------------------------------------------------------
+|                     ENDURANCE TESTING                          |
+------------------------------------------------------------------
+
+ The best way to gauge the health of a system is to run a series of
+ suspend/resumes over an extended period and analyze the behavior. This can be
+ accomplished with sleepgraph's -multi argument. You specify two numbers: the
+ number of tests to run OR the duration in days, hours, or minutes, and the
+ delay in seconds between them. For instance, -multi 20 5: execute 20 tests with
+ a 5 second delay between each, or -multi 24h 0: execute tests over a 24 hour
+ period with no delay between tests. You can include any other options you like
+ to generate the data you want. It's most useful to collect dev mode timelines
+ as the kprobes don't alter the performance much and you get more insight.
+
+ On completion, the output folder contains a series of folders for the
+ individual test data and a set of summary pages in the root. The summary.html
+ file is a tabular list of the tests with relevant info and links. The
+ summary-issue.html and summary-devices.html files include data taken from
+ all tests on kernel issues and device performance. The folder looks like this:
+
+  suspend-xN-{date}-{time}:
+       summary.html
+       summary-issues.html
+       summary-devices.html
+       suspend-{date}-{time} (1)
+       suspend-{date}-{time} (2)
+       ...
+
+ These are the relevant arguments to use for testing:
+
+  -m mode
+       Mode to initiate for suspend e.g. mem, freeze, standby (default: mem).
+
+  -rtcwake t
+       Use rtcwake to autoresume after t seconds (default: 15).
+
+  -gzip (optional)
+       Gzip the trace and dmesg logs to save space. The tool can also read in
+       gzipped logs for processing. This reduces the multitest folder size.
+
+  -dev (optional)
+       Add kernel source calls and threads to the timeline (default: disabled).
+
+  -multi n d
+       Execute n consecutive tests at d seconds intervals. The outputs will be
+       created in a new subdirectory: suspend-xN-{date}-{time}. When the multitest
+       run is done, the -summary command is called automatically to create summary
+       html files for all the data (unless you use -skiphtml). -skiphtml will
+       speed up the testing by not creating timelines or summary html files. You
+       can then run the tool again at a later time with -summary and -genhtml to
+       create the timelines.
+
+  -skiphtml (optional)
+       Run the test and capture the trace logs, but skip the timeline and summary
+       html generation. This can greatly speed up overall testing. You can then
+       copy the data to a faster host machine and run -summary -genhtml to
+       generate the timelines and summary.
+
+ These are the relevant commands to use after testing is complete:
+
+  -summary indir
+       Generate or regenerate the summary for a -multi test run. Creates three
+       files: summary.html, summary-issues.html, and summary-devices.html in the
+       current folder. summary.html is a table of tests with relevant info sorted
+       by kernel/host/mode, and links to the test html files. summary-issues.html
+       is a list of kernel issues found in dmesg from all the tests.
+       summary-devices.html is a list of devices and times from all the tests.
+
+  -genhtml
+       Used  with -summary to regenerate any missing html timelines from their
+       dmesg and ftrace logs. This will require a significant amount of time if
+       there are thousands of tests.
+
+Usage Examples
+_______________
+
+ A multitest is initiated like this:
+
+  %> sudo ./sleepgraph.py -m mem -rtcwake 10 -dev -gzip -multi 2000 0
+
+       or you can skip timeline generation in order to speed things up
+
+  %> sudo ./sleepgraph.py -m mem -rtcwake 10 -dev -gzip -multi 2000 0 -skiphtml
+
+ The tool will produce an output folder with all the test subfolders inside.
+ Each test subfolder contains the dmesg/ftrace logs and/or the html timeline
+ depending on whether you used the -skiphtml option. The root folder contains
+ the summary.html files.
+
+ The summary for an existing multitest is generated like this:
+
+  %> cd suspend-x2000-{date}-{time}
+  %> sleepgraph.py -summary .
+
+       or if you need to generate the html timelines you can use -genhtml
+
+  %> cd suspend-xN-{date}-{time}
+  %> sleepgraph.py -summary . -genhtml
 
 ------------------------------------------------------------------
 |                    CONFIGURATION FILES                         |
index d3b99a1..2823cd3 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # SPDX-License-Identifier: GPL-2.0-only
 #
 # Tool for analyzing boot timing
index 43aee64..5126271 100644 (file)
@@ -74,8 +74,10 @@ after the test is complete.
 Switch the display to the requested mode for the test using the xset command.
 This helps maintain the consistency of test data for better comparison.
 .TP
-\fB-skiphtml\fR
-Run the test and capture the trace logs, but skip the timeline generation.
+\fB-wifi\fR
+If a wifi connection is available, check that it reconnects after resume. Include
+the reconnect time in the total resume time calculation and treat wifi timeouts
+as resume failures.
 
 .SS "advanced"
 .TP
@@ -117,8 +119,24 @@ Include \fIt\fR ms delay before 1st suspend (default: 0 ms).
 Include \fIt\fR ms delay after last resume (default: 0 ms).
 .TP
 \fB-multi \fIn d\fR
-Execute \fIn\fR consecutive tests at \fId\fR seconds intervals. The outputs will
-be created in a new subdirectory with a summary page: suspend-xN-{date}-{time}.
+Used for endurance testing. If \fIn\fR is entirely numeric, it's treated as a count:
+Execute \fIn\fR consecutive tests at \fId\fR second intervals.
+If \fIn\fR is an integer followed by a "d", "h", or "m", it's treated as a duration:
+Execute tests continuously over \fIn\fR days, hours, or minutes at \fId\fR second intervals.
+The outputs will be created in a new subdirectory, for count: suspend-{date}-{time}-xN,
+for duration: suspend-{date}-{time}-Nm. When the multitest run is done, the \fI-summary\fR
+command is called automatically to create summary html files for all the data (unless you
+use \fI-skiphtml\fR). \fI-skiphtml\fR will speed up the testing by not creating timelines
+or summary html files. You can then run the tool again at a later time with \fI-summary\fR
+and \fI-genhtml\fR to create the timelines.
+.TP
+\fB-maxfail \fIn\fR
+Abort a -multi run after \fIn\fR consecutive fails. 0 means never abort (default = 0).
+.TP
+\fB-skiphtml\fR
+Run the test and capture the trace logs, but skip the timeline generation.
+You can generate the html timelines later with \fI-dmesg\fR & \fI-ftrace\fR, or
+by running \fI-summary\fR and \fI-genhtml\fR.
 
 .SS "ftrace debug"
 .TP
@@ -173,11 +191,20 @@ Set trace buffer size to N kilo-bytes (default: all of free memory up to 3GB)
 .SH COMMANDS
 .TP
 \fB-summary \fIindir\fR
-Create a summary page of all tests in \fIindir\fR. Creates summary.html
-in the current folder. The output page is a table of tests with
-suspend and resume values sorted by suspend mode, host, and kernel.
-Includes test averages by mode and links to the test html files.
-Use -genhtml to include tests with missing html.
+Create a set of summary pages for all tests in \fIindir\fR recursively.
+Creates summary.html, summary-issues.html, and summary-devices.html in the current folder.
+summary.html is a table of tests with relevant info sorted by kernel/host/mode,
+and links to the test html files. It identifies the minimum, maximum, and median
+suspend and resume times for you with highlights and links in the header.
+summary-issues.html is a list of kernel issues found in dmesg from all the tests.
+summary-devices.html is a list of devices and times from all the tests.
+
+Use \fI-genhtml\fR to regenerate any tests with missing html.
+.TP
+\fB-genhtml\fR
+Used with \fI-summary\fR to regenerate any missing html timelines from their
+dmesg and ftrace logs. This will require a significant amount of time if there
+are thousands of tests.
 .TP
 \fB-modes\fR
 List available suspend modes.
@@ -189,10 +216,7 @@ with any options you intend to use to see if they will work.
 \fB-fpdt\fR
 Print out the contents of the ACPI Firmware Performance Data Table.
 .TP
-\fB-battery\fR
-Print out battery status and current charge.
-.TP
-\fB-wifi\fR
+\fB-wificheck\fR
 Print out wifi status and connection details.
 .TP
 \fB-xon/-xoff/-xstandby/-xsuspend\fR
@@ -208,6 +232,9 @@ Print out system info extracted from BIOS. Reads /dev/mem directly instead of go
 \fB-devinfo\fR
 Print out the pm settings of all devices which support runtime suspend.
 .TP
+\fB-cmdinfo\fR
+Print out all the platform data collected from the system that makes it into the logs.
+.TP
 \fB-flist\fR
 Print the list of ftrace functions currently being captured. Functions
 that are not available as symbols in the current kernel are shown in red.
@@ -272,14 +299,20 @@ Run two suspends back to back, include a 500ms delay before, after, and in betwe
 .IP
 \f(CW$ sudo sleepgraph -m mem -rtcwake 15 -x2 -predelay 500 -x2delay 500 -postdelay 500\fR
 .PP
+Execute a suspend using a custom command.
+.IP
+\f(CW$ sudo sleepgraph -cmd "echo mem > /sys/power/state" -rtcwake 15\fR
+.PP
+
+.SS "endurance testing using -multi"
+.PP
 Do a batch run of 10 freezes with 30 seconds delay between runs.
 .IP
 \f(CW$ sudo sleepgraph -m freeze -rtcwake 15 -multi 10 30\fR
 .PP
-Execute a suspend using a custom command.
+Do a batch run of freezes for 24 hours.
 .IP
-\f(CW$ sudo sleepgraph -cmd "echo mem > /sys/power/state" -rtcwake 15\fR
-.PP
+\f(CW$ sudo sleepgraph -m freeze -rtcwake 15 -multi 24h 0\fR
 
 .SS "adding callgraph data"
 Add device callgraphs. Limit the trace depth and only show callgraphs 10ms or larger.
index f7d1c1f..9b0404d 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
 # SPDX-License-Identifier: GPL-2.0-only
 #
 # Tool for analyzing suspend/resume timing
@@ -58,7 +58,7 @@ import re
 import platform
 import signal
 import codecs
-from datetime import datetime
+from datetime import datetime, timedelta
 import struct
 import configparser
 import gzip
@@ -81,12 +81,13 @@ def ascii(text):
 #       store system values and test parameters
 class SystemValues:
        title = 'SleepGraph'
-       version = '5.5'
+       version = '5.6'
        ansi = False
        rs = 0
        display = ''
        gzip = False
        sync = False
+       wifi = False
        verbose = False
        testlog = True
        dmesglog = True
@@ -97,7 +98,8 @@ class SystemValues:
        cgphase = ''
        cgtest = -1
        cgskip = ''
-       multitest = {'run': False, 'count': 0, 'delay': 0}
+       maxfail = 0
+       multitest = {'run': False, 'count': 1000000, 'delay': 0}
        max_graph_depth = 0
        callloopmaxgap = 0.0001
        callloopmaxlen = 0.005
@@ -148,7 +150,7 @@ class SystemValues:
        x2delay = 0
        skiphtml = False
        usecallgraph = False
-       ftopfunc = 'suspend_devices_and_enter'
+       ftopfunc = 'pm_suspend'
        ftop = False
        usetraceevents = False
        usetracemarkers = True
@@ -164,6 +166,8 @@ class SystemValues:
        predelay = 0
        postdelay = 0
        pmdebug = ''
+       tmstart = 'SUSPEND START %Y%m%d-%H:%M:%S.%f'
+       tmend = 'RESUME COMPLETE %Y%m%d-%H:%M:%S.%f'
        tracefuncs = {
                'sys_sync': {},
                'ksys_sync': {},
@@ -183,9 +187,11 @@ class SystemValues:
                'acpi_s2idle_sync': {},
                'acpi_s2idle_begin': {},
                'acpi_s2idle_prepare': {},
+               'acpi_s2idle_prepare_late': {},
                'acpi_s2idle_wake': {},
                'acpi_s2idle_wakeup': {},
                'acpi_s2idle_restore': {},
+               'acpi_s2idle_restore_early': {},
                'hibernate_preallocate_memory': {},
                'create_basic_memory_bitmaps': {},
                'swsusp_write': {},
@@ -270,12 +276,23 @@ class SystemValues:
                'intel_opregion_init': {},
                'intel_fbdev_set_suspend': {},
        }
+       infocmds = [
+               [0, 'kparams', 'cat', '/proc/cmdline'],
+               [0, 'mcelog', 'mcelog'],
+               [0, 'pcidevices', 'lspci', '-tv'],
+               [0, 'usbdevices', 'lsusb', '-t'],
+               [1, 'interrupts', 'cat', '/proc/interrupts'],
+               [1, 'wakeups', 'cat', '/sys/kernel/debug/wakeup_sources'],
+               [2, 'gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/*'],
+               [2, 'suspendstats', 'sh', '-c', 'grep -v invalid /sys/power/suspend_stats/*'],
+               [2, 'cpuidle', 'sh', '-c', 'grep -v invalid /sys/devices/system/cpu/cpu*/cpuidle/state*/s2idle/*'],
+               [2, 'battery', 'sh', '-c', 'grep -v invalid /sys/class/power_supply/*/*'],
+       ]
        cgblacklist = []
        kprobes = dict()
        timeformat = '%.3f'
        cmdline = '%s %s' % \
                        (os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:]))
-       kparams = ''
        sudouser = ''
        def __init__(self):
                self.archargs = 'args_'+platform.machine()
@@ -295,6 +312,9 @@ class SystemValues:
                if os.getuid() == 0 and 'SUDO_USER' in os.environ and \
                        os.environ['SUDO_USER']:
                        self.sudouser = os.environ['SUDO_USER']
+       def resetlog(self):
+               self.logmsg = ''
+               self.platinfo = []
        def vprint(self, msg):
                self.logmsg += msg+'\n'
                if self.verbose or msg.startswith('WARNING:'):
@@ -304,11 +324,11 @@ class SystemValues:
                        return
                signame = self.signames[signum] if signum in self.signames else 'UNKNOWN'
                msg = 'Signal %s caused a tool exit, line %d' % (signame, frame.f_lineno)
-               sysvals.outputResult({'error':msg})
+               self.outputResult({'error':msg})
                sys.exit(3)
        def signalHandlerInit(self):
                capture = ['BUS', 'SYS', 'XCPU', 'XFSZ', 'PWR', 'HUP', 'INT', 'QUIT',
-                       'ILL', 'ABRT', 'FPE', 'SEGV', 'TERM', 'TSTP']
+                       'ILL', 'ABRT', 'FPE', 'SEGV', 'TERM']
                self.signames = dict()
                for i in capture:
                        s = 'SIG'+i
@@ -336,6 +356,8 @@ class SystemValues:
                        self.outputResult({'error':msg})
                        sys.exit(1)
                return False
+       def usable(self, file):
+               return (os.path.exists(file) and os.path.getsize(file) > 0)
        def getExec(self, cmd):
                try:
                        fp = Popen(['which', cmd], stdout=PIPE, stderr=PIPE).stdout
@@ -389,12 +411,6 @@ class SystemValues:
                r = info['bios-release-date'] if 'bios-release-date' in info else ''
                self.sysstamp = '# sysinfo | man:%s | plat:%s | cpu:%s | bios:%s | biosdate:%s | numcpu:%d | memsz:%d | memfr:%d' % \
                        (m, p, c, b, r, self.cpucount, self.memtotal, self.memfree)
-               try:
-                       kcmd = open('/proc/cmdline', 'r').read().strip()
-               except:
-                       kcmd = ''
-               if kcmd:
-                       self.sysstamp += '\n# kparams | %s' % kcmd
        def printSystemInfo(self, fatal=False):
                self.rootCheck(True)
                out = dmidecode(self.mempath, fatal)
@@ -441,6 +457,7 @@ class SystemValues:
                        self.testdir+'/'+self.prefix+'_'+self.suspendmode+'.html'
                if not os.path.isdir(self.testdir):
                        os.makedirs(self.testdir)
+               self.sudoUserchown(self.testdir)
        def getValueList(self, value):
                out = []
                for i in value.split(','):
@@ -486,7 +503,7 @@ class SystemValues:
                fp.close()
                self.dmesgstart = float(ktime)
        def getdmesg(self, testdata):
-               op = self.writeDatafileHeader(sysvals.dmesgfile, testdata)
+               op = self.writeDatafileHeader(self.dmesgfile, testdata)
                # store all new dmesg lines since initdmesg was called
                fp = Popen('dmesg', stdout=PIPE).stdout
                for line in fp:
@@ -716,9 +733,10 @@ class SystemValues:
                        if name == f:
                                return True
                return False
-       def initFtrace(self):
-               self.printSystemInfo(False)
-               pprint('INITIALIZING FTRACE...')
+       def initFtrace(self, quiet=False):
+               if not quiet:
+                       sysvals.printSystemInfo(False)
+                       pprint('INITIALIZING FTRACE...')
                # turn trace off
                self.fsetVal('0', 'tracing_on')
                self.cleanupFtrace()
@@ -746,7 +764,7 @@ class SystemValues:
                        if tgtsize < 65536:
                                tgtsize = int(self.fgetVal('buffer_size_kb')) * cpus
                                break
-               pprint('Setting trace buffers to %d kB (%d kB per cpu)' % (tgtsize, tgtsize/cpus))
+               self.vprint('Setting trace buffers to %d kB (%d kB per cpu)' % (tgtsize, tgtsize/cpus))
                # initialize the callgraph trace
                if(self.usecallgraph):
                        # set trace type
@@ -782,7 +800,8 @@ class SystemValues:
                        if self.usedevsrc:
                                for name in self.dev_tracefuncs:
                                        self.defaultKprobe(name, self.dev_tracefuncs[name])
-                       pprint('INITIALIZING KPROBES...')
+                       if not quiet:
+                               pprint('INITIALIZING KPROBES...')
                        self.addKprobes(self.verbose)
                if(self.usetraceevents):
                        # turn trace events on
@@ -827,21 +846,10 @@ class SystemValues:
                                fw = test['fw']
                                if(fw):
                                        fp.write('# fwsuspend %u fwresume %u\n' % (fw[0], fw[1]))
-                       if 'mcelog' in test:
-                               fp.write('# mcelog %s\n' % test['mcelog'])
                        if 'turbo' in test:
                                fp.write('# turbostat %s\n' % test['turbo'])
-                       if 'bat' in test:
-                               (a1, c1), (a2, c2) = test['bat']
-                               fp.write('# battery %s %d %s %d\n' % (a1, c1, a2, c2))
                        if 'wifi' in test:
-                               wstr = []
-                               for wifi in test['wifi']:
-                                       tmp = []
-                                       for key in sorted(wifi):
-                                               tmp.append('%s:%s' % (key, wifi[key]))
-                                       wstr.append('|'.join(tmp))
-                               fp.write('# wifi %s\n' % (','.join(wstr)))
+                               fp.write('# wifi %s\n' % test['wifi'])
                        if test['error'] or len(testdata) > 1:
                                fp.write('# enter_sleep_error %s\n' % test['error'])
                return fp
@@ -901,23 +909,7 @@ class SystemValues:
        def b64zip(self, data):
                out = base64.b64encode(codecs.encode(data.encode(), 'zlib')).decode()
                return out
-       def mcelog(self, clear=False):
-               cmd = self.getExec('mcelog')
-               if not cmd:
-                       return ''
-               if clear:
-                       call(cmd+' > /dev/null 2>&1', shell=True)
-                       return ''
-               try:
-                       fp = Popen([cmd], stdout=PIPE, stderr=PIPE).stdout
-                       out = ascii(fp.read()).strip()
-                       fp.close()
-               except:
-                       return ''
-               if not out:
-                       return ''
-               return self.b64zip(out)
-       def platforminfo(self):
+       def platforminfo(self, cmdafter):
                # add platform info on to a completed ftrace file
                if not os.path.exists(self.ftracefile):
                        return False
@@ -1001,31 +993,76 @@ class SystemValues:
                footer += '# platform-devinfo: %s\n' % self.b64zip(out)
 
                # add a line for each of these commands with their outputs
-               cmds = [
-                       ['pcidevices', 'lspci', '-tv'],
-                       ['interrupts', 'cat', '/proc/interrupts'],
-                       ['gpecounts', 'sh', '-c', 'grep -v invalid /sys/firmware/acpi/interrupts/gpe*'],
-               ]
-               for cargs in cmds:
-                       name = cargs[0]
-                       cmdline = ' '.join(cargs[1:])
-                       cmdpath = self.getExec(cargs[1])
-                       if not cmdpath:
+               for name, cmdline, info in cmdafter:
+                       footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
+
+               with self.openlog(self.ftracefile, 'a') as fp:
+                       fp.write(footer)
+               return True
+       def commonPrefix(self, list):
+               if len(list) < 2:
+                       return ''
+               prefix = list[0]
+               for s in list[1:]:
+                       while s[:len(prefix)] != prefix and prefix:
+                               prefix = prefix[:len(prefix)-1]
+                       if not prefix:
+                               break
+               if '/' in prefix and prefix[-1] != '/':
+                       prefix = prefix[0:prefix.rfind('/')+1]
+               return prefix
+       def dictify(self, text, format):
+               out = dict()
+               header = True if format == 1 else False
+               delim = ' ' if format == 1 else ':'
+               for line in text.split('\n'):
+                       if header:
+                               header, out['@'] = False, line
+                               continue
+                       line = line.strip()
+                       if delim in line:
+                               data = line.split(delim, 1)
+                               num = re.search(r'[\d]+', data[1])
+                               if format == 2 and num:
+                                       out[data[0].strip()] = num.group()
+                               else:
+                                       out[data[0].strip()] = data[1]
+               return out
+       def cmdinfo(self, begin, debug=False):
+               out = []
+               if begin:
+                       self.cmd1 = dict()
+               for cargs in self.infocmds:
+                       delta, name = cargs[0], cargs[1]
+                       cmdline, cmdpath = ' '.join(cargs[2:]), self.getExec(cargs[2])
+                       if not cmdpath or (begin and not delta):
                                continue
-                       cmd = [cmdpath] + cargs[2:]
                        try:
-                               fp = Popen(cmd, stdout=PIPE, stderr=PIPE).stdout
+                               fp = Popen([cmdpath]+cargs[3:], stdout=PIPE, stderr=PIPE).stdout
                                info = ascii(fp.read()).strip()
                                fp.close()
                        except:
                                continue
-                       if not info:
-                               continue
-                       footer += '# platform-%s: %s | %s\n' % (name, cmdline, self.b64zip(info))
-
-               with self.openlog(self.ftracefile, 'a') as fp:
-                       fp.write(footer)
-               return True
+                       if not debug and begin:
+                               self.cmd1[name] = self.dictify(info, delta)
+                       elif not debug and delta and name in self.cmd1:
+                               before, after = self.cmd1[name], self.dictify(info, delta)
+                               dinfo = ('\t%s\n' % before['@']) if '@' in before else ''
+                               prefix = self.commonPrefix(list(before.keys()))
+                               for key in sorted(before):
+                                       if key in after and before[key] != after[key]:
+                                               title = key.replace(prefix, '')
+                                               if delta == 2:
+                                                       dinfo += '\t%s : %s -> %s\n' % \
+                                                               (title, before[key].strip(), after[key].strip())
+                                               else:
+                                                       dinfo += '%10s (start) : %s\n%10s (after) : %s\n' % \
+                                                               (title, before[key], title, after[key])
+                               dinfo = '\tnothing changed' if not dinfo else dinfo.rstrip()
+                               out.append((name, cmdline, dinfo))
+                       else:
+                               out.append((name, cmdline, '\tnothing' if not info else info))
+               return out
        def haveTurbostat(self):
                if not self.tstat:
                        return False
@@ -1035,8 +1072,8 @@ class SystemValues:
                fp = Popen([cmd, '-v'], stdout=PIPE, stderr=PIPE).stderr
                out = ascii(fp.read()).strip()
                fp.close()
-               if re.match('turbostat version [0-9\.]* .*', out):
-                       sysvals.vprint(out)
+               if re.match('turbostat version .*', out):
+                       self.vprint(out)
                        return True
                return False
        def turbostat(self):
@@ -1056,11 +1093,11 @@ class SystemValues:
                fp.close()
                if not keyline or not valline or len(keyline) != len(valline):
                        errmsg = 'unrecognized turbostat output:\n'+rawout.strip()
-                       sysvals.vprint(errmsg)
-                       if not sysvals.verbose:
+                       self.vprint(errmsg)
+                       if not self.verbose:
                                pprint(errmsg)
                        return ''
-               if sysvals.verbose:
+               if self.verbose:
                        pprint(rawout.strip())
                out = []
                for key in keyline:
@@ -1068,30 +1105,36 @@ class SystemValues:
                        val = valline[idx]
                        out.append('%s=%s' % (key, val))
                return '|'.join(out)
-       def checkWifi(self):
-               out = dict()
-               iwcmd, ifcmd = self.getExec('iwconfig'), self.getExec('ifconfig')
-               if not iwcmd or not ifcmd:
-                       return out
-               fp = Popen(iwcmd, stdout=PIPE, stderr=PIPE).stdout
-               for line in fp:
-                       m = re.match('(?P<dev>\S*) .* ESSID:(?P<ess>\S*)', ascii(line))
-                       if not m:
+       def wifiDetails(self, dev):
+               try:
+                       info = open('/sys/class/net/%s/device/uevent' % dev, 'r').read().strip()
+               except:
+                       return dev
+               vals = [dev]
+               for prop in info.split('\n'):
+                       if prop.startswith('DRIVER=') or prop.startswith('PCI_ID='):
+                               vals.append(prop.split('=')[-1])
+               return ':'.join(vals)
+       def checkWifi(self, dev=''):
+               try:
+                       w = open('/proc/net/wireless', 'r').read().strip()
+               except:
+                       return ''
+               for line in reversed(w.split('\n')):
+                       m = re.match(' *(?P<dev>.*): (?P<stat>[0-9a-f]*) .*', w.split('\n')[-1])
+                       if not m or (dev and dev != m.group('dev')):
                                continue
-                       out['device'] = m.group('dev')
-                       if '"' in m.group('ess'):
-                               out['essid'] = m.group('ess').strip('"')
-                               break
-               fp.close()
-               if 'device' in out:
-                       fp = Popen([ifcmd, out['device']], stdout=PIPE, stderr=PIPE).stdout
-                       for line in fp:
-                               m = re.match('.* inet (?P<ip>[0-9\.]*)', ascii(line))
-                               if m:
-                                       out['ip'] = m.group('ip')
-                                       break
-                       fp.close()
-               return out
+                       return m.group('dev')
+               return ''
+       def pollWifi(self, dev, timeout=60):
+               start = time.time()
+               while (time.time() - start) < timeout:
+                       w = self.checkWifi(dev)
+                       if w:
+                               return '%s reconnected %.2f' % \
+                                       (self.wifiDetails(dev), max(0, time.time() - start))
+                       time.sleep(0.01)
+               return '%s timeout %d' % (self.wifiDetails(dev), timeout)
        def errorSummary(self, errinfo, msg):
                found = False
                for entry in errinfo:
@@ -1113,8 +1156,9 @@ class SystemValues:
                                arr[j] = arr[j]\
                                        .replace('\\', '\\\\').replace(']', '\]').replace('[', '\[')\
                                        .replace('.', '\.').replace('+', '\+').replace('*', '\*')\
-                                       .replace('(', '\(').replace(')', '\)')
-               mstr = ' '.join(arr)
+                                       .replace('(', '\(').replace(')', '\)').replace('}', '\}')\
+                                       .replace('{', '\{')
+               mstr = ' *'.join(arr)
                entry = {
                        'line': msg,
                        'match': mstr,
@@ -1122,6 +1166,44 @@ class SystemValues:
                        'urls': {self.hostname: [self.htmlfile]}
                }
                errinfo.append(entry)
+       def multistat(self, start, idx, finish):
+               if 'time' in self.multitest:
+                       id = '%d Duration=%dmin' % (idx+1, self.multitest['time'])
+               else:
+                       id = '%d/%d' % (idx+1, self.multitest['count'])
+               t = time.time()
+               if 'start' not in self.multitest:
+                       self.multitest['start'] = self.multitest['last'] = t
+                       self.multitest['total'] = 0.0
+                       pprint('TEST (%s) START' % id)
+                       return
+               dt = t - self.multitest['last']
+               if not start:
+                       if idx == 0 and self.multitest['delay'] > 0:
+                               self.multitest['total'] += self.multitest['delay']
+                       pprint('TEST (%s) COMPLETE -- Duration %.1fs' % (id, dt))
+                       return
+               self.multitest['total'] += dt
+               self.multitest['last'] = t
+               avg = self.multitest['total'] / idx
+               if 'time' in self.multitest:
+                       left = finish - datetime.now()
+                       left -= timedelta(microseconds=left.microseconds)
+               else:
+                       left = timedelta(seconds=((self.multitest['count'] - idx) * int(avg)))
+               pprint('TEST (%s) START - Avg Duration %.1fs, Time left %s' % \
+                       (id, avg, str(left)))
+       def multiinit(self, c, d):
+               sz, unit = 'count', 'm'
+               if c.endswith('d') or c.endswith('h') or c.endswith('m'):
+                       sz, unit, c = 'time', c[-1], c[:-1]
+               self.multitest['run'] = True
+               self.multitest[sz] = getArgInt('multi: n d (exec count)', c, 1, 1000000, False)
+               self.multitest['delay'] = getArgInt('multi: n d (delay between tests)', d, 0, 3600, False)
+               if unit == 'd':
+                       self.multitest[sz] *= 1440
+               elif unit == 'h':
+                       self.multitest[sz] *= 60
 
 sysvals = SystemValues()
 switchvalues = ['enable', 'disable', 'on', 'off', 'true', 'false', '1', '0']
@@ -1210,25 +1292,30 @@ class Data:
                'resume_complete': {'order': 9, 'color': '#FFFFCC'},
        }
        errlist = {
-               'HWERROR' : '.*\[ *Hardware Error *\].*',
-               'FWBUG'   : '.*\[ *Firmware Bug *\].*',
-               'BUG'     : '.*BUG.*',
-               'ERROR'   : '.*ERROR.*',
-               'WARNING' : '.*WARNING.*',
-               'IRQ'     : '.*genirq: .*',
-               'TASKFAIL': '.*Freezing of tasks *.*',
-               'ACPI'    : '.*ACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
-               'DEVFAIL' : '.* failed to (?P<b>[a-z]*) async: .*',
-               'DISKFULL': '.*No space left on device.*',
-               'USBERR'  : '.*usb .*device .*, error [0-9-]*',
-               'ATAERR'  : ' *ata[0-9\.]*: .*failed.*',
-               'MEIERR'  : ' *mei.*: .*failed.*',
-               'TPMERR'  : '(?i) *tpm *tpm[0-9]*: .*error.*',
+               'HWERROR' : r'.*\[ *Hardware Error *\].*',
+               'FWBUG'   : r'.*\[ *Firmware Bug *\].*',
+               'BUG'     : r'(?i).*\bBUG\b.*',
+               'ERROR'   : r'(?i).*\bERROR\b.*',
+               'WARNING' : r'(?i).*\bWARNING\b.*',
+               'FAULT'   : r'(?i).*\bFAULT\b.*',
+               'FAIL'    : r'(?i).*\bFAILED\b.*',
+               'INVALID' : r'(?i).*\bINVALID\b.*',
+               'CRASH'   : r'(?i).*\bCRASHED\b.*',
+               'IRQ'     : r'.*\bgenirq: .*',
+               'TASKFAIL': r'.*Freezing of tasks *.*',
+               'ACPI'    : r'.*\bACPI *(?P<b>[A-Za-z]*) *Error[: ].*',
+               'DISKFULL': r'.*\bNo space left on device.*',
+               'USBERR'  : r'.*usb .*device .*, error [0-9-]*',
+               'ATAERR'  : r' *ata[0-9\.]*: .*failed.*',
+               'MEIERR'  : r' *mei.*: .*failed.*',
+               'TPMERR'  : r'(?i) *tpm *tpm[0-9]*: .*error.*',
        }
        def __init__(self, num):
                idchar = 'abcdefghij'
                self.start = 0.0 # test start
                self.end = 0.0   # test end
+               self.hwstart = 0 # rtc test start
+               self.hwend = 0   # rtc test end
                self.tSuspended = 0.0 # low-level suspend start
                self.tResumed = 0.0   # low-level resume start
                self.tKernSus = 0.0   # kernel level suspend start
@@ -1240,10 +1327,8 @@ class Data:
                self.stamp = 0
                self.outfile = ''
                self.kerror = False
-               self.battery = 0
-               self.wifi = 0
+               self.wifi = dict()
                self.turbostat = 0
-               self.mcelog = 0
                self.enterfail = ''
                self.currphase = ''
                self.pstl = dict()    # process timeline
@@ -1308,6 +1393,8 @@ class Data:
                                continue
                        dir = 'suspend' if t < self.tSuspended else 'resume'
                        msg = m.group('msg')
+                       if re.match('capability: warning: .*', msg):
+                               continue
                        for err in self.errlist:
                                if re.match(self.errlist[err], msg):
                                        list.append((msg, err, dir, t, i, i))
@@ -1316,17 +1403,26 @@ class Data:
                msglist = []
                for msg, type, dir, t, idx1, idx2 in list:
                        msglist.append(msg)
-                       sysvals.vprint('kernel %s found in %s at %f' % (type, dir, t))
                        self.errorinfo[dir].append((type, t, idx1, idx2))
                if self.kerror:
                        sysvals.dmesglog = True
                if len(self.dmesgtext) < 1 and sysvals.dmesgfile:
                        lf.close()
                return msglist
-       def setStart(self, time):
+       def setStart(self, time, msg=''):
                self.start = time
-       def setEnd(self, time):
+               if msg:
+                       try:
+                               self.hwstart = datetime.strptime(msg, sysvals.tmstart)
+                       except:
+                               self.hwstart = 0
+       def setEnd(self, time, msg=''):
                self.end = time
+               if msg:
+                       try:
+                               self.hwend = datetime.strptime(msg, sysvals.tmend)
+                       except:
+                               self.hwend = 0
        def isTraceEventOutsideDeviceCalls(self, pid, time):
                for phase in self.sortedPhases():
                        list = self.dmesg[phase]['list']
@@ -1546,6 +1642,14 @@ class Data:
                                        self.trimTime(tS, tL, left)
                                        self.tLow.append('%.0f'%(tL*1000))
                        lp = phase
+       def getMemTime(self):
+               if not self.hwstart or not self.hwend:
+                       return
+               stime = (self.tSuspended - self.start) * 1000000
+               rtime = (self.end - self.tResumed) * 1000000
+               hws = self.hwstart + timedelta(microseconds=stime)
+               hwr = self.hwend - timedelta(microseconds=rtime)
+               self.tLow.append('%.0f'%((hwr - hws).total_seconds() * 1000))
        def getTimeValues(self):
                sktime = (self.tSuspended - self.tKernSus) * 1000
                rktime = (self.tKernRes - self.tResumed) * 1000
@@ -1883,9 +1987,9 @@ class Data:
                        c = self.addProcessUsageEvent(ps, tres)
                        if c > 0:
                                sysvals.vprint('%25s (res): %d' % (ps, c))
-       def handleEndMarker(self, time):
+       def handleEndMarker(self, time, msg=''):
                dm = self.dmesg
-               self.setEnd(time)
+               self.setEnd(time, msg)
                self.initDevicegroups()
                # give suspend_prepare an end if needed
                if 'suspend_prepare' in dm and dm['suspend_prepare']['end'] < 0:
@@ -2071,7 +2175,7 @@ class FTraceLine:
                if not self.fevent:
                        return False
                if sysvals.usetracemarkers:
-                       if(self.name == 'SUSPEND START'):
+                       if(self.name.startswith('SUSPEND START')):
                                return True
                        return False
                else:
@@ -2084,7 +2188,7 @@ class FTraceLine:
                if not self.fevent:
                        return False
                if sysvals.usetracemarkers:
-                       if(self.name == 'RESUME COMPLETE'):
+                       if(self.name.startswith('RESUME COMPLETE')):
                                return True
                        return False
                else:
@@ -2444,7 +2548,7 @@ class Timeline:
        def createHeader(self, sv, stamp):
                if(not stamp['time']):
                        return
-               self.html += '<div class="version"><a href="https://01.org/suspendresume">%s v%s</a></div>' \
+               self.html += '<div class="version"><a href="https://01.org/pm-graph">%s v%s</a></div>' \
                        % (sv.title, sv.version)
                if sv.logmsg and sv.testlog:
                        self.html += '<button id="showtest" class="logbtn btnfmt">log</button>'
@@ -2670,14 +2774,11 @@ class TestProps:
        stampfmt = '# [a-z]*-(?P<m>[0-9]{2})(?P<d>[0-9]{2})(?P<y>[0-9]{2})-'+\
                                '(?P<H>[0-9]{2})(?P<M>[0-9]{2})(?P<S>[0-9]{2})'+\
                                ' (?P<host>.*) (?P<mode>.*) (?P<kernel>.*)$'
-       batteryfmt = '^# battery (?P<a1>\w*) (?P<c1>\d*) (?P<a2>\w*) (?P<c2>\d*)'
-       wififmt    = '^# wifi (?P<w>.*)'
+       wififmt    = '^# wifi *(?P<d>\S*) *(?P<s>\S*) *(?P<t>[0-9\.]+).*'
        tstatfmt   = '^# turbostat (?P<t>\S*)'
-       mcelogfmt  = '^# mcelog (?P<m>\S*)'
        testerrfmt = '^# enter_sleep_error (?P<e>.*)'
        sysinfofmt = '^# sysinfo .*'
        cmdlinefmt = '^# command \| (?P<cmd>.*)'
-       kparamsfmt = '^# kparams \| (?P<kp>.*)'
        devpropfmt = '# Device Properties: .*'
        pinfofmt   = '# platform-(?P<val>[a-z,A-Z,0-9]*): (?P<info>.*)'
        tracertypefmt = '# tracer: (?P<t>.*)'
@@ -2695,11 +2796,8 @@ class TestProps:
                self.stamp = ''
                self.sysinfo = ''
                self.cmdline = ''
-               self.kparams = ''
                self.testerror = []
-               self.mcelog = []
                self.turbostat = []
-               self.battery = []
                self.wifi = []
                self.fwdata = []
                self.ftrace_line_fmt = self.ftrace_line_fmt_nop
@@ -2721,21 +2819,12 @@ class TestProps:
                elif re.match(self.sysinfofmt, line):
                        self.sysinfo = line
                        return True
-               elif re.match(self.kparamsfmt, line):
-                       self.kparams = line
-                       return True
                elif re.match(self.cmdlinefmt, line):
                        self.cmdline = line
                        return True
-               elif re.match(self.mcelogfmt, line):
-                       self.mcelog.append(line)
-                       return True
                elif re.match(self.tstatfmt, line):
                        self.turbostat.append(line)
                        return True
-               elif re.match(self.batteryfmt, line):
-                       self.battery.append(line)
-                       return True
                elif re.match(self.wififmt, line):
                        self.wifi.append(line)
                        return True
@@ -2749,6 +2838,8 @@ class TestProps:
        def parseStamp(self, data, sv):
                # global test data
                m = re.match(self.stampfmt, self.stamp)
+               if not self.stamp or not m:
+                       doError('data does not include the expected stamp')
                data.stamp = {'time': '', 'host': '', 'mode': ''}
                dt = datetime(int(m.group('y'))+2000, int(m.group('m')),
                        int(m.group('d')), int(m.group('H')), int(m.group('M')),
@@ -2780,10 +2871,6 @@ class TestProps:
                m = re.match(self.cmdlinefmt, self.cmdline)
                if m:
                        sv.cmdline = m.group('cmd')
-               if self.kparams:
-                       m = re.match(self.kparamsfmt, self.kparams)
-                       if m:
-                               sv.kparams = m.group('kp')
                if not sv.stamp:
                        sv.stamp = data.stamp
                # firmware data
@@ -2793,26 +2880,18 @@ class TestProps:
                                data.fwSuspend, data.fwResume = int(m.group('s')), int(m.group('r'))
                                if(data.fwSuspend > 0 or data.fwResume > 0):
                                        data.fwValid = True
-               # mcelog data
-               if len(self.mcelog) > data.testnumber:
-                       m = re.match(self.mcelogfmt, self.mcelog[data.testnumber])
-                       if m:
-                               data.mcelog = sv.b64unzip(m.group('m'))
                # turbostat data
                if len(self.turbostat) > data.testnumber:
                        m = re.match(self.tstatfmt, self.turbostat[data.testnumber])
                        if m:
                                data.turbostat = m.group('t')
-               # battery data
-               if len(self.battery) > data.testnumber:
-                       m = re.match(self.batteryfmt, self.battery[data.testnumber])
-                       if m:
-                               data.battery = m.groups()
                # wifi data
                if len(self.wifi) > data.testnumber:
                        m = re.match(self.wififmt, self.wifi[data.testnumber])
                        if m:
-                               data.wifi = m.group('w')
+                               data.wifi = {'dev': m.group('d'), 'stat': m.group('s'),
+                                       'time': float(m.group('t'))}
+                               data.stamp['wifi'] = m.group('d')
                # sleep mode enter errors
                if len(self.testerror) > data.testnumber:
                        m = re.match(self.testerrfmt, self.testerror[data.testnumber])
@@ -3012,13 +3091,13 @@ def appendIncompleteTraceLog(testruns):
                if(t.startMarker()):
                        data = testrun[testidx].data
                        tp.parseStamp(data, sysvals)
-                       data.setStart(t.time)
+                       data.setStart(t.time, t.name)
                        continue
                if(not data):
                        continue
                # find the end of resume
                if(t.endMarker()):
-                       data.setEnd(t.time)
+                       data.setEnd(t.time, t.name)
                        testidx += 1
                        if(testidx >= testcnt):
                                break
@@ -3081,7 +3160,7 @@ def parseTraceLog(live=False):
                doError('%s does not exist' % sysvals.ftracefile)
        if not live:
                sysvals.setupAllKprobes()
-       ksuscalls = ['pm_prepare_console']
+       ksuscalls = ['ksys_sync', 'pm_prepare_console']
        krescalls = ['pm_restore_console']
        tracewatch = ['irq_wakeup']
        if sysvals.usekprobes:
@@ -3094,7 +3173,7 @@ def parseTraceLog(live=False):
        testruns = []
        testdata = []
        testrun = 0
-       data = 0
+       data, limbo = 0, True
        tf = sysvals.openlog(sysvals.ftracefile, 'r')
        phase = 'suspend_prepare'
        for line in tf:
@@ -3141,16 +3220,16 @@ def parseTraceLog(live=False):
                        continue
                # find the start of suspend
                if(t.startMarker()):
-                       data = Data(len(testdata))
+                       data, limbo = Data(len(testdata)), False
                        testdata.append(data)
                        testrun = TestRun(data)
                        testruns.append(testrun)
                        tp.parseStamp(data, sysvals)
-                       data.setStart(t.time)
+                       data.setStart(t.time, t.name)
                        data.first_suspend_prepare = True
                        phase = data.setPhase('suspend_prepare', t.time, True)
                        continue
-               if(not data):
+               if(not data or limbo):
                        continue
                # process cpu exec line
                if t.type == 'tracing_mark_write':
@@ -3167,14 +3246,16 @@ def parseTraceLog(live=False):
                                continue
                # find the end of resume
                if(t.endMarker()):
-                       data.handleEndMarker(t.time)
+                       if data.tKernRes == 0:
+                               data.tKernRes = t.time
+                       data.handleEndMarker(t.time, t.name)
                        if(not sysvals.usetracemarkers):
                                # no trace markers? then quit and be sure to finish recording
                                # the event we used to trigger resume end
                                if('thaw_processes' in testrun.ttemp and len(testrun.ttemp['thaw_processes']) > 0):
                                        # if an entry exists, assume this is its end
                                        testrun.ttemp['thaw_processes'][-1]['end'] = t.time
-                               break
+                       limbo = True
                        continue
                # trace event processing
                if(t.fevent):
@@ -3197,7 +3278,7 @@ def parseTraceLog(live=False):
                                # -- phase changes --
                                # start of kernel suspend
                                if(re.match('suspend_enter\[.*', t.name)):
-                                       if(isbegin):
+                                       if(isbegin and data.tKernSus == 0):
                                                data.tKernSus = t.time
                                        continue
                                # suspend_prepare start
@@ -3225,7 +3306,7 @@ def parseTraceLog(live=False):
                                elif(re.match('machine_suspend\[.*', t.name)):
                                        if(isbegin):
                                                lp = data.lastPhase()
-                                               if lp == 'resume_machine':
+                                               if lp.startswith('resume_machine'):
                                                        data.dmesg[lp]['end'] = t.time
                                                phase = data.setPhase('suspend_machine', data.dmesg[lp]['end'], True)
                                                data.setPhase(phase, t.time, False)
@@ -3320,7 +3401,8 @@ def parseTraceLog(live=False):
                                        'proc': m_proc,
                                })
                                # start of kernel resume
-                               if(phase == 'suspend_prepare' and kprobename in ksuscalls):
+                               if(data.tKernSus == 0 and phase == 'suspend_prepare' \
+                                       and kprobename in ksuscalls):
                                        data.tKernSus = t.time
                        elif(t.freturn):
                                if(key not in tp.ktemp) or len(tp.ktemp[key]) < 1:
@@ -3355,7 +3437,7 @@ def parseTraceLog(live=False):
                sysvals.vprint('WARNING: ftrace start marker is missing')
        if data and not data.devicegroups:
                sysvals.vprint('WARNING: ftrace end marker is missing')
-               data.handleEndMarker(t.time)
+               data.handleEndMarker(t.time, t.name)
 
        if sysvals.suspendmode == 'command':
                for test in testruns:
@@ -3476,6 +3558,10 @@ def parseTraceLog(live=False):
                                        data.fwValid = False
                                sysvals.vprint('WARNING: phase "%s" is missing!' % p)
                        lp = p
+               if not terr and 'dev' in data.wifi and data.wifi['stat'] == 'timeout':
+                       terr = '%s%s failed in wifi_resume <i>(%s %.0fs timeout)</i>' % \
+                               (sysvals.suspendmode, tn, data.wifi['dev'], data.wifi['time'])
+                       error.append(terr)
                if not terr and data.enterfail:
                        pprint('test%s FAILED: enter %s failed with %s' % (tn, sysvals.suspendmode, data.enterfail))
                        terr = 'test%s failed to enter %s mode' % (tn, sysvals.suspendmode)
@@ -3933,7 +4019,7 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
        tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
        iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
        num = 0
-       useturbo = False
+       useturbo = usewifi = False
        lastmode = ''
        cnt = dict()
        for data in sorted(testruns, key=lambda v:(v['mode'], v['host'], v['kernel'], v['time'])):
@@ -3952,17 +4038,17 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
                        tAvg, tMin, tMax, tMed = [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [dict(), dict()]
                        iMin, iMed, iMax = [0, 0], [0, 0], [0, 0]
                        num = 0
-               pkgpc10 = syslpi = ''
+               pkgpc10 = syslpi = wifi = ''
                if 'pkgpc10' in data and 'syslpi' in data:
-                       pkgpc10 = data['pkgpc10']
-                       syslpi = data['syslpi']
-                       useturbo = True
+                       pkgpc10, syslpi, useturbo = data['pkgpc10'], data['syslpi'], True
+               if 'wifi' in data:
+                       wifi, usewifi = data['wifi'], True
                res = data['result']
                tVal = [float(data['suspend']), float(data['resume'])]
                list[mode]['data'].append([data['host'], data['kernel'],
                        data['time'], tVal[0], tVal[1], data['url'], res,
                        data['issues'], data['sus_worst'], data['sus_worsttime'],
-                       data['res_worst'], data['res_worsttime'], pkgpc10, syslpi])
+                       data['res_worst'], data['res_worsttime'], pkgpc10, syslpi, wifi])
                idx = len(list[mode]['data']) - 1
                if res.startswith('fail in'):
                        res = 'fail'
@@ -4002,7 +4088,12 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
        td = '\t<td>{0}</td>\n'
        tdh = '\t<td{1}>{0}</td>\n'
        tdlink = '\t<td><a href="{0}">html</a></td>\n'
-       colspan = '14' if useturbo else '12'
+       cols = 12
+       if useturbo:
+               cols += 2
+       if usewifi:
+               cols += 1
+       colspan = '%d' % cols
 
        # table header
        html += '<table>\n<tr>\n' + th.format('#') +\
@@ -4013,6 +4104,8 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
                th.format('Worst Resume Device') + th.format('RD Time')
        if useturbo:
                html += th.format('PkgPC10') + th.format('SysLPI')
+       if usewifi:
+               html += th.format('Wifi')
        html += th.format('Detail')+'</tr>\n'
        # export list into html
        head = '<tr class="head"><td>{0}</td><td>{1}</td>'+\
@@ -4076,6 +4169,8 @@ def createHTMLSummarySimple(testruns, htmlfile, title):
                        if useturbo:
                                html += td.format(d[12])                                                                # pkg_pc10
                                html += td.format(d[13])                                                                # syslpi
+                       if usewifi:
+                               html += td.format(d[14])                                                                # wifi
                        html += tdlink.format(d[5]) if d[5] else td.format('')          # url
                        html += '</tr>\n'
                        num += 1
@@ -4224,6 +4319,8 @@ def createHTML(testruns, testfail):
                        kerror = True
                if(sysvals.suspendmode in ['freeze', 'standby']):
                        data.trimFreezeTime(testruns[-1].tSuspended)
+               else:
+                       data.getMemTime()
 
        # html function templates
        html_error = '<div id="{1}" title="kernel error/warning" class="err" style="right:{0}%">{2}&rarr;</div>\n'
@@ -4242,13 +4339,10 @@ def createHTML(testruns, testfail):
                '<td class="green">Execution Time: <b>{0} ms</b></td>'\
                '<td class="yellow">Command: <b>{1}</b></td>'\
                '</tr>\n</table>\n'
-       html_timegroups = '<table class="time2">\n<tr>'\
-               '<td class="green" title="time from kernel enter_state({5}) to firmware mode [kernel time only]">{4}Kernel Suspend: {0} ms</td>'\
-               '<td class="purple">{4}Firmware Suspend: {1} ms</td>'\
-               '<td class="purple">{4}Firmware Resume: {2} ms</td>'\
-               '<td class="yellow" title="time from firmware mode to return from kernel enter_state({5}) [kernel time only]">{4}Kernel Resume: {3} ms</td>'\
-               '</tr>\n</table>\n'
        html_fail = '<table class="testfail"><tr><td>{0}</td></tr></table>\n'
+       html_kdesc = '<td class="{3}" title="time spent in kernel execution">{0}Kernel {2}: {1} ms</td>'
+       html_fwdesc = '<td class="{3}" title="time spent in firmware">{0}Firmware {2}: {1} ms</td>'
+       html_wifdesc = '<td class="yellow" title="time for wifi to reconnect after resume complete ({2})">{0}Wifi Resume: {1}</td>'
 
        # html format variables
        scaleH = 20
@@ -4264,13 +4358,10 @@ def createHTML(testruns, testfail):
        # Generate the header for this timeline
        for data in testruns:
                tTotal = data.end - data.start
-               sktime, rktime = data.getTimeValues()
                if(tTotal == 0):
                        doError('No timeline data')
-               if(len(data.tLow) > 0):
-                       low_time = '+'.join(data.tLow)
                if sysvals.suspendmode == 'command':
-                       run_time = '%.0f'%((data.end-data.start)*1000)
+                       run_time = '%.0f' % (tTotal * 1000)
                        if sysvals.testcommand:
                                testdesc = sysvals.testcommand
                        else:
@@ -4279,43 +4370,55 @@ def createHTML(testruns, testfail):
                                testdesc = ordinal(data.testnumber+1)+' '+testdesc
                        thtml = html_timetotal3.format(run_time, testdesc)
                        devtl.html += thtml
-               elif data.fwValid:
-                       suspend_time = '%.0f'%(sktime + (data.fwSuspend/1000000.0))
-                       resume_time = '%.0f'%(rktime + (data.fwResume/1000000.0))
-                       testdesc1 = 'Total'
-                       testdesc2 = ''
-                       stitle = 'time from kernel enter_state(%s) to low-power mode [kernel & firmware time]' % sysvals.suspendmode
-                       rtitle = 'time from low-power mode to return from kernel enter_state(%s) [firmware & kernel time]' % sysvals.suspendmode
-                       if(len(testruns) > 1):
-                               testdesc1 = testdesc2 = ordinal(data.testnumber+1)
-                               testdesc2 += ' '
-                       if(len(data.tLow) == 0):
-                               thtml = html_timetotal.format(suspend_time, \
-                                       resume_time, testdesc1, stitle, rtitle)
-                       else:
-                               thtml = html_timetotal2.format(suspend_time, low_time, \
-                                       resume_time, testdesc1, stitle, rtitle)
-                       devtl.html += thtml
+                       continue
+               # typical full suspend/resume header
+               stot, rtot = sktime, rktime = data.getTimeValues()
+               ssrc, rsrc, testdesc, testdesc2 = ['kernel'], ['kernel'], 'Kernel', ''
+               if data.fwValid:
+                       stot += (data.fwSuspend/1000000.0)
+                       rtot += (data.fwResume/1000000.0)
+                       ssrc.append('firmware')
+                       rsrc.append('firmware')
+                       testdesc = 'Total'
+               if 'time' in data.wifi and data.wifi['stat'] != 'timeout':
+                       rtot += data.end - data.tKernRes + (data.wifi['time'] * 1000.0)
+                       rsrc.append('wifi')
+                       testdesc = 'Total'
+               suspend_time, resume_time = '%.3f' % stot, '%.3f' % rtot
+               stitle = 'time from kernel suspend start to %s mode [%s time]' % \
+                       (sysvals.suspendmode, ' & '.join(ssrc))
+               rtitle = 'time from %s mode to kernel resume complete [%s time]' % \
+                       (sysvals.suspendmode, ' & '.join(rsrc))
+               if(len(testruns) > 1):
+                       testdesc = testdesc2 = ordinal(data.testnumber+1)
+                       testdesc2 += ' '
+               if(len(data.tLow) == 0):
+                       thtml = html_timetotal.format(suspend_time, \
+                               resume_time, testdesc, stitle, rtitle)
+               else:
+                       low_time = '+'.join(data.tLow)
+                       thtml = html_timetotal2.format(suspend_time, low_time, \
+                               resume_time, testdesc, stitle, rtitle)
+               devtl.html += thtml
+               if not data.fwValid and 'dev' not in data.wifi:
+                       continue
+               # extra detail when the times come from multiple sources
+               thtml = '<table class="time2">\n<tr>'
+               thtml += html_kdesc.format(testdesc2, '%.3f'%sktime, 'Suspend', 'green')
+               if data.fwValid:
                        sftime = '%.3f'%(data.fwSuspend / 1000000.0)
                        rftime = '%.3f'%(data.fwResume / 1000000.0)
-                       devtl.html += html_timegroups.format('%.3f'%sktime, \
-                               sftime, rftime, '%.3f'%rktime, testdesc2, sysvals.suspendmode)
-               else:
-                       suspend_time = '%.3f' % sktime
-                       resume_time = '%.3f' % rktime
-                       testdesc = 'Kernel'
-                       stitle = 'time from kernel enter_state(%s) to firmware mode [kernel time only]' % sysvals.suspendmode
-                       rtitle = 'time from firmware mode to return from kernel enter_state(%s) [kernel time only]' % sysvals.suspendmode
-                       if(len(testruns) > 1):
-                               testdesc = ordinal(data.testnumber+1)+' '+testdesc
-                       if(len(data.tLow) == 0):
-                               thtml = html_timetotal.format(suspend_time, \
-                                       resume_time, testdesc, stitle, rtitle)
+                       thtml += html_fwdesc.format(testdesc2, sftime, 'Suspend', 'green')
+                       thtml += html_fwdesc.format(testdesc2, rftime, 'Resume', 'yellow')
+               thtml += html_kdesc.format(testdesc2, '%.3f'%rktime, 'Resume', 'yellow')
+               if 'time' in data.wifi:
+                       if data.wifi['stat'] != 'timeout':
+                               wtime = '%.0f ms'%(data.end - data.tKernRes + (data.wifi['time'] * 1000.0))
                        else:
-                               thtml = html_timetotal2.format(suspend_time, low_time, \
-                                       resume_time, testdesc, stitle, rtitle)
-                       devtl.html += thtml
-
+                               wtime = 'TIMEOUT'
+                       thtml += html_wifdesc.format(testdesc2, wtime, data.wifi['dev'])
+               thtml += '</tr>\n</table>\n'
+               devtl.html += thtml
        if testfail:
                devtl.html += html_fail.format(testfail)
 
@@ -5082,28 +5185,32 @@ def setRuntimeSuspend(before=True):
 # Description:
 #       Execute system suspend through the sysfs interface, then copy the output
 #       dmesg and ftrace files to the test output directory.
-def executeSuspend():
+def executeSuspend(quiet=False):
        pm = ProcessMonitor()
        tp = sysvals.tpath
-       wifi = sysvals.checkWifi()
+       if sysvals.wifi:
+               wifi = sysvals.checkWifi()
        testdata = []
-       battery = True if getBattery() else False
        # run these commands to prepare the system for suspend
        if sysvals.display:
-               pprint('SET DISPLAY TO %s' % sysvals.display.upper())
+               if not quiet:
+                       pprint('SET DISPLAY TO %s' % sysvals.display.upper())
                displayControl(sysvals.display)
                time.sleep(1)
        if sysvals.sync:
-               pprint('SYNCING FILESYSTEMS')
+               if not quiet:
+                       pprint('SYNCING FILESYSTEMS')
                call('sync', shell=True)
        # mark the start point in the kernel ring buffer just as we start
        sysvals.initdmesg()
        # start ftrace
        if(sysvals.usecallgraph or sysvals.usetraceevents):
-               pprint('START TRACING')
+               if not quiet:
+                       pprint('START TRACING')
                sysvals.fsetVal('1', 'tracing_on')
                if sysvals.useprocmon:
                        pm.start()
+       sysvals.cmdinfo(True)
        # execute however many s/r runs requested
        for count in range(1,sysvals.execcount+1):
                # x2delay in between test runs
@@ -5119,15 +5226,14 @@ def executeSuspend():
                                pprint('SUSPEND START')
                        else:
                                pprint('SUSPEND START (press a key to resume)')
-               sysvals.mcelog(True)
-               bat1 = getBattery() if battery else False
                # set rtcwake
                if(sysvals.rtcwake):
-                       pprint('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
+                       if not quiet:
+                               pprint('will issue an rtcwake in %d seconds' % sysvals.rtcwaketime)
                        sysvals.rtcWakeAlarmOn()
                # start of suspend trace marker
                if(sysvals.usecallgraph or sysvals.usetraceevents):
-                       sysvals.fsetVal('SUSPEND START', 'trace_marker')
+                       sysvals.fsetVal(datetime.now().strftime(sysvals.tmstart), 'trace_marker')
                # predelay delay
                if(count == 1 and sysvals.predelay > 0):
                        sysvals.fsetVal('WAIT %d' % sysvals.predelay, 'trace_marker')
@@ -5174,37 +5280,33 @@ def executeSuspend():
                # return from suspend
                pprint('RESUME COMPLETE')
                if(sysvals.usecallgraph or sysvals.usetraceevents):
-                       sysvals.fsetVal('RESUME COMPLETE', 'trace_marker')
+                       sysvals.fsetVal(datetime.now().strftime(sysvals.tmend), 'trace_marker')
+               if sysvals.wifi and wifi:
+                       tdata['wifi'] = sysvals.pollWifi(wifi)
                if(sysvals.suspendmode == 'mem' or sysvals.suspendmode == 'command'):
                        tdata['fw'] = getFPDT(False)
-               mcelog = sysvals.mcelog()
-               if mcelog:
-                       tdata['mcelog'] = mcelog
-               bat2 = getBattery() if battery else False
-               if battery and bat1 and bat2:
-                       tdata['bat'] = (bat1, bat2)
-               if 'device' in wifi and 'ip' in wifi:
-                       tdata['wifi'] = (wifi, sysvals.checkWifi())
                testdata.append(tdata)
+       cmdafter = sysvals.cmdinfo(False)
        # stop ftrace
        if(sysvals.usecallgraph or sysvals.usetraceevents):
                if sysvals.useprocmon:
                        pm.stop()
                sysvals.fsetVal('0', 'tracing_on')
        # grab a copy of the dmesg output
-       pprint('CAPTURING DMESG')
+       if not quiet:
+               pprint('CAPTURING DMESG')
        sysvals.getdmesg(testdata)
        # grab a copy of the ftrace output
        if(sysvals.usecallgraph or sysvals.usetraceevents):
-               pprint('CAPTURING TRACE')
+               if not quiet:
+                       pprint('CAPTURING TRACE')
                op = sysvals.writeDatafileHeader(sysvals.ftracefile, testdata)
                fp = open(tp+'trace', 'r')
                for line in fp:
                        op.write(line)
                op.close()
                sysvals.fsetVal('', 'trace')
-               sysvals.platforminfo()
-       return testdata
+               sysvals.platforminfo(cmdafter)
 
 def readFile(file):
        if os.path.islink(file):
@@ -5447,25 +5549,6 @@ def dmidecode(mempath, fatal=False):
                count += 1
        return out
 
-def getBattery():
-       p, charge, bat = '/sys/class/power_supply', 0, {}
-       if not os.path.exists(p):
-               return False
-       for d in os.listdir(p):
-               type = sysvals.getVal(os.path.join(p, d, 'type')).strip().lower()
-               if type != 'battery':
-                       continue
-               for v in ['status', 'energy_now', 'capacity_now']:
-                       bat[v] = sysvals.getVal(os.path.join(p, d, v)).strip().lower()
-               break
-       if 'status' not in bat:
-               return False
-       ac = False if 'discharging' in bat['status'] else True
-       for v in ['energy_now', 'capacity_now']:
-               if v in bat and bat[v]:
-                       charge = int(bat[v])
-       return (ac, charge)
-
 def displayControl(cmd):
        xset, ret = 'timeout 10 xset -d :0.0 {0}', 0
        if sysvals.sudouser:
@@ -5715,6 +5798,17 @@ def statusCheck(probecheck=False):
                status = 'rtcwake is not properly supported'
        pprint('    is rtcwake supported: %s' % res)
 
+       # check info commands
+       pprint('    optional commands this tool may use for info:')
+       no = sysvals.colorText('MISSING')
+       yes = sysvals.colorText('FOUND', 32)
+       for c in ['turbostat', 'mcelog', 'lspci', 'lsusb']:
+               if c == 'turbostat':
+                       res = yes if sysvals.haveTurbostat() else no
+               else:
+                       res = yes if sysvals.getExec(c) else no
+               pprint('        %s: %s' % (c, res))
+
        if not probecheck:
                return status
 
@@ -5780,8 +5874,9 @@ def getArgFloat(name, args, min, max, main=True):
                doError(name+': value should be between %f and %f' % (min, max), True)
        return val
 
-def processData(live=False):
-       pprint('PROCESSING DATA')
+def processData(live=False, quiet=False):
+       if not quiet:
+               pprint('PROCESSING DATA')
        sysvals.vprint('usetraceevents=%s, usetracemarkers=%s, usekprobes=%s' % \
                (sysvals.usetraceevents, sysvals.usetracemarkers, sysvals.usekprobes))
        error = ''
@@ -5796,20 +5891,17 @@ def processData(live=False):
                        parseKernelLog(data)
                if(sysvals.ftracefile and (sysvals.usecallgraph or sysvals.usetraceevents)):
                        appendIncompleteTraceLog(testruns)
+       if not sysvals.stamp:
+               pprint('ERROR: data does not include the expected stamp')
+               return (testruns, {'error': 'timeline generation failed'})
        shown = ['bios', 'biosdate', 'cpu', 'host', 'kernel', 'man', 'memfr',
-                       'memsz', 'mode', 'numcpu', 'plat', 'time']
+                       'memsz', 'mode', 'numcpu', 'plat', 'time', 'wifi']
        sysvals.vprint('System Info:')
        for key in sorted(sysvals.stamp):
                if key in shown:
                        sysvals.vprint('    %-8s : %s' % (key.upper(), sysvals.stamp[key]))
-       if sysvals.kparams:
-               sysvals.vprint('Kparams:\n    %s' % sysvals.kparams)
        sysvals.vprint('Command:\n    %s' % sysvals.cmdline)
        for data in testruns:
-               if data.mcelog:
-                       sysvals.vprint('MCELOG Data:')
-                       for line in data.mcelog.split('\n'):
-                               sysvals.vprint('    %s' % line)
                if data.turbostat:
                        idx, s = 0, 'Turbostat:\n    '
                        for val in data.turbostat.split('|'):
@@ -5819,23 +5911,13 @@ def processData(live=False):
                                        s += '\n    '
                                s += val + ' '
                        sysvals.vprint(s)
-               if data.battery:
-                       a1, c1, a2, c2 = data.battery
-                       s = 'Battery:\n    Before - AC: %s, Charge: %d\n     After - AC: %s, Charge: %d' % \
-                               (a1, int(c1), a2, int(c2))
-                       sysvals.vprint(s)
-               if data.wifi:
-                       w = data.wifi.replace('|', ' ').split(',')
-                       s = 'Wifi:\n    Before %s\n     After %s' % \
-                               (w[0], w[1])
-                       sysvals.vprint(s)
                data.printDetails()
-               if len(sysvals.platinfo) > 0:
-                       sysvals.vprint('\nPlatform Info:')
-                       for info in sysvals.platinfo:
-                               sysvals.vprint(info[0]+' - '+info[1])
-                               sysvals.vprint(info[2])
-                       sysvals.vprint('')
+       if len(sysvals.platinfo) > 0:
+               sysvals.vprint('\nPlatform Info:')
+               for info in sysvals.platinfo:
+                       sysvals.vprint('[%s - %s]' % (info[0], info[1]))
+                       sysvals.vprint(info[2])
+               sysvals.vprint('')
        if sysvals.cgdump:
                for data in testruns:
                        data.debugPrint()
@@ -5845,7 +5927,8 @@ def processData(live=False):
                return (testruns, {'error': 'timeline generation failed'})
        sysvals.vprint('Creating the html timeline (%s)...' % sysvals.htmlfile)
        createHTML(testruns, error)
-       pprint('DONE')
+       if not quiet:
+               pprint('DONE')
        data = testruns[0]
        stamp = data.stamp
        stamp['suspend'], stamp['resume'] = data.getTimeValues()
@@ -5872,31 +5955,28 @@ def rerunTest(htmlfile=''):
                        doError('a directory already exists with this name: %s' % sysvals.htmlfile)
                elif not os.access(sysvals.htmlfile, os.W_OK):
                        doError('missing permission to write to %s' % sysvals.htmlfile)
-       testruns, stamp = processData(False)
-       sysvals.logmsg = ''
+       testruns, stamp = processData()
+       sysvals.resetlog()
        return stamp
 
 # Function: runTest
 # Description:
 #       execute a suspend/resume, gather the logs, and generate the output
-def runTest(n=0):
+def runTest(n=0, quiet=False):
        # prepare for the test
-       sysvals.initFtrace()
+       sysvals.initFtrace(quiet)
        sysvals.initTestOutput('suspend')
 
        # execute the test
-       testdata = executeSuspend()
+       executeSuspend(quiet)
        sysvals.cleanupFtrace()
        if sysvals.skiphtml:
+               sysvals.outputResult({}, n)
                sysvals.sudoUserchown(sysvals.testdir)
                return
-       if not testdata[0]['error']:
-               testruns, stamp = processData(True)
-               for data in testruns:
-                       del data
-       else:
-               stamp = testdata[0]
-
+       testruns, stamp = processData(True, quiet)
+       for data in testruns:
+               del data
        sysvals.sudoUserchown(sysvals.testdir)
        sysvals.outputResult(stamp, n)
        if 'error' in stamp:
@@ -5904,13 +5984,14 @@ def runTest(n=0):
        return 0
 
 def find_in_html(html, start, end, firstonly=True):
-       n, out = 0, []
-       while n < len(html):
-               m = re.search(start, html[n:])
+       n, cnt, out = 0, len(html), []
+       while n < cnt:
+               e = cnt if (n + 10000 > cnt or n == 0) else n + 10000
+               m = re.search(start, html[n:e])
                if not m:
                        break
                i = m.end()
-               m = re.search(end, html[n+i:])
+               m = re.search(end, html[n+i:e])
                if not m:
                        break
                j = m.start()
@@ -5945,7 +6026,7 @@ def data_from_html(file, outpath, issues, fulldetail=False):
        tstr = dt.strftime('%Y/%m/%d %H:%M:%S')
        error = find_in_html(html, '<table class="testfail"><tr><td>', '</td>')
        if error:
-               m = re.match('[a-z]* failed in (?P<p>[a-z0-9_]*) phase', error)
+               m = re.match('[a-z0-9]* failed in (?P<p>\S*).*', error)
                if m:
                        result = 'fail in %s' % m.group('p')
                else:
@@ -5974,6 +6055,9 @@ def data_from_html(file, outpath, issues, fulldetail=False):
                                elist[err[0]] += 1
                for i in elist:
                        ilist.append('%sx%d' % (i, elist[i]) if elist[i] > 1 else i)
+       wifi = find_in_html(html, 'Wifi Resume: ', '</td>')
+       if wifi:
+               extra['wifi'] = wifi
        low = find_in_html(html, 'freeze time: <b>', ' ms</b>')
        if low and '|' in low:
                issue = 'FREEZEx%d' % len(low.split('|'))
@@ -6048,13 +6132,15 @@ def genHtml(subdir, force=False):
        for dirname, dirnames, filenames in os.walk(subdir):
                sysvals.dmesgfile = sysvals.ftracefile = sysvals.htmlfile = ''
                for filename in filenames:
-                       if(re.match('.*_dmesg.txt', filename)):
-                               sysvals.dmesgfile = os.path.join(dirname, filename)
-                       elif(re.match('.*_ftrace.txt', filename)):
-                               sysvals.ftracefile = os.path.join(dirname, filename)
+                       file = os.path.join(dirname, filename)
+                       if sysvals.usable(file):
+                               if(re.match('.*_dmesg.txt', filename)):
+                                       sysvals.dmesgfile = file
+                               elif(re.match('.*_ftrace.txt', filename)):
+                                       sysvals.ftracefile = file
                sysvals.setOutputFile()
-               if sysvals.ftracefile and sysvals.htmlfile and \
-                       (force or not os.path.exists(sysvals.htmlfile)):
+               if (sysvals.dmesgfile or sysvals.ftracefile) and sysvals.htmlfile and \
+                       (force or not sysvals.usable(sysvals.htmlfile)):
                        pprint('FTRACE: %s' % sysvals.ftracefile)
                        if sysvals.dmesgfile:
                                pprint('DMESG : %s' % sysvals.dmesgfile)
@@ -6169,9 +6255,9 @@ def configFromFile(file):
                                sysvals.cgtest = getArgInt('cgtest', value, 0, 1, False)
                        elif(option == 'cgphase'):
                                d = Data(0)
-                               if value not in d.sortedPhases():
+                               if value not in d.phasedef:
                                        doError('invalid phase --> (%s: %s), valid phases are %s'\
-                                               % (option, value, d.sortedPhases()), True)
+                                               % (option, value, d.phasedef.keys()), True)
                                sysvals.cgphase = value
                        elif(option == 'fadd'):
                                file = sysvals.configFile(value)
@@ -6184,9 +6270,7 @@ def configFromFile(file):
                                nums = value.split()
                                if len(nums) != 2:
                                        doError('multi requires 2 integers (exec_count and delay)', True)
-                               sysvals.multitest['run'] = True
-                               sysvals.multitest['count'] = getArgInt('multi: n d (exec count)', nums[0], 2, 1000000, False)
-                               sysvals.multitest['delay'] = getArgInt('multi: n d (delay between tests)', nums[1], 0, 3600, False)
+                               sysvals.multiinit(nums[0], nums[1])
                        elif(option == 'devicefilter'):
                                sysvals.setDeviceFilter(value)
                        elif(option == 'expandcg'):
@@ -6342,6 +6426,7 @@ def printHelp():
        '   -srgap       Add a visible gap in the timeline between sus/res (default: disabled)\n'\
        '   -skiphtml    Run the test and capture the trace logs, but skip the timeline (default: disabled)\n'\
        '   -result fn   Export a results table to a text file for parsing.\n'\
+       '   -wifi        If a wifi connection is available, check that it reconnects after resume.\n'\
        '  [testprep]\n'\
        '   -sync        Sync the filesystems before starting the test\n'\
        '   -rs on/off   Enable/disable runtime suspend for all devices, restore all after test\n'\
@@ -6356,8 +6441,10 @@ def printHelp():
        '   -predelay t  Include t ms delay before 1st suspend (default: 0 ms)\n'\
        '   -postdelay t Include t ms delay after last resume (default: 0 ms)\n'\
        '   -mindev ms   Discard all device blocks shorter than ms milliseconds (e.g. 0.001 for us)\n'\
-       '   -multi n d   Execute <n> consecutive tests at <d> seconds intervals. The outputs will\n'\
-       '                be created in a new subdirectory with a summary page.\n'\
+       '   -multi n d   Execute <n> consecutive tests at <d> seconds intervals. If <n> is followed\n'\
+       '                by a "d", "h", or "m" execute for <n> days, hours, or mins instead.\n'\
+       '                The outputs will be created in a new subdirectory with a summary page.\n'\
+       '   -maxfail n   Abort a -multi run after n consecutive fails (default is 0 = never abort)\n'\
        '  [debug]\n'\
        '   -f           Use ftrace to create device callgraphs (default: disabled)\n'\
        '   -ftop        Use ftrace on the top level call: "%s" (default: disabled)\n'\
@@ -6379,11 +6466,11 @@ def printHelp():
        '   -modes       List available suspend modes\n'\
        '   -status      Test to see if the system is enabled to run this tool\n'\
        '   -fpdt        Print out the contents of the ACPI Firmware Performance Data Table\n'\
-       '   -battery     Print out battery info (if available)\n'\
-       '   -wifi        Print out wifi connection info (if wireless-tools and device exists)\n'\
+       '   -wificheck   Print out wifi connection info\n'\
        '   -x<mode>     Test xset by toggling the given mode (on/off/standby/suspend)\n'\
        '   -sysinfo     Print out system info extracted from BIOS\n'\
        '   -devinfo     Print out the pm settings of all devices which support runtime suspend\n'\
+       '   -cmdinfo     Print out all the platform info collected before and after suspend/resume\n'\
        '   -flist       Print the list of functions currently being captured in ftrace\n'\
        '   -flistall    Print all functions capable of being captured in ftrace\n'\
        '   -summary dir Create a summary of tests in this dir [-genhtml builds missing html]\n'\
@@ -6399,8 +6486,8 @@ if __name__ == '__main__':
        genhtml = False
        cmd = ''
        simplecmds = ['-sysinfo', '-modes', '-fpdt', '-flist', '-flistall',
-               '-devinfo', '-status', '-battery', '-xon', '-xoff', '-xstandby',
-               '-xsuspend', '-xinit', '-xreset', '-xstat', '-wifi']
+               '-devinfo', '-status', '-xon', '-xoff', '-xstandby', '-xsuspend',
+               '-xinit', '-xreset', '-xstat', '-wificheck', '-cmdinfo']
        if '-f' in sys.argv:
                sysvals.cgskip = sysvals.configFile('cgskip.txt')
        # loop through the command line arguments
@@ -6462,8 +6549,15 @@ if __name__ == '__main__':
                        sysvals.usedevsrc = True
                elif(arg == '-sync'):
                        sysvals.sync = True
+               elif(arg == '-wifi'):
+                       sysvals.wifi = True
                elif(arg == '-gzip'):
                        sysvals.gzip = True
+               elif(arg == '-info'):
+                       try:
+                               val = next(args)
+                       except:
+                               doError('-info requires one string argument', True)
                elif(arg == '-rs'):
                        try:
                                val = next(args)
@@ -6555,10 +6649,14 @@ if __name__ == '__main__':
                        sysvals.cgexp = True
                elif(arg == '-srgap'):
                        sysvals.srgap = 5
+               elif(arg == '-maxfail'):
+                       sysvals.maxfail = getArgInt('-maxfail', args, 0, 1000000)
                elif(arg == '-multi'):
-                       sysvals.multitest['run'] = True
-                       sysvals.multitest['count'] = getArgInt('-multi n d (exec count)', args, 2, 1000000)
-                       sysvals.multitest['delay'] = getArgInt('-multi n d (delay between tests)', args, 0, 3600)
+                       try:
+                               c, d = next(args), next(args)
+                       except:
+                               doError('-multi requires two values', True)
+                       sysvals.multiinit(c, d)
                elif(arg == '-o'):
                        try:
                                val = next(args)
@@ -6655,13 +6753,6 @@ if __name__ == '__main__':
                elif(cmd == 'fpdt'):
                        if not getFPDT(True):
                                ret = 1
-               elif(cmd == 'battery'):
-                       out = getBattery()
-                       if out:
-                               pprint('AC Connect    : %s\nBattery Charge: %d' % out)
-                       else:
-                               pprint('no battery found')
-                               ret = 1
                elif(cmd == 'sysinfo'):
                        sysvals.printSystemInfo(True)
                elif(cmd == 'devinfo'):
@@ -6679,13 +6770,15 @@ if __name__ == '__main__':
                        ret = displayControl(cmd[1:])
                elif(cmd == 'xstat'):
                        pprint('Display Status: %s' % displayControl('stat').upper())
-               elif(cmd == 'wifi'):
-                       out = sysvals.checkWifi()
-                       if 'device' not in out:
-                               pprint('WIFI interface not found')
+               elif(cmd == 'wificheck'):
+                       dev = sysvals.checkWifi()
+                       if dev:
+                               print('%s is connected' % sysvals.wifiDetails(dev))
                        else:
-                               for key in sorted(out):
-                                       pprint('%6s: %s' % (key.upper(), out[key]))
+                               print('No wifi connection found')
+               elif(cmd == 'cmdinfo'):
+                       for out in sysvals.cmdinfo(False, True):
+                               print('[%s - %s]\n%s\n' % out)
                sys.exit(ret)
 
        # if instructed, re-analyze existing data files
@@ -6720,24 +6813,38 @@ if __name__ == '__main__':
        setRuntimeSuspend(True)
        if sysvals.display:
                displayControl('init')
-       ret = 0
+       failcnt, ret = 0, 0
        if sysvals.multitest['run']:
                # run multiple tests in a separate subdirectory
                if not sysvals.outdir:
-                       s = 'suspend-x%d' % sysvals.multitest['count']
-                       sysvals.outdir = datetime.now().strftime(s+'-%y%m%d-%H%M%S')
+                       if 'time' in sysvals.multitest:
+                               s = '-%dm' % sysvals.multitest['time']
+                       else:
+                               s = '-x%d' % sysvals.multitest['count']
+                       sysvals.outdir = datetime.now().strftime('suspend-%y%m%d-%H%M%S'+s)
                if not os.path.isdir(sysvals.outdir):
                        os.makedirs(sysvals.outdir)
+               sysvals.sudoUserchown(sysvals.outdir)
+               finish = datetime.now()
+               if 'time' in sysvals.multitest:
+                       finish += timedelta(minutes=sysvals.multitest['time'])
                for i in range(sysvals.multitest['count']):
-                       if(i != 0):
+                       sysvals.multistat(True, i, finish)
+                       if i != 0 and sysvals.multitest['delay'] > 0:
                                pprint('Waiting %d seconds...' % (sysvals.multitest['delay']))
                                time.sleep(sysvals.multitest['delay'])
-                       pprint('TEST (%d/%d) START' % (i+1, sysvals.multitest['count']))
                        fmt = 'suspend-%y%m%d-%H%M%S'
                        sysvals.testdir = os.path.join(sysvals.outdir, datetime.now().strftime(fmt))
-                       ret = runTest(i+1)
-                       pprint('TEST (%d/%d) COMPLETE' % (i+1, sysvals.multitest['count']))
-                       sysvals.logmsg = ''
+                       ret = runTest(i+1, True)
+                       failcnt = 0 if not ret else failcnt + 1
+                       if sysvals.maxfail > 0 and failcnt >= sysvals.maxfail:
+                               pprint('Maximum fail count of %d reached, aborting multitest' % (sysvals.maxfail))
+                               break
+                       time.sleep(5)
+                       sysvals.resetlog()
+                       sysvals.multistat(False, i, finish)
+                       if 'time' in sysvals.multitest and datetime.now() >= finish:
+                               break
                if not sysvals.skiphtml:
                        runSummary(sysvals.outdir, False, False)
                sysvals.sudoUserchown(sysvals.outdir)
index c30079c..3ff0319 100644 (file)
@@ -30,8 +30,6 @@ test_tcpnotify_user
 test_libbpf
 test_tcp_check_syncookie_user
 test_sysctl
-test_hashmap
-test_btf_dump
 test_current_pid_tgid_new_ns
 xdping
 test_cpp
@@ -39,4 +37,4 @@ test_cpp
 /no_alu32
 /bpf_gcc
 /tools
-
+/runqslower
index 7729892..3d942be 100644 (file)
@@ -20,9 +20,10 @@ CLANG                ?= clang
 LLC            ?= llc
 LLVM_OBJCOPY   ?= llvm-objcopy
 BPF_GCC                ?= $(shell command -v bpf-gcc;)
-CFLAGS += -g -rdynamic -Wall -O2 $(GENFLAGS) -I$(CURDIR)               \
-         -I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR) -I$(TOOLSINCDIR)     \
-         -I$(APIDIR)                                                   \
+SAN_CFLAGS     ?=
+CFLAGS += -g -rdynamic -Wall -O2 $(GENFLAGS) $(SAN_CFLAGS)             \
+         -I$(CURDIR) -I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR)          \
+         -I$(TOOLSINCDIR) -I$(APIDIR)                                  \
          -Dbpf_prog_load=bpf_prog_test_load                            \
          -Dbpf_load_program=bpf_test_load_program
 LDLIBS += -lcap -lelf -lz -lrt -lpthread
@@ -32,7 +33,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test
        test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \
        test_sock test_btf test_sockmap get_cgroup_id_user test_socket_cookie \
        test_cgroup_storage \
-       test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \
+       test_netcnt test_tcpnotify_user test_sock_fields test_sysctl \
        test_progs-no_alu32 \
        test_current_pid_tgid_new_ns
 
@@ -141,7 +142,8 @@ VMLINUX_BTF := $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
 $(OUTPUT)/runqslower: $(BPFOBJ)
        $(Q)$(MAKE) $(submake_extras) -C $(TOOLSDIR)/bpf/runqslower     \
                    OUTPUT=$(SCRATCH_DIR)/ VMLINUX_BTF=$(VMLINUX_BTF)   \
-                   BPFOBJ=$(BPFOBJ) BPF_INCLUDE=$(INCLUDE_DIR)
+                   BPFOBJ=$(BPFOBJ) BPF_INCLUDE=$(INCLUDE_DIR) &&      \
+                   cp $(SCRATCH_DIR)/runqslower $@
 
 $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED): $(OUTPUT)/test_stub.o $(BPFOBJ)
 
@@ -241,7 +243,7 @@ define GCC_BPF_BUILD_RULE
        $(BPF_GCC) $3 $4 -O2 -c $1 -o $2
 endef
 
-SKEL_BLACKLIST := btf__% test_pinning_invalid.c
+SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c
 
 # Set up extra TRUNNER_XXX "temporary" variables in the environment (relies on
 # $eval()) and pass control to DEFINE_TEST_RUNNER_RULES.
@@ -323,7 +325,7 @@ $(TRUNNER_TEST_OBJS): $(TRUNNER_OUTPUT)/%.test.o:                   \
                      $(TRUNNER_BPF_SKELS)                              \
                      $$(BPFOBJ) | $(TRUNNER_OUTPUT)
        $$(call msg,TEST-OBJ,$(TRUNNER_BINARY),$$@)
-       cd $$(@D) && $$(CC) $$(CFLAGS) -c $(CURDIR)/$$< $$(LDLIBS) -o $$(@F)
+       cd $$(@D) && $$(CC) -I. $$(CFLAGS) -c $(CURDIR)/$$< $$(LDLIBS) -o $$(@F)
 
 $(TRUNNER_EXTRA_OBJS): $(TRUNNER_OUTPUT)/%.o:                          \
                       %.c                                              \
index f100298..7afa416 100644 (file)
@@ -1,26 +1,30 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <test_progs.h>
 
+#define nr_iters 2
+
 void test_bpf_obj_id(void)
 {
        const __u64 array_magic_value = 0xfaceb00c;
        const __u32 array_key = 0;
-       const int nr_iters = 2;
        const char *file = "./test_obj_id.o";
        const char *expected_prog_name = "test_obj_id";
        const char *expected_map_name = "test_map_id";
        const __u64 nsec_per_sec = 1000000000;
 
-       struct bpf_object *objs[nr_iters];
+       struct bpf_object *objs[nr_iters] = {};
+       struct bpf_link *links[nr_iters] = {};
+       struct bpf_program *prog;
        int prog_fds[nr_iters], map_fds[nr_iters];
        /* +1 to test for the info_len returned by kernel */
        struct bpf_prog_info prog_infos[nr_iters + 1];
        struct bpf_map_info map_infos[nr_iters + 1];
+       struct bpf_link_info link_infos[nr_iters + 1];
        /* Each prog only uses one map. +1 to test nr_map_ids
         * returned by kernel.
         */
        __u32 map_ids[nr_iters + 1];
-       char jited_insns[128], xlated_insns[128], zeros[128];
+       char jited_insns[128], xlated_insns[128], zeros[128], tp_name[128];
        __u32 i, next_id, info_len, nr_id_found, duration = 0;
        struct timespec real_time_ts, boot_time_ts;
        int err = 0;
@@ -36,14 +40,15 @@ void test_bpf_obj_id(void)
        CHECK(err >= 0 || errno != ENOENT,
              "get-fd-by-notexist-map-id", "err %d errno %d\n", err, errno);
 
-       for (i = 0; i < nr_iters; i++)
-               objs[i] = NULL;
+       err = bpf_link_get_fd_by_id(0);
+       CHECK(err >= 0 || errno != ENOENT,
+             "get-fd-by-notexist-link-id", "err %d errno %d\n", err, errno);
 
        /* Check bpf_obj_get_info_by_fd() */
        bzero(zeros, sizeof(zeros));
        for (i = 0; i < nr_iters; i++) {
                now = time(NULL);
-               err = bpf_prog_load(file, BPF_PROG_TYPE_SOCKET_FILTER,
+               err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT,
                                    &objs[i], &prog_fds[i]);
                /* test_obj_id.o is a dumb prog. It should never fail
                 * to load.
@@ -60,6 +65,17 @@ void test_bpf_obj_id(void)
                if (CHECK_FAIL(err))
                        goto done;
 
+               prog = bpf_object__find_program_by_title(objs[i],
+                                                        "raw_tp/sys_enter");
+               if (CHECK_FAIL(!prog))
+                       goto done;
+               links[i] = bpf_program__attach(prog);
+               err = libbpf_get_error(links[i]);
+               if (CHECK(err, "prog_attach", "prog #%d, err %d\n", i, err)) {
+                       links[i] = NULL;
+                       goto done;
+               }
+
                /* Check getting map info */
                info_len = sizeof(struct bpf_map_info) * 2;
                bzero(&map_infos[i], info_len);
@@ -107,7 +123,7 @@ void test_bpf_obj_id(void)
                load_time = (real_time_ts.tv_sec - boot_time_ts.tv_sec)
                        + (prog_infos[i].load_time / nsec_per_sec);
                if (CHECK(err ||
-                         prog_infos[i].type != BPF_PROG_TYPE_SOCKET_FILTER ||
+                         prog_infos[i].type != BPF_PROG_TYPE_RAW_TRACEPOINT ||
                          info_len != sizeof(struct bpf_prog_info) ||
                          (env.jit_enabled && !prog_infos[i].jited_prog_len) ||
                          (env.jit_enabled &&
@@ -120,7 +136,11 @@ void test_bpf_obj_id(void)
                          *(int *)(long)prog_infos[i].map_ids != map_infos[i].id ||
                          strcmp((char *)prog_infos[i].name, expected_prog_name),
                          "get-prog-info(fd)",
-                         "err %d errno %d i %d type %d(%d) info_len %u(%zu) jit_enabled %d jited_prog_len %u xlated_prog_len %u jited_prog %d xlated_prog %d load_time %lu(%lu) uid %u(%u) nr_map_ids %u(%u) map_id %u(%u) name %s(%s)\n",
+                         "err %d errno %d i %d type %d(%d) info_len %u(%zu) "
+                         "jit_enabled %d jited_prog_len %u xlated_prog_len %u "
+                         "jited_prog %d xlated_prog %d load_time %lu(%lu) "
+                         "uid %u(%u) nr_map_ids %u(%u) map_id %u(%u) "
+                         "name %s(%s)\n",
                          err, errno, i,
                          prog_infos[i].type, BPF_PROG_TYPE_SOCKET_FILTER,
                          info_len, sizeof(struct bpf_prog_info),
@@ -135,6 +155,33 @@ void test_bpf_obj_id(void)
                          *(int *)(long)prog_infos[i].map_ids, map_infos[i].id,
                          prog_infos[i].name, expected_prog_name))
                        goto done;
+
+               /* Check getting link info */
+               info_len = sizeof(struct bpf_link_info) * 2;
+               bzero(&link_infos[i], info_len);
+               link_infos[i].raw_tracepoint.tp_name = (__u64)&tp_name;
+               link_infos[i].raw_tracepoint.tp_name_len = sizeof(tp_name);
+               err = bpf_obj_get_info_by_fd(bpf_link__fd(links[i]),
+                                            &link_infos[i], &info_len);
+               if (CHECK(err ||
+                         link_infos[i].type != BPF_LINK_TYPE_RAW_TRACEPOINT ||
+                         link_infos[i].prog_id != prog_infos[i].id ||
+                         link_infos[i].raw_tracepoint.tp_name != (__u64)&tp_name ||
+                         strcmp((char *)link_infos[i].raw_tracepoint.tp_name,
+                                "sys_enter") ||
+                         info_len != sizeof(struct bpf_link_info),
+                         "get-link-info(fd)",
+                         "err %d errno %d info_len %u(%zu) type %d(%d) id %d "
+                         "prog_id %d (%d) tp_name %s(%s)\n",
+                         err, errno,
+                         info_len, sizeof(struct bpf_link_info),
+                         link_infos[i].type, BPF_LINK_TYPE_RAW_TRACEPOINT,
+                         link_infos[i].id,
+                         link_infos[i].prog_id, prog_infos[i].id,
+                         (char *)link_infos[i].raw_tracepoint.tp_name,
+                         "sys_enter"))
+                       goto done;
+
        }
 
        /* Check bpf_prog_get_next_id() */
@@ -247,7 +294,52 @@ void test_bpf_obj_id(void)
              "nr_id_found %u(%u)\n",
              nr_id_found, nr_iters);
 
+       /* Check bpf_link_get_next_id() */
+       nr_id_found = 0;
+       next_id = 0;
+       while (!bpf_link_get_next_id(next_id, &next_id)) {
+               struct bpf_link_info link_info;
+               int link_fd, cmp_res;
+
+               info_len = sizeof(link_info);
+               memset(&link_info, 0, info_len);
+
+               link_fd = bpf_link_get_fd_by_id(next_id);
+               if (link_fd < 0 && errno == ENOENT)
+                       /* The bpf_link is in the dead row */
+                       continue;
+               if (CHECK(link_fd < 0, "get-link-fd(next_id)",
+                         "link_fd %d next_id %u errno %d\n",
+                         link_fd, next_id, errno))
+                       break;
+
+               for (i = 0; i < nr_iters; i++)
+                       if (link_infos[i].id == next_id)
+                               break;
+
+               if (i == nr_iters)
+                       continue;
+
+               nr_id_found++;
+
+               err = bpf_obj_get_info_by_fd(link_fd, &link_info, &info_len);
+               cmp_res = memcmp(&link_info, &link_infos[i],
+                               offsetof(struct bpf_link_info, raw_tracepoint));
+               CHECK(err || info_len != sizeof(link_info) || cmp_res,
+                     "check get-link-info(next_id->fd)",
+                     "err %d errno %d info_len %u(%zu) memcmp %d\n",
+                     err, errno, info_len, sizeof(struct bpf_link_info),
+                     cmp_res);
+
+               close(link_fd);
+       }
+       CHECK(nr_id_found != nr_iters,
+             "check total link id found by get_next_id",
+             "nr_id_found %u(%u)\n", nr_id_found, nr_iters);
+
 done:
-       for (i = 0; i < nr_iters; i++)
+       for (i = 0; i < nr_iters; i++) {
+               bpf_link__destroy(links[i]);
                bpf_object__close(objs[i]);
+       }
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_map_in_map.c b/tools/testing/selftests/bpf/prog_tests/btf_map_in_map.c
new file mode 100644 (file)
index 0000000..f7ee8fa
--- /dev/null
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2020 Facebook */
+
+#include <test_progs.h>
+
+#include "test_btf_map_in_map.skel.h"
+
+void test_btf_map_in_map(void)
+{
+       int duration = 0, err, key = 0, val;
+       struct test_btf_map_in_map* skel;
+
+       skel = test_btf_map_in_map__open_and_load();
+       if (CHECK(!skel, "skel_open", "failed to open&load skeleton\n"))
+               return;
+
+       err = test_btf_map_in_map__attach(skel);
+       if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
+               goto cleanup;
+
+       /* inner1 = input, inner2 = input + 1 */
+       val = bpf_map__fd(skel->maps.inner_map1);
+       bpf_map_update_elem(bpf_map__fd(skel->maps.outer_arr), &key, &val, 0);
+       val = bpf_map__fd(skel->maps.inner_map2);
+       bpf_map_update_elem(bpf_map__fd(skel->maps.outer_hash), &key, &val, 0);
+       skel->bss->input = 1;
+       usleep(1);
+
+       bpf_map_lookup_elem(bpf_map__fd(skel->maps.inner_map1), &key, &val);
+       CHECK(val != 1, "inner1", "got %d != exp %d\n", val, 1);
+       bpf_map_lookup_elem(bpf_map__fd(skel->maps.inner_map2), &key, &val);
+       CHECK(val != 2, "inner2", "got %d != exp %d\n", val, 2);
+
+       /* inner1 = input + 1, inner2 = input */
+       val = bpf_map__fd(skel->maps.inner_map2);
+       bpf_map_update_elem(bpf_map__fd(skel->maps.outer_arr), &key, &val, 0);
+       val = bpf_map__fd(skel->maps.inner_map1);
+       bpf_map_update_elem(bpf_map__fd(skel->maps.outer_hash), &key, &val, 0);
+       skel->bss->input = 3;
+       usleep(1);
+
+       bpf_map_lookup_elem(bpf_map__fd(skel->maps.inner_map1), &key, &val);
+       CHECK(val != 4, "inner1", "got %d != exp %d\n", val, 4);
+       bpf_map_lookup_elem(bpf_map__fd(skel->maps.inner_map2), &key, &val);
+       CHECK(val != 3, "inner2", "got %d != exp %d\n", val, 3);
+
+cleanup:
+       test_btf_map_in_map__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/cls_redirect.c b/tools/testing/selftests/bpf/prog_tests/cls_redirect.c
new file mode 100644 (file)
index 0000000..f259085
--- /dev/null
@@ -0,0 +1,456 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+// Copyright (c) 2020 Cloudflare
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <linux/pkt_cls.h>
+
+#include <test_progs.h>
+
+#include "progs/test_cls_redirect.h"
+#include "test_cls_redirect.skel.h"
+
+#define ENCAP_IP INADDR_LOOPBACK
+#define ENCAP_PORT (1234)
+
+struct addr_port {
+       in_port_t port;
+       union {
+               struct in_addr in_addr;
+               struct in6_addr in6_addr;
+       };
+};
+
+struct tuple {
+       int family;
+       struct addr_port src;
+       struct addr_port dst;
+};
+
+static int start_server(const struct sockaddr *addr, socklen_t len, int type)
+{
+       int fd = socket(addr->sa_family, type, 0);
+       if (CHECK_FAIL(fd == -1))
+               return -1;
+       if (CHECK_FAIL(bind(fd, addr, len) == -1))
+               goto err;
+       if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1))
+               goto err;
+
+       return fd;
+
+err:
+       close(fd);
+       return -1;
+}
+
+static int connect_to_server(const struct sockaddr *addr, socklen_t len,
+                            int type)
+{
+       int fd = socket(addr->sa_family, type, 0);
+       if (CHECK_FAIL(fd == -1))
+               return -1;
+       if (CHECK_FAIL(connect(fd, addr, len)))
+               goto err;
+
+       return fd;
+
+err:
+       close(fd);
+       return -1;
+}
+
+static bool fill_addr_port(const struct sockaddr *sa, struct addr_port *ap)
+{
+       const struct sockaddr_in6 *in6;
+       const struct sockaddr_in *in;
+
+       switch (sa->sa_family) {
+       case AF_INET:
+               in = (const struct sockaddr_in *)sa;
+               ap->in_addr = in->sin_addr;
+               ap->port = in->sin_port;
+               return true;
+
+       case AF_INET6:
+               in6 = (const struct sockaddr_in6 *)sa;
+               ap->in6_addr = in6->sin6_addr;
+               ap->port = in6->sin6_port;
+               return true;
+
+       default:
+               return false;
+       }
+}
+
+static bool set_up_conn(const struct sockaddr *addr, socklen_t len, int type,
+                       int *server, int *conn, struct tuple *tuple)
+{
+       struct sockaddr_storage ss;
+       socklen_t slen = sizeof(ss);
+       struct sockaddr *sa = (struct sockaddr *)&ss;
+
+       *server = start_server(addr, len, type);
+       if (*server < 0)
+               return false;
+
+       if (CHECK_FAIL(getsockname(*server, sa, &slen)))
+               goto close_server;
+
+       *conn = connect_to_server(sa, slen, type);
+       if (*conn < 0)
+               goto close_server;
+
+       /* We want to simulate packets arriving at conn, so we have to
+        * swap src and dst.
+        */
+       slen = sizeof(ss);
+       if (CHECK_FAIL(getsockname(*conn, sa, &slen)))
+               goto close_conn;
+
+       if (CHECK_FAIL(!fill_addr_port(sa, &tuple->dst)))
+               goto close_conn;
+
+       slen = sizeof(ss);
+       if (CHECK_FAIL(getpeername(*conn, sa, &slen)))
+               goto close_conn;
+
+       if (CHECK_FAIL(!fill_addr_port(sa, &tuple->src)))
+               goto close_conn;
+
+       tuple->family = ss.ss_family;
+       return true;
+
+close_conn:
+       close(*conn);
+       *conn = -1;
+close_server:
+       close(*server);
+       *server = -1;
+       return false;
+}
+
+static socklen_t prepare_addr(struct sockaddr_storage *addr, int family)
+{
+       struct sockaddr_in *addr4;
+       struct sockaddr_in6 *addr6;
+
+       switch (family) {
+       case AF_INET:
+               addr4 = (struct sockaddr_in *)addr;
+               memset(addr4, 0, sizeof(*addr4));
+               addr4->sin_family = family;
+               addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+               return sizeof(*addr4);
+       case AF_INET6:
+               addr6 = (struct sockaddr_in6 *)addr;
+               memset(addr6, 0, sizeof(*addr6));
+               addr6->sin6_family = family;
+               addr6->sin6_addr = in6addr_loopback;
+               return sizeof(*addr6);
+       default:
+               fprintf(stderr, "Invalid family %d", family);
+               return 0;
+       }
+}
+
+static bool was_decapsulated(struct bpf_prog_test_run_attr *tattr)
+{
+       return tattr->data_size_out < tattr->data_size_in;
+}
+
+enum type {
+       UDP,
+       TCP,
+       __NR_KIND,
+};
+
+enum hops {
+       NO_HOPS,
+       ONE_HOP,
+};
+
+enum flags {
+       NONE,
+       SYN,
+       ACK,
+};
+
+enum conn {
+       KNOWN_CONN,
+       UNKNOWN_CONN,
+};
+
+enum result {
+       ACCEPT,
+       FORWARD,
+};
+
+struct test_cfg {
+       enum type type;
+       enum result result;
+       enum conn conn;
+       enum hops hops;
+       enum flags flags;
+};
+
+static int test_str(void *buf, size_t len, const struct test_cfg *test,
+                   int family)
+{
+       const char *family_str, *type, *conn, *hops, *result, *flags;
+
+       family_str = "IPv4";
+       if (family == AF_INET6)
+               family_str = "IPv6";
+
+       type = "TCP";
+       if (test->type == UDP)
+               type = "UDP";
+
+       conn = "known";
+       if (test->conn == UNKNOWN_CONN)
+               conn = "unknown";
+
+       hops = "no hops";
+       if (test->hops == ONE_HOP)
+               hops = "one hop";
+
+       result = "accept";
+       if (test->result == FORWARD)
+               result = "forward";
+
+       flags = "none";
+       if (test->flags == SYN)
+               flags = "SYN";
+       else if (test->flags == ACK)
+               flags = "ACK";
+
+       return snprintf(buf, len, "%s %s %s %s (%s, flags: %s)", family_str,
+                       type, result, conn, hops, flags);
+}
+
+static struct test_cfg tests[] = {
+       { TCP, ACCEPT, UNKNOWN_CONN, NO_HOPS, SYN },
+       { TCP, ACCEPT, UNKNOWN_CONN, NO_HOPS, ACK },
+       { TCP, FORWARD, UNKNOWN_CONN, ONE_HOP, ACK },
+       { TCP, ACCEPT, KNOWN_CONN, ONE_HOP, ACK },
+       { UDP, ACCEPT, UNKNOWN_CONN, NO_HOPS, NONE },
+       { UDP, FORWARD, UNKNOWN_CONN, ONE_HOP, NONE },
+       { UDP, ACCEPT, KNOWN_CONN, ONE_HOP, NONE },
+};
+
+static void encap_init(encap_headers_t *encap, uint8_t hop_count, uint8_t proto)
+{
+       const uint8_t hlen =
+               (sizeof(struct guehdr) / sizeof(uint32_t)) + hop_count;
+       *encap = (encap_headers_t){
+               .eth = { .h_proto = htons(ETH_P_IP) },
+               .ip = {
+                       .ihl = 5,
+                       .version = 4,
+                       .ttl = IPDEFTTL,
+                       .protocol = IPPROTO_UDP,
+                       .daddr = htonl(ENCAP_IP)
+               },
+               .udp = {
+                       .dest = htons(ENCAP_PORT),
+               },
+               .gue = {
+                       .hlen = hlen,
+                       .proto_ctype = proto
+               },
+               .unigue = {
+                       .hop_count = hop_count
+               },
+       };
+}
+
+static size_t build_input(const struct test_cfg *test, void *const buf,
+                         const struct tuple *tuple)
+{
+       in_port_t sport = tuple->src.port;
+       encap_headers_t encap;
+       struct iphdr ip;
+       struct ipv6hdr ipv6;
+       struct tcphdr tcp;
+       struct udphdr udp;
+       struct in_addr next_hop;
+       uint8_t *p = buf;
+       int proto;
+
+       proto = IPPROTO_IPIP;
+       if (tuple->family == AF_INET6)
+               proto = IPPROTO_IPV6;
+
+       encap_init(&encap, test->hops == ONE_HOP ? 1 : 0, proto);
+       p = mempcpy(p, &encap, sizeof(encap));
+
+       if (test->hops == ONE_HOP) {
+               next_hop = (struct in_addr){ .s_addr = htonl(0x7f000002) };
+               p = mempcpy(p, &next_hop, sizeof(next_hop));
+       }
+
+       proto = IPPROTO_TCP;
+       if (test->type == UDP)
+               proto = IPPROTO_UDP;
+
+       switch (tuple->family) {
+       case AF_INET:
+               ip = (struct iphdr){
+                       .ihl = 5,
+                       .version = 4,
+                       .ttl = IPDEFTTL,
+                       .protocol = proto,
+                       .saddr = tuple->src.in_addr.s_addr,
+                       .daddr = tuple->dst.in_addr.s_addr,
+               };
+               p = mempcpy(p, &ip, sizeof(ip));
+               break;
+       case AF_INET6:
+               ipv6 = (struct ipv6hdr){
+                       .version = 6,
+                       .hop_limit = IPDEFTTL,
+                       .nexthdr = proto,
+                       .saddr = tuple->src.in6_addr,
+                       .daddr = tuple->dst.in6_addr,
+               };
+               p = mempcpy(p, &ipv6, sizeof(ipv6));
+               break;
+       default:
+               return 0;
+       }
+
+       if (test->conn == UNKNOWN_CONN)
+               sport--;
+
+       switch (test->type) {
+       case TCP:
+               tcp = (struct tcphdr){
+                       .source = sport,
+                       .dest = tuple->dst.port,
+               };
+               if (test->flags == SYN)
+                       tcp.syn = true;
+               if (test->flags == ACK)
+                       tcp.ack = true;
+               p = mempcpy(p, &tcp, sizeof(tcp));
+               break;
+       case UDP:
+               udp = (struct udphdr){
+                       .source = sport,
+                       .dest = tuple->dst.port,
+               };
+               p = mempcpy(p, &udp, sizeof(udp));
+               break;
+       default:
+               return 0;
+       }
+
+       return (void *)p - buf;
+}
+
+static void close_fds(int *fds, int n)
+{
+       int i;
+
+       for (i = 0; i < n; i++)
+               if (fds[i] > 0)
+                       close(fds[i]);
+}
+
+void test_cls_redirect(void)
+{
+       struct test_cls_redirect *skel = NULL;
+       struct bpf_prog_test_run_attr tattr = {};
+       int families[] = { AF_INET, AF_INET6 };
+       struct sockaddr_storage ss;
+       struct sockaddr *addr;
+       socklen_t slen;
+       int i, j, err;
+
+       int servers[__NR_KIND][ARRAY_SIZE(families)] = {};
+       int conns[__NR_KIND][ARRAY_SIZE(families)] = {};
+       struct tuple tuples[__NR_KIND][ARRAY_SIZE(families)];
+
+       skel = test_cls_redirect__open();
+       if (CHECK_FAIL(!skel))
+               return;
+
+       skel->rodata->ENCAPSULATION_IP = htonl(ENCAP_IP);
+       skel->rodata->ENCAPSULATION_PORT = htons(ENCAP_PORT);
+
+       if (CHECK_FAIL(test_cls_redirect__load(skel)))
+               goto cleanup;
+
+       addr = (struct sockaddr *)&ss;
+       for (i = 0; i < ARRAY_SIZE(families); i++) {
+               slen = prepare_addr(&ss, families[i]);
+               if (CHECK_FAIL(!slen))
+                       goto cleanup;
+
+               if (CHECK_FAIL(!set_up_conn(addr, slen, SOCK_DGRAM,
+                                           &servers[UDP][i], &conns[UDP][i],
+                                           &tuples[UDP][i])))
+                       goto cleanup;
+
+               if (CHECK_FAIL(!set_up_conn(addr, slen, SOCK_STREAM,
+                                           &servers[TCP][i], &conns[TCP][i],
+                                           &tuples[TCP][i])))
+                       goto cleanup;
+       }
+
+       tattr.prog_fd = bpf_program__fd(skel->progs.cls_redirect);
+       for (i = 0; i < ARRAY_SIZE(tests); i++) {
+               struct test_cfg *test = &tests[i];
+
+               for (j = 0; j < ARRAY_SIZE(families); j++) {
+                       struct tuple *tuple = &tuples[test->type][j];
+                       char input[256];
+                       char tmp[256];
+
+                       test_str(tmp, sizeof(tmp), test, tuple->family);
+                       if (!test__start_subtest(tmp))
+                               continue;
+
+                       tattr.data_out = tmp;
+                       tattr.data_size_out = sizeof(tmp);
+
+                       tattr.data_in = input;
+                       tattr.data_size_in = build_input(test, input, tuple);
+                       if (CHECK_FAIL(!tattr.data_size_in))
+                               continue;
+
+                       err = bpf_prog_test_run_xattr(&tattr);
+                       if (CHECK_FAIL(err))
+                               continue;
+
+                       if (tattr.retval != TC_ACT_REDIRECT) {
+                               PRINT_FAIL("expected TC_ACT_REDIRECT, got %d\n",
+                                          tattr.retval);
+                               continue;
+                       }
+
+                       switch (test->result) {
+                       case ACCEPT:
+                               if (CHECK_FAIL(!was_decapsulated(&tattr)))
+                                       continue;
+                               break;
+                       case FORWARD:
+                               if (CHECK_FAIL(was_decapsulated(&tattr)))
+                                       continue;
+                               break;
+                       default:
+                               PRINT_FAIL("unknown result %d\n", test->result);
+                               continue;
+                       }
+               }
+       }
+
+cleanup:
+       test_cls_redirect__destroy(skel);
+       close_fds((int *)servers, sizeof(servers) / sizeof(servers[0][0]));
+       close_fds((int *)conns, sizeof(conns) / sizeof(conns[0][0]));
+}
index 31e177a..084ed26 100644 (file)
@@ -392,7 +392,7 @@ static struct core_reloc_test_case test_cases[] = {
                .input = STRUCT_TO_CHAR_PTR(core_reloc_existence___minimal) {
                        .a = 42,
                },
-               .input_len = sizeof(struct core_reloc_existence),
+               .input_len = sizeof(struct core_reloc_existence___minimal),
                .output = STRUCT_TO_CHAR_PTR(core_reloc_existence_output) {
                        .a_exists = 1,
                        .b_exists = 0,
diff --git a/tools/testing/selftests/bpf/prog_tests/enable_stats.c b/tools/testing/selftests/bpf/prog_tests/enable_stats.c
new file mode 100644 (file)
index 0000000..2cb2085
--- /dev/null
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "test_enable_stats.skel.h"
+
+void test_enable_stats(void)
+{
+       struct test_enable_stats *skel;
+       int stats_fd, err, prog_fd;
+       struct bpf_prog_info info;
+       __u32 info_len = sizeof(info);
+       int duration = 0;
+
+       skel = test_enable_stats__open_and_load();
+       if (CHECK(!skel, "skel_open_and_load", "skeleton open/load failed\n"))
+               return;
+
+       stats_fd = bpf_enable_stats(BPF_STATS_RUN_TIME);
+       if (CHECK(stats_fd < 0, "get_stats_fd", "failed %d\n", errno)) {
+               test_enable_stats__destroy(skel);
+               return;
+       }
+
+       err = test_enable_stats__attach(skel);
+       if (CHECK(err, "attach_raw_tp", "err %d\n", err))
+               goto cleanup;
+
+       test_enable_stats__detach(skel);
+
+       prog_fd = bpf_program__fd(skel->progs.test_enable_stats);
+       memset(&info, 0, info_len);
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (CHECK(err, "get_prog_info",
+                 "failed to get bpf_prog_info for fd %d\n", prog_fd))
+               goto cleanup;
+       if (CHECK(info.run_time_ns == 0, "check_stats_enabled",
+                 "failed to enable run_time_ns stats\n"))
+               goto cleanup;
+
+       CHECK(info.run_cnt != skel->bss->count, "check_run_cnt_valid",
+             "invalid run_cnt stats\n");
+
+cleanup:
+       test_enable_stats__destroy(skel);
+       close(stats_fd);
+}
index cde463a..c264251 100644 (file)
@@ -5,7 +5,8 @@
 static void test_fexit_bpf2bpf_common(const char *obj_file,
                                      const char *target_obj_file,
                                      int prog_cnt,
-                                     const char **prog_name)
+                                     const char **prog_name,
+                                     bool run_prog)
 {
        struct bpf_object *obj = NULL, *pkt_obj;
        int err, pkt_fd, i;
@@ -18,7 +19,8 @@ static void test_fexit_bpf2bpf_common(const char *obj_file,
 
        err = bpf_prog_load(target_obj_file, BPF_PROG_TYPE_UNSPEC,
                            &pkt_obj, &pkt_fd);
-       if (CHECK(err, "prog_load sched cls", "err %d errno %d\n", err, errno))
+       if (CHECK(err, "tgt_prog_load", "file %s err %d errno %d\n",
+                 target_obj_file, err, errno))
                return;
        DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
                            .attach_prog_fd = pkt_fd,
@@ -33,7 +35,7 @@ static void test_fexit_bpf2bpf_common(const char *obj_file,
 
        obj = bpf_object__open_file(obj_file, &opts);
        if (CHECK(IS_ERR_OR_NULL(obj), "obj_open",
-                 "failed to open fexit_bpf2bpf: %ld\n",
+                 "failed to open %s: %ld\n", obj_file,
                  PTR_ERR(obj)))
                goto close_prog;
 
@@ -49,6 +51,10 @@ static void test_fexit_bpf2bpf_common(const char *obj_file,
                if (CHECK(IS_ERR(link[i]), "attach_trace", "failed to link\n"))
                        goto close_prog;
        }
+
+       if (!run_prog)
+               goto close_prog;
+
        data_map = bpf_object__find_map_by_name(obj, "fexit_bp.bss");
        if (CHECK(!data_map, "find_data_map", "data map not found\n"))
                goto close_prog;
@@ -89,7 +95,7 @@ static void test_target_no_callees(void)
        test_fexit_bpf2bpf_common("./fexit_bpf2bpf_simple.o",
                                  "./test_pkt_md_access.o",
                                  ARRAY_SIZE(prog_name),
-                                 prog_name);
+                                 prog_name, true);
 }
 
 static void test_target_yes_callees(void)
@@ -103,7 +109,7 @@ static void test_target_yes_callees(void)
        test_fexit_bpf2bpf_common("./fexit_bpf2bpf.o",
                                  "./test_pkt_access.o",
                                  ARRAY_SIZE(prog_name),
-                                 prog_name);
+                                 prog_name, true);
 }
 
 static void test_func_replace(void)
@@ -120,7 +126,18 @@ static void test_func_replace(void)
        test_fexit_bpf2bpf_common("./fexit_bpf2bpf.o",
                                  "./test_pkt_access.o",
                                  ARRAY_SIZE(prog_name),
-                                 prog_name);
+                                 prog_name, true);
+}
+
+static void test_func_replace_verify(void)
+{
+       const char *prog_name[] = {
+               "freplace/do_bind",
+       };
+       test_fexit_bpf2bpf_common("./freplace_connect4.o",
+                                 "./connect4_prog.o",
+                                 ARRAY_SIZE(prog_name),
+                                 prog_name, false);
 }
 
 void test_fexit_bpf2bpf(void)
@@ -128,4 +145,5 @@ void test_fexit_bpf2bpf(void)
        test_target_no_callees();
        test_target_yes_callees();
        test_func_replace();
+       test_func_replace_verify();
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/hashmap.c b/tools/testing/selftests/bpf/prog_tests/hashmap.c
new file mode 100644 (file)
index 0000000..428d488
--- /dev/null
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * Tests for libbpf's hashmap.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+#include "test_progs.h"
+#include "bpf/hashmap.h"
+
+static int duration = 0;
+
+static size_t hash_fn(const void *k, void *ctx)
+{
+       return (long)k;
+}
+
+static bool equal_fn(const void *a, const void *b, void *ctx)
+{
+       return (long)a == (long)b;
+}
+
+static inline size_t next_pow_2(size_t n)
+{
+       size_t r = 1;
+
+       while (r < n)
+               r <<= 1;
+       return r;
+}
+
+static inline size_t exp_cap(size_t sz)
+{
+       size_t r = next_pow_2(sz);
+
+       if (sz * 4 / 3 > r)
+               r <<= 1;
+       return r;
+}
+
+#define ELEM_CNT 62
+
+static void test_hashmap_generic(void)
+{
+       struct hashmap_entry *entry, *tmp;
+       int err, bkt, found_cnt, i;
+       long long found_msk;
+       struct hashmap *map;
+
+       map = hashmap__new(hash_fn, equal_fn, NULL);
+       if (CHECK(IS_ERR(map), "hashmap__new",
+                 "failed to create map: %ld\n", PTR_ERR(map)))
+               return;
+
+       for (i = 0; i < ELEM_CNT; i++) {
+               const void *oldk, *k = (const void *)(long)i;
+               void *oldv, *v = (void *)(long)(1024 + i);
+
+               err = hashmap__update(map, k, v, &oldk, &oldv);
+               if (CHECK(err != -ENOENT, "hashmap__update",
+                         "unexpected result: %d\n", err))
+                       goto cleanup;
+
+               if (i % 2) {
+                       err = hashmap__add(map, k, v);
+               } else {
+                       err = hashmap__set(map, k, v, &oldk, &oldv);
+                       if (CHECK(oldk != NULL || oldv != NULL, "check_kv",
+                                 "unexpected k/v: %p=%p\n", oldk, oldv))
+                               goto cleanup;
+               }
+
+               if (CHECK(err, "elem_add", "failed to add k/v %ld = %ld: %d\n",
+                              (long)k, (long)v, err))
+                       goto cleanup;
+
+               if (CHECK(!hashmap__find(map, k, &oldv), "elem_find",
+                         "failed to find key %ld\n", (long)k))
+                       goto cleanup;
+               if (CHECK(oldv != v, "elem_val",
+                         "found value is wrong: %ld\n", (long)oldv))
+                       goto cleanup;
+       }
+
+       if (CHECK(hashmap__size(map) != ELEM_CNT, "hashmap__size",
+                 "invalid map size: %zu\n", hashmap__size(map)))
+               goto cleanup;
+       if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
+                 "hashmap_cap",
+                 "unexpected map capacity: %zu\n", hashmap__capacity(map)))
+               goto cleanup;
+
+       found_msk = 0;
+       hashmap__for_each_entry(map, entry, bkt) {
+               long k = (long)entry->key;
+               long v = (long)entry->value;
+
+               found_msk |= 1ULL << k;
+               if (CHECK(v - k != 1024, "check_kv",
+                         "invalid k/v pair: %ld = %ld\n", k, v))
+                       goto cleanup;
+       }
+       if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1, "elem_cnt",
+                 "not all keys iterated: %llx\n", found_msk))
+               goto cleanup;
+
+       for (i = 0; i < ELEM_CNT; i++) {
+               const void *oldk, *k = (const void *)(long)i;
+               void *oldv, *v = (void *)(long)(256 + i);
+
+               err = hashmap__add(map, k, v);
+               if (CHECK(err != -EEXIST, "hashmap__add",
+                         "unexpected add result: %d\n", err))
+                       goto cleanup;
+
+               if (i % 2)
+                       err = hashmap__update(map, k, v, &oldk, &oldv);
+               else
+                       err = hashmap__set(map, k, v, &oldk, &oldv);
+
+               if (CHECK(err, "elem_upd",
+                         "failed to update k/v %ld = %ld: %d\n",
+                         (long)k, (long)v, err))
+                       goto cleanup;
+               if (CHECK(!hashmap__find(map, k, &oldv), "elem_find",
+                         "failed to find key %ld\n", (long)k))
+                       goto cleanup;
+               if (CHECK(oldv != v, "elem_val",
+                         "found value is wrong: %ld\n", (long)oldv))
+                       goto cleanup;
+       }
+
+       if (CHECK(hashmap__size(map) != ELEM_CNT, "hashmap__size",
+                 "invalid updated map size: %zu\n", hashmap__size(map)))
+               goto cleanup;
+       if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
+                 "hashmap__capacity",
+                 "unexpected map capacity: %zu\n", hashmap__capacity(map)))
+               goto cleanup;
+
+       found_msk = 0;
+       hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
+               long k = (long)entry->key;
+               long v = (long)entry->value;
+
+               found_msk |= 1ULL << k;
+               if (CHECK(v - k != 256, "elem_check",
+                         "invalid updated k/v pair: %ld = %ld\n", k, v))
+                       goto cleanup;
+       }
+       if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1, "elem_cnt",
+                 "not all keys iterated after update: %llx\n", found_msk))
+               goto cleanup;
+
+       found_cnt = 0;
+       hashmap__for_each_key_entry(map, entry, (void *)0) {
+               found_cnt++;
+       }
+       if (CHECK(!found_cnt, "found_cnt",
+                 "didn't find any entries for key 0\n"))
+               goto cleanup;
+
+       found_msk = 0;
+       found_cnt = 0;
+       hashmap__for_each_key_entry_safe(map, entry, tmp, (void *)0) {
+               const void *oldk, *k;
+               void *oldv, *v;
+
+               k = entry->key;
+               v = entry->value;
+
+               found_cnt++;
+               found_msk |= 1ULL << (long)k;
+
+               if (CHECK(!hashmap__delete(map, k, &oldk, &oldv), "elem_del",
+                         "failed to delete k/v %ld = %ld\n",
+                         (long)k, (long)v))
+                       goto cleanup;
+               if (CHECK(oldk != k || oldv != v, "check_old",
+                         "invalid deleted k/v: expected %ld = %ld, got %ld = %ld\n",
+                         (long)k, (long)v, (long)oldk, (long)oldv))
+                       goto cleanup;
+               if (CHECK(hashmap__delete(map, k, &oldk, &oldv), "elem_del",
+                         "unexpectedly deleted k/v %ld = %ld\n",
+                         (long)oldk, (long)oldv))
+                       goto cleanup;
+       }
+
+       if (CHECK(!found_cnt || !found_msk, "found_entries",
+                 "didn't delete any key entries\n"))
+               goto cleanup;
+       if (CHECK(hashmap__size(map) != ELEM_CNT - found_cnt, "elem_cnt",
+                 "invalid updated map size (already deleted: %d): %zu\n",
+                 found_cnt, hashmap__size(map)))
+               goto cleanup;
+       if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
+                 "hashmap__capacity",
+                 "unexpected map capacity: %zu\n", hashmap__capacity(map)))
+               goto cleanup;
+
+       hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
+               const void *oldk, *k;
+               void *oldv, *v;
+
+               k = entry->key;
+               v = entry->value;
+
+               found_cnt++;
+               found_msk |= 1ULL << (long)k;
+
+               if (CHECK(!hashmap__delete(map, k, &oldk, &oldv), "elem_del",
+                         "failed to delete k/v %ld = %ld\n",
+                         (long)k, (long)v))
+                       goto cleanup;
+               if (CHECK(oldk != k || oldv != v, "elem_check",
+                         "invalid old k/v: expect %ld = %ld, got %ld = %ld\n",
+                         (long)k, (long)v, (long)oldk, (long)oldv))
+                       goto cleanup;
+               if (CHECK(hashmap__delete(map, k, &oldk, &oldv), "elem_del",
+                         "unexpectedly deleted k/v %ld = %ld\n",
+                         (long)k, (long)v))
+                       goto cleanup;
+       }
+
+       if (CHECK(found_cnt != ELEM_CNT || found_msk != (1ULL << ELEM_CNT) - 1,
+                 "found_cnt",
+                 "not all keys were deleted: found_cnt:%d, found_msk:%llx\n",
+                 found_cnt, found_msk))
+               goto cleanup;
+       if (CHECK(hashmap__size(map) != 0, "hashmap__size",
+                 "invalid updated map size (already deleted: %d): %zu\n",
+                 found_cnt, hashmap__size(map)))
+               goto cleanup;
+
+       found_cnt = 0;
+       hashmap__for_each_entry(map, entry, bkt) {
+               CHECK(false, "elem_exists",
+                     "unexpected map entries left: %ld = %ld\n",
+                     (long)entry->key, (long)entry->value);
+               goto cleanup;
+       }
+
+       hashmap__clear(map);
+       hashmap__for_each_entry(map, entry, bkt) {
+               CHECK(false, "elem_exists",
+                     "unexpected map entries left: %ld = %ld\n",
+                     (long)entry->key, (long)entry->value);
+               goto cleanup;
+       }
+
+cleanup:
+       hashmap__free(map);
+}
+
+static size_t collision_hash_fn(const void *k, void *ctx)
+{
+       return 0;
+}
+
+static void test_hashmap_multimap(void)
+{
+       void *k1 = (void *)0, *k2 = (void *)1;
+       struct hashmap_entry *entry;
+       struct hashmap *map;
+       long found_msk;
+       int err, bkt;
+
+       /* force collisions */
+       map = hashmap__new(collision_hash_fn, equal_fn, NULL);
+       if (CHECK(IS_ERR(map), "hashmap__new",
+                 "failed to create map: %ld\n", PTR_ERR(map)))
+               return;
+
+       /* set up multimap:
+        * [0] -> 1, 2, 4;
+        * [1] -> 8, 16, 32;
+        */
+       err = hashmap__append(map, k1, (void *)1);
+       if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
+               goto cleanup;
+       err = hashmap__append(map, k1, (void *)2);
+       if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
+               goto cleanup;
+       err = hashmap__append(map, k1, (void *)4);
+       if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
+               goto cleanup;
+
+       err = hashmap__append(map, k2, (void *)8);
+       if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
+               goto cleanup;
+       err = hashmap__append(map, k2, (void *)16);
+       if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
+               goto cleanup;
+       err = hashmap__append(map, k2, (void *)32);
+       if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
+               goto cleanup;
+
+       if (CHECK(hashmap__size(map) != 6, "hashmap_size",
+                 "invalid map size: %zu\n", hashmap__size(map)))
+               goto cleanup;
+
+       /* verify global iteration still works and sees all values */
+       found_msk = 0;
+       hashmap__for_each_entry(map, entry, bkt) {
+               found_msk |= (long)entry->value;
+       }
+       if (CHECK(found_msk != (1 << 6) - 1, "found_msk",
+                 "not all keys iterated: %lx\n", found_msk))
+               goto cleanup;
+
+       /* iterate values for key 1 */
+       found_msk = 0;
+       hashmap__for_each_key_entry(map, entry, k1) {
+               found_msk |= (long)entry->value;
+       }
+       if (CHECK(found_msk != (1 | 2 | 4), "found_msk",
+                 "invalid k1 values: %lx\n", found_msk))
+               goto cleanup;
+
+       /* iterate values for key 2 */
+       found_msk = 0;
+       hashmap__for_each_key_entry(map, entry, k2) {
+               found_msk |= (long)entry->value;
+       }
+       if (CHECK(found_msk != (8 | 16 | 32), "found_msk",
+                 "invalid k2 values: %lx\n", found_msk))
+               goto cleanup;
+
+cleanup:
+       hashmap__free(map);
+}
+
+static void test_hashmap_empty()
+{
+       struct hashmap_entry *entry;
+       int bkt;
+       struct hashmap *map;
+       void *k = (void *)0;
+
+       /* force collisions */
+       map = hashmap__new(hash_fn, equal_fn, NULL);
+       if (CHECK(IS_ERR(map), "hashmap__new",
+                 "failed to create map: %ld\n", PTR_ERR(map)))
+               goto cleanup;
+
+       if (CHECK(hashmap__size(map) != 0, "hashmap__size",
+                 "invalid map size: %zu\n", hashmap__size(map)))
+               goto cleanup;
+       if (CHECK(hashmap__capacity(map) != 0, "hashmap__capacity",
+                 "invalid map capacity: %zu\n", hashmap__capacity(map)))
+               goto cleanup;
+       if (CHECK(hashmap__find(map, k, NULL), "elem_find",
+                 "unexpected find\n"))
+               goto cleanup;
+       if (CHECK(hashmap__delete(map, k, NULL, NULL), "elem_del",
+                 "unexpected delete\n"))
+               goto cleanup;
+
+       hashmap__for_each_entry(map, entry, bkt) {
+               CHECK(false, "elem_found", "unexpected iterated entry\n");
+               goto cleanup;
+       }
+       hashmap__for_each_key_entry(map, entry, k) {
+               CHECK(false, "key_found", "unexpected key entry\n");
+               goto cleanup;
+       }
+
+cleanup:
+       hashmap__free(map);
+}
+
+void test_hashmap()
+{
+       if (test__start_subtest("generic"))
+               test_hashmap_generic();
+       if (test__start_subtest("multimap"))
+               test_hashmap_multimap();
+       if (test__start_subtest("empty"))
+               test_hashmap_empty();
+}
index 16a814e..56d80ad 100644 (file)
@@ -19,15 +19,16 @@ void test_mmap(void)
        const size_t map_sz = roundup_page(sizeof(struct map_data));
        const int zero = 0, one = 1, two = 2, far = 1500;
        const long page_size = sysconf(_SC_PAGE_SIZE);
-       int err, duration = 0, i, data_map_fd;
+       int err, duration = 0, i, data_map_fd, data_map_id, tmp_fd;
        struct bpf_map *data_map, *bss_map;
        void *bss_mmaped = NULL, *map_mmaped = NULL, *tmp1, *tmp2;
        struct test_mmap__bss *bss_data;
+       struct bpf_map_info map_info;
+       __u32 map_info_sz = sizeof(map_info);
        struct map_data *map_data;
        struct test_mmap *skel;
        __u64 val = 0;
 
-
        skel = test_mmap__open_and_load();
        if (CHECK(!skel, "skel_open_and_load", "skeleton open/load failed\n"))
                return;
@@ -36,6 +37,14 @@ void test_mmap(void)
        data_map = skel->maps.data_map;
        data_map_fd = bpf_map__fd(data_map);
 
+       /* get map's ID */
+       memset(&map_info, 0, map_info_sz);
+       err = bpf_obj_get_info_by_fd(data_map_fd, &map_info, &map_info_sz);
+       if (CHECK(err, "map_get_info", "failed %d\n", errno))
+               goto cleanup;
+       data_map_id = map_info.id;
+
+       /* mmap BSS map */
        bss_mmaped = mmap(NULL, bss_sz, PROT_READ | PROT_WRITE, MAP_SHARED,
                          bpf_map__fd(bss_map), 0);
        if (CHECK(bss_mmaped == MAP_FAILED, "bss_mmap",
@@ -98,6 +107,10 @@ void test_mmap(void)
                  "data_map freeze succeeded: err=%d, errno=%d\n", err, errno))
                goto cleanup;
 
+       err = mprotect(map_mmaped, map_sz, PROT_READ);
+       if (CHECK(err, "mprotect_ro", "mprotect to r/o failed %d\n", errno))
+               goto cleanup;
+
        /* unmap R/W mapping */
        err = munmap(map_mmaped, map_sz);
        map_mmaped = NULL;
@@ -111,6 +124,12 @@ void test_mmap(void)
                map_mmaped = NULL;
                goto cleanup;
        }
+       err = mprotect(map_mmaped, map_sz, PROT_WRITE);
+       if (CHECK(!err, "mprotect_wr", "mprotect() succeeded unexpectedly!\n"))
+               goto cleanup;
+       err = mprotect(map_mmaped, map_sz, PROT_EXEC);
+       if (CHECK(!err, "mprotect_ex", "mprotect() succeeded unexpectedly!\n"))
+               goto cleanup;
        map_data = map_mmaped;
 
        /* map/unmap in a loop to test ref counting */
@@ -197,6 +216,45 @@ void test_mmap(void)
        CHECK_FAIL(map_data->val[far] != 3 * 321);
 
        munmap(tmp2, 4 * page_size);
+
+       tmp1 = mmap(NULL, map_sz, PROT_READ, MAP_SHARED, data_map_fd, 0);
+       if (CHECK(tmp1 == MAP_FAILED, "last_mmap", "failed %d\n", errno))
+               goto cleanup;
+
+       test_mmap__destroy(skel);
+       skel = NULL;
+       CHECK_FAIL(munmap(bss_mmaped, bss_sz));
+       bss_mmaped = NULL;
+       CHECK_FAIL(munmap(map_mmaped, map_sz));
+       map_mmaped = NULL;
+
+       /* map should be still held by active mmap */
+       tmp_fd = bpf_map_get_fd_by_id(data_map_id);
+       if (CHECK(tmp_fd < 0, "get_map_by_id", "failed %d\n", errno)) {
+               munmap(tmp1, map_sz);
+               goto cleanup;
+       }
+       close(tmp_fd);
+
+       /* this should release data map finally */
+       munmap(tmp1, map_sz);
+
+       /* we need to wait for RCU grace period */
+       for (i = 0; i < 10000; i++) {
+               __u32 id = data_map_id - 1;
+               if (bpf_map_get_next_id(id, &id) || id > data_map_id)
+                       break;
+               usleep(1);
+       }
+
+       /* should fail to get map FD by non-existing ID */
+       tmp_fd = bpf_map_get_fd_by_id(data_map_id);
+       if (CHECK(tmp_fd >= 0, "get_map_by_id_after",
+                 "unexpectedly succeeded %d\n", tmp_fd)) {
+               close(tmp_fd);
+               goto cleanup;
+       }
+
 cleanup:
        if (bss_mmaped)
                CHECK_FAIL(munmap(bss_mmaped, bss_sz));
index 542240e..e74dc50 100644 (file)
@@ -80,9 +80,6 @@ void test_ns_current_pid_tgid(void)
                  "User pid/tgid %llu BPF pid/tgid %llu\n", id, bss.pid_tgid))
                goto cleanup;
 cleanup:
-       if (!link) {
-               bpf_link__destroy(link);
-               link = NULL;
-       }
+       bpf_link__destroy(link);
        bpf_object__close(obj);
 }
index 1450ea2..a122ce3 100644 (file)
@@ -6,6 +6,11 @@
 #include <test_progs.h>
 #include "bpf/libbpf_internal.h"
 
+/* AddressSanitizer sometimes crashes due to data dereference below, due to
+ * this being mmap()'ed memory. Disable instrumentation with
+ * no_sanitize_address attribute
+ */
+__attribute__((no_sanitize_address))
 static void on_sample(void *ctx, int cpu, void *data, __u32 size)
 {
        int cpu_data = *(int *)data, duration = 0;
index 9d9351d..7131674 100644 (file)
@@ -43,18 +43,18 @@ static struct sec_name_test tests[] = {
        {"lwt_seg6local", {0, BPF_PROG_TYPE_LWT_SEG6LOCAL, 0}, {-EINVAL, 0} },
        {
                "cgroup_skb/ingress",
-               {0, BPF_PROG_TYPE_CGROUP_SKB, 0},
+               {0, BPF_PROG_TYPE_CGROUP_SKB, BPF_CGROUP_INET_INGRESS},
                {0, BPF_CGROUP_INET_INGRESS},
        },
        {
                "cgroup_skb/egress",
-               {0, BPF_PROG_TYPE_CGROUP_SKB, 0},
+               {0, BPF_PROG_TYPE_CGROUP_SKB, BPF_CGROUP_INET_EGRESS},
                {0, BPF_CGROUP_INET_EGRESS},
        },
        {"cgroup/skb", {0, BPF_PROG_TYPE_CGROUP_SKB, 0}, {-EINVAL, 0} },
        {
                "cgroup/sock",
-               {0, BPF_PROG_TYPE_CGROUP_SOCK, 0},
+               {0, BPF_PROG_TYPE_CGROUP_SOCK, BPF_CGROUP_INET_SOCK_CREATE},
                {0, BPF_CGROUP_INET_SOCK_CREATE},
        },
        {
@@ -69,26 +69,38 @@ static struct sec_name_test tests[] = {
        },
        {
                "cgroup/dev",
-               {0, BPF_PROG_TYPE_CGROUP_DEVICE, 0},
+               {0, BPF_PROG_TYPE_CGROUP_DEVICE, BPF_CGROUP_DEVICE},
                {0, BPF_CGROUP_DEVICE},
        },
-       {"sockops", {0, BPF_PROG_TYPE_SOCK_OPS, 0}, {0, BPF_CGROUP_SOCK_OPS} },
+       {
+               "sockops",
+               {0, BPF_PROG_TYPE_SOCK_OPS, BPF_CGROUP_SOCK_OPS},
+               {0, BPF_CGROUP_SOCK_OPS},
+       },
        {
                "sk_skb/stream_parser",
-               {0, BPF_PROG_TYPE_SK_SKB, 0},
+               {0, BPF_PROG_TYPE_SK_SKB, BPF_SK_SKB_STREAM_PARSER},
                {0, BPF_SK_SKB_STREAM_PARSER},
        },
        {
                "sk_skb/stream_verdict",
-               {0, BPF_PROG_TYPE_SK_SKB, 0},
+               {0, BPF_PROG_TYPE_SK_SKB, BPF_SK_SKB_STREAM_VERDICT},
                {0, BPF_SK_SKB_STREAM_VERDICT},
        },
        {"sk_skb", {0, BPF_PROG_TYPE_SK_SKB, 0}, {-EINVAL, 0} },
-       {"sk_msg", {0, BPF_PROG_TYPE_SK_MSG, 0}, {0, BPF_SK_MSG_VERDICT} },
-       {"lirc_mode2", {0, BPF_PROG_TYPE_LIRC_MODE2, 0}, {0, BPF_LIRC_MODE2} },
+       {
+               "sk_msg",
+               {0, BPF_PROG_TYPE_SK_MSG, BPF_SK_MSG_VERDICT},
+               {0, BPF_SK_MSG_VERDICT},
+       },
+       {
+               "lirc_mode2",
+               {0, BPF_PROG_TYPE_LIRC_MODE2, BPF_LIRC_MODE2},
+               {0, BPF_LIRC_MODE2},
+       },
        {
                "flow_dissector",
-               {0, BPF_PROG_TYPE_FLOW_DISSECTOR, 0},
+               {0, BPF_PROG_TYPE_FLOW_DISSECTOR, BPF_FLOW_DISSECTOR},
                {0, BPF_FLOW_DISSECTOR},
        },
        {
@@ -158,17 +170,17 @@ static void test_prog_type_by_name(const struct sec_name_test *test)
                                      &expected_attach_type);
 
        CHECK(rc != test->expected_load.rc, "check_code",
-             "prog: unexpected rc=%d for %s", rc, test->sec_name);
+             "prog: unexpected rc=%d for %s\n", rc, test->sec_name);
 
        if (rc)
                return;
 
        CHECK(prog_type != test->expected_load.prog_type, "check_prog_type",
-             "prog: unexpected prog_type=%d for %s",
+             "prog: unexpected prog_type=%d for %s\n",
              prog_type, test->sec_name);
 
        CHECK(expected_attach_type != test->expected_load.expected_attach_type,
-             "check_attach_type", "prog: unexpected expected_attach_type=%d for %s",
+             "check_attach_type", "prog: unexpected expected_attach_type=%d for %s\n",
              expected_attach_type, test->sec_name);
 }
 
@@ -180,13 +192,13 @@ static void test_attach_type_by_name(const struct sec_name_test *test)
        rc = libbpf_attach_type_by_name(test->sec_name, &attach_type);
 
        CHECK(rc != test->expected_attach.rc, "check_ret",
-             "attach: unexpected rc=%d for %s", rc, test->sec_name);
+             "attach: unexpected rc=%d for %s\n", rc, test->sec_name);
 
        if (rc)
                return;
 
        CHECK(attach_type != test->expected_attach.attach_type,
-             "check_attach_type", "attach: unexpected attach_type=%d for %s",
+             "check_attach_type", "attach: unexpected attach_type=%d for %s\n",
              attach_type, test->sec_name);
 }
 
index d572e1a..47fa04a 100644 (file)
@@ -20,6 +20,7 @@
 #define CONNECT_PORT 4321
 #define TEST_DADDR (0xC0A80203)
 #define NS_SELF "/proc/self/ns/net"
+#define SERVER_MAP_PATH "/sys/fs/bpf/tc/globals/server_map"
 
 static const struct timeval timeo_sec = { .tv_sec = 3 };
 static const size_t timeo_optlen = sizeof(timeo_sec);
@@ -265,6 +266,7 @@ void test_sk_assign(void)
                TEST("ipv6 udp addr redir", AF_INET6, SOCK_DGRAM, true),
        };
        int server = -1;
+       int server_map;
        int self_net;
 
        self_net = open(NS_SELF, O_RDONLY);
@@ -278,9 +280,17 @@ void test_sk_assign(void)
                goto cleanup;
        }
 
+       server_map = bpf_obj_get(SERVER_MAP_PATH);
+       if (CHECK_FAIL(server_map < 0)) {
+               perror("Unable to open " SERVER_MAP_PATH);
+               goto cleanup;
+       }
+
        for (int i = 0; i < ARRAY_SIZE(tests) && !READ_ONCE(stop); i++) {
                struct test_sk_cfg *test = &tests[i];
                const struct sockaddr *addr;
+               const int zero = 0;
+               int err;
 
                if (!test__start_subtest(test->name))
                        continue;
@@ -288,7 +298,13 @@ void test_sk_assign(void)
                addr = (const struct sockaddr *)test->addr;
                server = start_server(addr, test->len, test->type);
                if (server == -1)
-                       goto cleanup;
+                       goto close;
+
+               err = bpf_map_update_elem(server_map, &zero, &server, BPF_ANY);
+               if (CHECK_FAIL(err)) {
+                       perror("Unable to update server_map");
+                       goto close;
+               }
 
                /* connect to unbound ports */
                prepare_addr(test->addr, test->family, CONNECT_PORT,
@@ -302,7 +318,10 @@ void test_sk_assign(void)
 
 close:
        close(server);
+       close(server_map);
 cleanup:
+       if (CHECK_FAIL(unlink(SERVER_MAP_PATH)))
+               perror("Unable to unlink " SERVER_MAP_PATH);
        if (CHECK_FAIL(setns(self_net, CLONE_NEWNET)))
                perror("Failed to setns("NS_SELF")");
        close(self_net);
index 1e4c258..b17eb20 100644 (file)
 
 char *CMD_ARGS[] = {"true", NULL};
 
-int heap_mprotect(void)
+#define GET_PAGE_ADDR(ADDR, PAGE_SIZE)                                 \
+       (char *)(((unsigned long) (ADDR + PAGE_SIZE)) & ~(PAGE_SIZE-1))
+
+int stack_mprotect(void)
 {
        void *buf;
        long sz;
@@ -25,12 +28,9 @@ int heap_mprotect(void)
        if (sz < 0)
                return sz;
 
-       buf = memalign(sz, 2 * sz);
-       if (buf == NULL)
-               return -ENOMEM;
-
-       ret = mprotect(buf, sz, PROT_READ | PROT_WRITE | PROT_EXEC);
-       free(buf);
+       buf = alloca(sz * 3);
+       ret = mprotect(GET_PAGE_ADDR(buf, sz), sz,
+                      PROT_READ | PROT_WRITE | PROT_EXEC);
        return ret;
 }
 
@@ -73,8 +73,8 @@ void test_test_lsm(void)
 
        skel->bss->monitored_pid = getpid();
 
-       err = heap_mprotect();
-       if (CHECK(errno != EPERM, "heap_mprotect", "want errno=EPERM, got %d\n",
+       err = stack_mprotect();
+       if (CHECK(errno != EPERM, "stack_mprotect", "want err=EPERM, got %d\n",
                  errno))
                goto close_prog;
 
index 05b294d..15ef353 100644 (file)
@@ -6,19 +6,34 @@
 
 void test_xdp_attach(void)
 {
+       __u32 duration = 0, id1, id2, id0 = 0, len;
        struct bpf_object *obj1, *obj2, *obj3;
        const char *file = "./test_xdp.o";
+       struct bpf_prog_info info = {};
        int err, fd1, fd2, fd3;
-       __u32 duration = 0;
        DECLARE_LIBBPF_OPTS(bpf_xdp_set_link_opts, opts,
                            .old_fd = -1);
 
+       len = sizeof(info);
+
        err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj1, &fd1);
        if (CHECK_FAIL(err))
                return;
+       err = bpf_obj_get_info_by_fd(fd1, &info, &len);
+       if (CHECK_FAIL(err))
+               goto out_1;
+       id1 = info.id;
+
        err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj2, &fd2);
        if (CHECK_FAIL(err))
                goto out_1;
+
+       memset(&info, 0, sizeof(info));
+       err = bpf_obj_get_info_by_fd(fd2, &info, &len);
+       if (CHECK_FAIL(err))
+               goto out_2;
+       id2 = info.id;
+
        err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj3, &fd3);
        if (CHECK_FAIL(err))
                goto out_2;
@@ -28,6 +43,11 @@ void test_xdp_attach(void)
        if (CHECK(err, "load_ok", "initial load failed"))
                goto out_close;
 
+       err = bpf_get_link_xdp_id(IFINDEX_LO, &id0, 0);
+       if (CHECK(err || id0 != id1, "id1_check",
+                 "loaded prog id %u != id1 %u, err %d", id0, id1, err))
+               goto out_close;
+
        err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd2, XDP_FLAGS_REPLACE,
                                       &opts);
        if (CHECK(!err, "load_fail", "load with expected id didn't fail"))
@@ -37,6 +57,10 @@ void test_xdp_attach(void)
        err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd2, 0, &opts);
        if (CHECK(err, "replace_ok", "replace valid old_fd failed"))
                goto out;
+       err = bpf_get_link_xdp_id(IFINDEX_LO, &id0, 0);
+       if (CHECK(err || id0 != id2, "id2_check",
+                 "loaded prog id %u != id2 %u, err %d", id0, id2, err))
+               goto out_close;
 
        err = bpf_set_link_xdp_fd_opts(IFINDEX_LO, fd3, 0, &opts);
        if (CHECK(!err, "replace_fail", "replace invalid old_fd didn't fail"))
@@ -51,6 +75,10 @@ void test_xdp_attach(void)
        if (CHECK(err, "remove_ok", "remove valid old_fd failed"))
                goto out;
 
+       err = bpf_get_link_xdp_id(IFINDEX_LO, &id0, 0);
+       if (CHECK(err || id0 != 0, "unload_check",
+                 "loaded prog id %u != 0, err %d", id0, err))
+               goto out_close;
 out:
        bpf_set_link_xdp_fd(IFINDEX_LO, -1, 0);
 out_close:
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_info.c b/tools/testing/selftests/bpf/prog_tests/xdp_info.c
new file mode 100644 (file)
index 0000000..d2d7a28
--- /dev/null
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/if_link.h>
+#include <test_progs.h>
+
+#define IFINDEX_LO 1
+
+void test_xdp_info(void)
+{
+       __u32 len = sizeof(struct bpf_prog_info), duration = 0, prog_id;
+       const char *file = "./xdp_dummy.o";
+       struct bpf_prog_info info = {};
+       struct bpf_object *obj;
+       int err, prog_fd;
+
+       /* Get prog_id for XDP_ATTACHED_NONE mode */
+
+       err = bpf_get_link_xdp_id(IFINDEX_LO, &prog_id, 0);
+       if (CHECK(err, "get_xdp_none", "errno=%d\n", errno))
+               return;
+       if (CHECK(prog_id, "prog_id_none", "unexpected prog_id=%u\n", prog_id))
+               return;
+
+       err = bpf_get_link_xdp_id(IFINDEX_LO, &prog_id, XDP_FLAGS_SKB_MODE);
+       if (CHECK(err, "get_xdp_none_skb", "errno=%d\n", errno))
+               return;
+       if (CHECK(prog_id, "prog_id_none_skb", "unexpected prog_id=%u\n",
+                 prog_id))
+               return;
+
+       /* Setup prog */
+
+       err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
+       if (CHECK_FAIL(err))
+               return;
+
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &len);
+       if (CHECK(err, "get_prog_info", "errno=%d\n", errno))
+               goto out_close;
+
+       err = bpf_set_link_xdp_fd(IFINDEX_LO, prog_fd, XDP_FLAGS_SKB_MODE);
+       if (CHECK(err, "set_xdp_skb", "errno=%d\n", errno))
+               goto out_close;
+
+       /* Get prog_id for single prog mode */
+
+       err = bpf_get_link_xdp_id(IFINDEX_LO, &prog_id, 0);
+       if (CHECK(err, "get_xdp", "errno=%d\n", errno))
+               goto out;
+       if (CHECK(prog_id != info.id, "prog_id", "prog_id not available\n"))
+               goto out;
+
+       err = bpf_get_link_xdp_id(IFINDEX_LO, &prog_id, XDP_FLAGS_SKB_MODE);
+       if (CHECK(err, "get_xdp_skb", "errno=%d\n", errno))
+               goto out;
+       if (CHECK(prog_id != info.id, "prog_id_skb", "prog_id not available\n"))
+               goto out;
+
+       err = bpf_get_link_xdp_id(IFINDEX_LO, &prog_id, XDP_FLAGS_DRV_MODE);
+       if (CHECK(err, "get_xdp_drv", "errno=%d\n", errno))
+               goto out;
+       if (CHECK(prog_id, "prog_id_drv", "unexpected prog_id=%u\n", prog_id))
+               goto out;
+
+out:
+       bpf_set_link_xdp_fd(IFINDEX_LO, -1, 0);
+out_close:
+       bpf_object__close(obj);
+}
index 7508511..c2c85c3 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/in.h>
 #include <linux/in6.h>
 #include <sys/socket.h>
+#include <netinet/tcp.h>
 
 #include <bpf/bpf_helpers.h>
 #include <bpf/bpf_endian.h>
 #define DST_REWRITE_IP4                0x7f000001U
 #define DST_REWRITE_PORT4      4444
 
+#ifndef TCP_CA_NAME_MAX
+#define TCP_CA_NAME_MAX 16
+#endif
+
 int _version SEC("version") = 1;
 
+__attribute__ ((noinline))
+int do_bind(struct bpf_sock_addr *ctx)
+{
+       struct sockaddr_in sa = {};
+
+       sa.sin_family = AF_INET;
+       sa.sin_port = bpf_htons(0);
+       sa.sin_addr.s_addr = bpf_htonl(SRC_REWRITE_IP4);
+
+       if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
+               return 0;
+
+       return 1;
+}
+
+static __inline int verify_cc(struct bpf_sock_addr *ctx,
+                             char expected[TCP_CA_NAME_MAX])
+{
+       char buf[TCP_CA_NAME_MAX];
+       int i;
+
+       if (bpf_getsockopt(ctx, SOL_TCP, TCP_CONGESTION, &buf, sizeof(buf)))
+               return 1;
+
+       for (i = 0; i < TCP_CA_NAME_MAX; i++) {
+               if (buf[i] != expected[i])
+                       return 1;
+               if (buf[i] == 0)
+                       break;
+       }
+
+       return 0;
+}
+
+static __inline int set_cc(struct bpf_sock_addr *ctx)
+{
+       char reno[TCP_CA_NAME_MAX] = "reno";
+       char cubic[TCP_CA_NAME_MAX] = "cubic";
+
+       if (bpf_setsockopt(ctx, SOL_TCP, TCP_CONGESTION, &reno, sizeof(reno)))
+               return 1;
+       if (verify_cc(ctx, reno))
+               return 1;
+
+       if (bpf_setsockopt(ctx, SOL_TCP, TCP_CONGESTION, &cubic, sizeof(cubic)))
+               return 1;
+       if (verify_cc(ctx, cubic))
+               return 1;
+
+       return 0;
+}
+
 SEC("cgroup/connect4")
 int connect_v4_prog(struct bpf_sock_addr *ctx)
 {
        struct bpf_sock_tuple tuple = {};
-       struct sockaddr_in sa;
        struct bpf_sock *sk;
 
        /* Verify that new destination is available. */
@@ -52,21 +108,15 @@ int connect_v4_prog(struct bpf_sock_addr *ctx)
 
        bpf_sk_release(sk);
 
+       /* Rewrite congestion control. */
+       if (ctx->type == SOCK_STREAM && set_cc(ctx))
+               return 0;
+
        /* Rewrite destination. */
        ctx->user_ip4 = bpf_htonl(DST_REWRITE_IP4);
        ctx->user_port = bpf_htons(DST_REWRITE_PORT4);
 
-       /* Rewrite source. */
-       memset(&sa, 0, sizeof(sa));
-
-       sa.sin_family = AF_INET;
-       sa.sin_port = bpf_htons(0);
-       sa.sin_addr.s_addr = bpf_htonl(SRC_REWRITE_IP4);
-
-       if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
-               return 0;
-
-       return 1;
+       return do_bind(ctx) ? 1 : 0;
 }
 
 char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/freplace_connect4.c b/tools/testing/selftests/bpf/progs/freplace_connect4.c
new file mode 100644 (file)
index 0000000..a0ae842
--- /dev/null
@@ -0,0 +1,18 @@
+#include <linux/stddef.h>
+#include <linux/ipv6.h>
+#include <linux/bpf.h>
+#include <linux/in.h>
+#include <sys/socket.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+
+SEC("freplace/do_bind")
+int new_do_bind(struct bpf_sock_addr *ctx)
+{
+  struct sockaddr_in sa = {};
+
+  bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa));
+  return 0;
+}
+
+char _license[] SEC("license") = "GPL";
index a4e3c22..b4598d4 100644 (file)
@@ -23,12 +23,12 @@ int BPF_PROG(test_int_hook, struct vm_area_struct *vma,
                return ret;
 
        __u32 pid = bpf_get_current_pid_tgid() >> 32;
-       int is_heap = 0;
+       int is_stack = 0;
 
-       is_heap = (vma->vm_start >= vma->vm_mm->start_brk &&
-                  vma->vm_end <= vma->vm_mm->brk);
+       is_stack = (vma->vm_start <= vma->vm_mm->start_stack &&
+                   vma->vm_end >= vma->vm_mm->start_stack);
 
-       if (is_heap && monitored_pid == pid) {
+       if (is_stack && monitored_pid == pid) {
                mprotect_count++;
                ret = -EPERM;
        }
index 88b0566..31538c9 100644 (file)
@@ -20,20 +20,12 @@ struct bpf_map_def SEC("maps") btf_map = {
 
 BPF_ANNOTATE_KV_PAIR(btf_map, int, struct ipv_counts);
 
-struct dummy_tracepoint_args {
-       unsigned long long pad;
-       struct sock *sock;
-};
-
 __attribute__((noinline))
-int test_long_fname_2(struct dummy_tracepoint_args *arg)
+int test_long_fname_2(void)
 {
        struct ipv_counts *counts;
        int key = 0;
 
-       if (!arg->sock)
-               return 0;
-
        counts = bpf_map_lookup_elem(&btf_map, &key);
        if (!counts)
                return 0;
@@ -44,15 +36,15 @@ int test_long_fname_2(struct dummy_tracepoint_args *arg)
 }
 
 __attribute__((noinline))
-int test_long_fname_1(struct dummy_tracepoint_args *arg)
+int test_long_fname_1(void)
 {
-       return test_long_fname_2(arg);
+       return test_long_fname_2();
 }
 
 SEC("dummy_tracepoint")
-int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+int _dummy_tracepoint(void *arg)
 {
-       return test_long_fname_1(arg);
+       return test_long_fname_1();
 }
 
 char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_btf_map_in_map.c b/tools/testing/selftests/bpf/progs/test_btf_map_in_map.c
new file mode 100644 (file)
index 0000000..e509379
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2020 Facebook */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct inner_map {
+       __uint(type, BPF_MAP_TYPE_ARRAY);
+       __uint(max_entries, 1);
+       __type(key, int);
+       __type(value, int);
+} inner_map1 SEC(".maps"),
+  inner_map2 SEC(".maps");
+
+struct outer_arr {
+       __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
+       __uint(max_entries, 3);
+       __uint(key_size, sizeof(int));
+       __uint(value_size, sizeof(int));
+       /* it's possible to use anonymous struct as inner map definition here */
+       __array(values, struct {
+               __uint(type, BPF_MAP_TYPE_ARRAY);
+               /* changing max_entries to 2 will fail during load
+                * due to incompatibility with inner_map definition */
+               __uint(max_entries, 1);
+               __type(key, int);
+               __type(value, int);
+       });
+} outer_arr SEC(".maps") = {
+       /* (void *) cast is necessary because we didn't use `struct inner_map`
+        * in __inner(values, ...)
+        * Actually, a conscious effort is required to screw up initialization
+        * of inner map slots, which is a great thing!
+        */
+       .values = { (void *)&inner_map1, 0, (void *)&inner_map2 },
+};
+
+struct outer_hash {
+       __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
+       __uint(max_entries, 5);
+       __uint(key_size, sizeof(int));
+       /* Here everything works flawlessly due to reuse of struct inner_map
+        * and compiler will complain at the attempt to use non-inner_map
+        * references below. This is great experience.
+        */
+       __array(values, struct inner_map);
+} outer_hash SEC(".maps") = {
+       .values = {
+               [0] = &inner_map2,
+               [4] = &inner_map1,
+       },
+};
+
+int input = 0;
+
+SEC("raw_tp/sys_enter")
+int handle__sys_enter(void *ctx)
+{
+       struct inner_map *inner_map;
+       int key = 0, val;
+
+       inner_map = bpf_map_lookup_elem(&outer_arr, &key);
+       if (!inner_map)
+               return 1;
+       val = input;
+       bpf_map_update_elem(inner_map, &key, &val, 0);
+
+       inner_map = bpf_map_lookup_elem(&outer_hash, &key);
+       if (!inner_map)
+               return 1;
+       val = input + 1;
+       bpf_map_update_elem(inner_map, &key, &val, 0);
+
+       return 0;
+}
+
+char _license[] SEC("license") = "GPL";
index a924e53..6c55601 100644 (file)
@@ -28,20 +28,12 @@ struct {
        __type(value, struct ipv_counts);
 } btf_map SEC(".maps");
 
-struct dummy_tracepoint_args {
-       unsigned long long pad;
-       struct sock *sock;
-};
-
 __attribute__((noinline))
-int test_long_fname_2(struct dummy_tracepoint_args *arg)
+int test_long_fname_2(void)
 {
        struct ipv_counts *counts;
        int key = 0;
 
-       if (!arg->sock)
-               return 0;
-
        counts = bpf_map_lookup_elem(&btf_map, &key);
        if (!counts)
                return 0;
@@ -57,15 +49,15 @@ int test_long_fname_2(struct dummy_tracepoint_args *arg)
 }
 
 __attribute__((noinline))
-int test_long_fname_1(struct dummy_tracepoint_args *arg)
+int test_long_fname_1(void)
 {
-       return test_long_fname_2(arg);
+       return test_long_fname_2();
 }
 
 SEC("dummy_tracepoint")
-int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+int _dummy_tracepoint(void *arg)
 {
-       return test_long_fname_1(arg);
+       return test_long_fname_1();
 }
 
 char _license[] SEC("license") = "GPL";
index 983aedd..506da7f 100644 (file)
@@ -17,20 +17,12 @@ struct bpf_map_def SEC("maps") btf_map = {
        .max_entries = 4,
 };
 
-struct dummy_tracepoint_args {
-       unsigned long long pad;
-       struct sock *sock;
-};
-
 __attribute__((noinline))
-int test_long_fname_2(struct dummy_tracepoint_args *arg)
+int test_long_fname_2(void)
 {
        struct ipv_counts *counts;
        int key = 0;
 
-       if (!arg->sock)
-               return 0;
-
        counts = bpf_map_lookup_elem(&btf_map, &key);
        if (!counts)
                return 0;
@@ -41,15 +33,15 @@ int test_long_fname_2(struct dummy_tracepoint_args *arg)
 }
 
 __attribute__((noinline))
-int test_long_fname_1(struct dummy_tracepoint_args *arg)
+int test_long_fname_1(void)
 {
-       return test_long_fname_2(arg);
+       return test_long_fname_2();
 }
 
 SEC("dummy_tracepoint")
-int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
+int _dummy_tracepoint(void *arg)
 {
-       return test_long_fname_1(arg);
+       return test_long_fname_1();
 }
 
 char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_cls_redirect.c b/tools/testing/selftests/bpf/progs/test_cls_redirect.c
new file mode 100644 (file)
index 0000000..1668b99
--- /dev/null
@@ -0,0 +1,1058 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+// Copyright (c) 2019, 2020 Cloudflare
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <linux/bpf.h>
+#include <linux/icmp.h>
+#include <linux/icmpv6.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/pkt_cls.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+
+#include "test_cls_redirect.h"
+
+#define offsetofend(TYPE, MEMBER) \
+       (offsetof(TYPE, MEMBER) + sizeof((((TYPE *)0)->MEMBER)))
+
+#define IP_OFFSET_MASK (0x1FFF)
+#define IP_MF (0x2000)
+
+char _license[] SEC("license") = "Dual BSD/GPL";
+
+/**
+ * Destination port and IP used for UDP encapsulation.
+ */
+static volatile const __be16 ENCAPSULATION_PORT;
+static volatile const __be32 ENCAPSULATION_IP;
+
+typedef struct {
+       uint64_t processed_packets_total;
+       uint64_t l3_protocol_packets_total_ipv4;
+       uint64_t l3_protocol_packets_total_ipv6;
+       uint64_t l4_protocol_packets_total_tcp;
+       uint64_t l4_protocol_packets_total_udp;
+       uint64_t accepted_packets_total_syn;
+       uint64_t accepted_packets_total_syn_cookies;
+       uint64_t accepted_packets_total_last_hop;
+       uint64_t accepted_packets_total_icmp_echo_request;
+       uint64_t accepted_packets_total_established;
+       uint64_t forwarded_packets_total_gue;
+       uint64_t forwarded_packets_total_gre;
+
+       uint64_t errors_total_unknown_l3_proto;
+       uint64_t errors_total_unknown_l4_proto;
+       uint64_t errors_total_malformed_ip;
+       uint64_t errors_total_fragmented_ip;
+       uint64_t errors_total_malformed_icmp;
+       uint64_t errors_total_unwanted_icmp;
+       uint64_t errors_total_malformed_icmp_pkt_too_big;
+       uint64_t errors_total_malformed_tcp;
+       uint64_t errors_total_malformed_udp;
+       uint64_t errors_total_icmp_echo_replies;
+       uint64_t errors_total_malformed_encapsulation;
+       uint64_t errors_total_encap_adjust_failed;
+       uint64_t errors_total_encap_buffer_too_small;
+       uint64_t errors_total_redirect_loop;
+} metrics_t;
+
+typedef enum {
+       INVALID = 0,
+       UNKNOWN,
+       ECHO_REQUEST,
+       SYN,
+       SYN_COOKIE,
+       ESTABLISHED,
+} verdict_t;
+
+typedef struct {
+       uint16_t src, dst;
+} flow_ports_t;
+
+_Static_assert(
+       sizeof(flow_ports_t) !=
+               offsetofend(struct bpf_sock_tuple, ipv4.dport) -
+                       offsetof(struct bpf_sock_tuple, ipv4.sport) - 1,
+       "flow_ports_t must match sport and dport in struct bpf_sock_tuple");
+_Static_assert(
+       sizeof(flow_ports_t) !=
+               offsetofend(struct bpf_sock_tuple, ipv6.dport) -
+                       offsetof(struct bpf_sock_tuple, ipv6.sport) - 1,
+       "flow_ports_t must match sport and dport in struct bpf_sock_tuple");
+
+typedef int ret_t;
+
+/* This is a bit of a hack. We need a return value which allows us to
+ * indicate that the regular flow of the program should continue,
+ * while allowing functions to use XDP_PASS and XDP_DROP, etc.
+ */
+static const ret_t CONTINUE_PROCESSING = -1;
+
+/* Convenience macro to call functions which return ret_t.
+ */
+#define MAYBE_RETURN(x)                           \
+       do {                                      \
+               ret_t __ret = x;                  \
+               if (__ret != CONTINUE_PROCESSING) \
+                       return __ret;             \
+       } while (0)
+
+/* Linux packet pointers are either aligned to NET_IP_ALIGN (aka 2 bytes),
+ * or not aligned if the arch supports efficient unaligned access.
+ *
+ * Since the verifier ensures that eBPF packet accesses follow these rules,
+ * we can tell LLVM to emit code as if we always had a larger alignment.
+ * It will yell at us if we end up on a platform where this is not valid.
+ */
+typedef uint8_t *net_ptr __attribute__((align_value(8)));
+
+typedef struct buf {
+       struct __sk_buff *skb;
+       net_ptr head;
+       /* NB: tail musn't have alignment other than 1, otherwise
+       * LLVM will go and eliminate code, e.g. when checking packet lengths.
+       */
+       uint8_t *const tail;
+} buf_t;
+
+static size_t buf_off(const buf_t *buf)
+{
+       /* Clang seems to optimize constructs like
+        *    a - b + c
+        * if c is known:
+        *    r? = c
+        *    r? -= b
+        *    r? += a
+        *
+        * This is a problem if a and b are packet pointers,
+        * since the verifier allows subtracting two pointers to
+        * get a scalar, but not a scalar and a pointer.
+        *
+        * Use inline asm to break this optimization.
+        */
+       size_t off = (size_t)buf->head;
+       asm("%0 -= %1" : "+r"(off) : "r"(buf->skb->data));
+       return off;
+}
+
+static bool buf_copy(buf_t *buf, void *dst, size_t len)
+{
+       if (bpf_skb_load_bytes(buf->skb, buf_off(buf), dst, len)) {
+               return false;
+       }
+
+       buf->head += len;
+       return true;
+}
+
+static bool buf_skip(buf_t *buf, const size_t len)
+{
+       /* Check whether off + len is valid in the non-linear part. */
+       if (buf_off(buf) + len > buf->skb->len) {
+               return false;
+       }
+
+       buf->head += len;
+       return true;
+}
+
+/* Returns a pointer to the start of buf, or NULL if len is
+ * larger than the remaining data. Consumes len bytes on a successful
+ * call.
+ *
+ * If scratch is not NULL, the function will attempt to load non-linear
+ * data via bpf_skb_load_bytes. On success, scratch is returned.
+ */
+static void *buf_assign(buf_t *buf, const size_t len, void *scratch)
+{
+       if (buf->head + len > buf->tail) {
+               if (scratch == NULL) {
+                       return NULL;
+               }
+
+               return buf_copy(buf, scratch, len) ? scratch : NULL;
+       }
+
+       void *ptr = buf->head;
+       buf->head += len;
+       return ptr;
+}
+
+static bool pkt_skip_ipv4_options(buf_t *buf, const struct iphdr *ipv4)
+{
+       if (ipv4->ihl <= 5) {
+               return true;
+       }
+
+       return buf_skip(buf, (ipv4->ihl - 5) * 4);
+}
+
+static bool ipv4_is_fragment(const struct iphdr *ip)
+{
+       uint16_t frag_off = ip->frag_off & bpf_htons(IP_OFFSET_MASK);
+       return (ip->frag_off & bpf_htons(IP_MF)) != 0 || frag_off > 0;
+}
+
+static struct iphdr *pkt_parse_ipv4(buf_t *pkt, struct iphdr *scratch)
+{
+       struct iphdr *ipv4 = buf_assign(pkt, sizeof(*ipv4), scratch);
+       if (ipv4 == NULL) {
+               return NULL;
+       }
+
+       if (ipv4->ihl < 5) {
+               return NULL;
+       }
+
+       if (!pkt_skip_ipv4_options(pkt, ipv4)) {
+               return NULL;
+       }
+
+       return ipv4;
+}
+
+/* Parse the L4 ports from a packet, assuming a layout like TCP or UDP. */
+static bool pkt_parse_icmp_l4_ports(buf_t *pkt, flow_ports_t *ports)
+{
+       if (!buf_copy(pkt, ports, sizeof(*ports))) {
+               return false;
+       }
+
+       /* Ports in the L4 headers are reversed, since we are parsing an ICMP
+        * payload which is going towards the eyeball.
+        */
+       uint16_t dst = ports->src;
+       ports->src = ports->dst;
+       ports->dst = dst;
+       return true;
+}
+
+static uint16_t pkt_checksum_fold(uint32_t csum)
+{
+       /* The highest reasonable value for an IPv4 header
+        * checksum requires two folds, so we just do that always.
+        */
+       csum = (csum & 0xffff) + (csum >> 16);
+       csum = (csum & 0xffff) + (csum >> 16);
+       return (uint16_t)~csum;
+}
+
+static void pkt_ipv4_checksum(struct iphdr *iph)
+{
+       iph->check = 0;
+
+       /* An IP header without options is 20 bytes. Two of those
+        * are the checksum, which we always set to zero. Hence,
+        * the maximum accumulated value is 18 / 2 * 0xffff = 0x8fff7,
+        * which fits in 32 bit.
+        */
+       _Static_assert(sizeof(struct iphdr) == 20, "iphdr must be 20 bytes");
+       uint32_t acc = 0;
+       uint16_t *ipw = (uint16_t *)iph;
+
+#pragma clang loop unroll(full)
+       for (size_t i = 0; i < sizeof(struct iphdr) / 2; i++) {
+               acc += ipw[i];
+       }
+
+       iph->check = pkt_checksum_fold(acc);
+}
+
+static bool pkt_skip_ipv6_extension_headers(buf_t *pkt,
+                                           const struct ipv6hdr *ipv6,
+                                           uint8_t *upper_proto,
+                                           bool *is_fragment)
+{
+       /* We understand five extension headers.
+        * https://tools.ietf.org/html/rfc8200#section-4.1 states that all
+        * headers should occur once, except Destination Options, which may
+        * occur twice. Hence we give up after 6 headers.
+        */
+       struct {
+               uint8_t next;
+               uint8_t len;
+       } exthdr = {
+               .next = ipv6->nexthdr,
+       };
+       *is_fragment = false;
+
+#pragma clang loop unroll(full)
+       for (int i = 0; i < 6; i++) {
+               switch (exthdr.next) {
+               case IPPROTO_FRAGMENT:
+                       *is_fragment = true;
+                       /* NB: We don't check that hdrlen == 0 as per spec. */
+                       /* fallthrough; */
+
+               case IPPROTO_HOPOPTS:
+               case IPPROTO_ROUTING:
+               case IPPROTO_DSTOPTS:
+               case IPPROTO_MH:
+                       if (!buf_copy(pkt, &exthdr, sizeof(exthdr))) {
+                               return false;
+                       }
+
+                       /* hdrlen is in 8-octet units, and excludes the first 8 octets. */
+                       if (!buf_skip(pkt,
+                                     (exthdr.len + 1) * 8 - sizeof(exthdr))) {
+                               return false;
+                       }
+
+                       /* Decode next header */
+                       break;
+
+               default:
+                       /* The next header is not one of the known extension
+                        * headers, treat it as the upper layer header.
+                        *
+                        * This handles IPPROTO_NONE.
+                        *
+                        * Encapsulating Security Payload (50) and Authentication
+                        * Header (51) also end up here (and will trigger an
+                        * unknown proto error later). They have a custom header
+                        * format and seem too esoteric to care about.
+                        */
+                       *upper_proto = exthdr.next;
+                       return true;
+               }
+       }
+
+       /* We never found an upper layer header. */
+       return false;
+}
+
+/* This function has to be inlined, because the verifier otherwise rejects it
+ * due to returning a pointer to the stack. This is technically correct, since
+ * scratch is allocated on the stack. However, this usage should be safe since
+ * it's the callers stack after all.
+ */
+static inline __attribute__((__always_inline__)) struct ipv6hdr *
+pkt_parse_ipv6(buf_t *pkt, struct ipv6hdr *scratch, uint8_t *proto,
+              bool *is_fragment)
+{
+       struct ipv6hdr *ipv6 = buf_assign(pkt, sizeof(*ipv6), scratch);
+       if (ipv6 == NULL) {
+               return NULL;
+       }
+
+       if (!pkt_skip_ipv6_extension_headers(pkt, ipv6, proto, is_fragment)) {
+               return NULL;
+       }
+
+       return ipv6;
+}
+
+/* Global metrics, per CPU
+ */
+struct bpf_map_def metrics_map SEC("maps") = {
+       .type = BPF_MAP_TYPE_PERCPU_ARRAY,
+       .key_size = sizeof(unsigned int),
+       .value_size = sizeof(metrics_t),
+       .max_entries = 1,
+};
+
+static metrics_t *get_global_metrics(void)
+{
+       uint64_t key = 0;
+       return bpf_map_lookup_elem(&metrics_map, &key);
+}
+
+static ret_t accept_locally(struct __sk_buff *skb, encap_headers_t *encap)
+{
+       const int payload_off =
+               sizeof(*encap) +
+               sizeof(struct in_addr) * encap->unigue.hop_count;
+       int32_t encap_overhead = payload_off - sizeof(struct ethhdr);
+
+       // Changing the ethertype if the encapsulated packet is ipv6
+       if (encap->gue.proto_ctype == IPPROTO_IPV6) {
+               encap->eth.h_proto = bpf_htons(ETH_P_IPV6);
+       }
+
+       if (bpf_skb_adjust_room(skb, -encap_overhead, BPF_ADJ_ROOM_MAC,
+                               BPF_F_ADJ_ROOM_FIXED_GSO)) {
+               return TC_ACT_SHOT;
+       }
+
+       return bpf_redirect(skb->ifindex, BPF_F_INGRESS);
+}
+
+static ret_t forward_with_gre(struct __sk_buff *skb, encap_headers_t *encap,
+                             struct in_addr *next_hop, metrics_t *metrics)
+{
+       metrics->forwarded_packets_total_gre++;
+
+       const int payload_off =
+               sizeof(*encap) +
+               sizeof(struct in_addr) * encap->unigue.hop_count;
+       int32_t encap_overhead =
+               payload_off - sizeof(struct ethhdr) - sizeof(struct iphdr);
+       int32_t delta = sizeof(struct gre_base_hdr) - encap_overhead;
+       uint16_t proto = ETH_P_IP;
+
+       /* Loop protection: the inner packet's TTL is decremented as a safeguard
+        * against any forwarding loop. As the only interesting field is the TTL
+        * hop limit for IPv6, it is easier to use bpf_skb_load_bytes/bpf_skb_store_bytes
+        * as they handle the split packets if needed (no need for the data to be
+        * in the linear section).
+        */
+       if (encap->gue.proto_ctype == IPPROTO_IPV6) {
+               proto = ETH_P_IPV6;
+               uint8_t ttl;
+               int rc;
+
+               rc = bpf_skb_load_bytes(
+                       skb, payload_off + offsetof(struct ipv6hdr, hop_limit),
+                       &ttl, 1);
+               if (rc != 0) {
+                       metrics->errors_total_malformed_encapsulation++;
+                       return TC_ACT_SHOT;
+               }
+
+               if (ttl == 0) {
+                       metrics->errors_total_redirect_loop++;
+                       return TC_ACT_SHOT;
+               }
+
+               ttl--;
+               rc = bpf_skb_store_bytes(
+                       skb, payload_off + offsetof(struct ipv6hdr, hop_limit),
+                       &ttl, 1, 0);
+               if (rc != 0) {
+                       metrics->errors_total_malformed_encapsulation++;
+                       return TC_ACT_SHOT;
+               }
+       } else {
+               uint8_t ttl;
+               int rc;
+
+               rc = bpf_skb_load_bytes(
+                       skb, payload_off + offsetof(struct iphdr, ttl), &ttl,
+                       1);
+               if (rc != 0) {
+                       metrics->errors_total_malformed_encapsulation++;
+                       return TC_ACT_SHOT;
+               }
+
+               if (ttl == 0) {
+                       metrics->errors_total_redirect_loop++;
+                       return TC_ACT_SHOT;
+               }
+
+               /* IPv4 also has a checksum to patch. While the TTL is only one byte,
+                * this function only works for 2 and 4 bytes arguments (the result is
+                * the same).
+                */
+               rc = bpf_l3_csum_replace(
+                       skb, payload_off + offsetof(struct iphdr, check), ttl,
+                       ttl - 1, 2);
+               if (rc != 0) {
+                       metrics->errors_total_malformed_encapsulation++;
+                       return TC_ACT_SHOT;
+               }
+
+               ttl--;
+               rc = bpf_skb_store_bytes(
+                       skb, payload_off + offsetof(struct iphdr, ttl), &ttl, 1,
+                       0);
+               if (rc != 0) {
+                       metrics->errors_total_malformed_encapsulation++;
+                       return TC_ACT_SHOT;
+               }
+       }
+
+       if (bpf_skb_adjust_room(skb, delta, BPF_ADJ_ROOM_NET,
+                               BPF_F_ADJ_ROOM_FIXED_GSO)) {
+               metrics->errors_total_encap_adjust_failed++;
+               return TC_ACT_SHOT;
+       }
+
+       if (bpf_skb_pull_data(skb, sizeof(encap_gre_t))) {
+               metrics->errors_total_encap_buffer_too_small++;
+               return TC_ACT_SHOT;
+       }
+
+       buf_t pkt = {
+               .skb = skb,
+               .head = (uint8_t *)(long)skb->data,
+               .tail = (uint8_t *)(long)skb->data_end,
+       };
+
+       encap_gre_t *encap_gre = buf_assign(&pkt, sizeof(encap_gre_t), NULL);
+       if (encap_gre == NULL) {
+               metrics->errors_total_encap_buffer_too_small++;
+               return TC_ACT_SHOT;
+       }
+
+       encap_gre->ip.protocol = IPPROTO_GRE;
+       encap_gre->ip.daddr = next_hop->s_addr;
+       encap_gre->ip.saddr = ENCAPSULATION_IP;
+       encap_gre->ip.tot_len =
+               bpf_htons(bpf_ntohs(encap_gre->ip.tot_len) + delta);
+       encap_gre->gre.flags = 0;
+       encap_gre->gre.protocol = bpf_htons(proto);
+       pkt_ipv4_checksum((void *)&encap_gre->ip);
+
+       return bpf_redirect(skb->ifindex, 0);
+}
+
+static ret_t forward_to_next_hop(struct __sk_buff *skb, encap_headers_t *encap,
+                                struct in_addr *next_hop, metrics_t *metrics)
+{
+       /* swap L2 addresses */
+       /* This assumes that packets are received from a router.
+        * So just swapping the MAC addresses here will make the packet go back to
+        * the router, which will send it to the appropriate machine.
+        */
+       unsigned char temp[ETH_ALEN];
+       memcpy(temp, encap->eth.h_dest, sizeof(temp));
+       memcpy(encap->eth.h_dest, encap->eth.h_source,
+              sizeof(encap->eth.h_dest));
+       memcpy(encap->eth.h_source, temp, sizeof(encap->eth.h_source));
+
+       if (encap->unigue.next_hop == encap->unigue.hop_count - 1 &&
+           encap->unigue.last_hop_gre) {
+               return forward_with_gre(skb, encap, next_hop, metrics);
+       }
+
+       metrics->forwarded_packets_total_gue++;
+       uint32_t old_saddr = encap->ip.saddr;
+       encap->ip.saddr = encap->ip.daddr;
+       encap->ip.daddr = next_hop->s_addr;
+       if (encap->unigue.next_hop < encap->unigue.hop_count) {
+               encap->unigue.next_hop++;
+       }
+
+       /* Remove ip->saddr, add next_hop->s_addr */
+       const uint64_t off = offsetof(typeof(*encap), ip.check);
+       int ret = bpf_l3_csum_replace(skb, off, old_saddr, next_hop->s_addr, 4);
+       if (ret < 0) {
+               return TC_ACT_SHOT;
+       }
+
+       return bpf_redirect(skb->ifindex, 0);
+}
+
+static ret_t skip_next_hops(buf_t *pkt, int n)
+{
+       switch (n) {
+       case 1:
+               if (!buf_skip(pkt, sizeof(struct in_addr)))
+                       return TC_ACT_SHOT;
+       case 0:
+               return CONTINUE_PROCESSING;
+
+       default:
+               return TC_ACT_SHOT;
+       }
+}
+
+/* Get the next hop from the GLB header.
+ *
+ * Sets next_hop->s_addr to 0 if there are no more hops left.
+ * pkt is positioned just after the variable length GLB header
+ * iff the call is successful.
+ */
+static ret_t get_next_hop(buf_t *pkt, encap_headers_t *encap,
+                         struct in_addr *next_hop)
+{
+       if (encap->unigue.next_hop > encap->unigue.hop_count) {
+               return TC_ACT_SHOT;
+       }
+
+       /* Skip "used" next hops. */
+       MAYBE_RETURN(skip_next_hops(pkt, encap->unigue.next_hop));
+
+       if (encap->unigue.next_hop == encap->unigue.hop_count) {
+               /* No more next hops, we are at the end of the GLB header. */
+               next_hop->s_addr = 0;
+               return CONTINUE_PROCESSING;
+       }
+
+       if (!buf_copy(pkt, next_hop, sizeof(*next_hop))) {
+               return TC_ACT_SHOT;
+       }
+
+       /* Skip the remainig next hops (may be zero). */
+       return skip_next_hops(pkt, encap->unigue.hop_count -
+                                          encap->unigue.next_hop - 1);
+}
+
+/* Fill a bpf_sock_tuple to be used with the socket lookup functions.
+ * This is a kludge that let's us work around verifier limitations:
+ *
+ *    fill_tuple(&t, foo, sizeof(struct iphdr), 123, 321)
+ *
+ * clang will substitue a costant for sizeof, which allows the verifier
+ * to track it's value. Based on this, it can figure out the constant
+ * return value, and calling code works while still being "generic" to
+ * IPv4 and IPv6.
+ */
+static uint64_t fill_tuple(struct bpf_sock_tuple *tuple, void *iph,
+                          uint64_t iphlen, uint16_t sport, uint16_t dport)
+{
+       switch (iphlen) {
+       case sizeof(struct iphdr): {
+               struct iphdr *ipv4 = (struct iphdr *)iph;
+               tuple->ipv4.daddr = ipv4->daddr;
+               tuple->ipv4.saddr = ipv4->saddr;
+               tuple->ipv4.sport = sport;
+               tuple->ipv4.dport = dport;
+               return sizeof(tuple->ipv4);
+       }
+
+       case sizeof(struct ipv6hdr): {
+               struct ipv6hdr *ipv6 = (struct ipv6hdr *)iph;
+               memcpy(&tuple->ipv6.daddr, &ipv6->daddr,
+                      sizeof(tuple->ipv6.daddr));
+               memcpy(&tuple->ipv6.saddr, &ipv6->saddr,
+                      sizeof(tuple->ipv6.saddr));
+               tuple->ipv6.sport = sport;
+               tuple->ipv6.dport = dport;
+               return sizeof(tuple->ipv6);
+       }
+
+       default:
+               return 0;
+       }
+}
+
+static verdict_t classify_tcp(struct __sk_buff *skb,
+                             struct bpf_sock_tuple *tuple, uint64_t tuplen,
+                             void *iph, struct tcphdr *tcp)
+{
+       struct bpf_sock *sk =
+               bpf_skc_lookup_tcp(skb, tuple, tuplen, BPF_F_CURRENT_NETNS, 0);
+       if (sk == NULL) {
+               return UNKNOWN;
+       }
+
+       if (sk->state != BPF_TCP_LISTEN) {
+               bpf_sk_release(sk);
+               return ESTABLISHED;
+       }
+
+       if (iph != NULL && tcp != NULL) {
+               /* Kludge: we've run out of arguments, but need the length of the ip header. */
+               uint64_t iphlen = sizeof(struct iphdr);
+               if (tuplen == sizeof(tuple->ipv6)) {
+                       iphlen = sizeof(struct ipv6hdr);
+               }
+
+               if (bpf_tcp_check_syncookie(sk, iph, iphlen, tcp,
+                                           sizeof(*tcp)) == 0) {
+                       bpf_sk_release(sk);
+                       return SYN_COOKIE;
+               }
+       }
+
+       bpf_sk_release(sk);
+       return UNKNOWN;
+}
+
+static verdict_t classify_udp(struct __sk_buff *skb,
+                             struct bpf_sock_tuple *tuple, uint64_t tuplen)
+{
+       struct bpf_sock *sk =
+               bpf_sk_lookup_udp(skb, tuple, tuplen, BPF_F_CURRENT_NETNS, 0);
+       if (sk == NULL) {
+               return UNKNOWN;
+       }
+
+       if (sk->state == BPF_TCP_ESTABLISHED) {
+               bpf_sk_release(sk);
+               return ESTABLISHED;
+       }
+
+       bpf_sk_release(sk);
+       return UNKNOWN;
+}
+
+static verdict_t classify_icmp(struct __sk_buff *skb, uint8_t proto,
+                              struct bpf_sock_tuple *tuple, uint64_t tuplen,
+                              metrics_t *metrics)
+{
+       switch (proto) {
+       case IPPROTO_TCP:
+               return classify_tcp(skb, tuple, tuplen, NULL, NULL);
+
+       case IPPROTO_UDP:
+               return classify_udp(skb, tuple, tuplen);
+
+       default:
+               metrics->errors_total_malformed_icmp++;
+               return INVALID;
+       }
+}
+
+static verdict_t process_icmpv4(buf_t *pkt, metrics_t *metrics)
+{
+       struct icmphdr icmp;
+       if (!buf_copy(pkt, &icmp, sizeof(icmp))) {
+               metrics->errors_total_malformed_icmp++;
+               return INVALID;
+       }
+
+       /* We should never receive encapsulated echo replies. */
+       if (icmp.type == ICMP_ECHOREPLY) {
+               metrics->errors_total_icmp_echo_replies++;
+               return INVALID;
+       }
+
+       if (icmp.type == ICMP_ECHO) {
+               return ECHO_REQUEST;
+       }
+
+       if (icmp.type != ICMP_DEST_UNREACH || icmp.code != ICMP_FRAG_NEEDED) {
+               metrics->errors_total_unwanted_icmp++;
+               return INVALID;
+       }
+
+       struct iphdr _ip4;
+       const struct iphdr *ipv4 = pkt_parse_ipv4(pkt, &_ip4);
+       if (ipv4 == NULL) {
+               metrics->errors_total_malformed_icmp_pkt_too_big++;
+               return INVALID;
+       }
+
+       /* The source address in the outer IP header is from the entity that
+        * originated the ICMP message. Use the original IP header to restore
+        * the correct flow tuple.
+        */
+       struct bpf_sock_tuple tuple;
+       tuple.ipv4.saddr = ipv4->daddr;
+       tuple.ipv4.daddr = ipv4->saddr;
+
+       if (!pkt_parse_icmp_l4_ports(pkt, (flow_ports_t *)&tuple.ipv4.sport)) {
+               metrics->errors_total_malformed_icmp_pkt_too_big++;
+               return INVALID;
+       }
+
+       return classify_icmp(pkt->skb, ipv4->protocol, &tuple,
+                            sizeof(tuple.ipv4), metrics);
+}
+
+static verdict_t process_icmpv6(buf_t *pkt, metrics_t *metrics)
+{
+       struct icmp6hdr icmp6;
+       if (!buf_copy(pkt, &icmp6, sizeof(icmp6))) {
+               metrics->errors_total_malformed_icmp++;
+               return INVALID;
+       }
+
+       /* We should never receive encapsulated echo replies. */
+       if (icmp6.icmp6_type == ICMPV6_ECHO_REPLY) {
+               metrics->errors_total_icmp_echo_replies++;
+               return INVALID;
+       }
+
+       if (icmp6.icmp6_type == ICMPV6_ECHO_REQUEST) {
+               return ECHO_REQUEST;
+       }
+
+       if (icmp6.icmp6_type != ICMPV6_PKT_TOOBIG) {
+               metrics->errors_total_unwanted_icmp++;
+               return INVALID;
+       }
+
+       bool is_fragment;
+       uint8_t l4_proto;
+       struct ipv6hdr _ipv6;
+       const struct ipv6hdr *ipv6 =
+               pkt_parse_ipv6(pkt, &_ipv6, &l4_proto, &is_fragment);
+       if (ipv6 == NULL) {
+               metrics->errors_total_malformed_icmp_pkt_too_big++;
+               return INVALID;
+       }
+
+       if (is_fragment) {
+               metrics->errors_total_fragmented_ip++;
+               return INVALID;
+       }
+
+       /* Swap source and dest addresses. */
+       struct bpf_sock_tuple tuple;
+       memcpy(&tuple.ipv6.saddr, &ipv6->daddr, sizeof(tuple.ipv6.saddr));
+       memcpy(&tuple.ipv6.daddr, &ipv6->saddr, sizeof(tuple.ipv6.daddr));
+
+       if (!pkt_parse_icmp_l4_ports(pkt, (flow_ports_t *)&tuple.ipv6.sport)) {
+               metrics->errors_total_malformed_icmp_pkt_too_big++;
+               return INVALID;
+       }
+
+       return classify_icmp(pkt->skb, l4_proto, &tuple, sizeof(tuple.ipv6),
+                            metrics);
+}
+
+static verdict_t process_tcp(buf_t *pkt, void *iph, uint64_t iphlen,
+                            metrics_t *metrics)
+{
+       metrics->l4_protocol_packets_total_tcp++;
+
+       struct tcphdr _tcp;
+       struct tcphdr *tcp = buf_assign(pkt, sizeof(_tcp), &_tcp);
+       if (tcp == NULL) {
+               metrics->errors_total_malformed_tcp++;
+               return INVALID;
+       }
+
+       if (tcp->syn) {
+               return SYN;
+       }
+
+       struct bpf_sock_tuple tuple;
+       uint64_t tuplen =
+               fill_tuple(&tuple, iph, iphlen, tcp->source, tcp->dest);
+       return classify_tcp(pkt->skb, &tuple, tuplen, iph, tcp);
+}
+
+static verdict_t process_udp(buf_t *pkt, void *iph, uint64_t iphlen,
+                            metrics_t *metrics)
+{
+       metrics->l4_protocol_packets_total_udp++;
+
+       struct udphdr _udp;
+       struct udphdr *udph = buf_assign(pkt, sizeof(_udp), &_udp);
+       if (udph == NULL) {
+               metrics->errors_total_malformed_udp++;
+               return INVALID;
+       }
+
+       struct bpf_sock_tuple tuple;
+       uint64_t tuplen =
+               fill_tuple(&tuple, iph, iphlen, udph->source, udph->dest);
+       return classify_udp(pkt->skb, &tuple, tuplen);
+}
+
+static verdict_t process_ipv4(buf_t *pkt, metrics_t *metrics)
+{
+       metrics->l3_protocol_packets_total_ipv4++;
+
+       struct iphdr _ip4;
+       struct iphdr *ipv4 = pkt_parse_ipv4(pkt, &_ip4);
+       if (ipv4 == NULL) {
+               metrics->errors_total_malformed_ip++;
+               return INVALID;
+       }
+
+       if (ipv4->version != 4) {
+               metrics->errors_total_malformed_ip++;
+               return INVALID;
+       }
+
+       if (ipv4_is_fragment(ipv4)) {
+               metrics->errors_total_fragmented_ip++;
+               return INVALID;
+       }
+
+       switch (ipv4->protocol) {
+       case IPPROTO_ICMP:
+               return process_icmpv4(pkt, metrics);
+
+       case IPPROTO_TCP:
+               return process_tcp(pkt, ipv4, sizeof(*ipv4), metrics);
+
+       case IPPROTO_UDP:
+               return process_udp(pkt, ipv4, sizeof(*ipv4), metrics);
+
+       default:
+               metrics->errors_total_unknown_l4_proto++;
+               return INVALID;
+       }
+}
+
+static verdict_t process_ipv6(buf_t *pkt, metrics_t *metrics)
+{
+       metrics->l3_protocol_packets_total_ipv6++;
+
+       uint8_t l4_proto;
+       bool is_fragment;
+       struct ipv6hdr _ipv6;
+       struct ipv6hdr *ipv6 =
+               pkt_parse_ipv6(pkt, &_ipv6, &l4_proto, &is_fragment);
+       if (ipv6 == NULL) {
+               metrics->errors_total_malformed_ip++;
+               return INVALID;
+       }
+
+       if (ipv6->version != 6) {
+               metrics->errors_total_malformed_ip++;
+               return INVALID;
+       }
+
+       if (is_fragment) {
+               metrics->errors_total_fragmented_ip++;
+               return INVALID;
+       }
+
+       switch (l4_proto) {
+       case IPPROTO_ICMPV6:
+               return process_icmpv6(pkt, metrics);
+
+       case IPPROTO_TCP:
+               return process_tcp(pkt, ipv6, sizeof(*ipv6), metrics);
+
+       case IPPROTO_UDP:
+               return process_udp(pkt, ipv6, sizeof(*ipv6), metrics);
+
+       default:
+               metrics->errors_total_unknown_l4_proto++;
+               return INVALID;
+       }
+}
+
+SEC("classifier/cls_redirect")
+int cls_redirect(struct __sk_buff *skb)
+{
+       metrics_t *metrics = get_global_metrics();
+       if (metrics == NULL) {
+               return TC_ACT_SHOT;
+       }
+
+       metrics->processed_packets_total++;
+
+       /* Pass bogus packets as long as we're not sure they're
+        * destined for us.
+        */
+       if (skb->protocol != bpf_htons(ETH_P_IP)) {
+               return TC_ACT_OK;
+       }
+
+       encap_headers_t *encap;
+
+       /* Make sure that all encapsulation headers are available in
+        * the linear portion of the skb. This makes it easy to manipulate them.
+        */
+       if (bpf_skb_pull_data(skb, sizeof(*encap))) {
+               return TC_ACT_OK;
+       }
+
+       buf_t pkt = {
+               .skb = skb,
+               .head = (uint8_t *)(long)skb->data,
+               .tail = (uint8_t *)(long)skb->data_end,
+       };
+
+       encap = buf_assign(&pkt, sizeof(*encap), NULL);
+       if (encap == NULL) {
+               return TC_ACT_OK;
+       }
+
+       if (encap->ip.ihl != 5) {
+               /* We never have any options. */
+               return TC_ACT_OK;
+       }
+
+       if (encap->ip.daddr != ENCAPSULATION_IP ||
+           encap->ip.protocol != IPPROTO_UDP) {
+               return TC_ACT_OK;
+       }
+
+       /* TODO Check UDP length? */
+       if (encap->udp.dest != ENCAPSULATION_PORT) {
+               return TC_ACT_OK;
+       }
+
+       /* We now know that the packet is destined to us, we can
+        * drop bogus ones.
+        */
+       if (ipv4_is_fragment((void *)&encap->ip)) {
+               metrics->errors_total_fragmented_ip++;
+               return TC_ACT_SHOT;
+       }
+
+       if (encap->gue.variant != 0) {
+               metrics->errors_total_malformed_encapsulation++;
+               return TC_ACT_SHOT;
+       }
+
+       if (encap->gue.control != 0) {
+               metrics->errors_total_malformed_encapsulation++;
+               return TC_ACT_SHOT;
+       }
+
+       if (encap->gue.flags != 0) {
+               metrics->errors_total_malformed_encapsulation++;
+               return TC_ACT_SHOT;
+       }
+
+       if (encap->gue.hlen !=
+           sizeof(encap->unigue) / 4 + encap->unigue.hop_count) {
+               metrics->errors_total_malformed_encapsulation++;
+               return TC_ACT_SHOT;
+       }
+
+       if (encap->unigue.version != 0) {
+               metrics->errors_total_malformed_encapsulation++;
+               return TC_ACT_SHOT;
+       }
+
+       if (encap->unigue.reserved != 0) {
+               return TC_ACT_SHOT;
+       }
+
+       struct in_addr next_hop;
+       MAYBE_RETURN(get_next_hop(&pkt, encap, &next_hop));
+
+       if (next_hop.s_addr == 0) {
+               metrics->accepted_packets_total_last_hop++;
+               return accept_locally(skb, encap);
+       }
+
+       verdict_t verdict;
+       switch (encap->gue.proto_ctype) {
+       case IPPROTO_IPIP:
+               verdict = process_ipv4(&pkt, metrics);
+               break;
+
+       case IPPROTO_IPV6:
+               verdict = process_ipv6(&pkt, metrics);
+               break;
+
+       default:
+               metrics->errors_total_unknown_l3_proto++;
+               return TC_ACT_SHOT;
+       }
+
+       switch (verdict) {
+       case INVALID:
+               /* metrics have already been bumped */
+               return TC_ACT_SHOT;
+
+       case UNKNOWN:
+               return forward_to_next_hop(skb, encap, &next_hop, metrics);
+
+       case ECHO_REQUEST:
+               metrics->accepted_packets_total_icmp_echo_request++;
+               break;
+
+       case SYN:
+               if (encap->unigue.forward_syn) {
+                       return forward_to_next_hop(skb, encap, &next_hop,
+                                                  metrics);
+               }
+
+               metrics->accepted_packets_total_syn++;
+               break;
+
+       case SYN_COOKIE:
+               metrics->accepted_packets_total_syn_cookies++;
+               break;
+
+       case ESTABLISHED:
+               metrics->accepted_packets_total_established++;
+               break;
+       }
+
+       return accept_locally(skb, encap);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_cls_redirect.h b/tools/testing/selftests/bpf/progs/test_cls_redirect.h
new file mode 100644 (file)
index 0000000..76eab0a
--- /dev/null
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright 2019, 2020 Cloudflare */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/udp.h>
+
+struct gre_base_hdr {
+       uint16_t flags;
+       uint16_t protocol;
+} __attribute__((packed));
+
+struct guehdr {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+       uint8_t hlen : 5, control : 1, variant : 2;
+#else
+       uint8_t variant : 2, control : 1, hlen : 5;
+#endif
+       uint8_t proto_ctype;
+       uint16_t flags;
+};
+
+struct unigue {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+       uint8_t _r : 2, last_hop_gre : 1, forward_syn : 1, version : 4;
+#else
+       uint8_t version : 4, forward_syn : 1, last_hop_gre : 1, _r : 2;
+#endif
+       uint8_t reserved;
+       uint8_t next_hop;
+       uint8_t hop_count;
+       // Next hops go here
+} __attribute__((packed));
+
+typedef struct {
+       struct ethhdr eth;
+       struct iphdr ip;
+       struct gre_base_hdr gre;
+} __attribute__((packed)) encap_gre_t;
+
+typedef struct {
+       struct ethhdr eth;
+       struct iphdr ip;
+       struct udphdr udp;
+       struct guehdr gue;
+       struct unigue unigue;
+} __attribute__((packed)) encap_headers_t;
diff --git a/tools/testing/selftests/bpf/progs/test_enable_stats.c b/tools/testing/selftests/bpf/progs/test_enable_stats.c
new file mode 100644 (file)
index 0000000..01a002a
--- /dev/null
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020 Facebook
+
+#include <linux/bpf.h>
+#include <stdint.h>
+#include <linux/types.h>
+#include <bpf/bpf_helpers.h>
+
+char _license[] SEC("license") = "GPL";
+
+__u64 count = 0;
+
+SEC("raw_tracepoint/sys_enter")
+int test_enable_stats(void *ctx)
+{
+       count += 1;
+       return 0;
+}
index 98b9de2..ded71b3 100644 (file)
@@ -3,16 +3,8 @@
  */
 #include <stddef.h>
 #include <linux/bpf.h>
-#include <linux/pkt_cls.h>
 #include <bpf/bpf_helpers.h>
 
-/* It is a dumb bpf program such that it must have no
- * issue to be loaded since testing the verifier is
- * not the focus here.
- */
-
-int _version SEC("version") = 1;
-
 struct {
        __uint(type, BPF_MAP_TYPE_ARRAY);
        __uint(max_entries, 1);
@@ -20,13 +12,13 @@ struct {
        __type(value, __u64);
 } test_map_id SEC(".maps");
 
-SEC("test_obj_id_dummy")
-int test_obj_id(struct __sk_buff *skb)
+SEC("raw_tp/sys_enter")
+int test_obj_id(void *ctx)
 {
        __u32 key = 0;
        __u64 *value;
 
        value = bpf_map_lookup_elem(&test_map_id, &key);
 
-       return TC_ACT_OK;
+       return 0;
 }
index 8f53084..1ecd987 100644 (file)
 #include <bpf/bpf_helpers.h>
 #include <bpf/bpf_endian.h>
 
+/* Pin map under /sys/fs/bpf/tc/globals/<map name> */
+#define PIN_GLOBAL_NS 2
+
+/* Must match struct bpf_elf_map layout from iproute2 */
+struct {
+       __u32 type;
+       __u32 size_key;
+       __u32 size_value;
+       __u32 max_elem;
+       __u32 flags;
+       __u32 id;
+       __u32 pinning;
+} server_map SEC("maps") = {
+       .type = BPF_MAP_TYPE_SOCKMAP,
+       .size_key = sizeof(int),
+       .size_value  = sizeof(__u64),
+       .max_elem = 1,
+       .pinning = PIN_GLOBAL_NS,
+};
+
 int _version SEC("version") = 1;
 char _license[] SEC("license") = "GPL";
 
@@ -72,7 +92,9 @@ handle_udp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple, bool ipv4)
 {
        struct bpf_sock_tuple ln = {0};
        struct bpf_sock *sk;
+       const int zero = 0;
        size_t tuple_len;
+       __be16 dport;
        int ret;
 
        tuple_len = ipv4 ? sizeof(tuple->ipv4) : sizeof(tuple->ipv6);
@@ -83,32 +105,11 @@ handle_udp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple, bool ipv4)
        if (sk)
                goto assign;
 
-       if (ipv4) {
-               if (tuple->ipv4.dport != bpf_htons(4321))
-                       return TC_ACT_OK;
-
-               ln.ipv4.daddr = bpf_htonl(0x7f000001);
-               ln.ipv4.dport = bpf_htons(1234);
-
-               sk = bpf_sk_lookup_udp(skb, &ln, sizeof(ln.ipv4),
-                                       BPF_F_CURRENT_NETNS, 0);
-       } else {
-               if (tuple->ipv6.dport != bpf_htons(4321))
-                       return TC_ACT_OK;
-
-               /* Upper parts of daddr are already zero. */
-               ln.ipv6.daddr[3] = bpf_htonl(0x1);
-               ln.ipv6.dport = bpf_htons(1234);
-
-               sk = bpf_sk_lookup_udp(skb, &ln, sizeof(ln.ipv6),
-                                       BPF_F_CURRENT_NETNS, 0);
-       }
+       dport = ipv4 ? tuple->ipv4.dport : tuple->ipv6.dport;
+       if (dport != bpf_htons(4321))
+               return TC_ACT_OK;
 
-       /* workaround: We can't do a single socket lookup here, because then
-        * the compiler will likely spill tuple_len to the stack. This makes it
-        * lose all bounds information in the verifier, which then rejects the
-        * call as unsafe.
-        */
+       sk = bpf_map_lookup_elem(&server_map, &zero);
        if (!sk)
                return TC_ACT_SHOT;
 
@@ -123,7 +124,9 @@ handle_tcp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple, bool ipv4)
 {
        struct bpf_sock_tuple ln = {0};
        struct bpf_sock *sk;
+       const int zero = 0;
        size_t tuple_len;
+       __be16 dport;
        int ret;
 
        tuple_len = ipv4 ? sizeof(tuple->ipv4) : sizeof(tuple->ipv6);
@@ -137,32 +140,11 @@ handle_tcp(struct __sk_buff *skb, struct bpf_sock_tuple *tuple, bool ipv4)
                bpf_sk_release(sk);
        }
 
-       if (ipv4) {
-               if (tuple->ipv4.dport != bpf_htons(4321))
-                       return TC_ACT_OK;
+       dport = ipv4 ? tuple->ipv4.dport : tuple->ipv6.dport;
+       if (dport != bpf_htons(4321))
+               return TC_ACT_OK;
 
-               ln.ipv4.daddr = bpf_htonl(0x7f000001);
-               ln.ipv4.dport = bpf_htons(1234);
-
-               sk = bpf_skc_lookup_tcp(skb, &ln, sizeof(ln.ipv4),
-                                       BPF_F_CURRENT_NETNS, 0);
-       } else {
-               if (tuple->ipv6.dport != bpf_htons(4321))
-                       return TC_ACT_OK;
-
-               /* Upper parts of daddr are already zero. */
-               ln.ipv6.daddr[3] = bpf_htonl(0x1);
-               ln.ipv6.dport = bpf_htons(1234);
-
-               sk = bpf_skc_lookup_tcp(skb, &ln, sizeof(ln.ipv6),
-                                       BPF_F_CURRENT_NETNS, 0);
-       }
-
-       /* workaround: We can't do a single socket lookup here, because then
-        * the compiler will likely spill tuple_len to the stack. This makes it
-        * lose all bounds information in the verifier, which then rejects the
-        * call as unsafe.
-        */
+       sk = bpf_map_lookup_elem(&server_map, &zero);
        if (!sk)
                return TC_ACT_SHOT;
 
index 2d0b0b8..5052523 100644 (file)
@@ -45,7 +45,7 @@ int sysctl_tcp_mem(struct bpf_sysctl *ctx)
        unsigned long tcp_mem[3] = {0, 0, 0};
        char value[MAX_VALUE_STR_LEN];
        unsigned char i, off = 0;
-       int ret;
+       volatile int ret;
 
        if (ctx->write)
                return 0;
index 8da77cd..305fae8 100644 (file)
@@ -2854,7 +2854,7 @@ static struct btf_raw_test raw_tests[] = {
        .value_type_id = 1,
        .max_entries = 4,
        .btf_load_err = true,
-       .err_str = "vlen != 0",
+       .err_str = "Invalid func linkage",
 },
 
 {
diff --git a/tools/testing/selftests/bpf/test_hashmap.c b/tools/testing/selftests/bpf/test_hashmap.c
deleted file mode 100644 (file)
index c490e01..0000000
+++ /dev/null
@@ -1,382 +0,0 @@
-// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
-
-/*
- * Tests for libbpf's hashmap.
- *
- * Copyright (c) 2019 Facebook
- */
-#include <stdio.h>
-#include <errno.h>
-#include <linux/err.h>
-#include "bpf/hashmap.h"
-
-#define CHECK(condition, format...) ({                                 \
-       int __ret = !!(condition);                                      \
-       if (__ret) {                                                    \
-               fprintf(stderr, "%s:%d:FAIL ", __func__, __LINE__);     \
-               fprintf(stderr, format);                                \
-       }                                                               \
-       __ret;                                                          \
-})
-
-size_t hash_fn(const void *k, void *ctx)
-{
-       return (long)k;
-}
-
-bool equal_fn(const void *a, const void *b, void *ctx)
-{
-       return (long)a == (long)b;
-}
-
-static inline size_t next_pow_2(size_t n)
-{
-       size_t r = 1;
-
-       while (r < n)
-               r <<= 1;
-       return r;
-}
-
-static inline size_t exp_cap(size_t sz)
-{
-       size_t r = next_pow_2(sz);
-
-       if (sz * 4 / 3 > r)
-               r <<= 1;
-       return r;
-}
-
-#define ELEM_CNT 62
-
-int test_hashmap_generic(void)
-{
-       struct hashmap_entry *entry, *tmp;
-       int err, bkt, found_cnt, i;
-       long long found_msk;
-       struct hashmap *map;
-
-       fprintf(stderr, "%s: ", __func__);
-
-       map = hashmap__new(hash_fn, equal_fn, NULL);
-       if (CHECK(IS_ERR(map), "failed to create map: %ld\n", PTR_ERR(map)))
-               return 1;
-
-       for (i = 0; i < ELEM_CNT; i++) {
-               const void *oldk, *k = (const void *)(long)i;
-               void *oldv, *v = (void *)(long)(1024 + i);
-
-               err = hashmap__update(map, k, v, &oldk, &oldv);
-               if (CHECK(err != -ENOENT, "unexpected result: %d\n", err))
-                       return 1;
-
-               if (i % 2) {
-                       err = hashmap__add(map, k, v);
-               } else {
-                       err = hashmap__set(map, k, v, &oldk, &oldv);
-                       if (CHECK(oldk != NULL || oldv != NULL,
-                                 "unexpected k/v: %p=%p\n", oldk, oldv))
-                               return 1;
-               }
-
-               if (CHECK(err, "failed to add k/v %ld = %ld: %d\n",
-                              (long)k, (long)v, err))
-                       return 1;
-
-               if (CHECK(!hashmap__find(map, k, &oldv),
-                         "failed to find key %ld\n", (long)k))
-                       return 1;
-               if (CHECK(oldv != v, "found value is wrong: %ld\n", (long)oldv))
-                       return 1;
-       }
-
-       if (CHECK(hashmap__size(map) != ELEM_CNT,
-                 "invalid map size: %zu\n", hashmap__size(map)))
-               return 1;
-       if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
-                 "unexpected map capacity: %zu\n", hashmap__capacity(map)))
-               return 1;
-
-       found_msk = 0;
-       hashmap__for_each_entry(map, entry, bkt) {
-               long k = (long)entry->key;
-               long v = (long)entry->value;
-
-               found_msk |= 1ULL << k;
-               if (CHECK(v - k != 1024, "invalid k/v pair: %ld = %ld\n", k, v))
-                       return 1;
-       }
-       if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1,
-                 "not all keys iterated: %llx\n", found_msk))
-               return 1;
-
-       for (i = 0; i < ELEM_CNT; i++) {
-               const void *oldk, *k = (const void *)(long)i;
-               void *oldv, *v = (void *)(long)(256 + i);
-
-               err = hashmap__add(map, k, v);
-               if (CHECK(err != -EEXIST, "unexpected add result: %d\n", err))
-                       return 1;
-
-               if (i % 2)
-                       err = hashmap__update(map, k, v, &oldk, &oldv);
-               else
-                       err = hashmap__set(map, k, v, &oldk, &oldv);
-
-               if (CHECK(err, "failed to update k/v %ld = %ld: %d\n",
-                              (long)k, (long)v, err))
-                       return 1;
-               if (CHECK(!hashmap__find(map, k, &oldv),
-                         "failed to find key %ld\n", (long)k))
-                       return 1;
-               if (CHECK(oldv != v, "found value is wrong: %ld\n", (long)oldv))
-                       return 1;
-       }
-
-       if (CHECK(hashmap__size(map) != ELEM_CNT,
-                 "invalid updated map size: %zu\n", hashmap__size(map)))
-               return 1;
-       if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
-                 "unexpected map capacity: %zu\n", hashmap__capacity(map)))
-               return 1;
-
-       found_msk = 0;
-       hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
-               long k = (long)entry->key;
-               long v = (long)entry->value;
-
-               found_msk |= 1ULL << k;
-               if (CHECK(v - k != 256,
-                         "invalid updated k/v pair: %ld = %ld\n", k, v))
-                       return 1;
-       }
-       if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1,
-                 "not all keys iterated after update: %llx\n", found_msk))
-               return 1;
-
-       found_cnt = 0;
-       hashmap__for_each_key_entry(map, entry, (void *)0) {
-               found_cnt++;
-       }
-       if (CHECK(!found_cnt, "didn't find any entries for key 0\n"))
-               return 1;
-
-       found_msk = 0;
-       found_cnt = 0;
-       hashmap__for_each_key_entry_safe(map, entry, tmp, (void *)0) {
-               const void *oldk, *k;
-               void *oldv, *v;
-
-               k = entry->key;
-               v = entry->value;
-
-               found_cnt++;
-               found_msk |= 1ULL << (long)k;
-
-               if (CHECK(!hashmap__delete(map, k, &oldk, &oldv),
-                         "failed to delete k/v %ld = %ld\n",
-                         (long)k, (long)v))
-                       return 1;
-               if (CHECK(oldk != k || oldv != v,
-                         "invalid deleted k/v: expected %ld = %ld, got %ld = %ld\n",
-                         (long)k, (long)v, (long)oldk, (long)oldv))
-                       return 1;
-               if (CHECK(hashmap__delete(map, k, &oldk, &oldv),
-                         "unexpectedly deleted k/v %ld = %ld\n",
-                         (long)oldk, (long)oldv))
-                       return 1;
-       }
-
-       if (CHECK(!found_cnt || !found_msk,
-                 "didn't delete any key entries\n"))
-               return 1;
-       if (CHECK(hashmap__size(map) != ELEM_CNT - found_cnt,
-                 "invalid updated map size (already deleted: %d): %zu\n",
-                 found_cnt, hashmap__size(map)))
-               return 1;
-       if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
-                 "unexpected map capacity: %zu\n", hashmap__capacity(map)))
-               return 1;
-
-       hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
-               const void *oldk, *k;
-               void *oldv, *v;
-
-               k = entry->key;
-               v = entry->value;
-
-               found_cnt++;
-               found_msk |= 1ULL << (long)k;
-
-               if (CHECK(!hashmap__delete(map, k, &oldk, &oldv),
-                         "failed to delete k/v %ld = %ld\n",
-                         (long)k, (long)v))
-                       return 1;
-               if (CHECK(oldk != k || oldv != v,
-                         "invalid old k/v: expect %ld = %ld, got %ld = %ld\n",
-                         (long)k, (long)v, (long)oldk, (long)oldv))
-                       return 1;
-               if (CHECK(hashmap__delete(map, k, &oldk, &oldv),
-                         "unexpectedly deleted k/v %ld = %ld\n",
-                         (long)k, (long)v))
-                       return 1;
-       }
-
-       if (CHECK(found_cnt != ELEM_CNT || found_msk != (1ULL << ELEM_CNT) - 1,
-                 "not all keys were deleted: found_cnt:%d, found_msk:%llx\n",
-                 found_cnt, found_msk))
-               return 1;
-       if (CHECK(hashmap__size(map) != 0,
-                 "invalid updated map size (already deleted: %d): %zu\n",
-                 found_cnt, hashmap__size(map)))
-               return 1;
-
-       found_cnt = 0;
-       hashmap__for_each_entry(map, entry, bkt) {
-               CHECK(false, "unexpected map entries left: %ld = %ld\n",
-                            (long)entry->key, (long)entry->value);
-               return 1;
-       }
-
-       hashmap__free(map);
-       hashmap__for_each_entry(map, entry, bkt) {
-               CHECK(false, "unexpected map entries left: %ld = %ld\n",
-                            (long)entry->key, (long)entry->value);
-               return 1;
-       }
-
-       fprintf(stderr, "OK\n");
-       return 0;
-}
-
-size_t collision_hash_fn(const void *k, void *ctx)
-{
-       return 0;
-}
-
-int test_hashmap_multimap(void)
-{
-       void *k1 = (void *)0, *k2 = (void *)1;
-       struct hashmap_entry *entry;
-       struct hashmap *map;
-       long found_msk;
-       int err, bkt;
-
-       fprintf(stderr, "%s: ", __func__);
-
-       /* force collisions */
-       map = hashmap__new(collision_hash_fn, equal_fn, NULL);
-       if (CHECK(IS_ERR(map), "failed to create map: %ld\n", PTR_ERR(map)))
-               return 1;
-
-
-       /* set up multimap:
-        * [0] -> 1, 2, 4;
-        * [1] -> 8, 16, 32;
-        */
-       err = hashmap__append(map, k1, (void *)1);
-       if (CHECK(err, "failed to add k/v: %d\n", err))
-               return 1;
-       err = hashmap__append(map, k1, (void *)2);
-       if (CHECK(err, "failed to add k/v: %d\n", err))
-               return 1;
-       err = hashmap__append(map, k1, (void *)4);
-       if (CHECK(err, "failed to add k/v: %d\n", err))
-               return 1;
-
-       err = hashmap__append(map, k2, (void *)8);
-       if (CHECK(err, "failed to add k/v: %d\n", err))
-               return 1;
-       err = hashmap__append(map, k2, (void *)16);
-       if (CHECK(err, "failed to add k/v: %d\n", err))
-               return 1;
-       err = hashmap__append(map, k2, (void *)32);
-       if (CHECK(err, "failed to add k/v: %d\n", err))
-               return 1;
-
-       if (CHECK(hashmap__size(map) != 6,
-                 "invalid map size: %zu\n", hashmap__size(map)))
-               return 1;
-
-       /* verify global iteration still works and sees all values */
-       found_msk = 0;
-       hashmap__for_each_entry(map, entry, bkt) {
-               found_msk |= (long)entry->value;
-       }
-       if (CHECK(found_msk != (1 << 6) - 1,
-                 "not all keys iterated: %lx\n", found_msk))
-               return 1;
-
-       /* iterate values for key 1 */
-       found_msk = 0;
-       hashmap__for_each_key_entry(map, entry, k1) {
-               found_msk |= (long)entry->value;
-       }
-       if (CHECK(found_msk != (1 | 2 | 4),
-                 "invalid k1 values: %lx\n", found_msk))
-               return 1;
-
-       /* iterate values for key 2 */
-       found_msk = 0;
-       hashmap__for_each_key_entry(map, entry, k2) {
-               found_msk |= (long)entry->value;
-       }
-       if (CHECK(found_msk != (8 | 16 | 32),
-                 "invalid k2 values: %lx\n", found_msk))
-               return 1;
-
-       fprintf(stderr, "OK\n");
-       return 0;
-}
-
-int test_hashmap_empty()
-{
-       struct hashmap_entry *entry;
-       int bkt;
-       struct hashmap *map;
-       void *k = (void *)0;
-
-       fprintf(stderr, "%s: ", __func__);
-
-       /* force collisions */
-       map = hashmap__new(hash_fn, equal_fn, NULL);
-       if (CHECK(IS_ERR(map), "failed to create map: %ld\n", PTR_ERR(map)))
-               return 1;
-
-       if (CHECK(hashmap__size(map) != 0,
-                 "invalid map size: %zu\n", hashmap__size(map)))
-               return 1;
-       if (CHECK(hashmap__capacity(map) != 0,
-                 "invalid map capacity: %zu\n", hashmap__capacity(map)))
-               return 1;
-       if (CHECK(hashmap__find(map, k, NULL), "unexpected find\n"))
-               return 1;
-       if (CHECK(hashmap__delete(map, k, NULL, NULL), "unexpected delete\n"))
-               return 1;
-
-       hashmap__for_each_entry(map, entry, bkt) {
-               CHECK(false, "unexpected iterated entry\n");
-               return 1;
-       }
-       hashmap__for_each_key_entry(map, entry, k) {
-               CHECK(false, "unexpected key entry\n");
-               return 1;
-       }
-
-       fprintf(stderr, "OK\n");
-       return 0;
-}
-
-int main(int argc, char **argv)
-{
-       bool failed = false;
-
-       if (test_hashmap_generic())
-               failed = true;
-       if (test_hashmap_multimap())
-               failed = true;
-       if (test_hashmap_empty())
-               failed = true;
-
-       return failed;
-}
index b521e0a..93970ec 100644 (file)
@@ -351,6 +351,7 @@ int extract_build_id(char *build_id, size_t size)
                len = size;
        memcpy(build_id, line, len);
        build_id[len] = '\0';
+       free(line);
        return 0;
 err:
        fclose(fp);
@@ -420,6 +421,18 @@ static int libbpf_print_fn(enum libbpf_print_level level,
        return 0;
 }
 
+static void free_str_set(const struct str_set *set)
+{
+       int i;
+
+       if (!set)
+               return;
+
+       for (i = 0; i < set->cnt; i++)
+               free((void *)set->strs[i]);
+       free(set->strs);
+}
+
 static int parse_str_list(const char *s, struct str_set *set)
 {
        char *input, *state = NULL, *next, **tmp, **strs = NULL;
@@ -756,11 +769,11 @@ int main(int argc, char **argv)
        fprintf(stdout, "Summary: %d/%d PASSED, %d SKIPPED, %d FAILED\n",
                env.succ_cnt, env.sub_succ_cnt, env.skip_cnt, env.fail_cnt);
 
-       free(env.test_selector.blacklist.strs);
-       free(env.test_selector.whitelist.strs);
+       free_str_set(&env.test_selector.blacklist);
+       free_str_set(&env.test_selector.whitelist);
        free(env.test_selector.num_set);
-       free(env.subtest_selector.blacklist.strs);
-       free(env.subtest_selector.whitelist.strs);
+       free_str_set(&env.subtest_selector.blacklist);
+       free_str_set(&env.subtest_selector.whitelist);
        free(env.subtest_selector.num_set);
 
        return env.fail_cnt ? EXIT_FAILURE : EXIT_SUCCESS;
index f4aff6b..10188cc 100644 (file)
@@ -105,6 +105,13 @@ struct ipv6_packet {
 } __packed;
 extern struct ipv6_packet pkt_v6;
 
+#define PRINT_FAIL(format...)                                                  \
+       ({                                                                     \
+               test__fail();                                                  \
+               fprintf(stdout, "%s:FAIL:%d ", __func__, __LINE__);            \
+               fprintf(stdout, ##format);                                     \
+       })
+
 #define _CHECK(condition, tag, duration, format...) ({                 \
        int __ret = !!(condition);                                      \
        int __save_errno = errno;                                       \
index 87eaa49..21a1ce2 100644 (file)
@@ -50,7 +50,7 @@
 #define MAX_INSNS      BPF_MAXINSNS
 #define MAX_TEST_INSNS 1000000
 #define MAX_FIXUPS     8
-#define MAX_NR_MAPS    19
+#define MAX_NR_MAPS    20
 #define MAX_TEST_RUNS  8
 #define POINTER_VALUE  0xcafe4all
 #define TEST_DATA_LEN  64
@@ -86,6 +86,7 @@ struct bpf_test {
        int fixup_map_array_small[MAX_FIXUPS];
        int fixup_sk_storage_map[MAX_FIXUPS];
        int fixup_map_event_output[MAX_FIXUPS];
+       int fixup_map_reuseport_array[MAX_FIXUPS];
        const char *errstr;
        const char *errstr_unpriv;
        uint32_t insn_processed;
@@ -637,6 +638,7 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
        int *fixup_map_array_small = test->fixup_map_array_small;
        int *fixup_sk_storage_map = test->fixup_sk_storage_map;
        int *fixup_map_event_output = test->fixup_map_event_output;
+       int *fixup_map_reuseport_array = test->fixup_map_reuseport_array;
 
        if (test->fill_helper) {
                test->fill_insns = calloc(MAX_TEST_INSNS, sizeof(struct bpf_insn));
@@ -806,6 +808,14 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
                        fixup_map_event_output++;
                } while (*fixup_map_event_output);
        }
+       if (*fixup_map_reuseport_array) {
+               map_fds[19] = __create_map(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
+                                          sizeof(u32), sizeof(u64), 1, 0);
+               do {
+                       prog[*fixup_map_reuseport_array].imm = map_fds[19];
+                       fixup_map_reuseport_array++;
+               } while (*fixup_map_reuseport_array);
+       }
 }
 
 static int set_admin(bool admin)
@@ -943,7 +953,12 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
        attr.insns = prog;
        attr.insns_cnt = prog_len;
        attr.license = "GPL";
-       attr.log_level = verbose || expected_ret == VERBOSE_ACCEPT ? 1 : 4;
+       if (verbose)
+               attr.log_level = 1;
+       else if (expected_ret == VERBOSE_ACCEPT)
+               attr.log_level = 2;
+       else
+               attr.log_level = 4;
        attr.prog_flags = pflags;
 
        fd_prog = bpf_load_program_xattr(&attr, bpf_vlog, sizeof(bpf_vlog));
index 4d0d095..a253a06 100644 (file)
        .result = REJECT
 },
 {
-       "bounds check mixed 32bit and 64bit arithmatic. test1",
+       "bounds check mixed 32bit and 64bit arithmetic. test1",
        .insns = {
        BPF_MOV64_IMM(BPF_REG_0, 0),
        BPF_MOV64_IMM(BPF_REG_1, -1),
        .result = ACCEPT
 },
 {
-       "bounds check mixed 32bit and 64bit arithmatic. test2",
+       "bounds check mixed 32bit and 64bit arithmetic. test2",
        .insns = {
        BPF_MOV64_IMM(BPF_REG_0, 0),
        BPF_MOV64_IMM(BPF_REG_1, -1),
index 130553e..99f8f58 100644 (file)
        .result = ACCEPT,
        .retval = 1,
 },
+{
+       "perfevent for cgroup dev",
+       .insns =  { __PERF_EVENT_INSNS__ },
+       .prog_type = BPF_PROG_TYPE_CGROUP_DEVICE,
+       .fixup_map_event_output = { 4 },
+       .result = ACCEPT,
+       .retval = 1,
+},
+{
+       "perfevent for cgroup sysctl",
+       .insns =  { __PERF_EVENT_INSNS__ },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SYSCTL,
+       .fixup_map_event_output = { 4 },
+       .result = ACCEPT,
+       .retval = 1,
+},
+{
+       "perfevent for cgroup sockopt",
+       .insns =  { __PERF_EVENT_INSNS__ },
+       .prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT,
+       .fixup_map_event_output = { 4 },
+       .result = ACCEPT,
+       .retval = 1,
+},
index da7a4b3..fc4e301 100644 (file)
@@ -1,33 +1,3 @@
-{
-       "prevent map lookup in sockmap",
-       .insns = {
-       BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
-       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
-       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
-       BPF_LD_MAP_FD(BPF_REG_1, 0),
-       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
-       BPF_EXIT_INSN(),
-       },
-       .fixup_map_sockmap = { 3 },
-       .result = REJECT,
-       .errstr = "cannot pass map_type 15 into func bpf_map_lookup_elem",
-       .prog_type = BPF_PROG_TYPE_SOCK_OPS,
-},
-{
-       "prevent map lookup in sockhash",
-       .insns = {
-       BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
-       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
-       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
-       BPF_LD_MAP_FD(BPF_REG_1, 0),
-       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
-       BPF_EXIT_INSN(),
-       },
-       .fixup_map_sockhash = { 3 },
-       .result = REJECT,
-       .errstr = "cannot pass map_type 18 into func bpf_map_lookup_elem",
-       .prog_type = BPF_PROG_TYPE_SOCK_OPS,
-},
 {
        "prevent map lookup in stack trace",
        .insns = {
index 9ed192e..0bc51ad 100644 (file)
        .prog_type = BPF_PROG_TYPE_XDP,
        .result = ACCEPT,
 },
+{
+       "bpf_map_lookup_elem(sockmap, &key)",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1, 0),
+       BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_sockmap = { 3 },
+       .prog_type = BPF_PROG_TYPE_SK_SKB,
+       .result = REJECT,
+       .errstr = "Unreleased reference id=2 alloc_insn=5",
+},
+{
+       "bpf_map_lookup_elem(sockhash, &key)",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1, 0),
+       BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_sockhash = { 3 },
+       .prog_type = BPF_PROG_TYPE_SK_SKB,
+       .result = REJECT,
+       .errstr = "Unreleased reference id=2 alloc_insn=5",
+},
+{
+       "bpf_map_lookup_elem(sockmap, &key); sk->type [fullsock field]; bpf_sk_release(sk)",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1, 0),
+       BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, type)),
+       BPF_EMIT_CALL(BPF_FUNC_sk_release),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_sockmap = { 3 },
+       .prog_type = BPF_PROG_TYPE_SK_SKB,
+       .result = ACCEPT,
+},
+{
+       "bpf_map_lookup_elem(sockhash, &key); sk->type [fullsock field]; bpf_sk_release(sk)",
+       .insns = {
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+       BPF_LD_MAP_FD(BPF_REG_1, 0),
+       BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, type)),
+       BPF_EMIT_CALL(BPF_FUNC_sk_release),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_sockhash = { 3 },
+       .prog_type = BPF_PROG_TYPE_SK_SKB,
+       .result = ACCEPT,
+},
+{
+       "bpf_sk_select_reuseport(ctx, reuseport_array, &key, flags)",
+       .insns = {
+       BPF_MOV64_IMM(BPF_REG_4, 0),
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -4),
+       BPF_LD_MAP_FD(BPF_REG_2, 0),
+       BPF_EMIT_CALL(BPF_FUNC_sk_select_reuseport),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_reuseport_array = { 4 },
+       .prog_type = BPF_PROG_TYPE_SK_REUSEPORT,
+       .result = ACCEPT,
+},
+{
+       "bpf_sk_select_reuseport(ctx, sockmap, &key, flags)",
+       .insns = {
+       BPF_MOV64_IMM(BPF_REG_4, 0),
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -4),
+       BPF_LD_MAP_FD(BPF_REG_2, 0),
+       BPF_EMIT_CALL(BPF_FUNC_sk_select_reuseport),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_sockmap = { 4 },
+       .prog_type = BPF_PROG_TYPE_SK_REUSEPORT,
+       .result = ACCEPT,
+},
+{
+       "bpf_sk_select_reuseport(ctx, sockhash, &key, flags)",
+       .insns = {
+       BPF_MOV64_IMM(BPF_REG_4, 0),
+       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0),
+       BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -4),
+       BPF_LD_MAP_FD(BPF_REG_2, 0),
+       BPF_EMIT_CALL(BPF_FUNC_sk_select_reuseport),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_sockmap = { 4 },
+       .prog_type = BPF_PROG_TYPE_SK_REUSEPORT,
+       .result = ACCEPT,
+},
index 7276620..8bfeb77 100644 (file)
        },
        .result = ACCEPT,
 },
+{
+       "store PTR_TO_STACK in R10 to array map using BPF_B",
+       .insns = {
+       /* Load pointer to map. */
+       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+       BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),
+       BPF_LD_MAP_FD(BPF_REG_1, 0),
+       BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 2),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+       /* Copy R10 to R9. */
+       BPF_MOV64_REG(BPF_REG_9, BPF_REG_10),
+       /* Pollute other registers with unaligned values. */
+       BPF_MOV64_IMM(BPF_REG_2, -1),
+       BPF_MOV64_IMM(BPF_REG_3, -1),
+       BPF_MOV64_IMM(BPF_REG_4, -1),
+       BPF_MOV64_IMM(BPF_REG_5, -1),
+       BPF_MOV64_IMM(BPF_REG_6, -1),
+       BPF_MOV64_IMM(BPF_REG_7, -1),
+       BPF_MOV64_IMM(BPF_REG_8, -1),
+       /* Store both R9 and R10 with BPF_B and read back. */
+       BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_10, 0),
+       BPF_LDX_MEM(BPF_B, BPF_REG_2, BPF_REG_1, 0),
+       BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_9, 0),
+       BPF_LDX_MEM(BPF_B, BPF_REG_3, BPF_REG_1, 0),
+       /* Should read back as same value. */
+       BPF_JMP_REG(BPF_JEQ, BPF_REG_2, BPF_REG_3, 2),
+       BPF_MOV64_IMM(BPF_REG_0, 1),
+       BPF_EXIT_INSN(),
+       BPF_MOV64_IMM(BPF_REG_0, 42),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_array_48b = { 3 },
+       .result = ACCEPT,
+       .retval = 42,
+       .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+},
index 7f6c232..ed1c2ce 100644 (file)
@@ -88,6 +88,7 @@
        BPF_EXIT_INSN(),
        },
        .fixup_map_hash_48b = { 3 },
+       .errstr_unpriv = "leaking pointer from stack off -8",
        .errstr = "R0 invalid mem access 'inv'",
        .result = REJECT,
        .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
index 9f97414..ad539ec 100755 (executable)
@@ -151,6 +151,19 @@ regions_test()
 
        check_region_snapshot_count dummy post-second-delete 2
 
+       sid=$(devlink -j region new $DL_HANDLE/dummy | jq '.[][][][]')
+       check_err $? "Failed to create a new snapshot with id allocated by the kernel"
+
+       check_region_snapshot_count dummy post-first-request 3
+
+       devlink region dump $DL_HANDLE/dummy snapshot $sid >> /dev/null
+       check_err $? "Failed to dump a snapshot with id allocated by the kernel"
+
+       devlink region del $DL_HANDLE/dummy snapshot $sid
+       check_err $? "Failed to delete snapshot with id allocated by the kernel"
+
+       check_region_snapshot_count dummy post-first-request 2
+
        log_test "regions test"
 }
 
index c2c8de4..e59d985 100644 (file)
@@ -11,5 +11,6 @@ CONFIG_PREEMPTIRQ_DELAY_TEST=m
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_SAMPLES=y
+CONFIG_SAMPLE_FTRACE_DIRECT=m
 CONFIG_SAMPLE_TRACE_PRINTK=m
 CONFIG_KALLSYMS_ALL=y
index aefab0c..f598538 100644 (file)
@@ -10,10 +10,7 @@ if ! grep -q function_graph available_tracers; then
     exit_unsupported
 fi
 
-if [ ! -f set_ftrace_filter ]; then
-    echo "set_ftrace_filter not found? Is dynamic ftrace not set?"
-    exit_unsupported
-fi
+check_filter_file set_ftrace_filter
 
 do_reset() {
     if [ -e /proc/sys/kernel/stack_tracer_enabled ]; then
index c8a5209..d610f47 100644 (file)
@@ -9,6 +9,8 @@ if ! grep -q function_graph available_tracers; then
     exit_unsupported
 fi
 
+check_filter_file set_ftrace_filter
+
 fail() { # msg
     echo $1
     exit_fail
index f4e92af..28936f4 100644 (file)
@@ -9,6 +9,8 @@ if ! grep -q function available_tracers; then
     exit_unsupported
 fi
 
+check_filter_file set_ftrace_filter
+
 disable_tracing
 clear_trace
 
index 8aa46a2..71db68a 100644 (file)
@@ -15,10 +15,7 @@ if [ ! -f set_ftrace_notrace_pid ]; then
     exit_unsupported
 fi
 
-if [ ! -f set_ftrace_filter ]; then
-    echo "set_ftrace_filter not found? Is function tracer not set?"
-    exit_unsupported
-fi
+check_filter_file set_ftrace_filter
 
 do_function_fork=1
 
index f2ee1e8..d58403c 100644 (file)
@@ -16,10 +16,7 @@ if [ ! -f set_ftrace_pid ]; then
     exit_unsupported
 fi
 
-if [ ! -f set_ftrace_filter ]; then
-    echo "set_ftrace_filter not found? Is function tracer not set?"
-    exit_unsupported
-fi
+check_filter_file set_ftrace_filter
 
 do_function_fork=1
 
index 1a52f28..b2aff78 100644 (file)
@@ -3,7 +3,7 @@
 # description: ftrace - stacktrace filter command
 # flags: instance
 
-[ ! -f set_ftrace_filter ] && exit_unsupported
+check_filter_file set_ftrace_filter
 
 echo _do_fork:stacktrace >> set_ftrace_filter
 
index ca2ffd7..e9b1fd5 100644 (file)
 #
 
 # The triggers are set within the set_ftrace_filter file
-if [ ! -f set_ftrace_filter ]; then
-    echo "set_ftrace_filter not found? Is dynamic ftrace not set?"
-    exit_unsupported
-fi
+check_filter_file set_ftrace_filter
 
 do_reset() {
     reset_ftrace_filter
index 9330c87..1a4b4a4 100644 (file)
@@ -2,7 +2,7 @@
 # SPDX-License-Identifier: GPL-2.0
 # description: ftrace - function trace on module
 
-[ ! -f set_ftrace_filter ] && exit_unsupported
+check_filter_file set_ftrace_filter
 
 : "mod: allows to filter a non exist function"
 echo 'non_exist_func:mod:non_exist_module' > set_ftrace_filter
index dfbae63..a3dadb6 100644 (file)
@@ -18,10 +18,7 @@ if ! grep -q function_graph available_tracers; then
     exit_unsupported;
 fi
 
-if [ ! -f set_ftrace_filter ]; then
-    echo "set_ftrace_filter not found? Is dynamic ftrace not set?"
-    exit_unsupported
-fi
+check_filter_file set_ftrace_filter
 
 if [ ! -f function_profile_enabled ]; then
     echo "function_profile_enabled not found, function profiling enabled?"
index 51f6e61..70bad44 100644 (file)
 #
 
 # The triggers are set within the set_ftrace_filter file
-if [ ! -f set_ftrace_filter ]; then
-    echo "set_ftrace_filter not found? Is dynamic ftrace not set?"
-    exit_unsupported
-fi
+check_filter_file set_ftrace_filter
 
 fail() { # mesg
     echo $1
index b414f0e..51e9e80 100644 (file)
@@ -8,6 +8,8 @@ if [ ! -f stack_trace ]; then
   exit_unsupported
 fi
 
+check_filter_file stack_trace_filter
+
 echo > stack_trace_filter
 echo 0 > stack_max_size
 echo 1 > /proc/sys/kernel/stack_tracer_enabled
index 1947387..3ed173f 100644 (file)
 #
 
 # The triggers are set within the set_ftrace_filter file
-if [ ! -f set_ftrace_filter ]; then
-    echo "set_ftrace_filter not found? Is dynamic ftrace not set?"
-    exit_unsupported
-fi
+check_filter_file set_ftrace_filter
 
 fail() { # mesg
     echo $1
index 5d45505..61a3c7e 100644 (file)
@@ -1,3 +1,9 @@
+check_filter_file() { # check filter file introduced by dynamic ftrace
+    if [ ! -f "$1" ]; then
+        echo "$1 not found? Is dynamic ftrace not set?"
+        exit_unsupported
+    fi
+}
 
 clear_trace() { # reset trace output
     echo > trace
index 1bcb67d..81490ec 100644 (file)
@@ -38,7 +38,7 @@ for width in 64 32 16 8; do
   echo 0 > events/kprobes/testprobe/enable
 
   : "Confirm the arguments is recorded in given types correctly"
-  ARGS=`grep "testprobe" trace | sed -e 's/.* arg1=\(.*\) arg2=\(.*\) arg3=\(.*\) arg4=\(.*\)/\1 \2 \3 \4/'`
+  ARGS=`grep "testprobe" trace | head -n 1 | sed -e 's/.* arg1=\(.*\) arg2=\(.*\) arg3=\(.*\) arg4=\(.*\)/\1 \2 \3 \4/'`
   check_types $ARGS $width
 
   : "Clear event for next loop"
index 7650a82..df50728 100644 (file)
@@ -5,6 +5,8 @@
 [ -f kprobe_events ] || exit_unsupported # this is configurable
 grep "function" available_tracers || exit_unsupported # this is configurable
 
+check_filter_file set_ftrace_filter
+
 # prepare
 echo nop > current_tracer
 echo _do_fork > set_ftrace_filter
index 0bb8061..32bdc97 100644 (file)
@@ -1,13 +1,13 @@
 # SPDX-License-Identifier: GPL-2.0
 
-MOUNT_CFLAGS := $(shell pkg-config --cflags mount 2>/dev/null)
-MOUNT_LDLIBS := $(shell pkg-config --libs mount 2>/dev/null)
-ifeq ($(MOUNT_LDLIBS),)
-MOUNT_LDLIBS := -lmount -I/usr/include/libmount
+VAR_CFLAGS := $(shell pkg-config --cflags mount 2>/dev/null)
+VAR_LDLIBS := $(shell pkg-config --libs mount 2>/dev/null)
+ifeq ($(VAR_LDLIBS),)
+VAR_LDLIBS := -lmount -I/usr/include/libmount
 endif
 
-CFLAGS += -O2 -g -std=gnu99 -Wall -I../../../../usr/include/ $(MOUNT_CFLAGS)
-LDLIBS += $(MOUNT_LDLIBS)
+CFLAGS += -O2 -g -std=gnu99 -Wall -I../../../../usr/include/ $(VAR_CFLAGS)
+LDLIBS += $(VAR_LDLIBS)
 
 TEST_PROGS := gpio-mockup.sh
 TEST_FILES := gpio-mockup-sysfs.sh
index 7340fd6..39f0fa2 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 CFLAGS := $(CFLAGS) -Wall -D_GNU_SOURCE
-LDLIBS := $(LDLIBS) -lm
+LDLIBS += -lm
 
 uname_M := $(shell uname -m 2>/dev/null || echo not)
 ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
index 4c156ae..5ec4d9e 100644 (file)
@@ -137,7 +137,7 @@ int dump_queue(struct msgque_data *msgque)
        for (kern_id = 0; kern_id < 256; kern_id++) {
                ret = msgctl(kern_id, MSG_STAT, &ds);
                if (ret < 0) {
-                       if (errno == -EINVAL)
+                       if (errno == EINVAL)
                                continue;
                        printf("Failed to get stats for IPC queue with id %d\n",
                                        kern_id);
index e84d901..676b3a8 100644 (file)
@@ -33,7 +33,7 @@ tap_timeout()
 {
        # Make sure tests will time out if utility is available.
        if [ -x /usr/bin/timeout ] ; then
-               /usr/bin/timeout "$kselftest_timeout" "$1"
+               /usr/bin/timeout --foreground "$kselftest_timeout" "$1"
        else
                "$1"
        fi
diff --git a/tools/testing/selftests/kselftest_deps.sh b/tools/testing/selftests/kselftest_deps.sh
new file mode 100755 (executable)
index 0000000..bbc0464
--- /dev/null
@@ -0,0 +1,272 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# kselftest_deps.sh
+#
+# Checks for kselftest build dependencies on the build system.
+# Copyright (c) 2020 Shuah Khan <skhan@linuxfoundation.org>
+#
+#
+
+usage()
+{
+
+echo -e "Usage: $0 -[p] <compiler> [test_name]\n"
+echo -e "\tkselftest_deps.sh [-p] gcc"
+echo -e "\tkselftest_deps.sh [-p] gcc vm"
+echo -e "\tkselftest_deps.sh [-p] aarch64-linux-gnu-gcc"
+echo -e "\tkselftest_deps.sh [-p] aarch64-linux-gnu-gcc vm\n"
+echo "- Should be run in selftests directory in the kernel repo."
+echo "- Checks if Kselftests can be built/cross-built on a system."
+echo "- Parses all test/sub-test Makefile to find library dependencies."
+echo "- Runs compile test on a trivial C file with LDLIBS specified"
+echo "  in the test Makefiles to identify missing library dependencies."
+echo "- Prints suggested target list for a system filtering out tests"
+echo "  failed the build dependency check from the TARGETS in Selftests"
+echo "  main Makefile when optional -p is specified."
+echo "- Prints pass/fail dependency check for each tests/sub-test."
+echo "- Prints pass/fail targets and libraries."
+echo "- Default: runs dependency checks on all tests."
+echo "- Optional test name can be specified to check dependencies for it."
+exit 1
+
+}
+
+# Start main()
+main()
+{
+
+base_dir=`pwd`
+# Make sure we're in the selftests top-level directory.
+if [ $(basename "$base_dir") !=  "selftests" ]; then
+       echo -e "\tPlease run $0 in"
+       echo -e "\ttools/testing/selftests directory ..."
+       exit 1
+fi
+
+print_targets=0
+
+while getopts "p" arg; do
+    case $arg in
+        p)
+               print_targets=1
+       shift;;
+    esac
+done
+
+if [ $# -eq 0 ]
+then
+       usage
+fi
+
+# Compiler
+CC=$1
+
+tmp_file=$(mktemp).c
+trap "rm -f $tmp_file.o $tmp_file $tmp_file.bin" EXIT
+#echo $tmp_file
+
+pass=$(mktemp).out
+trap "rm -f $pass" EXIT
+#echo $pass
+
+fail=$(mktemp).out
+trap "rm -f $fail" EXIT
+#echo $fail
+
+# Generate tmp source fire for compile test
+cat << "EOF" > $tmp_file
+int main()
+{
+}
+EOF
+
+# Save results
+total_cnt=0
+fail_trgts=()
+fail_libs=()
+fail_cnt=0
+pass_trgts=()
+pass_libs=()
+pass_cnt=0
+
+# Get all TARGETS from selftests Makefile
+targets=$(egrep "^TARGETS +|^TARGETS =" Makefile | cut -d "=" -f2)
+
+# Single test case
+if [ $# -eq 2 ]
+then
+       test=$2/Makefile
+
+       l1_test $test
+       l2_test $test
+       l3_test $test
+
+       print_results $1 $2
+       exit $?
+fi
+
+# Level 1: LDLIBS set static.
+#
+# Find all LDLIBS set statically for all executables built by a Makefile
+# and filter out VAR_LDLIBS to discard the following:
+#      gpio/Makefile:LDLIBS += $(VAR_LDLIBS)
+# Append space at the end of the list to append more tests.
+
+l1_tests=$(grep -r --include=Makefile "^LDLIBS" | \
+               grep -v "VAR_LDLIBS" | awk -F: '{print $1}')
+
+# Level 2: LDLIBS set dynamically.
+#
+# Level 2
+# Some tests have multiple valid LDLIBS lines for individual sub-tests
+# that need dependency checks. Find them and append them to the tests
+# e.g: vm/Makefile:$(OUTPUT)/userfaultfd: LDLIBS += -lpthread
+# Filter out VAR_LDLIBS to discard the following:
+#      memfd/Makefile:$(OUTPUT)/fuse_mnt: LDLIBS += $(VAR_LDLIBS)
+# Append space at the end of the list to append more tests.
+
+l2_tests=$(grep -r --include=Makefile ": LDLIBS" | \
+               grep -v "VAR_LDLIBS" | awk -F: '{print $1}')
+
+# Level 3
+# gpio,  memfd and others use pkg-config to find mount and fuse libs
+# respectively and save it in VAR_LDLIBS. If pkg-config doesn't find
+# any, VAR_LDLIBS set to default.
+# Use the default value and filter out pkg-config for dependency check.
+# e.g:
+# gpio/Makefile
+#      VAR_LDLIBS := $(shell pkg-config --libs mount) 2>/dev/null)
+# memfd/Makefile
+#      VAR_LDLIBS := $(shell pkg-config fuse --libs 2>/dev/null)
+
+l3_tests=$(grep -r --include=Makefile "^VAR_LDLIBS" | \
+               grep -v "pkg-config" | awk -F: '{print $1}')
+
+#echo $l1_tests
+#echo $l2_1_tests
+#echo $l3_tests
+
+all_tests
+print_results $1 $2
+
+exit $?
+}
+# end main()
+
+all_tests()
+{
+       for test in $l1_tests; do
+               l1_test $test
+       done
+
+       for test in $l2_tests; do
+               l2_test $test
+       done
+
+       for test in $l3_tests; do
+               l3_test $test
+       done
+}
+
+# Use same parsing used for l1_tests and pick libraries this time.
+l1_test()
+{
+       test_libs=$(grep --include=Makefile "^LDLIBS" $test | \
+                       grep -v "VAR_LDLIBS" | \
+                       sed -e 's/\:/ /' | \
+                       sed -e 's/+/ /' | cut -d "=" -f 2)
+
+       check_libs $test $test_libs
+}
+
+# Use same parsing used for l2__tests and pick libraries this time.
+l2_test()
+{
+       test_libs=$(grep --include=Makefile ": LDLIBS" $test | \
+                       grep -v "VAR_LDLIBS" | \
+                       sed -e 's/\:/ /' | sed -e 's/+/ /' | \
+                       cut -d "=" -f 2)
+
+       check_libs $test $test_libs
+}
+
+l3_test()
+{
+       test_libs=$(grep --include=Makefile "^VAR_LDLIBS" $test | \
+                       grep -v "pkg-config" | sed -e 's/\:/ /' |
+                       sed -e 's/+/ /' | cut -d "=" -f 2)
+
+       check_libs $test $test_libs
+}
+
+check_libs()
+{
+
+if [[ ! -z "${test_libs// }" ]]
+then
+
+       #echo $test_libs
+
+       for lib in $test_libs; do
+
+       let total_cnt+=1
+       $CC -o $tmp_file.bin $lib $tmp_file > /dev/null 2>&1
+       if [ $? -ne 0 ]; then
+               echo "FAIL: $test dependency check: $lib" >> $fail
+               let fail_cnt+=1
+               fail_libs+="$lib "
+               fail_target=$(echo "$test" | cut -d "/" -f1)
+               fail_trgts+="$fail_target "
+               targets=$(echo "$targets" | grep -v "$fail_target")
+       else
+               echo "PASS: $test dependency check passed $lib" >> $pass
+               let pass_cnt+=1
+               pass_libs+="$lib "
+               pass_trgts+="$(echo "$test" | cut -d "/" -f1) "
+       fi
+
+       done
+fi
+}
+
+print_results()
+{
+       echo -e "========================================================";
+       echo -e "Kselftest Dependency Check for [$0 $1 $2] results..."
+
+       if [ $print_targets -ne 0 ]
+       then
+       echo -e "Suggested Selftest Targets for your configuration:"
+       echo -e "$targets";
+       fi
+
+       echo -e "========================================================";
+       echo -e "Checked tests defining LDLIBS dependencies"
+       echo -e "--------------------------------------------------------";
+       echo -e "Total tests with Dependencies:"
+       echo -e "$total_cnt Pass: $pass_cnt Fail: $fail_cnt";
+
+       if [ $pass_cnt -ne 0 ]; then
+       echo -e "--------------------------------------------------------";
+       cat $pass
+       echo -e "--------------------------------------------------------";
+       echo -e "Targets passed build dependency check on system:"
+       echo -e "$(echo "$pass_trgts" | xargs -n1 | sort -u | xargs)"
+       fi
+
+       if [ $fail_cnt -ne 0 ]; then
+       echo -e "--------------------------------------------------------";
+       cat $fail
+       echo -e "--------------------------------------------------------";
+       echo -e "Targets failed build dependency check on system:"
+       echo -e "$(echo "$fail_trgts" | xargs -n1 | sort -u | xargs)"
+       echo -e "--------------------------------------------------------";
+       echo -e "Missing libraries system"
+       echo -e "$(echo "$fail_libs" | xargs -n1 | sort -u | xargs)"
+       fi
+
+       echo -e "--------------------------------------------------------";
+       echo -e "========================================================";
+}
+
+main "$@"
index 2902f6a..c9f03ef 100644 (file)
 
 #define __TEST_IMPL(test_name, _signal) \
        static void test_name(struct __test_metadata *_metadata); \
+       static inline void wrapper_##test_name( \
+               struct __test_metadata *_metadata, \
+               struct __fixture_variant_metadata *variant) \
+       { \
+               test_name(_metadata); \
+       } \
        static struct __test_metadata _##test_name##_object = \
-               { .name = "global." #test_name, \
-                 .fn = &test_name, .termsig = _signal, \
+               { .name = #test_name, \
+                 .fn = &wrapper_##test_name, \
+                 .fixture = &_fixture_global, \
+                 .termsig = _signal, \
                  .timeout = TEST_TIMEOUT_DEFAULT, }; \
        static void __attribute__((constructor)) _register_##test_name(void) \
        { \
  * populated and cleaned up using FIXTURE_SETUP() and FIXTURE_TEARDOWN().
  */
 #define FIXTURE(fixture_name) \
+       FIXTURE_VARIANT(fixture_name); \
+       static struct __fixture_metadata _##fixture_name##_fixture_object = \
+               { .name =  #fixture_name, }; \
        static void __attribute__((constructor)) \
        _register_##fixture_name##_data(void) \
        { \
-               __fixture_count++; \
+               __register_fixture(&_##fixture_name##_fixture_object); \
        } \
        FIXTURE_DATA(fixture_name)
 
 #define FIXTURE_SETUP(fixture_name) \
        void fixture_name##_setup( \
                struct __test_metadata __attribute__((unused)) *_metadata, \
-               FIXTURE_DATA(fixture_name) __attribute__((unused)) *self)
+               FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \
+               const FIXTURE_VARIANT(fixture_name) \
+                       __attribute__((unused)) *variant)
+
 /**
  * FIXTURE_TEARDOWN(fixture_name)
  * *_metadata* is included so that EXPECT_* and ASSERT_* work correctly.
                struct __test_metadata __attribute__((unused)) *_metadata, \
                FIXTURE_DATA(fixture_name) __attribute__((unused)) *self)
 
+/**
+ * FIXTURE_VARIANT(fixture_name) - Optionally called once per fixture
+ * to declare fixture variant
+ *
+ * @fixture_name: fixture name
+ *
+ * .. code-block:: c
+ *
+ *     FIXTURE_VARIANT(datatype name) {
+ *       type property1;
+ *       ...
+ *     };
+ *
+ * Defines type of constant parameters provided to FIXTURE_SETUP() and TEST_F()
+ * as *variant*. Variants allow the same tests to be run with different
+ * arguments.
+ */
+#define FIXTURE_VARIANT(fixture_name) struct _fixture_variant_##fixture_name
+
+/**
+ * FIXTURE_VARIANT_ADD(fixture_name, variant_name) - Called once per fixture
+ * variant to setup and register the data
+ *
+ * @fixture_name: fixture name
+ * @variant_name: name of the parameter set
+ *
+ * .. code-block:: c
+ *
+ *     FIXTURE_ADD(datatype name) {
+ *       .property1 = val1;
+ *       ...
+ *     };
+ *
+ * Defines a variant of the test fixture, provided to FIXTURE_SETUP() and
+ * TEST_F() as *variant*. Tests of each fixture will be run once for each
+ * variant.
+ */
+#define FIXTURE_VARIANT_ADD(fixture_name, variant_name) \
+       extern FIXTURE_VARIANT(fixture_name) \
+               _##fixture_name##_##variant_name##_variant; \
+       static struct __fixture_variant_metadata \
+               _##fixture_name##_##variant_name##_object = \
+               { .name = #variant_name, \
+                 .data = &_##fixture_name##_##variant_name##_variant}; \
+       static void __attribute__((constructor)) \
+               _register_##fixture_name##_##variant_name(void) \
+       { \
+               __register_fixture_variant(&_##fixture_name##_fixture_object, \
+                       &_##fixture_name##_##variant_name##_object);    \
+       } \
+       FIXTURE_VARIANT(fixture_name) \
+               _##fixture_name##_##variant_name##_variant =
+
 /**
  * TEST_F(fixture_name, test_name) - Emits test registration and helpers for
  * fixture-based test cases
 #define __TEST_F_IMPL(fixture_name, test_name, signal, tmout) \
        static void fixture_name##_##test_name( \
                struct __test_metadata *_metadata, \
-               FIXTURE_DATA(fixture_name) *self); \
+               FIXTURE_DATA(fixture_name) *self, \
+               const FIXTURE_VARIANT(fixture_name) *variant); \
        static inline void wrapper_##fixture_name##_##test_name( \
-               struct __test_metadata *_metadata) \
+               struct __test_metadata *_metadata, \
+               struct __fixture_variant_metadata *variant) \
        { \
                /* fixture data is alloced, setup, and torn down per call. */ \
                FIXTURE_DATA(fixture_name) self; \
                memset(&self, 0, sizeof(FIXTURE_DATA(fixture_name))); \
-               fixture_name##_setup(_metadata, &self); \
+               fixture_name##_setup(_metadata, &self, variant->data); \
                /* Let setup failure terminate early. */ \
                if (!_metadata->passed) \
                        return; \
-               fixture_name##_##test_name(_metadata, &self); \
+               fixture_name##_##test_name(_metadata, &self, variant->data); \
                fixture_name##_teardown(_metadata, &self); \
        } \
        static struct __test_metadata \
                      _##fixture_name##_##test_name##_object = { \
-               .name = #fixture_name "." #test_name, \
+               .name = #test_name, \
                .fn = &wrapper_##fixture_name##_##test_name, \
+               .fixture = &_##fixture_name##_fixture_object, \
                .termsig = signal, \
                .timeout = tmout, \
         }; \
        } \
        static void fixture_name##_##test_name( \
                struct __test_metadata __attribute__((unused)) *_metadata, \
-               FIXTURE_DATA(fixture_name) __attribute__((unused)) *self)
+               FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \
+               const FIXTURE_VARIANT(fixture_name) \
+                       __attribute__((unused)) *variant)
 
 /**
  * TEST_HARNESS_MAIN - Simple wrapper to run the test harness
        } \
 } while (0); OPTIONAL_HANDLER(_assert)
 
+/* List helpers */
+#define __LIST_APPEND(head, item) \
+{ \
+       /* Circular linked list where only prev is circular. */ \
+       if (head == NULL) { \
+               head = item; \
+               item->next = NULL; \
+               item->prev = item; \
+               return; \
+       } \
+       if (__constructor_order == _CONSTRUCTOR_ORDER_FORWARD) { \
+               item->next = NULL; \
+               item->prev = head->prev; \
+               item->prev->next = item; \
+               head->prev = item; \
+       } else { \
+               item->next = head; \
+               item->next->prev = item; \
+               item->prev = item; \
+               head = item; \
+       } \
+}
+
+struct __test_metadata;
+struct __fixture_variant_metadata;
+
+/* Contains all the information about a fixture. */
+struct __fixture_metadata {
+       const char *name;
+       struct __test_metadata *tests;
+       struct __fixture_variant_metadata *variant;
+       struct __fixture_metadata *prev, *next;
+} _fixture_global __attribute__((unused)) = {
+       .name = "global",
+       .prev = &_fixture_global,
+};
+
+static struct __fixture_metadata *__fixture_list = &_fixture_global;
+static int __constructor_order;
+
+#define _CONSTRUCTOR_ORDER_FORWARD   1
+#define _CONSTRUCTOR_ORDER_BACKWARD -1
+
+static inline void __register_fixture(struct __fixture_metadata *f)
+{
+       __LIST_APPEND(__fixture_list, f);
+}
+
+struct __fixture_variant_metadata {
+       const char *name;
+       const void *data;
+       struct __fixture_variant_metadata *prev, *next;
+};
+
+static inline void
+__register_fixture_variant(struct __fixture_metadata *f,
+                          struct __fixture_variant_metadata *variant)
+{
+       __LIST_APPEND(f->variant, variant);
+}
+
 /* Contains all the information for test execution and status checking. */
 struct __test_metadata {
        const char *name;
-       void (*fn)(struct __test_metadata *);
+       void (*fn)(struct __test_metadata *,
+                  struct __fixture_variant_metadata *);
        pid_t pid;      /* pid of test when being run */
+       struct __fixture_metadata *fixture;
        int termsig;
        int passed;
        int trigger; /* extra handler after the evaluation */
@@ -646,15 +781,6 @@ struct __test_metadata {
        struct __test_metadata *prev, *next;
 };
 
-/* Storage for the (global) tests to be run. */
-static struct __test_metadata *__test_list;
-static unsigned int __test_count;
-static unsigned int __fixture_count;
-static int __constructor_order;
-
-#define _CONSTRUCTOR_ORDER_FORWARD   1
-#define _CONSTRUCTOR_ORDER_BACKWARD -1
-
 /*
  * Since constructors are called in reverse order, reverse the test
  * list so tests are run in source declaration order.
@@ -666,25 +792,7 @@ static int __constructor_order;
  */
 static inline void __register_test(struct __test_metadata *t)
 {
-       __test_count++;
-       /* Circular linked list where only prev is circular. */
-       if (__test_list == NULL) {
-               __test_list = t;
-               t->next = NULL;
-               t->prev = t;
-               return;
-       }
-       if (__constructor_order == _CONSTRUCTOR_ORDER_FORWARD) {
-               t->next = NULL;
-               t->prev = __test_list->prev;
-               t->prev->next = t;
-               __test_list->prev = t;
-       } else {
-               t->next = __test_list;
-               t->next->prev = t;
-               t->prev = t;
-               __test_list = t;
-       }
+       __LIST_APPEND(t->fixture->tests, t);
 }
 
 static inline int __bail(int for_realz, bool no_print, __u8 step)
@@ -705,7 +813,7 @@ static void __timeout_handler(int sig, siginfo_t *info, void *ucontext)
        /* Sanity check handler execution environment. */
        if (!t) {
                fprintf(TH_LOG_STREAM,
-                       "no active test in SIGARLM handler!?\n");
+                       "no active test in SIGALRM handler!?\n");
                abort();
        }
        if (sig != SIGALRM || sig != info->si_signo) {
@@ -731,7 +839,7 @@ void __wait_for_test(struct __test_metadata *t)
        if (sigaction(SIGALRM, &action, &saved_action)) {
                t->passed = 0;
                fprintf(TH_LOG_STREAM,
-                       "%s: unable to install SIGARLM handler\n",
+                       "%s: unable to install SIGALRM handler\n",
                        t->name);
                return;
        }
@@ -743,7 +851,7 @@ void __wait_for_test(struct __test_metadata *t)
        if (sigaction(SIGALRM, &saved_action, NULL)) {
                t->passed = 0;
                fprintf(TH_LOG_STREAM,
-                       "%s: unable to uninstall SIGARLM handler\n",
+                       "%s: unable to uninstall SIGALRM handler\n",
                        t->name);
                return;
        }
@@ -790,43 +898,67 @@ void __wait_for_test(struct __test_metadata *t)
        }
 }
 
-void __run_test(struct __test_metadata *t)
+void __run_test(struct __fixture_metadata *f,
+               struct __fixture_variant_metadata *variant,
+               struct __test_metadata *t)
 {
+       /* reset test struct */
        t->passed = 1;
        t->trigger = 0;
-       printf("[ RUN      ] %s\n", t->name);
+       t->step = 0;
+       t->no_print = 0;
+
+       printf("[ RUN      ] %s%s%s.%s\n",
+              f->name, variant->name[0] ? "." : "", variant->name, t->name);
        t->pid = fork();
        if (t->pid < 0) {
                printf("ERROR SPAWNING TEST CHILD\n");
                t->passed = 0;
        } else if (t->pid == 0) {
-               t->fn(t);
+               t->fn(t, variant);
                /* return the step that failed or 0 */
                _exit(t->passed ? 0 : t->step);
        } else {
                __wait_for_test(t);
        }
-       printf("[     %4s ] %s\n", (t->passed ? "OK" : "FAIL"), t->name);
+       printf("[     %4s ] %s%s%s.%s\n", (t->passed ? "OK" : "FAIL"),
+              f->name, variant->name[0] ? "." : "", variant->name, t->name);
 }
 
 static int test_harness_run(int __attribute__((unused)) argc,
                            char __attribute__((unused)) **argv)
 {
+       struct __fixture_variant_metadata no_variant = { .name = "", };
+       struct __fixture_variant_metadata *v;
+       struct __fixture_metadata *f;
        struct __test_metadata *t;
        int ret = 0;
+       unsigned int case_count = 0, test_count = 0;
        unsigned int count = 0;
        unsigned int pass_count = 0;
 
+       for (f = __fixture_list; f; f = f->next) {
+               for (v = f->variant ?: &no_variant; v; v = v->next) {
+                       case_count++;
+                       for (t = f->tests; t; t = t->next)
+                               test_count++;
+               }
+       }
+
        /* TODO(wad) add optional arguments similar to gtest. */
        printf("[==========] Running %u tests from %u test cases.\n",
-              __test_count, __fixture_count + 1);
-       for (t = __test_list; t; t = t->next) {
-               count++;
-               __run_test(t);
-               if (t->passed)
-                       pass_count++;
-               else
-                       ret = 1;
+              test_count, case_count);
+       for (f = __fixture_list; f; f = f->next) {
+               for (v = f->variant ?: &no_variant; v; v = v->next) {
+                       for (t = f->tests; t; t = t->next) {
+                               count++;
+                               __run_test(f, v, t);
+                               if (t->passed)
+                                       pass_count++;
+                               else
+                                       ret = 1;
+                       }
+               }
        }
        printf("[==========] %u / %u tests passed.\n", pass_count, count);
        printf("[  %s  ]\n", (ret ? "FAILED" : "PASSED"));
index 0a15f9e..4da8b56 100644 (file)
@@ -4,14 +4,25 @@ CFLAGS += -I../../../../include/uapi/
 CFLAGS += -I../../../../include/
 CFLAGS += -I../../../../usr/include/
 
-TEST_GEN_PROGS := memfd_test fuse_test fuse_mnt
+TEST_GEN_PROGS := memfd_test
 TEST_PROGS := run_fuse_test.sh run_hugetlbfs_test.sh
+TEST_GEN_FILES := fuse_test fuse_mnt
 
-fuse_mnt.o: CFLAGS += $(shell pkg-config fuse --cflags)
+VAR_CFLAGS := $(shell pkg-config fuse --cflags 2>/dev/null)
+ifeq ($(VAR_CFLAGS),)
+VAR_CFLAGS := -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
+endif
+
+VAR_LDLIBS := $(shell pkg-config fuse --libs 2>/dev/null)
+ifeq ($(VAR_LDLIBS),)
+VAR_LDLIBS := -lfuse -pthread
+endif
+
+fuse_mnt.o: CFLAGS += $(VAR_CFLAGS)
 
 include ../lib.mk
 
-$(OUTPUT)/fuse_mnt: LDLIBS += $(shell pkg-config fuse --libs)
+$(OUTPUT)/fuse_mnt: LDLIBS += $(VAR_LDLIBS)
 
 $(OUTPUT)/memfd_test: memfd_test.c common.c
 $(OUTPUT)/fuse_test: fuse_test.c common.c
index 3f386eb..895ec99 100644 (file)
@@ -16,6 +16,7 @@ TEST_PROGS += altnames.sh icmp_redirect.sh ip6_gre_headroom.sh
 TEST_PROGS += route_localnet.sh
 TEST_PROGS += reuseaddr_ports_exhausted.sh
 TEST_PROGS += txtimestamp.sh
+TEST_PROGS += vrf-xfrm-tests.sh
 TEST_PROGS_EXTENDED := in_netns.sh
 TEST_GEN_FILES =  socket nettest
 TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any
index 796670e..dd0e5fe 100755 (executable)
@@ -19,8 +19,8 @@ ret=0
 ksft_skip=4
 
 # all tests in this script. Can be overridden with -t option
-IPV4_TESTS="ipv4_fcnal ipv4_grp_fcnal ipv4_withv6_fcnal ipv4_fcnal_runtime"
-IPV6_TESTS="ipv6_fcnal ipv6_grp_fcnal ipv6_fcnal_runtime"
+IPV4_TESTS="ipv4_fcnal ipv4_grp_fcnal ipv4_withv6_fcnal ipv4_fcnal_runtime ipv4_compat_mode"
+IPV6_TESTS="ipv6_fcnal ipv6_grp_fcnal ipv6_fcnal_runtime ipv6_compat_mode"
 
 ALL_TESTS="basic ${IPV4_TESTS} ${IPV6_TESTS}"
 TESTS="${ALL_TESTS}"
@@ -150,31 +150,31 @@ setup()
        $IP li add veth1 type veth peer name veth2
        $IP li set veth1 up
        $IP addr add 172.16.1.1/24 dev veth1
-       $IP -6 addr add 2001:db8:91::1/64 dev veth1
+       $IP -6 addr add 2001:db8:91::1/64 dev veth1 nodad
 
        $IP li add veth3 type veth peer name veth4
        $IP li set veth3 up
        $IP addr add 172.16.2.1/24 dev veth3
-       $IP -6 addr add 2001:db8:92::1/64 dev veth3
+       $IP -6 addr add 2001:db8:92::1/64 dev veth3 nodad
 
        $IP li set veth2 netns peer up
        ip -netns peer addr add 172.16.1.2/24 dev veth2
-       ip -netns peer -6 addr add 2001:db8:91::2/64 dev veth2
+       ip -netns peer -6 addr add 2001:db8:91::2/64 dev veth2 nodad
 
        $IP li set veth4 netns peer up
        ip -netns peer addr add 172.16.2.2/24 dev veth4
-       ip -netns peer -6 addr add 2001:db8:92::2/64 dev veth4
+       ip -netns peer -6 addr add 2001:db8:92::2/64 dev veth4 nodad
 
        ip -netns remote li add veth5 type veth peer name veth6
        ip -netns remote li set veth5 up
        ip -netns remote addr add dev veth5 172.16.101.1/24
-       ip -netns remote addr add dev veth5 2001:db8:101::1/64
+       ip -netns remote -6 addr add dev veth5 2001:db8:101::1/64 nodad
        ip -netns remote ro add 172.16.0.0/22 via 172.16.101.2
        ip -netns remote -6 ro add 2001:db8:90::/40 via 2001:db8:101::2
 
        ip -netns remote li set veth6 netns peer up
        ip -netns peer addr add dev veth6 172.16.101.2/24
-       ip -netns peer addr add dev veth6 2001:db8:101::2/64
+       ip -netns peer -6 addr add dev veth6 2001:db8:101::2/64 nodad
        set +e
 }
 
@@ -248,11 +248,38 @@ check_route6()
        local expected="$2"
        local out
 
-       out=$($IP -6 route ls match ${pfx} 2>/dev/null)
+       out=$($IP -6 route ls match ${pfx} 2>/dev/null | sed -e 's/pref medium//')
 
        check_output "${out}" "${expected}"
 }
 
+start_ip_monitor()
+{
+       local mtype=$1
+
+       # start the monitor in the background
+       tmpfile=`mktemp /var/run/nexthoptestXXX`
+       mpid=`($IP monitor $mtype > $tmpfile & echo $!) 2>/dev/null`
+       sleep 0.2
+       echo "$mpid $tmpfile"
+}
+
+stop_ip_monitor()
+{
+       local mpid=$1
+       local tmpfile=$2
+       local el=$3
+
+       # check the monitor results
+       kill $mpid
+       lines=`wc -l $tmpfile | cut "-d " -f1`
+       test $lines -eq $el
+       rc=$?
+       rm -rf $tmpfile
+
+       return $rc
+}
+
 ################################################################################
 # basic operations (add, delete, replace) on nexthops and nexthop groups
 #
@@ -423,8 +450,6 @@ ipv6_fcnal_runtime()
        echo "IPv6 functional runtime"
        echo "-----------------------"
 
-       sleep 5
-
        #
        # IPv6 - the basics
        #
@@ -481,12 +506,12 @@ ipv6_fcnal_runtime()
        run_cmd "$IP -6 nexthop add id 85 dev veth1"
        run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 85"
        log_test $? 0 "IPv6 route with device only nexthop"
-       check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 85 dev veth1 metric 1024 pref medium"
+       check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 85 dev veth1 metric 1024"
 
        run_cmd "$IP nexthop add id 123 group 81/85"
        run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 123"
        log_test $? 0 "IPv6 multipath route with nexthop mix - dev only + gw"
-       check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 123 metric 1024 nexthop via 2001:db8:91::2 dev veth1 weight 1 nexthop dev veth1 weight 1 pref medium"
+       check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 123 metric 1024 nexthop via 2001:db8:91::2 dev veth1 weight 1 nexthop dev veth1 weight 1"
 
        #
        # IPv6 route with v4 nexthop - not allowed
@@ -749,6 +774,29 @@ ipv4_fcnal_runtime()
        run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
        log_test $? 0 "Ping - multipath"
 
+       run_cmd "$IP ro delete 172.16.101.1/32 nhid 122"
+
+       #
+       # multiple default routes
+       # - tests fib_select_default
+       run_cmd "$IP nexthop add id 501 via 172.16.1.2 dev veth1"
+       run_cmd "$IP ro add default nhid 501"
+       run_cmd "$IP ro add default via 172.16.1.3 dev veth1 metric 20"
+       run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+       log_test $? 0 "Ping - multiple default routes, nh first"
+
+       # flip the order
+       run_cmd "$IP ro del default nhid 501"
+       run_cmd "$IP ro del default via 172.16.1.3 dev veth1 metric 20"
+       run_cmd "$IP ro add default via 172.16.1.2 dev veth1 metric 20"
+       run_cmd "$IP nexthop replace id 501 via 172.16.1.3 dev veth1"
+       run_cmd "$IP ro add default nhid 501 metric 20"
+       run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+       log_test $? 0 "Ping - multiple default routes, nh second"
+
+       run_cmd "$IP nexthop delete nhid 501"
+       run_cmd "$IP ro del default"
+
        #
        # IPv4 with blackhole nexthops
        #
@@ -843,6 +891,11 @@ ipv4_fcnal_runtime()
                $IP neigh sh | grep 'dev veth1'
        fi
 
+       run_cmd "$IP ro del 172.16.101.1/32 via inet6 ${lladdr} dev veth1"
+       run_cmd "$IP -4 ro add default via inet6 ${lladdr} dev veth1"
+       run_cmd "ip netns exec me ping -c1 -w1 172.16.101.1"
+       log_test $? 0 "IPv4 default route with IPv6 gateway"
+
        #
        # MPLS as an example of LWT encap
        #
@@ -857,6 +910,173 @@ ipv4_fcnal_runtime()
        log_test $? 0 "IPv4 route with MPLS encap, v6 gw - check"
 }
 
+sysctl_nexthop_compat_mode_check()
+{
+       local sysctlname="net.ipv4.nexthop_compat_mode"
+       local lprefix=$1
+
+       IPE="ip netns exec me"
+
+       $IPE sysctl -q $sysctlname 2>&1 >/dev/null
+       if [ $? -ne 0 ]; then
+               echo "SKIP: kernel lacks nexthop compat mode sysctl control"
+               return $ksft_skip
+       fi
+
+       out=$($IPE sysctl $sysctlname 2>/dev/null)
+       log_test $? 0 "$lprefix default nexthop compat mode check"
+       check_output "${out}" "$sysctlname = 1"
+}
+
+sysctl_nexthop_compat_mode_set()
+{
+       local sysctlname="net.ipv4.nexthop_compat_mode"
+       local mode=$1
+       local lprefix=$2
+
+       IPE="ip netns exec me"
+
+       out=$($IPE sysctl -w $sysctlname=$mode)
+       log_test $? 0 "$lprefix set compat mode - $mode"
+       check_output "${out}" "net.ipv4.nexthop_compat_mode = $mode"
+}
+
+ipv6_compat_mode()
+{
+       local rc
+
+       echo
+       echo "IPv6 nexthop api compat mode test"
+       echo "--------------------------------"
+
+       sysctl_nexthop_compat_mode_check "IPv6"
+       if [ $? -eq $ksft_skip ]; then
+               return $ksft_skip
+       fi
+
+       run_cmd "$IP nexthop add id 62 via 2001:db8:91::2 dev veth1"
+       run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
+       run_cmd "$IP nexthop add id 122 group 62/63"
+       ipmout=$(start_ip_monitor route)
+
+       run_cmd "$IP -6 ro add 2001:db8:101::1/128 nhid 122"
+       # route add notification should contain expanded nexthops
+       stop_ip_monitor $ipmout 3
+       log_test $? 0 "IPv6 compat mode on - route add notification"
+
+       # route dump should contain expanded nexthops
+       check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 122 metric 1024 pref medium nexthop via 2001:db8:91::2 dev veth1 weight 1 nexthop via 2001:db8:91::3 dev veth1 weight 1"
+       log_test $? 0 "IPv6 compat mode on - route dump"
+
+       # change in nexthop group should generate route notification
+       run_cmd "$IP nexthop add id 64 via 2001:db8:91::4 dev veth1"
+       ipmout=$(start_ip_monitor route)
+       run_cmd "$IP nexthop replace id 122 group 62/64"
+       stop_ip_monitor $ipmout 3
+
+       log_test $? 0 "IPv6 compat mode on - nexthop change"
+
+       # set compat mode off
+       sysctl_nexthop_compat_mode_set 0 "IPv6"
+
+       run_cmd "$IP -6 ro del 2001:db8:101::1/128 nhid 122"
+
+       run_cmd "$IP nexthop add id 62 via 2001:db8:91::2 dev veth1"
+       run_cmd "$IP nexthop add id 63 via 2001:db8:91::3 dev veth1"
+       run_cmd "$IP nexthop add id 122 group 62/63"
+       ipmout=$(start_ip_monitor route)
+
+       run_cmd "$IP -6 ro add 2001:db8:101::1/128 nhid 122"
+       # route add notification should not contain expanded nexthops
+       stop_ip_monitor $ipmout 1
+       log_test $? 0 "IPv6 compat mode off - route add notification"
+
+       # route dump should not contain expanded nexthops
+       check_route6 "2001:db8:101::1" "2001:db8:101::1 nhid 122 metric 1024 pref medium"
+       log_test $? 0 "IPv6 compat mode off - route dump"
+
+       # change in nexthop group should not generate route notification
+       run_cmd "$IP nexthop add id 64 via 2001:db8:91::4 dev veth1"
+       ipmout=$(start_ip_monitor route)
+       run_cmd "$IP nexthop replace id 122 group 62/64"
+       stop_ip_monitor $ipmout 0
+       log_test $? 0 "IPv6 compat mode off - nexthop change"
+
+       # nexthop delete should not generate route notification
+       ipmout=$(start_ip_monitor route)
+       run_cmd "$IP nexthop del id 122"
+       stop_ip_monitor $ipmout 0
+       log_test $? 0 "IPv6 compat mode off - nexthop delete"
+
+       # set compat mode back on
+       sysctl_nexthop_compat_mode_set 1 "IPv6"
+}
+
+ipv4_compat_mode()
+{
+       local rc
+
+       echo
+       echo "IPv4 nexthop api compat mode"
+       echo "----------------------------"
+
+       sysctl_nexthop_compat_mode_check "IPv4"
+       if [ $? -eq $ksft_skip ]; then
+               return $ksft_skip
+       fi
+
+       run_cmd "$IP nexthop add id 21 via 172.16.1.2 dev veth1"
+       run_cmd "$IP nexthop add id 22 via 172.16.1.2 dev veth1"
+       run_cmd "$IP nexthop add id 122 group 21/22"
+       ipmout=$(start_ip_monitor route)
+
+       run_cmd "$IP ro add 172.16.101.1/32 nhid 122"
+       stop_ip_monitor $ipmout 3
+
+       # route add notification should contain expanded nexthops
+       log_test $? 0 "IPv4 compat mode on - route add notification"
+
+       # route dump should contain expanded nexthops
+       check_route "172.16.101.1" "172.16.101.1 nhid 122 nexthop via 172.16.1.2 dev veth1 weight 1 nexthop via 172.16.1.2 dev veth1 weight 1"
+       log_test $? 0 "IPv4 compat mode on - route dump"
+
+       # change in nexthop group should generate route notification
+       run_cmd "$IP nexthop add id 23 via 172.16.1.3 dev veth1"
+       ipmout=$(start_ip_monitor route)
+       run_cmd "$IP nexthop replace id 122 group 21/23"
+       stop_ip_monitor $ipmout 3
+       log_test $? 0 "IPv4 compat mode on - nexthop change"
+
+       sysctl_nexthop_compat_mode_set 0 "IPv4"
+
+       # cleanup
+       run_cmd "$IP ro del 172.16.101.1/32 nhid 122"
+
+       ipmout=$(start_ip_monitor route)
+       run_cmd "$IP ro add 172.16.101.1/32 nhid 122"
+       stop_ip_monitor $ipmout 1
+       # route add notification should not contain expanded nexthops
+       log_test $? 0 "IPv4 compat mode off - route add notification"
+
+       # route dump should not contain expanded nexthops
+       check_route "172.16.101.1" "172.16.101.1 nhid 122"
+       log_test $? 0 "IPv4 compat mode off - route dump"
+
+       # change in nexthop group should not generate route notification
+       ipmout=$(start_ip_monitor route)
+       run_cmd "$IP nexthop replace id 122 group 21/22"
+       stop_ip_monitor $ipmout 0
+       log_test $? 0 "IPv4 compat mode off - nexthop change"
+
+       # nexthop delete should not generate route notification
+       ipmout=$(start_ip_monitor route)
+       run_cmd "$IP nexthop del id 122"
+       stop_ip_monitor $ipmout 0
+       log_test $? 0 "IPv4 compat mode off - nexthop delete"
+
+       sysctl_nexthop_compat_mode_set 1 "IPv4"
+}
+
 basic()
 {
        echo
index b761670..84205c3 100755 (executable)
@@ -618,16 +618,22 @@ fib_nexthop_test()
 
 fib_suppress_test()
 {
+       echo
+       echo "FIB rule with suppress_prefixlength"
+       setup
+
        $IP link add dummy1 type dummy
        $IP link set dummy1 up
        $IP -6 route add default dev dummy1
        $IP -6 rule add table main suppress_prefixlength 0
-       ping -f -c 1000 -W 1 1234::1 || true
+       ping -f -c 1000 -W 1 1234::1 >/dev/null 2>&1
        $IP -6 rule del table main suppress_prefixlength 0
        $IP link del dummy1
 
        # If we got here without crashing, we're good.
-       return 0
+       log_test 0 0 "FIB rule suppress test"
+
+       cleanup
 }
 
 ################################################################################
index b500818..1181d64 100755 (executable)
 
 ALL_TESTS="
        ping_ipv4
+       ping_ipv6
        test_ip_dsfield
        test_ip_dscp
        test_ip_ecn
        test_ip_dscp_ecn
+       test_ip6_dsfield
+       test_ip6_dscp
+       test_ip6_ecn
 "
 
 NUM_NETIFS=4
@@ -107,6 +111,11 @@ ping_ipv4()
        ping_test $h1 192.0.2.2
 }
 
+ping_ipv6()
+{
+       ping6_test $h1 2001:db8:1::2
+}
+
 do_test_pedit_dsfield_common()
 {
        local pedit_locus=$1; shift
@@ -228,6 +237,63 @@ test_ip_dscp_ecn()
        do_test_ip_dscp_ecn "dev $swp2 egress"
 }
 
+do_test_ip6_dsfield()
+{
+       local locus=$1; shift
+       local dsfield
+
+       for dsfield in 0 1 2 3 128 252 253 254 255; do
+               do_test_pedit_dsfield "$locus"                          \
+                                 "ip6 traffic_class set $dsfield"      \
+                                 ipv6 "ip_tos $dsfield"                \
+                                 "-6 -A 2001:db8:1::1 -B 2001:db8:1::2"
+       done
+}
+
+test_ip6_dsfield()
+{
+       do_test_ip6_dsfield "dev $swp1 ingress"
+       do_test_ip6_dsfield "dev $swp2 egress"
+}
+
+do_test_ip6_dscp()
+{
+       local locus=$1; shift
+       local dscp
+
+       for dscp in 0 1 2 3 32 61 62 63; do
+               do_test_pedit_dsfield "$locus"                                 \
+                           "ip6 traffic_class set $((dscp << 2)) retain 0xfc" \
+                           ipv6 "ip_tos $(((dscp << 2) | 1))"                 \
+                           "-6 -A 2001:db8:1::1 -B 2001:db8:1::2"
+       done
+}
+
+test_ip6_dscp()
+{
+       do_test_ip6_dscp "dev $swp1 ingress"
+       do_test_ip6_dscp "dev $swp2 egress"
+}
+
+do_test_ip6_ecn()
+{
+       local locus=$1; shift
+       local ecn
+
+       for ecn in 0 1 2 3; do
+               do_test_pedit_dsfield "$locus"                          \
+                               "ip6 traffic_class set $ecn retain 0x3" \
+                               ipv6 "ip_tos $((124 | $ecn))"           \
+                               "-6 -A 2001:db8:1::1 -B 2001:db8:1::2"
+       done
+}
+
+test_ip6_ecn()
+{
+       do_test_ip6_ecn "dev $swp1 ingress"
+       do_test_ip6_ecn "dev $swp2 egress"
+}
+
 trap cleanup EXIT
 
 setup_prepare
index 813d02d..d9eca22 100755 (executable)
@@ -2,7 +2,8 @@
 # SPDX-License-Identifier: GPL-2.0
 
 ALL_TESTS="gact_drop_and_ok_test mirred_egress_redirect_test \
-       mirred_egress_mirror_test gact_trap_test"
+       mirred_egress_mirror_test matchall_mirred_egress_mirror_test \
+       gact_trap_test"
 NUM_NETIFS=4
 source tc_common.sh
 source lib.sh
@@ -50,6 +51,9 @@ switch_destroy()
 mirred_egress_test()
 {
        local action=$1
+       local protocol=$2
+       local classifier=$3
+       local classifier_args=$4
 
        RET=0
 
@@ -62,9 +66,9 @@ mirred_egress_test()
        tc_check_packets "dev $h2 ingress" 101 1
        check_fail $? "Matched without redirect rule inserted"
 
-       tc filter add dev $swp1 ingress protocol ip pref 1 handle 101 flower \
-               $tcflags dst_ip 192.0.2.2 action mirred egress $action \
-               dev $swp2
+       tc filter add dev $swp1 ingress protocol $protocol pref 1 handle 101 \
+               $classifier $tcflags $classifier_args \
+               action mirred egress $action dev $swp2
 
        $MZ $h1 -c 1 -p 64 -a $h1mac -b $h2mac -A 192.0.2.1 -B 192.0.2.2 \
                -t ip -q
@@ -72,10 +76,11 @@ mirred_egress_test()
        tc_check_packets "dev $h2 ingress" 101 1
        check_err $? "Did not match incoming $action packet"
 
-       tc filter del dev $swp1 ingress protocol ip pref 1 handle 101 flower
+       tc filter del dev $swp1 ingress protocol $protocol pref 1 handle 101 \
+               $classifier
        tc filter del dev $h2 ingress protocol ip pref 1 handle 101 flower
 
-       log_test "mirred egress $action ($tcflags)"
+       log_test "mirred egress $classifier $action ($tcflags)"
 }
 
 gact_drop_and_ok_test()
@@ -187,12 +192,17 @@ cleanup()
 
 mirred_egress_redirect_test()
 {
-       mirred_egress_test "redirect"
+       mirred_egress_test "redirect" "ip" "flower" "dst_ip 192.0.2.2"
 }
 
 mirred_egress_mirror_test()
 {
-       mirred_egress_test "mirror"
+       mirred_egress_test "mirror" "ip" "flower" "dst_ip 192.0.2.2"
+}
+
+matchall_mirred_egress_mirror_test()
+{
+       mirred_egress_test "mirror" "all" "matchall" ""
 }
 
 trap cleanup EXIT
index 71a62e7..77c09cd 100755 (executable)
 #      Same as pmtu_ipv4_vxlan4, but using a generic UDP IPv4/IPv6
 #      encapsulation (GUE) over IPv4/IPv6, instead of VXLAN
 #
+# - pmtu_ipv{4,6}_ipv{4,6}_exception
+#      Same as pmtu_ipv4_vxlan4, but using a IPv4/IPv6 tunnel over IPv4/IPv6,
+#      instead of VXLAN
+#
 # - pmtu_vti4_exception
 #      Set up vti tunnel on top of veth, with xfrm states and policies, in two
 #      namespaces with matching endpoints. Check that route exception is not
@@ -151,6 +155,10 @@ tests="
        pmtu_ipv6_gue4_exception        IPv6 over gue4: PMTU exceptions         1
        pmtu_ipv4_gue6_exception        IPv4 over gue6: PMTU exceptions         1
        pmtu_ipv6_gue6_exception        IPv6 over gue6: PMTU exceptions         1
+       pmtu_ipv4_ipv4_exception        IPv4 over IPv4: PMTU exceptions         1
+       pmtu_ipv6_ipv4_exception        IPv6 over IPv4: PMTU exceptions         1
+       pmtu_ipv4_ipv6_exception        IPv4 over IPv6: PMTU exceptions         1
+       pmtu_ipv6_ipv6_exception        IPv6 over IPv6: PMTU exceptions         1
        pmtu_vti6_exception             vti6: PMTU exceptions                   0
        pmtu_vti4_exception             vti4: PMTU exceptions                   0
        pmtu_vti4_default_mtu           vti4: default MTU assignment            0
@@ -363,6 +371,62 @@ setup_gue66() {
        setup_fou_or_gue 6 6 gue
 }
 
+setup_ipvX_over_ipvY() {
+       inner=${1}
+       outer=${2}
+
+       if [ "${outer}" -eq 4 ]; then
+               a_addr="${prefix4}.${a_r1}.1"
+               b_addr="${prefix4}.${b_r1}.1"
+               if [ "${inner}" -eq 4 ]; then
+                       type="ipip"
+                       mode="ipip"
+               else
+                       type="sit"
+                       mode="ip6ip"
+               fi
+       else
+               a_addr="${prefix6}:${a_r1}::1"
+               b_addr="${prefix6}:${b_r1}::1"
+               type="ip6tnl"
+               if [ "${inner}" -eq 4 ]; then
+                       mode="ipip6"
+               else
+                       mode="ip6ip6"
+               fi
+       fi
+
+       run_cmd ${ns_a} ip link add ip_a type ${type} local ${a_addr} remote ${b_addr} mode ${mode} || return 2
+       run_cmd ${ns_b} ip link add ip_b type ${type} local ${b_addr} remote ${a_addr} mode ${mode}
+
+       run_cmd ${ns_a} ip link set ip_a up
+       run_cmd ${ns_b} ip link set ip_b up
+
+       if [ "${inner}" = "4" ]; then
+               run_cmd ${ns_a} ip addr add ${tunnel4_a_addr}/${tunnel4_mask} dev ip_a
+               run_cmd ${ns_b} ip addr add ${tunnel4_b_addr}/${tunnel4_mask} dev ip_b
+       else
+               run_cmd ${ns_a} ip addr add ${tunnel6_a_addr}/${tunnel6_mask} dev ip_a
+               run_cmd ${ns_b} ip addr add ${tunnel6_b_addr}/${tunnel6_mask} dev ip_b
+       fi
+}
+
+setup_ip4ip4() {
+       setup_ipvX_over_ipvY 4 4
+}
+
+setup_ip6ip4() {
+       setup_ipvX_over_ipvY 6 4
+}
+
+setup_ip4ip6() {
+       setup_ipvX_over_ipvY 4 6
+}
+
+setup_ip6ip6() {
+       setup_ipvX_over_ipvY 6 6
+}
+
 setup_namespaces() {
        for n in ${NS_A} ${NS_B} ${NS_R1} ${NS_R2}; do
                ip netns add ${n} || return 1
@@ -908,6 +972,64 @@ test_pmtu_ipv6_gue6_exception() {
        test_pmtu_ipvX_over_fouY_or_gueY 6 6 gue
 }
 
+test_pmtu_ipvX_over_ipvY_exception() {
+       inner=${1}
+       outer=${2}
+       ll_mtu=4000
+
+       setup namespaces routing ip${inner}ip${outer} || return 2
+
+       trace "${ns_a}" ip_a         "${ns_b}"  ip_b  \
+             "${ns_a}" veth_A-R1    "${ns_r1}" veth_R1-A \
+             "${ns_b}" veth_B-R1    "${ns_r1}" veth_R1-B
+
+       if [ ${inner} -eq 4 ]; then
+               ping=ping
+               dst=${tunnel4_b_addr}
+       else
+               ping=${ping6}
+               dst=${tunnel6_b_addr}
+       fi
+
+       if [ ${outer} -eq 4 ]; then
+               #                      IPv4 header
+               exp_mtu=$((${ll_mtu} - 20))
+       else
+               #                      IPv6 header   Option 4
+               exp_mtu=$((${ll_mtu} - 40          - 8))
+       fi
+
+       # Create route exception by exceeding link layer MTU
+       mtu "${ns_a}"  veth_A-R1 $((${ll_mtu} + 1000))
+       mtu "${ns_r1}" veth_R1-A $((${ll_mtu} + 1000))
+       mtu "${ns_b}"  veth_B-R1 ${ll_mtu}
+       mtu "${ns_r1}" veth_R1-B ${ll_mtu}
+
+       mtu "${ns_a}" ip_a $((${ll_mtu} + 1000)) || return
+       mtu "${ns_b}" ip_b $((${ll_mtu} + 1000)) || return
+       run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -w 1 -s $((${ll_mtu} + 500)) ${dst}
+
+       # Check that exception was created
+       pmtu="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst})"
+       check_pmtu_value ${exp_mtu} "${pmtu}" "exceeding link layer MTU on ip${inner}ip${outer} interface"
+}
+
+test_pmtu_ipv4_ipv4_exception() {
+       test_pmtu_ipvX_over_ipvY_exception 4 4
+}
+
+test_pmtu_ipv6_ipv4_exception() {
+       test_pmtu_ipvX_over_ipvY_exception 6 4
+}
+
+test_pmtu_ipv4_ipv6_exception() {
+       test_pmtu_ipvX_over_ipvY_exception 4 6
+}
+
+test_pmtu_ipv6_ipv6_exception() {
+       test_pmtu_ipvX_over_ipvY_exception 6 6
+}
+
 test_pmtu_vti4_exception() {
        setup namespaces veth vti4 xfrm4 || return 2
        trace "${ns_a}" veth_a    "${ns_b}" veth_b \
index 35505b3..4555f88 100644 (file)
@@ -165,9 +165,10 @@ void *child_thread(void *arg)
                        socklen_t zc_len = sizeof(zc);
                        int res;
 
+                       memset(&zc, 0, sizeof(zc));
                        zc.address = (__u64)((unsigned long)addr);
                        zc.length = chunk_size;
-                       zc.recv_skip_hint = 0;
+
                        res = getsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE,
                                         &zc, &zc_len);
                        if (res == -1)
@@ -281,12 +282,14 @@ static void setup_sockaddr(int domain, const char *str_addr,
 static void do_accept(int fdlisten)
 {
        pthread_attr_t attr;
+       int rcvlowat;
 
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
+       rcvlowat = chunk_size;
        if (setsockopt(fdlisten, SOL_SOCKET, SO_RCVLOWAT,
-                      &chunk_size, sizeof(chunk_size)) == -1) {
+                      &rcvlowat, sizeof(rcvlowat)) == -1) {
                perror("setsockopt SO_RCVLOWAT");
        }
 
index 0ea44d9..c5282e6 100644 (file)
@@ -101,6 +101,21 @@ FIXTURE(tls)
        bool notls;
 };
 
+FIXTURE_VARIANT(tls)
+{
+       unsigned int tls_version;
+};
+
+FIXTURE_VARIANT_ADD(tls, 12)
+{
+       .tls_version = TLS_1_2_VERSION,
+};
+
+FIXTURE_VARIANT_ADD(tls, 13)
+{
+       .tls_version = TLS_1_3_VERSION,
+};
+
 FIXTURE_SETUP(tls)
 {
        struct tls12_crypto_info_aes_gcm_128 tls12;
@@ -112,7 +127,7 @@ FIXTURE_SETUP(tls)
        len = sizeof(addr);
 
        memset(&tls12, 0, sizeof(tls12));
-       tls12.info.version = TLS_1_3_VERSION;
+       tls12.info.version = variant->tls_version;
        tls12.info.cipher_type = TLS_CIPHER_AES_GCM_128;
 
        addr.sin_family = AF_INET;
@@ -733,7 +748,7 @@ TEST_F(tls, bidir)
                struct tls12_crypto_info_aes_gcm_128 tls12;
 
                memset(&tls12, 0, sizeof(tls12));
-               tls12.info.version = TLS_1_3_VERSION;
+               tls12.info.version = variant->tls_version;
                tls12.info.cipher_type = TLS_CIPHER_AES_GCM_128;
 
                ret = setsockopt(self->fd, SOL_TLS, TLS_RX, &tls12,
@@ -1258,78 +1273,4 @@ TEST(keysizes) {
        close(cfd);
 }
 
-TEST(tls12) {
-       int fd, cfd;
-       bool notls;
-
-       struct tls12_crypto_info_aes_gcm_128 tls12;
-       struct sockaddr_in addr;
-       socklen_t len;
-       int sfd, ret;
-
-       notls = false;
-       len = sizeof(addr);
-
-       memset(&tls12, 0, sizeof(tls12));
-       tls12.info.version = TLS_1_2_VERSION;
-       tls12.info.cipher_type = TLS_CIPHER_AES_GCM_128;
-
-       addr.sin_family = AF_INET;
-       addr.sin_addr.s_addr = htonl(INADDR_ANY);
-       addr.sin_port = 0;
-
-       fd = socket(AF_INET, SOCK_STREAM, 0);
-       sfd = socket(AF_INET, SOCK_STREAM, 0);
-
-       ret = bind(sfd, &addr, sizeof(addr));
-       ASSERT_EQ(ret, 0);
-       ret = listen(sfd, 10);
-       ASSERT_EQ(ret, 0);
-
-       ret = getsockname(sfd, &addr, &len);
-       ASSERT_EQ(ret, 0);
-
-       ret = connect(fd, &addr, sizeof(addr));
-       ASSERT_EQ(ret, 0);
-
-       ret = setsockopt(fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls"));
-       if (ret != 0) {
-               notls = true;
-               printf("Failure setting TCP_ULP, testing without tls\n");
-       }
-
-       if (!notls) {
-               ret = setsockopt(fd, SOL_TLS, TLS_TX, &tls12,
-                                sizeof(tls12));
-               ASSERT_EQ(ret, 0);
-       }
-
-       cfd = accept(sfd, &addr, &len);
-       ASSERT_GE(cfd, 0);
-
-       if (!notls) {
-               ret = setsockopt(cfd, IPPROTO_TCP, TCP_ULP, "tls",
-                                sizeof("tls"));
-               ASSERT_EQ(ret, 0);
-
-               ret = setsockopt(cfd, SOL_TLS, TLS_RX, &tls12,
-                                sizeof(tls12));
-               ASSERT_EQ(ret, 0);
-       }
-
-       close(sfd);
-
-       char const *test_str = "test_read";
-       int send_len = 10;
-       char buf[10];
-
-       send_len = strlen(test_str) + 1;
-       EXPECT_EQ(send(fd, test_str, send_len, 0), send_len);
-       EXPECT_NE(recv(cfd, buf, send_len, 0), -1);
-       EXPECT_EQ(memcmp(buf, test_str, send_len), 0);
-
-       close(fd);
-       close(cfd);
-}
-
 TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/net/vrf-xfrm-tests.sh b/tools/testing/selftests/net/vrf-xfrm-tests.sh
new file mode 100755 (executable)
index 0000000..184da81
--- /dev/null
@@ -0,0 +1,436 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Various combinations of VRF with xfrms and qdisc.
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+PAUSE_ON_FAIL=no
+VERBOSE=0
+ret=0
+
+HOST1_4=192.168.1.1
+HOST2_4=192.168.1.2
+HOST1_6=2001:db8:1::1
+HOST2_6=2001:db8:1::2
+
+XFRM1_4=10.0.1.1
+XFRM2_4=10.0.1.2
+XFRM1_6=fc00:1000::1
+XFRM2_6=fc00:1000::2
+IF_ID=123
+
+VRF=red
+TABLE=300
+
+AUTH_1=0xd94fcfea65fddf21dc6e0d24a0253508
+AUTH_2=0xdc6e0d24a0253508d94fcfea65fddf21
+ENC_1=0xfc46c20f8048be9725930ff3fb07ac2a91f0347dffeacf62
+ENC_2=0x3fb07ac2a91f0347dffeacf62fc46c20f8048be9725930ff
+SPI_1=0x02122b77
+SPI_2=0x2b770212
+
+which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
+
+################################################################################
+#
+log_test()
+{
+       local rc=$1
+       local expected=$2
+       local msg="$3"
+
+       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
+                       echo "hit enter to continue, 'q' to quit"
+                       read a
+                       [ "$a" = "q" ] && exit 1
+               fi
+       fi
+}
+
+run_cmd_host1()
+{
+       local cmd="$*"
+       local out
+       local rc
+
+       if [ "$VERBOSE" = "1" ]; then
+               printf "    COMMAND: $cmd\n"
+       fi
+
+       out=$(eval ip netns exec host1 $cmd 2>&1)
+       rc=$?
+       if [ "$VERBOSE" = "1" ]; then
+               if [ -n "$out" ]; then
+                       echo
+                       echo "    $out"
+               fi
+               echo
+       fi
+
+       return $rc
+}
+
+################################################################################
+# create namespaces for hosts and sws
+
+create_vrf()
+{
+       local ns=$1
+       local vrf=$2
+       local table=$3
+
+       if [ -n "${ns}" ]; then
+               ns="-netns ${ns}"
+       fi
+
+       ip ${ns} link add ${vrf} type vrf table ${table}
+       ip ${ns} link set ${vrf} up
+       ip ${ns} route add vrf ${vrf} unreachable default metric 8192
+       ip ${ns} -6 route add vrf ${vrf} unreachable default metric 8192
+
+       ip ${ns} addr add 127.0.0.1/8 dev ${vrf}
+       ip ${ns} -6 addr add ::1 dev ${vrf} nodad
+
+       ip ${ns} ru del pref 0
+       ip ${ns} ru add pref 32765 from all lookup local
+       ip ${ns} -6 ru del pref 0
+       ip ${ns} -6 ru add pref 32765 from all lookup local
+}
+
+create_ns()
+{
+       local ns=$1
+       local addr=$2
+       local addr6=$3
+
+       [ -z "${addr}" ] && addr="-"
+       [ -z "${addr6}" ] && addr6="-"
+
+       ip netns add ${ns}
+
+       ip -netns ${ns} link set lo up
+       if [ "${addr}" != "-" ]; then
+               ip -netns ${ns} addr add dev lo ${addr}
+       fi
+       if [ "${addr6}" != "-" ]; then
+               ip -netns ${ns} -6 addr add dev lo ${addr6}
+       fi
+
+       ip -netns ${ns} ro add unreachable default metric 8192
+       ip -netns ${ns} -6 ro add unreachable default metric 8192
+
+       ip netns exec ${ns} sysctl -qw net.ipv4.ip_forward=1
+       ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1
+       ip netns exec ${ns} sysctl -qw net.ipv6.conf.all.forwarding=1
+       ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.forwarding=1
+       ip netns exec ${ns} sysctl -qw net.ipv6.conf.default.accept_dad=0
+}
+
+# create veth pair to connect namespaces and apply addresses.
+connect_ns()
+{
+       local ns1=$1
+       local ns1_dev=$2
+       local ns1_addr=$3
+       local ns1_addr6=$4
+       local ns2=$5
+       local ns2_dev=$6
+       local ns2_addr=$7
+       local ns2_addr6=$8
+       local ns1arg
+       local ns2arg
+
+       if [ -n "${ns1}" ]; then
+               ns1arg="-netns ${ns1}"
+       fi
+       if [ -n "${ns2}" ]; then
+               ns2arg="-netns ${ns2}"
+       fi
+
+       ip ${ns1arg} li add ${ns1_dev} type veth peer name tmp
+       ip ${ns1arg} li set ${ns1_dev} up
+       ip ${ns1arg} li set tmp netns ${ns2} name ${ns2_dev}
+       ip ${ns2arg} li set ${ns2_dev} up
+
+       if [ "${ns1_addr}" != "-" ]; then
+               ip ${ns1arg} addr add dev ${ns1_dev} ${ns1_addr}
+               ip ${ns2arg} addr add dev ${ns2_dev} ${ns2_addr}
+       fi
+
+       if [ "${ns1_addr6}" != "-" ]; then
+               ip ${ns1arg} addr add dev ${ns1_dev} ${ns1_addr6} nodad
+               ip ${ns2arg} addr add dev ${ns2_dev} ${ns2_addr6} nodad
+       fi
+}
+
+################################################################################
+
+cleanup()
+{
+       ip netns del host1
+       ip netns del host2
+}
+
+setup()
+{
+       create_ns "host1"
+       create_ns "host2"
+
+       connect_ns "host1" eth0 ${HOST1_4}/24 ${HOST1_6}/64 \
+                  "host2" eth0 ${HOST2_4}/24 ${HOST2_6}/64
+
+       create_vrf "host1" ${VRF} ${TABLE}
+       ip -netns host1 link set dev eth0 master ${VRF}
+}
+
+cleanup_xfrm()
+{
+       for ns in host1 host2
+       do
+               for x in state policy
+               do
+                       ip -netns ${ns} xfrm ${x} flush
+                       ip -6 -netns ${ns} xfrm ${x} flush
+               done
+       done
+}
+
+setup_xfrm()
+{
+       local h1_4=$1
+       local h2_4=$2
+       local h1_6=$3
+       local h2_6=$4
+       local devarg="$5"
+
+       #
+       # policy
+       #
+
+       # host1 - IPv4 out
+       ip -netns host1 xfrm policy add \
+         src ${h1_4} dst ${h2_4} ${devarg} dir out \
+         tmpl src ${HOST1_4} dst ${HOST2_4} proto esp mode tunnel
+
+       # host2 - IPv4 in
+       ip -netns host2 xfrm policy add \
+         src ${h1_4} dst ${h2_4} dir in \
+         tmpl src ${HOST1_4} dst ${HOST2_4} proto esp mode tunnel
+
+       # host1 - IPv4 in
+       ip -netns host1 xfrm policy add \
+         src ${h2_4} dst ${h1_4} ${devarg} dir in \
+         tmpl src ${HOST2_4} dst ${HOST1_4} proto esp mode tunnel
+
+       # host2 - IPv4 out
+       ip -netns host2 xfrm policy add \
+         src ${h2_4} dst ${h1_4} dir out \
+         tmpl src ${HOST2_4} dst ${HOST1_4} proto esp mode tunnel
+
+
+       # host1 - IPv6 out
+       ip -6 -netns host1 xfrm policy add \
+         src ${h1_6} dst ${h2_6} ${devarg} dir out \
+         tmpl src ${HOST1_6} dst ${HOST2_6} proto esp mode tunnel
+
+       # host2 - IPv6 in
+       ip -6 -netns host2 xfrm policy add \
+         src ${h1_6} dst ${h2_6} dir in \
+         tmpl src ${HOST1_6} dst ${HOST2_6} proto esp mode tunnel
+
+       # host1 - IPv6 in
+       ip -6 -netns host1 xfrm policy add \
+         src ${h2_6} dst ${h1_6} ${devarg} dir in \
+         tmpl src ${HOST2_6} dst ${HOST1_6} proto esp mode tunnel
+
+       # host2 - IPv6 out
+       ip -6 -netns host2 xfrm policy add \
+         src ${h2_6} dst ${h1_6} dir out \
+         tmpl src ${HOST2_6} dst ${HOST1_6} proto esp mode tunnel
+
+       #
+       # state
+       #
+       ip -netns host1 xfrm state add src ${HOST1_4} dst ${HOST2_4} \
+           proto esp spi ${SPI_1} reqid 0 mode tunnel \
+           replay-window 4 replay-oseq 0x4 \
+           auth-trunc 'hmac(md5)' ${AUTH_1} 96 \
+           enc 'cbc(des3_ede)' ${ENC_1} \
+           sel src ${h1_4} dst ${h2_4} ${devarg}
+
+       ip -netns host2 xfrm state add src ${HOST1_4} dst ${HOST2_4} \
+           proto esp spi ${SPI_1} reqid 0 mode tunnel \
+           replay-window 4 replay-oseq 0x4 \
+           auth-trunc 'hmac(md5)' ${AUTH_1} 96 \
+           enc 'cbc(des3_ede)' ${ENC_1} \
+           sel src ${h1_4} dst ${h2_4}
+
+
+       ip -netns host1 xfrm state add src ${HOST2_4} dst ${HOST1_4} \
+           proto esp spi ${SPI_2} reqid 0 mode tunnel \
+           replay-window 4 replay-oseq 0x4 \
+           auth-trunc 'hmac(md5)' ${AUTH_2} 96 \
+           enc 'cbc(des3_ede)' ${ENC_2} \
+           sel src ${h2_4} dst ${h1_4} ${devarg}
+
+       ip -netns host2 xfrm state add src ${HOST2_4} dst ${HOST1_4} \
+           proto esp spi ${SPI_2} reqid 0 mode tunnel \
+           replay-window 4 replay-oseq 0x4 \
+           auth-trunc 'hmac(md5)' ${AUTH_2} 96 \
+           enc 'cbc(des3_ede)' ${ENC_2} \
+           sel src ${h2_4} dst ${h1_4}
+
+
+       ip -6 -netns host1 xfrm state add src ${HOST1_6} dst ${HOST2_6} \
+           proto esp spi ${SPI_1} reqid 0 mode tunnel \
+           replay-window 4 replay-oseq 0x4 \
+           auth-trunc 'hmac(md5)' ${AUTH_1} 96 \
+           enc 'cbc(des3_ede)' ${ENC_1} \
+           sel src ${h1_6} dst ${h2_6} ${devarg}
+
+       ip -6 -netns host2 xfrm state add src ${HOST1_6} dst ${HOST2_6} \
+           proto esp spi ${SPI_1} reqid 0 mode tunnel \
+           replay-window 4 replay-oseq 0x4 \
+           auth-trunc 'hmac(md5)' ${AUTH_1} 96 \
+           enc 'cbc(des3_ede)' ${ENC_1} \
+           sel src ${h1_6} dst ${h2_6}
+
+
+       ip -6 -netns host1 xfrm state add src ${HOST2_6} dst ${HOST1_6} \
+           proto esp spi ${SPI_2} reqid 0 mode tunnel \
+           replay-window 4 replay-oseq 0x4 \
+           auth-trunc 'hmac(md5)' ${AUTH_2} 96 \
+           enc 'cbc(des3_ede)' ${ENC_2} \
+           sel src ${h2_6} dst ${h1_6} ${devarg}
+
+       ip -6 -netns host2 xfrm state add src ${HOST2_6} dst ${HOST1_6} \
+           proto esp spi ${SPI_2} reqid 0 mode tunnel \
+           replay-window 4 replay-oseq 0x4 \
+           auth-trunc 'hmac(md5)' ${AUTH_2} 96 \
+           enc 'cbc(des3_ede)' ${ENC_2} \
+           sel src ${h2_6} dst ${h1_6}
+}
+
+cleanup_xfrm_dev()
+{
+       ip -netns host1 li del xfrm0
+       ip -netns host2 addr del ${XFRM2_4}/24 dev eth0
+       ip -netns host2 addr del ${XFRM2_6}/64 dev eth0
+}
+
+setup_xfrm_dev()
+{
+       local vrfarg="vrf ${VRF}"
+
+       ip -netns host1 li add type xfrm dev eth0 if_id ${IF_ID}
+       ip -netns host1 li set xfrm0 ${vrfarg} up
+       ip -netns host1 addr add ${XFRM1_4}/24 dev xfrm0
+       ip -netns host1 addr add ${XFRM1_6}/64 dev xfrm0
+
+       ip -netns host2 addr add ${XFRM2_4}/24 dev eth0
+       ip -netns host2 addr add ${XFRM2_6}/64 dev eth0
+
+       setup_xfrm ${XFRM1_4} ${XFRM2_4} ${XFRM1_6} ${XFRM2_6} "if_id ${IF_ID}"
+}
+
+run_tests()
+{
+       cleanup_xfrm
+
+       # no IPsec
+       run_cmd_host1 ip vrf exec ${VRF} ping -c1 -w1 ${HOST2_4}
+       log_test $? 0 "IPv4 no xfrm policy"
+       run_cmd_host1 ip vrf exec ${VRF} ${ping6} -c1 -w1 ${HOST2_6}
+       log_test $? 0 "IPv6 no xfrm policy"
+
+       # xfrm without VRF in sel
+       setup_xfrm ${HOST1_4} ${HOST2_4} ${HOST1_6} ${HOST2_6}
+       run_cmd_host1 ip vrf exec ${VRF} ping -c1 -w1 ${HOST2_4}
+       log_test $? 0 "IPv4 xfrm policy based on address"
+       run_cmd_host1 ip vrf exec ${VRF} ${ping6} -c1 -w1 ${HOST2_6}
+       log_test $? 0 "IPv6 xfrm policy based on address"
+       cleanup_xfrm
+
+       # xfrm with VRF in sel
+       # Known failure: ipv4 resets the flow oif after the lookup. Fix is
+       # not straightforward.
+       # setup_xfrm ${HOST1_4} ${HOST2_4} ${HOST1_6} ${HOST2_6} "dev ${VRF}"
+       # run_cmd_host1 ip vrf exec ${VRF} ping -c1 -w1 ${HOST2_4}
+       # log_test $? 0 "IPv4 xfrm policy with VRF in selector"
+       run_cmd_host1 ip vrf exec ${VRF} ${ping6} -c1 -w1 ${HOST2_6}
+       log_test $? 0 "IPv6 xfrm policy with VRF in selector"
+       cleanup_xfrm
+
+       # xfrm with enslaved device in sel
+       # Known failures: combined with the above, __xfrm{4,6}_selector_match
+       # needs to consider both l3mdev and enslaved device index.
+       # setup_xfrm ${HOST1_4} ${HOST2_4} ${HOST1_6} ${HOST2_6} "dev eth0"
+       # run_cmd_host1 ip vrf exec ${VRF} ping -c1 -w1 ${HOST2_4}
+       # log_test $? 0 "IPv4 xfrm policy with enslaved device in selector"
+       # run_cmd_host1 ip vrf exec ${VRF} ${ping6} -c1 -w1 ${HOST2_6}
+       # log_test $? 0 "IPv6 xfrm policy with enslaved device in selector"
+       # cleanup_xfrm
+
+       # xfrm device
+       setup_xfrm_dev
+       run_cmd_host1 ip vrf exec ${VRF} ping -c1 -w1 ${XFRM2_4}
+       log_test $? 0 "IPv4 xfrm policy with xfrm device"
+       run_cmd_host1 ip vrf exec ${VRF} ${ping6} -c1 -w1 ${XFRM2_6}
+       log_test $? 0 "IPv6 xfrm policy with xfrm device"
+       cleanup_xfrm_dev
+}
+
+################################################################################
+# usage
+
+usage()
+{
+        cat <<EOF
+usage: ${0##*/} OPTS
+
+        -p          Pause on fail
+        -v          verbose mode (show commands and output)
+
+done
+EOF
+}
+
+################################################################################
+# main
+
+while getopts :pv o
+do
+       case $o in
+               p) PAUSE_ON_FAIL=yes;;
+               v) VERBOSE=$(($VERBOSE + 1));;
+               h) usage; exit 0;;
+               *) usage; exit 1;;
+       esac
+done
+
+cleanup 2>/dev/null
+setup
+
+echo
+echo "No qdisc on VRF device"
+run_tests
+
+run_cmd_host1 tc qdisc add dev ${VRF} root netem delay 100ms
+echo
+echo "netem qdisc on VRF device"
+run_tests
+
+printf "\nTests passed: %3d\n" ${nsuccess}
+printf "Tests failed: %3d\n"   ${nfail}
+
+exit $ret
index c0dd102..da7a9dd 100644 (file)
@@ -269,14 +269,16 @@ int main(int argc, char *argv[])
                               "  %d programmable periodic signals\n"
                               "  %d pulse per second\n"
                               "  %d programmable pins\n"
-                              "  %d cross timestamping\n",
+                              "  %d cross timestamping\n"
+                              "  %d adjust_phase\n",
                               caps.max_adj,
                               caps.n_alarm,
                               caps.n_ext_ts,
                               caps.n_per_out,
                               caps.pps,
                               caps.n_pins,
-                              caps.cross_timestamping);
+                              caps.cross_timestamping,
+                              caps.adjust_phase);
                }
        }
 
index 89fb3e0..c0aa46c 100644 (file)
@@ -2803,12 +2803,13 @@ TEST(syscall_restart)
                         offsetof(struct seccomp_data, nr)),
 
 #ifdef __NR_sigreturn
-               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_sigreturn, 6, 0),
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_sigreturn, 7, 0),
 #endif
-               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 5, 0),
-               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_exit, 4, 0),
-               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_rt_sigreturn, 3, 0),
-               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_nanosleep, 4, 0),
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 6, 0),
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_exit, 5, 0),
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_rt_sigreturn, 4, 0),
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_nanosleep, 5, 0),
+               BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_clock_nanosleep, 4, 0),
                BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_restart_syscall, 4, 0),
 
                /* Allow __NR_write for easy logging. */
@@ -2895,7 +2896,8 @@ TEST(syscall_restart)
        ASSERT_EQ(PTRACE_EVENT_SECCOMP, (status >> 16));
        ASSERT_EQ(0, ptrace(PTRACE_GETEVENTMSG, child_pid, NULL, &msg));
        ASSERT_EQ(0x100, msg);
-       EXPECT_EQ(__NR_nanosleep, get_syscall(_metadata, child_pid));
+       ret = get_syscall(_metadata, child_pid);
+       EXPECT_TRUE(ret == __NR_nanosleep || ret == __NR_clock_nanosleep);
 
        /* Might as well check siginfo for sanity while we're here. */
        ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &info));
index f8ea6f5..72cdc3c 100644 (file)
             "$TC actions flush action pedit"
         ]
     },
+    {
+        "id": "94bb",
+        "name": "Add pedit action with LAYERED_OP ip6 traffic_class",
+        "category": [
+            "actions",
+            "pedit",
+            "layered_op"
+        ],
+        "setup": [
+            [
+                "$TC actions flush action pedit",
+                0,
+                1,
+                255
+            ]
+        ],
+        "cmdUnderTest": "$TC actions add action pedit ex munge ip6 traffic_class set 0x40 continue",
+        "expExitCode": "0",
+        "verifyCmd": "$TC actions list action pedit",
+        "matchPattern": "ipv6\\+0: val 04000000 mask f00fffff",
+        "matchCount": "1",
+        "teardown": [
+            "$TC actions flush action pedit"
+        ]
+    },
     {
         "id": "6f5e",
         "name": "Add pedit action with LAYERED_OP ip6 flow_lbl",
index e566c70..a3e4318 100755 (executable)
@@ -713,9 +713,8 @@ def set_operation_mode(pm, parser, args, remaining):
         exit(0)
 
     if args.list:
-        if args.list:
-            list_test_cases(alltests)
-            exit(0)
+        list_test_cases(alltests)
+        exit(0)
 
     if len(alltests):
         req_plugins = pm.get_required_plugins(alltests)
index b630c7b..8155c2e 100755 (executable)
@@ -1,17 +1,8 @@
 #!/bin/bash
 # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
-self.flags = flags
 
-# Kselftest framework requirement - SKIP code is 4.
-ksft_skip=4
-
-
-if [ -f /dev/tpm0 ] ; then
-       python -m unittest -v tpm2_tests.SmokeTest
-       python -m unittest -v tpm2_tests.AsyncTest
-else
-       exit $ksft_skip
-fi
+python -m unittest -v tpm2_tests.SmokeTest
+python -m unittest -v tpm2_tests.AsyncTest
 
 CLEAR_CMD=$(which tpm2_clear)
 if [ -n $CLEAR_CMD ]; then
index 180b469..a6f5e34 100755 (executable)
@@ -1,11 +1,4 @@
 #!/bin/bash
 # SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
 
-# Kselftest framework requirement - SKIP code is 4.
-ksft_skip=4
-
-if [ -f /dev/tpmrm0 ] ; then
-       python -m unittest -v tpm2_tests.SpaceTest
-else
-       exit $ksft_skip
-fi
+python -m unittest -v tpm2_tests.SpaceTest
index d31db05..6998877 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 # Makefile for vm selftests
 uname_M := $(shell uname -m 2>/dev/null || echo not)
-ARCH ?= $(shell echo $(uname_M) | sed -e 's/aarch64.*/arm64/')
+MACHINE ?= $(shell echo $(uname_M) | sed -e 's/aarch64.*/arm64/')
 
 CFLAGS = -Wall -I ../../../../usr/include $(EXTRA_CFLAGS)
 LDLIBS = -lrt
@@ -20,7 +20,7 @@ TEST_GEN_FILES += thuge-gen
 TEST_GEN_FILES += transhuge-stress
 TEST_GEN_FILES += userfaultfd
 
-ifneq (,$(filter $(ARCH),arm64 ia64 mips64 parisc64 ppc64 riscv64 s390x sh64 sparc64 x86_64))
+ifneq (,$(filter $(MACHINE),arm64 ia64 mips64 parisc64 ppc64 ppc64le riscv64 s390x sh64 sparc64 x86_64))
 TEST_GEN_FILES += va_128TBswitch
 TEST_GEN_FILES += virtual_address_range
 TEST_GEN_FILES += write_to_hugetlbfs
index 665009e..76ca5e7 100755 (executable)
@@ -59,7 +59,7 @@ else
 fi
 
 #filter 64bit architectures
-ARCH64STR="arm64 ia64 mips64 parisc64 ppc64 riscv64 s390x sh64 sparc64 x86_64"
+ARCH64STR="arm64 ia64 mips64 parisc64 ppc64 ppc64le riscv64 s390x sh64 sparc64 x86_64"
 if [ -z $ARCH ]; then
   ARCH=`uname -m 2>/dev/null | sed -e 's/aarch64.*/arm64/'`
 fi
index 936e1ca..17a1f53 100755 (executable)
@@ -48,8 +48,11 @@ cleanup() {
        exec 2>/dev/null
        printf "$orig_message_cost" > /proc/sys/net/core/message_cost
        ip0 link del dev wg0
+       ip0 link del dev wg1
        ip1 link del dev wg0
+       ip1 link del dev wg1
        ip2 link del dev wg0
+       ip2 link del dev wg1
        local to_kill="$(ip netns pids $netns0) $(ip netns pids $netns1) $(ip netns pids $netns2)"
        [[ -n $to_kill ]] && kill $to_kill
        pp ip netns del $netns1
@@ -77,18 +80,20 @@ ip0 link set wg0 netns $netns2
 key1="$(pp wg genkey)"
 key2="$(pp wg genkey)"
 key3="$(pp wg genkey)"
+key4="$(pp wg genkey)"
 pub1="$(pp wg pubkey <<<"$key1")"
 pub2="$(pp wg pubkey <<<"$key2")"
 pub3="$(pp wg pubkey <<<"$key3")"
+pub4="$(pp wg pubkey <<<"$key4")"
 psk="$(pp wg genpsk)"
 [[ -n $key1 && -n $key2 && -n $psk ]]
 
 configure_peers() {
        ip1 addr add 192.168.241.1/24 dev wg0
-       ip1 addr add fd00::1/24 dev wg0
+       ip1 addr add fd00::1/112 dev wg0
 
        ip2 addr add 192.168.241.2/24 dev wg0
-       ip2 addr add fd00::2/24 dev wg0
+       ip2 addr add fd00::2/112 dev wg0
 
        n1 wg set wg0 \
                private-key <(echo "$key1") \
@@ -230,9 +235,38 @@ n1 ping -W 1 -c 1 192.168.241.2
 n1 wg set wg0 private-key <(echo "$key3")
 n2 wg set wg0 peer "$pub3" preshared-key <(echo "$psk") allowed-ips 192.168.241.1/32 peer "$pub1" remove
 n1 ping -W 1 -c 1 192.168.241.2
+n2 wg set wg0 peer "$pub3" remove
+
+# Test that we can route wg through wg
+ip1 addr flush dev wg0
+ip2 addr flush dev wg0
+ip1 addr add fd00::5:1/112 dev wg0
+ip2 addr add fd00::5:2/112 dev wg0
+n1 wg set wg0 private-key <(echo "$key1") peer "$pub2" preshared-key <(echo "$psk") allowed-ips fd00::5:2/128 endpoint 127.0.0.1:2
+n2 wg set wg0 private-key <(echo "$key2") listen-port 2 peer "$pub1" preshared-key <(echo "$psk") allowed-ips fd00::5:1/128 endpoint 127.212.121.99:9998
+ip1 link add wg1 type wireguard
+ip2 link add wg1 type wireguard
+ip1 addr add 192.168.241.1/24 dev wg1
+ip1 addr add fd00::1/112 dev wg1
+ip2 addr add 192.168.241.2/24 dev wg1
+ip2 addr add fd00::2/112 dev wg1
+ip1 link set mtu 1340 up dev wg1
+ip2 link set mtu 1340 up dev wg1
+n1 wg set wg1 listen-port 5 private-key <(echo "$key3") peer "$pub4" allowed-ips 192.168.241.2/32,fd00::2/128 endpoint [fd00::5:2]:5
+n2 wg set wg1 listen-port 5 private-key <(echo "$key4") peer "$pub3" allowed-ips 192.168.241.1/32,fd00::1/128 endpoint [fd00::5:1]:5
+tests
+# Try to set up a routing loop between the two namespaces
+ip1 link set netns $netns0 dev wg1
+ip0 addr add 192.168.241.1/24 dev wg1
+ip0 link set up dev wg1
+n0 ping -W 1 -c 1 192.168.241.2
+n1 wg set wg0 peer "$pub2" endpoint 192.168.241.2:7
+ip2 link del wg0
+ip2 link del wg1
+! n0 ping -W 1 -c 10 -f 192.168.241.2 || false # Should not crash kernel
 
+ip0 link del wg1
 ip1 link del wg0
-ip2 link del wg0
 
 # Test using NAT. We now change the topology to this:
 # ┌────────────────────────────────────────┐    ┌────────────────────────────────────────────────┐     ┌────────────────────────────────────────┐
@@ -282,6 +316,20 @@ pp sleep 3
 n2 ping -W 1 -c 1 192.168.241.1
 n1 wg set wg0 peer "$pub2" persistent-keepalive 0
 
+# Test that onion routing works, even when it loops
+n1 wg set wg0 peer "$pub3" allowed-ips 192.168.242.2/32 endpoint 192.168.241.2:5
+ip1 addr add 192.168.242.1/24 dev wg0
+ip2 link add wg1 type wireguard
+ip2 addr add 192.168.242.2/24 dev wg1
+n2 wg set wg1 private-key <(echo "$key3") listen-port 5 peer "$pub1" allowed-ips 192.168.242.1/32
+ip2 link set wg1 up
+n1 ping -W 1 -c 1 192.168.242.2
+ip2 link del wg1
+n1 wg set wg0 peer "$pub3" endpoint 192.168.242.2:5
+! n1 ping -W 1 -c 1 192.168.242.2 || false # Should not crash kernel
+n1 wg set wg0 peer "$pub3" remove
+ip1 addr del 192.168.242.1/24 dev wg0
+
 # Do a wg-quick(8)-style policy routing for the default route, making sure vethc has a v6 address to tease out bugs.
 ip1 -6 addr add fc00::9/96 dev vethc
 ip1 -6 route add default via fc00::1
index 990c510..f52f1e2 100644 (file)
@@ -10,3 +10,4 @@ CONFIG_CMDLINE_BOOL=y
 CONFIG_CMDLINE="console=hvc0 wg.success=hvc1"
 CONFIG_SECTION_MISMATCH_WARN_ONLY=y
 CONFIG_FRAME_WARN=1280
+CONFIG_THREAD_SHIFT=14
index f33f32f..b587b9a 100644 (file)
@@ -4,7 +4,7 @@ test: virtio_test vringh_test
 virtio_test: virtio_ring.o virtio_test.o
 vringh_test: vringh_test.o vringh.o virtio_ring.o
 
-CFLAGS += -g -O2 -Werror -Wall -I. -I../include/ -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE
+CFLAGS += -g -O2 -Werror -Wall -I. -I../include/ -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE -include ../../include/linux/kconfig.h
 vpath %.c ../../drivers/virtio ../../drivers/vhost
 mod:
        ${MAKE} -C `pwd`/../.. M=`pwd`/vhost_test V=${V}
@@ -22,7 +22,8 @@ OOT_CONFIGS=\
        CONFIG_VHOST=m \
        CONFIG_VHOST_NET=n \
        CONFIG_VHOST_SCSI=n \
-       CONFIG_VHOST_VSOCK=n
+       CONFIG_VHOST_VSOCK=n \
+       CONFIG_VHOST_RING=n
 OOT_BUILD=KCFLAGS="-I "${OOT_VHOST} ${MAKE} -C ${OOT_KSRC} V=${V}
 oot-build:
        echo "UNSUPPORTED! Don't use the resulting modules in production!"
index d0351f8..04d563f 100644 (file)
@@ -1,4 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
+#include <stdlib.h>
 #if defined(__i386__) || defined(__x86_64__)
 #define barrier() asm volatile("" ::: "memory")
 #define virt_mb() __sync_synchronize()
diff --git a/tools/virtio/generated/autoconf.h b/tools/virtio/generated/autoconf.h
new file mode 100644 (file)
index 0000000..e69de29
index 903dc9c..2c51bcc 100644 (file)
@@ -7,4 +7,5 @@
 
 #define READ_ONCE(var) (*((volatile typeof(var) *)(&(var))))
 
+#define __aligned(x) __attribute((__aligned__(x)))
 #endif
index 20f6cf0..9860622 100644 (file)
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 # Makefile for vm tools
 #
+include ../scripts/Makefile.include
+
 TARGETS=page-types slabinfo page_owner_sort
 
 LIB_DIR = ../lib/api