Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
authorDavid S. Miller <davem@davemloft.net>
Thu, 7 May 2020 05:10:13 +0000 (22:10 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 7 May 2020 05:10:13 +0000 (22:10 -0700)
Conflicts were all overlapping changes.

Signed-off-by: David S. Miller <davem@davemloft.net>
950 files changed:
Documentation/ABI/testing/sysfs-class-net
Documentation/admin-guide/kernel-parameters.txt
Documentation/admin-guide/serial-console.rst
Documentation/admin-guide/sysctl/net.rst
Documentation/bpf/index.rst
Documentation/dev-tools/kselftest.rst
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/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/filesystems/afs.rst
Documentation/hwmon/bcm54140.rst [new file with mode: 0644]
Documentation/hwmon/index.rst
Documentation/networking/6pack.rst [moved from Documentation/networking/6pack.txt with 90% similarity]
Documentation/networking/altera_tse.rst [moved from Documentation/networking/altera_tse.txt with 85% similarity]
Documentation/networking/arcnet-hardware.rst [moved from Documentation/networking/arcnet-hardware.txt with 66% similarity]
Documentation/networking/arcnet.rst [moved from Documentation/networking/arcnet.txt with 76% similarity]
Documentation/networking/atm.rst [moved from Documentation/networking/atm.txt with 89% similarity]
Documentation/networking/ax25.rst [moved from Documentation/networking/ax25.txt with 91% similarity]
Documentation/networking/baycom.rst [moved from Documentation/networking/baycom.txt with 58% similarity]
Documentation/networking/bonding.rst [moved from Documentation/networking/bonding.txt with 75% similarity]
Documentation/networking/caif/caif.rst
Documentation/networking/caif/index.rst [new file with mode: 0644]
Documentation/networking/caif/linux_caif.rst [moved from Documentation/networking/caif/Linux-CAIF.txt with 90% similarity]
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 [moved from Documentation/networking/cdc_mbim.txt with 88% similarity]
Documentation/networking/checksum-offloads.rst
Documentation/networking/cops.rst [new file with mode: 0644]
Documentation/networking/cops.txt [deleted file]
Documentation/networking/cxacru.rst [moved from Documentation/networking/cxacru.txt with 66% similarity]
Documentation/networking/dccp.rst [moved from Documentation/networking/dccp.txt with 94% similarity]
Documentation/networking/dctcp.rst [moved from Documentation/networking/dctcp.txt with 89% similarity]
Documentation/networking/decnet.rst [moved from Documentation/networking/decnet.txt with 87% similarity]
Documentation/networking/defza.rst [moved from Documentation/networking/defza.txt with 91% similarity]
Documentation/networking/device_drivers/3com/3c509.rst [moved from Documentation/networking/device_drivers/3com/3c509.txt with 68% similarity]
Documentation/networking/device_drivers/3com/vortex.rst [moved from Documentation/networking/device_drivers/3com/vortex.txt with 72% similarity]
Documentation/networking/device_drivers/amazon/ena.rst [moved from Documentation/networking/device_drivers/amazon/ena.txt with 86% similarity]
Documentation/networking/device_drivers/aquantia/atlantic.rst [moved from Documentation/networking/device_drivers/aquantia/atlantic.txt with 63% similarity]
Documentation/networking/device_drivers/chelsio/cxgb.rst [moved from Documentation/networking/device_drivers/chelsio/cxgb.txt with 81% similarity]
Documentation/networking/device_drivers/cirrus/cs89x0.rst [moved from Documentation/networking/device_drivers/cirrus/cs89x0.txt with 61% similarity]
Documentation/networking/device_drivers/davicom/dm9000.rst [moved from Documentation/networking/device_drivers/davicom/dm9000.txt with 92% similarity]
Documentation/networking/device_drivers/dec/de4x5.rst [moved from Documentation/networking/device_drivers/dec/de4x5.txt with 78% similarity]
Documentation/networking/device_drivers/dec/dmfe.rst [moved from Documentation/networking/device_drivers/dec/dmfe.txt with 68% similarity]
Documentation/networking/device_drivers/dlink/dl2k.rst [moved from Documentation/networking/device_drivers/dlink/dl2k.txt with 59% similarity]
Documentation/networking/device_drivers/freescale/dpaa.rst [moved from Documentation/networking/device_drivers/freescale/dpaa.txt with 79% similarity]
Documentation/networking/device_drivers/freescale/gianfar.rst [moved from Documentation/networking/device_drivers/freescale/gianfar.txt with 82% similarity]
Documentation/networking/device_drivers/index.rst
Documentation/networking/device_drivers/intel/e100.rst
Documentation/networking/device_drivers/intel/ipw2100.rst [moved from Documentation/networking/device_drivers/intel/ipw2100.txt with 70% similarity]
Documentation/networking/device_drivers/intel/ipw2200.rst [moved from Documentation/networking/device_drivers/intel/ipw2200.txt with 64% similarity]
Documentation/networking/device_drivers/intel/ixgb.rst
Documentation/networking/device_drivers/microsoft/netvsc.rst [moved from Documentation/networking/device_drivers/microsoft/netvsc.txt with 83% similarity]
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 [moved from Documentation/networking/device_drivers/neterion/vxge.txt with 80% similarity]
Documentation/networking/device_drivers/qualcomm/rmnet.rst [moved from Documentation/networking/device_drivers/qualcomm/rmnet.txt with 73% similarity]
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 [moved from Documentation/networking/device_drivers/ti/cpsw_switchdev.txt with 51% similarity]
Documentation/networking/device_drivers/ti/tlan.rst [moved from Documentation/networking/device_drivers/ti/tlan.txt with 73% similarity]
Documentation/networking/device_drivers/toshiba/spider_net.rst [moved from Documentation/networking/device_drivers/toshiba/spider_net.txt with 88% similarity]
Documentation/networking/devlink/devlink-region.rst
Documentation/networking/dns_resolver.rst [moved from Documentation/networking/dns_resolver.txt with 89% similarity]
Documentation/networking/driver.rst [moved from Documentation/networking/driver.txt with 85% similarity]
Documentation/networking/eql.rst [moved from Documentation/networking/eql.txt with 62% similarity]
Documentation/networking/ethtool-netlink.rst
Documentation/networking/fib_trie.rst [moved from Documentation/networking/fib_trie.txt with 96% similarity]
Documentation/networking/filter.rst [moved from Documentation/networking/filter.txt with 77% similarity]
Documentation/networking/fore200e.rst [moved from Documentation/networking/fore200e.txt with 94% similarity]
Documentation/networking/framerelay.rst [moved from Documentation/networking/framerelay.txt with 93% similarity]
Documentation/networking/gen_stats.rst [moved from Documentation/networking/gen_stats.txt with 60% similarity]
Documentation/networking/generic-hdlc.rst [moved from Documentation/networking/generic-hdlc.txt with 75% similarity]
Documentation/networking/generic_netlink.rst [moved from Documentation/networking/generic_netlink.txt with 64% similarity]
Documentation/networking/gtp.rst [moved from Documentation/networking/gtp.txt with 79% similarity]
Documentation/networking/hinic.rst [moved from Documentation/networking/hinic.txt with 97% similarity]
Documentation/networking/ila.rst [moved from Documentation/networking/ila.txt with 82% similarity]
Documentation/networking/index.rst
Documentation/networking/ip-sysctl.rst [moved from Documentation/networking/ip-sysctl.txt with 83% similarity]
Documentation/networking/ip_dynaddr.rst [moved from Documentation/networking/ip_dynaddr.txt with 65% similarity]
Documentation/networking/ipddp.rst [moved from Documentation/networking/ipddp.txt with 89% similarity]
Documentation/networking/iphase.rst [moved from Documentation/networking/iphase.txt with 50% similarity]
Documentation/networking/ipsec.rst [moved from Documentation/networking/ipsec.txt with 90% similarity]
Documentation/networking/ipv6.rst [moved from Documentation/networking/ipv6.txt with 93% similarity]
Documentation/networking/ipvlan.rst [moved from Documentation/networking/ipvlan.txt with 54% similarity]
Documentation/networking/ipvs-sysctl.rst [moved from Documentation/networking/ipvs-sysctl.txt with 62% similarity]
Documentation/networking/kcm.rst [moved from Documentation/networking/kcm.txt with 84% similarity]
Documentation/networking/l2tp.rst [moved from Documentation/networking/l2tp.txt with 79% similarity]
Documentation/networking/lapb-module.rst [moved from Documentation/networking/lapb-module.txt with 74% similarity]
Documentation/networking/ltpc.rst [moved from Documentation/networking/ltpc.txt with 85% similarity]
Documentation/networking/mac80211-injection.rst [moved from Documentation/networking/mac80211-injection.txt with 67% similarity]
Documentation/networking/mpls-sysctl.rst [moved from Documentation/networking/mpls-sysctl.txt with 82% similarity]
Documentation/networking/multiqueue.rst [moved from Documentation/networking/multiqueue.txt with 76% similarity]
Documentation/networking/netconsole.rst [moved from Documentation/networking/netconsole.txt with 66% similarity]
Documentation/networking/netdev-features.rst [moved from Documentation/networking/netdev-features.txt with 95% similarity]
Documentation/networking/netdevices.rst [moved from Documentation/networking/netdevices.txt with 89% similarity]
Documentation/networking/netfilter-sysctl.rst [moved from Documentation/networking/netfilter-sysctl.txt with 62% similarity]
Documentation/networking/netif-msg.rst [new file with mode: 0644]
Documentation/networking/netif-msg.txt [deleted file]
Documentation/networking/nf_conntrack-sysctl.rst [moved from Documentation/networking/nf_conntrack-sysctl.txt with 85% similarity]
Documentation/networking/nf_flowtable.rst [moved from Documentation/networking/nf_flowtable.txt with 76% similarity]
Documentation/networking/openvswitch.rst [moved from Documentation/networking/openvswitch.txt with 95% similarity]
Documentation/networking/operstates.rst [moved from Documentation/networking/operstates.txt with 87% similarity]
Documentation/networking/packet_mmap.rst [new file with mode: 0644]
Documentation/networking/packet_mmap.txt [deleted file]
Documentation/networking/phonet.rst [moved from Documentation/networking/phonet.txt with 82% similarity]
Documentation/networking/pktgen.rst [moved from Documentation/networking/pktgen.txt with 62% similarity]
Documentation/networking/plip.rst [moved from Documentation/networking/PLIP.txt with 92% similarity]
Documentation/networking/ppp_generic.rst [moved from Documentation/networking/ppp_generic.txt with 91% similarity]
Documentation/networking/proc_net_tcp.rst [moved from Documentation/networking/proc_net_tcp.txt with 83% similarity]
Documentation/networking/radiotap-headers.rst [moved from Documentation/networking/radiotap-headers.txt with 70% similarity]
Documentation/networking/ray_cs.rst [moved from Documentation/networking/ray_cs.txt with 65% similarity]
Documentation/networking/rds.rst [moved from Documentation/networking/rds.txt with 59% similarity]
Documentation/networking/regulatory.rst [moved from Documentation/networking/regulatory.txt with 94% similarity]
Documentation/networking/rxrpc.rst [moved from Documentation/networking/rxrpc.txt with 85% similarity]
Documentation/networking/sctp.rst [moved from Documentation/networking/sctp.txt with 64% similarity]
Documentation/networking/secid.rst [moved from Documentation/networking/secid.txt with 87% similarity]
Documentation/networking/seg6-sysctl.rst [new file with mode: 0644]
Documentation/networking/seg6-sysctl.txt [deleted file]
Documentation/networking/skfp.rst [moved from Documentation/networking/skfp.txt with 68% similarity]
Documentation/networking/snmp_counter.rst
Documentation/networking/strparser.rst [moved from Documentation/networking/strparser.txt with 80% similarity]
Documentation/networking/switchdev.rst [moved from Documentation/networking/switchdev.txt with 84% similarity]
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 [moved from Documentation/networking/tcp-thin.txt with 97% similarity]
Documentation/networking/team.rst [moved from Documentation/networking/team.txt with 67% similarity]
Documentation/networking/timestamping.rst [moved from Documentation/networking/timestamping.txt with 89% similarity]
Documentation/networking/tproxy.rst [moved from Documentation/networking/tproxy.txt with 70% similarity]
Documentation/networking/tuntap.rst [moved from Documentation/networking/tuntap.txt with 58% similarity]
Documentation/networking/udplite.rst [moved from Documentation/networking/udplite.txt with 65% similarity]
Documentation/networking/vrf.rst [new file with mode: 0644]
Documentation/networking/vrf.txt [deleted file]
Documentation/networking/vxlan.rst [moved from Documentation/networking/vxlan.txt with 73% similarity]
Documentation/networking/x25-iface.rst [moved from Documentation/networking/x25-iface.txt with 96% similarity]
Documentation/networking/x25.rst [moved from Documentation/networking/x25.txt with 96% similarity]
Documentation/networking/xfrm_device.rst [moved from Documentation/networking/xfrm_device.txt with 92% similarity]
Documentation/networking/xfrm_proc.rst [moved from Documentation/networking/xfrm_proc.txt with 95% similarity]
Documentation/networking/xfrm_sync.rst [moved from Documentation/networking/xfrm_sync.txt with 82% similarity]
Documentation/networking/xfrm_sysctl.rst [moved from Documentation/networking/xfrm_sysctl.txt with 52% similarity]
Documentation/networking/z8530drv.rst [moved from Documentation/networking/z8530drv.txt with 57% similarity]
Documentation/timers/timers-howto.rst
MAINTAINERS
arch/arm/boot/dts/qcom-ipq4019.dtsi
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/kernel/armv8_deprecated.c
arch/arm64/kernel/fpsimd.c
arch/mips/lasat/sysctl.c
arch/riscv/net/bpf_jit_comp32.c
arch/s390/appldata/appldata_base.c
arch/s390/kernel/debug.c
arch/s390/kernel/topology.c
arch/s390/mm/cmm.c
arch/x86/kernel/itmt.c
drivers/atm/Kconfig
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/random.c
drivers/crypto/chelsio/chcr_algo.c
drivers/crypto/chelsio/chcr_crypto.h
drivers/crypto/chelsio/chcr_ipsec.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 [moved from drivers/net/ethernet/mellanox/mlx5/core/qp.c with 55% similarity]
drivers/infiniband/hw/mlx5/srq_cmd.c
drivers/macintosh/mac_hid.c
drivers/media/rc/bpf-lirc.c
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_srab.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/sja1105.h
drivers/net/dsa/sja1105/sja1105_clocking.c
drivers/net/dsa/sja1105/sja1105_ethtool.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/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/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_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/cavium/liquidio/octeon_device.h
drivers/net/ethernet/chelsio/Kconfig
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/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_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/intel/e1000/e1000_main.c
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/e1000e/regs.h
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/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/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/mlx4_en.h
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/cq.c
drivers/net/ethernet/mellanox/mlx5/core/debugfs.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_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/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/reg.h
drivers/net/ethernet/mellanox/mlxsw/spectrum.c
drivers/net/ethernet/mellanox/mlxsw/spectrum.h
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_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_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/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_regs.c
drivers/net/ethernet/myricom/myri10ge/myri10ge.c
drivers/net/ethernet/neterion/Kconfig
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_main.c
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/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-stm32.c
drivers/net/ethernet/stmicro/stmmac/dwmac4.h
drivers/net/ethernet/stmicro/stmmac/dwmac4_core.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/xilinx/ll_temac_main.c
drivers/net/fddi/Kconfig
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/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/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/broadcom.c
drivers/net/phy/cortina.c
drivers/net/phy/dp83867.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/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/usb/ax88179_178a.c
drivers/net/usb/sierra_net.c
drivers/net/vrf.c
drivers/net/wan/Kconfig
drivers/net/wireless/Kconfig
drivers/net/wireless/intel/ipw2x00/Kconfig
drivers/net/wireless/intel/ipw2x00/ipw2100.c
drivers/net/wireless/intersil/hostap/hostap_hw.c
drivers/of/of_mdio.c
drivers/parport/procfs.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/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/staging/fsl-dpaa2/ethsw/README
fs/dcache.c
fs/drop_caches.c
fs/file_table.c
fs/fscache/main.c
fs/inode.c
fs/proc/proc_sysctl.c
fs/quota/dquot.c
fs/xfs/xfs_sysctl.c
include/linux/bpf-cgroup.h
include/linux/bpf.h
include/linux/bpf_types.h
include/linux/brcmphy.h
include/linux/compaction.h
include/linux/coredump.h
include/linux/delay.h
include/linux/etherdevice.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/hugetlb.h
include/linux/if_bridge.h
include/linux/inet_diag.h
include/linux/kprobes.h
include/linux/latencytop.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/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/printk.h
include/linux/ptp_clock_kernel.h
include/linux/sched/sysctl.h
include/linux/security.h
include/linux/sysctl.h
include/linux/tcp.h
include/linux/timer.h
include/linux/vermagic.h
include/linux/vmstat.h
include/linux/writeback.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/ip6_route.h
include/net/ipv6.h
include/net/ipv6_stubs.h
include/net/mptcp.h
include/net/netfilter/nf_tables.h
include/net/netlink.h
include/net/netns/ipv4.h
include/net/sch_generic.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 [moved from drivers/net/ethernet/mscc/ocelot_ptp.h with 52% similarity]
include/trace/events/qrtr.h [new file with mode: 0644]
include/uapi/linux/bpf.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/if.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/mrp_bridge.h [new file with mode: 0644]
include/uapi/linux/netfilter/nf_conntrack_common.h
include/uapi/linux/netfilter/nf_nat.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]
ipc/ipc_sysctl.c
ipc/mq_sysctl.c
kernel/bpf/btf.c
kernel/bpf/cgroup.c
kernel/bpf/core.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/kprobes.c
kernel/latencytop.c
kernel/module.c
kernel/pid_namespace.c
kernel/printk/printk.c
kernel/sched/core.c
kernel/sched/fair.c
kernel/sched/rt.c
kernel/sched/topology.c
kernel/seccomp.c
kernel/sysctl.c
kernel/time/timer.c
kernel/trace/bpf_trace.c
kernel/trace/trace.c
kernel/umh.c
kernel/utsname_sysctl.c
kernel/watchdog.c
lib/nlattr.c
mm/compaction.c
mm/hugetlb.c
mm/page-writeback.c
mm/page_alloc.c
mm/util.c
mm/vmstat.c
net/8021q/vlan_dev.c
net/Kconfig
net/atm/Kconfig
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/soft-interface.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/filter.c
net/core/gen_stats.c
net/core/link_watch.c
net/core/neighbour.c
net/core/net-sysfs.c
net/core/netpoll.c
net/core/pktgen.c
net/core/rtnetlink.c
net/core/skbuff.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/dsa_priv.h
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/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/ipv6/Kconfig
net/ipv6/addrconf.c
net/ipv6/addrconf_core.c
net/ipv6/anycast.c
net/ipv6/ila/ila.h
net/ipv6/ila/ila_xlat.c
net/ipv6/ndisc.c
net/ipv6/route.c
net/ipv6/sysctl_net_ipv6.c
net/l2tp/l2tp_eth.c
net/lapb/Kconfig
net/mac80211/tx.c
net/mpls/af_mpls.c
net/mptcp/subflow.c
net/netfilter/Kconfig
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/nf_conntrack_standalone.c
net/netfilter/nf_flow_table_offload.c
net/netfilter/nf_log.c
net/netfilter/nf_tables_api.c
net/netfilter/nft_nat.c
net/netlink/Makefile
net/netlink/genetlink.c
net/netlink/policy.c [new file with mode: 0644]
net/netrom/af_netrom.c
net/openvswitch/datapath.h
net/openvswitch/meter.c
net/openvswitch/meter.h
net/phonet/sysctl.c
net/qrtr/ns.c
net/rds/tcp.c
net/rose/af_rose.c
net/rxrpc/Kconfig
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_fq.c
net/sched/sch_generic.c
net/sched/sch_red.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/sysctl.c
net/sunrpc/xprtrdma/svc_rdma.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/pmsr.c
net/wireless/radiotap.c
net/x25/Kconfig
net/xdp/xsk.c
samples/pktgen/README.rst
scripts/mod/modpost.c
security/apparmor/lsm.c
security/min_addr.c
security/yama/yama_lsm.c
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/include/uapi/linux/bpf.h
tools/include/uapi/linux/if_link.h
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.map
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/hashmap.c [moved from tools/testing/selftests/bpf/test_hashmap.c with 53% similarity]
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/sk_assign.c
tools/testing/selftests/bpf/progs/connect4_prog.c
tools/testing/selftests/bpf/progs/test_btf_map_in_map.c [new file with mode: 0644]
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_progs.c
tools/testing/selftests/bpf/test_progs.h
tools/testing/selftests/bpf/test_verifier.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/drivers/net/netdevsim/devlink.sh
tools/testing/selftests/kselftest_harness.h
tools/testing/selftests/net/Makefile
tools/testing/selftests/net/fib_nexthops.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/tls.c
tools/testing/selftests/net/vrf-xfrm-tests.sh [new file with mode: 0755]
tools/testing/selftests/ptp/testptp.c
tools/testing/selftests/tc-testing/tc-tests/actions/pedit.json

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 7bc83f3..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]
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 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 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 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 5aa141c..9b1f114 100644 (file)
@@ -81,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 ff8b0f2..26c492a 100644 (file)
@@ -82,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 d220279..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:
 
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 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.
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
similarity index 90%
rename from Documentation/networking/6pack.txt
rename to Documentation/networking/6pack.rst
index 8f33942..bc5bf1f 100644 (file)
@@ -1,27 +1,36 @@
+.. 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
+
+: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 
+  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
@@ -36,6 +45,7 @@ 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
@@ -44,12 +54,14 @@ 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
@@ -75,12 +87,14 @@ 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.
@@ -94,27 +108,28 @@ 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:
+  kissattach.c file. At the top, insert the following lines::
+
+    #ifndef N_6PACK
+    #define N_6PACK (N_AX25+1)
+    #endif
 
-  #ifndef N_6PACK
-  #define N_6PACK (N_AX25+1)
-  #endif
+  Then find the line:
 
-  Then find the line
-   
-  int disc = N_AX25;
+    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 
+- 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'. 
+  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
@@ -138,6 +153,7 @@ 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,
similarity index 85%
rename from Documentation/networking/altera_tse.txt
rename to Documentation/networking/altera_tse.rst
index 50b8589..7a70400 100644 (file)
@@ -1,6 +1,12 @@
-       Altera Triple-Speed Ethernet MAC driver
+.. SPDX-License-Identifier: GPL-2.0
 
-Copyright (C) 2008-2014 Altera Corporation
+.. 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
@@ -46,23 +52,33 @@ 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
+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).
+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::
 
-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. Driver information and notes
+===============================
 
-4.1) Transmit process
+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
@@ -70,7 +86,8 @@ 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
+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
@@ -79,34 +96,39 @@ 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
+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
+--------------------------
+ - 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.
@@ -118,17 +140,18 @@ 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
+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
- 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:
 
similarity index 66%
rename from Documentation/networking/arcnet-hardware.txt
rename to Documentation/networking/arcnet-hardware.rst
index 731de41..ac249ac 100644 (file)
@@ -1,11 +1,15 @@
------------------------------------------------------------------------------
-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?
------------------------------------------------------------------------------
+.. 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,
@@ -14,8 +18,8 @@ e-mail apenwarr@worldvisions.ca with any settings for your particular card,
 or any other information you have!
 
 
-INTRODUCTION TO ARCNET
-----------------------
+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.
@@ -30,7 +34,7 @@ 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.  
+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
@@ -71,19 +75,24 @@ 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
------------------------
+Cabling ARCnet Networks
+=======================
+
+This section was rewritten by
+
+       Vojtech Pavlik     <vojtech@suse.cz>
 
-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>
+
+       - 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
@@ -103,13 +112,13 @@ 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:
+with four BNC connectors containing four 47 Ohm resistors::
 
-   |         | wires
-   R         + junction
--R-+-R-      R 47 Ohm resistors
-   R
-   |
+          |         | 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
@@ -127,14 +136,15 @@ And now to the cabling.  What you can connect together:
 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
+
+       (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.
@@ -142,22 +152,22 @@ And now to the cabling.  What you can connect together:
 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:
+An example of a typical ARCnet network::
 
-           R                     S - STAR type card              
+          R                     S - STAR type card
     S------H--------A-------S    R - Terminator
-           |        |            H - Hub                         
-           |        |            A - Active hub                  
-           |   S----H----S                                       
-           S        |                                            
-                    |                                            
-                    S                                            
-                                                                          
+          |        |            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:
+cable. A typical BUS ARCnet network looks like::
 
     RT----T------T------T------T------TR
      B    B      B      B      B      B
@@ -168,63 +178,63 @@ cable. A typical BUS ARCnet network looks like:
 
 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:
+hub::
 
-         A------T------T------TR
-         |      B      B      B
+        A------T------T------TR
+        |      B      B      B
      S---H---S
-         |
-         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:
+BUS network in place of a BUS card and a terminator::
 
      S------T------T------S
-            B      B
+           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  
-                                                               
+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:
+the empty connectors of cards on the ends of the chain.  An example::
 
-          ___________   ___________
-      _R_|_         _|_|_         _|_R_  
-     |     |       |     |       |     |      
-     |Card |       |Card |       |Card |     
-     |_____|       |_____|       |_____|          
+         ___________   ___________
+      _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. 
+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:
+well.  An example::
 
     RP-------P--------P--------H-----P------P-----PR
-                               |
+                              |
       RP-----H--------P--------H-----P------PR
-             |                 |
-             PR                PR
+            |                 |
+            PR                PR
 
     R - RJ Terminator
     P - TP Card
@@ -234,11 +244,13 @@ 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
@@ -248,8 +260,8 @@ 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
--------------------
+Setting the Jumpers
+===================
 
 All ARCnet cards should have a total of four or five different settings:
 
@@ -261,43 +273,51 @@ All ARCnet cards should have a total of four or five different settings:
     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.
-             
+            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
+
+       ("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.
-       
+
+       ======   =========================================================
+       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
@@ -308,14 +328,18 @@ All ARCnet cards should have a total of four or five different settings:
   - 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
+       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
@@ -326,6 +350,7 @@ All ARCnet cards should have a total of four or five different settings:
     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
@@ -336,28 +361,34 @@ All ARCnet cards should have a total of four or five different settings:
     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. 
+
+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)
+                                       terminated)
        OFF (short)     ON              Card init
        ON              ON              Normal state - everything OK, nothing
-                                         happens
+                                       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
@@ -366,7 +397,7 @@ 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. 
+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.
 
@@ -378,8 +409,9 @@ 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
@@ -404,17 +436,19 @@ Cards Listed in this file (in this order, mostly):
        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.
 
+* 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!):
+
+  - 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)
@@ -423,7 +457,7 @@ Unclassified Stuff
      [...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; 
+     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..
@@ -432,136 +466,130 @@ Unclassified Stuff
 
      --- CUT ---
 
+Standard Microsystems Corp (SMC)
+================================
+
+PC100, PC110, PC120, PC130 (8-bit cards) and PC500, PC600 (16-bit cards)
+------------------------------------------------------------------------
 
-** 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. 
+    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)!
 
+::
+
+            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
-===============================================================
+  - 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 
+using information from the following Original SMC Manual
 
-             "Configuration Guide for
-             ARCNET(R)-PC130E/PC270
-            Network Controller Boards
-                Pub. # 900.044A
-                   June, 1989"
+            "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  
+SMC is a registered trademark of the Standard Microsystems Corporation
 
-The PC130E is an enhanced version of the PC130 board, is equipped with a 
+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 
+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 
+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 
+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
+        8 7 6 5 4 3 2 1
     ________________________________________________________________
    |   |       S1        |                                          |
    |   |_________________|                                          |
@@ -587,27 +615,27 @@ It can be used in a star or a daisy-chained network.
        |                                             |
        |_____________________________________________|
 
-Legend:
+Legend::
 
-SMC 90C63      ARCNET Controller / Transceiver /Logic
-S1     1-3:    I/O Base Address Select
+  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)
+  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)
+  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
@@ -615,10 +643,10 @@ 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
+of eight possible I/O Base addresses using the following table::
 
 
    Switch | Hex I/O
@@ -635,14 +663,16 @@ of eight possible I/O Base addresses using the following table
 
 
 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 
+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 *)
    -----------|---------|-----------
@@ -650,115 +680,111 @@ positions, determined by the offset, switches 7 and 8 of group S1.
    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.
+
+  *) 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 
+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 
+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 
+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:
+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
+       | 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::
 
-Note: There is another Version of the PC500 called Short Version, which 
+      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.
-        
+       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. 
+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 
+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"
+        "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  
+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.
@@ -769,7 +795,9 @@ 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 
+::
+
+       1
        0 9 8 7 6 5 4 3 2 1     6 5 4 3 2 1
     ____________________________________________________________________
    < |         SW1         | |     SW2     |                            |
@@ -796,34 +824,34 @@ It can be used in a star or a daisy-chained (BUS) network.
    >    |  |                                             |
    <____|  |_____________________________________________|
 
-Legend:
+Legend::
 
-SW1    1-6:    I/O Base Address Select
+  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)
+  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)
+  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 
+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:
+The node ID is the sum of the values of all switches set to "1"
+These values are::
 
     Switch | Value
     -------|-------
@@ -836,30 +864,30 @@ These values are:
       7    |  64
       8    | 128
 
-Some Examples:
+Some Examples::
 
-    Switch         | Hex     | Decimal 
+    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 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 
+   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
+of 32 possible I/O Base addresses using the following table::
 
    Switch       | Hex I/O
    6 5  4 3 2 1 | Address
@@ -899,16 +927,18 @@ of 32 possible I/O Base addresses using the following table
 
 
 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 
+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 | 
-   ---------|-------- 
+   10 9 8 7 |
+   ---------|--------
     0 0 1 1 |  3
     0 1 0 0 |  4
     0 1 0 1 |  5
@@ -919,52 +949,50 @@ be supported: 3, 4, 5, 7, 9, 10, 11, 12.
     1 1 0 0 | 12
 
 
-Setting the Timeouts 
---------------------
+Setting the Timeouts
+^^^^^^^^^^^^^^^^^^^^
 
-The two jumpers JP2 (1-4) are used to determine the timeout parameters. 
+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 
+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 
+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:
+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
+       | 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:
+LEDs, RJ11 jacks, etc.) and 8 bit. Here's a little drawing::
 
-    _______________________________________   
+    _______________________________________
    | +---------+  +---------+              |____
    | |   S2    |  |   S1    |              |
    | +---------+  +---------+              |
@@ -976,12 +1004,12 @@ LEDs, RJ11 jacks, etc.) and 8 bit. Here's a little drawing:
    |  +===+                                |
    |                                       |
    |   .. JP1   +----------+               |
-   |   ..       | big chip |               |   
+   |   ..       | 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,
@@ -992,71 +1020,76 @@ 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 **
+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
+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")
+     ------------------------------------
+    |                                    |
+    |              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
@@ -1065,19 +1098,22 @@ Switches        Base
 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 
+01100          C:5000-C:57ff
 11100          C:5800-C:5fff
 00010          C:C000-C:C7ff   C:E000-C:ffff
 10010          C:C800-C:Cfff
@@ -1094,7 +1130,7 @@ Switches        RAM           ROM
 00101          D:8000-D:87ff   D:A000-D:bfff
 10101          D:8800-D:8fff
 01101          D:9000-D:97ff
-11101          D:9800-D:9fff 
+11101          D:9800-D:9fff
 00011          D:C000-D:c7ff   D:E000-D:ffff
 10011          D:C800-D:cfff
 01011          D:D000-D:d7ff
@@ -1103,34 +1139,37 @@ Switches        RAM           ROM
 10111          E:0800-E:0fff
 01111          E:1000-E:17ff
 11111          E:1800-E:1fff
+========        ============= ================
 
 
-*****************************************************************************
+PureData Corp
+=============
 
-** 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.
+       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.
+       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:
+       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 |
@@ -1140,28 +1179,28 @@ Jumpers:
 
 Carl de Billy <CARL@carainfo.com> explains J3 and J4:
 
-       J3 Diagram:
+   J3 Diagram::
 
-           .-------.
-         o | o   o |
-           :-------:    TWIST Technology
-         o | o   o |
-           `-------'
-           .-------.
-           | o   o | o
-           :-------:    COAX Technology
-           | o   o | o
-           `-------'
+          .-------.
+        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 
+  - 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 
+  - 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.
 
@@ -1169,40 +1208,43 @@ Carl de Billy <CARL@carainfo.com> explains J3 and J4:
 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.
+       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
-       -----------------------------------------
+       ==========      =========================
+       Switch No.      ARCnet address
+       12345678
+       ==========      =========================
        00000000        FF      (Don't use this!)
        00000001        FE
        00000010        FD
-       ....
-       11111101        2       
+       ...
+       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.
+       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.
+       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
-       -------------------------------
+       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
@@ -1211,29 +1253,31 @@ DIP Switches:
        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.
+       (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.
+       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.
+       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?)
+       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
@@ -1250,40 +1294,36 @@ DIP Switches:
        11011           0xC800 (guessed - crashes tested system)
        11101           0xC500 (guessed - crashes tested system)
        11111           0xC400 (guessed - crashes tested system)
-       
-       
-*****************************************************************************
+       =============   ============================================
+
+CNet Technology Inc. (8-bit cards)
+==================================
 
-** 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"
+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
+- 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
+
+::
 
     __________________________________________________________________
    |                                                                  |
@@ -1307,75 +1347,77 @@ P/N 120SBT ARCNET 8 bit XT/AT Star, Bus, Twisted Pair
    |  >  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)
+        |                                             |
+        |_____________________________________________|
+
+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"  
+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 
+   =======  ======  =====
+   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 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
+of eight possible I/O Base addresses using the following table::
 
 
    Switch      | Hex I/O
@@ -1392,13 +1434,15 @@ of eight possible I/O Base addresses using the following table
 
 
 Setting the Base Memory (RAM) buffer Address
---------------------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-The memory buffer (RAM) requires 2K. The base of this buffer can be 
+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 *)
    --------------------|---------|-----------
@@ -1410,22 +1454,24 @@ Switches 1-5 of switch block SW1 select the Memory Base address.
    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
+  *) 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.
+JP2, JP3, JP4, JP5, JP6. JP2 is the default::
 
-   Jumper | IRQ     
+   Jumper | IRQ
    -------|-----
      2    |  2
      3    |  3
@@ -1435,71 +1481,66 @@ JP2, JP3, JP4, JP5, JP6. JP2 is the default.
 
 
 Setting the Internal Terminator on CN120AB/TP/SBT
---------------------------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-The jumper JP12 is used to enable the internal terminator
+The jumper JP12 is used to enable the internal terminator::
 
-                         -----
-       0                |  0  |     
+                        -----
+       0                |  0  |
      -----   ON         |     |  ON
     |  0  |             |  0  |
     |     |  OFF         -----   OFF
     |  0  |                0
      -----
-   Terminator          Terminator 
+   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  |
     |     | |     |      -----   -----
-    |  0  | |  0  |        0       0 
+    |  0  | |  0  |        0       0
      -----   -----
-     Coaxial Cable       Twisted Pair Cable 
+     Coaxial Cable       Twisted Pair Cable
        (Default)
 
 
 Setting the Timeout Parameters
-------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-The jumpers labeled EXT1 and EXT2 are used to determine the timeout 
+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)
+===================================
 
-*****************************************************************************
-
-** 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 
+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 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
+- 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
+
+::
 
    ___________________________________________________________________
   <                             _________________________          ___|
@@ -1526,30 +1567,30 @@ P/N 160TP  ARCNET 16 bit XT/AT Twisted Pair
   >            |  |                                       |
   <____________|  |_______________________________________|
 
-Legend:
+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
+  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:
+The node ID is the sum of the values of all switches set to "1"
+These values are::
 
    Switch | Label | Value
    -------|-------|-------
@@ -1562,32 +1603,32 @@ These values are:
      7    | ID6   |  64
      8    | ID7   | 128
 
-Some Examples:
+Some Examples::
 
-    Switch         | Hex     | Decimal 
+    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 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:
+address using the following table::
 
-             Switch        | Hex I/O
+            Switch        | Hex I/O
     1   2   3   4   5   6  | Address
    ------------------------|--------
    OFF ON  ON  OFF OFF ON  |  260
@@ -1604,10 +1645,10 @@ Note: Other IO-Base addresses seem to be selectable, but only the above
 
 
 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.
+Base address of the RAM (2K) and the PROM::
 
    Switch          | Hex RAM | Hex ROM
     7   8   9  10  | Address | Address
@@ -1616,17 +1657,19 @@ Base address of the RAM (2K) and the PROM.
    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
+.. 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:
+JP3 through JP13 using the following table::
 
-   Jumper | IRQ     
+   Jumper | IRQ
    -------|-----------------
      3    |  14
      4    |  15
@@ -1640,10 +1683,12 @@ JP3 through JP13 using the following table:
     12    |   7
     13    |   2 (=9) Default!
 
-Note:  - Do not use JP11=IRQ6, it may conflict with your Floppy Disk
-         Controller
+.. 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
+        Hard Disk, it may conflict with their controllers
 
 
 Setting the Timeout Parameters
@@ -1653,14 +1698,16 @@ The jumpers labeled JP1 and JP2 are used to determine the timeout
 parameters. These two jumpers are normally left open.
 
 
-*****************************************************************************
+Lantech
+=======
 
-** 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                                                 |
    |   ___________                                               __|
@@ -1683,25 +1730,27 @@ parameters. These two jumpers are normally left open.
    |      |    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
+       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
@@ -1710,43 +1759,48 @@ 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)
+       678| I/O Address
+       ---|------------
+       000|    260
+       001|    failed probe
+       010|    2E0
+       011|    380
+       100|    290
+       101|    350
+       110|    failed probe
+       111|    3E0
 
-JP 4  : Boot PROM enable   CLOSE - enabled
-                           OPEN  - disabled
+  SW 2  : Node ID (binary coded)
 
-JP 6  : IRQ set (ONLY ONE jumper on 1-5 for IRQ 2-6)
+  JP 4  : Boot PROM enable   CLOSE - enabled
+                            OPEN  - disabled
 
+  JP 6  : IRQ set (ONLY ONE jumper on 1-5 for IRQ 2-6)
 
-*****************************************************************************
 
-** Acer **
+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   |
@@ -1762,51 +1816,51 @@ PC100, but has some additional jumpers I don't know the meaning of.
   |                    _____               |
   |                   |     |   _____      |
   |                   |     |  |     |  ___|
-  |                   |     |  |     | |    
-  |  _____            | 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
+  |                   |     |  |     | |
+  |  _____            | 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.
@@ -1815,7 +1869,7 @@ 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:
+These values are::
 
    Switch | Value
    -------|-------
@@ -1832,40 +1886,40 @@ 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
+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 
+     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. 
+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.
+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 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)
@@ -1873,10 +1927,10 @@ Jumpers 7-10 of switch block SW1 select the Memory Base address.
 
 
 Setting the Interrupt Line
---------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Jumpers 1-5 of the jumper block J1 control the IRQ level. ON means 
-shorted, OFF means open.
+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   |
@@ -1889,65 +1943,67 @@ shorted, OFF means open.
 
 
 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?
+==========
 
-*****************************************************************************
-
-** 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
+::
+
+         _______________________________________________________
+        |                         _________                     |
+        |                        |   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.
@@ -1955,8 +2011,8 @@ 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:
+The node ID is the sum of the values of all switches set to "1"
+These values are::
 
    Switch | Value
    -------|-------
@@ -1971,10 +2027,10 @@ These values are:
 
 
 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
+of eight possible I/O Base addresses using the following table::
 
 
    Switch      | Hex I/O
@@ -1991,13 +2047,16 @@ of eight possible I/O Base addresses using the following table
 
 
 Setting the Base Memory (RAM) buffer Address
---------------------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-The memory buffer (RAM) requires 2K. The base of this buffer can be 
+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 *)
    --------------------|---------|-----------
@@ -2009,16 +2068,16 @@ Jumpers 3-5 of switch block SW1 select the Memory Base address.
    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.
+
+  *) 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.
+Switches 1-5 of the switch block SW3 control the IRQ level::
 
     Jumper              |  IRQ
     1   2   3   4   5   |
@@ -2031,64 +2090,67 @@ Switches 1-5 of the switch block SW3 control the IRQ level.
 
 
 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
+=======
 
-** 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)
+::
+
+   _____________________________________________________________________
+  |  ___________   |                         |            ______        |
+  | |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)
@@ -2097,7 +2159,7 @@ 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:
+These values are::
 
    Switch | Label | Value
    -------|-------|-------
@@ -2111,10 +2173,10 @@ These values are:
      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:
+of eight possible I/O Base addresses using the following table::
 
 
    Switch      | Hex I/O
@@ -2122,7 +2184,7 @@ of eight possible I/O Base addresses using the following table:
    ------------|--------
    ON  ON  ON  |  260  (Manufacturer's default)
    OFF ON  ON  |  290
-   ON  OFF ON  |  2E0                         
+   ON  OFF ON  |  2E0
    OFF OFF ON  |  2F0
    ON  ON  OFF |  300
    OFF ON  OFF |  350
@@ -2131,35 +2193,38 @@ of eight possible I/O Base addresses using the following table:
 
 
 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  OFF ON  ON  |  C4000  |  C6000  (Manufacturer's default)
    ON  ON  ON  OFF ON  |  CC000  |  CE000
-   ON  ON  OFF OFF ON  |  D0000  |  D2000  
+   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.
+   *) 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.
+shorted, OFF means open::
 
     Jumper              |  IRQ
     1   2   3   4   5   |
@@ -2172,19 +2237,21 @@ shorted, OFF means open.
 
 
 Setting the Timeout Parameters
-------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-The jumpers J3 are used to set the timeout parameters. These two 
+The jumpers J3 are used to set the timeout parameters. These two
 jumpers are normally left open.
 
-  
-*****************************************************************************
+Thomas-Conrad
+=============
 
-** Thomas-Conrad **
 Model #500-6242-0097 REV A (8-bit card)
 ---------------------------------------
+
   - from Lars Karlsson <100617.3473@compuserve.com>
 
+::
+
      ________________________________________________________
    |          ________   ________                           |_____
    |         |........| |........|                            |
@@ -2194,11 +2261,11 @@ Model #500-6242-0097 REV A (8-bit card)
    |                                              address |   |
    |    ______                                    switch  |   |
    |   |      |                                           |   |
-   |   |      |                                           |___|    
+   |   |      |                                           |___|
    |   |      |                                 ______        |___._
    |   |______|                                |______|         ____| BNC
    |                                            Jumper-        _____| Connector
-   |   Main chip                                block  _    __|   '  
+   |   Main chip                                block  _    __|   '
    |                                                  | |  |    RJ Connector
    |                                                  |_|  |    with 110 Ohm
    |                                                       |__  Terminator
@@ -2208,46 +2275,49 @@ Model #500-6242-0097 REV A (8-bit card)
    |   |___________|   |_____|                             |__
    |  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
+           I/O
 
-         1 2 3 4 5 6 7 8
+          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
+  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.
+      ShMem address.
 
-      1 2 3 4 5 6 7 8
+       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
+  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
+        IRQ
 
 
-   3 4 5 7 2
-   . . . . .
-   . . . . .
+     3 4 5 7 2
+     . . . . .
+     . . . . .
 
 
 There is a DIP-switch with 8 switches, used to set the shared memory address
@@ -2266,10 +2336,9 @@ 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. ??
+=============================
 
-*****************************************************************************
-
-** Waterloo Microsystems Inc. ?? **
 8-bit card (C) 1985
 -------------------
   - from Robert Michael Best <rmb117@cs.usask.ca>
@@ -2283,103 +2352,104 @@ 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 
+
+::
+
+   _____________________________________________________________________
+  | \/  \/              ___  __ __                                      |
+  | 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
+::
+
+  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 
+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
+=======
 
-** 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
@@ -2388,8 +2458,10 @@ it is "Made in Taiwan"
 
 This description has been written by Juergen Seifert <seifert@htwm.de>
 using information from the Original
-                    "ARCnet Installation Manual"
 
+                   "ARCnet Installation Manual"
+
+::
 
     ________________________________________________________________
    | |STAR| BUS| T/P|                                               |
@@ -2416,32 +2488,32 @@ using information from the Original
        |        \ IRQ   / T T O                      |
        |__________________1_2_M______________________|
 
-Legend:
+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
+  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:
+The node ID is the sum of the values of all switches set to "1"
+These values are::
 
     Switch | Value
     -------|-------
@@ -2454,30 +2526,30 @@ These values are:
       2    |  64
       1    | 128
 
-Some Examples:
+Some Examples::
 
-    Switch         | Hex     | Decimal 
+    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 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
+of eight possible I/O Base addresses using the following table::
 
    Switch      | Hex I/O
     1   2   3  | Address
@@ -2493,7 +2565,7 @@ of eight possible I/O Base addresses using the following table
 
 
 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.
@@ -2501,6 +2573,8 @@ 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 *)
    -----------|---------|-----------
@@ -2508,60 +2582,62 @@ positions, determined by the offset, switches 7 and 8 of group SW2.
    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.
+
+   *) 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)
@@ -2572,8 +2648,8 @@ must be set to the same timeout values.
 On means jumper installed, Off means jumper not installed
 
 
-NONAME 16-BIT ARCNET
-====================
+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,
@@ -2584,13 +2660,16 @@ the booklet there is a different way of counting ... 2-9, 2-10, A-1,
 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"
 
+                   "ARCnet Installation Manual"
+
+::
 
    ___________________________________________________________________
   <                    _________________  _________________           |
@@ -2622,15 +2701,15 @@ 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:
+The node ID is the sum of the values of all switches set to "1"
+These values are::
 
     Switch | Value
     -------|-------
@@ -2643,30 +2722,30 @@ These values are:
       2    |  64
       1    | 128
 
-Some Examples:
+Some Examples::
 
-    Switch         | Hex     | Decimal 
+    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 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
+of eight possible I/O Base addresses using the following table::
 
    Switch      | Hex I/O
     3   2   1  | Address
@@ -2682,13 +2761,13 @@ of eight possible I/O Base addresses using the following table
 
 
 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.
+positions, determined by the offset, switches 4 and 5 of group SW1::
 
    Switch     | Hex RAM | Hex ROM
    8 7 6  5 4 | Address | Address
@@ -2697,111 +2776,111 @@ positions, determined by the offset, switches 4 and 5 of group SW1.
    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 
+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 
+::
+
+         ____________________________________________________________
+        |                 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 
+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.
@@ -2809,8 +2888,8 @@ 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:
+The node ID is the sum of the values of all switches set to "1"
+These values are::
 
    Switch | Label | Value
    -------|-------|-------
@@ -2823,30 +2902,30 @@ These values are:
      7    | ID6   |  64
      8    | ID7   | 128
 
-Some Examples:
+Some Examples::
 
-    Switch         | Hex     | Decimal 
+    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 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
+of eight possible I/O Base addresses using the following table::
 
 
    Switch      | Hex I/O
@@ -2863,13 +2942,16 @@ of eight possible I/O Base addresses using the following table
 
 
 Setting the Base Memory (RAM) buffer Address
---------------------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-The memory buffer (RAM) requires 2K. The base of this buffer can be 
+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 *)
    --------------------|---------|-----------
@@ -2881,15 +2963,15 @@ Jumpers 3-5 of jumper block SW1 select the Memory Base address.
    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.
+
+  *) 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.
+Jumpers 1-5 of the jumper block SW3 control the IRQ level::
 
     Jumper              |  IRQ
     1   2   3   4   5   |
@@ -2902,23 +2984,24 @@ Jumpers 1-5 of the jumper block SW3 control the IRQ level.
 
 
 Setting the Timeout Parameters
-------------------------------
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-The jumpers 6-7 of the jumper block SW3 are used to determine the timeout 
+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     |                      |  |
    |   ___________             |_____________|                      |  |
@@ -2936,7 +3019,7 @@ parameters. These two jumpers are normally left in the OFF position.
    |  |________________|     |              |    : B   |-           |  |
    |    1 2 3 4 5 6 7 8      |              |    : O   |-           |  |
    |                         |_________o____|..../ A   |-    _______|  |
-   |    ____________________                |      R   |-   |       |------,   
+   |    ____________________                |      R   |-   |       |------,
    |   |                    |               |      D   |-   |  BNC  |   #  |
    |   > 2764 PROM SOCKET   |               |__________|-   |_______|------'
    |   |____________________|              _________                |  |
@@ -2945,23 +3028,24 @@ parameters. These two jumpers are normally left in the OFF position.
    |___                                               ______________|  |
        |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:
+                                                                     \|
+
+Legend::
 
-SL90C65        ARCNET Controller / Transceiver /Logic
-SW1    1-5:    IRQ Select
+  SL90C65      ARCNET Controller / Transceiver /Logic
+  SW1  1-5:    IRQ Select
          6:    ET1
          7:    ET2
-         8:    ROM ENABLE 
-SW2    1-3:    Memory Buffer/PROM Address
+         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 
+  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)
@@ -2976,10 +3060,10 @@ are normally left off (down).
 
 
 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
+of eight possible I/O Base addresses using the following table::
 
 
    Switch | Hex I/O
@@ -2996,7 +3080,7 @@ of eight possible I/O Base addresses using the following table
 
 
 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.
@@ -3004,13 +3088,16 @@ 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 |  ?????  |  ?????  
+   0 1 1 |  ?????  |  ?????
    1 0 0 |  ?????  |  ?????
    1 0 1 |  ?????  |  ?????
    1 1 0 |  ?????  |  ?????
@@ -3018,7 +3105,7 @@ I could, however, only verify two settings...
 
 
 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
@@ -3026,8 +3113,9 @@ 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:
+The node ID is the sum of the values of all switches set to "1"
+These values are::
+
     Switch | Value
     -------|-------
       1    |   1
@@ -3039,70 +3127,80 @@ These values are:
       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  <-'
-  
+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
+=====
 
-** 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
+
+
+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     
+110     DC000
 111     E0000
+===     ================
 
 Settings for I/O Address (Top Jumper Line)
+
+===     ====
 123     Port
+===     ====
 000    260
 001    290
 010    2E0
@@ -3111,23 +3209,26 @@ Settings for I/O Address (Top Jumper Line)
 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.
similarity index 76%
rename from Documentation/networking/arcnet.txt
rename to Documentation/networking/arcnet.rst
index aff97f4..e93d982 100644 (file)
@@ -1,11 +1,18 @@
-----------------------------------------------------------------------------
-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.
-----------------------------------------------------------------------------
+.. 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:
+attention::
+
                This driver's getting fat and beefy,
                But my cat is still named Fifi.
 
@@ -24,28 +31,21 @@ 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?
+.. warning::
 
-(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.)
+   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?
 
-My e-mail address is: apenwarr@worldvisions.ca
+   (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 
+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
@@ -62,12 +62,13 @@ 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. 
+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
@@ -80,17 +81,20 @@ Other Drivers and Info
 ----------------------
 
 You can try my ARCNET page on the World Wide Web at:
-       http://www.qis.net/~jschmitz/arcnet/    
+
+       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.
 
@@ -99,19 +103,20 @@ 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. 
+access.
 
 
 Installing the Driver
 ---------------------
 
-All you will need to do in order to install the driver is:
+All you will need to do in order to install the driver is::
+
        make config
-               (be sure to choose ARCnet in the network devices 
+               (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.
@@ -125,10 +130,12 @@ There are four chipset options:
 
 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:
+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:
+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.
@@ -136,14 +143,17 @@ 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 
+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:
+
+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:
+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>
 
@@ -160,8 +170,10 @@ 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>] 
+
+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>
@@ -169,44 +181,49 @@ If you load the chipset support as a module, the options are:
  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 
+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:
+Command line options::
+
  arcrimi=<shmem>,<irq>,<node_ID>[,<name>]
 
-If you load the chipset support as a module, the options are:
+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 
+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' 
+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 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:
+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 
+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.
@@ -224,69 +241,78 @@ 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. 
+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 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::
 
-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>
+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
+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.
+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
+
+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. 
+       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
+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.
@@ -295,7 +321,8 @@ OS/2: I've been told it works under Warp Connect with an ARCnet driver from
        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
+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.
@@ -307,16 +334,17 @@ 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. 
+       ======  ===============================================================
+       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. 
+               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
+
+       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
@@ -328,8 +356,8 @@ The ARCnet driver v2.10 ALPHA supports three protocols, each on its own
                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
+
+       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)
@@ -338,9 +366,10 @@ The ARCnet driver v2.10 ALPHA supports three protocols, each on its own
                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
@@ -358,19 +387,21 @@ can set up your network then:
    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
-   
+
+   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.
 
 
@@ -391,29 +422,32 @@ can set up your network then:
    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. 
+   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
+
+       - 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
+
+   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 */
-       
+                                       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
@@ -425,7 +459,7 @@ can set up your network then:
    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
@@ -435,53 +469,54 @@ can set up your network then:
    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
+
+   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
-   
+
+   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. 
+
+   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. 
+   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]
+   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)
+
+                            (IP Masquerade)
+         /---------------\         *            /---------------\
+         |               |         *            |               |
+         |               +-Freedom-*-Gatekeeper-+               |
+         |               |    |    *            |               |
+         \-------+-------/    |    *            \-------+-------/
+                 |            |                         |
+              Insight         |                      Patience
+                          (Internet)
 
 
 
@@ -491,6 +526,7 @@ 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
@@ -525,7 +561,7 @@ 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. 
+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,
@@ -535,9 +571,11 @@ 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:
+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.
 
@@ -546,7 +584,7 @@ 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. 
+If you don't understand that, you probably don't want to know anyway.
 E-mail me about your problem.
 
 
similarity index 89%
rename from Documentation/networking/atm.txt
rename to Documentation/networking/atm.rst
index 82921ce..c1df8c0 100644 (file)
@@ -1,3 +1,9 @@
+.. 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
similarity index 91%
rename from Documentation/networking/ax25.txt
rename to Documentation/networking/ax25.rst
index 8257dbf..824afd7 100644 (file)
@@ -1,3 +1,9 @@
+.. 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
similarity index 58%
rename from Documentation/networking/baycom.txt
rename to Documentation/networking/baycom.rst
index 688f18f..fe2d010 100644 (file)
@@ -1,26 +1,31 @@
-                   LINUX DRIVERS FOR BAYCOM MODEMS
+.. SPDX-License-Identifier: GPL-2.0
 
-       Thomas M. Sailer, HB9JNX/AE4WA, <sailer@ife.ee.ethz.ch>
+===============================
+Linux Drivers for Baycom Modems
+===============================
 
-!!NEW!! (04/98) The drivers for the baycom modems have been split into
+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. 
+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,
+  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. 
+  delta status bits, you may try baycom_ser_hdx.
 
-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
@@ -37,45 +42,48 @@ baycom_epp:
 
 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.
-
-
+======= ========================================================================
+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 
+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
@@ -88,20 +96,22 @@ 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).
+``/etc/modprobe.d/*.conf``).
+
+Examples::
 
-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).
+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
@@ -115,29 +125,33 @@ 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.
+======= =================================================================
+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.
+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.
+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
@@ -154,5 +168,7 @@ 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
similarity index 75%
rename from Documentation/networking/bonding.txt
rename to Documentation/networking/bonding.rst
index e3abfbd..24168b0 100644 (file)
@@ -1,10 +1,15 @@
+.. SPDX-License-Identifier: GPL-2.0
 
-               Linux Ethernet Bonding Driver HOWTO
+===================================
+Linux Ethernet Bonding Driver HOWTO
+===================================
 
-               Latest update: 27 April 2011
+Latest update: 27 April 2011
+
+Initial release: Thomas Davis <tadavis at lbl.gov>
+
+Corrections, HA extensions: 2000/10/03-15:
 
-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>
@@ -13,98 +18,98 @@ Corrections, HA extensions : 2000/10/03-15 :
 
 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
+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
+
+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
+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
-=================
+.. Table of Contents
 
-1. Bonding Driver Installation
+   1. Bonding Driver Installation
 
-2. Bonding Driver Options
+   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
+   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
+   4. Querying Bonding Configuration
+   4.1 Bonding Configuration
+   4.2 Network Configuration
 
-5. Switch Configuration
+   5. Switch Configuration
 
-6. 802.1q VLAN Support
+   6. 802.1q VLAN Support
 
-7. Link Monitoring
-7.1    ARP Monitor Operation
-7.2    Configuring Multiple ARP Targets
-7.3    MII Monitor Operation
+   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
+   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
+   9. SNMP agents
 
-10. Promiscuous mode
+   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
+   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
+   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
+   13. Switch Behavior Issues
+   13.1        Link Establishment and Failover Delays
+   13.2        Duplicated Incoming Packets
 
-14. Hardware Specific Considerations
-14.1   IBM BladeCenter
+   14. Hardware Specific Considerations
+   14.1        IBM BladeCenter
 
-15. Frequently Asked Questions
+   15. Frequently Asked Questions
 
-16. Resources and Links
+   16. Resources and Links
 
 
 1. Bonding Driver Installation
 ==============================
 
-       Most popular distro kernels ship with the bonding driver
+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
@@ -113,54 +118,54 @@ the following steps:
 1.1 Configure and build the kernel with bonding
 -----------------------------------------------
 
-       The current version of the bonding driver is available in the
+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
+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.
+Build and install the new kernel and modules.
 
 1.2 Bonding Control Utility
--------------------------------------
+---------------------------
 
-        It is recommended to configure bonding via iproute2 (netlink)
+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
+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
+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
+``/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
+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
+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
+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
+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:
+The parameters are as follows:
 
 active_slave
 
@@ -246,10 +251,13 @@ 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.
@@ -699,7 +707,7 @@ mode
                swapped with the new curr_active_slave that was
                chosen.
 
-num_grat_arp
+num_grat_arp,
 num_unsol_na
 
        Specify the number of peer notifications (gratuitous ARPs and
@@ -729,13 +737,13 @@ packets_per_slave
 
 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.
+       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
 
@@ -977,88 +985,88 @@ lp_interval
 3. Configuring Bonding Devices
 ==============================
 
-       You can configure bonding using either your distro's network
+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
+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,
+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.
+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:
+Else, issue the command::
 
-$ rpm -qf /sbin/ifup
+       $ rpm -qf /sbin/ifup
 
-       It will respond with a line of text starting with either
+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:
+Next, to determine if your installation supports bonding,
+issue the command::
 
-$ grep ifenslave /sbin/ifup
+    $ grep ifenslave /sbin/ifup
 
-       If this returns any matches, then your initscripts or
+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
+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
+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
+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:
+name of the configuration file for each device will be of the form::
 
-ifcfg-id-xx:xx:xx:xx:xx:xx
+    ifcfg-id-xx:xx:xx:xx:xx:xx
 
-       Where the "xx" portion will be replaced with the digits from
+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
+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:
+something like this::
 
-BOOTPROTO='dhcp'
-STARTMODE='on'
-USERCTL='no'
-UNIQUE='XNzu.WeZGOGF+4wE'
-_nm_name='bus-pci-0001:61:01.0'
+       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:
+Change the BOOTPROTO and STARTMODE lines to the following::
 
-BOOTPROTO='none'
-STARTMODE='off'
+       BOOTPROTO='none'
+       STARTMODE='off'
 
-       Do not alter the UNIQUE or _nm_name lines.  Remove any other
+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,
+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
@@ -1066,49 +1074,52 @@ 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
+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 STARTMODE specifies when the device is brought online.
 The possible values are:
 
-       onboot:  The device is started at boot time.  If you're not
+       ======== ======================================================
+       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
+       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
+       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.
+       off or   The device configuration is ignored.
+       ignore
+       ======== ======================================================
 
-       The line BONDING_MASTER='yes' indicates that the device is a
+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
+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
+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
@@ -1120,34 +1131,34 @@ 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,
+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:
+effect.  This can be accomplished via the following::
 
-# /etc/init.d/network restart
+       # /etc/init.d/network restart
 
-       Note that the network control script (/sbin/ifdown) will
+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
+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:
+Additional general options and details of the ifcfg file
+format can be found in an example ifcfg template file::
 
-/etc/sysconfig/network/ifcfg.template
+       /etc/sysconfig/network/ifcfg.template
 
-       Note that the template does not document the various BONDING_
+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'
+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
@@ -1157,7 +1168,7 @@ sent to the network.
 3.1.2 Configuring Multiple Bonds with Sysconfig
 -----------------------------------------------
 
-       The sysconfig network initialization system is capable of
+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
@@ -1165,14 +1176,14 @@ 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
+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.
+the system ``/etc/modules.d/*.conf`` configuration files.
 
 3.2 Configuration with Initscripts Support
 ------------------------------------------
 
-       This section applies to distros using a recent version of
+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
@@ -1180,7 +1191,7 @@ 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
+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
@@ -1188,19 +1199,19 @@ 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
+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:
+Place the following text in the file::
 
-DEVICE=eth0
-USERCTL=no
-ONBOOT=yes
-MASTER=bond0
-SLAVE=yes
-BOOTPROTO=none
+       DEVICE=eth0
+       USERCTL=no
+       ONBOOT=yes
+       MASTER=bond0
+       SLAVE=yes
+       BOOTPROTO=none
 
-       The DEVICE= line will be different for every ethX device and
+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.
@@ -1208,69 +1219,70 @@ 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
+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,
+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
+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:
+file, e.g. a line of the format::
 
-BONDING_OPTS="mode=active-backup arp_interval=60 arp_ip_target=192.168.1.254"
+  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
+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.,
+queried targets, e.g.,::
 
-       arp_ip_target=+192.168.1.1 arp_ip_target=+192.168.1.2
+    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.
+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
+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
+       alias bond0 bonding
+       options bond0 mode=balance-alb miimon=100
 
-       Replace the sample parameters with the appropriate set of
+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
+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
+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
+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.
@@ -1278,7 +1290,7 @@ is case sensitive.
 3.2.2 Configuring Multiple Bonds with Initscripts
 -------------------------------------------------
 
-       Initscripts packages that are included with Fedora 7 and Red Hat
+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,
@@ -1290,77 +1302,77 @@ below.
 3.3 Configuring Bonding Manually with iproute2
 -----------------------------------------------
 
-       This section applies to distros whose network initialization
+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
+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
+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:
+/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
+       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
+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
+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.,
+configuration, it is necessary to run the initialization script, e.g.,::
 
-# /etc/init.d/boot.local
+       # /etc/init.d/boot.local
 
-       or
+or::
 
-# /etc/rc.d/rc.local
+       # /etc/rc.d/rc.local
 
-       It may be desirable in such a case to create a separate script
+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
+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:
+the following::
 
-# ifconfig bond0 down
-# rmmod bonding
-# rmmod e100
+       # ifconfig bond0 down
+       # rmmod bonding
+       # rmmod e100
 
-       Again, for convenience, it may be desirable to create a script
+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
+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
+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
+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
+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
@@ -1368,35 +1380,35 @@ 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
+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:
+sets of bonding options in ``/etc/modprobe.d/*.conf``, for example::
 
-alias bond0 bonding
-options bond0 -o bond0 mode=balance-rr miimon=100
+       alias bond0 bonding
+       options bond0 -o bond0 mode=balance-rr miimon=100
 
-alias bond1 bonding
-options bond1 -o bond1 mode=balance-alb miimon=50
+       alias bond1 bonding
+       options bond1 -o bond1 mode=balance-alb miimon=50
 
-       will load the bonding module two times.  The first instance is
+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),
+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:
+as follows::
 
-install bond1 /sbin/modprobe --ignore-install bonding -o bond1 \
-       mode=balance-alb miimon=50
+       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
+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
+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
@@ -1407,18 +1419,18 @@ kernels, and also lack sysfs support).
 3.4 Configuring Bonding Manually via Sysfs
 ------------------------------------------
 
-       Starting with version 3.0.0, Channel Bonding may be configured
+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
+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
+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
@@ -1426,38 +1438,45 @@ example paths accordingly.
 
 Creating and Destroying Bonds
 -----------------------------
-To add a new bond foo:
-# echo +foo > /sys/class/net/bonding_masters
+To add a new bond foo::
+
+       # echo +foo > /sys/class/net/bonding_masters
+
+To remove an existing bond bar::
 
-To remove an existing bond bar:
-# echo -bar > /sys/class/net/bonding_masters
+       # echo -bar > /sys/class/net/bonding_masters
 
-To show all existing bonds:
-# cat /sys/class/net/bonding_masters
+To show all existing bonds::
 
-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.
+       # 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
+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 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
+To free slave eth0 from bond bond0::
 
-       When an interface is enslaved to a bond, symlinks between the
+       # 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
+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
@@ -1465,127 +1484,143 @@ the name of the bond interface.
 
 Changing a Bond's Configuration
 -------------------------------
-       Each bond may be configured individually by manipulating the
+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-
+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
+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.
+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,
+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
+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:
+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
+       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
+To add a second bond, with two e1000 interfaces in
 active-backup mode, using ARP monitoring, add the following lines to
-your init script:
+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
+       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
+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 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.
+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
+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.
+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
+       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
+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
@@ -1604,37 +1639,37 @@ 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
+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:
+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
+       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: 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
+       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:
+The queue_id for a slave can be set using the command::
 
-# echo "eth1:2" > /sys/class/net/bond0/bonding/queue_id
+       # 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
@@ -1645,12 +1680,12 @@ 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:
+device. The following commands would accomplish this::
 
-# tc qdisc add dev bond0 handle 1 root multiq
+       # 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
+       # 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
@@ -1663,7 +1698,7 @@ 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 
+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
@@ -1689,31 +1724,31 @@ 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.
+       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
+             # 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.
+       code generates random priority and sets it::
 
-       # sys_prio=$(( 1 + RANDOM + RANDOM ))
-       # echo $sys_prio > /sys/class/net/bond0/bonding/ad_actor_sys_prio
+           # 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.
+       sets it::
 
-       # usr_port_key=$(( RANDOM & 0x3FF ))
-       # echo $usr_port_key > /sys/class/net/bond0/bonding/ad_user_port_key
+           # usr_port_key=$(( RANDOM & 0x3FF ))
+           # echo $usr_port_key > /sys/class/net/bond0/bonding/ad_user_port_key
 
 
 4 Querying Bonding Configuration
@@ -1722,81 +1757,81 @@ few bonding parameters:
 4.1 Bonding Configuration
 -------------------------
 
-       Each bonding device has a read-only file residing in the
+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
+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:
+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 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
+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
+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
+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
+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
+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
+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
@@ -1804,7 +1839,7 @@ 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
+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
@@ -1820,7 +1855,7 @@ with another EtherChannel group.
 6. 802.1q VLAN Support
 ======================
 
-       It is possible to configure VLAN devices over a bond interface
+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
@@ -1829,7 +1864,7 @@ 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
+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
@@ -1839,7 +1874,7 @@ 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
+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
@@ -1847,23 +1882,23 @@ 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
+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
+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
+1. Remove all VLAN interfaces then recreate them
 
-       2. Set the bonding interface's hardware address so that it
+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
+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.
 
@@ -1871,24 +1906,24 @@ mode, which might not be what you want.
 7. Link Monitoring
 ==================
 
-       The bonding driver at present supports two schemes for
+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
+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
+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
+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
@@ -1900,36 +1935,36 @@ 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
+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:
+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
+ # 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:
+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
+    # 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
+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),
+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
@@ -1937,7 +1972,7 @@ 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
+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
@@ -1952,25 +1987,25 @@ up.
 8.1 Adventures in Routing
 -------------------------
 
-       When bonding is configured, it is important that the slave
+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:
+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
+  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
+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
+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
@@ -1978,7 +2013,7 @@ 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
+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
@@ -1987,22 +2022,22 @@ route additions may cause trouble.
 8.2 Ethernet Device Renaming
 ----------------------------
 
-       On systems with network configuration scripts that do not
+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:
+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
+       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
+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,
@@ -2010,36 +2045,36 @@ 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:
+Adding the following::
 
-add above bonding e1000 tg3
+       add above bonding e1000 tg3
 
-       causes modprobe to load e1000 then tg3, in that order, when
+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.
+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:
+/etc/modprobe.d/ as::
 
-softdep bonding pre: tg3 e1000
+       softdep bonding pre: tg3 e1000
 
-       This will load tg3 and e1000 modules before loading the bonding one.
+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
+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
+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
+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
@@ -2051,7 +2086,7 @@ 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
+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.
@@ -2059,7 +2094,7 @@ traffic while still maintaining carrier on.
 9. SNMP agents
 ===============
 
-       If running SNMP agents, the bonding driver should be loaded
+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
@@ -2070,6 +2105,8 @@ 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
@@ -2081,7 +2118,7 @@ in the ifDescr table (ifDescr.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
+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.
@@ -2097,7 +2134,7 @@ correctly associated with ifDescr.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
+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.
@@ -2105,34 +2142,34 @@ association.
 10. Promiscuous mode
 ====================
 
-       When running network monitoring tools, e.g., tcpdump, it is
+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,
+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
+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
+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
+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
+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
+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
@@ -2142,7 +2179,7 @@ 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
+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
@@ -2150,32 +2187,32 @@ 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"
+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
+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
+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.
@@ -2183,19 +2220,21 @@ 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
+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
+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
+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
@@ -2205,7 +2244,7 @@ broadcast: This mode is really a special purpose mode, and is suitable
 11.2.2 HA Link Monitoring Selection for Multiple Switch Topology
 ----------------------------------------------------------------
 
-       The choice of link monitoring ultimately depends upon your
+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
@@ -2213,7 +2252,7 @@ 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
+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,
@@ -2222,7 +2261,7 @@ 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
+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).
@@ -2238,18 +2277,18 @@ suitable switches.
 12.1 Maximizing Throughput in a Single Switch Topology
 ------------------------------------------------------
 
-       In a single switch configuration, the best method to maximize
+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
+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
+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:
+other networks.  An example would be the following::
 
 
      +----------+                     +----------+
@@ -2259,25 +2298,25 @@ other networks.  An example would be the following:
      |          |eth1            port2|          | here somewhere
      +----------+                     +----------+
 
-       The router may be a dedicated router device, or another host
+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
+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
+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
+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:
+following::
 
     +----------+            +----------+       +--------+
     |          |eth0   port1|          +-------+ Host B |
@@ -2287,19 +2326,19 @@ following:
     +----------+            +----------+port4             +--------+
 
 
-       Again, the switch may be a dedicated switch device, or another
+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
+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
+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
@@ -2309,11 +2348,12 @@ 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,
+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
+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
@@ -2351,7 +2391,8 @@ balance-rr: This mode is the only mode that will permit a single
        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
+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
@@ -2361,7 +2402,8 @@ active-backup: There is not much advantage in this network topology to
        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
+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
@@ -2373,10 +2415,12 @@ balance-xor: This mode will limit traffic such that packets destined
        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
+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
+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,
@@ -2390,7 +2434,7 @@ broadcast: Like active-backup, there is not much advantage to this
        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.  
+       bandwidth.
 
        Additionally, the linux bonding 802.3ad implementation
        distributes traffic by peer (using an XOR of MAC addresses
@@ -2404,7 +2448,8 @@ broadcast: Like active-backup, there is not much advantage to this
        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.
+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
@@ -2422,7 +2467,8 @@ balance-tlb: The balance-tlb mode balances outgoing traffic by peer.
        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.
+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,
@@ -2435,7 +2481,7 @@ balance-alb: This mode is everything that balance-tlb is, and more.
 12.1.2 MT Link Monitoring for Single Switch Topology
 ----------------------------------------------------
 
-       The choice of link monitoring may largely depend upon which
+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
@@ -2444,27 +2490,27 @@ assurance as the ARP monitor).
 12.2 Maximum Throughput in a Multiple Switch Topology
 -----------------------------------------------------
 
-       Multiple switches may be utilized to optimize for throughput
+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
+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
@@ -2472,14 +2518,14 @@ 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
+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
+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
@@ -2492,7 +2538,7 @@ 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
+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
@@ -2505,10 +2551,10 @@ host in the network is configured with bonding).
 13.1 Link Establishment and Failover Delays
 -------------------------------------------
 
-       Some switches exhibit undesirable behavior with regard to the
+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
+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
@@ -2517,12 +2563,12 @@ 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
+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
+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
@@ -2532,7 +2578,7 @@ 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
+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.
@@ -2540,31 +2586,31 @@ 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
+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
+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
+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
@@ -2574,7 +2620,7 @@ 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
+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
@@ -2583,16 +2629,16 @@ dynamic" will accomplish this).
 14. Hardware Specific Considerations
 ====================================
 
-       This section contains additional information for configuring
+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.
+This applies to the JS20 and similar systems.
 
-       On the JS20 blades, the bonding driver supports only
+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.
@@ -2600,7 +2646,7 @@ below.
 JS20 network adapter information
 --------------------------------
 
-       All JS20s come with two Broadcom Gigabit Ethernet ports
+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.
@@ -2608,36 +2654,36 @@ 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
+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
+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"
+"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
+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
+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,
+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
+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
@@ -2647,24 +2693,24 @@ Topology," above.
 Requirements for specific modes
 -------------------------------
 
-       The balance-rr mode requires the use of passthrough modules
+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
+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.
+The active-backup mode has no additional requirements.
 
 Link monitoring issues
 ----------------------
 
-       When an Ethernet Switch Module is in place, only the ARP
+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
@@ -2672,166 +2718,173 @@ 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
+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
+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
+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.
+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
+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
+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.
+There is no limit.
 
 4.  How many slaves can a bonding device have?
+----------------------------------------------
 
-       This is limited only by the number of network interfaces Linux
+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
+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
+
+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
+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.
+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.
+The full answer to this depends upon the desired mode.
 
-       In the basic balance modes (balance-rr and balance-xor), it
+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
+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
+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.
+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
+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
+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:
+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
+       # ifconfig bond0 hw ether 00:11:22:33:44:55
 
-# ip link set bond0 address 66:77:88:99:aa:bb
+       # 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):
+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...
+       # ifconfig bond0 down ; modprobe -r bonding
+       # ifconfig bond0 .... up
+       # ifenslave bond0 eth...
 
-       This method will automatically take the address from the next
+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
+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
+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).
+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
+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
+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
+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
+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/ 
+
+ - 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.
-
--- END --
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
similarity index 90%
rename from Documentation/networking/caif/Linux-CAIF.txt
rename to Documentation/networking/caif/linux_caif.rst
index 0aa4bd3..a048086 100644 (file)
@@ -1,12 +1,19 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+==========
 Linux CAIF
-===========
-copyright (C) ST-Ericsson AB 2010
-Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
-License terms: GNU General Public License (GPL) version 2
+==========
+
+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.
@@ -16,13 +23,16 @@ ST-Ericsson modems support a number of transports between modem
 and host. Currently, UART and Loopback are available for Linux.
 
 
-Architecture:
-------------
+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
    !
@@ -46,12 +56,12 @@ The implementation of CAIF is divided into:
 
 
 
-I M P L E M E N T A T I O N
-===========================
+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
@@ -59,8 +69,11 @@ 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 ==
+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.
@@ -73,7 +86,8 @@ The Core CAIF implementation contains:
        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:
 
@@ -112,7 +126,7 @@ The CAIF Protocol implementation contains:
       - CFSERL CAIF Serial layer. Handles concatenation/split of frames
        into CAIF Frames with correct length.
 
-
+::
 
                    +---------+
                    | Config  |
@@ -143,18 +157,24 @@ The CAIF Protocol implementation contains:
 
 
 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
+      - Layers are stacked by setting the pointers::
+
                  layer->up , layer->dn
-      -        In order to send data upwards, each layer should do
+
+      -        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
+
+      - 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
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:
 
 
similarity index 88%
rename from Documentation/networking/cdc_mbim.txt
rename to Documentation/networking/cdc_mbim.rst
index 4e68f0b..0048409 100644 (file)
@@ -1,5 +1,8 @@
-     cdc_mbim - Driver for CDC MBIM Mobile Broadband modems
-    ========================================================
+.. 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
@@ -19,9 +22,9 @@ by a cdc_ncm driver parameter:
 
 prefer_mbim
 -----------
-Type:          Boolean
-Valid Range:   N/Y (0-1)
-Default Value: Y (MBIM is preferred)
+: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
@@ -44,11 +47,13 @@ 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
@@ -76,7 +81,7 @@ 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:
+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
@@ -119,13 +124,15 @@ 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>
@@ -178,7 +185,7 @@ 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:
+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
 
@@ -207,6 +214,7 @@ 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)
@@ -218,7 +226,7 @@ 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:
+symlink::
 
   ip link add link wwan0 name wwan0.dss5 type vlan id 261
   ip link set dev wwan0.dss5 up
@@ -236,7 +244,7 @@ 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:
+example::
 
   static struct sock_filter dssfilter[] = {
        /* use special negative offsets to get VLAN tag */
@@ -249,11 +257,11 @@ example:
        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_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 */
+       BPF_STMT(BPF_RET|BPF_K, (u_int)-1),     /* accept */
+       BPF_STMT(BPF_RET|BPF_K, 0),             /* ignore */
   };
 
 
@@ -266,6 +274,7 @@ 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
@@ -280,7 +289,7 @@ 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:
+example::
 
   ip link add link wwan0 name wwan0.0 type vlan id 4094
 
@@ -290,7 +299,7 @@ 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:
+sessions on the shared USB data channel::
 
   VLAN ID       MBIM type   MBIM SessionID           Notes
   ---------------------------------------------------------
@@ -310,30 +319,37 @@ sessions on the shared USB data channel:
 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
+ 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
+ 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"
+ 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"
+ 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"
+ 5) "MBIM (Mobile Broadband Interface Model) Registry"
+
        - http://compliance.usb.org/mbim/
 
-[6] "/sys/kernel/debug/usb/devices output format"
+ 6) "/sys/kernel/debug/usb/devices output format"
+
        - Documentation/driver-api/usb/usb.rst
 
-[7] "/sys/bus/usb/devices/.../descriptors"
+ 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"
similarity index 66%
rename from Documentation/networking/cxacru.txt
rename to Documentation/networking/cxacru.rst
index 2cce044..6088af2 100644 (file)
@@ -1,3 +1,9 @@
+.. 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
@@ -19,29 +25,35 @@ several sysfs attribute files for retrieving device statistics:
 
 * adsl_headend
 * adsl_headend_environment
-       Information about the remote headend.
+
+       - Information about the remote headend.
 
 * adsl_config
-       Configuration writing interface.
-       Write parameters in hexadecimal format <index>=<value>,
-       separated by whitespace, e.g.:
+
+       - 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.
+
+       - 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.
+
+       - Downstream stats.
 
 * upstream_attenuation (dB)
 * upstream_bits_per_frame
 * upstream_rate (kbps)
 * upstream_snr_margin (dB)
 * transmitter_power (dBm/Hz)
-       Upstream stats.
+
+       - Upstream stats.
 
 * downstream_crc_errors
 * downstream_fec_errors
@@ -49,48 +61,56 @@ several sysfs attribute files for retrieving device statistics:
 * upstream_crc_errors
 * upstream_fec_errors
 * upstream_hec_errors
-       Error counts.
+
+       - Error counts.
 
 * line_startable
-       Indicates that ADSL support on the device
-       is/can be enabled, see adsl_start.
+
+       - 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"
+
+        - "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"
+
+        - "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)"
+
+        - "" (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.
+
+       - 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:
+        - "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
similarity index 94%
rename from Documentation/networking/dccp.txt
rename to Documentation/networking/dccp.rst
index 55c575f..dde16be 100644 (file)
@@ -1,16 +1,18 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============
 DCCP protocol
 =============
 
 
-Contents
-========
-- Introduction
-- Missing features
-- Socket options
-- Sysctl variables
-- IOCTLs
-- Other tunables
-- Notes
+.. Contents
+   - Introduction
+   - Missing features
+   - Socket options
+   - Sysctl variables
+   - IOCTLs
+   - Other tunables
+   - Notes
 
 
 Introduction
@@ -38,6 +40,7 @@ The Linux DCCP implementation does not currently support all the features that a
 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
@@ -54,7 +57,8 @@ 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:
+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) */
@@ -94,7 +98,7 @@ 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.
+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.
 
@@ -113,6 +117,7 @@ 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.
@@ -123,11 +128,13 @@ DCCP_SOCKOPT_RECV_CSCOV is for the receiver and has a different meaning: it
 
 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
+       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
+       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
@@ -182,7 +189,7 @@ sync_ratelimit = 125 ms
 IOCTLS
 ======
 FIONREAD
-       Works as in udp(7): returns in the `int' argument pointer the size of
+       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.
 
 
@@ -191,10 +198,12 @@ 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:
+       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).
similarity index 89%
rename from Documentation/networking/dctcp.txt
rename to Documentation/networking/dctcp.rst
index 13a8577..4cc8bb2 100644 (file)
@@ -1,11 +1,14 @@
+.. 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:
+To enable it on end hosts::
 
   sysctl -w net.ipv4.tcp_congestion_control=dctcp
   sysctl -w net.ipv4.tcp_ecn_fallback=0 (optional)
@@ -25,14 +28,19 @@ 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
+
+      "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:
similarity index 87%
rename from Documentation/networking/decnet.txt
rename to Documentation/networking/decnet.rst
index d192f8b..b8bc11f 100644 (file)
@@ -1,26 +1,31 @@
-                    Linux DECnet Networking Layer Information
-                   ===========================================
+.. SPDX-License-Identifier: GPL-2.0
 
-1) Other documentation....
+=========================================
+Linux DECnet Networking Layer Information
+=========================================
 
-   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
+1. Other documentation....
+==========================
 
-2) Configuring the kernel
+   - 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)
+    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)
+    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
@@ -29,7 +34,7 @@ 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
- 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
@@ -37,7 +42,8 @@ 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
+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.
@@ -56,7 +62,7 @@ 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:
+The kernel command line takes options looking like the following::
 
     decnet.addr=1,2
 
@@ -82,7 +88,7 @@ address of the node in order for it to be autoconfigured (and then appear in
 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:
+add the line::
 
     MACADDR=AA:00:04:00:03:04
 
@@ -95,7 +101,7 @@ 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:
+is available. Usually this will be eth0, for example::
 
     echo -n "eth0" >/proc/sys/net/decnet/default_device
 
@@ -106,7 +112,9 @@ 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
+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
@@ -122,20 +130,21 @@ 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. 
+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 ?
+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 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.
@@ -160,7 +169,8 @@ kernel subsystem is working.
      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
+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
@@ -175,18 +185,19 @@ information (_most_ of which _is_ _essential_) includes:
  - 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 
+ - 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
+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. 
+interact and how to get the best performance from your hardware.
 
-Ethernet cards are designed to normally only pass received network frames 
+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
@@ -197,8 +208,8 @@ 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 
+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
@@ -210,7 +221,8 @@ to gain the best efficiency. Better still is to use a card which supports
 NAPI as well.
 
 
-8) Mailing list
+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
@@ -218,7 +230,8 @@ list that you can join, details are at:
 
 http://sourceforge.net/mail/?group_id=4993
 
-9) Legal Info
+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.
similarity index 91%
rename from Documentation/networking/defza.txt
rename to Documentation/networking/defza.rst
index 663e4a9..73c2f79 100644 (file)
@@ -1,4 +1,10 @@
-Notes on the DEC FDDIcontroller 700 (DEFZA-xx) driver v.1.1.4.
+.. 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
@@ -1,17 +1,21 @@
+.. 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>
 
-----------------------------------------------------------------------------
-
-(0) Introduction
+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
@@ -21,11 +25,11 @@ be (but sometimes are) confused with the similarly-numbered PCI-bus "3c905"
 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)
- 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
@@ -33,32 +37,34 @@ 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
+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:
+for LILO parameters for doing this::
 
-    ether=10,0x310,3,0x3c509,eth0 
+    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:
+option::
 
    options 3c509 irq=10,11
 
 
-(2) Full-duplex mode
+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: 
+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
@@ -78,27 +84,32 @@ 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
+
+.. 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)
@@ -106,6 +117,7 @@ For versions of the driver v1.18c and above, the available transceiver types are
 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
@@ -116,26 +128,30 @@ 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
+
+Interpretation of error messages and common problems
+----------------------------------------------------
 
 Error Messages
+^^^^^^^^^^^^^^
 
-eth0: Infinite loop in interrupt, status 2011. 
+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. 
+     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. 
+     interrupt should always be incrementing faster than the others.
+
+No received packets
+^^^^^^^^^^^^^^^^^^^
 
-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
@@ -146,26 +162,37 @@ 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 
+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. 
+likely have an unterminated network or the incorrect media transceiver selected.
+
+3c509B card is not detected on machines with an ISA PnP BIOS.
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-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. 
+setup program.
+
+3c509 card is not detected on overclocked machines
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-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. 
+to an absurdly high value, such as 5000.
+
 
+Decoding Status and Error Messages
+----------------------------------
 
-(4b) Decoding Status and Error Messages
 
-The bits in the main status register are: 
+The bits in the main status register are:
 
+=====  ======================================
 value  description
+=====  ======================================
 0x01   Interrupt latch
 0x02   Tx overrun, or Rx underrun
 0x04   Tx complete
@@ -174,30 +201,38 @@ value     description
 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: 
+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).
+=====  ============================================
+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 
+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 
+0x82
+^^^^
+
 Out of window collision. This typically occurs when some other Ethernet
-host is incorrectly set to full duplex on a half duplex network. 
+host is incorrectly set to full duplex on a half duplex network.
+
+0x88
+^^^^
 
-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
@@ -207,7 +242,8 @@ 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)
+Revision history (this file)
+============================
 
 28Feb02 v1.0  DR   New; major portions based on Becker original 3c509 docs
 
@@ -1,5 +1,13 @@
-Documentation/networking/device_drivers/3com/vortex.txt
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================
+3Com Vortex device driver
+=========================
+
+Documentation/networking/device_drivers/3com/vortex.rst
+
 Andrew Morton
+
 30 April 2000
 
 
@@ -8,12 +16,12 @@ 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. 
+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>
+- 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.
@@ -24,58 +32,58 @@ Since kernel 2.3.99-pre6, this driver incorporates the support for the
 
 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
+       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:
+its module is loaded.  These are usually placed in ``/etc/modprobe.d/*.conf``
+configuration files.  Example::
 
-options 3c59x debug=3 rx_copybreak=300
+    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:
+placed in /etc/pcmcia/config.opts::
 
-module "3c59x" opts "debug=3 rx_copybreak=300"
+    module "3c59x" opts "debug=3 rx_copybreak=300"
 
 
 The supported parameters are:
@@ -89,7 +97,7 @@ 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:
+  them with option 0x204 you would use::
 
     options=0x204,0x204
 
@@ -97,6 +105,8 @@ options=N1,N2,N3,...
   have the following meanings:
 
   Possible media type settings
+
+       ==      =================================
        0       10baseT
        1       10Mbs AUI
        2       undefined
@@ -108,17 +118,20 @@ options=N1,N2,N3,...
        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:
+  For example::
 
     insmod 3c59x options=0x204
 
@@ -127,14 +140,14 @@ options=N1,N2,N3,...
 
 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
+  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'
+  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
@@ -143,13 +156,13 @@ full_duplex=N1,N2,N3...
 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.
+  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. 
+  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.
@@ -170,14 +183,14 @@ rx_copybreak=M
 
   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 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. 
+  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
@@ -186,7 +199,7 @@ max_interrupt_work=N
 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. 
+  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.
@@ -196,11 +209,11 @@ hw_checksums=N1,N2,N3,...
 
   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'.
+  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'.
+  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.
@@ -210,8 +223,8 @@ hw_checksums=N1,N2,N3,...
   decrease in throughput for send().  There is no effect upon receive
   efficiency.
 
-compaq_ioaddr=N
-compaq_irq=N
+compaq_ioaddr=N,
+compaq_irq=N,
 compaq_device_id=N
 
   "Variables to work-around the Compaq PCI BIOS32 problem"....
@@ -219,7 +232,7 @@ compaq_device_id=N
 watchdog=N
 
   Sets the time duration (in milliseconds) after which the kernel
-  decides that the transmitter has become stuck and needs to be reset. 
+  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).
@@ -227,7 +240,7 @@ watchdog=N
 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
+  Becker's ``ether-wake`` application may be used to wake suspended
   machines.
 
   Also enables the NIC's power management support.
@@ -235,7 +248,7 @@ enable_wol=N1,N2,N3,...
 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.
+  the ``enable_wol`` array above will override any setting of this.
 
 Media selection
 ---------------
@@ -325,12 +338,12 @@ Autonegotiation notes
 
   Cisco switches    (Jeff Busch <jbusch@deja.com>)
 
-    My "standard config" for ports to which PC's/servers connect directly:
+    My "standard config" for ports to which PC's/servers connect directly::
 
-        interface FastEthernet0/N
-        description machinename
-        load-interval 30
-        spanning-tree portfast
+       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").
@@ -368,9 +381,9 @@ steps you should take:
 
   But for most problems it is useful to provide the following:
 
-   o Kernel version, driver version
+   - Kernel version, driver version
 
-   o A copy of the banner message which the driver generates when
+   - 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
@@ -378,68 +391,68 @@ steps you should take:
      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:
+     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?
+   - 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?
 
-   o Any additional module parameters which you may be providing to the driver.
+   - Any additional module parameters which you may be providing to the driver.
 
-   o Any kernel logs which are produced.  The more the merrier. 
+   - 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:
+     following line to /etc/syslog.conf::
 
-         kern.* /var/log/messages
+        kern.* /var/log/messages
 
-     Then restart syslogd with:
+     Then restart syslogd with::
 
-         /etc/rc.d/init.d/syslog restart
+        /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
+    - 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
+        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.
+        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.
+        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.
+        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.
+        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
@@ -1,8 +1,12 @@
-Linux kernel driver for Elastic Network Adapter (ENA) family:
-=============================================================
+.. SPDX-License-Identifier: GPL-2.0
+
+============================================================
+Linux kernel driver for Elastic Network Adapter (ENA) family
+============================================================
+
+Overview
+========
 
-Overview:
-=========
 ENA is a networking interface designed to make good use of modern CPU
 features and system architectures.
 
@@ -35,32 +39,40 @@ 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:
+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
 ===================================
-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.
+
+=================   ======================================================
+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)
@@ -78,6 +90,7 @@ 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
@@ -96,12 +109,16 @@ 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 -
+       ====================    ===============
+       Link state change       **X**
+       Fatal error             **X**
        Notification            Suspend traffic
        Notification            Resume traffic
-       Keep-Alive              - X -
+       Keep-Alive              **X**
+       ====================    ===============
 
 ACQ and AENQ share the same MSI-X vector.
 
@@ -113,8 +130,8 @@ 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:
-====================
+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.
@@ -123,11 +140,15 @@ 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
@@ -142,6 +163,7 @@ Note: Not all ENA devices support LLQ, and this feature is negotiated
 
 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.
@@ -151,8 +173,8 @@ benefits:
   packet is running.
 - In hardware interrupt re-direction.
 
-Interrupt Modes:
-================
+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).
@@ -163,9 +185,12 @@ 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:
+The management interrupt is named::
+
    ena-mgmnt@pci:<PCI domain:bus:slot.function>
-and for each queue pair, an interrupt is named:
+
+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
@@ -173,8 +198,8 @@ 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:
-=====================
+Interrupt Moderation
+====================
 ENA driver and device can operate in conventional or adaptive interrupt
 moderation mode.
 
@@ -202,45 +227,46 @@ 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:
-=============
+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:
-====
+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:
-===========
+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:
-====
+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:
-===================
+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:
-====
+RSS
+===
 - The ENA device supports RSS that allows flexible Rx traffic
   steering.
 - Toeplitz and CRC32 hash functions are supported.
@@ -255,11 +281,13 @@ RSS:
 - The user can provide a hash key, hash function, and configure the
   indirection table through ethtool(8).
 
-DATA PATH:
-==========
-Tx:
----
+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.)
@@ -271,8 +299,10 @@ end_start_xmit() is called by the stack. This function does the following:
 - 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.
@@ -280,14 +310,16 @@ end_start_xmit() is called by the stack. This function does the following:
 - 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:
----
+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
@@ -296,13 +328,17 @@ Rx:
   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().
@@ -1,83 +1,96 @@
-Marvell(Aquantia) AQtion Driver for the aQuantia Multi-Gigabit PCI Express
-Family of Ethernet Adapters
-=============================================================================
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
 
-Contents
-========
+===============================
+Marvell(Aquantia) AQtion Driver
+===============================
 
-- Identifying Your Adapter
-- Configuration
-- Supported ethtool options
-- Command Line Parameters
-- Config file parameters
-- Support
-- License
+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.
+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.
+This release tested with passive Direct Attach Cables (DAC) and SFP+/LC
+Optical Transceiver.
 
 Configuration
-=========================
-  Viewing Link Messages
-  ---------------------
+=============
+
+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:
+  your console, set dmesg to eight by entering the following::
 
        dmesg -n 8
 
-  NOTE: This setting is not saved across reboots.
+  .. note::
 
-  Jumbo Frames
-  ------------
+     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:
+  increase the MTU size.  For example::
 
-        ip link set mtu 16000 dev enp1s0
+       ip link set mtu 16000 dev enp1s0
 
-  ethtool
-  -------
+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
+----
   NAPI (Rx polling mode) is supported in the atlantic driver.
 
 Supported ethtool options
-============================
- Viewing adapter settings
- ---------------------
- ethtool <ethX>
+=========================
+
+Viewing adapter settings
+------------------------
+
+ ::
 
- Output example:
+    ethtool <ethX>
+
+ Output example::
 
   Settings for enp1s0:
     Supported ports: [ TP ]
     Supported link modes:   100baseT/Full
-                            1000baseT/Full
-                            10000baseT/Full
-                            2500baseT/Full
-                            5000baseT/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
+                           1000baseT/Full
+                           10000baseT/Full
+                           2500baseT/Full
+                           5000baseT/Full
     Advertised pause frame use: Symmetric
     Advertised auto-negotiation: Yes
     Advertised FEC modes: Not reported
@@ -92,16 +105,22 @@ Supported ethtool options
     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:
+
+ .. 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>
+Viewing adapter information
+---------------------------
 
- Output example:
+ ::
+
+  ethtool -i <ethX>
+
+ Output example::
 
   driver: atlantic
   version: 5.2.0-050200rc5-generic-kern
@@ -115,12 +134,16 @@ Supported ethtool options
   supports-priv-flags: no
 
 
- Viewing Ethernet adapter statistics:
- ---------------------
- ethtool -S <ethX>
+Viewing Ethernet adapter statistics
+-----------------------------------
+
+ ::
 
- Output example:
- NIC statistics:
+    ethtool -S <ethX>
+
+ Output example::
+
+  NIC statistics:
      InPackets: 13238607
      InUCast: 13293852
      InMCast: 52
@@ -164,85 +187,95 @@ Supported ethtool options
      Queue[3] InLroPackets: 0
      Queue[3] InErrors: 0
 
- Interrupt coalescing support
- ---------------------------------
- ITR mode, TX/RX coalescing timings could be viewed with:
+Interrupt coalescing support
+----------------------------
 
- ethtool -c <ethX>
+ ITR mode, TX/RX coalescing timings could be viewed with::
 
- and changed with:
+    ethtool -c <ethX>
 
- ethtool -C <ethX> tx-usecs <usecs> rx-usecs <usecs>
+ and changed with::
 
- To disable coalescing:
+    ethtool -C <ethX> tx-usecs <usecs> rx-usecs <usecs>
 
- ethtool -C <ethX> tx-usecs 0 rx-usecs 0 tx-max-frames 1 tx-max-frames 1
+ To disable coalescing::
 
- Wake on LAN support
- ---------------------------------
+    ethtool -C <ethX> tx-usecs 0 rx-usecs 0 tx-max-frames 1 tx-max-frames 1
 
- WOL support by magic packet:
+Wake on LAN support
+-------------------
 
- ethtool -s <ethX> wol g
+ WOL support by magic packet::
 
- To disable WOL:
+    ethtool -s <ethX> wol g
 
- ethtool -s <ethX> wol d
+ To disable WOL::
 
- Set and check the driver message level
- ---------------------------------
+    ethtool -s <ethX> wol d
+
+Set and check the driver message level
+--------------------------------------
 
  Set message level
 
- ethtool -s <ethX> msglvl <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.
+ ======   =============================
+ 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 <ethX> | grep "Current message level"
 
- ethtool -s <ethX> msglvl 0
+ If you want to disable the output of messages::
+
+    ethtool -s <ethX> msglvl 0
+
+RX flow rules (ntuple filters)
+------------------------------
 
- 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>".
+ via ``ethtool -N <device> <filter>``.
 
- To enable or disable the RX flow rules:
+ To enable or disable the RX flow rules::
 
- ethtool -K ethX ntuple <on|off>
   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)
@@ -253,32 +286,34 @@ Supported ethtool options
  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:
+ 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>
   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
+ 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 ``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:
+ 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
   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:
+ 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
   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.
 
 
@@ -287,8 +322,9 @@ Supported ethtool options
  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
+ 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
@@ -297,17 +333,17 @@ Supported ethtool options
  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 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:
+ To see the list of filters currently present::
 
- ethtool <-u|-n|--show-nfc|--show-ntuple> <ethX>
   ethtool <-u|-n|--show-nfc|--show-ntuple> <ethX>
 
- Rules may be deleted from the table itself. This is done using:
+ Rules may be deleted from the table itself. This is done using::
 
- sudo ethtool <-N|-U|--config-nfc|--config-ntuple> <ethX> delete <loc>
   sudo ethtool <-N|-U|--config-nfc|--config-ntuple> <ethX> delete <loc>
 
  - loc is the rule number to be deleted.
 
@@ -316,34 +352,37 @@ Supported ethtool options
  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
- ---------------------------------
+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
+ Example::
+
+    ethtool -N eth0 flow-type udp4 action 0 loc 32
+
+UDP GSO hardware offload
+------------------------
 
- 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/
+ 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:
+ UDP GSO is configured by::
 
     ethtool -K eth0 tx-udp-segmentation on
 
- Private flags (testing)
- ---------------------------------
+Private flags (testing)
+-----------------------
 
- Atlantic driver supports private flags for hardware custom features:
+ Atlantic driver supports private flags for hardware custom features::
 
        $ ethtool --show-priv-flags ethX
 
@@ -354,7 +393,7 @@ Supported ethtool options
        PHYInternalLoopback: off
        PHYExternalLoopback: off
 
- Example:
+ Example::
 
        $ ethtool --set-priv-flags ethX DMASystemLoopback on
 
@@ -370,93 +409,130 @@ 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.
+
+======   ==============================================================
+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)
+.. 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.
+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
+
+==  ========
+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.
+
+The system consumes less energy but it also introduces more latency in packets
+processing.
 
 Valid values
-0 - disabled
-1 - enabled
+
+==  ========
+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.
@@ -472,7 +548,8 @@ License
 =======
 
 aQuantia Corporation Network Driver
-Copyright(c) 2014 - 2019 aQuantia Corporation.
+
+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,
@@ -1,13 +1,18 @@
-                 Chelsio N210 10Gb Ethernet Network Controller
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
 
-                         Driver Release Notes for Linux
+=============================================
+Chelsio N210 10Gb Ethernet Network Controller
+=============================================
 
-                                 Version 2.1.1
+Driver Release Notes for Linux
 
-                                 June 20, 2005
+Version 2.1.1
+
+June 20, 2005
+
+.. Contents
 
-CONTENTS
-========
  INTRODUCTION
  FEATURES
  PERFORMANCE
@@ -16,7 +21,7 @@ CONTENTS
  SUPPORT
 
 
-INTRODUCTION
+Introduction
 ============
 
  This document describes the Linux driver for Chelsio 10Gb Ethernet Network
@@ -24,11 +29,11 @@ INTRODUCTION
  compatible with the Chelsio N110 model 10Gb NICs.
 
 
-FEATURES
+Features
 ========
 
- Adaptive Interrupts (adaptive-rx)
- ---------------------------------
+Adaptive Interrupts (adaptive-rx)
+---------------------------------
 
   This feature provides an adaptive algorithm that adjusts the interrupt
   coalescing parameters, allowing the driver to dynamically adapt the latency
@@ -39,24 +44,24 @@ FEATURES
   ethtool manpage for additional usage information.
 
   By default, adaptive-rx is disabled.
-  To enable adaptive-rx:
+  To enable adaptive-rx::
 
       ethtool -C <interface> adaptive-rx on
 
-  To disable adaptive-rx, use ethtool:
+  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:
+  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:
+  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:
+  You may also provide a timer latency value while disabling adaptive-rx::
 
       ethtool -C <interface> adaptive-rx off rx-usecs <microseconds>
 
@@ -64,13 +69,13 @@ FEATURES
   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:
+  To view the status of the adaptive-rx and timer latency values::
 
       ethtool -c <interface>
 
 
- TCP Segmentation Offloading (TSO) Support
- -----------------------------------------
+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
@@ -80,20 +85,20 @@ FEATURES
   Please see the ethtool manpage for additional usage information.
 
   By default, TSO is enabled.
-  To disable TSO:
+  To disable TSO::
 
       ethtool -K <interface> tso off
 
-  To enable TSO:
+  To enable TSO::
 
       ethtool -K <interface> tso on
 
-  To view the status of TSO:
+  To view the status of TSO::
 
       ethtool -k <interface>
 
 
-PERFORMANCE
+Performance
 ===========
 
  The following information is provided as an example of how to change system
@@ -111,59 +116,81 @@ PERFORMANCE
  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
+  Setting PCI Latency Timer::
+
+      setpci -d 1425::
+
+* 0x0c.l=0x0000F800
+
+  Disabling TCP timestamp::
 
-  Disabling TCP timestamp:
       sysctl -w net.ipv4.tcp_timestamps=0
 
-  Disabling SACK:
+  Disabling SACK::
+
       sysctl -w net.ipv4.tcp_sack=0
 
-  Setting large number of incoming connection requests:
+  Setting large number of incoming connection requests::
+
       sysctl -w net.ipv4.tcp_max_syn_backlog=3000
 
-  Setting maximum receive socket buffer size:
+  Setting maximum receive socket buffer size::
+
       sysctl -w net.core.rmem_max=1024000
 
-  Setting maximum send socket buffer size:
+  Setting maximum send socket buffer size::
+
       sysctl -w net.core.wmem_max=1024000
 
-  Set smp_affinity (on a multiprocessor system) to a single CPU:
+  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:
+  Setting default receive socket buffer size::
+
       sysctl -w net.core.rmem_default=524287
 
-  Setting default send socket buffer size:
+  Setting default send socket buffer size::
+
       sysctl -w net.core.wmem_default=524287
 
-  Setting maximum option memory buffers:
+  Setting maximum option memory buffers::
+
       sysctl -w net.core.optmem_max=524287
 
-  Setting maximum backlog (# of unprocessed packets before kernel drops):
+  Setting maximum backlog (# of unprocessed packets before kernel drops)::
+
       sysctl -w net.core.netdev_max_backlog=300000
 
-  Setting TCP read buffers (min/default/max):
+  Setting TCP read buffers (min/default/max)::
+
       sysctl -w net.ipv4.tcp_rmem="10000000 10000000 10000000"
 
-  Setting TCP write buffers (min/pressure/max):
+  Setting TCP write buffers (min/pressure/max)::
+
       sysctl -w net.ipv4.tcp_wmem="10000000 10000000 10000000"
 
-  Setting TCP buffer space (min/pressure/max):
+  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:
+
+   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:
+
+   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:
@@ -174,30 +201,35 @@ PERFORMANCE
    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
+Driver Messages
 ===============
 
  The following messages are the most common messages logged by syslog. These
  may be found in /var/log/messages.
 
-  Driver up:
+  Driver up::
+
      Chelsio Network Driver - version 2.1.1
 
-  NIC detected:
+  NIC detected::
+
      eth#: Chelsio N210 1x10GBaseX NIC (rev #), PCIX 133MHz/64-bit
 
-  Link up:
+  Link up::
+
      eth#: link is up at 10 Gbps, full duplex
 
-  Link down:
+  Link down::
+
      eth#: link is down
 
 
-KNOWN ISSUES
+Known Issues
 ============
 
  These issues have been identified during testing. The following information
@@ -214,27 +246,33 @@ KNOWN ISSUES
 
       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
+      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
+      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
+      this feature, add the following line to your bootloader::
+
+         noirqbalance
+
+         Example using the Grub bootloader::
 
-          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
+             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.
@@ -277,12 +315,13 @@ KNOWN ISSUES
       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:
+       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
+          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
@@ -293,8 +332,10 @@ KNOWN ISSUES
       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.
+      .. 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
@@ -320,14 +361,16 @@ KNOWN ISSUES
       particular CPU: runon 0 ifup eth0
 
 
-SUPPORT
+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.
@@ -343,10 +386,8 @@ 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
+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.
-
-===============================================================================
+Copyright |copy| 2003-2005 Chelsio Communications. All rights reserved.
@@ -1,79 +1,84 @@
+.. SPDX-License-Identifier: GPL-2.0
 
-NOTE
-----
+================================================
+Cirrus Logic LAN CS8900/CS8920 Ethernet Adapters
+================================================
 
-This document was contributed by Cirrus Logic for kernel 2.2.5.  This version
-has been updated for 2.3.48 by Andrew Morton.
+.. 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.
 
 
-
-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 
+
+
+.. 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
+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
@@ -85,22 +90,25 @@ or loaded at run-time as a device driver module.
 
 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.
+  ===================  ====================================================
+  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
+1.3. System Requirements
+------------------------
 
 The following hardware is required:
 
-   * Cirrus Logic LAN (CS8900/20-based) Ethernet ISA Adapter   
+   * Cirrus Logic LAN (CS8900/20-based) Ethernet ISA Adapter
 
    * IBM or IBM-compatible PC with:
      * An 80386 or higher processor
@@ -118,20 +126,21 @@ The following software is required:
 
    * 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)   
+   * GNU Toolkit (gcc and make) v2.6 or above (if compiling into kernel
+     or a module)
 
 
 
-1.4 LICENSING INFORMATION
+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 
+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
@@ -139,28 +148,29 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 
 
-2.0 ADAPTER INSTALLATION and CONFIGURATION
-===============================================================================
+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.  
+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 
+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 
+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:
+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
@@ -169,15 +179,16 @@ with the following "default" settings:
   Optimization:               DOS Client
   Transmission Mode:   Half-duplex
   BootProm:            None
-  Media Type:         Autodetect (3-media cards) or 
-                       10BASE-T (10BASE-T only adapter)
+  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. 
+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
+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
@@ -185,82 +196,83 @@ 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.  
+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.                                     *
-        ****************************************************************
+       ****************************************************************
+       *                    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
-===============================================================================
+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 
+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:
+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
+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
+  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)
+  debug=#              - debug level (only available if the driver was compiled
+                        for debugging)
 
-NOTES:
+**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.  
+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
+   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.  
+   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 
+   io
+   irq
    media type (no autodetect)
 
 f) The following additional parameters are CS89XX defaults (values
@@ -282,13 +294,13 @@ h) Many Linux distributions use the 'modprobe' command to load
    module when it is loaded.  All the configuration options which are
    described above may be placed within /etc/conf.modules.
 
-   For example:
+   For example::
 
-   > cat /etc/conf.modules
-   ...
-   alias eth0 cs89x0
-   options cs89x0 io=0x0200 dma=5 use_dma=1
-   ...
+     > 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
@@ -305,9 +317,9 @@ j) The cs89x0 supports DMA for receiving only.  DMA mode is
 
 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
+   with the command::
 
-   cat /proc/isapnp
+     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.
@@ -321,11 +333,11 @@ 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:
+   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:
+   or they may be placed in /etc/lilo.conf::
 
        image=/boot/bzImage-2.3.48
          append="cs89x0_dma=5"
@@ -337,237 +349,246 @@ n) If the cs89x0 driver is compiled directly into the kernel, DMA
    (64k mode is not available).
 
 
-4.0 COMPILING THE DRIVER
-===============================================================================
+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 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
+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.0 TESTING AND TROUBLESHOOTING
-===============================================================================
+5. Testing and Troubleshooting
+==============================
 
-5.1 KNOWN DEFECTS and LIMITATIONS
+5.1. Known Defects and Limitations
+----------------------------------
 
-Refer to the RELEASE.TXT file distributed as part of this archive for a list of 
+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
+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 
+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 
+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.
+.. 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.
+   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
+   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.  
+   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
+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 
+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 
+
+     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 
+
+     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 
+
+     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 
+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
+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.) 
+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 
+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 
+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.
+    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. 
+    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.
 
-    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 
+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.  
+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 
+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 
+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
+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 
+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).
+    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.
+    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.
+    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.
+    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 
+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 
+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 
+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
+::
 
+  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
+6. Technical Support
+====================
 
-Cirrus Logic's CS89XX Technical Application Support can be reached at:
+6.1. Contacting Cirrus Logic's Technical Support
+------------------------------------------------
 
-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
+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. 
+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.)
 
@@ -575,7 +596,7 @@ Much of the following information as possible.
 
     * 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).    
+    * Configured for media auto-detect or specific media type (which type).
 
 3.) PC System's Configuration
 
@@ -590,35 +611,37 @@ Much of the following information as possible.
 
     * CS89XX driver and version
     * Your network operating system and version
-    * Your system's OS version 
+    * Your system's OS version
     * Version of all protocol support files
 
 5.) Any Error Message displayed.
 
 
 
-6.3 OBTAINING THE LATEST DRIVER VERSION
+6.3 Obtaining the Latest Driver Version
+---------------------------------------
 
-You can obtain the latest CS89XX drivers and support software from Cirrus Logic's 
+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 
+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
+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:
+parameter ``cs89x0_media=`` has been implemented.  Usage is::
 
        cs89x0_media=rj45    or
        cs89x0_media=aui     or
        cs89x0_media=bnc
-
@@ -1,7 +1,11 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================
 DM9000 Network driver
 =====================
 
 Copyright 2008 Simtec Electronics,
+
          Ben Dooks <ben@simtec.co.uk> <ben-linux@fluff.org>
 
 
@@ -30,9 +34,9 @@ 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:
+An example from arch/arm/mach-s3c2410/mach-bast.c is::
 
-static struct resource bast_dm9k_resource[] = {
+  static struct resource bast_dm9k_resource[] = {
        [0] = {
                .start = S3C2410_CS5 + BAST_PA_DM9000,
                .end   = S3C2410_CS5 + BAST_PA_DM9000 + 3,
@@ -48,14 +52,14 @@ static struct resource bast_dm9k_resource[] = {
                .end   = IRQ_DM9000,
                .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
        }
-};
+  };
 
-static struct platform_device bast_device_dm9k = {
+  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
@@ -64,13 +68,13 @@ 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:
+the optional platform data to pass extra configuration data::
 
-static struct dm9000_plat_data bast_dm9k_platdata = {
+  static struct dm9000_plat_data bast_dm9k_platdata = {
        .flags          = DM9000_PLATF_16BITONLY,
-};
+  };
 
-static struct platform_device bast_device_dm9k = {
+  static struct platform_device bast_device_dm9k = {
        .name           = "dm9000",
        .id             = 0,
        .num_resources  = ARRAY_SIZE(bast_dm9k_resource),
@@ -78,7 +82,7 @@ static struct platform_device bast_device_dm9k = {
        .dev            = {
                .platform_data = &bast_dm9k_platdata,
        }
-};
+  };
 
 The platform data is defined in include/linux/dm9000.h and described below.
 
@@ -1,48 +1,54 @@
+.. 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
+        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 
+        - 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!) 
+        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:
+    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
+                 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
 
     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.
+       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:
+       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   
+                  insmod de4x5 io=0xghh           where g = bus number
+                                                       hh = device number
 
-       NB: autoprobing for modules is now supported by default. You may just
-           use:
+       .. note::
 
-                   insmod de4x5
+          autoprobing for modules is now supported by default. You may just
+          use::
 
-           to load all available boards. For a specific board, still 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).
+       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.
+       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). 
+    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) 
+    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
@@ -90,7 +98,7 @@
     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. 
+    '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
     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:
+           =========  ===============================================
+           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:
+    upper case. Examples::
+
+       insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
 
-        insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
+    For a compiled in driver, in linux/drivers/net/CONFIG, place e.g.::
 
-    For a compiled in driver, in linux/drivers/net/CONFIG, place e.g.
-       DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' 
+       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
@@ -1,6 +1,11 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================================================
+Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux
+==============================================================
+
 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
@@ -16,29 +21,29 @@ 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 :
+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 :
+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 :
+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 :
+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 :
+Then you may have to modify the default routing table with command::
 
        route add default eth0
 
@@ -48,10 +53,10 @@ 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.
+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:
@@ -60,7 +65,7 @@ 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>
+Marcelo Tosatti <marcelo@conectiva.com.br>
+Alan Cox <alan@lxorguk.ukuu.org.uk>
+Jeff Garzik <jgarzik@pobox.com>
+Vojtech Pavlik <vojtech@suse.cz>
@@ -1,10 +1,13 @@
+.. SPDX-License-Identifier: GPL-2.0
 
-    D-Link DL2000-based Gigabit Ethernet Adapter Installation
-    for Linux
-    May 23, 2002
+=========================================================
+D-Link DL2000-based Gigabit Ethernet Adapter Installation
+=========================================================
+
+May 23, 2002
+
+.. Contents
 
-Contents
-========
  - Compatibility List
  - Quick Install
  - Compiling the Driver
@@ -15,12 +18,13 @@ Contents
 
 
 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.
+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
@@ -34,28 +38,32 @@ on the environments below.
 
 Quick Install
 =============
-Install linux driver as following command:
+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
 
-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:
+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
+
+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.
+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.
@@ -73,93 +81,108 @@ 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
+::
+
+    [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
+::
+
+    [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
-  -------------------
+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:
+  module enter the command::
+
+    insmod dl2k.o
+
+  or::
+
+    insmod dl2k.o <optional parameter> ; add parameter
 
-  insmod dl2k.o
+---------------------------------------------------------
 
-  or
+  example::
 
-  insmod dl2k.o <optional parameter>   ; add parameter
+    insmod dl2k.o media=100mbps_hd
 
-  ===============================================================
-   example: insmod dl2k.o media=100mbps_hd
-   or      insmod dl2k.o media=3
-   or      insmod dl2k.o media=3,2     ; for 2 cards
-  ===============================================================
+   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:
+  it is necessary to issue the following command::
 
-  ifconfig eth0 up
+    ifconfig eth0 up
 
   Finally, to bind the driver to the active protocol (e.g., TCP/IP with
-  Linux), enter the following command:
+  Linux), enter the following command::
 
-  ifup eth0
+    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:
+  The commands to unload a driver are as follows::
 
-  ifdown eth0
-  ifconfig eth0 down
-  rmmod dl2k.o
+    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.
+  to see the current network configuration::
 
-  lsmod
-  ifconfig
+    lsmod
+    ifconfig
 
 
-  Automated Installation
-  ----------------------
+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
-  -----------------
+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:
+     /etc/modprobe.d/ directory. Add the following lines::
 
-     alias ethx dl2k
-     options dl2k <optional parameters>
+       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
@@ -180,11 +203,15 @@ 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
+
+===============================   ==============================================
+mtu=packet_size                          Specifies the maximum packet size. default
                                  is 1500.
 
-media=media_type               - Specifies the media type the NIC operates at.
+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.
@@ -198,85 +225,90 @@ media=media_type          - Specifies the media type the NIC operates at.
                                  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
+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,
+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 
+                                 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 
+
+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 
+                                 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. 
+                                 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 
+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, 
+
+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, 
+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:
+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
+    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.
+
+    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
+    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
+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,
+
+    Note that RH 7.0 didn't provide correct header files in /usr/include,
     including those files will make a wrong version driver.
 
@@ -1,12 +1,14 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================
 The QorIQ DPAA Ethernet Driver
 ==============================
 
 Authors:
-Madalin Bucur <madalin.bucur@nxp.com>
-Camelia Groza <camelia.groza@nxp.com>
+Madalin Bucur <madalin.bucur@nxp.com>
+Camelia Groza <camelia.groza@nxp.com>
 
-Contents
-========
+.. Contents
 
        - DPAA Ethernet Overview
        - DPAA Ethernet Supported SoCs
@@ -34,7 +36,7 @@ following drivers in the Linux kernel:
  - Queue Manager (QMan), Buffer Manager (BMan)
     drivers/soc/fsl/qbman
 
-A simplified view of the dpaa_eth interfaces mapped to FMan MACs:
+A simplified view of the dpaa_eth interfaces mapped to FMan MACs::
 
   dpaa_eth       /eth0\     ...       /ethN\
   driver        |      |             |      |
@@ -42,89 +44,93 @@ A simplified view of the dpaa_eth interfaces mapped to FMan MACs:
        -Ports  / Tx  Rx \    ...    / Tx  Rx \
   FMan        |          |         |          |
        -MACs  |   MAC0   |         |   MACN   |
-             /   dtsec0   \  ...  /   dtsecN   \ (or tgec)
-            /              \     /              \(or memac)
+            /   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:
-              ________________________________
+The dpaa_eth relation to the QMan, BMan and FMan::
+
+             ________________________________
   dpaa_eth   /            eth0                \
   driver    /                                  \
   ---------   -^-   -^-   -^-   ---    ---------
   QMan driver / \   / \   / \  \   /  | BMan    |
-             |Rx | |Rx | |Tx | |Tx |  | driver  |
+            |Rx | |Rx | |Tx | |Tx |  | driver  |
   ---------  |Dfl| |Err| |Cnf| |FQs|  |         |
   QMan HW    |FQ | |FQ | |FQs| |   |  |         |
-             /   \ /   \ /   \  \ /   |         |
+            /   \ /   \ /   \  \ /   |         |
   ---------   ---   ---   ---   -v-    ---------
-            |        FMan QMI         |         |
-            | FMan HW       FMan BMI  | BMan HW |
-              -----------------------   --------
+           |        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           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
+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:
+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
+  # 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
+  # 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
+  # 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
 ==============================
@@ -167,7 +173,9 @@ classes as follows:
        * 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: \
+::
+
+  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
@@ -201,11 +209,11 @@ 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.
+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:
+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
 
@@ -216,7 +224,7 @@ 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.:
+on or off through ethtool, i.e.::
 
        # ethtool -K fm1-mac9 rx-hashing off
        # ethtool -k fm1-mac9 | grep hash
@@ -246,6 +254,7 @@ The following statistics are exported for each interface through ethtool:
        - 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
@@ -254,7 +263,7 @@ The following statistics are exported for each interface through ethtool:
 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
+         /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
+         /sys/devices/platform/soc/<addr>.fman/<addr>.ethernet/dpaa-ethernet.<id>/net/fm<nr>-mac<nr>/bpids
@@ -1,10 +1,15 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================
 The Gianfar Ethernet Driver
+===========================
 
-Author: Andy Fleming <afleming@freescale.com>
-Updated: 2005-07-28
+:Author: Andy Fleming <afleming@freescale.com>
+:Updated: 2005-07-28
 
 
-CHECKSUM OFFLOADING
+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
@@ -15,13 +20,15 @@ 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
+Multicasting
+============
 
 The gianfar driver supports using the group hash table on the
 TSEC (and the extended hash table on the eTSEC) for multicast
@@ -29,13 +36,15 @@ 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
+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
+Ethtool
+=======
 
 The gianfar driver supports the use of ethtool for many
 configuration options.  You must run ethtool only on currently
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
@@ -1,31 +1,37 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
 
-Intel(R) PRO/Wireless 2100 Driver for Linux in support of:
+===========================================
+Intel(R) PRO/Wireless 2100 Driver for Linux
+===========================================
 
-Intel(R) PRO/Wireless 2100 Network Connection
+Support for:
 
-Copyright (C) 2003-2006, Intel Corporation
+- Intel(R) PRO/Wireless 2100 Network Connection
+
+Copyright |copy| 2003-2006, Intel Corporation
 
 README.ipw2100
 
-Version: git-1.1.5
-Date   : January 25, 2006
+: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
+.. 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
------------------------------------------------
+0. IMPORTANT INFORMATION BEFORE USING THIS DRIVER
+=================================================
 
 Important Notice FOR ALL USERS OR DISTRIBUTORS!!!!
 
@@ -75,10 +81,10 @@ 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 
+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
@@ -89,10 +95,11 @@ 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 
+- Wireless Tools support
 - 802.1x (tested with XSupplicant 1.0.1)
 
 Enabled (but not supported) features:
@@ -105,11 +112,11 @@ 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:
+syntax::
 
        modprobe ipw2100 [<option>=<VAL1><,VAL2>...]
 
@@ -119,61 +126,76 @@ For example, to disable the radio on driver loading, enter:
 
 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 */
+=========      ==============  ============  ==============================
+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 
+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 ------
+**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'
+  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.
+       .. note::
+
+             'debug_level' is only enabled if CONFIG_IPW2100_DEBUG is turn on.
+
+**Device Level**
+
+For the device level files look in::
 
------ Device Level ------
-For the device level files look in
-       
        /sys/bus/pci/drivers/ipw2100/{PCI-ID}/
 
-For example:
+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
+       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
+       .. 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
@@ -186,9 +208,10 @@ 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 
+===================
+
+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>.
@@ -197,52 +220,57 @@ 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 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 
+       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`)
+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, 
+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 
+       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 
+       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 
+       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::
 
-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 
+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
@@ -250,44 +278,46 @@ 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:  
+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:  
+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.
+  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 
+  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 
+
+  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 
+  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
 
@@ -1,8 +1,15 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
 
-Intel(R) PRO/Wireless 2915ABG Driver for Linux in support of:
+==============================================
+Intel(R) PRO/Wireless 2915ABG Driver for Linux
+==============================================
 
-Intel(R) PRO/Wireless 2200BG Network Connection
-Intel(R) PRO/Wireless 2915ABG Network Connection
+
+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
@@ -10,37 +17,37 @@ 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
+Copyright |copy| 2004-2006, Intel Corporation
 
 README.ipw2200
 
-Version: 1.1.2
-Date   : March 30, 2006
+: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
+.. 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!!!! 
+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
@@ -56,7 +63,7 @@ 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. 
+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
@@ -70,7 +77,7 @@ 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.  
+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
@@ -78,23 +85,24 @@ 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 
+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 
+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 
+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
@@ -102,14 +110,14 @@ 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 
++ Wireless Extension support
 + Full B and G rate support (2200 and 2915)
 + Full A rate support (2915 only)
 + Transmit power control
@@ -122,102 +130,107 @@ supported:
 + long/short preamble support
 + Monitor mode (aka RFMon)
 
-The distinction between officially supported and enabled is a reflection 
+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. 
+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.  
+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:
+The general form is::
 
-% modprobe ipw2200 parameter=value
+    % 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 
+       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.  
+       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
+       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 
+       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.  
+       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 
+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:
+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 
+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 
+       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.  
+       Can be used to configure which IEEE mode the driver will
+       support.
+
+       Usage::
+
+           % iwpriv eth1 set_mode {mode}
 
-       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 
+       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.
@@ -225,99 +238,123 @@ The supported private methods are:
   set_preamble
        Can be used to set the configuration of preamble length:
 
-       Usage:
-       % iwpriv eth1 set_preamble {mode}
+       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:
------------------------------------------------
+1.4. Sysfs Helper Files
+-----------------------
 
-The Linux kernel provides a pseudo file system that can be used to 
+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:
+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
+    % cat /sys/bus/pci/drivers/ipw2200/debug_level
 
-Will report the current debug level of the driver's logging subsystem 
+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:
+You can set the debug level via::
 
-% echo $VALUE > /sys/bus/pci/drivers/ipw2200/debug_level
+    % 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 
+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 
+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  
-       
+  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::
 
-For the device level files, look in
-       
        /sys/bus/pci/drivers/ipw2200/{PCI-ID}/
 
-For example:
+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)
+       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 
+       ==  ==================================================
+       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
+
+       ==  =================
+       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.
+       ==  ================
+       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
@@ -326,44 +363,59 @@ 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 
+       +------+----------------------------+--------------------+
+       |      |                            | 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 
+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 
+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.
 
@@ -371,21 +423,21 @@ For Example:
 iwconfig eth1 mode ad-hoc essid testing channel 2
 
 2.3. Merging Ad-Hoc Networks
------------------------------------------------
+----------------------------
 
 
-3.  Interaction with Wireless Tools
------------------------------------------------
+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
@@ -394,35 +446,35 @@ 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
------------------------------------------------
+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 
+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: 
+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 
+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 
+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 
+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
-----------------------------------------------
+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
@@ -433,40 +485,42 @@ The firmware can be downloaded from the following URL:
     http://ipw2200.sf.net/
 
 
-6.  Support
------------------------------------------------
+6. Support
+==========
 
-For direct support of the 1.0.0 version, you can contact 
+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
------------------------------------------------
+7. License
+==========
 
-  Copyright(c) 2003 - 2006 Intel Corporation. All rights reserved.
+  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 
+  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 
+
+  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 
+  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
@@ -1,3 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================
 Hyper-V network driver
 ======================
 
@@ -10,15 +13,15 @@ Windows 10.
 Features
 ========
 
-  Checksum offload
-  ----------------
+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
-  --------------------
+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.
@@ -32,30 +35,37 @@ Features
   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
-  --------------------------------
+
+  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)
-  -------------------------------------------------------------
+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:
+  later. It may be changed by ethtool command::
+
        ethtool -K eth0 lro on
        ethtool -K eth0 lro off
 
-  SR-IOV support
-  --------------
+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
@@ -70,8 +80,8 @@ Features
   flow direction is desired, these should be applied directly to the
   VF slave device.
 
-  Receive Buffer
-  --------------
+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
@@ -83,8 +93,8 @@ Features
   will use slower method to handle very large packets or if the send buffer
   area is exhausted.
 
-  XDP support
-  -----------
+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
@@ -99,7 +109,8 @@ Features
   overwritten by setting of synthetic NIC.
 
   XDP program cannot run with LRO (RSC) enabled, so you need to disable LRO
-  before running XDP:
+  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.)
@@ -1,24 +1,30 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================================================================
 Neterion's (Formerly S2io) X3100 Series 10GbE PCIe Server Adapter Linux driver
 ==============================================================================
 
-Contents
---------
+.. Contents
+
+  1) Introduction
+  2) Features supported
+  3) Configurable driver parameters
+  4) Troubleshooting
 
-1) Introduction
-2) Features supported
-3) Configurable driver parameters
-4) Troubleshooting
+1. Introduction
+===============
 
-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
+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
@@ -26,41 +32,49 @@ supported independently on each function.
 
 (See below for a complete list of features supported for both IPv4 and IPv6)
 
-2) Features supported:
-----------------------
+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
+
+       - 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).
 
@@ -69,25 +83,33 @@ x)   Multiple hardware queues: (Enabled by default)
 
 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
+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
@@ -1,4 +1,11 @@
+.. 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
@@ -18,17 +25,18 @@ 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 -
+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
+  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
@@ -44,24 +52,27 @@ 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)
+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
+    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 -
+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
 
@@ -71,9 +82,11 @@ 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.
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
@@ -1,30 +1,44 @@
-* Texas Instruments CPSW switchdev based ethernet driver 2.0
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================================
+Texas Instruments CPSW switchdev based ethernet driver
+======================================================
+
+:Version: 2.0
+
+Port renaming
+=============
 
-- 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}"
+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
+=============
 
-====================
-# 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:
+  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.
+   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
 
@@ -38,22 +52,25 @@ but without enabling "switch" mode, or to different bridges.
                cmode runtime value false
 
 Devlink configuration parameters
-====================
+================================
+
 See Documentation/networking/devlink/ti-cpsw-switch.rst
 
-====================
-# Bridging in dual mac mode
-====================
+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.
+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 -
+
+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
@@ -61,11 +78,12 @@ configured in vlan unaware mode or default_pvid has to be adjusted.
        ip link set dev sw0p1 master br0
        ip link set dev sw0p2 master br0
 
-====================
-# Enabling "switch"
-====================
+Enabling "switch"
+=================
+
 The Switch mode can be enabled by configuring devlink driver parameter
-"switch_mode" to 1/true:
+"switch_mode" to 1/true::
+
        devlink dev param set platform/48484000.switch \
        name switch_mode value 1 cmode runtime
 
@@ -79,9 +97,11 @@ marking packets with offload_fwd_mark flag unless "ale_bypass=0"
 
 All configuration is implemented via switchdev API.
 
-====================
-# Bridge setup
-====================
+Bridge setup
+============
+
+::
+
        devlink dev param set platform/48484000.switch \
        name switch_mode value 1 cmode runtime
 
@@ -91,56 +111,65 @@ All configuration is implemented via switchdev API.
        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
+       [*] 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.
+
+
+On/off STP
+==========
 
-Note. Steps [*] are mandatory.
+::
 
-====================
-# VLAN configuration
-====================
-bridge vlan add dev br0 vid 1 pvid untagged self <---- add cpu port to VLAN 1
+       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
+Add extra VLANs
+===============
 
- 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
+ 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
+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
+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
@@ -148,9 +177,11 @@ 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
 
@@ -158,52 +189,54 @@ Access and Trunk port
  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).
+Note. Setting PVID on Bridge device itself working only for
+default VLAN (default_pvid).
+
+NFS
+===
 
-=====================
- 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
+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
@@ -1,20 +1,33 @@
+.. 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
 
 
-TLAN driver for Linux, version 1.14a
-README
 
 
-I.  Supported Devices.
+
+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
@@ -25,13 +38,14 @@ I.  Supported Devices.
     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       0012            Olicom OC-2325
     108d       0013            Olicom OC-2183
-    108d       0014            Olicom OC-2326  
+    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.
@@ -41,21 +55,25 @@ I.  Supported Devices.
 
     The "Netelligent 10 T/2 PCI UTP/Coax" (b012) device is untested,
     but I do not expect any problems.
-    
 
-II.   Driver Options
+
+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
+          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
+          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.)
@@ -70,41 +88,45 @@ II.   Driver Options
 
        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 
+          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:
+          example::
 
-          ether=0,0,0x1,0x7,eth0
+               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
+               ====   ===============
+               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. 
+          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
+          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.
+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.
@@ -113,5 +135,6 @@ III.  Things to try if you have problems.
 
 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
 
@@ -1,6 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0
 
-            The Spidernet Device Driver
-            ===========================
+===========================
+The Spidernet Device Driver
+===========================
 
 Written by Linas Vepstas <linas@austin.ibm.com>
 
@@ -78,15 +80,15 @@ 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
+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
+    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.
@@ -101,11 +103,11 @@ 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
+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
+    "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
@@ -137,19 +139,19 @@ 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
+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
@@ -198,7 +200,3 @@ 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
similarity index 89%
rename from Documentation/networking/dns_resolver.txt
rename to Documentation/networking/dns_resolver.rst
index eaa8f9a..add4d59 100644 (file)
@@ -1,8 +1,10 @@
-                            ===================
-                            DNS Resolver Module
-                            ===================
+.. SPDX-License-Identifier: GPL-2.0
 
-Contents:
+===================
+DNS Resolver Module
+===================
+
+.. Contents:
 
  - Overview.
  - Compilation.
@@ -12,8 +14,7 @@ Contents:
  - Debugging.
 
 
-========
-OVERVIEW
+Overview
 ========
 
 The DNS resolver module provides a way for kernel services to make DNS queries
@@ -33,50 +34,50 @@ It does not yet support the following AFS features:
 This code is extracted from the CIFS filesystem.
 
 
-===========
-COMPILATION
+Compilation
 ===========
 
-The module should be enabled by turning on the kernel configuration options:
+The module should be enabled by turning on the kernel configuration options::
 
        CONFIG_DNS_RESOLVER     - tristate "DNS Resolver support"
 
 
-==========
-SETTING UP
+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:
+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.
+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
+Usage
 =====
 
 To make use of this facility, one of the following functions that are
-implemented in the module can be called after doing:
+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);
+     ::
+
+       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:
+     form::
 
                [<type>:]<name>
 
@@ -107,16 +108,14 @@ 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
+Reading DNS Keys from Userspace
 ===============================
 
 Keys of dns_resolver type can be read from userspace using keyctl_read() or
 "keyctl read/print/pipe".
 
 
-=========
-MECHANISM
+Mechanism
 =========
 
 The dnsresolver module registers a key type called "dns_resolver".  Keys of
@@ -147,11 +146,10 @@ See <file:Documentation/security/keys/request-key.rst> for further
 information about request-key function.
 
 
-=========
-DEBUGGING
+Debugging
 =========
 
 Debugging messages can be turned on dynamically by writing a 1 into the
-following file:
+following file::
 
-        /sys/module/dnsresolver/parameters/debug
+       /sys/module/dnsresolver/parameters/debug
similarity index 85%
rename from Documentation/networking/driver.txt
rename to Documentation/networking/driver.rst
index da59e28..c8f59db 100644 (file)
@@ -1,4 +1,8 @@
-Document about softnet driver issues
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================
+Softnet Driver Issues
+=====================
 
 Transmit path guidelines:
 
@@ -8,7 +12,7 @@ Transmit path guidelines:
    transmit function will become busy.
 
    Instead it must maintain the queue properly.  For example,
-   for a driver implementing scatter-gather this means:
+   for a driver implementing scatter-gather this means::
 
        static netdev_tx_t drv_hard_start_xmit(struct sk_buff *skb,
                                               struct net_device *dev)
@@ -38,25 +42,25 @@ Transmit path guidelines:
                return NETDEV_TX_OK;
        }
 
-   And then at the end of your TX reclamation event handling:
+   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))
+           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:
+   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:
+   and::
 
                if (TX_BUFFS_AVAIL(dp) == 0)
 
-   and:
+   and::
 
        if (netif_queue_stopped(dp->dev) &&
-            TX_BUFFS_AVAIL(dp) > 0)
+           TX_BUFFS_AVAIL(dp) > 0)
                netif_wake_queue(dp->dev);
 
 2) An ndo_start_xmit method must not modify the shared parts of a
@@ -86,7 +90,7 @@ 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 
+   be aborted. If necessary, poll or wait for completion of
    any reset commands.
 
 2) The ndo_stop routine will be called by unregister_netdevice
similarity index 62%
rename from Documentation/networking/eql.txt
rename to Documentation/networking/eql.rst
index 0f15501..a628c4c 100644 (file)
@@ -1,5 +1,11 @@
-  EQL Driver: Serial IP Load Balancing HOWTO
+.. 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
@@ -12,7 +18,8 @@
   which was only created to patch cleanly in the very latest kernel
   source trees. (Yes, it worked fine.)
 
-  1.  Introduction
+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,
   Hey, we can all dream you know...
 
 
-  2.  Kernel Configuration
+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
+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:
-
-
+  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:
+  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
+2.2. Building The Kernel
+------------------------
 
   After patching the kernel, run make config and configure the kernel
   for your hardware.
@@ -90,7 +90,8 @@
   After configuration, make and install according to your habit.
 
 
-  3.  Network Configuration
+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
   connection.
 
 
-  3.1. /etc/rc.d/rc.inet1
+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:
-
+  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:
+  life so much easier::
 
-
-
-       ______________________________________________________________________
        route add default eql
-       ______________________________________________________________________
 
 
-  3.2. Enslaving Devices By Hand
+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
 
 
   The syntax for enslaving a device is "eql_enslave <master-name>
-  <slave-name> <estimated-bps>".  Here are some example enslavings:
-
+  <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.--)
-
-
+  for you.--)::
 
-       ______________________________________________________________________
        eql_emancipate eql sl0
        eql_emancipate eql ppp0
        eql_emancipate eql sl1
-       ______________________________________________________________________
 
 
-
-
-
-  3.3. DSLIP Configuration for the eql Device
+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:
-
-
-
-
-
-
-
-
-
-
-
+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
 
-  ______________________________________________________________________
-  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
+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
   year.
 
 
-  4.  About the Slave Scheduler Algorithm
+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
   traffic and the "slower" modem starved.
 
 
-  5.  Testers' Reports
+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
   balancing" driver config option.
 
 
-  o  icee from LinuxNET patched 1.1.86 without any rejects and was able
+  -  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
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+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
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-  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
   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:
+  would take the following times::
 
       bits/sec seconds
       345600   8.3
   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,
+  ======  ========     ===  ========   ======= ======= ===
+  #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
similarity index 96%
rename from Documentation/networking/fib_trie.txt
rename to Documentation/networking/fib_trie.rst
index fe71938..f1435b7 100644 (file)
@@ -1,8 +1,12 @@
-                       LC-trie implementation notes.
+.. SPDX-License-Identifier: GPL-2.0
+
+============================
+LC-trie implementation notes
+============================
 
 Node types
 ----------
-leaf 
+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.
@@ -13,7 +17,7 @@ trie node or tnode
 
 A few concepts explained
 ------------------------
-Bits (tnode) 
+Bits (tnode)
        The number of bits in the key segment used for indexing into the
        child array - the "child index". See Level Compression.
 
@@ -23,7 +27,7 @@ Pos (tnode)
 
 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" 
+       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
@@ -56,8 +60,8 @@ full_children
 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. 
+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
similarity index 77%
rename from Documentation/networking/filter.txt
rename to Documentation/networking/filter.rst
index 2f0f8b1..a1d3e19 100644 (file)
@@ -1,3 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======================================================
 Linux Socket Filtering aka Berkeley Packet Filter (BPF)
 =======================================================
 
@@ -42,10 +45,10 @@ 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
+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
+.. [1] Documentation/userspace-api/seccomp_filter.rst
 
 Original BPF paper:
 
@@ -59,23 +62,23 @@ Structure
 ---------
 
 User space applications include <linux/filter.h> which contains the
-following relevant structures:
+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 */
-};
+       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.
+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;
-};
+       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).
@@ -83,55 +86,57 @@ 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);
+::
+
+    #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
@@ -178,15 +183,17 @@ 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
+                  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):
+consists of the following elements (as already mentioned)::
 
   op:16, jt:8, jf:8, k:32
 
@@ -201,8 +208,9 @@ 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
@@ -241,11 +249,13 @@ opcodes as defined in linux/filter.h stand for:
   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
@@ -259,6 +269,7 @@ The next table shows addressing formats from the 2nd column:
   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
@@ -267,8 +278,9 @@ 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
@@ -285,18 +297,19 @@ Possible BPF extensions are shown in the following table:
   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:
+**ARP packets**::
 
   ldh [12]
   jne #0x806, drop
   ret #-1
   drop: ret #0
 
-** IPv4 TCP packets:
+**IPv4 TCP packets**::
 
   ldh [12]
   jne #0x800, drop
@@ -305,14 +318,15 @@ Examples for low-level BPF:
   ret #-1
   drop: ret #0
 
-** (Accelerated) VLAN w/ id 10:
+**(Accelerated) VLAN w/ id 10**::
 
   ld vlan_tci
   jneq #10, drop
   ret #-1
   drop: ret #0
 
-** icmp random packet sampling, 1 in 4
+**icmp random packet sampling, 1 in 4**:
+
   ldh [12]
   jne #0x800, drop
   ldb [23]
@@ -324,7 +338,7 @@ Examples for low-level BPF:
   ret #-1
   drop: ret #0
 
-** SECCOMP filter example:
+**SECCOMP filter example**::
 
   ld [4]                  /* offsetof(struct seccomp_data, arch) */
   jne #0xc000003e, bad    /* AUDIT_ARCH_X86_64 */
@@ -345,18 +359,18 @@ Examples for low-level BPF:
 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:
+ARP code::
 
-$ ./bpf_asm foo
-4,40 0 0 12,21 0 1 2054,6 0 0 4294967295,6 0 0 0,
+    $ ./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:
+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 },
+    $ ./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
@@ -365,9 +379,9 @@ 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:
+Starting bpf_dbg is trivial and just requires issuing::
 
-# ./bpf_dbg
+    # ./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
@@ -381,84 +395,100 @@ 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
+* 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
+  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
+* load pcap foo.pcap
+
   Loads standard tcpdump pcap file.
 
-> run [<n>]
+* 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
+* 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 },
+* 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
+* 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>]
+  * 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>
+* 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
-#
+* quit
+
   Exits bpf_dbg.
 
 JIT compiler
@@ -468,23 +498,23 @@ 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:
+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:
+opcode image into the kernel log via::
 
   echo 2 > /proc/sys/net/core/bpf_jit_enable
 
-Example output from dmesg:
+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
+    [ 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
@@ -493,78 +523,78 @@ is discouraged and introspection through bpftool (under tools/bpf/bpftool/) is t
 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
+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.
@@ -663,9 +693,9 @@ Some core changes of the new internal format:
 
 - 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 */".
+  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:
@@ -684,32 +714,32 @@ Some core changes of the new internal format:
   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:
+  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; }
+    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:
+  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
+    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:
+  Function f2 in eBPF may look like::
 
-  f2:
-    bpf_mov R2, R1
-    bpf_add R1, 1
-    bpf_call f3
-    bpf_exit
+    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
+  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.
 
@@ -722,6 +752,8 @@ Some core changes of the new internal format:
   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
@@ -737,7 +769,7 @@ Some core changes of the new internal format:
   ... 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:
+  Then the following internal BPF pseudo-program::
 
     bpf_mov R6, R1 /* save ctx */
     bpf_mov R2, 2
@@ -755,7 +787,7 @@ Some core changes of the new internal format:
     bpf_add R0, R7
     bpf_exit
 
-  After JIT to x86_64 may look like:
+  After JIT to x86_64 may look like::
 
     push %rbp
     mov %rsp,%rbp
@@ -781,21 +813,21 @@ Some core changes of the new internal format:
     leaveq
     retq
 
-  Which is in this example equivalent in C to:
+  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);
+       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.
+  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:
+  For example the following program is invalid::
 
     bpf_mov R1, 1
     bpf_call foo
@@ -814,7 +846,7 @@ 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:
+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
 
@@ -824,7 +856,7 @@ 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
+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.
@@ -849,7 +881,7 @@ 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:
+field is divided into three parts::
 
   +----------------+--------+--------------------+
   |   4 bits       |  1 bit |   3 bits           |
@@ -859,8 +891,9 @@ field is divided into three parts:
 
 Three LSB bits store instruction class which is one of:
 
-  Classic BPF classes:    eBPF classes:
-
+  ===================     ===============
+  Classic BPF classes     eBPF classes
+  ===================     ===============
   BPF_LD    0x00          BPF_LD    0x00
   BPF_LDX   0x01          BPF_LDX   0x01
   BPF_ST    0x02          BPF_ST    0x02
@@ -869,25 +902,28 @@ Three LSB bits store instruction class which is one of:
   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
+    ::
+
+       BPF_K     0x00
+       BPF_X     0x08
 
- * in classic BPF, this means:
+ * 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
+       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:
+ * 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
+       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:
+If BPF_CLASS(code) == BPF_ALU or BPF_ALU64 [ in eBPF ], BPF_OP(code) is one of::
 
   BPF_ADD   0x00
   BPF_SUB   0x10
@@ -904,7 +940,7 @@ If BPF_CLASS(code) == BPF_ALU or BPF_ALU64 [ in eBPF ], BPF_OP(code) is one of:
   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:
+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
@@ -934,7 +970,7 @@ 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'
+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
@@ -942,7 +978,7 @@ 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:
+For load and store instructions the 8-bit 'code' field is divided as::
 
   +--------+--------+-------------------+
   | 3 bits | 2 bits |   3 bits          |
@@ -952,19 +988,21 @@ For load and store instructions the 8-bit 'code' field is divided as:
 
 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:
+... 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:
+Mode modifier is one of::
 
   BPF_IMM  0x00  /* used for 32-bit mov in classic BPF and 64-bit in eBPF */
   BPF_ABS  0x20
@@ -979,7 +1017,7 @@ eBPF has two non-generic instructions: (BPF_ABS | <size> | BPF_LD) and
 
 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
+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
@@ -992,26 +1030,26 @@ 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:
+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:
+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
+    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
+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.
@@ -1037,38 +1075,48 @@ 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:
+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:
+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')
+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:
+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
@@ -1079,10 +1127,13 @@ 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:
+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.
@@ -1113,48 +1164,61 @@ 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/
+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_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.
+                       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.
+                       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.
+  '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
@@ -1188,7 +1252,7 @@ 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
+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.
 
@@ -1196,17 +1260,18 @@ 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 */
+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
+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).
@@ -1215,52 +1280,58 @@ 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)
+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)
+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
+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).
+``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'
+
+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
+
+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
+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
+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:
+using normal C code as::
+
   void *data = (void *)(long)skb->data;
   void *data_end = (void *)(long)skb->data_end;
   struct eth_hdr *eth = data;
@@ -1268,13 +1339,14 @@ using normal C code as:
   struct udphdr *udp = data + sizeof(*eth) + sizeof(*iph);
 
   if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*udp) > data_end)
-          return 0;
+         return 0;
   if (eth->h_proto != htons(ETH_P_IP))
-          return 0;
+         return 0;
   if (iph->protocol != IPPROTO_UDP || iph->ihl != 5)
-          return 0;
+         return 0;
   if (udp->dest == 53 || udp->source == 9)
-          ...;
+         ...;
+
 which makes such programs easier to write comparing to LD_ABS insn
 and significantly faster.
 
@@ -1284,23 +1356,24 @@ eBPF maps
 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)
+  ``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)
+  ``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)
+  ``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)
+  ``err = bpf(BPF_MAP_DELETE_ELEM, union bpf_attr *attr, u32 size)``
   using attr->map_fd, attr->key
 
 - to delete map: close(fd)
@@ -1312,10 +1385,11 @@ 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
+
+  - type
+  - max number of elements
+  - key size in bytes
+  - value size in bytes
 
 Pruning
 -------
@@ -1339,57 +1413,75 @@ 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[] = {
+Program with unreachable instructions::
+
+  static struct bpf_insn prog[] = {
   BPF_EXIT_INSN(),
   BPF_EXIT_INSN(),
-};
+  };
+
 Error:
+
   unreachable insn 1
 
-Program that reads uninitialized register:
+Program that reads uninitialized register::
+
   BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
   BPF_EXIT_INSN(),
-Error:
+
+Error::
+
   0: (bf) r0 = r2
   R2 !read_ok
 
-Program that doesn't initialize R0 before exiting:
+Program that doesn't initialize R0 before exiting::
+
   BPF_MOV64_REG(BPF_REG_2, BPF_REG_1),
   BPF_EXIT_INSN(),
-Error:
+
+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 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::
 
-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:
+
+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:
+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:
+
+Error::
+
   0: (7a) *(u64 *)(r10 -8) = 0
   1: (bf) r2 = r10
   2: (07) r2 += -8
@@ -1398,7 +1490,8 @@ Error:
   fd 0 is not pointing to valid bpf_map
 
 Program that doesn't check return value of map_lookup_elem() before accessing
-map element:
+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),
@@ -1406,7 +1499,9 @@ map element:
   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:
+
+Error::
+
   0: (7a) *(u64 *)(r10 -8) = 0
   1: (bf) r2 = r10
   2: (07) r2 += -8
@@ -1416,7 +1511,8 @@ Error:
   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:
+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),
@@ -1425,7 +1521,9 @@ accesses the memory with incorrect alignment:
   BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
   BPF_ST_MEM(BPF_DW, BPF_REG_0, 4, 0),
   BPF_EXIT_INSN(),
-Error:
+
+Error::
+
   0: (7a) *(u64 *)(r10 -8) = 0
   1: (bf) r2 = r10
   2: (07) r2 += -8
@@ -1438,7 +1536,8 @@ Error:
 
 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:
+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),
@@ -1449,7 +1548,9 @@ to do so in the other side of 'if' branch:
   BPF_EXIT_INSN(),
   BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 1),
   BPF_EXIT_INSN(),
-Error:
+
+Error::
+
   0: (7a) *(u64 *)(r10 -8) = 0
   1: (bf) r2 = r10
   2: (07) r2 += -8
@@ -1465,8 +1566,8 @@ Error:
   R0 invalid mem access 'imm'
 
 Program that performs a socket lookup then sets the pointer to NULL without
-checking it:
-value:
+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),
@@ -1477,7 +1578,9 @@ value:
   BPF_EMIT_CALL(BPF_FUNC_sk_lookup_tcp),
   BPF_MOV64_IMM(BPF_REG_0, 0),
   BPF_EXIT_INSN(),
-Error:
+
+Error::
+
   0: (b7) r2 = 0
   1: (63) *(u32 *)(r10 -8) = r2
   2: (bf) r2 = r10
@@ -1491,7 +1594,8 @@ Error:
   Unreleased reference id=1, alloc_insn=7
 
 Program that performs a socket lookup but does not NULL-check the returned
-value:
+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),
@@ -1501,7 +1605,9 @@ value:
   BPF_MOV64_IMM(BPF_REG_5, 0),
   BPF_EMIT_CALL(BPF_FUNC_sk_lookup_tcp),
   BPF_EXIT_INSN(),
-Error:
+
+Error::
+
   0: (b7) r2 = 0
   1: (63) *(u32 *)(r10 -8) = r2
   2: (bf) r2 = r10
@@ -1519,7 +1625,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:
+enabled via Kconfig::
 
   CONFIG_TEST_BPF=m
 
@@ -1540,6 +1646,6 @@ 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>
+Jay Schulist <jschlst@samba.org>
+Daniel Borkmann <daniel@iogearbox.net>
+Alexei Starovoitov <ast@kernel.org>
similarity index 94%
rename from Documentation/networking/fore200e.txt
rename to Documentation/networking/fore200e.rst
index 1f98f62..55df9ec 100644 (file)
@@ -1,6 +1,8 @@
+.. 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
@@ -27,8 +29,8 @@ 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 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
similarity index 93%
rename from Documentation/networking/framerelay.txt
rename to Documentation/networking/framerelay.rst
index 1a0b720..6d90439 100644 (file)
@@ -1,4 +1,10 @@
-Frame Relay (FR) support for linux is built into a two tiered system of device 
+.. 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
@@ -7,18 +13,18 @@ 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'... 
+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, 
+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 
+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.
 
@@ -32,8 +38,7 @@ 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 
+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 
+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.
-
similarity index 60%
rename from Documentation/networking/gen_stats.txt
rename to Documentation/networking/gen_stats.rst
index 179b18c..595a83b 100644 (file)
@@ -1,67 +1,76 @@
+.. 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;
-       ...
-};
+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)::
 
-Update statistics, in dequeue() methods only, (while owning qdisc->running)
-mystruct->tstats.packet++;
-mystruct->qstats.backlog += skb->pkt_len;
+       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;
+    my_dumping_routine(struct sk_buff *skb, ...)
+    {
+           struct gnet_dump dump;
 
-       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_start_copy(skb, TCA_STATS2, &mystruct->lock, &dump,
+                                   TCA_PAD) < 0)
+                   goto rtattr_failure;
 
-       if (gnet_stats_finish_copy(&dump) < 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.
+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;
-       ...
-}
+    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
@@ -77,7 +86,7 @@ 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.
@@ -92,18 +101,19 @@ Rate Estimator:
    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:
+3) Now initialize a new estimator::
 
-   int ret = gen_new_estimator(my_basicstats,my_rate_est_stats,
-       mystats_lock, attr_with_tcestimator_struct);
+    int ret = gen_new_estimator(my_basicstats,my_rate_est_stats,
+       mystats_lock, attr_with_tcestimator_struct);
 
-   if ret == 0
-       success
-   else
-       failed
+    if ret == 0
+       success
+    else
+       failed
 
 From now on, every time you dump my_rate_est_stats it will contain
 up-to-date info.
@@ -115,5 +125,5 @@ 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>
+Thomas Graf <tgraf@suug.ch>
+Jamal Hadi Salim <hadi@cyberus.ca>
similarity index 75%
rename from Documentation/networking/generic-hdlc.txt
rename to Documentation/networking/generic-hdlc.rst
index 4eb3cc4..1c3bb5c 100644 (file)
@@ -1,14 +1,22 @@
+.. 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
@@ -24,19 +32,24 @@ 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:
+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:
+Usually you want something like::
 
        sethdlc hdlc0 clock int rate 128000
        sethdlc hdlc0 cisco interval 10 timeout 25
-or
+
+or::
+
        sethdlc hdlc0 rs232 clock ext
        sethdlc hdlc0 fr lmi ansi
        sethdlc hdlc0 create 99
@@ -49,46 +62,63 @@ 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)
+* 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
+           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
+
+  - 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.
 
@@ -101,26 +131,34 @@ Frame-Relay only:
 Board-specific issues
 ---------------------
 
-n2.o and c101.o need parameters to work:
+n2.o and c101.o need parameters to work::
 
        insmod n2 hw=io,irq,ram,ports[:io,irq,...]
-example:
+
+example::
+
        insmod n2 hw=0x300,10,0xD0000,01
 
-or
+or::
+
        insmod c101 hw=irq,ram[:irq,...]
-example:
+
+example::
+
        insmod c101 hw=9,0xdc000
 
-If built into the kernel, these drivers need kernel (command line) parameters:
+If built into the kernel, these drivers need kernel (command line) parameters::
+
        n2.hw=io,irq,ram,ports:...
-or
+
+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):
+"private" command to see port's packet descriptor rings (in kernel logs)::
 
        sethdlc hdlc0 private
 
similarity index 64%
rename from Documentation/networking/generic_netlink.txt
rename to Documentation/networking/generic_netlink.rst
index 3e07111..59e04cc 100644 (file)
@@ -1,3 +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
similarity index 79%
rename from Documentation/networking/gtp.txt
rename to Documentation/networking/gtp.rst
index 6966bbe..1563fb9 100644 (file)
@@ -1,12 +1,18 @@
+.. 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>
+=====================================
+
+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 ==
+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)
@@ -41,7 +47,8 @@ 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 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
@@ -70,7 +77,8 @@ 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 ==
+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
@@ -82,7 +90,8 @@ to make use of the Linux kernel GTP-U support:
 * ergw (GGSN + P-GW in Erlang):
   https://github.com/travelping/ergw
 
-== Userspace Library / Command Line Utilities ==
+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
@@ -90,7 +99,8 @@ interface provided by the Kernel GTP module:
 
 http://git.osmocom.org/libgtpnl/
 
-== Protocol Versions ==
+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.
@@ -105,7 +115,8 @@ 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 ==
+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.
@@ -114,22 +125,25 @@ 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 ==
+Mailing List
+============
 
-If yo have questions regarding how to use the Kernel GTP module from
+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 ==
+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 ==
+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
@@ -139,9 +153,11 @@ 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 ==
+Architectural Details
+=====================
 
-=== Local GTP-U entity and tunnel identification ===
+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.
@@ -164,15 +180,15 @@ Therefore:
     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:
+[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 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
@@ -183,7 +199,8 @@ multiple or unknown peers.
 Therefore, the receiving side identifies tunnels exclusively based on
 TEIDs, not based on the source IP!
 
-== APN vs. Network Device ==
+APN vs. Network Device
+======================
 
 The GTP-U driver creates a Linux network device for each Gi/SGi
 interface.
@@ -201,29 +218,33 @@ 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.
+(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
+.. 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
similarity index 97%
rename from Documentation/networking/hinic.txt
rename to Documentation/networking/hinic.rst
index 989366a..867ac8f 100644 (file)
@@ -1,3 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============================================================
 Linux Kernel Driver for Huawei Intelligent NIC(HiNIC) family
 ============================================================
 
@@ -110,7 +113,7 @@ hinic_dev - de/constructs the Logical Tx and Rx Queues.
 (hinic_main.c, hinic_dev.h)
 
 
-Miscellaneous:
+Miscellaneous
 =============
 
 Common functions that are used by HW and Logical Device.
similarity index 82%
rename from Documentation/networking/ila.txt
rename to Documentation/networking/ila.rst
index a17dac9..5ac0a62 100644 (file)
@@ -1,4 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===================================
 Identifier Locator Addressing (ILA)
+===================================
 
 
 Introduction
@@ -26,11 +30,13 @@ 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
+  - 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
+  - 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.
 
@@ -51,17 +57,20 @@ ILA terminology
                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
+  - 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
+  - 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
+  - ILA node
+               A network node capable of performing ILA translations. This
                can be an ILA router, ILA forwarding cache, or ILA host.
 
 
@@ -82,18 +91,18 @@ 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.
+as showing ILA hosts and routers::
 
     +--------+                                                +--------+
     | Host A +-+                                         +--->| Host B |
     |        | |              (2) ILA                   (')   |        |
     +--------+ |            ...addressed....           (   )  +--------+
-               V  +---+--+  .  packet      .  +---+--+  (_)
+              V  +---+--+  .  packet      .  +---+--+  (_)
    (1) SIR     |  | ILA  |----->-------->---->| ILA  |   |   (3) SIR
     addressed  +->|router|  .              .  |router|->-+    addressed
     packet        +---+--+  .     IPv6     .  +---+--+        packet
-                   /        .    Network   .
-                  /         .              .   +--+-++--------+
+                  /        .    Network   .
+                 /         .              .   +--+-++--------+
     +--------+   /          .              .   |ILA ||  Host  |
     |  Host  +--+           .              .- -|host||        |
     |        |              .              .   +--+-++--------+
@@ -173,7 +182,7 @@ 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.
+unstructured sixty-four bit value::
 
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                            Identifier                         |
@@ -184,7 +193,7 @@ unstructured sixty-four bit value.
 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.
+still sixty-four bits::
 
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                            Identifier                         |
@@ -193,7 +202,7 @@ still sixty-four bits.
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 The C-bit may used to explicitly indicate that checksum neutral
-mapping has been applied to an ILA address. The format is:
+mapping has been applied to an ILA address. The format is::
 
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |     |C|                    Identifier                         |
@@ -204,7 +213,7 @@ mapping has been applied to an ILA address. The format is:
 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.
+used with the identifier type as illustrated below::
 
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      | Type|                      Identifier                         |
@@ -213,7 +222,7 @@ used with the identifier type as illustrated below.
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 If the identifier type and the C-bit can be present simultaneously so
-the identifier format would be:
+the identifier format would be::
 
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      | Type|C|                    Identifier                         |
@@ -258,28 +267,30 @@ 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
+::
+
+     # 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 6538ede..0186e27 100644 (file)
@@ -15,6 +15,7 @@ Contents:
    device_drivers/index
    dsa/index
    devlink/index
+   caif/index
    ethtool-netlink
    ieee802154
    j1939
@@ -24,6 +25,7 @@ Contents:
    failover
    net_dim
    net_failover
+   page_pool
    phy
    sfp-phylink
    alias
@@ -36,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
 
similarity index 83%
rename from Documentation/networking/ip-sysctl.txt
rename to Documentation/networking/ip-sysctl.rst
index 9375324..b72f89d 100644 (file)
@@ -1,8 +1,15 @@
-/proc/sys/net/ipv4/* Variables:
+.. SPDX-License-Identifier: GPL-2.0
+
+=========
+IP Sysctl
+=========
+
+/proc/sys/net/ipv4/* Variables
+==============================
 
 ip_forward - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
+       0 - disabled (default)
+       not 0 - enabled
 
        Forward Packets between interfaces.
 
@@ -38,6 +45,7 @@ ip_no_pmtu_disc - INTEGER
        could break other protocols.
 
        Possible values: 0-3
+
        Default: FALSE
 
 min_pmtu - INTEGER
@@ -51,16 +59,20 @@ ip_forward_use_pmtu - BOOLEAN
        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
+
+       - 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
@@ -68,63 +80,80 @@ fib_multipath_use_neigh - BOOLEAN
        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
+
+       - 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
+
+       - 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
+
+       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.
+
+       - 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.
@@ -132,11 +161,14 @@ neigh/default/unres_qlen_bytes - INTEGER
 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
@@ -183,7 +215,8 @@ ipfrag_max_dist - INTEGER
        from different IP datagrams, which could result in data corruption.
        Default: 64
 
-INET peer storage:
+INET peer storage
+=================
 
 inet_peer_threshold - INTEGER
        The approximate size of the storage.  Starting from this threshold
@@ -203,7 +236,8 @@ inet_peer_maxttl - INTEGER
        when the number of entries in the pool is very small).
        Measured in seconds.
 
-TCP variables:
+TCP variables
+=============
 
 somaxconn - INTEGER
        Limit of socket listen() backlog, known in userspace as SOMAXCONN.
@@ -222,18 +256,22 @@ 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
@@ -244,6 +282,7 @@ tcp_autocorking - BOOLEAN
        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
@@ -265,6 +304,7 @@ tcp_mtu_probe_floor - INTEGER
 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.
 
@@ -277,6 +317,7 @@ tcp_congestion_control - STRING
        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
@@ -286,9 +327,12 @@ 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
+
+               - 0 disables TLP
+               - 3 or 4 enables TLP
+
        Default: 3
 
 tcp_ecn - INTEGER
@@ -297,12 +341,17 @@ tcp_ecn - INTEGER
        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.
+
+               =  =====================================================
+               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
@@ -312,6 +361,7 @@ tcp_ecn_fallback - BOOLEAN
        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
@@ -324,7 +374,9 @@ tcp_fin_timeout - INTEGER
        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
@@ -390,7 +442,8 @@ tcp_l3mdev_accept - BOOLEAN
        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)
+
+       Default: 0 (disabled)
 
 tcp_low_latency - BOOLEAN
        This is a legacy option, it has no effect anymore.
@@ -410,10 +463,14 @@ tcp_max_orphans - INTEGER
 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.
 
@@ -445,7 +502,9 @@ tcp_min_rtt_wlen - INTEGER
        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
@@ -457,9 +516,10 @@ tcp_moderate_rcvbuf - BOOLEAN
 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.
+
+       - 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
@@ -481,6 +541,7 @@ tcp_no_metrics_save - BOOLEAN
 
 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
@@ -489,6 +550,7 @@ tcp_orphan_retries - INTEGER
        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.
@@ -497,11 +559,15 @@ 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
+       =========   =============================================================
+       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
 
@@ -509,12 +575,14 @@ 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
@@ -550,12 +618,14 @@ 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.
@@ -581,6 +651,14 @@ tcp_comp_sack_delay_ns - LONG INTEGER
 
        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.
@@ -592,12 +670,14 @@ tcp_slow_start_after_idle - BOOLEAN
        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
@@ -646,15 +726,18 @@ tcp_fastopen - INTEGER
        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
+
+       =====  ======== ======================================================
+         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
+         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
+       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
 
@@ -668,6 +751,7 @@ tcp_fastopen_blackhole_timeout_sec - INTEGER
        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
@@ -698,20 +782,24 @@ tcp_syn_retries - INTEGER
        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.
+       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
@@ -720,6 +808,7 @@ tcp_pacing_ss_ratio - INTEGER
        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
@@ -727,6 +816,7 @@ tcp_pacing_ca_ratio - INTEGER
        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
@@ -734,16 +824,20 @@ tcp_tso_win_divisor - INTEGER
        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
+
+       - 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
@@ -752,11 +846,14 @@ tcp_window_scaling - BOOLEAN
 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
@@ -764,6 +861,7 @@ tcp_wmem - vector of 3 INTEGERs: min, default, max
        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
@@ -784,6 +882,7 @@ tcp_workaround_signed_windows - BOOLEAN
        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
@@ -795,7 +894,8 @@ tcp_thin_linear_timeouts - BOOLEAN
        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
+       Documentation/networking/tcp-thin.rst
+
        Default: 0
 
 tcp_limit_output_bytes - INTEGER
@@ -807,6 +907,7 @@ tcp_limit_output_bytes - INTEGER
        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
@@ -822,7 +923,8 @@ tcp_rx_skb_cache - BOOLEAN
 
        Default: 0 (disabled)
 
-UDP variables:
+UDP variables
+=============
 
 udp_l3mdev_accept - BOOLEAN
        Enabling this option allows a "global" bound socket to work
@@ -830,7 +932,8 @@ udp_l3mdev_accept - BOOLEAN
        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)
+
+       Default: 0 (disabled)
 
 udp_mem - vector of 3 INTEGERs: min, pressure, max
        Number of pages allowed for queueing by all UDP sockets.
@@ -849,15 +952,18 @@ 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 variables
+=============
 
 raw_l3mdev_accept - BOOLEAN
        Enabling this option allows a "global" bound socket to work
@@ -865,9 +971,11 @@ raw_l3mdev_accept - BOOLEAN
        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:
+CIPSOv4 Variables
+=================
 
 cipso_cache_enable - BOOLEAN
        If set, enable additions to and lookups from the CIPSO label mapping
@@ -875,6 +983,7 @@ cipso_cache_enable - BOOLEAN
        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
@@ -884,6 +993,7 @@ cipso_cache_bucket_size - INTEGER
        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
@@ -891,6 +1001,7 @@ cipso_rbm_optfmt - BOOLEAN
        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
@@ -900,9 +1011,11 @@ cipso_rbm_structvalid - BOOLEAN
        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 Variables
+============
 
 ip_local_port_range - 2 INTEGERS
        Defines the local port range that is used by TCP and UDP to
@@ -931,12 +1044,12 @@ ip_local_reserved_ports - list of comma separated ranges
        assignments.
 
        You can reserve ports which are not in the current
-       ip_local_port_range, e.g.:
+       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
+           $ 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
@@ -956,6 +1069,7 @@ ip_unprivileged_port_start - INTEGER
 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
@@ -972,6 +1086,7 @@ ip_dynaddr - BOOLEAN
        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
@@ -981,6 +1096,7 @@ ip_early_demux - BOOLEAN
 
        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
@@ -992,21 +1108,25 @@ ping_group_range - 2 INTEGERS
 
 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
@@ -1016,46 +1136,55 @@ icmp_ratelimit - INTEGER
        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 *
+               3 Destination Unreachable [1]_
+               4 Source Quench [1]_
                5 Redirect
                8 Echo Request
-               B Time Exceeded *
-               C Parameter Problem *
+               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
+               = =========================
 
-       * These are rate limited by default (see default mask above)
+       .. [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
@@ -1100,32 +1229,39 @@ igmp_max_memberships - INTEGER
 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.
+       - 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::
 
-       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.
+          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/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
+``conf/all/*``
+         is special, changes the settings for all interfaces
 
 log_martians - BOOLEAN
        Log packets with impossible addresses to kernel log.
@@ -1136,14 +1272,21 @@ log_martians - BOOLEAN
 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)
+
+       default:
+
+               - TRUE (host)
+               - FALSE (router)
 
 forwarding - BOOLEAN
        Enable IP forwarding on this interface.  This controls whether packets
@@ -1168,12 +1311,14 @@ medium_id - INTEGER
 
 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).
 
@@ -1186,6 +1331,7 @@ proxy_arp_pvlan - BOOLEAN
        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.
@@ -1194,26 +1340,33 @@ proxy_arp_pvlan - BOOLEAN
 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
@@ -1222,15 +1375,20 @@ bootp_relay - BOOLEAN
        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)
+
+       default
+
+               - TRUE (router)
+               - FALSE (host)
 
 accept_local - BOOLEAN
        Accept packets with local source addresses. In combination with
@@ -1241,18 +1399,19 @@ accept_local - BOOLEAN
 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.
+       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
@@ -1265,19 +1424,19 @@ rp_filter - INTEGER
        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.
+       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,
@@ -1287,26 +1446,27 @@ 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.
+
+       - 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.
 
@@ -1317,32 +1477,37 @@ arp_announce - INTEGER
 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
+
+       - 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.
+
+        ==  ==========================================================
+         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
+
+       - 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.
@@ -1378,11 +1543,13 @@ disable_xfrm - BOOLEAN
 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
@@ -1393,19 +1560,23 @@ promote_secondaries - BOOLEAN
 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
@@ -1417,21 +1588,24 @@ xfrm4_gc_thresh - INTEGER
 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
 
+- Andi Kleen
+  ak@muc.de
+- Nicolas Delon
+  delon.nicolas@wanadoo.fr
 
 
 
-/proc/sys/net/ipv6/* Variables:
+
+/proc/sys/net/ipv6/* Variables
+==============================
 
 IPv6 has no global variables such as tcp_*.  tcp_* settings under ipv4/ also
 apply to IPv6 [XXX?].
@@ -1440,8 +1614,9 @@ 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
+
+               - TRUE: disable IPv4-mapped address feature
+               - FALSE: enable IPv4-mapped address feature
 
        Default: FALSE (as specified in RFC3493)
 
@@ -1449,8 +1624,10 @@ 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
+
+       - TRUE: enabled
+       - FALSE: disabled
+
        Default: TRUE
 
 auto_flowlabels - INTEGER
@@ -1458,22 +1635,28 @@ auto_flowlabels - INTEGER
        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
+
+       =  ===========================================================
+       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
+       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
+       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
+
+       - TRUE: enabled
+       - FALSE: disabled
+
        Default: true
 
 flowlabel_reflect - INTEGER
@@ -1483,49 +1666,59 @@ flowlabel_reflect - INTEGER
        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"
+       - 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.
+       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.
+       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
+
+       - 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
+
+       - 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
@@ -1533,6 +1726,7 @@ max_dst_opts_number - INTEGER
        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
@@ -1540,16 +1734,19 @@ max_hbh_opts_number - INTEGER
        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
@@ -1558,8 +1755,21 @@ skip_notify_on_dev_down - BOOLEAN
        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
@@ -1580,18 +1790,20 @@ 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()
+        == =======================================================
+        -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/*:
+``conf/default/*``:
        Change the interface-specific default settings.
 
 
-conf/all/*:
+``conf/all/*``:
        Change all the interface-specific settings.
 
        [XXX:  Other special features than forwarding?]
@@ -1615,9 +1827,10 @@ fwmark_reflect - BOOLEAN
        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/*:
+``conf/interface/*``:
        Change special settings per interface.
 
        The functional behaviour for certain settings is different
@@ -1632,31 +1845,40 @@ accept_ra - INTEGER
        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.
+               ==  ===========================================================
+                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.
+       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.
+       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.
+
+          - 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.
@@ -1669,8 +1891,10 @@ accept_ra_min_hop_limit - INTEGER
 accept_ra_pinfo - BOOLEAN
        Learn Prefix Information in Router Advertisement.
 
-       Functional default: enabled if accept_ra is enabled.
-                           disabled if accept_ra is disabled.
+       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.
@@ -1678,8 +1902,10 @@ accept_ra_rt_info_min_plen - INTEGER
        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.
+       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.
@@ -1687,33 +1913,41 @@ accept_ra_rt_info_max_plen - INTEGER
        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.
+       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.
+       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.
+       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.
+       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.
+       >= 0: Accept only routing header type 2.
+       < 0: Do not accept routing header.
 
        Default: 0
 
@@ -1721,24 +1955,30 @@ 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.
+       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.
+       .. 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):
+               - 0 Forwarding disabled
+               - 1 Forwarding enabled
+
+       **FALSE (0)**:
 
        By default, Host behaviour is assumed.  This means:
 
@@ -1749,7 +1989,7 @@ forwarding - INTEGER
           Advertisements (and do autoconfiguration).
        4. If accept_redirects is TRUE (default), accept Redirects.
 
-       TRUE (1):
+       **TRUE (1)**:
 
        If local forwarding is enabled, Router behaviour is assumed.
        This means exactly the reverse from the above:
@@ -1760,19 +2000,22 @@ forwarding - INTEGER
        4. Redirects are ignored.
 
        Default: 0 (disabled) if global forwarding is disabled (default),
-                otherwise 1 (enabled).
+       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
@@ -1784,15 +2027,18 @@ router_probe_interval - INTEGER
 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
@@ -1804,28 +2050,35 @@ use_oif_addrs_only - BOOLEAN
 
 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)
+
+         * <= 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)
+
+       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
+
+       *   >0 : enabled
+       *    0 : system default
+       *   <0 : disabled
 
        Default: 0 (addresses are removed)
 
@@ -1834,11 +2087,13 @@ max_desync_factor - INTEGER
        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
@@ -1846,12 +2101,14 @@ max_addresses - INTEGER
        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),
@@ -1865,10 +2122,13 @@ disable_ipv6 - BOOLEAN
 
 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.
+
+        == ==============================================================
+         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.
@@ -1876,6 +2136,7 @@ accept_dad - INTEGER
 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:
@@ -1893,9 +2154,10 @@ force_tllao - BOOLEAN
 
 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.
+
+       * 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
@@ -1904,33 +2166,38 @@ ndisc_tclass - INTEGER
        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)
+
+       * 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
+       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
+
+       * 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
+
+       * 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,
@@ -1941,8 +2208,9 @@ use_optimistic - BOOLEAN
        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
+
+       * 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.
@@ -1964,12 +2232,14 @@ stable_secret - IPv6 address
 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
+       =  =================================================================
+       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
+       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
@@ -1991,13 +2261,18 @@ enhanced_dad - BOOLEAN
        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/*:
+``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
@@ -2018,16 +2293,19 @@ ratemask - list of comma separated ranges
 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
@@ -2043,43 +2321,52 @@ 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.
+       - 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.
+       - 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.
+       - 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.
+       - 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.
+       - 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.
+       - 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:
+``proc/sys/net/sctp/*`` Variables:
+==================================
 
 addip_enable - BOOLEAN
        Enable or disable extension of  Dynamic Address Reconfiguration
@@ -2144,11 +2431,13 @@ addip_noauth_enable - BOOLEAN
        we provide this variable to control the enforcement of the
        authentication requirement.
 
-       1: Allow ADD-IP extension to be used without authentication.  This
+       == ===============================================================
+       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
+       0  Enforce the authentication requirement
+       == ===============================================================
 
        Default: 0
 
@@ -2158,8 +2447,8 @@ auth_enable - BOOLEAN
        required for secure operation of Dynamic Address Reconfiguration
        (ADD-IP) extension.
 
-       1: Enable this extension.
-       0: Disable this extension.
+       1: Enable this extension.
+       0: Disable this extension.
 
        Default: 0
 
@@ -2167,8 +2456,8 @@ 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
+       1: Enable extension
+       0: Disable
 
        Default: 1
 
@@ -2270,8 +2559,8 @@ 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
+       1: Enable cookie lifetime extension.
+       0: Disable
 
        Default: 1
 
@@ -2279,9 +2568,11 @@ 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).
@@ -2300,16 +2591,16 @@ rcvbuf_policy - INTEGER
        to each association instead of the socket.  This prevents the described
        blocking.
 
-       1: rcvbuf space is per association
-       0: rcvbuf space is per socket
+       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.
+       1: Send buffer is tracked per association
+       0: Send buffer is tracked per socket.
 
        Default: 0
 
@@ -2342,19 +2633,23 @@ sctp_wmem  - vector of 3 INTEGERs: min, default, max
 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
+       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/*
+``/proc/sys/net/core/*``
+========================
+
        Please see: Documentation/admin-guide/sysctl/net.rst for descriptions of these entries.
 
 
-/proc/sys/net/unix/*
+``/proc/sys/net/unix/*``
+========================
+
 max_dgram_qlen - INTEGER
        The maximum length of dgram socket receive queue
 
similarity index 65%
rename from Documentation/networking/ip_dynaddr.txt
rename to Documentation/networking/ip_dynaddr.rst
index 45f3c12..eacc0c7 100644 (file)
@@ -1,10 +1,15 @@
+.. 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:
+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
@@ -12,18 +17,24 @@ If enabled[*] and forwarding interface has changed:
       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
+``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:
+.. [#] 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)
+
+  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>
+Juanjo  <jjciarla@raiz.uncu.edu.ar>
similarity index 89%
rename from Documentation/networking/ipddp.txt
rename to Documentation/networking/ipddp.rst
index ba5c217..be7091b 100644 (file)
@@ -1,7 +1,12 @@
-Text file for ipddp.c:
-       AppleTalk-IP Decapsulation and AppleTalk-IP Encapsulation
+.. SPDX-License-Identifier: GPL-2.0
 
-This text file is written by Jay Schulist <jschlst@samba.org>
+=========================================================
+AppleTalk-IP Decapsulation and AppleTalk-IP Encapsulation
+=========================================================
+
+Documentation ipddp.c
+
+This file is written by Jay Schulist <jschlst@samba.org>
 
 Introduction
 ------------
@@ -21,7 +26,7 @@ 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
similarity index 50%
rename from Documentation/networking/iphase.txt
rename to Documentation/networking/iphase.rst
index 670b72f..92d9b75 100644 (file)
@@ -1,27 +1,35 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================================
+ATM (i)Chip IA Linux Driver Source
+==================================
+
+                             READ ME FISRT
 
-                              READ ME FISRT
-                 ATM (i)Chip IA Linux Driver Source
 --------------------------------------------------------------------------------
-                     Read This Before You Begin!
+
+                    Read This Before You Begin!
+
 --------------------------------------------------------------------------------
 
 Description
------------
+===========
 
-This is the README file for the Interphase PCI ATM (i)Chip IA Linux driver 
+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 
+    - 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. 
+    - 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 
+    - 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.
@@ -29,128 +37,155 @@ The features and limitations of this driver are as follows:
 
 
 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.
+       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 
+
+    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. 
+    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:
+         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
+             cat /proc/atm/devices
 
-          If the driver is loaded successfully, the output of the command will 
-          be similar to the following lines:
+         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 )
+             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.
+         You can also check the system log file /var/log/messages for messages
+         related to the ATM driver.
 
-5. Ia Driver Configuration 
+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
+    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> 
+       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.   
+           - 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 
+    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 
+   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
+
+      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.
+
+      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.
 
+      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.
 
-OUTSTANDING ISSUES
-------------------
+      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
+        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
similarity index 90%
rename from Documentation/networking/ipsec.txt
rename to Documentation/networking/ipsec.rst
index ba794b7..afe9d7b 100644 (file)
@@ -1,12 +1,20 @@
+.. 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
+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
+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
similarity index 93%
rename from Documentation/networking/ipv6.txt
rename to Documentation/networking/ipv6.rst
index 6cd74fa..ba09c2f 100644 (file)
@@ -1,9 +1,15 @@
+.. 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
+``/etc/modules.d/*.conf`` configuration files, or in a distro-specific
 configuration file.
 
 The available ipv6 module parameters are listed below.  If a parameter
similarity index 54%
rename from Documentation/networking/ipvlan.txt
rename to Documentation/networking/ipvlan.rst
index 27a38e5..694adcb 100644 (file)
@@ -1,11 +1,15 @@
+.. SPDX-License-Identifier: GPL-2.0
 
-                            IPVLAN Driver HOWTO
+===================
+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
+================
+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
@@ -13,34 +17,48 @@ outside of it.
 
 
 2. Building and Installation:
-       In order to build the driver, please select the config item CONFIG_IPVLAN.
+=============================
+
+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
+=================
+
+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
+        MODE: l3 (default) | l3s | l2
+        FLAGS: bridge (default) | private | vepa
+
+e.g.
 
-    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
+       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,
+===================
+
+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.
@@ -48,39 +66,50 @@ 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
+------------
+
+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
+------------
+
+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)
+-------------
+
+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
+==============
+
+At this time following mode flags are available
 
 5.1 bridge:
-       This is the default option. To configure the IPvlan port in this mode,
+-----------
+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
+------------
+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.
+---------
+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
@@ -89,18 +118,25 @@ 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
+=======================================
+
+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.
+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                                                |
@@ -117,30 +153,37 @@ namespace where L2 on the slave could be changed / misused.
   +==============================#==============================+
 
 
-       (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
+(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
similarity index 62%
rename from Documentation/networking/ipvs-sysctl.txt
rename to Documentation/networking/ipvs-sysctl.rst
index 0568986..be36c46 100644 (file)
@@ -1,23 +1,30 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========
+IPvs-sysctl
+===========
+
 /proc/sys/net/ipv4/vs/* Variables:
+==================================
 
 am_droprate - INTEGER
-        default 10
+       default 10
 
-        It sets the always mode drop rate, which is used in the mode 3
-        of the drop_rate defense.
+       It sets the always mode drop rate, which is used in the mode 3
+       of the drop_rate defense.
 
 amemthresh - INTEGER
-        default 1024
+       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.
+       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
+       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.
@@ -44,8 +51,8 @@ conn_reuse_mode - INTEGER
        real servers to a very busy cluster.
 
 conntrack - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
+       0 - disabled (default)
+       not 0 - enabled
 
        If set, maintain connection tracking entries for
        connections handled by IPVS.
@@ -61,28 +68,28 @@ conntrack - BOOLEAN
        Only available when IPVS is compiled with CONFIG_IP_VS_NFCT enabled.
 
 cache_bypass - BOOLEAN
-        0 - disabled (default)
-        not 0 - enabled
+       - 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.
+       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
+       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.
 
@@ -92,58 +99,58 @@ debug_level - INTEGER
        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.
+       - 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)
+       - 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 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.
+       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.
+       - 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
+       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.
@@ -158,8 +165,8 @@ expire_quiescent_template - BOOLEAN
        connection and the destination server is quiescent.
 
 ignore_tunneled - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
+       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
@@ -168,30 +175,30 @@ ignore_tunneled - BOOLEAN
        ipvs routing loops when ipvs is also acting as a real server).
 
 nat_icmp_send - BOOLEAN
-        0 - disabled (default)
-        not 0 - enabled
+       - 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.
+       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)
+       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)
+       - 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.
+       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
@@ -248,8 +255,8 @@ sync_ports - INTEGER
        8848+sync_ports-1.
 
 snat_reroute - BOOLEAN
-       0 - disabled
-       not 0 - enabled (default)
+       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
@@ -270,6 +277,7 @@ sync_persist_mode - INTEGER
        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.
similarity index 84%
rename from Documentation/networking/kcm.txt
rename to Documentation/networking/kcm.rst
index b773a52..db0f556 100644 (file)
@@ -1,35 +1,38 @@
+.. 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 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
@@ -37,7 +40,7 @@ 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.
@@ -45,14 +48,14 @@ 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
@@ -60,7 +63,7 @@ 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.
 
@@ -110,23 +113,23 @@ User interface
 Creating a multiplexor
 ----------------------
 
-A new multiplexor and initial KCM socket is created by a socket call:
+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
+- 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:
+a KCM socket. This is accomplished by an ioctl on a KCM socket::
 
   /* From linux/kcm.h */
   struct kcm_clone {
-        int fd;
+       int fd;
   };
 
   struct kcm_clone info;
@@ -142,11 +145,11 @@ 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.:
+ioctl on a KCM socket for the multiplexor. e.g.::
 
   /* From linux/kcm.h */
   struct kcm_attach {
-        int fd;
+       int fd;
        int bpf_fd;
   };
 
@@ -160,18 +163,19 @@ ioctl on a KCM socket for the multiplexor. e.g.:
   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
+
+  - 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:
+"unattach" ioctl is done with the kcm_unattach structure as the argument::
 
   /* From linux/kcm.h */
   struct kcm_unattach {
-        int fd;
+       int fd;
   };
 
   struct kcm_unattach info;
@@ -190,7 +194,7 @@ 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:
+while. Example use::
 
   int val = 1;
 
@@ -200,7 +204,7 @@ BFP programs for message delineation
 ------------------------------------
 
 BPF programs can be compiled using the BPF LLVM backend. For example,
-the BPF program for parsing Thrift is:
+the BPF program for parsing Thrift is::
 
   #include "bpf.h" /* for __sk_buff */
   #include "bpf_helpers.h" /* for load_word intrinsic */
@@ -250,6 +254,7 @@ 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.
similarity index 79%
rename from Documentation/networking/l2tp.txt
rename to Documentation/networking/l2tp.rst
index 9bc271c..a48238a 100644 (file)
@@ -1,3 +1,9 @@
+.. 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
@@ -121,14 +127,16 @@ 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.
+=========   ===========================================================
+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.
@@ -177,20 +185,22 @@ 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.
+debugfs filesystem must first be mounted::
 
-# mount -t debugfs debugfs /debug
+       # mount -t debugfs debugfs /debug
 
-Files under the l2tp directory can then be accessed.
+Files under the l2tp directory can then be accessed::
 
-# cat /debug/l2tp/tunnels
+       # 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
@@ -211,14 +221,14 @@ 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:-
+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
+       # 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
@@ -228,75 +238,78 @@ 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.
+data over the network. e.g.::
 
-# ping 10.5.1.1
+       # 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;
+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
 =======================
similarity index 74%
rename from Documentation/networking/lapb-module.txt
rename to Documentation/networking/lapb-module.rst
index d4fc8f2..ff586bc 100644 (file)
@@ -1,8 +1,14 @@
-               The Linux LAPB Module Interface 1.3
+.. SPDX-License-Identifier: GPL-2.0
 
-                     Jonathan Naylor 29.12.96
+===============================
+The Linux LAPB Module Interface
+===============================
 
-Changed (Henner Eisen, 2000-10-29): int return value for data_indication() 
+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
@@ -32,16 +38,16 @@ 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.
+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);
-};
+       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
@@ -54,19 +60,19 @@ 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;
-};
+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.
@@ -78,11 +84,14 @@ 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.
@@ -99,8 +108,9 @@ Functions
 
 The LAPB module provides a number of function entry points.
 
+::
 
-int lapb_register(void *token, struct lapb_register_struct);
+    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
@@ -111,33 +121,42 @@ 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);
+    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);
+    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);
+    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,
@@ -145,42 +164,54 @@ 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);
+    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);
+    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);
+    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);
+    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
@@ -188,9 +219,10 @@ 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
 ---------
@@ -200,49 +232,58 @@ 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);
+    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);
+    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);
+    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);
+    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);
+    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
@@ -254,8 +295,9 @@ 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);
+    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
similarity index 85%
rename from Documentation/networking/ltpc.txt
rename to Documentation/networking/ltpc.rst
index 0bf3220..0ad197f 100644 (file)
@@ -1,3 +1,9 @@
+.. 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
@@ -15,7 +21,7 @@ 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:
+as the following to your /etc/lilo.conf::
 
  append="ltpc=0x240,9,1"
 
@@ -25,13 +31,13 @@ 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:
+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:
+need to add a line such as::
 
  /sbin/ifconfig lt0 127.0.0.42
 
@@ -42,7 +48,7 @@ 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
+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"
@@ -57,13 +63,13 @@ 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
+line in atalkd.conf is::
 
  lt0 -phase 1
 
---------------------------------------
 
-Card Configuration:
+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.
@@ -73,38 +79,44 @@ board.  Set the switches so as not to conflict with other hardware.
        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:
+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
+See Documentation/networking/ipddp.rst for more information about the
 kernel driver and userspace tools needed.
 
---------------------------------------
 
-BUGS:
+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
@@ -120,12 +132,13 @@ 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
+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>
+Bradford Johnson <bradford@math.umn.edu>
 
--- Updated 11/09/1998 by David Huggins-Daines <dhd@debian.org>
+Updated 11/09/1998 by David Huggins-Daines <dhd@debian.org>
@@ -1,16 +1,19 @@
+.. 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:
+following format::
 
  [ radiotap header  ]
  [ ieee80211 header ]
  [ payload ]
 
 The radiotap format is discussed in
-./Documentation/networking/radiotap-headers.txt.
+./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
@@ -18,15 +21,19 @@ 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
+   =========================  ===========================================
+   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
+   =============================  ========================================
+   IEEE80211_RADIOTAP_F_TX_NOACK  frame should be sent without waiting for
                                  an ACK even if it is a unicast frame
+   =============================  ========================================
 
  * IEEE80211_RADIOTAP_RATE
 
@@ -37,8 +44,10 @@ radiotap headers and used to control injection:
    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_MCS_SGI    use short guard interval
+   IEEE80211_RADIOTAP_MCS_BW_40  send in HT40 mode
+   ============================  ========================
 
  * IEEE80211_RADIOTAP_DATA_RETRIES
 
@@ -51,17 +60,17 @@ radiotap headers and used to control injection:
    without own rate control). Also other fields are parsed
 
    flags field
-   IEEE80211_RADIOTAP_VHT_FLAG_SGI: use short guard interval
+       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
+       * 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
+Here is an example valid radiotap header defining some parameters::
 
        0x00, 0x00, // <-- radiotap version
        0x0b, 0x00, // <- radiotap header length
@@ -71,7 +80,7 @@ Here is an example valid radiotap header defining some parameters
        0x01 //<-- antenna
 
 The ieee80211 header follows immediately afterwards, looking for example like
-this:
+this::
 
        0x08, 0x01, 0x00, 0x00,
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
@@ -84,10 +93,10 @@ 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:
+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:
similarity index 82%
rename from Documentation/networking/mpls-sysctl.txt
rename to Documentation/networking/mpls-sysctl.rst
index 025cc9b..0a2ac88 100644 (file)
@@ -1,4 +1,11 @@
+.. 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
@@ -17,6 +24,7 @@ platform_labels - INTEGER
        no longer fit in the table.
 
        Possible values: 0 - 1048575
+
        Default: 0
 
 ip_ttl_propagate - BOOL
@@ -27,8 +35,8 @@ ip_ttl_propagate - BOOL
        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)
+       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
@@ -36,6 +44,7 @@ default_ttl - INTEGER
        or ip_ttl_propagate has been disabled.
 
        Possible values: 1 - 255
+
        Default: 255
 
 conf/<interface>/input - BOOL
@@ -44,5 +53,5 @@ conf/<interface>/input - BOOL
        If disabled, packets will be discarded without further
        processing.
 
-       0 - disabled (default)
-       not 0 - enabled
+       0 - disabled (default)
+       not 0 - enabled
similarity index 76%
rename from Documentation/networking/multiqueue.txt
rename to Documentation/networking/multiqueue.rst
index 4caa0e3..0a57616 100644 (file)
@@ -1,17 +1,17 @@
+.. SPDX-License-Identifier: GPL-2.0
 
-               HOWTO for multiqueue network device support
-               ===========================================
+===========================================
+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
@@ -26,8 +26,7 @@ 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.
@@ -46,22 +45,22 @@ 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:
+is called eth0, run the following command::
 
-# tc qdisc add dev eth0 root handle 1: multiq
+    # 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:
+queues, the band mapping would look like::
 
-band 0 => queue 0
-band 1 => queue 1
-band 2 => queue 2
-band 3 => queue 3
+    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.
@@ -69,11 +68,11 @@ 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:
+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
+    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>
+:Author: Alexander Duyck <alexander.h.duyck@intel.com>
+:Original Author: Peter P. Waskiewicz Jr. <peter.p.waskiewicz.jr@intel.com>
similarity index 66%
rename from Documentation/networking/netconsole.txt
rename to Documentation/networking/netconsole.rst
index 296ea00..1f5c4a0 100644 (file)
@@ -1,7 +1,16 @@
+.. 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>
@@ -23,34 +32,34 @@ Sender and receiver configuration:
 ==================================
 
 It takes a string configuration parameter "netconsole" in the
-following format:
+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)
+       +             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:
+Examples::
 
  linux netconsole=4444@10.0.0.1/eth1,9353@10.0.0.2/12:34:56:78:9a:bc
 
-  or
+or::
 
  insmod netconsole netconsole=@/,@10.0.0.2/
 
-  or using IPv6
+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:
+complete string enclosed in "quotes", thusly::
 
  modprobe netconsole netconsole="@/,@10.0.0.2/;@/eth1,6892@10.0.0.3/"
 
@@ -67,14 +76,19 @@ for example:
 
    On distributions using a BSD-based netcat version (e.g. Fedora,
    openSUSE and Ubuntu) the listening port must be specified without
-   the -p switch:
+   the -p switch::
+
+       nc -u -l -p <port>' / 'nc -u -l <port>
+
+    or::
 
-   'nc -u -l -p <port>' / 'nc -u -l <port>' or
-   'netcat -u -l -p <port>' / 'netcat -u -l <port>'
+       netcat -u -l -p <port>' / 'netcat -u -l <port>
 
 3) socat
 
-   'socat udp-recv:<port> -'
+::
+
+   socat udp-recv:<port> -
 
 Dynamic reconfiguration:
 ========================
@@ -92,7 +106,7 @@ 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):
+To add a remote logging target (target names can be arbitrary)::
 
  cd /sys/kernel/config/netconsole/
  mkdir target1
@@ -102,12 +116,13 @@ 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:
+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)
@@ -117,12 +132,13 @@ The interface exposes these parameters of a netconsole target to userspace:
        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:
+To update a target's parameters::
 
  cat enabled                           # check if enabled is 1
  echo 0 > enabled                      # disable the target (if required)
@@ -140,12 +156,12 @@ 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.
+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.
+following format which is the same as /dev/kmsg::
 
  <level>,<sequnum>,<timestamp>,<contflag>;<message text>
 
@@ -155,12 +171,12 @@ 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.
+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.
+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.
@@ -168,39 +184,52 @@ chunk, the 2nd chunk." may be split as follows.
 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.
+.. 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: 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::
 
-TIP: to find out the MAC address of, say, 10.0.0.2, you may try using:
+   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.
 
- ping -c 1 10.0.0.2 ; /sbin/arp -n | grep 10.0.0.2
+.. note::
 
-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.
+   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: 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::
 
-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:
+   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
      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.
+   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
similarity index 95%
rename from Documentation/networking/netdev-features.txt
rename to Documentation/networking/netdev-features.rst
index 58dd1c1..a2d7d71 100644 (file)
@@ -1,3 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================================================
 Netdev features mess and how to get out from it alive
 =====================================================
 
@@ -6,8 +9,8 @@ Author:
 
 
 
- Part I: Feature sets
-======================
+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)
@@ -39,8 +42,8 @@ one used internally by network core:
 
 
 
- Part II: Controlling enabled features
-=======================================
+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
@@ -65,8 +68,8 @@ driver except by means of ndo_fix_features callback.
 
 
 
- Part III: Implementation hints
-================================
+Part III: Implementation hints
+==============================
 
  * ndo_fix_features:
 
@@ -94,8 +97,8 @@ Errors returned are not (and cannot be) propagated anywhere except dmesg.
 
 
 
- Part IV: Features
-===================
+Part IV: Features
+=================
 
 For current list of features, see include/linux/netdev_features.h.
 This section describes semantics of some of them.
similarity index 89%
rename from Documentation/networking/netdevices.txt
rename to Documentation/networking/netdevices.rst
index 7fec206..5a85fcc 100644 (file)
@@ -1,5 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0
 
+=====================================
 Network Devices, the Kernel, and You!
+=====================================
 
 
 Introduction
@@ -75,11 +78,12 @@ ndo_start_xmit:
        Don't use it for new drivers.
 
        Context: Process with BHs disabled or BH (timer),
-                will be called with interrupts disabled by netconsole.
+                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 
+       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.
 
@@ -95,10 +99,13 @@ ndo_set_rx_mode:
 struct napi_struct synchronization rules
 ========================================
 napi->poll:
-       Synchronization: NAPI_STATE_SCHED bit in napi->state.  Device
+       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.
+
+       Context:
+                softirq
+                will be called with interrupts disabled by netconsole.
@@ -1,8 +1,15 @@
+.. 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
+       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
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
-
@@ -1,8 +1,15 @@
+.. 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
+       0 - disabled (default)
+       not 0 - enabled
 
        Enable connection tracking flow accounting. 64-bit byte and packet
        counters per flow are added.
@@ -16,8 +23,8 @@ nf_conntrack_buckets - INTEGER
        This sysctl is only writeable in the initial net namespace.
 
 nf_conntrack_checksum - BOOLEAN
-       0 - disabled
-       not 0 - enabled (default)
+       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
@@ -27,8 +34,8 @@ nf_conntrack_count - INTEGER (read-only)
        Number of currently allocated flow entries.
 
 nf_conntrack_events - BOOLEAN
-       0 - disabled
-       not 0 - enabled (default)
+       0 - disabled
+       not 0 - enabled (default)
 
        If this option is enabled, the connection tracking code will
        provide userspace with connection tracking events via ctnetlink.
@@ -62,8 +69,8 @@ nf_conntrack_generic_timeout - INTEGER (seconds)
        protocols.
 
 nf_conntrack_helper - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
+       0 - disabled (default)
+       not 0 - enabled
 
        Enable automatic conntrack helper assignment.
        If disabled it is required to set up iptables rules to assign
@@ -81,14 +88,14 @@ nf_conntrack_icmpv6_timeout - INTEGER (seconds)
        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
+       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.
 
@@ -97,15 +104,15 @@ nf_conntrack_max - INTEGER
        nf_conntrack_buckets value * 4.
 
 nf_conntrack_tcp_be_liberal - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
+       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)
+       0 - disabled
+       not 0 - enabled (default)
 
        If it is set to zero, we disable picking up already established
        connections.
@@ -148,8 +155,8 @@ nf_conntrack_tcp_timeout_unacknowledged - INTEGER (seconds)
        default 300
 
 nf_conntrack_timestamp - BOOLEAN
-       0 - disabled (default)
-       not 0 - enabled
+       0 - disabled (default)
+       not 0 - enabled
 
        Enable connection tracking flow timestamping.
 
similarity index 76%
rename from Documentation/networking/nf_flowtable.txt
rename to Documentation/networking/nf_flowtable.rst
index 0bf32d1..b6e1fa1 100644 (file)
@@ -1,3 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====================================
 Netfilter's flowtable infrastructure
 ====================================
 
@@ -31,15 +34,17 @@ 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  |
-                                    \__________/   \_________/
-                                         ^               |
-                                         |               |
+::
+
+                                        userspace process
+                                         ^              |
+                                         |              |
+                                    _____|____     ____\/___
+                                   /          \   /         \
+                                   |   input   |  |  output  |
+                                   \__________/   \_________/
+                                        ^               |
+                                        |               |
       _________      __________      ---------     _____\/_____
      /         \    /          \     |Routing |   /            \
   -->  ingress  ---> prerouting ---> |decision|   | postrouting |--> neigh_xmit
@@ -59,7 +64,7 @@ including the Netfilter hooks and the flowtable fastpath bypass.
       \ /                                                                 |
        |__yes_________________fastpath bypass ____________________________|
 
-               Fig.1 Netfilter hooks and flowtable interactions
+              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
@@ -72,18 +77,18 @@ Example configuration
 ---------------------
 
 Enabling the flowtable bypass is relatively easy, you only need to create a
-flowtable and add one rule to your forward chain.
+flowtable and add one rule to your forward chain::
 
-        table inet x {
+       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
-                }
-        }
+               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
@@ -101,12 +106,12 @@ 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
+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].
+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
+.. [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
similarity index 95%
rename from Documentation/networking/openvswitch.txt
rename to Documentation/networking/openvswitch.rst
index b3b9ac6..1a8353d 100644 (file)
@@ -1,3 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============================================
 Open vSwitch datapath developer documentation
 =============================================
 
@@ -80,13 +83,13 @@ 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:
+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.:
+Often we ellipsize arguments not important to the discussion, e.g.::
 
     in_port(1), eth(...), eth_type(0x0800), ipv4(...), tcp(...)
 
@@ -151,20 +154,20 @@ 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:
+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:
+essentially like this, ignoring metadata::
 
     eth(...), eth_type(0x8100)
 
@@ -172,7 +175,7 @@ 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:
+flow key much like this::
 
     eth(...), vlan(vid=10, pcp=0), eth_type(0x0800), ip(proto=6, ...), tcp(...)
 
@@ -187,7 +190,7 @@ 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:
+VLAN 10 is actually expressed as::
 
     eth(...), eth_type(0x8100), vlan(vid=10, pcp=0), encap(eth_type(0x0800),
     ip(proto=6, ...), tcp(...)))
@@ -215,14 +218,14 @@ 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:
+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:
+an all-zero-bits vlan and an empty encap attribute, like this::
 
     eth(...), eth_type(0x8100), vlan(0), encap()
 
similarity index 87%
rename from Documentation/networking/operstates.txt
rename to Documentation/networking/operstates.rst
index b203d13..9c918f7 100644 (file)
@@ -1,5 +1,12 @@
+.. 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
@@ -20,6 +27,7 @@ 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
@@ -30,16 +38,20 @@ 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:
 
@@ -47,26 +59,33 @@ 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.
@@ -75,6 +94,7 @@ 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
@@ -126,6 +146,7 @@ netif_carrier_ok() && !netif_dormant():
 
 
 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
@@ -139,18 +160,18 @@ 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
+- 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.
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
-
similarity index 82%
rename from Documentation/networking/phonet.txt
rename to Documentation/networking/phonet.rst
index 8100358..8668dcb 100644 (file)
@@ -1,3 +1,7 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+============================
 Linux Phonet protocol family
 ============================
 
@@ -11,6 +15,7 @@ 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,
@@ -21,7 +26,7 @@ depending on the device, such as:
 Packets format
 --------------
 
-Phonet packets have a common header as follows:
+Phonet packets have a common header as follows::
 
   struct phonethdr {
     uint8_t  pn_media;  /* Media type (link-layer identifier) */
@@ -72,7 +77,7 @@ only the (default) Linux FIFO qdisc should be used with them.
 Network layer
 -------------
 
-The Phonet socket address family maps the Phonet packet header:
+The Phonet socket address family maps the Phonet packet header::
 
   struct sockaddr_pn {
     sa_family_t spn_family;    /* AF_PHONET */
@@ -94,6 +99,8 @@ 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);
@@ -105,7 +112,7 @@ other peer.
 
   sendto(fd, msg, msglen, 0, (struct sockaddr *)&addr, sizeof(addr));
   len = recvfrom(fd, buf, sizeof(buf), 0,
-                 (struct sockaddr *)&addr, &addrlen);
+                (struct sockaddr *)&addr, &addrlen);
 
 This protocol follows the SOCK_DGRAM connection-less semantics.
 However, connect() and getpeername() are not supported, as they did
@@ -116,7 +123,7 @@ Resource subscription
 ---------------------
 
 A Phonet datagram socket can be subscribed to any number of 8-bits
-Phonet resources, as follow:
+Phonet resources, as follow::
 
   uint32_t res = 0xXX;
   ioctl(fd, SIOCPNADDRESOURCE, &res);
@@ -137,6 +144,8 @@ 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);
@@ -161,7 +170,7 @@ Connections are traditionally established between two endpoints by a
 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:
+e.g. the Nokia Slim Modem in the ST-Ericsson U8500 platform::
 
   struct sockaddr_spn spn;
   int fd;
@@ -177,38 +186,45 @@ e.g. the Nokia Slim Modem in the ST-Ericsson U8500 platform:
   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.
+.. 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_NONE:
+      The socket operates normally (default).
 
-    PNPIPE_ENCAP_IP: The socket is used as a backend for a virtual IP
+    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_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.
+  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.
+
+Copyright |copy| 2008 Nokia Corporation.
similarity index 62%
rename from Documentation/networking/pktgen.txt
rename to Documentation/networking/pktgen.rst
index d2fd78f..7afa1c9 100644 (file)
@@ -1,7 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0
 
-
-                  HOWTO for the linux packet generator
-                  ------------------------------------
+====================================
+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
@@ -9,17 +10,18 @@ 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:
+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]
 
-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::
 
-For monitoring and control pktgen creates:
        /proc/net/pktgen/pgctrl
        /proc/net/pktgen/kpktgend_X
-        /proc/net/pktgen/ethX
+       /proc/net/pktgen/ethX
 
 
 Tuning NIC for max performance
@@ -28,7 +30,8 @@ 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:
+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
@@ -46,7 +49,8 @@ This cleanup issue is specifically the case for the driver ixgbe
 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):
+For ixgbe use e.g. "30" resulting in approx 33K interrupts/sec (1/30*10^6)::
+
  # ethtool -C ethX rx-usecs 30
 
 
@@ -55,7 +59,7 @@ 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
+Example: /proc/net/pktgen/kpktgend_0::
 
  Running:
  Stopped: eth4@0
@@ -64,6 +68,7 @@ Example: /proc/net/pktgen/kpktgend_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
 
@@ -73,7 +78,7 @@ 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
+device@something
 
 The part after "@" can be anything, but it is custom to use the thread
 number.
@@ -83,30 +88,30 @@ 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
+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
@@ -114,11 +119,12 @@ 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
+scripts, i.e.::
+
+    export PGDEV=/proc/net/pktgen/eth4@0
+    source samples/pktgen/functions.sh
 
-Examples:
+Examples::
 
  pg_ctrl start           starts injection.
  pg_ctrl stop            aborts injection. Also, ^C aborts generator.
@@ -126,17 +132,17 @@ Examples:
  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
+                        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.
+                        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!)
+                        (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.
@@ -149,46 +155,46 @@ Examples:
 
  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
+                        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.
+                        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.
+                        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
+                        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.
+                        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.
+                        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.
+                        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,
+                                        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.
@@ -232,10 +238,14 @@ 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:
+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
+Usage:::
+
+  ./pktgen_sample01_simple.sh [-vx] -i ethX
+
   -i : ($DEV)       output interface/device (required)
   -s : ($PKT_SIZE)  packet size
   -d : ($DEST_IP)   destination IP
@@ -250,13 +260,13 @@ 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:
+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.
+    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
@@ -271,10 +281,10 @@ 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:
+can be enabled by simply setting::
 
-pgset "flag IPSEC"
-pgset "flows 1"
+    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
@@ -284,115 +294,117 @@ to employ.
 Current commands and configuration options
 ==========================================
 
-** Pgcontrol commands:
+**Pgcontrol commands**::
 
-start
-stop
-reset
+    start
+    stop
+    reset
 
-** Thread commands:
+**Thread commands**::
 
-add_device
-rem_device_all
+    add_device
+    rem_device_all
 
 
-** Device commands:
+**Device commands**::
 
-count
-clone_skb
-burst
-debug
+    count
+    clone_skb
+    burst
+    debug
 
-frags
-delay
+    frags
+    delay
 
-src_mac_count
-dst_mac_count
+    src_mac_count
+    dst_mac_count
 
-pkt_size
-min_pkt_size
-max_pkt_size
+    pkt_size
+    min_pkt_size
+    max_pkt_size
 
-queue_map_min
-queue_map_max
-skb_priority
+    queue_map_min
+    queue_map_max
+    skb_priority
 
-tos           (ipv4)
-traffic_class (ipv6)
+    tos           (ipv4)
+    traffic_class (ipv6)
 
-mpls
+    mpls
 
-udp_src_min
-udp_src_max
+    udp_src_min
+    udp_src_max
 
-udp_dst_min
-udp_dst_max
+    udp_dst_min
+    udp_dst_max
 
-node
+    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
+    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)
+    spi (ipsec)
 
-dst_min
-dst_max
+    dst_min
+    dst_max
 
-src_min
-src_max
+    src_min
+    src_max
 
-dst_mac
-src_mac
+    dst_mac
+    src_mac
 
-clear_counters
+    clear_counters
 
-src6
-dst6
-dst6_max
-dst6_min
+    src6
+    dst6
+    dst6_max
+    dst6_min
 
-flows
-flowlen
+    flows
+    flowlen
 
-rate
-ratep
+    rate
+    ratep
 
-xmit_mode <start_xmit|netif_receive>
+    xmit_mode <start_xmit|netif_receive>
 
-vlan_cfi
-vlan_id
-vlan_p
+    vlan_cfi
+    vlan_id
+    vlan_p
 
-svlan_cfi
-svlan_id
-svlan_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/
+
+- 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
+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.
 
similarity index 92%
rename from Documentation/networking/PLIP.txt
rename to Documentation/networking/plip.rst
index ad7e3f7..0eda745 100644 (file)
@@ -1,4 +1,8 @@
+.. 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
@@ -83,7 +87,7 @@ 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. 
+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
@@ -115,7 +119,7 @@ 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''
+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.
 
@@ -132,7 +136,7 @@ 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:
+two DB-25 male connectors symmetrically connected as follows::
 
     STROBE output      1*
     D0->ERROR  2 - 15          15 - 2
@@ -146,7 +150,8 @@ two DB-25 male connectors symmetrically connected as follows:
     SLCTIN     17 - 17
     extra grounds are 18*,19*,20*,21*,22*,23*,24*
     GROUND     25 - 25
-* Do not connect these pins on either end
+
+    * 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.
@@ -155,14 +160,14 @@ Parallel Transfer Mode 1
 ========================
 
 The second data transfer method relies on both machines having
-bi-directional parallel ports, rather than output-only ``printer''
+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
+``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:
+The cable for this transfer mode should be constructed as follows::
 
     STROBE->BUSY 1 - 11
     D0->D0     2 - 2
@@ -179,7 +184,8 @@ The cable for this transfer mode should be constructed as follows:
     GND->ERROR 18 - 15
     extra grounds are 19*,20*,21*,22*,23*,24*
     GROUND     25 - 25
-* Do not connect these pins on either end
+
+    * 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.
@@ -188,7 +194,7 @@ 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:
+standard in Mode 0.  That standard specifies the following protocol::
 
    send header nibble '0x8'
    count-low octet
@@ -196,20 +202,21 @@ standard in Mode 0.  That standard specifies the following protocol:
    ... data octets
    checksum octet
 
-Each octet is sent as
+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. 
+line.
 
-Restated:
+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
+  (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
similarity index 91%
rename from Documentation/networking/ppp_generic.txt
rename to Documentation/networking/ppp_generic.rst
index fd563af..e605043 100644 (file)
@@ -1,8 +1,12 @@
-               PPP Generic Driver and Channel Interface
-               ----------------------------------------
+.. SPDX-License-Identifier: GPL-2.0
 
-                           Paul Mackerras
+========================================
+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
@@ -19,7 +23,7 @@ functionality which is of use in any PPP implementation, including:
 * 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
+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
@@ -102,7 +106,7 @@ 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
+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
@@ -111,8 +115,8 @@ 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
+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
@@ -121,16 +125,16 @@ 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
+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
+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
@@ -322,6 +326,8 @@ an interface unit are:
   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
@@ -335,6 +341,7 @@ an interface unit are:
        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
@@ -345,17 +352,20 @@ an interface unit are:
   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'
+  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.
 
@@ -377,7 +387,7 @@ an interface unit are:
   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.
+  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.
 
@@ -391,31 +401,33 @@ an interface unit are:
 
 * 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
+  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`` 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
+* 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
+  ``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
similarity index 83%
rename from Documentation/networking/proc_net_tcp.txt
rename to Documentation/networking/proc_net_tcp.rst
index 4a79209..7d9dfe3 100644 (file)
@@ -1,15 +1,21 @@
+.. 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 
+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):
+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 
+   46: 010310AC:9C4C 030310AC:1770 01
    |      |      |      |      |   |--> connection state
    |      |      |      |      |------> remote TCP port number
    |      |      |      |-------------> remote IPv4 address
@@ -17,7 +23,7 @@ up into 3 parts because of the length of the line):
    |      |---------------------------> local IPv4 address
    |----------------------------------> number of entry
 
-   00000150:00000000 01:00000019 00000000  
+   00000150:00000000 01:00000019 00000000
       |        |     |     |       |--> number of unrecovered RTO timeouts
       |        |     |     |----------> number of jiffies until timer expires
       |        |     |----------------> timer_active (see below)
@@ -25,7 +31,7 @@ up into 3 parts because of the length of the line):
       |-------------------------------> transmit-queue
 
    1000        0 54165785 4 cd1e6040 25 4 27 3 -1
-    |          |    |     |    |     |  | |  | |--> slow start size threshold, 
+    |          |    |     |    |     |  | |  | |--> slow start size threshold,
     |          |    |     |    |     |  | |  |      or -1 if the threshold
     |          |    |     |    |     |  | |  |      is >= 0xFFFF
     |          |    |     |    |     |  | |  |----> sending congestion window
@@ -40,9 +46,12 @@ up into 3 parts because of the length of the line):
     |---------------------------------------------> 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 
+  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
+ ==  ================================================================
@@ -1,3 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================
 How to use radiotap headers
 ===========================
 
@@ -5,9 +8,9 @@ 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:
+information you need to know on them from::
 
-./include/net/ieee80211_radiotap.h
+    ./include/net/ieee80211_radiotap.h
 
 This document gives an overview and warns on some corner cases.
 
@@ -21,6 +24,8 @@ 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> ... ]
@@ -76,6 +81,8 @@ ieee80211_radiotap_header.
 Example valid radiotap header
 -----------------------------
 
+::
+
        0x00, 0x00, // <-- radiotap version + pad byte
        0x0b, 0x00, // <- radiotap header length
        0x04, 0x0c, 0x00, 0x00, // <-- bitmap
@@ -89,64 +96,64 @@ 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:
+its prototypes available in include/net/cfg80211.h.  You use it like this::
 
-#include <net/cfg80211.h>
+    #include <net/cfg80211.h>
 
-/* buf points to the start of the radiotap header part */
+    /* 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);
+    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) {
+           while (!ret) {
 
-               ret = ieee80211_radiotap_iterator_next(&iterator);
+                   ret = ieee80211_radiotap_iterator_next(&iterator);
 
-               if (ret)
-                       continue;
+                   if (ret)
+                           continue;
 
-               /* see if this argument is something we can use */
+                   /* 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;
+                   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_ANTENNA:
+                           /* radiotap uses 0 for 1st ant */
+                           antenna = *iterator.this_arg);
+                           break;
 
-               case IEEE80211_RADIOTAP_DBM_TX_POWER:
-                       pwr = *iterator.this_arg;
-                       break;
+                   case IEEE80211_RADIOTAP_DBM_TX_POWER:
+                           pwr = *iterator.this_arg;
+                           break;
 
-               default:
-                       break;
-               }
-       }  /* while more rt headers */
+                   default:
+                           break;
+                   }
+           }  /* while more rt headers */
 
-       if (ret != -ENOENT)
-               return TXRX_DROP;
+           if (ret != -ENOENT)
+                   return TXRX_DROP;
 
-       /* discard the radiotap header part */
-       buf += iterator.max_length;
-       buflen -= iterator.max_length;
+           /* discard the radiotap header part */
+           buf += iterator.max_length;
+           buflen -= iterator.max_length;
 
-       ...
+           ...
 
-}
+    }
 
 Andy Green <andy@warmcat.com>
similarity index 65%
rename from Documentation/networking/ray_cs.txt
rename to Documentation/networking/ray_cs.rst
index c0c1230..9a46d1a 100644 (file)
@@ -1,6 +1,14 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: <isonum.txt>
+
+=========================
+Raylink wireless LAN card
+=========================
+
 September 21, 1999
 
-Copyright (c) 1998  Corey Thomas (corey@world.std.com)
+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
@@ -13,7 +21,7 @@ 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 
+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
@@ -29,6 +37,7 @@ with nondefault parameters, they can be edited in
 will find them all.
 
 Information on card services is available at:
+
        http://pcmcia-cs.sourceforge.net/
 
 
@@ -39,72 +48,78 @@ 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 
+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:
+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"
+  #### 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"
+  card "RayLink PC Card WLAN Adapter"
+    manfid 0x01a6, 0x0000
+    bind "ray_cs"
 
-module "misc/ray_cs" opts ""
-#### end of /etc/pcmcia/ray_cs.opts #####################
+  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 
+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
+=============== =============== =============================================
+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 
+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
+                               Recommended method for controlling
+                               interrupts is in /etc/pcmcia/config.opts
 
-net_type       integer         0 (default) = adhoc network, 
+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
+psm            integer         0 = continuously active,
                                1 = power save mode (not useful yet)
 
 pc_debug       integer         (0-5) larger values for more verbose
@@ -114,14 +129,14 @@ 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.
+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)
+translate      integer         0 = no translation (encapsulate frames),
                                1 = translation    (RFC1042/802.1)
-
+=============== =============== =============================================
 
 More on sniffer mode:
 
@@ -136,7 +151,7 @@ package which parses the 802.11 headers.
 
 Known Problems and missing features
 
-        Does not work with non x86
+       Does not work with non x86
 
        Does not work with SMP
 
similarity index 59%
rename from Documentation/networking/rds.txt
rename to Documentation/networking/rds.rst
index eec6169..44936c2 100644 (file)
@@ -1,3 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==
+RDS
+===
 
 Overview
 ========
@@ -24,36 +29,39 @@ 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).
+       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.
+       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.
+
+       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
+
+       RDS supports a number of sysctls in /proc/sys/net/rds
 
 
 Socket Interface
@@ -66,89 +74,88 @@ Socket Interface
        options.
 
   fd = socket(PF_RDS, SOCK_SEQPACKET, 0);
-        This creates a new, unbound RDS socket.
+       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.
+       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
+       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.
+       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 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 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.
+       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.
+       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.
+       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.
+       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).
+       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.
+       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 ..)
+       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
@@ -180,32 +187,39 @@ RDS Protocol
   Message header
 
     The message header is a 'struct rds_header' (see rds.h):
+
     Fields:
+
       h_sequence:
-          per-packet sequence number
+         per-packet sequence number
       h_ack:
-          piggybacked acknowledgment of last packet received
+         piggybacked acknowledgment of last packet received
       h_len:
-          length of data, not including header
+         length of data, not including header
       h_sport:
-          source port
+         source port
       h_dport:
-          destination port
+         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
+         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)
+         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
+         unused, for future use
       h_csum:
-          header checksum
+         header checksum
       h_exthdr:
-          optional data can be passed here. This is currently used for
-          passing RDMA-related information.
+         optional data can be passed here. This is currently used for
+         passing RDMA-related information.
 
   ACK and retransmit handling
 
@@ -260,7 +274,7 @@ RDS Protocol
 
 
 RDS Transport Layer
-==================
+===================
 
   As mentioned above, RDS is not IB-specific. Its code is divided
   into a general RDS layer and a transport layer.
@@ -281,19 +295,25 @@ RDS Kernel Structures
     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.
 
@@ -317,53 +337,58 @@ 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
+    - 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
+    - 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)
+    - 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
+    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
+    - 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
+    - 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
+    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
+    copy data into user iovec
+    handle CMSGs
+    return to application
 
 Multipath RDS (mprds)
 =====================
similarity index 94%
rename from Documentation/networking/regulatory.txt
rename to Documentation/networking/regulatory.rst
index 381e5b2..8701b91 100644 (file)
@@ -1,5 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======================================
 Linux wireless regulatory documentation
----------------------------------------
+=======================================
 
 This document gives a brief review over how the Linux wireless
 regulatory infrastructure works.
@@ -57,7 +60,7 @@ Users can use iw:
 
 http://wireless.kernel.org/en/users/Documentation/iw
 
-An example:
+An example::
 
   # set regulatory domain to "Costa Rica"
   iw reg set CR
@@ -104,9 +107,9 @@ 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:
+domain value to a specific alpha2 as follows::
 
-static struct zd_reg_alpha2_map reg_alpha2_map[] = {
+  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 */
@@ -116,10 +119,10 @@ static struct zd_reg_alpha2_map reg_alpha2_map[] = {
        { ZD_REGDOMAIN_FRANCE, "FR" },
 
 Then you can define a routine to map your read EEPROM value to an alpha2,
-as follows:
+as follows::
 
-static int zd_reg2alpha2(u8 regdomain, char *alpha2)
-{
+  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++) {
@@ -131,12 +134,14 @@ static int zd_reg2alpha2(u8 regdomain, char *alpha2)
                }
        }
        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);
@@ -156,9 +161,9 @@ 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
+Example cache of some regulatory domain::
 
-struct ieee80211_regdomain mydriver_jp_regdom = {
+  struct ieee80211_regdomain mydriver_jp_regdom = {
        .n_reg_rules = 3,
        .alpha2 =  "JP",
        //.alpha2 =  "99", /* If I have no alpha2 to map it to */
@@ -173,9 +178,9 @@ struct ieee80211_regdomain mydriver_jp_regdom = {
                        NL80211_RRF_NO_IR|
                        NL80211_RRF_DFS),
        }
-};
+  };
 
-Then in some part of your code after your wiphy has been registered:
+Then in some part of your code after your wiphy has been registered::
 
        struct ieee80211_regdomain *rd;
        int size_of_regd;
similarity index 85%
rename from Documentation/networking/rxrpc.txt
rename to Documentation/networking/rxrpc.rst
index 180e07d..5ad3511 100644 (file)
@@ -1,6 +1,8 @@
-                           ======================
-                           RxRPC NETWORK PROTOCOL
-                           ======================
+.. 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
@@ -9,36 +11,35 @@ receive data, aborts and errors.
 
 Contents of this document:
 
- (*) Overview.
+ (#) Overview.
 
- (*) RxRPC protocol summary.
+ (#) RxRPC protocol summary.
 
- (*) AF_RXRPC driver model.
+ (#) AF_RXRPC driver model.
 
- (*) Control messages.
+ (#) Control messages.
 
- (*) Socket options.
+ (#) Socket options.
 
- (*) Security.
+ (#) Security.
 
- (*) Example client usage.
+ (#) Example client usage.
 
- (*) Example server usage.
+ (#) Example server usage.
 
- (*) AF_RXRPC kernel interface.
+ (#) AF_RXRPC kernel interface.
 
- (*) Configurable parameters.
+ (#) Configurable parameters.
 
 
-========
-OVERVIEW
+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):
+(as does SunRPC)::
 
                +-------------+
                | Application |
@@ -85,31 +86,30 @@ 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
+RxRPC Protocol Summary
 ======================
 
 An overview of the RxRPC protocol:
 
- (*) RxRPC sits on top of another networking protocol (UDP is the only option
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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:
+     connection is described by seven numbers::
 
        Local address   }
        Local port      } Transport (UDP) address
@@ -119,22 +119,22 @@ An overview of the RxRPC protocol:
        Connection ID
        Service ID
 
- (*) Each RxRPC operation is a "call".  A connection may make up to four
+ (#) 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,
+ (#) 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
+ (#) 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.
+ (#) The first four bytes of the request data are the service operation ID.
 
- (*) Security is negotiated on a per-connection basis.  The connection is
+ (#) 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
@@ -143,146 +143,145 @@ An overview of the RxRPC protocol:
      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
+ (#) 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.
+ (#) 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
+ (#) 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
+ (#) 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.
+ (#) An call may be aborted by either end at any time up to its completion.
 
 
-=====================
-AF_RXRPC DRIVER MODEL
+AF_RXRPC Driver Model
 =====================
 
 About the AF_RXRPC driver:
 
- (*) The AF_RXRPC protocol transparently uses internal sockets of the transport
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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.
+ (#) A server-side connection is shared if the client says it is.
 
- (*) ACK'ing is handled by the protocol driver automatically, including ping
+ (#) ACK'ing is handled by the protocol driver automatically, including ping
      replying.
 
- (*) SO_KEEPALIVE automatically pings the other side to keep the connection
+ (#) 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
+ (#) 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
+ (#) 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,
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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,
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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,
+ (#) 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 control data part of the msghdr struct is used for a number of things:
 
-     (*) The tag of the intended or affected call.
+     (#) The tag of the intended or affected call.
 
-     (*) Sending or receiving errors, aborts and busy notifications.
+     (#) Sending or receiving errors, aborts and busy notifications.
 
-     (*) Notifications of incoming calls.
+     (#) Notifications of incoming calls.
 
-     (*) Sending debug requests and receiving debug replies [TODO].
+     (#) Sending debug requests and receiving debug replies [TODO].
 
- (*) When the kernel has received and set up an incoming call, it sends a
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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.
@@ -294,7 +293,7 @@ Notes on sendmsg:
 
 Notes on recvmsg:
 
- (*) If there's a sequence of data messages belonging to a particular call on
+ (#) 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,
@@ -320,13 +319,13 @@ Notes on recvmsg:
      flagged.
 
 
-================
-CONTROL MESSAGES
+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
@@ -340,10 +339,11 @@ calls, to invoke certain actions and to report certain conditions.  These are:
        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
+ (#) 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
@@ -351,7 +351,7 @@ calls, to invoke certain actions and to report certain conditions.  These are:
      message.  recvmsg() passes it in conjunction with all messages except
      those of the RXRPC_NEW_CALL message.
 
- (*) RXRPC_ABORT
+ (#) 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
@@ -359,13 +359,13 @@ calls, to invoke certain actions and to report certain conditions.  These are:
      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
+ (#) 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
+ (#) 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
@@ -373,13 +373,13 @@ calls, to invoke certain actions and to report certain conditions.  These are:
      indicating the problem, and an RXRPC_USER_CALL_ID will indicate the call
      affected.
 
- (*) RXRPC_BUSY
+ (#) 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
+ (#) 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
@@ -387,13 +387,13 @@ calls, to invoke certain actions and to report certain conditions.  These are:
      indicating the problem, and an RXRPC_USER_CALL_ID will indicate the call
      affected.
 
- (*) RXRPC_NEW_CALL
+ (#) 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
+ (#) 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
@@ -402,12 +402,12 @@ calls, to invoke certain actions and to report certain conditions.  These are:
      return error ENODATA.  If the user ID is already in use by another call,
      then error EBADSLT will be returned.
 
- (*) RXRPC_EXCLUSIVE_CALL
+ (#) 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
+ (#) 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
@@ -419,7 +419,7 @@ calls, to invoke certain actions and to report certain conditions.  These are:
      future communication to that server and RXRPC_UPGRADE_SERVICE should no
      longer be set.
 
- (*) RXRPC_TX_LENGTH
+ (#) 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
@@ -443,7 +443,7 @@ SOCKET OPTIONS
 
 AF_RXRPC sockets support a few socket options at the SOL_RXRPC level:
 
- (*) RXRPC_SECURITY_KEY
+ (#) 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
@@ -452,17 +452,17 @@ AF_RXRPC sockets support a few socket options at the SOL_RXRPC level:
      The optval pointer points to the description string, and optlen indicates
      how long the string is, without the NUL terminator.
 
- (*) RXRPC_SECURITY_KEYRING
+ (#) 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
+ (#) 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
+ (#) 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
@@ -482,14 +482,14 @@ AF_RXRPC sockets support a few socket options at the SOL_RXRPC level:
         Encrypted checksum plus entire packet padded and encrypted, including
         actual packet length.
 
- (*) RXRPC_UPGRADEABLE_SERVICE
+ (#) 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
+ (#) RXRPC_SUPPORTED_CMSG
 
      This is a read-only option that writes an int into the buffer indicating
      the highest control message type supported.
@@ -509,7 +509,7 @@ 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:
+form::
 
        struct rxrpc_key_sec2_v1 {
                uint16_t        security_index; /* 2 */
@@ -546,14 +546,14 @@ EXAMPLE CLIENT USAGE
 
 A client would issue an operation by:
 
- (1) An RxRPC socket is set up 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:
+ (2) A local address can optionally be bound::
 
        struct sockaddr_rxrpc srx = {
                .srx_family     = AF_RXRPC,
@@ -570,20 +570,20 @@ A client would issue an operation by:
      several unrelated RxRPC sockets.  Security is handled on a basis of
      per-RxRPC virtual connection.
 
- (3) The security is set:
+ (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:
+     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):
+     be done through sendmsg)::
 
        struct sockaddr_rxrpc srx = {
                .srx_family     = AF_RXRPC,
@@ -598,7 +598,9 @@ A client would issue an operation by:
  (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
+       ==================      ===================================
+       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.
@@ -635,13 +637,12 @@ any more calls (further calls to the same destination will be blocked until the
 probe is concluded).
 
 
-====================
-EXAMPLE SERVER USAGE
+Example Server Usage
 ====================
 
 A server would be set up to accept operations in the following manner:
 
- (1) An RxRPC socket is created by:
+ (1) An RxRPC socket is created by::
 
        server = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);
 
@@ -649,7 +650,7 @@ A server would be set up to accept operations in the following manner:
      socket used - usually IPv4.
 
  (2) Security is set up if desired by giving the socket a keyring with server
-     secret keys in it:
+     secret keys in it::
 
        keyring = add_key("keyring", "AFSkeys", NULL, 0,
                          KEY_SPEC_PROCESS_KEYRING);
@@ -663,7 +664,7 @@ A server would be set up to accept operations in the following manner:
      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:
+ (3) A local address must then be bound::
 
        struct sockaddr_rxrpc srx = {
                .srx_family     = AF_RXRPC,
@@ -680,7 +681,7 @@ A server would be set up to accept operations in the following manner:
      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:
+     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,
@@ -690,14 +691,14 @@ A server would be set up to accept operations in the following manner:
      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:
+ (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:
+     attached::
 
        RXRPC_NEW_CALL
 
@@ -709,8 +710,10 @@ A server would be set up to accept operations in the following manner:
  (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
+       ==================      ==============================
+       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
@@ -722,12 +725,17 @@ A server would be set up to accept operations in the following manner:
 
      All data will be delivered with the following control message attached:
 
-       RXRPC_USER_CALL_ID      - specifies the user ID for this call
+
+       ==================      ===================================
+       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
+       ==================      ===================================
+       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.
@@ -736,8 +744,10 @@ A server would be set up to accept operations in the following manner:
      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)
+       ==================      ===================================
+       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.
@@ -746,8 +756,10 @@ A server would be set up to accept operations in the following manner:
      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)
+       ==================      ===================================
+       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.
@@ -757,8 +769,7 @@ the one server socket, using control messages on sendmsg() and recvmsg() to
 determine the call affected.
 
 
-=========================
-AF_RXRPC KERNEL INTERFACE
+AF_RXRPC Kernel Interface
 =========================
 
 The AF_RXRPC module also provides an interface for use by in-kernel utilities
@@ -786,7 +797,7 @@ then it passes this to the kernel interface functions.
 
 The kernel interface functions are as follows:
 
- (*) Begin a new client call.
+ (#) Begin a new client call::
 
        struct rxrpc_call *
        rxrpc_kernel_begin_call(struct socket *sock,
@@ -837,7 +848,7 @@ The kernel interface functions are as follows:
      returned.  The caller now holds a reference on this and it must be
      properly ended.
 
- (*) End a client call.
+ (#) End a client call::
 
        void rxrpc_kernel_end_call(struct socket *sock,
                                   struct rxrpc_call *call);
@@ -846,7 +857,7 @@ The kernel interface functions are as follows:
      from AF_RXRPC's knowledge and will not be seen again in association with
      the specified call.
 
- (*) Send data through a call.
+ (#) Send data through a call::
 
        typedef void (*rxrpc_notify_end_tx_t)(struct sock *sk,
                                              unsigned long user_call_ID,
@@ -872,7 +883,7 @@ The kernel interface functions are as follows:
      called with the call-state spinlock held to prevent any reply or final ACK
      from being delivered first.
 
- (*) Receive data from a call.
+ (#) Receive data from a call::
 
        int rxrpc_kernel_recv_data(struct socket *sock,
                                   struct rxrpc_call *call,
@@ -902,12 +913,14 @@ The kernel interface functions are as follows:
       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.
+      ``*_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.
+ (#) Abort a call??
+
+     ::
 
        void rxrpc_kernel_abort_call(struct socket *sock,
                                     struct rxrpc_call *call,
@@ -916,7 +929,7 @@ The kernel interface functions are as follows:
      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.
+ (#) Intercept received RxRPC messages::
 
        typedef void (*rxrpc_interceptor_t)(struct sock *sk,
                                            unsigned long user_call_ID,
@@ -937,7 +950,8 @@ The kernel interface functions are as follows:
 
      The skb->mark field indicates the type of message:
 
-       MARK                            MEANING
+       =============================== =======================================
+       Mark                            Meaning
        =============================== =======================================
        RXRPC_SKB_MARK_DATA             Data message
        RXRPC_SKB_MARK_FINAL_ACK        Final ACK received for an incoming call
@@ -946,6 +960,7 @@ The kernel interface functions are as follows:
        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().
@@ -961,7 +976,7 @@ The kernel interface functions are as follows:
      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.
+ (#) Accept an incoming call::
 
        struct rxrpc_call *
        rxrpc_kernel_accept_call(struct socket *sock,
@@ -975,7 +990,7 @@ The kernel interface functions are as follows:
      returned.  The caller now holds a reference on this and it must be
      properly ended.
 
- (*) Reject an incoming call.
+ (#) Reject an incoming call::
 
        int rxrpc_kernel_reject_call(struct socket *sock);
 
@@ -984,21 +999,21 @@ The kernel interface functions are as follows:
      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.
+ (#) 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.
+ (#) 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.
+ (#) Set the total transmit data size on a call::
 
        void rxrpc_kernel_set_tx_length(struct socket *sock,
                                        struct rxrpc_call *call,
@@ -1009,14 +1024,14 @@ The kernel interface functions are as follows:
      size should be set when the call is begun.  tx_total_len may not be less
      than zero.
 
- (*) Get call RTT.
+ (#) 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.
+ (#) Check call still alive::
 
        bool rxrpc_kernel_check_life(struct socket *sock,
                                     struct rxrpc_call *call,
@@ -1024,7 +1039,7 @@ The kernel interface functions are as follows:
        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
+     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
@@ -1040,7 +1055,7 @@ The kernel interface functions are as follows:
      first function to change.  Note that this must be called in TASK_RUNNING
      state.
 
- (*) Get reply timestamp.
+ (#) Get reply timestamp::
 
        bool rxrpc_kernel_get_reply_time(struct socket *sock,
                                         struct rxrpc_call *call,
@@ -1048,10 +1063,10 @@ The kernel interface functions are as follows:
 
      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
+     successful, the timestamp will be stored into ``*_ts`` and true will be
      returned; false will be returned otherwise.
 
- (*) Get remote client epoch.
+ (#) Get remote client epoch::
 
        u32 rxrpc_kernel_get_epoch(struct socket *sock,
                                   struct rxrpc_call *call)
@@ -1065,7 +1080,7 @@ The kernel interface functions are as follows:
      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.
+ (#) Set the maxmimum lifespan on a call::
 
        void rxrpc_kernel_set_max_life(struct socket *sock,
                                       struct rxrpc_call *call,
@@ -1076,14 +1091,13 @@ The kernel interface functions are as follows:
      aborted and -ETIME or -ETIMEDOUT will be returned.
 
 
-=======================
-CONFIGURABLE PARAMETERS
+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
+ (#) 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
@@ -1093,60 +1107,60 @@ adjusted through sysctls in /proc/net/rxrpc/:
      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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
+ (#) 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
similarity index 64%
rename from Documentation/networking/sctp.txt
rename to Documentation/networking/sctp.rst
index 97b810c..9f4d9c8 100644 (file)
@@ -1,35 +1,42 @@
-Linux Kernel SCTP 
+.. SPDX-License-Identifier: GPL-2.0
+
+=================
+Linux Kernel SCTP
+=================
 
 This is the current BETA release of the Linux Kernel SCTP reference
-implementation.  
+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.  
+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 
+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.    
+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:  
+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.   
+- 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.   
+- 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>
similarity index 87%
rename from Documentation/networking/secid.txt
rename to Documentation/networking/secid.rst
index 95ea067..b45141a 100644 (file)
@@ -1,3 +1,9 @@
+.. 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
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.
similarity index 68%
rename from Documentation/networking/skfp.txt
rename to Documentation/networking/skfp.rst
index 203ec66..58f5481 100644 (file)
@@ -1,35 +1,41 @@
-(C)Copyright 1998-2000 SysKonnect,
-===========================================================================
+.. 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
+.. 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
-============
+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.
+this driver.
 
-Chapter 3: Gives some general information.
+Chapter 3:
+          Gives some general information.
 
 Chapter 4: Describes common problems and solutions.
 
@@ -37,14 +43,13 @@ Chapter 5: Shows the changed functionality of the adapter LEDs.
 
 Chapter 6: History of development.
 
-***
-
 
-(2) SUPPORTED ADAPTERS
-======================
+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)
@@ -55,157 +60,187 @@ SysKonnect adapters:
   - 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
-=======================
+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 :-)
-***
 
+Makes my life much easier :-)
 
-(4) TROUBLESHOOTING
-===================
+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 ...'
+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
+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
+
+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
+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.
+5. Function of the Adapter LEDs
+===============================
 
-        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.
+       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.
 
-        All implementations indicate that a driver is not loaded if
-        all LEDs are off.
+       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
-===========
+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).
+
+       - tar archive instead of standard package format (rpm).
        - FDDI statistic is empty.
        - not tested with 2.1.xx kernels
        - integration in kernel not tested
@@ -216,5 +251,3 @@ v1.00 beta 01 (980507)
        - 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
 
similarity index 80%
rename from Documentation/networking/strparser.txt
rename to Documentation/networking/strparser.rst
index a7d354d..6cab1f7 100644 (file)
@@ -1,4 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================
 Stream Parser (strparser)
+=========================
 
 Introduction
 ============
@@ -34,8 +38,10 @@ that is called when a full message has been completed.
 Functions
 =========
 
-strp_init(struct strparser *strp, struct sock *sk,
-         const struct strp_callbacks *cb)
+     ::
+
+       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
@@ -43,31 +49,41 @@ strp_init(struct strparser *strp, struct sock *sk,
      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)
+     ::
+
+       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)
+     ::
+
+       void strp_unpause(struct strparser *strp)
 
      Unpause a paused stream parser.
 
-void strp_stop(struct strparser *strp);
+     ::
+
+       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);
+     ::
+
+       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)
+     ::
+
+       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
@@ -75,7 +91,9 @@ int strp_process(struct strparser *strp, struct sk_buff *orig_skb,
     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);
+    ::
+
+       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
@@ -83,7 +101,9 @@ void strp_data_ready(struct strparser *strp);
     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);
+    ::
+
+       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
@@ -94,7 +114,9 @@ Callbacks
 
 There are six callbacks:
 
-int (*parse_msg)(struct strparser *strp, struct sk_buff *skb);
+    ::
+
+       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
@@ -107,14 +129,16 @@ int (*parse_msg)(struct strparser *strp, struct sk_buff *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)
+    =========    ===========================================================
+    >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
@@ -123,7 +147,9 @@ int (*parse_msg)(struct strparser *strp, struct sk_buff *skb);
     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)
+    ::
+
+       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
@@ -131,14 +157,18 @@ void (*lock)(struct strparser *strp)
     function is to lock_sock for the associated socket. In general
     mode the callback must be set appropriately.
 
-void (*unlock)(struct strparser *strp)
+    ::
+
+       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);
+    ::
+
+       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
@@ -152,7 +182,9 @@ void (*rcv_msg)(struct strparser *strp, struct sk_buff *skb);
     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);
+    ::
+
+       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
@@ -160,7 +192,9 @@ int (*read_sock_done)(struct strparser *strp, int err);
      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);
+     ::
+
+       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
@@ -204,4 +238,3 @@ Author
 ======
 
 Tom Herbert (tom@quantonium.net)
-
similarity index 84%
rename from Documentation/networking/switchdev.txt
rename to Documentation/networking/switchdev.rst
index 86174ce..ddc3f35 100644 (file)
@@ -1,7 +1,13 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+===============================================
 Ethernet switch device driver model (switchdev)
 ===============================================
-Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
-Copyright (c) 2014-2015 Scott Feldman <sfeldma@gmail.com>
+
+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
@@ -12,53 +18,57 @@ 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 tools
 
        user space                   |
       +-------------------------------------------------------------------+
        kernel                       | Netlink
-                                    |
-                     +--------------+-------------------------------+
-                     |         Network stack                        |
-                     |           (Linux)                            |
-                     |                                              |
-                     +----------------------------------------------+
-
-                           sw1p2     sw1p4     sw1p6
-                      sw1p1  +  sw1p3  +  sw1p5  +          eth1
-                        +    |    +    |    +    |            +
-                        |    |    |    |    |    |            |
-                     +--+----+----+----+----+----+---+  +-----+-----+
-                     |         Switch driver         |  |    mgmt   |
-                     |        (this document)        |  |   driver  |
-                     |                               |  |           |
-                     +--------------+----------------+  +-----------+
-                                    |
+                                   |
+                    +--------------+-------------------------------+
+                    |         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
+                    +--------------+----------------+
+                    |         Switch device (sw1)   |
+                    |  +----+                       +--------+
+                    |  |    v offloaded data path   | mgmt port
+                    |  |    |                       |
+                    +--|----|----+----+----+----+---+
+                       |    |    |    |    |    |
+                       +    +    +    +    +    +
+                      p1   p2   p3   p4   p5   p6
 
-                             front-panel ports
+                            front-panel ports
 
 
-                                    Fig 1.
+                                   Fig 1.
 
 
 Include Files
 -------------
 
-#include <linux/netdevice.h>
-#include <net/switchdev.h>
+::
+
+    #include <linux/netdevice.h>
+    #include <net/switchdev.h>
 
 
 Configuration
@@ -114,10 +124,10 @@ 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:
+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}"
+    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
@@ -173,7 +183,7 @@ 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:
+entries are installed, for example, using iproute2 bridge cmd::
 
        bridge fdb add ADDR dev DEV [vlan VID] [self]
 
@@ -185,7 +195,7 @@ 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:
+traffic.  To enable VLAN support, turn on VLAN filtering::
 
        echo 1 >/sys/class/net/<bridge>/bridge/vlan_filtering
 
@@ -194,7 +204,7 @@ 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:
+in turn, will notify the bridge driver using the switchdev notifier call::
 
        err = call_switchdev_notifiers(val, dev, info, extack);
 
@@ -202,7 +212,7 @@ 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":
+command will label these entries "offload"::
 
        $ bridge fdb
        52:54:00:12:35:01 dev sw1p1 master br0 permanent
@@ -219,11 +229,11 @@ command will label these entries "offload":
        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:
+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:
+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
@@ -314,12 +324,16 @@ 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:
+===================  ===================================================
+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 */
@@ -332,12 +346,12 @@ FIB_EVENT_ENTRY_ADD and FIB_EVENT_ENTRY_DEL events pass:
                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.
+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:
+listing::
 
        $ ip route show
        default via 192.168.0.2 dev eth0
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.
-
similarity index 97%
rename from Documentation/networking/tcp-thin.txt
rename to Documentation/networking/tcp-thin.rst
index 151e229..b06765c 100644 (file)
@@ -1,5 +1,9 @@
+.. 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
@@ -42,6 +46,7 @@ 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
similarity index 67%
rename from Documentation/networking/team.txt
rename to Documentation/networking/team.rst
index 5a01368..0a7f3a0 100644 (file)
@@ -1,2 +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
similarity index 89%
rename from Documentation/networking/timestamping.txt
rename to Documentation/networking/timestamping.rst
index 8dd6333..1adead6 100644 (file)
@@ -1,9 +1,16 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============
+Timestamping
+============
+
 
 1. Control Interfaces
+=====================
 
 The interfaces for receiving network packages timestamps are:
 
-SO_TIMESTAMP
+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.
@@ -13,7 +20,7 @@ The interfaces for receiving network packages timestamps are:
   SO_TIMESTAMP_OLD and in struct __kernel_sock_timeval for
   SO_TIMESTAMP_NEW options respectively.
 
-SO_TIMESTAMPNS
+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
@@ -22,17 +29,18 @@ The interfaces for receiving network packages timestamps are:
   and in struct __kernel_timespec for SO_TIMESTAMPNS_NEW options
   respectively.
 
-IP_MULTICAST_LOOP + SO_TIMESTAMP[NS]
+IP_MULTICAST_LOOP + SO_TIMESTAMP[NS]
   Only for multicast:approximate transmit timestamp obtained by
   reading the looped packet receive timestamp.
 
-SO_TIMESTAMPING
+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):
+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
@@ -59,10 +67,11 @@ 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):
+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
+socket option takes a bitmap of flags, not a boolean. In::
 
   err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));
 
@@ -76,6 +85,7 @@ 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
@@ -106,7 +116,6 @@ SOF_TIMESTAMPING_TX_SOFTWARE:
   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
@@ -132,6 +141,7 @@ SOF_TIMESTAMPING_TX_ACK:
 
 
 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
@@ -151,11 +161,11 @@ SOF_TIMESTAMPING_RAW_HARDWARE:
 
 
 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
@@ -183,7 +193,6 @@ SOF_TIMESTAMPING_OPT_ID:
 
 
 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
@@ -193,7 +202,6 @@ SOF_TIMESTAMPING_OPT_CMSG:
 
 
 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
@@ -202,7 +210,6 @@ SOF_TIMESTAMPING_OPT_TSONLY:
   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
@@ -213,7 +220,6 @@ SOF_TIMESTAMPING_OPT_STATS:
   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
@@ -223,7 +229,6 @@ SOF_TIMESTAMPING_OPT_PKTINFO:
   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,
@@ -242,12 +247,13 @@ 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:
+setsockopt::
 
   struct msghdr *msg;
   ...
@@ -264,7 +270,7 @@ 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:
+setsockopt to receive timestamps::
 
   __u32 val = SOF_TIMESTAMPING_SOFTWARE |
              SOF_TIMESTAMPING_OPT_ID /* or any other flag */;
@@ -272,6 +278,7 @@ setsockopt to receive timestamps:
 
 
 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
@@ -331,6 +338,7 @@ 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
@@ -339,20 +347,21 @@ 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:
+For SO_TIMESTAMPING_OLD::
 
-struct scm_timestamping {
-       struct timespec ts[3];
-};
+       struct scm_timestamping {
+               struct timespec ts[3];
+       };
 
-For SO_TIMESTAMPING_NEW:
+For SO_TIMESTAMPING_NEW::
 
-struct scm_timestamping64 {
-       struct __kernel_timespec ts[3];
+       struct scm_timestamping64 {
+               struct __kernel_timespec ts[3];
 
 Always use SO_TIMESTAMPING_NEW timestamp to always get timestamp in
 struct scm_timestamping64 format.
@@ -377,6 +386,7 @@ 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
@@ -393,6 +403,7 @@ 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
@@ -408,6 +419,7 @@ 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,
@@ -416,6 +428,7 @@ 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
@@ -427,6 +440,7 @@ 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
@@ -436,6 +450,7 @@ 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
@@ -447,16 +462,17 @@ 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:
+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_* */
-};
+       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
@@ -487,44 +503,47 @@ 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
-        */
-};
+::
+
+    /* 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
@@ -533,22 +552,23 @@ 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:
+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;
-};
+    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
+  SKBTX_IN_PROGRESS in skb_shinfo(skb)->tx_flags , e.g. with::
 
       skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
 
similarity index 70%
rename from Documentation/networking/tproxy.txt
rename to Documentation/networking/tproxy.rst
index b9a1888..00dc3a1 100644 (file)
@@ -1,3 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================
 Transparent proxy support
 =========================
 
@@ -11,39 +14,39 @@ From Linux 4.18 transparent proxy support is also available in nf_tables.
 ================================
 
 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:
+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
+    # 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:
+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
+    # 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:
+delivered locally::
 
-# ip rule add fwmark 1 lookup 100
-# ip route add local 0.0.0.0/0 dev lo table 100
+    # 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));
+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
@@ -61,10 +64,10 @@ 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:
+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
+    # iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TPROXY \
+      --tproxy-mark 0x1/0x1 --on-port 50080
 
 Or the following rule to nft:
 
@@ -82,10 +85,12 @@ nf_tables implementation.
 ====================================
 
 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
 
similarity index 58%
rename from Documentation/networking/tuntap.txt
rename to Documentation/networking/tuntap.rst
index 0104830..a59d1dd 100644 (file)
@@ -1,20 +1,28 @@
-Universal TUN/TAP device driver.
-Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
 
-  Linux, Solaris drivers 
-  Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
+===============================
+Universal TUN/TAP device driver
+===============================
 
-  FreeBSD TAP driver 
-  Copyright (c) 1999-2000 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+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. 
+==============
+
+  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. 
+  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
@@ -33,41 +41,51 @@ Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
   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:
+2. Configuration
+================
+
+  Create device node::
+
      mkdir /dev/net (if it doesn't exist already)
      mknod /dev/net/tun c 10 200
-  
-  Set permissions:
+
+  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.
+
+  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
+
+  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:
+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")
+``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>
@@ -78,45 +96,51 @@ Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
       int fd, err;
 
       if( (fd = open("/dev/net/tun", O_RDWR)) < 0 )
-         return tun_alloc_old(dev);
+        return tun_alloc_old(dev);
 
       memset(&ifr, 0, sizeof(ifr));
 
-      /* Flags: IFF_TUN   - TUN device (no Ethernet headers) 
-       *        IFF_TAP   - TAP device  
+      /* 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; 
+       *        IFF_NO_PI - Do not provide packet information
+       */
+      ifr.ifr_flags = IFF_TUN;
       if( *dev )
-         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+        strncpy(ifr.ifr_name, dev, IFNAMSIZ);
 
       if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
-         close(fd);
-         return err;
+        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: 
+  }
+
+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:
+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.
 
-  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.
 
-  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>
@@ -127,7 +151,7 @@ Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
       int fd, err, i;
 
       if (!dev)
-          return -1;
+         return -1;
 
       memset(&ifr, 0, sizeof(ifr));
       /* Flags: IFF_TUN   - TUN device (no Ethernet headers)
@@ -140,30 +164,30 @@ Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
       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;
+         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]);
+         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.
+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
+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>
@@ -175,53 +199,61 @@ Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
       memset(&ifr, 0, sizeof(ifr));
 
       if (enable)
-         ifr.ifr_flags = IFF_ATTACH_QUEUE;
+        ifr.ifr_flags = IFF_ATTACH_QUEUE;
       else
-         ifr.ifr_flags = IFF_DETACH_QUEUE;
+        ifr.ifr_flags = IFF_DETACH_QUEUE;
 
       return ioctl(fd, TUNSETQUEUE, (void *)&ifr);
   }
 
-Universal TUN/TAP device driver Frequently Asked Question.
-   
+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
+
+  - 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. 
+
+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 ? 
+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. 
+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 
+(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, 
+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. 
+
+Yes. Linux and FreeBSD drivers support Ethernet bridging.
similarity index 65%
rename from Documentation/networking/udplite.txt
rename to Documentation/networking/udplite.rst
index 53a7268..2c225f2 100644 (file)
@@ -1,6 +1,8 @@
-  ===========================================================================
-                      The UDP-Lite protocol (RFC 3828)
-  ===========================================================================
+.. SPDX-License-Identifier: GPL-2.0
+
+================================
+The UDP-Lite protocol (RFC 3828)
+================================
 
 
   UDP-Lite is a Standards-Track IETF transport protocol whose characteristic
   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.
+   - 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.
 
-   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
+   - 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
 
-   o The Wireshark UDP-Lite WiKi (with capture files):
-       https://wiki.wireshark.org/Lightweight_User_Datagram_Protocol
+   - 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
+   - The Protocol Spec, RFC 3828, http://www.ietf.org/rfc/rfc3828.txt
 
 
-  I) APPLICATIONS
+1. Applications
+===============
 
   Several applications have been ported successfully to UDP-Lite. Ethereal
-  (now called wireshark) has UDP-Litev4/v6 support by default. 
+  (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
+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:
+  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,
+  or, respectively,
+
+  ::
 
       s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE);
 
 
     * Sender checksum coverage: UDPLITE_SEND_CSCOV
 
-      For example,
+      For example::
 
-        int val = 20;
-        setsockopt(s, SOL_UDPLITE, UDPLITE_SEND_CSCOV, &val, sizeof(int));
+       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
       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:
+      packets with a minimum coverage of 20 are admitted::
 
-        int min = 20;
-        setsockopt(s, SOL_UDPLITE, UDPLITE_RECV_CSCOV, &min, sizeof(int));
+       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
 
   A detailed discussion of UDP-Lite checksum coverage options is in section IV.
 
-
-  III) HEADER FILES
+3. Header Files
+===============
 
   The socket API requires support through header files in /usr/include:
 
     * /usr/include/netinet/in.h
-        to define IPPROTO_UDPLITE
+      to define IPPROTO_UDPLITE
 
     * /usr/include/netinet/udplite.h
-        for UDP-Lite header fields and protocol constants
+      for UDP-Lite header fields and protocol constants
 
-  For testing purposes, the following can serve as a `mini' header file:
+  For testing purposes, the following can serve as a ``mini`` header file::
 
     #define IPPROTO_UDPLITE       136
     #define SOL_UDPLITE           136
 
   Ready-made header files for various distros are in the UDP-Lite tarball.
 
+4. Kernel Behaviour with Regards to the Various Socket Options
+==============================================================
 
-  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).
   3) Disabling the Checksum Computation
 
   On both sender and receiver, checksumming will always be performed
-  and cannot be disabled using SO_NO_CHECK. Thus
+  and cannot be disabled using SO_NO_CHECK. Thus::
 
-        setsockopt(sockfd, SOL_SOCKET, SO_NO_CHECK,  ... );
+       setsockopt(sockfd, SOL_SOCKET, SO_NO_CHECK,  ... );
 
-  will always will be ignored, while the value of
+  will always will be ignored, while the value of::
 
-        getsockopt(sockfd, SOL_SOCKET, SO_NO_CHECK, &value, ...);
+       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.
   first one contains the L4 header.
 
   The send buffer size has implications on the checksum coverage length.
-  Consider the following example:
+  Consider the following example::
 
-  Payload: 1536 bytes          Send Buffer:     1024 bytes
-  MTU:     1500 bytes          Coverage Length:  856 bytes
+    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:
+  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
+    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
   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.
+  several tiny fragments, consider the following example::
 
-  Payload: 1024 bytes            Send buffer size: 1024 bytes
-  MTU:      300 bytes            Coverage length:   575 bytes
+    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*************
+    +-+-----------+--------------+--------------+--------------+
+    |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
   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:
+  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
   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
+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
+  and can (with newer versions of netstat) be viewed using::
 
-                            netstat -svu
+                           netstat -svu
 
   This displays UDP-Lite statistics variables, whose meaning is as follows.
 
-   InDatagrams:     The total number of datagrams delivered to users.
+   ============     =====================================================
+   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).
+   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
+   InErrors         Number of erroneous UDP-Lite packets. Errors include:
 
-   OutDatagrams:    Total number of sent datagrams.
+                     * 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
 
-   These statistics derive from the UDP MIB (RFC 2013).
+   OutDatagrams     Total number of sent datagrams.
+   ============     =====================================================
 
+   These statistics derive from the UDP MIB (RFC 2013).
 
-  VI) IPTABLES
+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,
+  If you copy and paste the following line into /etc/protocols::
 
-  udplite 136     UDP-Lite        # UDP-Lite [RFC 3828]
+    udplite 136     UDP-Lite        # UDP-Lite [RFC 3828]
 
-  then
-              iptables -A INPUT -p udplite -j LOG
+  then::
 
-  will produce logging output to syslog. Dropping and rejecting packets also works.
+             iptables -A INPUT -p udplite -j LOG
 
+  will produce logging output to syslog. Dropping and rejecting packets also works.
 
-  VII) MAINTAINER ADDRESS
+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
+
+                   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
similarity index 73%
rename from Documentation/networking/vxlan.txt
rename to Documentation/networking/vxlan.rst
index c28f498..ce239fa 100644 (file)
@@ -1,3 +1,6 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======================================================
 Virtual eXtensible Local Area Networking documentation
 ======================================================
 
@@ -21,8 +24,9 @@ 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
+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
@@ -32,20 +36,25 @@ 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
+2. Delete vxlan device::
+
+    # ip link delete vxlan0
 
-3. Show vxlan info
-  # ip -d link show 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
+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
 
-2. Delete forwarding table entry
-  # bridge fdb delete 00:17:42:8a:b4:05 dev vxlan0
+3. Show forwarding table::
 
-3. Show forwarding table
-  # bridge fdb show dev vxlan0
+    # bridge fdb show dev vxlan0
similarity index 96%
rename from Documentation/networking/x25-iface.txt
rename to Documentation/networking/x25-iface.rst
index 7f213b5..df40189 100644 (file)
@@ -1,4 +1,10 @@
-                       X.25 Device Driver Interface 1.1
+.. SPDX-License-Identifier: GPL-2.0
+
+============================-
+X.25 Device Driver Interface
+============================-
+
+Version 1.1
 
                           Jonathan Naylor 26.12.96
 
@@ -99,7 +105,7 @@ 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. 
+    to the peer.
     This will reliably suppress packet loss. The LAPB protocol will
     automatically cause the peer to re-transmit the dropped packet
     later.
similarity index 96%
rename from Documentation/networking/x25.txt
rename to Documentation/networking/x25.rst
index c91c6d7..00e45d3 100644 (file)
@@ -1,4 +1,8 @@
+.. 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
similarity index 92%
rename from Documentation/networking/xfrm_device.txt
rename to Documentation/networking/xfrm_device.rst
index a1c904d..da1073a 100644 (file)
@@ -1,7 +1,9 @@
+.. SPDX-License-Identifier: GPL-2.0
 
 ===============================================
 XFRM device - offloading the IPsec computations
 ===============================================
+
 Shannon Nelson <shannon.nelson@oracle.com>
 
 
@@ -19,7 +21,7 @@ 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:
+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 \
@@ -34,15 +36,17 @@ 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 {
+::
+
+  /* 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
@@ -58,6 +62,8 @@ 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;
@@ -65,16 +71,20 @@ The XFRM code's listener will finish the setup on NETDEV_REGISTER.
 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.
+that can be used to convey context into the fast-path offload requests::
 
                xs->xso.offload_handle = context;
 
@@ -88,7 +98,7 @@ 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.
+send accordingly::
 
                xs = xfrm_input_state(skb);
                context = xs->xso.offload_handle;
@@ -105,18 +115,21 @@ 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
+       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
+       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
+       indicate the success and/or error status of the offload::
+
                xo = xfrm_offload(skb);
                xo->flags = CRYPTO_DONE;
                xo->status = crypto_status;
@@ -136,5 +149,3 @@ 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.
-
-
similarity index 95%
rename from Documentation/networking/xfrm_proc.txt
rename to Documentation/networking/xfrm_proc.rst
index 2eae619..0a771c5 100644 (file)
@@ -1,5 +1,9 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================================
 XFRM proc - /proc/net/xfrm_* files
 ==================================
+
 Masahide NAKAMURA <nakam@linux-ipv6.org>
 
 
@@ -14,42 +18,58 @@ as part of the linux private MIB.  These counters can be viewed in
 
 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
 
@@ -57,26 +77,37 @@ 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
similarity index 82%
rename from Documentation/networking/xfrm_sync.txt
rename to Documentation/networking/xfrm_sync.rst
index 8d88e0f..6246503 100644 (file)
@@ -1,3 +1,8 @@
+.. 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
@@ -40,30 +45,32 @@ 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:
+aevent_id structure looks like::
 
    struct xfrm_aevent_id {
-             struct xfrm_usersa_id           sa_id;
-             xfrm_address_t                  saddr;
-             __u32                           flags;
-             __u32                           reqid;
+            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 */
+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).
@@ -80,23 +87,27 @@ to get notified of these events.
 -----------------------------------------
 
 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
@@ -108,6 +119,7 @@ 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)
@@ -120,37 +132,45 @@ in incremental packet count. The default is two packets.
 ----------------
 
 a) XFRM_MSG_GETAE issued by user-->kernel.
-XFRM_MSG_GETAE does not carry any TLVs.
+   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
+* 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.
+   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.
+     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.
+    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.
similarity index 52%
rename from Documentation/networking/xfrm_sysctl.txt
rename to Documentation/networking/xfrm_sysctl.rst
index 5bbd167..47b9bbd 100644 (file)
@@ -1,4 +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
similarity index 57%
rename from Documentation/networking/z8530drv.txt
rename to Documentation/networking/z8530drv.rst
index 2206abb..d294276 100644 (file)
@@ -1,33 +1,30 @@
+.. 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
+    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
+    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 <<
+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
 ===============================
@@ -50,7 +47,7 @@ AX.25-HOWTO on how to emulate a KISS TNC on network device drivers.
 (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
+Before you can use a module, you'll have to load it with::
 
        insmod scc.o
 
@@ -75,61 +72,73 @@ The file itself consists of two main sections.
 ==========================================
 
 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
+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
+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
+         default), measured in Hertz
 
-board   - the "type" of the board:
+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")
+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!
+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)
+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
+just change::
 
        #define MAXSCC 4
 
@@ -138,75 +147,81 @@ 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
+::
+
+       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
+::
+
+       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
+::
+
+       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).
@@ -220,17 +235,19 @@ 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:
+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 
+       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
+::
+
+       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... 
+to edit scc_config.h...
 
 
 1.2.2 channel configuration
@@ -239,58 +256,58 @@ to edit scc_config.h...
 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
+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... 
+(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 
+    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
@@ -299,15 +316,15 @@ security holes...
 2.1 Kernel AX.25
 ================
 
-To set up an AX.25 device you can simply type:
+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 
+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.
 
@@ -318,16 +335,16 @@ 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:
+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:
+Now "ifconfig" the scc device::
 
        ifconfig scc3 44.128.1.1 hw ax25 dl0tha-9
 
-You can now axattach a pseudo-TTY:
+You can now axattach a pseudo-TTY::
 
        axattach /dev/ptys0 axlink
 
@@ -335,11 +352,11 @@ 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:
+and outgoing frames from "axlink" to "9k6" and start::
 
        rxecho
 
-Or simply use "kissbridge" coming with z8530drv-utils:
+Or simply use "kissbridge" coming with z8530drv-utils::
 
        ifconfig scc3 hw ax25 dl0tha-9
        kissbridge scc3 /dev/ptys0
@@ -351,55 +368,57 @@ Or simply use "kissbridge" coming with z8530drv-utils:
 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:
+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
+       dl1bke-u:~$ sccstat scc0
 
-Parameters:
+       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
+       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:
+       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
+       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
+============== ==============================================================
+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
@@ -411,32 +430,34 @@ driver or the kernel AX.25.
 ======================
 
 
-The setting of parameters of the emulated KISS TNC is done in the 
+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":
+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
+===========   =====
+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:
@@ -447,92 +468,92 @@ speed:
      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 
+     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 
+     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) 
+     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 
+     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 
+     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.
+     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 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. 
+     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 
+     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 
+     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. 
+     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 
+     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 
+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.
@@ -541,7 +562,7 @@ idle
 
 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 
+     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 ;-)
 
@@ -555,32 +576,38 @@ txoff:
      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 
+     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 
+     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 
+
+     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.
+
+     ===  =======================================================
+     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 
+
+     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, 
+
+     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
@@ -595,19 +622,19 @@ softdcd:
      Example: sccparam /dev/scc0 soft on
 
 
-4. Problems 
+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. 
+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 
+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.
@@ -651,7 +678,9 @@ 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
+::
+
+       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 88bf36a..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
@@ -193,7 +193,7 @@ 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
@@ -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
@@ -1274,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
@@ -3191,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
@@ -4693,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
@@ -4727,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
@@ -7814,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
@@ -7866,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
@@ -8741,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
@@ -8933,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/
@@ -9514,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/
 
@@ -10078,7 +10078,7 @@ S:      Maintained
 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
@@ -11655,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
@@ -13261,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
@@ -14030,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
 
@@ -14218,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
@@ -14592,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
@@ -14998,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
@@ -15873,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
@@ -16970,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
@@ -17160,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
@@ -18105,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
@@ -18643,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 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 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 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 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;
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 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 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 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 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 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 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 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 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 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 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 2210759..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 {
@@ -5699,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;
 
@@ -5778,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;
 
@@ -5964,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;
 
@@ -6070,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;
                }
@@ -6258,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:
@@ -6271,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);
 }
@@ -6449,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 */
similarity index 55%
rename from drivers/net/ethernet/mellanox/mlx5/core/qp.c
rename to drivers/infiniband/hw/mlx5/qpc.c
index c3aea4c..ea62735 100644 (file)
@@ -1,46 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
 /*
- * Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved.
- *
- * This software is available to you under a choice of one of two
- * licenses.  You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available from the file
- * COPYING in the main directory of this source tree, or the
- * OpenIB.org BSD license below:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      - Redistributions of source code must retain the above
- *        copyright notice, this list of conditions and the following
- *        disclaimer.
- *
- *      - Redistributions in binary form must reproduce the above
- *        copyright notice, this list of conditions and the following
- *        disclaimer in the documentation and/or other materials
- *        provided with the distribution.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
+ * Copyright (c) 2013-2020, Mellanox Technologies inc. All rights reserved.
  */
 
 #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_ib.h"
+#include "qp.h"
 
-#include "mlx5_core.h"
-#include "lib/eq.h"
-
-static int mlx5_core_drain_dct(struct mlx5_core_dev *dev,
+static int mlx5_core_drain_dct(struct mlx5_ib_dev *dev,
                               struct mlx5_core_dct *dct);
 
 static struct mlx5_core_rsc_common *
@@ -124,11 +93,9 @@ static int rsc_event_notifier(struct notifier_block *nb,
 {
        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;
 
@@ -155,22 +122,12 @@ static int rsc_event_notifier(struct notifier_block *nb,
        }
 
        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);
+       if (!common)
                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);
+       if (!is_event_type_allowed((rsn >> MLX5_USER_INDEX_LEN), event_type))
                goto out;
-       }
 
        switch (common->res) {
        case MLX5_RES_QP:
@@ -185,7 +142,7 @@ static int rsc_event_notifier(struct notifier_block *nb,
                        complete(&dct->drained);
                break;
        default:
-               mlx5_core_warn(dev, "invalid resource type for 0x%x\n", rsn);
+               break;
        }
 out:
        mlx5_core_put_rsc(common);
@@ -193,11 +150,10 @@ out:
        return NOTIFY_OK;
 }
 
-static int create_resource_common(struct mlx5_core_dev *dev,
-                                 struct mlx5_core_qp *qp,
-                                 int rsc_type)
+static int create_resource_common(struct mlx5_ib_dev *dev,
+                                 struct mlx5_core_qp *qp, int rsc_type)
 {
-       struct mlx5_qp_table *table = &dev->priv.qp_table;
+       struct mlx5_qp_table *table = &dev->qp_table;
        int err;
 
        qp->common.res = rsc_type;
@@ -216,10 +172,10 @@ static int create_resource_common(struct mlx5_core_dev *dev,
        return 0;
 }
 
-static void destroy_resource_common(struct mlx5_core_dev *dev,
+static void destroy_resource_common(struct mlx5_ib_dev *dev,
                                    struct mlx5_core_qp *qp)
 {
-       struct mlx5_qp_table *table = &dev->priv.qp_table;
+       struct mlx5_qp_table *table = &dev->qp_table;
        unsigned long flags;
 
        spin_lock_irqsave(&table->lock, flags);
@@ -230,24 +186,19 @@ static void destroy_resource_common(struct mlx5_core_dev *dev,
        wait_for_completion(&qp->common.free);
 }
 
-static int _mlx5_core_destroy_dct(struct mlx5_core_dev *dev,
+static int _mlx5_core_destroy_dct(struct mlx5_ib_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};
+       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->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
+               if (dev->mdev->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;
-               }
+
+               return err;
        }
        wait_for_completion(&dct->drained);
 destroy:
@@ -256,15 +207,12 @@ destroy:
        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));
+       err = mlx5_cmd_exec_in(dev->mdev, destroy_dct, in);
        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)
+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;
@@ -272,11 +220,9 @@ int mlx5_core_create_dct(struct mlx5_core_dev *dev,
        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);
+       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);
@@ -289,108 +235,83 @@ 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,
+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)] = {0};
-       u32 dout[MLX5_ST_SZ_DW(destroy_qp_out)];
-       u32 din[MLX5_ST_SZ_DW(destroy_qp_in)];
+       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, in, inlen, out, sizeof(out));
+       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);
-       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);
+       mlx5_debug_qp_add(dev->mdev, qp);
 
        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));
+       mlx5_cmd_exec_in(dev->mdev, destroy_qp, din);
        return err;
 }
-EXPORT_SYMBOL_GPL(mlx5_core_create_qp);
 
-static int mlx5_core_drain_dct(struct mlx5_core_dev *dev,
+static int mlx5_core_drain_dct(struct mlx5_ib_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};
+       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(dev, (void *)&in, sizeof(in),
-                            (void *)&out, sizeof(out));
+       return mlx5_cmd_exec_in(dev->mdev, drain_dct, in);
 }
 
-int mlx5_core_destroy_dct(struct mlx5_core_dev *dev,
+int mlx5_core_destroy_dct(struct mlx5_ib_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)
+int mlx5_core_destroy_qp(struct mlx5_ib_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;
+       u32 in[MLX5_ST_SZ_DW(destroy_qp_in)] = {};
 
-       mlx5_debug_qp_remove(dev, qp);
+       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);
-       err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
-       if (err)
-               return err;
-
-       atomic_dec(&dev->num_qps);
+       mlx5_cmd_exec_in(dev->mdev, destroy_qp, in);
        return 0;
 }
-EXPORT_SYMBOL_GPL(mlx5_core_destroy_qp);
 
-int mlx5_core_set_delay_drop(struct mlx5_core_dev *dev,
+int mlx5_core_set_delay_drop(struct mlx5_ib_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};
+       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(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev->mdev, set_delay_drop_params, in);
 }
-EXPORT_SYMBOL_GPL(mlx5_core_set_delay_drop);
 
 struct mbox_info {
        u32 *in;
@@ -496,120 +417,112 @@ static int modify_qp_mbox_alloc(struct mlx5_core_dev *dev, u16 opcode, int 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)
+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, opcode, qp->qpn,
+       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, mbox.in, mbox.inlen, mbox.out, mbox.outlen);
+       err = mlx5_cmd_exec(dev->mdev, 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)
+int mlx5_init_qp_table(struct mlx5_ib_dev *dev)
 {
-       struct mlx5_qp_table *table = &dev->priv.qp_table;
+       struct mlx5_qp_table *table = &dev->qp_table;
 
-       memset(table, 0, sizeof(*table));
        spin_lock_init(&table->lock);
        INIT_RADIX_TREE(&table->tree, GFP_ATOMIC);
-       mlx5_qp_debugfs_init(dev);
+       mlx5_qp_debugfs_init(dev->mdev);
 
        table->nb.notifier_call = rsc_event_notifier;
-       mlx5_notifier_register(dev, &table->nb);
+       mlx5_notifier_register(dev->mdev, &table->nb);
+
+       return 0;
 }
 
-void mlx5_cleanup_qp_table(struct mlx5_core_dev *dev)
+void mlx5_cleanup_qp_table(struct mlx5_ib_dev *dev)
 {
-       struct mlx5_qp_table *table = &dev->priv.qp_table;
+       struct mlx5_qp_table *table = &dev->qp_table;
 
-       mlx5_notifier_unregister(dev, &table->nb);
-       mlx5_qp_debugfs_cleanup(dev);
+       mlx5_notifier_unregister(dev->mdev, &table->nb);
+       mlx5_qp_debugfs_cleanup(dev->mdev);
 }
 
-int mlx5_core_qp_query(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp,
+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)] = {0};
+       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, in, sizeof(in), out, outlen);
+       return mlx5_cmd_exec(dev->mdev, 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,
+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)] = {0};
+       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, (void *)&in, sizeof(in),
-                            (void *)out, outlen);
+       return mlx5_cmd_exec(dev->mdev, (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)
+int mlx5_core_xrcd_alloc(struct mlx5_ib_dev *dev, u32 *xrcdn)
 {
-       u32 out[MLX5_ST_SZ_DW(alloc_xrcd_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(alloc_xrcd_in)]   = {0};
+       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(dev, in, sizeof(in), out, sizeof(out));
+       err = mlx5_cmd_exec_inout(dev->mdev, alloc_xrcd, in, 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)
+int mlx5_core_xrcd_dealloc(struct mlx5_ib_dev *dev, u32 xrcdn)
 {
-       u32 out[MLX5_ST_SZ_DW(dealloc_xrcd_out)] = {0};
-       u32 in[MLX5_ST_SZ_DW(dealloc_xrcd_in)]   = {0};
+       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(dev, in, sizeof(in), out, sizeof(out));
+       return mlx5_cmd_exec_in(dev->mdev, dealloc_xrcd, in);
 }
-EXPORT_SYMBOL_GPL(mlx5_core_xrcd_dealloc);
 
-static void destroy_rq_tracked(struct mlx5_core_dev *dev, u32 rqn, u16 uid)
+static void destroy_rq_tracked(struct mlx5_ib_dev *dev, u32 rqn, u16 uid)
 {
-       u32 in[MLX5_ST_SZ_DW(destroy_rq_in)]   = {};
-       u32 out[MLX5_ST_SZ_DW(destroy_rq_out)] = {};
+       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(dev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(dev->mdev, destroy_rq, in);
 }
 
-int mlx5_core_create_rq_tracked(struct mlx5_core_dev *dev, u32 *in, int inlen,
+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, in, inlen, &rqn);
+       err = mlx5_core_create_rq(dev->mdev, in, inlen, &rqn);
        if (err)
                return err;
 
@@ -626,39 +539,37 @@ err_destroy_rq:
 
        return err;
 }
-EXPORT_SYMBOL(mlx5_core_create_rq_tracked);
 
-void mlx5_core_destroy_rq_tracked(struct mlx5_core_dev *dev,
+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);
 }
-EXPORT_SYMBOL(mlx5_core_destroy_rq_tracked);
 
-static void destroy_sq_tracked(struct mlx5_core_dev *dev, u32 sqn, u16 uid)
+static void destroy_sq_tracked(struct mlx5_ib_dev *dev, u32 sqn, u16 uid)
 {
-       u32 in[MLX5_ST_SZ_DW(destroy_sq_in)]   = {};
-       u32 out[MLX5_ST_SZ_DW(destroy_sq_out)] = {};
+       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(dev, in, sizeof(in), out, sizeof(out));
+       mlx5_cmd_exec_in(dev->mdev, destroy_sq, in);
 }
 
-int mlx5_core_create_sq_tracked(struct mlx5_core_dev *dev, u32 *in, int inlen,
+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;
-       u32 sqn;
 
-       err = mlx5_core_create_sq(dev, in, inlen, &sqn);
+       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);
-       sq->qpn = sqn;
        err = create_resource_common(dev, sq, MLX5_RES_SQ);
        if (err)
                goto err_destroy_sq;
@@ -670,68 +581,25 @@ err_destroy_sq:
 
        return err;
 }
-EXPORT_SYMBOL(mlx5_core_create_sq_tracked);
 
-void mlx5_core_destroy_sq_tracked(struct mlx5_core_dev *dev,
+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);
 }
-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,
+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->priv.qp_table;
+       struct mlx5_qp_table *table = &dev->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 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 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 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 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 661c25e..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"
index c283593..ceb8be6 100644 (file)
@@ -1484,8 +1484,7 @@ static int b53_arl_rw_op(struct b53_device *dev, unsigned int op)
 }
 
 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;
@@ -1495,10 +1494,10 @@ static int b53_arl_read(struct b53_device *dev, u64 mac,
        if (ret)
                return ret;
 
-       bitmap_zero(free_bins, dev->num_arl_entries);
+       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;
 
@@ -1521,10 +1520,10 @@ static int b53_arl_read(struct b53_device *dev, u64 mac,
                return 0;
        }
 
-       if (bitmap_weight(free_bins, dev->num_arl_entries) == 0)
+       if (bitmap_weight(free_bins, dev->num_arl_bins) == 0)
                return -ENOSPC;
 
-       *idx = find_first_bit(free_bins, dev->num_arl_entries);
+       *idx = find_first_bit(free_bins, dev->num_arl_bins);
 
        return -ENOENT;
 }
@@ -1550,7 +1549,8 @@ 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;
@@ -1692,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)
@@ -1702,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;
 }
@@ -2185,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;
@@ -2204,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,
        },
@@ -2213,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,
        },
@@ -2222,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,
@@ -2234,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,
@@ -2246,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,
@@ -2258,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,
@@ -2270,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,
@@ -2282,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,
@@ -2294,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,
@@ -2306,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,
@@ -2318,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,
@@ -2330,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,
@@ -2342,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,
@@ -2354,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,
@@ -2366,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,
@@ -2378,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,
@@ -2390,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,
@@ -2402,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,
@@ -2414,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,
@@ -2442,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 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 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 e2c6bf0..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>
@@ -495,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
@@ -505,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);
@@ -530,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)
@@ -549,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);
 }
@@ -740,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 9af1065..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 {
index 8bf395f..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),
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 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 9e1860d..7df67bf 100644 (file)
@@ -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 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 8a70ffe..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), },
+
        {}
 };
 
@@ -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..04bc53a 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;
 
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 d1a8371..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;
        }
@@ -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;
@@ -10265,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++;
                }
        }
 }
@@ -10894,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");
@@ -10901,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");
@@ -11829,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;
@@ -11889,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 |
index f6a3250..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;
 
@@ -1356,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
@@ -1815,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 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 cea2f99..3a9a51f 100644 (file)
@@ -645,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 79636c7..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);
@@ -1045,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 |
@@ -1069,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. */
@@ -2669,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;
 }
 
@@ -2710,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,
@@ -2721,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 */
@@ -2766,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);
@@ -3421,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;
@@ -3437,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) {
@@ -3504,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);
 
@@ -3528,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
@@ -3546,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))
@@ -3612,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;
 
@@ -3628,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
         */
@@ -3638,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 */
@@ -3653,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);
 
@@ -3698,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;
@@ -3710,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 },
@@ -3744,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 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 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 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 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..172acb6 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(sfi->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 e74dd1f..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 */
@@ -543,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;
index dc6f876..2e20914 100644 (file)
@@ -976,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);
 
@@ -1123,7 +1123,7 @@ 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);
@@ -1652,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);
 
@@ -1659,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;
 }
 
@@ -1818,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);
 
@@ -1830,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;
 
@@ -1843,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;
                }
 
@@ -1866,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;
        }
 
@@ -1888,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);
 
@@ -1898,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;
 
@@ -1911,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 {
@@ -1931,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);
@@ -2065,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.
@@ -2095,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) {
@@ -2130,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;
@@ -2146,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)
@@ -3674,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;
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 0d51cbc..05bc6e2 100644 (file)
@@ -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 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 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 0904710..f6a1f86 100644 (file)
@@ -65,7 +65,7 @@ u32 mtk_r32(struct mtk_eth *eth, unsigned reg)
        return __raw_readl(eth->base + reg);
 }
 
-u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned reg)
+static u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned reg)
 {
        u32 val;
 
@@ -1122,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 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..216e6b2 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,
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 a30edb4..9dff7b0 100644 (file)
@@ -392,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;
@@ -415,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);
 
@@ -492,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);
 
@@ -504,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)
@@ -524,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 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 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 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 5ce6ebb..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
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 23701c0..0864b76 100644 (file)
@@ -370,7 +370,7 @@ enum {
        MLX5E_SQ_STATE_PENDING_XSK_TX,
 };
 
-struct mlx5e_sq_wqe_info {
+struct mlx5e_icosq_wqe_info {
        u8  opcode;
        u8 num_wqebbs;
 
@@ -552,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 */
@@ -1013,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;
@@ -1103,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 a172c5e..5568ded 100644 (file)
@@ -73,9 +73,7 @@ struct mlx5_ct_ft {
 struct mlx5_ct_entry {
        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];
@@ -384,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) {
@@ -603,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;
 
@@ -687,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;
 }
@@ -1131,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)
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)
@@ -138,23 +138,6 @@ mlx5e_xdp_no_room_for_inline_pkt(struct mlx5e_xdp_mpwqe *session)
 }
 
 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,
                         struct mlx5e_xdpsq_stats *stats)
@@ -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 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 b314adf..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;
+
+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));
 
-       mlx5e_netdev_set_tcs(netdev);
-       netif_set_real_num_tx_queues(netdev, num_txqs);
-       netif_set_real_num_rx_queues(netdev, num_rxqs);
+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;
        }
@@ -5002,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,
@@ -5363,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)
index f372e94..1eac7a5 100644 (file)
@@ -2051,26 +2051,22 @@ static int register_devlink_port(struct mlx5_core_dev *dev,
                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,
                                       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,
                                              pfnum);
-               dl_port_index = rep->vport;
-       } else if (mlx5_eswitch_is_vf_vport(dev->priv.eswitch,
-                                           rpriv->rep->vport)) {
+       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,
                                              pfnum, rep->vport - 1);
-               dl_port_index = vport_to_devlink_port_index(dev, rep->vport);
-       }
 
        return devlink_port_register(devlink, &rpriv->dl_port, dl_port_index);
 }
index e2beb89..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;
@@ -618,19 +596,21 @@ int 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;
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 a574c58..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;
@@ -1824,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,
@@ -1863,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;
@@ -1905,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;
        }
@@ -2661,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;
@@ -2794,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);
@@ -4707,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;
@@ -4802,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;
@@ -4840,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;
@@ -4854,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);
@@ -4893,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 acb2021..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);
 }
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 c1848b5..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;
index 5d9def1..57ac2ef 100644 (file)
@@ -784,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;
 
@@ -792,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;
 
@@ -808,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;
@@ -1468,7 +1468,7 @@ query_vports:
 out:
        *mode = mlx5_mode;
        return 0;
-}       
+}
 
 static void esw_destroy_restore_table(struct mlx5_eswitch *esw)
 {
@@ -1484,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;
@@ -1894,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;
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 f99e175..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"
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);
 
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 18719ac..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,12 +693,6 @@ 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)
-{
-       pr_info("CQ event %u on CQ #%u\n", event, mcq->cqn);
-}
-
 static void dr_cq_complete(struct mlx5_core_cq *mcq,
                           struct mlx5_eqe *eqe)
 {
@@ -761,7 +759,6 @@ 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));
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 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 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 01cff71..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)
 {
@@ -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 = {
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 890b078..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)
@@ -54,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) {
@@ -107,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;
                        }
@@ -191,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;
@@ -372,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)
 {
@@ -461,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;
                }
@@ -506,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;
@@ -553,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;
@@ -575,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;
@@ -612,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;
@@ -633,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 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 f70bb81..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);
 
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 02350c3..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>
@@ -1348,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 |
@@ -1996,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
@@ -2535,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);
@@ -2556,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 641af92..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
 
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);
index 7d4fd1b..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),
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 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 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 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 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 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 2e4aaed..2ac9dfb 100644 (file)
@@ -83,13 +83,9 @@ static int intel_serdes_powerup(struct net_device *ndev, void *priv_data)
        serdes_phy_addr = intel_priv->mdio_adhoc_addr;
 
        /* assert clk_req */
-       data = mdiobus_read(priv->mii, serdes_phy_addr,
-                           SERDES_GCR0);
-
+       data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0);
        data |= SERDES_PLL_CLK;
-
-       mdiobus_write(priv->mii, serdes_phy_addr,
-                     SERDES_GCR0, data);
+       mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
 
        /* check for clk_ack assertion */
        data = serdes_status_poll(priv, serdes_phy_addr,
@@ -103,13 +99,9 @@ static int intel_serdes_powerup(struct net_device *ndev, void *priv_data)
        }
 
        /* assert lane reset */
-       data = mdiobus_read(priv->mii, serdes_phy_addr,
-                           SERDES_GCR0);
-
+       data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0);
        data |= SERDES_RST;
-
-       mdiobus_write(priv->mii, serdes_phy_addr,
-                     SERDES_GCR0, data);
+       mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
 
        /* check for assert lane reset reflection */
        data = serdes_status_poll(priv, serdes_phy_addr,
@@ -123,14 +115,12 @@ static int intel_serdes_powerup(struct net_device *ndev, void *priv_data)
        }
 
        /*  move power state to P0 */
-       data = mdiobus_read(priv->mii, serdes_phy_addr,
-                           SERDES_GCR0);
+       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);
+       mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
 
        /* Check for P0 state */
        data = serdes_status_poll(priv, serdes_phy_addr,
@@ -159,14 +149,12 @@ static void intel_serdes_powerdown(struct net_device *ndev, void *intel_data)
        serdes_phy_addr = intel_priv->mdio_adhoc_addr;
 
        /*  move power state to P3 */
-       data = mdiobus_read(priv->mii, serdes_phy_addr,
-                           SERDES_GCR0);
+       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);
+       mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
 
        /* Check for P3 state */
        data = serdes_status_poll(priv, serdes_phy_addr,
@@ -180,13 +168,9 @@ static void intel_serdes_powerdown(struct net_device *ndev, void *intel_data)
        }
 
        /* de-assert clk_req */
-       data = mdiobus_read(priv->mii, serdes_phy_addr,
-                           SERDES_GCR0);
-
+       data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0);
        data &= ~SERDES_PLL_CLK;
-
-       mdiobus_write(priv->mii, serdes_phy_addr,
-                     SERDES_GCR0, data);
+       mdiobus_write(priv->mii, serdes_phy_addr, SERDES_GCR0, data);
 
        /* check for clk_ack de-assert */
        data = serdes_status_poll(priv, serdes_phy_addr,
@@ -200,13 +184,9 @@ static void intel_serdes_powerdown(struct net_device *ndev, void *intel_data)
        }
 
        /* de-assert lane reset */
-       data = mdiobus_read(priv->mii, serdes_phy_addr,
-                           SERDES_GCR0);
-
+       data = mdiobus_read(priv->mii, serdes_phy_addr, SERDES_GCR0);
        data &= ~SERDES_RST;
-
-       mdiobus_write(priv->mii, serdes_phy_addr,
-                     SERDES_GCR0, data);
+       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,
@@ -252,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;
@@ -324,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;
@@ -341,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,
@@ -366,7 +347,7 @@ static int ehl_sgmii_data(struct pci_dev *pdev,
        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,
 };
 
@@ -380,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,
 };
 
@@ -399,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,
 };
 
@@ -412,7 +393,7 @@ static int ehl_pse0_sgmii1g_data(struct pci_dev *pdev,
        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,
 };
 
@@ -431,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,
 };
 
@@ -444,23 +425,18 @@ static int ehl_pse1_sgmii1g_data(struct pci_dev *pdev,
        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,
@@ -474,7 +450,7 @@ static int tgl_sgmii_data(struct pci_dev *pdev,
        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,
 };
 
@@ -577,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,
 };
 
@@ -600,11 +576,9 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
        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);
+       intel_priv = devm_kzalloc(&pdev->dev, sizeof(*intel_priv), GFP_KERNEL);
        if (!intel_priv)
                return -ENOMEM;
 
@@ -631,15 +605,9 @@ 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);
 
@@ -650,14 +618,23 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
        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;
 }
 
 /**
@@ -671,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);
 }
@@ -742,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 = {
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 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 a999d6b..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
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 8e34878..988e907 100644 (file)
@@ -99,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
@@ -109,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
@@ -137,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 2517ffb..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;
 
@@ -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;
@@ -1900,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 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 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 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 ebcfbae..5de57fc 100644 (file)
@@ -2457,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 b671bea..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);
        }
@@ -1434,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;
@@ -1453,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,
@@ -1530,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;
@@ -1542,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);
@@ -1628,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;
 
@@ -1671,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() */
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 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,
 };
@@ -148,16 +147,6 @@ 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
  * @offset:    Offset of IPA memory to be read or written
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 a21534f..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,
@@ -1309,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);
        }
@@ -1374,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);
@@ -1388,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,
@@ -1445,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);
        }
 
@@ -1468,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 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 d0d31cb..20b53e2 100644 (file)
@@ -4049,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 0482adc..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;
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 ae4873f..97201d5 100644 (file)
@@ -782,6 +782,19 @@ static struct phy_driver broadcom_drivers[] = {
        .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,
        .name           = "Broadcom BCM89610",
@@ -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 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 1f1a01c..d4c2e62 100644 (file)
@@ -753,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,
@@ -771,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..38bf40e 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;
 
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 7a4eb3f..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);
        }
@@ -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 3a4d83f..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,
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;
@@ -1458,6 +1462,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);
 
@@ -1045,32 +1045,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()
  * @up: indicates whether the link is currently up.
@@ -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 04845a4..8c1e027 100644 (file)
@@ -1647,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 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 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 56f8aab..43928a1 100644 (file)
@@ -867,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 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 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 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 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 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;
@@ -118,10 +94,47 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio,
         * 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 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 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 52d77db..7711651 100644 (file)
@@ -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 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 569966b..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.
@@ -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 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 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 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 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 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 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 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 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 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 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 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>
 
@@ -266,6 +267,17 @@ static inline void eth_hw_addr_random(struct net_device *dev)
 }
 
 /**
+ * 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
  * @src: Pointer to a six-byte array Ethernet address source
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 45cc10c..5ee9e58 100644 (file)
@@ -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 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 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 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 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 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 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,
@@ -3908,6 +3922,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 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 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 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 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 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 4f8159e..e60db06 100644 (file)
@@ -217,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 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 dc23657..1eaaa93 100644 (file)
@@ -2,6 +2,10 @@
 #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>
 
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 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 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 70e48f6..46ac804 100644 (file)
@@ -5211,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 efc8350..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,
 };
 
@@ -258,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 */
 };
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 9947eb1..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,
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 3bce201..e602756 100644 (file)
@@ -68,6 +68,7 @@ static inline bool rsk_is_mptcp(const struct request_sock *req)
        return tcp_rsk(req)->is_mptcp;
 }
 
+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);
 bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
@@ -194,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 4ff7c81..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;
 };
 
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 8428aa6..ab87a8b 100644 (file)
@@ -711,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 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 dcf9a72..43b87a8 100644 (file)
@@ -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 efc8b61..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
 };
 
@@ -550,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 {
@@ -621,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);
similarity index 52%
rename from drivers/net/ethernet/mscc/ocelot_ptp.h
rename to include/soc/mscc/ocelot_ptp.h
index 9ede14a..4a6b2f7 100644 (file)
@@ -4,15 +4,21 @@
  *
  * 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)
@@ -38,4 +44,15 @@ enum {
 
 #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
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 f9b7fdd..101b0c8 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:
  *
@@ -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 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 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 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 */
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 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 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 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 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 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 7626b80..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
 };
 
 /*
@@ -1546,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)
@@ -2181,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)
@@ -2210,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);
@@ -2251,35 +2272,35 @@ 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
 
@@ -2292,36 +2313,77 @@ static 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;
 
-/* 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.
+       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();
+
+       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)
@@ -2345,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)
@@ -2360,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:
@@ -2402,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;
@@ -2447,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;
@@ -2515,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);
@@ -3313,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,
@@ -3337,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;
 
@@ -3464,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;
@@ -3645,13 +3816,10 @@ static int link_update(union bpf_attr *attr)
                goto out_put_progs;
        }
 
-#ifdef CONFIG_CGROUP_BPF
-       if (link->ops == &bpf_cgroup_link_lops) {
-               ret = cgroup_bpf_replace(link, old_prog, new_prog);
-               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)
@@ -3663,6 +3831,102 @@ out_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;
@@ -3780,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 fa1d824..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;
 }
 
@@ -3915,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:
@@ -3923,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:
@@ -4093,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,
@@ -4604,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)
@@ -5609,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;
@@ -5631,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) {
@@ -5643,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) {
@@ -6515,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;
                        }
@@ -8409,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;
@@ -8685,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;
@@ -10208,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;
@@ -10270,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 */
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 633b4ae..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);
 
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 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 9a2fbf9..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;
@@ -2718,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;
@@ -2792,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 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 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 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 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 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 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 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 bcabbe0..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;
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 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 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 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 80b87b1..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));
 }
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 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 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 a0f5dbe..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;
 
@@ -670,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;
                }
        }
 
@@ -1102,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 5222881..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
@@ -6227,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
@@ -6239,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);
@@ -6284,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);
 
@@ -6464,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);
 
@@ -9136,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
@@ -9196,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;
@@ -9243,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);
@@ -9825,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;
@@ -9914,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 899edce..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,
index 7d6ceaa..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,
@@ -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 1161392..b607ea6 100644 (file)
@@ -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 4773ad6..880e89c 100644 (file)
@@ -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 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 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 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 62f4ee3..ea0fcf7 100644 (file)
@@ -1588,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
@@ -1631,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)) {
@@ -1639,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);
 
@@ -1667,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);
@@ -1750,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;
@@ -1760,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;
@@ -1777,7 +1796,7 @@ int dsa_slave_create(struct dsa_port *port)
        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);
@@ -1796,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);
@@ -1816,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 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 c0dd561..fc94f82 100644 (file)
@@ -2366,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);
@@ -2419,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;
@@ -2463,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 55ca2e5..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)
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,
@@ -711,6 +703,15 @@ static struct ctl_table ipv4_net_table[] = {
                .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,
                .maxlen         = sizeof(int),
@@ -1321,6 +1322,13 @@ static struct ctl_table ipv4_net_table[] = {
                .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,
                .maxlen         = sizeof(int),
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 b996dc1..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);
        }
 }
 
@@ -4323,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);
@@ -4335,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));
@@ -4344,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.
@@ -4351,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--;
@@ -5271,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;
 
@@ -5292,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)
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 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 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 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 8d41803..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;
        }
 
@@ -3754,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);
 }
@@ -4277,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;
                }
        }
@@ -5579,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;
@@ -6113,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 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 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 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 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 67a4e35..009d5c4 100644 (file)
@@ -845,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);
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 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 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 9780bd9..3558e76 100644 (file)
@@ -4669,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)
 {
@@ -4946,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;
@@ -5072,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 = {
@@ -5094,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);
                }
@@ -5123,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;
@@ -5201,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 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 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)
@@ -64,6 +64,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.
  */
 static void nr_remove_socket(struct sock *sk)
@@ -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 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 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 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)
@@ -65,6 +65,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.
  */
 char *rose2asc(char *buf, const rose_address *addr)
@@ -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 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 0a7ecc2..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);
 }
@@ -3523,6 +3527,27 @@ 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))
@@ -3679,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 1bcf8fb..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.
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 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 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[] = {
@@ -736,6 +1835,10 @@ static struct smc_wr_rx_handler smc_llc_rx_handlers[] = {
        },
        {
                .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 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 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 692bcd3..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 },
@@ -703,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 },
@@ -736,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
@@ -762,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),
@@ -797,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,
@@ -4406,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 c350108..f6e6609 100644 (file)
@@ -322,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.
@@ -406,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 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 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 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 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 7bbf1b6..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:
  *
@@ -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 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 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 8f480e2..977add1 100644 (file)
@@ -310,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;
@@ -318,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;
@@ -389,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;
@@ -1914,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;
@@ -2026,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;
@@ -2052,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;
@@ -2068,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)
 {
@@ -2163,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;
                }
        }
@@ -2549,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;
@@ -3482,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);
-               }
+               err = bpf_object__create_map(obj, map);
+               if (err)
+                       goto err_out;
 
-               if (*pfd < 0) {
-                       size_t j;
-
-                       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
@@ -4851,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)
 {
@@ -4876,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;
        }
@@ -5955,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;
@@ -5970,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);
@@ -6516,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;
@@ -6672,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)) {
@@ -6679,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)
@@ -7006,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;
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 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);
+}
@@ -5,26 +5,17 @@
  *
  * Copyright (c) 2019 Facebook
  */
-#include <stdio.h>
-#include <errno.h>
-#include <linux/err.h>
+#include "test_progs.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;                                                          \
-})
+static int duration = 0;
 
-size_t hash_fn(const void *k, void *ctx)
+static size_t hash_fn(const void *k, void *ctx)
 {
        return (long)k;
 }
 
-bool equal_fn(const void *a, const void *b, void *ctx)
+static bool equal_fn(const void *a, const void *b, void *ctx)
 {
        return (long)a == (long)b;
 }
@@ -49,53 +40,55 @@ static inline size_t exp_cap(size_t sz)
 
 #define ELEM_CNT 62
 
-int test_hashmap_generic(void)
+static void 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;
+       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, "unexpected result: %d\n", err))
-                       return 1;
+               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,
+                       if (CHECK(oldk != NULL || oldv != NULL, "check_kv",
                                  "unexpected k/v: %p=%p\n", oldk, oldv))
-                               return 1;
+                               goto cleanup;
                }
 
-               if (CHECK(err, "failed to add k/v %ld = %ld: %d\n",
+               if (CHECK(err, "elem_add", "failed to add k/v %ld = %ld: %d\n",
                               (long)k, (long)v, err))
-                       return 1;
+                       goto cleanup;
 
-               if (CHECK(!hashmap__find(map, k, &oldv),
+               if (CHECK(!hashmap__find(map, k, &oldv), "elem_find",
                          "failed to find key %ld\n", (long)k))
-                       return 1;
-               if (CHECK(oldv != v, "found value is wrong: %ld\n", (long)oldv))
-                       return 1;
+                       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,
+       if (CHECK(hashmap__size(map) != ELEM_CNT, "hashmap__size",
                  "invalid map size: %zu\n", hashmap__size(map)))
-               return 1;
+               goto cleanup;
        if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
+                 "hashmap_cap",
                  "unexpected map capacity: %zu\n", hashmap__capacity(map)))
-               return 1;
+               goto cleanup;
 
        found_msk = 0;
        hashmap__for_each_entry(map, entry, bkt) {
@@ -103,42 +96,47 @@ int test_hashmap_generic(void)
                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(v - k != 1024, "check_kv",
+                         "invalid k/v pair: %ld = %ld\n", k, v))
+                       goto cleanup;
        }
-       if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1,
+       if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1, "elem_cnt",
                  "not all keys iterated: %llx\n", found_msk))
-               return 1;
+               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, "unexpected add result: %d\n", err))
-                       return 1;
+               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, "failed to update k/v %ld = %ld: %d\n",
-                              (long)k, (long)v, err))
-                       return 1;
-               if (CHECK(!hashmap__find(map, k, &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))
-                       return 1;
-               if (CHECK(oldv != v, "found value is wrong: %ld\n", (long)oldv))
-                       return 1;
+                       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,
+       if (CHECK(hashmap__size(map) != ELEM_CNT, "hashmap__size",
                  "invalid updated map size: %zu\n", hashmap__size(map)))
-               return 1;
+               goto cleanup;
        if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
+                 "hashmap__capacity",
                  "unexpected map capacity: %zu\n", hashmap__capacity(map)))
-               return 1;
+               goto cleanup;
 
        found_msk = 0;
        hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
@@ -146,20 +144,21 @@ int test_hashmap_generic(void)
                long v = (long)entry->value;
 
                found_msk |= 1ULL << k;
-               if (CHECK(v - k != 256,
+               if (CHECK(v - k != 256, "elem_check",
                          "invalid updated k/v pair: %ld = %ld\n", k, v))
-                       return 1;
+                       goto cleanup;
        }
-       if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1,
+       if (CHECK(found_msk != (1ULL << ELEM_CNT) - 1, "elem_cnt",
                  "not all keys iterated after update: %llx\n", found_msk))
-               return 1;
+               goto cleanup;
 
        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;
+       if (CHECK(!found_cnt, "found_cnt",
+                 "didn't find any entries for key 0\n"))
+               goto cleanup;
 
        found_msk = 0;
        found_cnt = 0;
@@ -173,30 +172,31 @@ int test_hashmap_generic(void)
                found_cnt++;
                found_msk |= 1ULL << (long)k;
 
-               if (CHECK(!hashmap__delete(map, k, &oldk, &oldv),
+               if (CHECK(!hashmap__delete(map, k, &oldk, &oldv), "elem_del",
                          "failed to delete k/v %ld = %ld\n",
                          (long)k, (long)v))
-                       return 1;
-               if (CHECK(oldk != k || oldv != 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))
-                       return 1;
-               if (CHECK(hashmap__delete(map, k, &oldk, &oldv),
+                       goto cleanup;
+               if (CHECK(hashmap__delete(map, k, &oldk, &oldv), "elem_del",
                          "unexpectedly deleted k/v %ld = %ld\n",
                          (long)oldk, (long)oldv))
-                       return 1;
+                       goto cleanup;
        }
 
-       if (CHECK(!found_cnt || !found_msk,
+       if (CHECK(!found_cnt || !found_msk, "found_entries",
                  "didn't delete any key entries\n"))
-               return 1;
-       if (CHECK(hashmap__size(map) != ELEM_CNT - found_cnt,
+               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)))
-               return 1;
+               goto cleanup;
        if (CHECK(hashmap__capacity(map) != exp_cap(hashmap__size(map)),
+                 "hashmap__capacity",
                  "unexpected map capacity: %zu\n", hashmap__capacity(map)))
-               return 1;
+               goto cleanup;
 
        hashmap__for_each_entry_safe(map, entry, tmp, bkt) {
                const void *oldk, *k;
@@ -208,53 +208,56 @@ int test_hashmap_generic(void)
                found_cnt++;
                found_msk |= 1ULL << (long)k;
 
-               if (CHECK(!hashmap__delete(map, k, &oldk, &oldv),
+               if (CHECK(!hashmap__delete(map, k, &oldk, &oldv), "elem_del",
                          "failed to delete k/v %ld = %ld\n",
                          (long)k, (long)v))
-                       return 1;
-               if (CHECK(oldk != k || oldv != 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))
-                       return 1;
-               if (CHECK(hashmap__delete(map, k, &oldk, &oldv),
+                       goto cleanup;
+               if (CHECK(hashmap__delete(map, k, &oldk, &oldv), "elem_del",
                          "unexpectedly deleted k/v %ld = %ld\n",
                          (long)k, (long)v))
-                       return 1;
+                       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))
-               return 1;
-       if (CHECK(hashmap__size(map) != 0,
+               goto cleanup;
+       if (CHECK(hashmap__size(map) != 0, "hashmap__size",
                  "invalid updated map size (already deleted: %d): %zu\n",
                  found_cnt, hashmap__size(map)))
-               return 1;
+               goto cleanup;
 
        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;
+               CHECK(false, "elem_exists",
+                     "unexpected map entries left: %ld = %ld\n",
+                     (long)entry->key, (long)entry->value);
+               goto cleanup;
        }
 
-       hashmap__free(map);
+       hashmap__clear(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;
+               CHECK(false, "elem_exists",
+                     "unexpected map entries left: %ld = %ld\n",
+                     (long)entry->key, (long)entry->value);
+               goto cleanup;
        }
 
-       fprintf(stderr, "OK\n");
-       return 0;
+cleanup:
+       hashmap__free(map);
 }
 
-size_t collision_hash_fn(const void *k, void *ctx)
+static size_t collision_hash_fn(const void *k, void *ctx)
 {
        return 0;
 }
 
-int test_hashmap_multimap(void)
+static void test_hashmap_multimap(void)
 {
        void *k1 = (void *)0, *k2 = (void *)1;
        struct hashmap_entry *entry;
@@ -262,121 +265,116 @@ int test_hashmap_multimap(void)
        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;
-
+       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, "failed to add k/v: %d\n", err))
-               return 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, "failed to add k/v: %d\n", err))
-               return 1;
+       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, "failed to add k/v: %d\n", err))
-               return 1;
+       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, "failed to add k/v: %d\n", err))
-               return 1;
+       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, "failed to add k/v: %d\n", err))
-               return 1;
+       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, "failed to add k/v: %d\n", err))
-               return 1;
+       if (CHECK(err, "elem_add", "failed to add k/v: %d\n", err))
+               goto cleanup;
 
-       if (CHECK(hashmap__size(map) != 6,
+       if (CHECK(hashmap__size(map) != 6, "hashmap_size",
                  "invalid map size: %zu\n", hashmap__size(map)))
-               return 1;
+               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,
+       if (CHECK(found_msk != (1 << 6) - 1, "found_msk",
                  "not all keys iterated: %lx\n", found_msk))
-               return 1;
+               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),
+       if (CHECK(found_msk != (1 | 2 | 4), "found_msk",
                  "invalid k1 values: %lx\n", found_msk))
-               return 1;
+               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),
+       if (CHECK(found_msk != (8 | 16 | 32), "found_msk",
                  "invalid k2 values: %lx\n", found_msk))
-               return 1;
+               goto cleanup;
 
-       fprintf(stderr, "OK\n");
-       return 0;
+cleanup:
+       hashmap__free(map);
 }
 
-int test_hashmap_empty()
+static void 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(IS_ERR(map), "hashmap__new",
+                 "failed to create map: %ld\n", PTR_ERR(map)))
+               goto cleanup;
 
-       if (CHECK(hashmap__size(map) != 0,
+       if (CHECK(hashmap__size(map) != 0, "hashmap__size",
                  "invalid map size: %zu\n", hashmap__size(map)))
-               return 1;
-       if (CHECK(hashmap__capacity(map) != 0,
+               goto cleanup;
+       if (CHECK(hashmap__capacity(map) != 0, "hashmap__capacity",
                  "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;
+               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, "unexpected iterated entry\n");
-               return 1;
+               CHECK(false, "elem_found", "unexpected iterated entry\n");
+               goto cleanup;
        }
        hashmap__for_each_key_entry(map, entry, k) {
-               CHECK(false, "unexpected key entry\n");
-               return 1;
+               CHECK(false, "key_found", "unexpected key entry\n");
+               goto cleanup;
        }
 
-       fprintf(stderr, "OK\n");
-       return 0;
+cleanup:
+       hashmap__free(map);
 }
 
-int main(int argc, char **argv)
+void test_hashmap()
 {
-       bool failed = false;
-
-       if (test_hashmap_generic())
-               failed = true;
-       if (test_hashmap_multimap())
-               failed = true;
-       if (test_hashmap_empty())
-               failed = true;
-
-       return failed;
+       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 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 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 ad3c498..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))
@@ -33,6 +38,43 @@ int do_bind(struct bpf_sock_addr *ctx)
        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)
 {
@@ -66,6 +108,10 @@ 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);
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";
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 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 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,34 +1,4 @@
 {
-       "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 = {
        BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
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 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 2bb8c81..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.
                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)
@@ -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 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 6560ed7..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
@@ -866,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
        #
@@ -880,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 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 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 f8ea6f5..72cdc3c 100644 (file)
         ]
     },
     {
+        "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",
         "category": [