From 0290bd291cc0e0488e35e66bf39efcd7d9d9122b Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 10 Dec 2019 09:23:51 -0500 Subject: netdev: pass the stuck queue to the timeout handler This allows incrementing the correct timeout statistic without any mess. Down the road, devices can learn to reset just the specific queue. The patch was generated with the following script: use strict; use warnings; our $^I = '.bak'; my @work = ( ["arch/m68k/emu/nfeth.c", "nfeth_tx_timeout"], ["arch/um/drivers/net_kern.c", "uml_net_tx_timeout"], ["arch/um/drivers/vector_kern.c", "vector_net_tx_timeout"], ["arch/xtensa/platforms/iss/network.c", "iss_net_tx_timeout"], ["drivers/char/pcmcia/synclink_cs.c", "hdlcdev_tx_timeout"], ["drivers/infiniband/ulp/ipoib/ipoib_main.c", "ipoib_timeout"], ["drivers/infiniband/ulp/ipoib/ipoib_main.c", "ipoib_timeout"], ["drivers/message/fusion/mptlan.c", "mpt_lan_tx_timeout"], ["drivers/misc/sgi-xp/xpnet.c", "xpnet_dev_tx_timeout"], ["drivers/net/appletalk/cops.c", "cops_timeout"], ["drivers/net/arcnet/arcdevice.h", "arcnet_timeout"], ["drivers/net/arcnet/arcnet.c", "arcnet_timeout"], ["drivers/net/arcnet/com20020.c", "arcnet_timeout"], ["drivers/net/ethernet/3com/3c509.c", "el3_tx_timeout"], ["drivers/net/ethernet/3com/3c515.c", "corkscrew_timeout"], ["drivers/net/ethernet/3com/3c574_cs.c", "el3_tx_timeout"], ["drivers/net/ethernet/3com/3c589_cs.c", "el3_tx_timeout"], ["drivers/net/ethernet/3com/3c59x.c", "vortex_tx_timeout"], ["drivers/net/ethernet/3com/3c59x.c", "vortex_tx_timeout"], ["drivers/net/ethernet/3com/typhoon.c", "typhoon_tx_timeout"], ["drivers/net/ethernet/8390/8390.h", "ei_tx_timeout"], ["drivers/net/ethernet/8390/8390.h", "eip_tx_timeout"], ["drivers/net/ethernet/8390/8390.c", "ei_tx_timeout"], ["drivers/net/ethernet/8390/8390p.c", "eip_tx_timeout"], ["drivers/net/ethernet/8390/ax88796.c", "ax_ei_tx_timeout"], ["drivers/net/ethernet/8390/axnet_cs.c", "axnet_tx_timeout"], ["drivers/net/ethernet/8390/etherh.c", "__ei_tx_timeout"], ["drivers/net/ethernet/8390/hydra.c", "__ei_tx_timeout"], ["drivers/net/ethernet/8390/mac8390.c", "__ei_tx_timeout"], ["drivers/net/ethernet/8390/mcf8390.c", "__ei_tx_timeout"], ["drivers/net/ethernet/8390/lib8390.c", "__ei_tx_timeout"], ["drivers/net/ethernet/8390/ne2k-pci.c", "ei_tx_timeout"], ["drivers/net/ethernet/8390/pcnet_cs.c", "ei_tx_timeout"], ["drivers/net/ethernet/8390/smc-ultra.c", "ei_tx_timeout"], ["drivers/net/ethernet/8390/wd.c", "ei_tx_timeout"], ["drivers/net/ethernet/8390/zorro8390.c", "__ei_tx_timeout"], ["drivers/net/ethernet/adaptec/starfire.c", "tx_timeout"], ["drivers/net/ethernet/agere/et131x.c", "et131x_tx_timeout"], ["drivers/net/ethernet/allwinner/sun4i-emac.c", "emac_timeout"], ["drivers/net/ethernet/alteon/acenic.c", "ace_watchdog"], ["drivers/net/ethernet/amazon/ena/ena_netdev.c", "ena_tx_timeout"], ["drivers/net/ethernet/amd/7990.h", "lance_tx_timeout"], ["drivers/net/ethernet/amd/7990.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/a2065.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/am79c961a.c", "am79c961_timeout"], ["drivers/net/ethernet/amd/amd8111e.c", "amd8111e_tx_timeout"], ["drivers/net/ethernet/amd/ariadne.c", "ariadne_tx_timeout"], ["drivers/net/ethernet/amd/atarilance.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/au1000_eth.c", "au1000_tx_timeout"], ["drivers/net/ethernet/amd/declance.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/lance.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/mvme147.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/ni65.c", "ni65_timeout"], ["drivers/net/ethernet/amd/nmclan_cs.c", "mace_tx_timeout"], ["drivers/net/ethernet/amd/pcnet32.c", "pcnet32_tx_timeout"], ["drivers/net/ethernet/amd/sunlance.c", "lance_tx_timeout"], ["drivers/net/ethernet/amd/xgbe/xgbe-drv.c", "xgbe_tx_timeout"], ["drivers/net/ethernet/apm/xgene-v2/main.c", "xge_timeout"], ["drivers/net/ethernet/apm/xgene/xgene_enet_main.c", "xgene_enet_timeout"], ["drivers/net/ethernet/apple/macmace.c", "mace_tx_timeout"], ["drivers/net/ethernet/atheros/ag71xx.c", "ag71xx_tx_timeout"], ["drivers/net/ethernet/atheros/alx/main.c", "alx_tx_timeout"], ["drivers/net/ethernet/atheros/atl1c/atl1c_main.c", "atl1c_tx_timeout"], ["drivers/net/ethernet/atheros/atl1e/atl1e_main.c", "atl1e_tx_timeout"], ["drivers/net/ethernet/atheros/atlx/atl.c", "atlx_tx_timeout"], ["drivers/net/ethernet/atheros/atlx/atl1.c", "atlx_tx_timeout"], ["drivers/net/ethernet/atheros/atlx/atl2.c", "atl2_tx_timeout"], ["drivers/net/ethernet/broadcom/b44.c", "b44_tx_timeout"], ["drivers/net/ethernet/broadcom/bcmsysport.c", "bcm_sysport_tx_timeout"], ["drivers/net/ethernet/broadcom/bnx2.c", "bnx2_tx_timeout"], ["drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h", "bnx2x_tx_timeout"], ["drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c", "bnx2x_tx_timeout"], ["drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c", "bnx2x_tx_timeout"], ["drivers/net/ethernet/broadcom/bnxt/bnxt.c", "bnxt_tx_timeout"], ["drivers/net/ethernet/broadcom/genet/bcmgenet.c", "bcmgenet_timeout"], ["drivers/net/ethernet/broadcom/sb1250-mac.c", "sbmac_tx_timeout"], ["drivers/net/ethernet/broadcom/tg3.c", "tg3_tx_timeout"], ["drivers/net/ethernet/calxeda/xgmac.c", "xgmac_tx_timeout"], ["drivers/net/ethernet/cavium/liquidio/lio_main.c", "liquidio_tx_timeout"], ["drivers/net/ethernet/cavium/liquidio/lio_vf_main.c", "liquidio_tx_timeout"], ["drivers/net/ethernet/cavium/liquidio/lio_vf_rep.c", "lio_vf_rep_tx_timeout"], ["drivers/net/ethernet/cavium/thunder/nicvf_main.c", "nicvf_tx_timeout"], ["drivers/net/ethernet/cirrus/cs89x0.c", "net_timeout"], ["drivers/net/ethernet/cisco/enic/enic_main.c", "enic_tx_timeout"], ["drivers/net/ethernet/cisco/enic/enic_main.c", "enic_tx_timeout"], ["drivers/net/ethernet/cortina/gemini.c", "gmac_tx_timeout"], ["drivers/net/ethernet/davicom/dm9000.c", "dm9000_timeout"], ["drivers/net/ethernet/dec/tulip/de2104x.c", "de_tx_timeout"], ["drivers/net/ethernet/dec/tulip/tulip_core.c", "tulip_tx_timeout"], ["drivers/net/ethernet/dec/tulip/winbond-840.c", "tx_timeout"], ["drivers/net/ethernet/dlink/dl2k.c", "rio_tx_timeout"], ["drivers/net/ethernet/dlink/sundance.c", "tx_timeout"], ["drivers/net/ethernet/emulex/benet/be_main.c", "be_tx_timeout"], ["drivers/net/ethernet/ethoc.c", "ethoc_tx_timeout"], ["drivers/net/ethernet/faraday/ftgmac100.c", "ftgmac100_tx_timeout"], ["drivers/net/ethernet/fealnx.c", "fealnx_tx_timeout"], ["drivers/net/ethernet/freescale/dpaa/dpaa_eth.c", "dpaa_tx_timeout"], ["drivers/net/ethernet/freescale/fec_main.c", "fec_timeout"], ["drivers/net/ethernet/freescale/fec_mpc52xx.c", "mpc52xx_fec_tx_timeout"], ["drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c", "fs_timeout"], ["drivers/net/ethernet/freescale/gianfar.c", "gfar_timeout"], ["drivers/net/ethernet/freescale/ucc_geth.c", "ucc_geth_timeout"], ["drivers/net/ethernet/fujitsu/fmvj18x_cs.c", "fjn_tx_timeout"], ["drivers/net/ethernet/google/gve/gve_main.c", "gve_tx_timeout"], ["drivers/net/ethernet/hisilicon/hip04_eth.c", "hip04_timeout"], ["drivers/net/ethernet/hisilicon/hix5hd2_gmac.c", "hix5hd2_net_timeout"], ["drivers/net/ethernet/hisilicon/hns/hns_enet.c", "hns_nic_net_timeout"], ["drivers/net/ethernet/hisilicon/hns3/hns3_enet.c", "hns3_nic_net_timeout"], ["drivers/net/ethernet/huawei/hinic/hinic_main.c", "hinic_tx_timeout"], ["drivers/net/ethernet/i825xx/82596.c", "i596_tx_timeout"], ["drivers/net/ethernet/i825xx/ether1.c", "ether1_timeout"], ["drivers/net/ethernet/i825xx/lib82596.c", "i596_tx_timeout"], ["drivers/net/ethernet/i825xx/sun3_82586.c", "sun3_82586_timeout"], ["drivers/net/ethernet/ibm/ehea/ehea_main.c", "ehea_tx_watchdog"], ["drivers/net/ethernet/ibm/emac/core.c", "emac_tx_timeout"], ["drivers/net/ethernet/ibm/emac/core.c", "emac_tx_timeout"], ["drivers/net/ethernet/ibm/ibmvnic.c", "ibmvnic_tx_timeout"], ["drivers/net/ethernet/intel/e100.c", "e100_tx_timeout"], ["drivers/net/ethernet/intel/e1000/e1000_main.c", "e1000_tx_timeout"], ["drivers/net/ethernet/intel/e1000e/netdev.c", "e1000_tx_timeout"], ["drivers/net/ethernet/intel/fm10k/fm10k_netdev.c", "fm10k_tx_timeout"], ["drivers/net/ethernet/intel/i40e/i40e_main.c", "i40e_tx_timeout"], ["drivers/net/ethernet/intel/iavf/iavf_main.c", "iavf_tx_timeout"], ["drivers/net/ethernet/intel/ice/ice_main.c", "ice_tx_timeout"], ["drivers/net/ethernet/intel/ice/ice_main.c", "ice_tx_timeout"], ["drivers/net/ethernet/intel/igb/igb_main.c", "igb_tx_timeout"], ["drivers/net/ethernet/intel/igbvf/netdev.c", "igbvf_tx_timeout"], ["drivers/net/ethernet/intel/ixgb/ixgb_main.c", "ixgb_tx_timeout"], ["drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c", "adapter->netdev->netdev_ops->ndo_tx_timeout(adapter->netdev);"], ["drivers/net/ethernet/intel/ixgbe/ixgbe_main.c", "ixgbe_tx_timeout"], ["drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c", "ixgbevf_tx_timeout"], ["drivers/net/ethernet/jme.c", "jme_tx_timeout"], ["drivers/net/ethernet/korina.c", "korina_tx_timeout"], ["drivers/net/ethernet/lantiq_etop.c", "ltq_etop_tx_timeout"], ["drivers/net/ethernet/marvell/mv643xx_eth.c", "mv643xx_eth_tx_timeout"], ["drivers/net/ethernet/marvell/pxa168_eth.c", "pxa168_eth_tx_timeout"], ["drivers/net/ethernet/marvell/skge.c", "skge_tx_timeout"], ["drivers/net/ethernet/marvell/sky2.c", "sky2_tx_timeout"], ["drivers/net/ethernet/marvell/sky2.c", "sky2_tx_timeout"], ["drivers/net/ethernet/mediatek/mtk_eth_soc.c", "mtk_tx_timeout"], ["drivers/net/ethernet/mellanox/mlx4/en_netdev.c", "mlx4_en_tx_timeout"], ["drivers/net/ethernet/mellanox/mlx4/en_netdev.c", "mlx4_en_tx_timeout"], ["drivers/net/ethernet/mellanox/mlx5/core/en_main.c", "mlx5e_tx_timeout"], ["drivers/net/ethernet/micrel/ks8842.c", "ks8842_tx_timeout"], ["drivers/net/ethernet/micrel/ksz884x.c", "netdev_tx_timeout"], ["drivers/net/ethernet/microchip/enc28j60.c", "enc28j60_tx_timeout"], ["drivers/net/ethernet/microchip/encx24j600.c", "encx24j600_tx_timeout"], ["drivers/net/ethernet/natsemi/sonic.h", "sonic_tx_timeout"], ["drivers/net/ethernet/natsemi/sonic.c", "sonic_tx_timeout"], ["drivers/net/ethernet/natsemi/jazzsonic.c", "sonic_tx_timeout"], ["drivers/net/ethernet/natsemi/macsonic.c", "sonic_tx_timeout"], ["drivers/net/ethernet/natsemi/natsemi.c", "ns_tx_timeout"], ["drivers/net/ethernet/natsemi/ns83820.c", "ns83820_tx_timeout"], ["drivers/net/ethernet/natsemi/xtsonic.c", "sonic_tx_timeout"], ["drivers/net/ethernet/neterion/s2io.h", "s2io_tx_watchdog"], ["drivers/net/ethernet/neterion/s2io.c", "s2io_tx_watchdog"], ["drivers/net/ethernet/neterion/vxge/vxge-main.c", "vxge_tx_watchdog"], ["drivers/net/ethernet/netronome/nfp/nfp_net_common.c", "nfp_net_tx_timeout"], ["drivers/net/ethernet/nvidia/forcedeth.c", "nv_tx_timeout"], ["drivers/net/ethernet/nvidia/forcedeth.c", "nv_tx_timeout"], ["drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c", "pch_gbe_tx_timeout"], ["drivers/net/ethernet/packetengines/hamachi.c", "hamachi_tx_timeout"], ["drivers/net/ethernet/packetengines/yellowfin.c", "yellowfin_tx_timeout"], ["drivers/net/ethernet/pensando/ionic/ionic_lif.c", "ionic_tx_timeout"], ["drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c", "netxen_tx_timeout"], ["drivers/net/ethernet/qlogic/qla3xxx.c", "ql3xxx_tx_timeout"], ["drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c", "qlcnic_tx_timeout"], ["drivers/net/ethernet/qualcomm/emac/emac.c", "emac_tx_timeout"], ["drivers/net/ethernet/qualcomm/qca_spi.c", "qcaspi_netdev_tx_timeout"], ["drivers/net/ethernet/qualcomm/qca_uart.c", "qcauart_netdev_tx_timeout"], ["drivers/net/ethernet/rdc/r6040.c", "r6040_tx_timeout"], ["drivers/net/ethernet/realtek/8139cp.c", "cp_tx_timeout"], ["drivers/net/ethernet/realtek/8139too.c", "rtl8139_tx_timeout"], ["drivers/net/ethernet/realtek/atp.c", "tx_timeout"], ["drivers/net/ethernet/realtek/r8169_main.c", "rtl8169_tx_timeout"], ["drivers/net/ethernet/renesas/ravb_main.c", "ravb_tx_timeout"], ["drivers/net/ethernet/renesas/sh_eth.c", "sh_eth_tx_timeout"], ["drivers/net/ethernet/renesas/sh_eth.c", "sh_eth_tx_timeout"], ["drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c", "sxgbe_tx_timeout"], ["drivers/net/ethernet/seeq/ether3.c", "ether3_timeout"], ["drivers/net/ethernet/seeq/sgiseeq.c", "timeout"], ["drivers/net/ethernet/sfc/efx.c", "efx_watchdog"], ["drivers/net/ethernet/sfc/falcon/efx.c", "ef4_watchdog"], ["drivers/net/ethernet/sgi/ioc3-eth.c", "ioc3_timeout"], ["drivers/net/ethernet/sgi/meth.c", "meth_tx_timeout"], ["drivers/net/ethernet/silan/sc92031.c", "sc92031_tx_timeout"], ["drivers/net/ethernet/sis/sis190.c", "sis190_tx_timeout"], ["drivers/net/ethernet/sis/sis900.c", "sis900_tx_timeout"], ["drivers/net/ethernet/smsc/epic100.c", "epic_tx_timeout"], ["drivers/net/ethernet/smsc/smc911x.c", "smc911x_timeout"], ["drivers/net/ethernet/smsc/smc9194.c", "smc_timeout"], ["drivers/net/ethernet/smsc/smc91c92_cs.c", "smc_tx_timeout"], ["drivers/net/ethernet/smsc/smc91x.c", "smc_timeout"], ["drivers/net/ethernet/stmicro/stmmac/stmmac_main.c", "stmmac_tx_timeout"], ["drivers/net/ethernet/sun/cassini.c", "cas_tx_timeout"], ["drivers/net/ethernet/sun/ldmvsw.c", "sunvnet_tx_timeout_common"], ["drivers/net/ethernet/sun/niu.c", "niu_tx_timeout"], ["drivers/net/ethernet/sun/sunbmac.c", "bigmac_tx_timeout"], ["drivers/net/ethernet/sun/sungem.c", "gem_tx_timeout"], ["drivers/net/ethernet/sun/sunhme.c", "happy_meal_tx_timeout"], ["drivers/net/ethernet/sun/sunqe.c", "qe_tx_timeout"], ["drivers/net/ethernet/sun/sunvnet.c", "sunvnet_tx_timeout_common"], ["drivers/net/ethernet/sun/sunvnet_common.c", "sunvnet_tx_timeout_common"], ["drivers/net/ethernet/sun/sunvnet_common.h", "sunvnet_tx_timeout_common"], ["drivers/net/ethernet/synopsys/dwc-xlgmac-net.c", "xlgmac_tx_timeout"], ["drivers/net/ethernet/ti/cpmac.c", "cpmac_tx_timeout"], ["drivers/net/ethernet/ti/cpsw.c", "cpsw_ndo_tx_timeout"], ["drivers/net/ethernet/ti/cpsw_priv.c", "cpsw_ndo_tx_timeout"], ["drivers/net/ethernet/ti/cpsw_priv.h", "cpsw_ndo_tx_timeout"], ["drivers/net/ethernet/ti/davinci_emac.c", "emac_dev_tx_timeout"], ["drivers/net/ethernet/ti/netcp_core.c", "netcp_ndo_tx_timeout"], ["drivers/net/ethernet/ti/tlan.c", "tlan_tx_timeout"], ["drivers/net/ethernet/toshiba/ps3_gelic_net.h", "gelic_net_tx_timeout"], ["drivers/net/ethernet/toshiba/ps3_gelic_net.c", "gelic_net_tx_timeout"], ["drivers/net/ethernet/toshiba/ps3_gelic_wireless.c", "gelic_net_tx_timeout"], ["drivers/net/ethernet/toshiba/spider_net.c", "spider_net_tx_timeout"], ["drivers/net/ethernet/toshiba/tc35815.c", "tc35815_tx_timeout"], ["drivers/net/ethernet/via/via-rhine.c", "rhine_tx_timeout"], ["drivers/net/ethernet/wiznet/w5100.c", "w5100_tx_timeout"], ["drivers/net/ethernet/wiznet/w5300.c", "w5300_tx_timeout"], ["drivers/net/ethernet/xilinx/xilinx_emaclite.c", "xemaclite_tx_timeout"], ["drivers/net/ethernet/xircom/xirc2ps_cs.c", "xirc_tx_timeout"], ["drivers/net/fjes/fjes_main.c", "fjes_tx_retry"], ["drivers/net/slip/slip.c", "sl_tx_timeout"], ["include/linux/usb/usbnet.h", "usbnet_tx_timeout"], ["drivers/net/usb/aqc111.c", "usbnet_tx_timeout"], ["drivers/net/usb/asix_devices.c", "usbnet_tx_timeout"], ["drivers/net/usb/asix_devices.c", "usbnet_tx_timeout"], ["drivers/net/usb/asix_devices.c", "usbnet_tx_timeout"], ["drivers/net/usb/ax88172a.c", "usbnet_tx_timeout"], ["drivers/net/usb/ax88179_178a.c", "usbnet_tx_timeout"], ["drivers/net/usb/catc.c", "catc_tx_timeout"], ["drivers/net/usb/cdc_mbim.c", "usbnet_tx_timeout"], ["drivers/net/usb/cdc_ncm.c", "usbnet_tx_timeout"], ["drivers/net/usb/dm9601.c", "usbnet_tx_timeout"], ["drivers/net/usb/hso.c", "hso_net_tx_timeout"], ["drivers/net/usb/int51x1.c", "usbnet_tx_timeout"], ["drivers/net/usb/ipheth.c", "ipheth_tx_timeout"], ["drivers/net/usb/kaweth.c", "kaweth_tx_timeout"], ["drivers/net/usb/lan78xx.c", "lan78xx_tx_timeout"], ["drivers/net/usb/mcs7830.c", "usbnet_tx_timeout"], ["drivers/net/usb/pegasus.c", "pegasus_tx_timeout"], ["drivers/net/usb/qmi_wwan.c", "usbnet_tx_timeout"], ["drivers/net/usb/r8152.c", "rtl8152_tx_timeout"], ["drivers/net/usb/rndis_host.c", "usbnet_tx_timeout"], ["drivers/net/usb/rtl8150.c", "rtl8150_tx_timeout"], ["drivers/net/usb/sierra_net.c", "usbnet_tx_timeout"], ["drivers/net/usb/smsc75xx.c", "usbnet_tx_timeout"], ["drivers/net/usb/smsc95xx.c", "usbnet_tx_timeout"], ["drivers/net/usb/sr9700.c", "usbnet_tx_timeout"], ["drivers/net/usb/sr9800.c", "usbnet_tx_timeout"], ["drivers/net/usb/usbnet.c", "usbnet_tx_timeout"], ["drivers/net/vmxnet3/vmxnet3_drv.c", "vmxnet3_tx_timeout"], ["drivers/net/wan/cosa.c", "cosa_net_timeout"], ["drivers/net/wan/farsync.c", "fst_tx_timeout"], ["drivers/net/wan/fsl_ucc_hdlc.c", "uhdlc_tx_timeout"], ["drivers/net/wan/lmc/lmc_main.c", "lmc_driver_timeout"], ["drivers/net/wan/x25_asy.c", "x25_asy_timeout"], ["drivers/net/wimax/i2400m/netdev.c", "i2400m_tx_timeout"], ["drivers/net/wireless/intel/ipw2x00/ipw2100.c", "ipw2100_tx_timeout"], ["drivers/net/wireless/intersil/hostap/hostap_main.c", "prism2_tx_timeout"], ["drivers/net/wireless/intersil/hostap/hostap_main.c", "prism2_tx_timeout"], ["drivers/net/wireless/intersil/hostap/hostap_main.c", "prism2_tx_timeout"], ["drivers/net/wireless/intersil/orinoco/main.c", "orinoco_tx_timeout"], ["drivers/net/wireless/intersil/orinoco/orinoco_usb.c", "orinoco_tx_timeout"], ["drivers/net/wireless/intersil/orinoco/orinoco.h", "orinoco_tx_timeout"], ["drivers/net/wireless/intersil/prism54/islpci_dev.c", "islpci_eth_tx_timeout"], ["drivers/net/wireless/intersil/prism54/islpci_eth.c", "islpci_eth_tx_timeout"], ["drivers/net/wireless/intersil/prism54/islpci_eth.h", "islpci_eth_tx_timeout"], ["drivers/net/wireless/marvell/mwifiex/main.c", "mwifiex_tx_timeout"], ["drivers/net/wireless/quantenna/qtnfmac/core.c", "qtnf_netdev_tx_timeout"], ["drivers/net/wireless/quantenna/qtnfmac/core.h", "qtnf_netdev_tx_timeout"], ["drivers/net/wireless/rndis_wlan.c", "usbnet_tx_timeout"], ["drivers/net/wireless/wl3501_cs.c", "wl3501_tx_timeout"], ["drivers/net/wireless/zydas/zd1201.c", "zd1201_tx_timeout"], ["drivers/s390/net/qeth_core.h", "qeth_tx_timeout"], ["drivers/s390/net/qeth_core_main.c", "qeth_tx_timeout"], ["drivers/s390/net/qeth_l2_main.c", "qeth_tx_timeout"], ["drivers/s390/net/qeth_l2_main.c", "qeth_tx_timeout"], ["drivers/s390/net/qeth_l3_main.c", "qeth_tx_timeout"], ["drivers/s390/net/qeth_l3_main.c", "qeth_tx_timeout"], ["drivers/staging/ks7010/ks_wlan_net.c", "ks_wlan_tx_timeout"], ["drivers/staging/qlge/qlge_main.c", "qlge_tx_timeout"], ["drivers/staging/rtl8192e/rtl8192e/rtl_core.c", "_rtl92e_tx_timeout"], ["drivers/staging/rtl8192u/r8192U_core.c", "tx_timeout"], ["drivers/staging/unisys/visornic/visornic_main.c", "visornic_xmit_timeout"], ["drivers/staging/wlan-ng/p80211netdev.c", "p80211knetdev_tx_timeout"], ["drivers/tty/n_gsm.c", "gsm_mux_net_tx_timeout"], ["drivers/tty/synclink.c", "hdlcdev_tx_timeout"], ["drivers/tty/synclink_gt.c", "hdlcdev_tx_timeout"], ["drivers/tty/synclinkmp.c", "hdlcdev_tx_timeout"], ["net/atm/lec.c", "lec_tx_timeout"], ["net/bluetooth/bnep/netdev.c", "bnep_net_timeout"] ); for my $p (@work) { my @pair = @$p; my $file = $pair[0]; my $func = $pair[1]; print STDERR $file , ": ", $func,"\n"; our @ARGV = ($file); while () { if (m/($func\s*\(struct\s+net_device\s+\*[A-Za-z_]?[A-Za-z-0-9_]*)(\))/) { print STDERR "found $1+$2 in $file\n"; } if (s/($func\s*\(struct\s+net_device\s+\*[A-Za-z_]?[A-Za-z-0-9_]*)(\))/$1, unsigned int txqueue$2/) { print STDERR "$func found in $file\n"; } print; } } where the list of files and functions is simply from: git grep ndo_tx_timeout, with manual addition of headers in the rare cases where the function is from a header, then manually changing the few places which actually call ndo_tx_timeout. Signed-off-by: Michael S. Tsirkin Acked-by: Heiner Kallweit Acked-by: Jakub Kicinski Acked-by: Shannon Nelson Reviewed-by: Martin Habets changes from v9: fixup a forward declaration changes from v9: more leftovers from v3 change changes from v8: fix up a missing direct call to timeout rebased on net-next changes from v7: fixup leftovers from v3 change changes from v6: fix typo in rtl driver changes from v5: add missing files (allow any net device argument name) changes from v4: add a missing driver header changes from v3: change queue # to unsigned Changes from v2: added headers Changes from v1: Fix errors found by kbuild: generalize the pattern a bit, to pick up a couple of instances missed by the previous version. Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +- drivers/net/ethernet/marvell/pxa168_eth.c | 2 +- drivers/net/ethernet/marvell/skge.c | 2 +- drivers/net/ethernet/marvell/sky2.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index d5b644131cff..c43820597218 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2590,7 +2590,7 @@ static void tx_timeout_task(struct work_struct *ugly) } } -static void mv643xx_eth_tx_timeout(struct net_device *dev) +static void mv643xx_eth_tx_timeout(struct net_device *dev, unsigned int txqueue) { struct mv643xx_eth_private *mp = netdev_priv(dev); diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 3fb7ee3d4d13..1a6877902dd6 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -742,7 +742,7 @@ txq_reclaim_end: return released; } -static void pxa168_eth_tx_timeout(struct net_device *dev) +static void pxa168_eth_tx_timeout(struct net_device *dev, unsigned int txqueue) { struct pxa168_eth_private *pep = netdev_priv(dev); diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index 095f6c71b4fa..8ca15958e752 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -2884,7 +2884,7 @@ static void skge_tx_clean(struct net_device *dev) skge->tx_ring.to_clean = e; } -static void skge_tx_timeout(struct net_device *dev) +static void skge_tx_timeout(struct net_device *dev, unsigned int txqueue) { struct skge_port *skge = netdev_priv(dev); diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index 5f56ee83e3b1..acd1cba987fb 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -2358,7 +2358,7 @@ static void sky2_qlink_intr(struct sky2_hw *hw) /* Transmit timeout is only called if we are running, carrier is up * and tx queue is full (stopped). */ -static void sky2_tx_timeout(struct net_device *dev) +static void sky2_tx_timeout(struct net_device *dev, unsigned int txqueue) { struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; -- cgit v1.2.1 From ef8e0b808b908c49670ef00b95c7848e233ca3bd Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 13 Dec 2019 18:22:12 +0000 Subject: net: mvpp2: update mvpp2 validate() implementation Fix up the mvpp2 validate implementation to adopt the same behaviour as mvneta: - only allow the link modes that the specified PHY interface mode supports with the exception of 1000base-X and 2500base-X. - use the basex helper to deal with SFP modules that can be switched between 1000base-X vs 2500base-X. This gives consistent behaviour between mvneta and mvpp2. This commit depends on "net: phylink: extend clause 45 PHY validation workaround" so is not marked for backporting to stable kernels. Signed-off-by: Russell King Acked-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 62dc2f362a16..3d9eb5747d9e 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -4792,6 +4792,8 @@ static void mvpp2_phylink_validate(struct phylink_config *config, phylink_set(mask, 10000baseER_Full); phylink_set(mask, 10000baseKR_Full); } + if (state->interface != PHY_INTERFACE_MODE_NA) + break; /* Fall-through */ case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: @@ -4802,13 +4804,23 @@ static void mvpp2_phylink_validate(struct phylink_config *config, phylink_set(mask, 10baseT_Full); phylink_set(mask, 100baseT_Half); phylink_set(mask, 100baseT_Full); + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + if (state->interface != PHY_INTERFACE_MODE_NA) + break; /* Fall-through */ case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_2500BASEX: - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); - phylink_set(mask, 2500baseT_Full); - phylink_set(mask, 2500baseX_Full); + if (port->comphy || + state->interface != PHY_INTERFACE_MODE_2500BASEX) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + } + if (port->comphy || + state->interface == PHY_INTERFACE_MODE_2500BASEX) { + phylink_set(mask, 2500baseT_Full); + phylink_set(mask, 2500baseX_Full); + } break; default: goto empty_set; @@ -4817,6 +4829,8 @@ static void mvpp2_phylink_validate(struct phylink_config *config, bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); bitmap_and(state->advertising, state->advertising, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + + phylink_helper_basex_speed(state); return; empty_set: -- cgit v1.2.1 From 6791c102602b428b729890f6a38a319dfe225aba Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 15:47:36 +0000 Subject: net: mvpp2: cycle comphy to power it down Presently, at boot time, the comphys are enabled. For firmware compatibility reasons, the comphy driver does not power down the comphys at boot. Consequently, the ethernet comphys are left active until the network interfaces are brought through an up/down cycle. If the port is never used, the port wastes power needlessly. Arrange for the ethernet comphys to be cycled by the mvpp2 driver as if the interface went through an up/down cycle during driver probe, thereby powering them down. This saves: 270mW per 10G SFP+ port on the Macchiatobin Single Shot (eth0/eth1) 370mW per 10G PHY port on the Macchiatobin Double Shot (eth0/eth1) 160mW on the SFP port on either Macchiatobin flavour (eth3) Signed-off-by: Russell King Acked-by: Antoine Tenart Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 3d9eb5747d9e..28eba99888c3 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -5425,6 +5425,16 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->phylink = NULL; } + /* Cycle the comphy to power it down, saving 270mW per port - + * don't worry about an error powering it up. When the comphy + * driver does this, we can remove this code. + */ + if (port->comphy) { + err = mvpp22_comphy_init(port); + if (err == 0) + phy_power_off(port->comphy); + } + err = register_netdev(dev); if (err < 0) { dev_err(&pdev->dev, "failed to register netdev\n"); -- cgit v1.2.1 From e0f909bc3a242296da9ccff78277f26d4883a79d Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 3 Jan 2020 20:43:23 +0000 Subject: net: switch to using PHY_INTERFACE_MODE_10GBASER rather than 10GKR Switch network drivers, phy drivers, and SFP/phylink over to use the more correct 10GBASE-R, rather than 10GBASE-KR. 10GBASE-KR is backplane ethernet, which is 10GBASE-R with autonegotiation on top, which our current usage on the affected platforms does not have. The only remaining user of PHY_INTERFACE_MODE_10GKR is the Aquantia PHY, which has a separate mode for 10GBASE-KR. For Marvell mvpp2, we detect 10GBASE-KR, and rewrite it to 10GBASE-R for compatibility with existing DT - this is the only network driver at present that makes use of PHY_INTERFACE_MODE_10GKR. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index cd32c7196a7f..72133cbe55d4 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -1114,7 +1114,7 @@ mvpp2_shared_interrupt_mask_unmask(struct mvpp2_port *port, bool mask) /* Port configuration routines */ static bool mvpp2_is_xlg(phy_interface_t interface) { - return interface == PHY_INTERFACE_MODE_10GKR || + return interface == PHY_INTERFACE_MODE_10GBASER || interface == PHY_INTERFACE_MODE_XAUI; } @@ -1200,7 +1200,7 @@ static int mvpp22_gop_init(struct mvpp2_port *port) case PHY_INTERFACE_MODE_2500BASEX: mvpp22_gop_init_sgmii(port); break; - case PHY_INTERFACE_MODE_10GKR: + case PHY_INTERFACE_MODE_10GBASER: if (port->gop_id != 0) goto invalid_conf; mvpp22_gop_init_10gkr(port); @@ -1649,7 +1649,7 @@ static void mvpp22_pcs_reset_deassert(struct mvpp2_port *port) xpcs = priv->iface_base + MVPP22_XPCS_BASE(port->gop_id); switch (port->phy_interface) { - case PHY_INTERFACE_MODE_10GKR: + case PHY_INTERFACE_MODE_10GBASER: val = readl(mpcs + MVPP22_MPCS_CLK_RESET); val |= MAC_CLK_RESET_MAC | MAC_CLK_RESET_SD_RX | MAC_CLK_RESET_SD_TX; @@ -4758,7 +4758,7 @@ static void mvpp2_phylink_validate(struct phylink_config *config, /* Invalid combinations */ switch (state->interface) { - case PHY_INTERFACE_MODE_10GKR: + case PHY_INTERFACE_MODE_10GBASER: case PHY_INTERFACE_MODE_XAUI: if (port->gop_id != 0) goto empty_set; @@ -4780,7 +4780,7 @@ static void mvpp2_phylink_validate(struct phylink_config *config, phylink_set(mask, Asym_Pause); switch (state->interface) { - case PHY_INTERFACE_MODE_10GKR: + case PHY_INTERFACE_MODE_10GBASER: case PHY_INTERFACE_MODE_XAUI: case PHY_INTERFACE_MODE_NA: if (port->gop_id == 0) { @@ -5247,6 +5247,15 @@ static int mvpp2_port_probe(struct platform_device *pdev, goto err_free_netdev; } + /* + * Rewrite 10GBASE-KR to 10GBASE-R for compatibility with existing DT. + * Existing usage of 10GBASE-KR is not correct; no backplane + * negotiation is done, and this driver does not actually support + * 10GBASE-KR. + */ + if (phy_mode == PHY_INTERFACE_MODE_10GKR) + phy_mode = PHY_INTERFACE_MODE_10GBASER; + if (port_node) { comphy = devm_of_phy_get(&pdev->dev, port_node, NULL); if (IS_ERR(comphy)) { -- cgit v1.2.1 From 1657adccaa37e1895356658212a1847f617ebf8f Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 13 Jan 2020 10:28:12 +0100 Subject: net: mvneta: change page pool nid to NUMA_NO_NODE With 'commit 44768decb7c0 ("page_pool: handle page recycle for NUMA_NO_NODE condition")' we can safely change nid to NUMA_NO_NODE and accommodate future NUMA aware hardware using mvneta network interface Signed-off-by: Lorenzo Bianconi Acked-by: Jesper Dangaard Brouer Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/mvneta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 71a872d46bc4..587e35d10dc7 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3071,7 +3071,7 @@ static int mvneta_create_page_pool(struct mvneta_port *pp, .order = 0, .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV, .pool_size = size, - .nid = cpu_to_node(0), + .nid = NUMA_NO_NODE, .dev = pp->dev->dev.parent, .dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE, .offset = pp->rx_offset_correction, -- cgit v1.2.1 From c5d19a6ecfce72d0352191d75f03eea4748a8c45 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 21 Jan 2020 22:05:14 +0100 Subject: net: convert additional drivers to use phy_do_ioctl The first batch of driver conversions missed a few cases where we can use phy_do_ioctl too. Signed-off-by: Heiner Kallweit Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/pxa168_eth.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 1a6877902dd6..7a0d785b826c 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1344,15 +1344,6 @@ static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum, return 0; } -static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr, - int cmd) -{ - if (dev->phydev) - return phy_mii_ioctl(dev->phydev, ifr, cmd); - - return -EOPNOTSUPP; -} - #ifdef CONFIG_NET_POLL_CONTROLLER static void pxa168_eth_netpoll(struct net_device *dev) { @@ -1387,7 +1378,7 @@ static const struct net_device_ops pxa168_eth_netdev_ops = { .ndo_set_rx_mode = pxa168_eth_set_rx_mode, .ndo_set_mac_address = pxa168_eth_set_mac_address, .ndo_validate_addr = eth_validate_addr, - .ndo_do_ioctl = pxa168_eth_do_ioctl, + .ndo_do_ioctl = phy_do_ioctl, .ndo_change_mtu = pxa168_eth_change_mtu, .ndo_tx_timeout = pxa168_eth_tx_timeout, #ifdef CONFIG_NET_POLL_CONTROLLER -- cgit v1.2.1 From 79572c98c554dcdb080bca547c871a51716dcdf8 Mon Sep 17 00:00:00 2001 From: Sven Auhagen Date: Sat, 25 Jan 2020 08:07:03 +0000 Subject: mvneta driver disallow XDP program on hardware buffer management Recently XDP Support was added to the mvneta driver for software buffer management only. It is still possible to attach an XDP program if hardware buffer management is used. It is not doing anything at that point. The patch disallows attaching XDP programs to mvneta if hardware buffer management is used. I am sorry about that. It is my first submission and I am having some troubles with the format of my emails. v4 -> v5: - Remove extra tabs v3 -> v4: - Please ignore v3 I accidentally submitted my other patch with git-send-mail and v4 is correct v2 -> v3: - My mailserver corrupted the patch resubmission with git-send-email v1 -> v2: - Fixing the patches indentation Signed-off-by: Sven Auhagen Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 0449d4b28ade..2dfbfdff45a8 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -4226,6 +4226,12 @@ static int mvneta_xdp_setup(struct net_device *dev, struct bpf_prog *prog, return -EOPNOTSUPP; } + if (pp->bm_priv) { + NL_SET_ERR_MSG_MOD(extack, + "Hardware Buffer Management not supported on XDP"); + return -EOPNOTSUPP; + } + need_update = !!pp->xdp_prog != !!prog; if (running && need_update) mvneta_stop(dev); -- cgit v1.2.1 From 165475779ba1d4758429f48d65960473df9dc35a Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Mon, 27 Jan 2020 18:35:15 +0530 Subject: octeontx2-pf: Add Marvell OcteonTX2 NIC driver This patch adds template for the Marvell's OcteonTX2 network controller's physical function driver. Just the probe, PCI specific initialization and netdev registration. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/octeontx2/Kconfig | 8 + drivers/net/ethernet/marvell/octeontx2/Makefile | 2 + .../net/ethernet/marvell/octeontx2/nic/Makefile | 10 + .../ethernet/marvell/octeontx2/nic/otx2_common.h | 77 ++++++++ .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 214 +++++++++++++++++++++ .../net/ethernet/marvell/octeontx2/nic/otx2_reg.h | 51 +++++ 6 files changed, 362 insertions(+) create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/Makefile create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/Kconfig b/drivers/net/ethernet/marvell/octeontx2/Kconfig index fb34fbd62088..ced514c05c97 100644 --- a/drivers/net/ethernet/marvell/octeontx2/Kconfig +++ b/drivers/net/ethernet/marvell/octeontx2/Kconfig @@ -25,3 +25,11 @@ config NDC_DIS_DYNAMIC_CACHING This config option disables caching of dynamic entries such as NIX SQEs , NPA stack pages etc in NDC. Also locks down NIX SQ/CQ/RQ/RSS and NPA Aura/Pool contexts. + +config OCTEONTX2_PF + tristate "Marvell OcteonTX2 NIC Physical Function driver" + select OCTEONTX2_MBOX + depends on (64BIT && COMPILE_TEST) || ARM64 + depends on PCI + help + This driver supports Marvell's OcteonTX2 NIC physical function. diff --git a/drivers/net/ethernet/marvell/octeontx2/Makefile b/drivers/net/ethernet/marvell/octeontx2/Makefile index e579dcd54c97..0064a69e0f72 100644 --- a/drivers/net/ethernet/marvell/octeontx2/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/Makefile @@ -3,4 +3,6 @@ # Makefile for Marvell OcteonTX2 device drivers. # +obj-$(CONFIG_OCTEONTX2_MBOX) += af/ obj-$(CONFIG_OCTEONTX2_AF) += af/ +obj-$(CONFIG_OCTEONTX2_PF) += nic/ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile new file mode 100644 index 000000000000..622b803e1a5d --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Marvell's OcteonTX2 ethernet device drivers +# + +obj-$(CONFIG_OCTEONTX2_PF) += octeontx2_nicpf.o + +octeontx2_nicpf-y := otx2_pf.o + +ccflags-y += -I$(srctree)/drivers/net/ethernet/marvell/octeontx2/af diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h new file mode 100644 index 000000000000..9d52ab364471 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2020 Marvell International Ltd. + * + * 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. + */ + +#ifndef OTX2_COMMON_H +#define OTX2_COMMON_H + +#include + +#include "otx2_reg.h" + +/* PCI device IDs */ +#define PCI_DEVID_OCTEONTX2_RVU_PF 0xA063 + +/* PCI BAR nos */ +#define PCI_CFG_REG_BAR_NUM 2 + +struct otx2_hw { + struct pci_dev *pdev; + u16 rx_queues; + u16 tx_queues; + u16 max_queues; +}; + +struct otx2_nic { + void __iomem *reg_base; + struct net_device *netdev; + + struct otx2_hw hw; + struct pci_dev *pdev; + struct device *dev; +}; + +/* Register read/write APIs */ +static inline void __iomem *otx2_get_regaddr(struct otx2_nic *nic, u64 offset) +{ + u64 blkaddr; + + switch ((offset >> RVU_FUNC_BLKADDR_SHIFT) & RVU_FUNC_BLKADDR_MASK) { + case BLKTYPE_NIX: + blkaddr = BLKADDR_NIX0; + break; + case BLKTYPE_NPA: + blkaddr = BLKADDR_NPA; + break; + default: + blkaddr = BLKADDR_RVUM; + break; + }; + + offset &= ~(RVU_FUNC_BLKADDR_MASK << RVU_FUNC_BLKADDR_SHIFT); + offset |= (blkaddr << RVU_FUNC_BLKADDR_SHIFT); + + return nic->reg_base + offset; +} + +static inline void otx2_write64(struct otx2_nic *nic, u64 offset, u64 val) +{ + void __iomem *addr = otx2_get_regaddr(nic, offset); + + writeq(val, addr); +} + +static inline u64 otx2_read64(struct otx2_nic *nic, u64 offset) +{ + void __iomem *addr = otx2_get_regaddr(nic, offset); + + return readq(addr); +} + +#endif /* OTX2_COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c new file mode 100644 index 000000000000..d3af2006ce3c --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Physcial Function ethernet driver + * + * Copyright (C) 2020 Marvell International Ltd. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "otx2_common.h" + +#define DRV_NAME "octeontx2-nicpf" +#define DRV_STRING "Marvell OcteonTX2 NIC Physical Function Driver" +#define DRV_VERSION "1.0" + +/* Supported devices */ +static const struct pci_device_id otx2_pf_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_RVU_PF) }, + { 0, } /* end of table */ +}; + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION(DRV_STRING); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRV_VERSION); +MODULE_DEVICE_TABLE(pci, otx2_pf_id_table); + +static int otx2_set_real_num_queues(struct net_device *netdev, + int tx_queues, int rx_queues) +{ + int err; + + err = netif_set_real_num_tx_queues(netdev, tx_queues); + if (err) { + netdev_err(netdev, + "Failed to set no of Tx queues: %d\n", tx_queues); + return err; + } + + err = netif_set_real_num_rx_queues(netdev, rx_queues); + if (err) + netdev_err(netdev, + "Failed to set no of Rx queues: %d\n", rx_queues); + return err; +} + +static int otx2_open(struct net_device *netdev) +{ + netif_carrier_off(netdev); + + return 0; +} + +static int otx2_stop(struct net_device *netdev) +{ + return 0; +} + +static const struct net_device_ops otx2_netdev_ops = { + .ndo_open = otx2_open, + .ndo_stop = otx2_stop, +}; + +static int otx2_check_pf_usable(struct otx2_nic *nic) +{ + u64 rev; + + rev = otx2_read64(nic, RVU_PF_BLOCK_ADDRX_DISC(BLKADDR_RVUM)); + rev = (rev >> 12) & 0xFF; + /* Check if AF has setup revision for RVUM block, + * otherwise this driver probe should be deferred + * until AF driver comes up. + */ + if (!rev) { + dev_warn(nic->dev, + "AF is not initialized, deferring probe\n"); + return -EPROBE_DEFER; + } + return 0; +} + +static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct net_device *netdev; + struct otx2_nic *pf; + struct otx2_hw *hw; + int err, qcount; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device\n"); + return err; + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + dev_err(dev, "PCI request regions failed 0x%x\n", err); + return err; + } + + err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); + if (err) { + dev_err(dev, "DMA mask config failed, abort\n"); + goto err_release_regions; + } + + pci_set_master(pdev); + + /* Set number of queues */ + qcount = min_t(int, num_online_cpus(), num_online_cpus()); + + netdev = alloc_etherdev_mqs(sizeof(*pf), qcount, qcount); + if (!netdev) { + err = -ENOMEM; + goto err_release_regions; + } + + pci_set_drvdata(pdev, netdev); + SET_NETDEV_DEV(netdev, &pdev->dev); + pf = netdev_priv(netdev); + pf->netdev = netdev; + pf->pdev = pdev; + pf->dev = dev; + + hw = &pf->hw; + hw->pdev = pdev; + hw->rx_queues = qcount; + hw->tx_queues = qcount; + hw->max_queues = qcount; + + /* Map CSRs */ + pf->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0); + if (!pf->reg_base) { + dev_err(dev, "Unable to map physical function CSRs, aborting\n"); + err = -ENOMEM; + goto err_free_netdev; + } + + err = otx2_check_pf_usable(pf); + if (err) + goto err_free_netdev; + + err = otx2_set_real_num_queues(netdev, hw->tx_queues, hw->rx_queues); + if (err) + goto err_free_netdev; + + netdev->netdev_ops = &otx2_netdev_ops; + + err = register_netdev(netdev); + if (err) { + dev_err(dev, "Failed to register netdevice\n"); + goto err_free_netdev; + } + + return 0; + +err_free_netdev: + pci_set_drvdata(pdev, NULL); + free_netdev(netdev); +err_release_regions: + pci_release_regions(pdev); + return err; +} + +static void otx2_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct otx2_nic *pf; + + if (!netdev) + return; + + pf = netdev_priv(netdev); + + unregister_netdev(netdev); + pci_free_irq_vectors(pf->pdev); + pci_set_drvdata(pdev, NULL); + free_netdev(netdev); + pci_release_regions(pdev); +} + +static struct pci_driver otx2_pf_driver = { + .name = DRV_NAME, + .id_table = otx2_pf_id_table, + .probe = otx2_probe, + .shutdown = otx2_remove, + .remove = otx2_remove, +}; + +static int __init otx2_rvupf_init_module(void) +{ + pr_info("%s: %s\n", DRV_NAME, DRV_STRING); + + return pci_register_driver(&otx2_pf_driver); +} + +static void __exit otx2_rvupf_cleanup_module(void) +{ + pci_unregister_driver(&otx2_pf_driver); +} + +module_init(otx2_rvupf_init_module); +module_exit(otx2_rvupf_cleanup_module); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h new file mode 100644 index 000000000000..d0bd64ab4f1a --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2020 Marvell International Ltd. + * + * 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. + */ + +#ifndef OTX2_REG_H +#define OTX2_REG_H + +#include + +/* RVU PF registers */ +#define RVU_PF_VFX_PFVF_MBOX0 (0x00000) +#define RVU_PF_VFX_PFVF_MBOX1 (0x00008) +#define RVU_PF_VFX_PFVF_MBOXX(a, b) (0x0 | (a) << 12 | (b) << 3) +#define RVU_PF_VF_BAR4_ADDR (0x10) +#define RVU_PF_BLOCK_ADDRX_DISC(a) (0x200 | (a) << 3) +#define RVU_PF_VFME_STATUSX(a) (0x800 | (a) << 3) +#define RVU_PF_VFTRPENDX(a) (0x820 | (a) << 3) +#define RVU_PF_VFTRPEND_W1SX(a) (0x840 | (a) << 3) +#define RVU_PF_VFPF_MBOX_INTX(a) (0x880 | (a) << 3) +#define RVU_PF_VFPF_MBOX_INT_W1SX(a) (0x8A0 | (a) << 3) +#define RVU_PF_VFPF_MBOX_INT_ENA_W1SX(a) (0x8C0 | (a) << 3) +#define RVU_PF_VFPF_MBOX_INT_ENA_W1CX(a) (0x8E0 | (a) << 3) +#define RVU_PF_VFFLR_INTX(a) (0x900 | (a) << 3) +#define RVU_PF_VFFLR_INT_W1SX(a) (0x920 | (a) << 3) +#define RVU_PF_VFFLR_INT_ENA_W1SX(a) (0x940 | (a) << 3) +#define RVU_PF_VFFLR_INT_ENA_W1CX(a) (0x960 | (a) << 3) +#define RVU_PF_VFME_INTX(a) (0x980 | (a) << 3) +#define RVU_PF_VFME_INT_W1SX(a) (0x9A0 | (a) << 3) +#define RVU_PF_VFME_INT_ENA_W1SX(a) (0x9C0 | (a) << 3) +#define RVU_PF_VFME_INT_ENA_W1CX(a) (0x9E0 | (a) << 3) +#define RVU_PF_PFAF_MBOX0 (0xC00) +#define RVU_PF_PFAF_MBOX1 (0xC08) +#define RVU_PF_PFAF_MBOXX(a) (0xC00 | (a) << 3) +#define RVU_PF_INT (0xc20) +#define RVU_PF_INT_W1S (0xc28) +#define RVU_PF_INT_ENA_W1S (0xc30) +#define RVU_PF_INT_ENA_W1C (0xc38) +#define RVU_PF_MSIX_VECX_ADDR(a) (0x000 | (a) << 4) +#define RVU_PF_MSIX_VECX_CTL(a) (0x008 | (a) << 4) +#define RVU_PF_MSIX_PBAX(a) (0xF0000 | (a) << 3) + +#define RVU_FUNC_BLKADDR_SHIFT 20 +#define RVU_FUNC_BLKADDR_MASK 0x1FULL + +#endif /* OTX2_REG_H */ -- cgit v1.2.1 From 5a6d7c9daef392370d873f8a626bb525629cdd6a Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Mon, 27 Jan 2020 18:35:16 +0530 Subject: octeontx2-pf: Mailbox communication with AF In the resource virtualization unit (RVU) each of the PF and AF (admin function) share a 64KB of reserved memory region for communication. This patch initializes PF <=> AF mailbox IRQs, registers handlers for processing these communication messages. Also adds support to process these messages in both directions ie responses to PF initiated DOWN (PF => AF) messages and AF initiated UP messages (AF => PF). Mbox communication APIs and message formats are defined in AF driver (drivers/net/ethernet/marvell/octeontx2/af), mbox.h from AF driver is included here to avoid duplication. Signed-off-by: Geetha sowjanya Signed-off-by: Christina Jacob Signed-off-by: Subbaraya Sundeep Signed-off-by: Aleksey Makarov Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- .../net/ethernet/marvell/octeontx2/nic/Makefile | 2 +- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 28 ++ .../ethernet/marvell/octeontx2/nic/otx2_common.h | 166 ++++++++++ .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 359 ++++++++++++++++++++- 4 files changed, 552 insertions(+), 3 deletions(-) create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile index 622b803e1a5d..339fde892a36 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_OCTEONTX2_PF) += octeontx2_nicpf.o -octeontx2_nicpf-y := otx2_pf.o +octeontx2_nicpf-y := otx2_pf.o otx2_common.o ccflags-y += -I$(srctree)/drivers/net/ethernet/marvell/octeontx2/af diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c new file mode 100644 index 000000000000..cbab325872bc --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2020 Marvell International Ltd. + * + * 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. + */ + +#include +#include + +#include "otx2_reg.h" +#include "otx2_common.h" + +#define M(_name, _id, _fn_name, _req_type, _rsp_type) \ +int __weak \ +otx2_mbox_up_handler_ ## _fn_name(struct otx2_nic *pfvf, \ + struct _req_type *req, \ + struct _rsp_type *rsp) \ +{ \ + /* Nothing to do here */ \ + return 0; \ +} \ +EXPORT_SYMBOL(otx2_mbox_up_handler_ ## _fn_name); +MBOX_UP_CGX_MESSAGES +#undef M diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 9d52ab364471..a21eaaf6e824 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -13,6 +13,7 @@ #include +#include #include "otx2_reg.h" /* PCI device IDs */ @@ -20,12 +21,31 @@ /* PCI BAR nos */ #define PCI_CFG_REG_BAR_NUM 2 +#define PCI_MBOX_BAR_NUM 4 + +#define NAME_SIZE 32 + +struct mbox { + struct otx2_mbox mbox; + struct work_struct mbox_wrk; + struct otx2_mbox mbox_up; + struct work_struct mbox_up_wrk; + struct otx2_nic *pfvf; + void *bbuf_base; /* Bounce buffer for mbox memory */ + struct mutex lock; /* serialize mailbox access */ + int num_msgs; /* mbox number of messages */ + int up_num_msgs; /* mbox_up number of messages */ +}; struct otx2_hw { struct pci_dev *pdev; u16 rx_queues; u16 tx_queues; u16 max_queues; + + /* MSI-X */ + char *irq_name; + cpumask_var_t *affinity_mask; }; struct otx2_nic { @@ -35,6 +55,12 @@ struct otx2_nic { struct otx2_hw hw; struct pci_dev *pdev; struct device *dev; + + /* Mbox */ + struct mbox mbox; + struct workqueue_struct *mbox_wq; + + u16 pcifunc; /* RVU PF_FUNC */ }; /* Register read/write APIs */ @@ -74,4 +100,144 @@ static inline u64 otx2_read64(struct otx2_nic *nic, u64 offset) return readq(addr); } +/* Mbox bounce buffer APIs */ +static inline int otx2_mbox_bbuf_init(struct mbox *mbox, struct pci_dev *pdev) +{ + struct otx2_mbox *otx2_mbox; + struct otx2_mbox_dev *mdev; + + mbox->bbuf_base = devm_kmalloc(&pdev->dev, MBOX_SIZE, GFP_KERNEL); + if (!mbox->bbuf_base) + return -ENOMEM; + + /* Overwrite mbox mbase to point to bounce buffer, so that PF/VF + * prepare all mbox messages in bounce buffer instead of directly + * in hw mbox memory. + */ + otx2_mbox = &mbox->mbox; + mdev = &otx2_mbox->dev[0]; + mdev->mbase = mbox->bbuf_base; + + otx2_mbox = &mbox->mbox_up; + mdev = &otx2_mbox->dev[0]; + mdev->mbase = mbox->bbuf_base; + return 0; +} + +static inline void otx2_sync_mbox_bbuf(struct otx2_mbox *mbox, int devid) +{ + u16 msgs_offset = ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN); + void *hw_mbase = mbox->hwbase + (devid * MBOX_SIZE); + struct otx2_mbox_dev *mdev = &mbox->dev[devid]; + struct mbox_hdr *hdr; + u64 msg_size; + + if (mdev->mbase == hw_mbase) + return; + + hdr = hw_mbase + mbox->rx_start; + msg_size = hdr->msg_size; + + if (msg_size > mbox->rx_size - msgs_offset) + msg_size = mbox->rx_size - msgs_offset; + + /* Copy mbox messages from mbox memory to bounce buffer */ + memcpy(mdev->mbase + mbox->rx_start, + hw_mbase + mbox->rx_start, msg_size + msgs_offset); +} + +static inline void otx2_mbox_lock_init(struct mbox *mbox) +{ + mutex_init(&mbox->lock); +} + +static inline void otx2_mbox_lock(struct mbox *mbox) +{ + mutex_lock(&mbox->lock); +} + +static inline void otx2_mbox_unlock(struct mbox *mbox) +{ + mutex_unlock(&mbox->lock); +} + +/* Mbox APIs */ +static inline int otx2_sync_mbox_msg(struct mbox *mbox) +{ + int err; + + if (!otx2_mbox_nonempty(&mbox->mbox, 0)) + return 0; + otx2_mbox_msg_send(&mbox->mbox, 0); + err = otx2_mbox_wait_for_rsp(&mbox->mbox, 0); + if (err) + return err; + + return otx2_mbox_check_rsp_msgs(&mbox->mbox, 0); +} + +static inline int otx2_sync_mbox_up_msg(struct mbox *mbox, int devid) +{ + int err; + + if (!otx2_mbox_nonempty(&mbox->mbox_up, devid)) + return 0; + otx2_mbox_msg_send(&mbox->mbox_up, devid); + err = otx2_mbox_wait_for_rsp(&mbox->mbox_up, devid); + if (err) + return err; + + return otx2_mbox_check_rsp_msgs(&mbox->mbox_up, devid); +} + +/* Use this API to send mbox msgs in atomic context + * where sleeping is not allowed + */ +static inline int otx2_sync_mbox_msg_busy_poll(struct mbox *mbox) +{ + int err; + + if (!otx2_mbox_nonempty(&mbox->mbox, 0)) + return 0; + otx2_mbox_msg_send(&mbox->mbox, 0); + err = otx2_mbox_busy_poll_for_rsp(&mbox->mbox, 0); + if (err) + return err; + + return otx2_mbox_check_rsp_msgs(&mbox->mbox, 0); +} + +#define M(_name, _id, _fn_name, _req_type, _rsp_type) \ +static struct _req_type __maybe_unused \ +*otx2_mbox_alloc_msg_ ## _fn_name(struct mbox *mbox) \ +{ \ + struct _req_type *req; \ + \ + req = (struct _req_type *)otx2_mbox_alloc_msg_rsp( \ + &mbox->mbox, 0, sizeof(struct _req_type), \ + sizeof(struct _rsp_type)); \ + if (!req) \ + return NULL; \ + req->hdr.sig = OTX2_MBOX_REQ_SIG; \ + req->hdr.id = _id; \ + return req; \ +} + +MBOX_MESSAGES +#undef M + +#define M(_name, _id, _fn_name, _req_type, _rsp_type) \ +int \ +otx2_mbox_up_handler_ ## _fn_name(struct otx2_nic *pfvf, \ + struct _req_type *req, \ + struct _rsp_type *rsp); \ + +MBOX_UP_CGX_MESSAGES +#undef M + +#define RVU_PFVF_PF_SHIFT 10 +#define RVU_PFVF_PF_MASK 0x3F +#define RVU_PFVF_FUNC_SHIFT 0 +#define RVU_PFVF_FUNC_MASK 0x3FF + #endif /* OTX2_COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index d3af2006ce3c..e21bc10be3dd 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -35,6 +35,322 @@ MODULE_LICENSE("GPL v2"); MODULE_VERSION(DRV_VERSION); MODULE_DEVICE_TABLE(pci, otx2_pf_id_table); +enum { + TYPE_PFAF, + TYPE_PFVF, +}; + +static void otx2_queue_work(struct mbox *mw, struct workqueue_struct *mbox_wq, + int first, int mdevs, u64 intr, int type) +{ + struct otx2_mbox_dev *mdev; + struct otx2_mbox *mbox; + struct mbox_hdr *hdr; + int i; + + for (i = first; i < mdevs; i++) { + /* start from 0 */ + if (!(intr & BIT_ULL(i - first))) + continue; + + mbox = &mw->mbox; + mdev = &mbox->dev[i]; + if (type == TYPE_PFAF) + otx2_sync_mbox_bbuf(mbox, i); + hdr = mdev->mbase + mbox->rx_start; + /* The hdr->num_msgs is set to zero immediately in the interrupt + * handler to ensure that it holds a correct value next time + * when the interrupt handler is called. + * pf->mbox.num_msgs holds the data for use in pfaf_mbox_handler + * pf>mbox.up_num_msgs holds the data for use in + * pfaf_mbox_up_handler. + */ + if (hdr->num_msgs) { + mw[i].num_msgs = hdr->num_msgs; + hdr->num_msgs = 0; + if (type == TYPE_PFAF) + memset(mbox->hwbase + mbox->rx_start, 0, + ALIGN(sizeof(struct mbox_hdr), + sizeof(u64))); + + queue_work(mbox_wq, &mw[i].mbox_wrk); + } + + mbox = &mw->mbox_up; + mdev = &mbox->dev[i]; + if (type == TYPE_PFAF) + otx2_sync_mbox_bbuf(mbox, i); + hdr = mdev->mbase + mbox->rx_start; + if (hdr->num_msgs) { + mw[i].up_num_msgs = hdr->num_msgs; + hdr->num_msgs = 0; + if (type == TYPE_PFAF) + memset(mbox->hwbase + mbox->rx_start, 0, + ALIGN(sizeof(struct mbox_hdr), + sizeof(u64))); + + queue_work(mbox_wq, &mw[i].mbox_up_wrk); + } + } +} + +static void otx2_process_pfaf_mbox_msg(struct otx2_nic *pf, + struct mbox_msghdr *msg) +{ + if (msg->id >= MBOX_MSG_MAX) { + dev_err(pf->dev, + "Mbox msg with unknown ID 0x%x\n", msg->id); + return; + } + + if (msg->sig != OTX2_MBOX_RSP_SIG) { + dev_err(pf->dev, + "Mbox msg with wrong signature %x, ID 0x%x\n", + msg->sig, msg->id); + return; + } + + switch (msg->id) { + case MBOX_MSG_READY: + pf->pcifunc = msg->pcifunc; + break; + default: + if (msg->rc) + dev_err(pf->dev, + "Mbox msg response has err %d, ID 0x%x\n", + msg->rc, msg->id); + break; + } +} + +static void otx2_pfaf_mbox_handler(struct work_struct *work) +{ + struct otx2_mbox_dev *mdev; + struct mbox_hdr *rsp_hdr; + struct mbox_msghdr *msg; + struct otx2_mbox *mbox; + struct mbox *af_mbox; + struct otx2_nic *pf; + int offset, id; + + af_mbox = container_of(work, struct mbox, mbox_wrk); + mbox = &af_mbox->mbox; + mdev = &mbox->dev[0]; + rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + + offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); + pf = af_mbox->pfvf; + + for (id = 0; id < af_mbox->num_msgs; id++) { + msg = (struct mbox_msghdr *)(mdev->mbase + offset); + otx2_process_pfaf_mbox_msg(pf, msg); + offset = mbox->rx_start + msg->next_msgoff; + mdev->msgs_acked++; + } + + otx2_mbox_reset(mbox, 0); +} + +static int otx2_process_mbox_msg_up(struct otx2_nic *pf, + struct mbox_msghdr *req) +{ + /* Check if valid, if not reply with a invalid msg */ + if (req->sig != OTX2_MBOX_REQ_SIG) { + otx2_reply_invalid_msg(&pf->mbox.mbox_up, 0, 0, req->id); + return -ENODEV; + } + + switch (req->id) { +#define M(_name, _id, _fn_name, _req_type, _rsp_type) \ + case _id: { \ + struct _rsp_type *rsp; \ + int err; \ + \ + rsp = (struct _rsp_type *)otx2_mbox_alloc_msg( \ + &pf->mbox.mbox_up, 0, \ + sizeof(struct _rsp_type)); \ + if (!rsp) \ + return -ENOMEM; \ + \ + rsp->hdr.id = _id; \ + rsp->hdr.sig = OTX2_MBOX_RSP_SIG; \ + rsp->hdr.pcifunc = 0; \ + rsp->hdr.rc = 0; \ + \ + err = otx2_mbox_up_handler_ ## _fn_name( \ + pf, (struct _req_type *)req, rsp); \ + return err; \ + } +MBOX_UP_CGX_MESSAGES +#undef M + break; + default: + otx2_reply_invalid_msg(&pf->mbox.mbox_up, 0, 0, req->id); + return -ENODEV; + } + return 0; +} + +static void otx2_pfaf_mbox_up_handler(struct work_struct *work) +{ + struct mbox *af_mbox = container_of(work, struct mbox, mbox_up_wrk); + struct otx2_mbox *mbox = &af_mbox->mbox_up; + struct otx2_mbox_dev *mdev = &mbox->dev[0]; + struct otx2_nic *pf = af_mbox->pfvf; + int offset, id, devid = 0; + struct mbox_hdr *rsp_hdr; + struct mbox_msghdr *msg; + + rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); + + offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); + + for (id = 0; id < af_mbox->up_num_msgs; id++) { + msg = (struct mbox_msghdr *)(mdev->mbase + offset); + + devid = msg->pcifunc & RVU_PFVF_FUNC_MASK; + /* Skip processing VF's messages */ + if (!devid) + otx2_process_mbox_msg_up(pf, msg); + offset = mbox->rx_start + msg->next_msgoff; + } + + otx2_mbox_msg_send(mbox, 0); +} + +static irqreturn_t otx2_pfaf_mbox_intr_handler(int irq, void *pf_irq) +{ + struct otx2_nic *pf = (struct otx2_nic *)pf_irq; + struct mbox *mbox; + + /* Clear the IRQ */ + otx2_write64(pf, RVU_PF_INT, BIT_ULL(0)); + + mbox = &pf->mbox; + otx2_queue_work(mbox, pf->mbox_wq, 0, 1, 1, TYPE_PFAF); + + return IRQ_HANDLED; +} + +static void otx2_disable_mbox_intr(struct otx2_nic *pf) +{ + int vector = pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_AFPF_MBOX); + + /* Disable AF => PF mailbox IRQ */ + otx2_write64(pf, RVU_PF_INT_ENA_W1C, BIT_ULL(0)); + free_irq(vector, pf); +} + +static int otx2_register_mbox_intr(struct otx2_nic *pf, bool probe_af) +{ + struct otx2_hw *hw = &pf->hw; + struct msg_req *req; + char *irq_name; + int err; + + /* Register mailbox interrupt handler */ + irq_name = &hw->irq_name[RVU_PF_INT_VEC_AFPF_MBOX * NAME_SIZE]; + snprintf(irq_name, NAME_SIZE, "RVUPFAF Mbox"); + err = request_irq(pci_irq_vector(pf->pdev, RVU_PF_INT_VEC_AFPF_MBOX), + otx2_pfaf_mbox_intr_handler, 0, irq_name, pf); + if (err) { + dev_err(pf->dev, + "RVUPF: IRQ registration failed for PFAF mbox irq\n"); + return err; + } + + /* Enable mailbox interrupt for msgs coming from AF. + * First clear to avoid spurious interrupts, if any. + */ + otx2_write64(pf, RVU_PF_INT, BIT_ULL(0)); + otx2_write64(pf, RVU_PF_INT_ENA_W1S, BIT_ULL(0)); + + if (!probe_af) + return 0; + + /* Check mailbox communication with AF */ + req = otx2_mbox_alloc_msg_ready(&pf->mbox); + if (!req) { + otx2_disable_mbox_intr(pf); + return -ENOMEM; + } + err = otx2_sync_mbox_msg(&pf->mbox); + if (err) { + dev_warn(pf->dev, + "AF not responding to mailbox, deferring probe\n"); + otx2_disable_mbox_intr(pf); + return -EPROBE_DEFER; + } + + return 0; +} + +static void otx2_pfaf_mbox_destroy(struct otx2_nic *pf) +{ + struct mbox *mbox = &pf->mbox; + + if (pf->mbox_wq) { + flush_workqueue(pf->mbox_wq); + destroy_workqueue(pf->mbox_wq); + pf->mbox_wq = NULL; + } + + if (mbox->mbox.hwbase) + iounmap((void __iomem *)mbox->mbox.hwbase); + + otx2_mbox_destroy(&mbox->mbox); + otx2_mbox_destroy(&mbox->mbox_up); +} + +static int otx2_pfaf_mbox_init(struct otx2_nic *pf) +{ + struct mbox *mbox = &pf->mbox; + void __iomem *hwbase; + int err; + + mbox->pfvf = pf; + pf->mbox_wq = alloc_workqueue("otx2_pfaf_mailbox", + WQ_UNBOUND | WQ_HIGHPRI | + WQ_MEM_RECLAIM, 1); + if (!pf->mbox_wq) + return -ENOMEM; + + /* Mailbox is a reserved memory (in RAM) region shared between + * admin function (i.e AF) and this PF, shouldn't be mapped as + * device memory to allow unaligned accesses. + */ + hwbase = ioremap_wc(pci_resource_start(pf->pdev, PCI_MBOX_BAR_NUM), + pci_resource_len(pf->pdev, PCI_MBOX_BAR_NUM)); + if (!hwbase) { + dev_err(pf->dev, "Unable to map PFAF mailbox region\n"); + err = -ENOMEM; + goto exit; + } + + err = otx2_mbox_init(&mbox->mbox, hwbase, pf->pdev, pf->reg_base, + MBOX_DIR_PFAF, 1); + if (err) + goto exit; + + err = otx2_mbox_init(&mbox->mbox_up, hwbase, pf->pdev, pf->reg_base, + MBOX_DIR_PFAF_UP, 1); + if (err) + goto exit; + + err = otx2_mbox_bbuf_init(mbox, pf->pdev); + if (err) + goto exit; + + INIT_WORK(&mbox->mbox_wrk, otx2_pfaf_mbox_handler); + INIT_WORK(&mbox->mbox_up_wrk, otx2_pfaf_mbox_up_handler); + otx2_mbox_lock_init(&pf->mbox); + + return 0; +exit: + otx2_pfaf_mbox_destroy(pf); + return err; +} + static int otx2_set_real_num_queues(struct net_device *netdev, int tx_queues, int rx_queues) { @@ -96,6 +412,7 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct otx2_nic *pf; struct otx2_hw *hw; int err, qcount; + int num_vec; err = pcim_enable_device(pdev); if (err) { @@ -139,6 +456,17 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) hw->tx_queues = qcount; hw->max_queues = qcount; + 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) + goto err_free_netdev; + + hw->affinity_mask = devm_kcalloc(&hw->pdev->dev, num_vec, + sizeof(cpumask_var_t), GFP_KERNEL); + if (!hw->affinity_mask) + goto err_free_netdev; + /* Map CSRs */ pf->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0); if (!pf->reg_base) { @@ -151,20 +479,44 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto err_free_netdev; + err = pci_alloc_irq_vectors(hw->pdev, RVU_PF_INT_VEC_CNT, + RVU_PF_INT_VEC_CNT, PCI_IRQ_MSIX); + if (err < 0) { + dev_err(dev, "%s: Failed to alloc %d IRQ vectors\n", + __func__, num_vec); + goto err_free_netdev; + } + + /* Init PF <=> AF mailbox stuff */ + err = otx2_pfaf_mbox_init(pf); + if (err) + goto err_free_irq_vectors; + + /* Register mailbox interrupt */ + err = otx2_register_mbox_intr(pf, true); + if (err) + goto err_mbox_destroy; + err = otx2_set_real_num_queues(netdev, hw->tx_queues, hw->rx_queues); if (err) - goto err_free_netdev; + goto err_disable_mbox_intr; netdev->netdev_ops = &otx2_netdev_ops; err = register_netdev(netdev); if (err) { dev_err(dev, "Failed to register netdevice\n"); - goto err_free_netdev; + goto err_disable_mbox_intr; } return 0; +err_disable_mbox_intr: + otx2_disable_mbox_intr(pf); +err_mbox_destroy: + otx2_pfaf_mbox_destroy(pf); +err_free_irq_vectors: + pci_free_irq_vectors(hw->pdev); err_free_netdev: pci_set_drvdata(pdev, NULL); free_netdev(netdev); @@ -184,9 +536,12 @@ static void otx2_remove(struct pci_dev *pdev) pf = netdev_priv(netdev); unregister_netdev(netdev); + otx2_disable_mbox_intr(pf); + otx2_pfaf_mbox_destroy(pf); pci_free_irq_vectors(pf->pdev); pci_set_drvdata(pdev, NULL); free_netdev(netdev); + pci_release_regions(pdev); } -- cgit v1.2.1 From 05fcc9e08955b7c04afb7f53402902f4a86e553c Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Mon, 27 Jan 2020 18:35:17 +0530 Subject: octeontx2-pf: Attach NIX and NPA block LFs For a PF to function as a NIC, NPA (for Rx buffers, Tx descriptors etc) and NIX (for rcv, send and completion queues) are the minimum resources needed. So request admin function (AF) to attach one each of NIX and NPA block LFs (local functions). Only AF can configure a LF's contexts, so request AF to allocate memory for NPA aura/pool and NIX RQ/SQ/CQ HW contexts. Upon receiving response, save some of the HW constants like number of pointers per stack page, size of send queue buffer (SQBs, where SQEs are queued by HW) e.t.c which are later used to initialize queues. A HW context here is like a state machine maintained for a descriptor queue. eg size, head/tail pointers, irq etc etc. HW maintains this in memory. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 167 +++++++++++++++++++++ .../ethernet/marvell/octeontx2/nic/otx2_common.h | 36 +++++ .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 67 ++++++++- .../ethernet/marvell/octeontx2/nic/otx2_struct.h | 20 +++ 4 files changed, 286 insertions(+), 4 deletions(-) create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index cbab325872bc..1fa09e9cfeb2 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -13,6 +13,173 @@ #include "otx2_reg.h" #include "otx2_common.h" +#include "otx2_struct.h" + +int otx2_config_nix(struct otx2_nic *pfvf) +{ + struct nix_lf_alloc_req *nixlf; + struct nix_lf_alloc_rsp *rsp; + int err; + + pfvf->qset.xqe_size = NIX_XQESZ_W16 ? 128 : 512; + + /* Get memory to put this msg */ + nixlf = otx2_mbox_alloc_msg_nix_lf_alloc(&pfvf->mbox); + if (!nixlf) + return -ENOMEM; + + /* Set RQ/SQ/CQ counts */ + nixlf->rq_cnt = pfvf->hw.rx_queues; + nixlf->sq_cnt = pfvf->hw.tx_queues; + nixlf->cq_cnt = pfvf->qset.cq_cnt; + nixlf->xqe_sz = NIX_XQESZ_W16; + /* We don't know absolute NPA LF idx attached. + * AF will replace 'RVU_DEFAULT_PF_FUNC' with + * NPA LF attached to this RVU PF/VF. + */ + nixlf->npa_func = RVU_DEFAULT_PF_FUNC; + /* Disable alignment pad, enable L2 length check, + * enable L4 TCP/UDP checksum verification. + */ + nixlf->rx_cfg = BIT_ULL(33) | BIT_ULL(35) | BIT_ULL(37); + + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) + return err; + + rsp = (struct nix_lf_alloc_rsp *)otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, + &nixlf->hdr); + if (IS_ERR(rsp)) + return PTR_ERR(rsp); + + if (rsp->qints < 1) + return -ENXIO; + + return rsp->hdr.rc; +} + +int otx2_config_npa(struct otx2_nic *pfvf) +{ + struct otx2_qset *qset = &pfvf->qset; + struct npa_lf_alloc_req *npalf; + struct otx2_hw *hw = &pfvf->hw; + int aura_cnt; + + /* Pool - Stack of free buffer pointers + * Aura - Alloc/frees pointers from/to pool for NIX DMA. + */ + + if (!hw->pool_cnt) + return -EINVAL; + + qset->pool = devm_kzalloc(pfvf->dev, sizeof(struct otx2_pool) * + hw->pool_cnt, GFP_KERNEL); + if (!qset->pool) + return -ENOMEM; + + /* Get memory to put this msg */ + npalf = otx2_mbox_alloc_msg_npa_lf_alloc(&pfvf->mbox); + if (!npalf) + return -ENOMEM; + + /* Set aura and pool counts */ + npalf->nr_pools = hw->pool_cnt; + aura_cnt = ilog2(roundup_pow_of_two(hw->pool_cnt)); + npalf->aura_sz = (aura_cnt >= ilog2(128)) ? (aura_cnt - 6) : 1; + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +int otx2_detach_resources(struct mbox *mbox) +{ + struct rsrc_detach *detach; + + otx2_mbox_lock(mbox); + detach = otx2_mbox_alloc_msg_detach_resources(mbox); + if (!detach) { + otx2_mbox_unlock(mbox); + return -ENOMEM; + } + + /* detach all */ + detach->partial = false; + + /* Send detach request to AF */ + otx2_mbox_msg_send(&mbox->mbox, 0); + otx2_mbox_unlock(mbox); + return 0; +} + +int otx2_attach_npa_nix(struct otx2_nic *pfvf) +{ + struct rsrc_attach *attach; + struct msg_req *msix; + int err; + + otx2_mbox_lock(&pfvf->mbox); + /* Get memory to put this msg */ + attach = otx2_mbox_alloc_msg_attach_resources(&pfvf->mbox); + if (!attach) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + attach->npalf = true; + attach->nixlf = true; + + /* Send attach request to AF */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + otx2_mbox_unlock(&pfvf->mbox); + return err; + } + + /* Get NPA and NIX MSIX vector offsets */ + msix = otx2_mbox_alloc_msg_msix_offset(&pfvf->mbox); + if (!msix) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + otx2_mbox_unlock(&pfvf->mbox); + return err; + } + otx2_mbox_unlock(&pfvf->mbox); + + if (pfvf->hw.npa_msixoff == MSIX_VECTOR_INVALID || + pfvf->hw.nix_msixoff == MSIX_VECTOR_INVALID) { + dev_err(pfvf->dev, + "RVUPF: Invalid MSIX vector offset for NPA/NIX\n"); + return -EINVAL; + } + + return 0; +} + +/* Mbox message handlers */ +void mbox_handler_npa_lf_alloc(struct otx2_nic *pfvf, + struct npa_lf_alloc_rsp *rsp) +{ + pfvf->hw.stack_pg_ptrs = rsp->stack_pg_ptrs; + pfvf->hw.stack_pg_bytes = rsp->stack_pg_bytes; +} + +void mbox_handler_nix_lf_alloc(struct otx2_nic *pfvf, + struct nix_lf_alloc_rsp *rsp) +{ + pfvf->hw.sqb_size = rsp->sqb_size; + pfvf->hw.rx_chan_base = rsp->rx_chan_base; + pfvf->hw.tx_chan_base = rsp->tx_chan_base; +} + +void mbox_handler_msix_offset(struct otx2_nic *pfvf, + struct msix_offset_rsp *rsp) +{ + pfvf->hw.npa_msixoff = rsp->npa_msixoff; + pfvf->hw.nix_msixoff = rsp->nix_msixoff; +} #define M(_name, _id, _fn_name, _req_type, _rsp_type) \ int __weak \ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index a21eaaf6e824..cdb1c567d5dc 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -25,6 +25,17 @@ #define NAME_SIZE 32 +struct otx2_pool { + struct qmem *stack; +}; + +struct otx2_qset { +#define OTX2_MAX_CQ_CNT 64 + u16 cq_cnt; + u16 xqe_size; /* Size of CQE i.e 128 or 512 bytes */ + struct otx2_pool *pool; +}; + struct mbox { struct otx2_mbox mbox; struct work_struct mbox_wrk; @@ -42,8 +53,19 @@ struct otx2_hw { u16 rx_queues; u16 tx_queues; u16 max_queues; + u16 pool_cnt; + + /* NPA */ + u32 stack_pg_ptrs; /* No of ptrs per stack page */ + u32 stack_pg_bytes; /* Size of stack page */ + u16 sqb_size; + + u16 rx_chan_base; + u16 tx_chan_base; /* MSI-X */ + u16 npa_msixoff; /* Offset of NPA vectors */ + u16 nix_msixoff; /* Offset of NIX vectors */ char *irq_name; cpumask_var_t *affinity_mask; }; @@ -52,6 +74,7 @@ struct otx2_nic { void __iomem *reg_base; struct net_device *netdev; + struct otx2_qset qset; struct otx2_hw hw; struct pci_dev *pdev; struct device *dev; @@ -240,4 +263,17 @@ MBOX_UP_CGX_MESSAGES #define RVU_PFVF_FUNC_SHIFT 0 #define RVU_PFVF_FUNC_MASK 0x3FF +/* RVU block related APIs */ +int otx2_attach_npa_nix(struct otx2_nic *pfvf); +int otx2_detach_resources(struct mbox *mbox); +int otx2_config_npa(struct otx2_nic *pfvf); +int otx2_config_nix(struct otx2_nic *pfvf); + +/* Mbox handlers */ +void mbox_handler_msix_offset(struct otx2_nic *pfvf, + struct msix_offset_rsp *rsp); +void mbox_handler_npa_lf_alloc(struct otx2_nic *pfvf, + struct npa_lf_alloc_rsp *rsp); +void mbox_handler_nix_lf_alloc(struct otx2_nic *pfvf, + struct nix_lf_alloc_rsp *rsp); #endif /* OTX2_COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index e21bc10be3dd..ef5dba442abf 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -114,6 +114,15 @@ static void otx2_process_pfaf_mbox_msg(struct otx2_nic *pf, case MBOX_MSG_READY: pf->pcifunc = msg->pcifunc; break; + case MBOX_MSG_MSIX_OFFSET: + mbox_handler_msix_offset(pf, (struct msix_offset_rsp *)msg); + break; + case MBOX_MSG_NPA_LF_ALLOC: + mbox_handler_npa_lf_alloc(pf, (struct npa_lf_alloc_rsp *)msg); + break; + case MBOX_MSG_NIX_LF_ALLOC: + mbox_handler_nix_lf_alloc(pf, (struct nix_lf_alloc_rsp *)msg); + break; default: if (msg->rc) dev_err(pf->dev, @@ -372,9 +381,20 @@ static int otx2_set_real_num_queues(struct net_device *netdev, static int otx2_open(struct net_device *netdev) { + struct otx2_nic *pf = netdev_priv(netdev); + int err = 0; + netif_carrier_off(netdev); - return 0; + pf->qset.cq_cnt = pf->hw.rx_queues + pf->hw.tx_queues; + + /* NPA init */ + err = otx2_config_npa(pf); + if (err) + return err; + + /* NIX init */ + return otx2_config_nix(pf); } static int otx2_stop(struct net_device *netdev) @@ -405,6 +425,31 @@ static int otx2_check_pf_usable(struct otx2_nic *nic) return 0; } +static int otx2_realloc_msix_vectors(struct otx2_nic *pf) +{ + struct otx2_hw *hw = &pf->hw; + int num_vec, err; + + /* NPA interrupts are inot registered, so alloc only + * upto NIX vector offset. + */ + num_vec = hw->nix_msixoff; +#define NIX_LF_CINT_VEC_START 0x40 + num_vec += NIX_LF_CINT_VEC_START + hw->max_queues; + + otx2_disable_mbox_intr(pf); + pci_free_irq_vectors(hw->pdev); + pci_free_irq_vectors(hw->pdev); + err = pci_alloc_irq_vectors(hw->pdev, num_vec, num_vec, PCI_IRQ_MSIX); + if (err < 0) { + dev_err(pf->dev, "%s: Failed to realloc %d IRQ vectors\n", + __func__, num_vec); + return err; + } + + return otx2_register_mbox_intr(pf, false); +} + static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct device *dev = &pdev->dev; @@ -435,7 +480,7 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_master(pdev); /* Set number of queues */ - qcount = min_t(int, num_online_cpus(), num_online_cpus()); + qcount = min_t(int, num_online_cpus(), OTX2_MAX_CQ_CNT); netdev = alloc_etherdev_mqs(sizeof(*pf), qcount, qcount); if (!netdev) { @@ -497,20 +542,33 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto err_mbox_destroy; - err = otx2_set_real_num_queues(netdev, hw->tx_queues, hw->rx_queues); + /* Request AF to attach NPA and NIX LFs to this PF. + * NIX and NPA LFs are needed for this PF to function as a NIC. + */ + err = otx2_attach_npa_nix(pf); if (err) goto err_disable_mbox_intr; + err = otx2_realloc_msix_vectors(pf); + if (err) + goto err_detach_rsrc; + + err = otx2_set_real_num_queues(netdev, hw->tx_queues, hw->rx_queues); + if (err) + goto err_detach_rsrc; + netdev->netdev_ops = &otx2_netdev_ops; err = register_netdev(netdev); if (err) { dev_err(dev, "Failed to register netdevice\n"); - goto err_disable_mbox_intr; + goto err_detach_rsrc; } return 0; +err_detach_rsrc: + otx2_detach_resources(&pf->mbox); err_disable_mbox_intr: otx2_disable_mbox_intr(pf); err_mbox_destroy: @@ -536,6 +594,7 @@ static void otx2_remove(struct pci_dev *pdev) pf = netdev_priv(netdev); unregister_netdev(netdev); + otx2_detach_resources(&pf->mbox); otx2_disable_mbox_intr(pf); otx2_pfaf_mbox_destroy(pf); pci_free_irq_vectors(pf->pdev); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h new file mode 100644 index 000000000000..e37f89f6c965 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2020 Marvell International Ltd. + * + * 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. + */ + +#ifndef OTX2_STRUCT_H +#define OTX2_STRUCT_H + +/* NIX WQE/CQE size 128 byte or 512 byte */ +enum nix_cqesz_e { + NIX_XQESZ_W64 = 0x0, + NIX_XQESZ_W16 = 0x1, +}; + +#endif /* OTX2_STRUCT_H */ -- cgit v1.2.1 From caa2da34fd25a37e9fd43343b6966fb9d730a6d5 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Mon, 27 Jan 2020 18:35:18 +0530 Subject: octeontx2-pf: Initialize and config queues This patch does the initialization of all queues ie the receive buffer pools, receive and transmit queues, completion or notification queues etc. Allocates all required resources (eg transmit schedulers, receive buffers etc) and configures them for proper functioning of queues. Also sets up receive queue's RED dropping levels. Co-developed-by: Geetha sowjanya Signed-off-by: Geetha sowjanya Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/octeontx2/af/common.h | 9 +- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 723 +++++++++++++++++++++ .../ethernet/marvell/octeontx2/nic/otx2_common.h | 131 +++- .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 235 ++++++- .../net/ethernet/marvell/octeontx2/nic/otx2_reg.h | 96 +++ .../ethernet/marvell/octeontx2/nic/otx2_struct.h | 11 + .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.h | 102 +++ 7 files changed, 1290 insertions(+), 17 deletions(-) create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/af/common.h b/drivers/net/ethernet/marvell/octeontx2/af/common.h index 784207bae5f8..cd33c2e6ca5f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/common.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/common.h @@ -143,8 +143,13 @@ enum nix_scheduler { NIX_TXSCH_LVL_CNT = 0x5, }; -#define TXSCH_TL1_DFLT_RR_QTM ((1 << 24) - 1) -#define TXSCH_TL1_DFLT_RR_PRIO (0x1ull) +#define TXSCH_RR_QTM_MAX ((1 << 24) - 1) +#define TXSCH_TL1_DFLT_RR_QTM TXSCH_RR_QTM_MAX +#define TXSCH_TL1_DFLT_RR_PRIO (0x1ull) +#define MAX_SCHED_WEIGHT 0xFF +#define DFLT_RR_WEIGHT 71 +#define DFLT_RR_QTM ((DFLT_RR_WEIGHT * TXSCH_RR_QTM_MAX) \ + / MAX_SCHED_WEIGHT) /* Min/Max packet sizes, excluding FCS */ #define NIC_HW_MIN_FRS 40 diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 1fa09e9cfeb2..3ebbf0462b8f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -15,6 +15,388 @@ #include "otx2_common.h" #include "otx2_struct.h" +dma_addr_t otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, + gfp_t gfp) +{ + dma_addr_t iova; + + /* Check if request can be accommodated in previous allocated page */ + if (pool->page && ((pool->page_offset + pool->rbsize) <= + (PAGE_SIZE << pool->rbpage_order))) { + pool->pageref++; + goto ret; + } + + otx2_get_page(pool); + + /* Allocate a new page */ + pool->page = alloc_pages(gfp | __GFP_COMP | __GFP_NOWARN, + pool->rbpage_order); + if (unlikely(!pool->page)) + return -ENOMEM; + + pool->page_offset = 0; +ret: + iova = (u64)otx2_dma_map_page(pfvf, pool->page, pool->page_offset, + pool->rbsize, DMA_FROM_DEVICE); + if (!iova) { + if (!pool->page_offset) + __free_pages(pool->page, pool->rbpage_order); + pool->page = NULL; + return -ENOMEM; + } + pool->page_offset += pool->rbsize; + return iova; +} + +static int otx2_get_link(struct otx2_nic *pfvf) +{ + int link = 0; + u16 map; + + /* cgx lmac link */ + if (pfvf->hw.tx_chan_base >= CGX_CHAN_BASE) { + map = pfvf->hw.tx_chan_base & 0x7FF; + link = 4 * ((map >> 8) & 0xF) + ((map >> 4) & 0xF); + } + /* LBK channel */ + if (pfvf->hw.tx_chan_base < SDP_CHAN_BASE) + link = 12; + + return link; +} + +int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) +{ + struct otx2_hw *hw = &pfvf->hw; + struct nix_txschq_config *req; + u64 schq, parent; + + req = otx2_mbox_alloc_msg_nix_txschq_cfg(&pfvf->mbox); + if (!req) + return -ENOMEM; + + req->lvl = lvl; + req->num_regs = 1; + + schq = hw->txschq_list[lvl][0]; + /* Set topology e.t.c configuration */ + if (lvl == NIX_TXSCH_LVL_SMQ) { + req->reg[0] = NIX_AF_SMQX_CFG(schq); + req->regval[0] |= (0x20ULL << 51) | (0x80ULL << 39) | + (0x2ULL << 36); + req->num_regs++; + /* MDQ config */ + parent = hw->txschq_list[NIX_TXSCH_LVL_TL4][0]; + req->reg[1] = NIX_AF_MDQX_PARENT(schq); + req->regval[1] = parent << 16; + req->num_regs++; + /* Set DWRR quantum */ + req->reg[2] = NIX_AF_MDQX_SCHEDULE(schq); + req->regval[2] = DFLT_RR_QTM; + } else if (lvl == NIX_TXSCH_LVL_TL4) { + parent = hw->txschq_list[NIX_TXSCH_LVL_TL3][0]; + req->reg[0] = NIX_AF_TL4X_PARENT(schq); + req->regval[0] = parent << 16; + req->num_regs++; + req->reg[1] = NIX_AF_TL4X_SCHEDULE(schq); + req->regval[1] = DFLT_RR_QTM; + } else if (lvl == NIX_TXSCH_LVL_TL3) { + parent = hw->txschq_list[NIX_TXSCH_LVL_TL2][0]; + req->reg[0] = NIX_AF_TL3X_PARENT(schq); + req->regval[0] = parent << 16; + req->num_regs++; + req->reg[1] = NIX_AF_TL3X_SCHEDULE(schq); + req->regval[1] = DFLT_RR_QTM; + } else if (lvl == NIX_TXSCH_LVL_TL2) { + parent = hw->txschq_list[NIX_TXSCH_LVL_TL1][0]; + req->reg[0] = NIX_AF_TL2X_PARENT(schq); + req->regval[0] = parent << 16; + + req->num_regs++; + req->reg[1] = NIX_AF_TL2X_SCHEDULE(schq); + req->regval[1] = TXSCH_TL1_DFLT_RR_PRIO << 24 | DFLT_RR_QTM; + + req->num_regs++; + req->reg[2] = NIX_AF_TL3_TL2X_LINKX_CFG(schq, + otx2_get_link(pfvf)); + /* Enable this queue and backpressure */ + req->regval[2] = BIT_ULL(13) | BIT_ULL(12); + + } else if (lvl == NIX_TXSCH_LVL_TL1) { + /* Default config for TL1. + * For VF this is always ignored. + */ + + /* Set DWRR quantum */ + req->reg[0] = NIX_AF_TL1X_SCHEDULE(schq); + req->regval[0] = TXSCH_TL1_DFLT_RR_QTM; + + req->num_regs++; + req->reg[1] = NIX_AF_TL1X_TOPOLOGY(schq); + req->regval[1] = (TXSCH_TL1_DFLT_RR_PRIO << 1); + + req->num_regs++; + req->reg[2] = NIX_AF_TL1X_CIR(schq); + req->regval[2] = 0; + } + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +int otx2_txsch_alloc(struct otx2_nic *pfvf) +{ + struct nix_txsch_alloc_req *req; + int lvl; + + /* Get memory to put this msg */ + req = otx2_mbox_alloc_msg_nix_txsch_alloc(&pfvf->mbox); + if (!req) + return -ENOMEM; + + /* Request one schq per level */ + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) + req->schq[lvl] = 1; + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +int otx2_txschq_stop(struct otx2_nic *pfvf) +{ + struct nix_txsch_free_req *free_req; + int lvl, schq, err; + + otx2_mbox_lock(&pfvf->mbox); + /* Free the transmit schedulers */ + free_req = otx2_mbox_alloc_msg_nix_txsch_free(&pfvf->mbox); + if (!free_req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + free_req->flags = TXSCHQ_FREE_ALL; + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + + /* Clear the txschq list */ + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + for (schq = 0; schq < MAX_TXSCHQ_PER_FUNC; schq++) + pfvf->hw.txschq_list[lvl][schq] = 0; + } + return err; +} + +void otx2_sqb_flush(struct otx2_nic *pfvf) +{ + int qidx, sqe_tail, sqe_head; + u64 incr, *ptr, val; + + ptr = (u64 *)otx2_get_regaddr(pfvf, NIX_LF_SQ_OP_STATUS); + for (qidx = 0; qidx < pfvf->hw.tx_queues; qidx++) { + incr = (u64)qidx << 32; + while (1) { + val = otx2_atomic64_add(incr, ptr); + sqe_head = (val >> 20) & 0x3F; + sqe_tail = (val >> 28) & 0x3F; + if (sqe_head == sqe_tail) + break; + usleep_range(1, 3); + } + } +} + +/* RED and drop levels of CQ on packet reception. + * For CQ level is measure of emptiness ( 0x0 = full, 255 = empty). + */ +#define RQ_PASS_LVL_CQ(skid, qsize) ((((skid) + 16) * 256) / (qsize)) +#define RQ_DROP_LVL_CQ(skid, qsize) (((skid) * 256) / (qsize)) + +/* RED and drop levels of AURA for packet reception. + * For AURA level is measure of fullness (0x0 = empty, 255 = full). + * Eg: For RQ length 1K, for pass/drop level 204/230. + * RED accepts pkts if free pointers > 102 & <= 205. + * Drops pkts if free pointers < 102. + */ +#define RQ_PASS_LVL_AURA (255 - ((95 * 256) / 100)) /* RED when 95% is full */ +#define RQ_DROP_LVL_AURA (255 - ((99 * 256) / 100)) /* Drop when 99% is full */ + +/* Send skid of 2000 packets required for CQ size of 4K CQEs. */ +#define SEND_CQ_SKID 2000 + +static int otx2_rq_init(struct otx2_nic *pfvf, u16 qidx, u16 lpb_aura) +{ + struct otx2_qset *qset = &pfvf->qset; + struct nix_aq_enq_req *aq; + + /* Get memory to put this msg */ + aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox); + if (!aq) + return -ENOMEM; + + aq->rq.cq = qidx; + aq->rq.ena = 1; + aq->rq.pb_caching = 1; + aq->rq.lpb_aura = lpb_aura; /* Use large packet buffer aura */ + aq->rq.lpb_sizem1 = (DMA_BUFFER_LEN(pfvf->rbsize) / 8) - 1; + aq->rq.xqe_imm_size = 0; /* Copying of packet to CQE not needed */ + aq->rq.flow_tagw = 32; /* Copy full 32bit flow_tag to CQE header */ + aq->rq.lpb_drop_ena = 1; /* Enable RED dropping for AURA */ + aq->rq.xqe_drop_ena = 1; /* Enable RED dropping for CQ/SSO */ + aq->rq.xqe_pass = RQ_PASS_LVL_CQ(pfvf->hw.rq_skid, qset->rqe_cnt); + aq->rq.xqe_drop = RQ_DROP_LVL_CQ(pfvf->hw.rq_skid, qset->rqe_cnt); + aq->rq.lpb_aura_pass = RQ_PASS_LVL_AURA; + aq->rq.lpb_aura_drop = RQ_DROP_LVL_AURA; + + /* Fill AQ info */ + aq->qidx = qidx; + aq->ctype = NIX_AQ_CTYPE_RQ; + aq->op = NIX_AQ_INSTOP_INIT; + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +static int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura) +{ + struct otx2_qset *qset = &pfvf->qset; + struct otx2_snd_queue *sq; + struct nix_aq_enq_req *aq; + struct otx2_pool *pool; + int err; + + pool = &pfvf->qset.pool[sqb_aura]; + sq = &qset->sq[qidx]; + sq->sqe_size = NIX_SQESZ_W16 ? 64 : 128; + sq->sqe_cnt = qset->sqe_cnt; + + err = qmem_alloc(pfvf->dev, &sq->sqe, 1, sq->sqe_size); + if (err) + return err; + + sq->sqe_base = sq->sqe->base; + + sq->sqe_per_sqb = (pfvf->hw.sqb_size / sq->sqe_size) - 1; + sq->num_sqbs = (qset->sqe_cnt + sq->sqe_per_sqb) / sq->sqe_per_sqb; + sq->aura_id = sqb_aura; + sq->aura_fc_addr = pool->fc_addr->base; + sq->lmt_addr = (__force u64 *)(pfvf->reg_base + LMT_LF_LMTLINEX(qidx)); + sq->io_addr = (__force u64)otx2_get_regaddr(pfvf, NIX_LF_OP_SENDX(0)); + + /* Get memory to put this msg */ + aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox); + if (!aq) + return -ENOMEM; + + aq->sq.cq = pfvf->hw.rx_queues + qidx; + aq->sq.max_sqe_size = NIX_MAXSQESZ_W16; /* 128 byte */ + aq->sq.cq_ena = 1; + aq->sq.ena = 1; + /* Only one SMQ is allocated, map all SQ's to that SMQ */ + aq->sq.smq = pfvf->hw.txschq_list[NIX_TXSCH_LVL_SMQ][0]; + aq->sq.smq_rr_quantum = DFLT_RR_QTM; + aq->sq.default_chan = pfvf->hw.tx_chan_base; + aq->sq.sqe_stype = NIX_STYPE_STF; /* Cache SQB */ + aq->sq.sqb_aura = sqb_aura; + /* Due pipelining impact minimum 2000 unused SQ CQE's + * need to maintain to avoid CQ overflow. + */ + aq->sq.cq_limit = ((SEND_CQ_SKID * 256) / (sq->sqe_cnt)); + + /* Fill AQ info */ + aq->qidx = qidx; + aq->ctype = NIX_AQ_CTYPE_SQ; + aq->op = NIX_AQ_INSTOP_INIT; + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) +{ + struct otx2_qset *qset = &pfvf->qset; + struct nix_aq_enq_req *aq; + struct otx2_cq_queue *cq; + int err, pool_id; + + cq = &qset->cq[qidx]; + cq->cq_idx = qidx; + if (qidx < pfvf->hw.rx_queues) { + cq->cq_type = CQ_RX; + cq->cqe_cnt = qset->rqe_cnt; + } else { + cq->cq_type = CQ_TX; + cq->cqe_cnt = qset->sqe_cnt; + } + cq->cqe_size = pfvf->qset.xqe_size; + + /* Allocate memory for CQEs */ + err = qmem_alloc(pfvf->dev, &cq->cqe, cq->cqe_cnt, cq->cqe_size); + if (err) + return err; + + /* Save CQE CPU base for faster reference */ + cq->cqe_base = cq->cqe->base; + /* In case where all RQs auras point to single pool, + * all CQs receive buffer pool also point to same pool. + */ + pool_id = ((cq->cq_type == CQ_RX) && + (pfvf->hw.rqpool_cnt != pfvf->hw.rx_queues)) ? 0 : qidx; + cq->rbpool = &qset->pool[pool_id]; + + /* Get memory to put this msg */ + aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox); + if (!aq) + return -ENOMEM; + + aq->cq.ena = 1; + aq->cq.qsize = Q_SIZE(cq->cqe_cnt, 4); + aq->cq.caching = 1; + aq->cq.base = cq->cqe->iova; + aq->cq.avg_level = 255; + + if (qidx < pfvf->hw.rx_queues) { + aq->cq.drop = RQ_DROP_LVL_CQ(pfvf->hw.rq_skid, cq->cqe_cnt); + aq->cq.drop_ena = 1; + } + + /* Fill AQ info */ + aq->qidx = qidx; + aq->ctype = NIX_AQ_CTYPE_CQ; + aq->op = NIX_AQ_INSTOP_INIT; + + return otx2_sync_mbox_msg(&pfvf->mbox); +} + +int otx2_config_nix_queues(struct otx2_nic *pfvf) +{ + int qidx, err; + + /* Initialize RX queues */ + for (qidx = 0; qidx < pfvf->hw.rx_queues; qidx++) { + u16 lpb_aura = otx2_get_pool_idx(pfvf, AURA_NIX_RQ, qidx); + + err = otx2_rq_init(pfvf, qidx, lpb_aura); + if (err) + return err; + } + + /* Initialize TX queues */ + for (qidx = 0; qidx < pfvf->hw.tx_queues; qidx++) { + u16 sqb_aura = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx); + + err = otx2_sq_init(pfvf, qidx, sqb_aura); + if (err) + return err; + } + + /* Initialize completion queues */ + for (qidx = 0; qidx < pfvf->qset.cq_cnt; qidx++) { + err = otx2_cq_init(pfvf, qidx); + if (err) + return err; + } + + return 0; +} + int otx2_config_nix(struct otx2_nic *pfvf) { struct nix_lf_alloc_req *nixlf; @@ -58,6 +440,302 @@ int otx2_config_nix(struct otx2_nic *pfvf) return rsp->hdr.rc; } +void otx2_sq_free_sqbs(struct otx2_nic *pfvf) +{ + struct otx2_qset *qset = &pfvf->qset; + struct otx2_hw *hw = &pfvf->hw; + struct otx2_snd_queue *sq; + int sqb, qidx; + u64 iova, pa; + + for (qidx = 0; qidx < hw->tx_queues; qidx++) { + sq = &qset->sq[qidx]; + if (!sq->sqb_ptrs) + continue; + for (sqb = 0; sqb < sq->sqb_count; sqb++) { + if (!sq->sqb_ptrs[sqb]) + continue; + iova = sq->sqb_ptrs[sqb]; + pa = otx2_iova_to_phys(pfvf->iommu_domain, iova); + dma_unmap_page_attrs(pfvf->dev, iova, hw->sqb_size, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + put_page(virt_to_page(phys_to_virt(pa))); + } + sq->sqb_count = 0; + } +} + +void otx2_free_aura_ptr(struct otx2_nic *pfvf, int type) +{ + int pool_id, pool_start = 0, pool_end = 0, size = 0; + u64 iova, pa; + + if (type == AURA_NIX_SQ) { + pool_start = otx2_get_pool_idx(pfvf, type, 0); + pool_end = pool_start + pfvf->hw.sqpool_cnt; + size = pfvf->hw.sqb_size; + } + if (type == AURA_NIX_RQ) { + pool_start = otx2_get_pool_idx(pfvf, type, 0); + pool_end = pfvf->hw.rqpool_cnt; + size = pfvf->rbsize; + } + + /* Free SQB and RQB pointers from the aura pool */ + for (pool_id = pool_start; pool_id < pool_end; pool_id++) { + iova = otx2_aura_allocptr(pfvf, pool_id); + while (iova) { + if (type == AURA_NIX_RQ) + iova -= OTX2_HEAD_ROOM; + + pa = otx2_iova_to_phys(pfvf->iommu_domain, iova); + dma_unmap_page_attrs(pfvf->dev, iova, size, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + put_page(virt_to_page(phys_to_virt(pa))); + iova = otx2_aura_allocptr(pfvf, pool_id); + } + } +} + +void otx2_aura_pool_free(struct otx2_nic *pfvf) +{ + struct otx2_pool *pool; + int pool_id; + + if (!pfvf->qset.pool) + return; + + for (pool_id = 0; pool_id < pfvf->hw.pool_cnt; pool_id++) { + pool = &pfvf->qset.pool[pool_id]; + qmem_free(pfvf->dev, pool->stack); + qmem_free(pfvf->dev, pool->fc_addr); + } + devm_kfree(pfvf->dev, pfvf->qset.pool); +} + +static int otx2_aura_init(struct otx2_nic *pfvf, int aura_id, + int pool_id, int numptrs) +{ + struct npa_aq_enq_req *aq; + struct otx2_pool *pool; + int err; + + pool = &pfvf->qset.pool[pool_id]; + + /* Allocate memory for HW to update Aura count. + * Alloc one cache line, so that it fits all FC_STYPE modes. + */ + if (!pool->fc_addr) { + err = qmem_alloc(pfvf->dev, &pool->fc_addr, 1, OTX2_ALIGN); + if (err) + return err; + } + + /* Initialize this aura's context via AF */ + aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); + if (!aq) { + /* Shared mbox memory buffer is full, flush it and retry */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) + return err; + aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); + if (!aq) + return -ENOMEM; + } + + aq->aura_id = aura_id; + /* Will be filled by AF with correct pool context address */ + aq->aura.pool_addr = pool_id; + aq->aura.pool_caching = 1; + aq->aura.shift = ilog2(numptrs) - 8; + aq->aura.count = numptrs; + aq->aura.limit = numptrs; + aq->aura.avg_level = 255; + aq->aura.ena = 1; + aq->aura.fc_ena = 1; + aq->aura.fc_addr = pool->fc_addr->iova; + aq->aura.fc_hyst_bits = 0; /* Store count on all updates */ + + /* Fill AQ info */ + aq->ctype = NPA_AQ_CTYPE_AURA; + aq->op = NPA_AQ_INSTOP_INIT; + + return 0; +} + +static int otx2_pool_init(struct otx2_nic *pfvf, u16 pool_id, + int stack_pages, int numptrs, int buf_size) +{ + struct npa_aq_enq_req *aq; + struct otx2_pool *pool; + int err; + + pool = &pfvf->qset.pool[pool_id]; + /* Alloc memory for stack which is used to store buffer pointers */ + err = qmem_alloc(pfvf->dev, &pool->stack, + stack_pages, pfvf->hw.stack_pg_bytes); + if (err) + return err; + + pool->rbsize = buf_size; + pool->rbpage_order = get_order(buf_size); + + /* Initialize this pool's context via AF */ + aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); + if (!aq) { + /* Shared mbox memory buffer is full, flush it and retry */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + qmem_free(pfvf->dev, pool->stack); + return err; + } + aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); + if (!aq) { + qmem_free(pfvf->dev, pool->stack); + return -ENOMEM; + } + } + + aq->aura_id = pool_id; + aq->pool.stack_base = pool->stack->iova; + aq->pool.stack_caching = 1; + aq->pool.ena = 1; + aq->pool.buf_size = buf_size / 128; + aq->pool.stack_max_pages = stack_pages; + aq->pool.shift = ilog2(numptrs) - 8; + aq->pool.ptr_start = 0; + aq->pool.ptr_end = ~0ULL; + + /* Fill AQ info */ + aq->ctype = NPA_AQ_CTYPE_POOL; + aq->op = NPA_AQ_INSTOP_INIT; + + return 0; +} + +int otx2_sq_aura_pool_init(struct otx2_nic *pfvf) +{ + int qidx, pool_id, stack_pages, num_sqbs; + struct otx2_qset *qset = &pfvf->qset; + struct otx2_hw *hw = &pfvf->hw; + struct otx2_snd_queue *sq; + struct otx2_pool *pool; + int err, ptr; + s64 bufptr; + + /* Calculate number of SQBs needed. + * + * For a 128byte SQE, and 4K size SQB, 31 SQEs will fit in one SQB. + * Last SQE is used for pointing to next SQB. + */ + num_sqbs = (hw->sqb_size / 128) - 1; + num_sqbs = (qset->sqe_cnt + num_sqbs) / num_sqbs; + + /* Get no of stack pages needed */ + stack_pages = + (num_sqbs + hw->stack_pg_ptrs - 1) / hw->stack_pg_ptrs; + + for (qidx = 0; qidx < hw->tx_queues; qidx++) { + pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx); + /* Initialize aura context */ + err = otx2_aura_init(pfvf, pool_id, pool_id, num_sqbs); + if (err) + goto fail; + + /* Initialize pool context */ + err = otx2_pool_init(pfvf, pool_id, stack_pages, + num_sqbs, hw->sqb_size); + if (err) + goto fail; + } + + /* Flush accumulated messages */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) + goto fail; + + /* Allocate pointers and free them to aura/pool */ + for (qidx = 0; qidx < hw->tx_queues; qidx++) { + pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_SQ, qidx); + pool = &pfvf->qset.pool[pool_id]; + + sq = &qset->sq[qidx]; + sq->sqb_count = 0; + sq->sqb_ptrs = kcalloc(num_sqbs, sizeof(u64 *), GFP_KERNEL); + if (!sq->sqb_ptrs) + return -ENOMEM; + + for (ptr = 0; ptr < num_sqbs; ptr++) { + bufptr = otx2_alloc_rbuf(pfvf, pool, GFP_KERNEL); + if (bufptr <= 0) + return bufptr; + otx2_aura_freeptr(pfvf, pool_id, bufptr); + sq->sqb_ptrs[sq->sqb_count++] = (u64)bufptr; + } + otx2_get_page(pool); + } + + return 0; +fail: + otx2_mbox_reset(&pfvf->mbox.mbox, 0); + otx2_aura_pool_free(pfvf); + return err; +} + +int otx2_rq_aura_pool_init(struct otx2_nic *pfvf) +{ + struct otx2_hw *hw = &pfvf->hw; + int stack_pages, pool_id, rq; + struct otx2_pool *pool; + int err, ptr, num_ptrs; + s64 bufptr; + + num_ptrs = pfvf->qset.rqe_cnt; + + stack_pages = + (num_ptrs + hw->stack_pg_ptrs - 1) / hw->stack_pg_ptrs; + + for (rq = 0; rq < hw->rx_queues; rq++) { + pool_id = otx2_get_pool_idx(pfvf, AURA_NIX_RQ, rq); + /* Initialize aura context */ + err = otx2_aura_init(pfvf, pool_id, pool_id, num_ptrs); + if (err) + goto fail; + } + for (pool_id = 0; pool_id < hw->rqpool_cnt; pool_id++) { + err = otx2_pool_init(pfvf, pool_id, stack_pages, + num_ptrs, pfvf->rbsize); + if (err) + goto fail; + } + + /* Flush accumulated messages */ + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) + goto fail; + + /* Allocate pointers and free them to aura/pool */ + for (pool_id = 0; pool_id < hw->rqpool_cnt; pool_id++) { + pool = &pfvf->qset.pool[pool_id]; + for (ptr = 0; ptr < num_ptrs; ptr++) { + bufptr = otx2_alloc_rbuf(pfvf, pool, GFP_KERNEL); + if (bufptr <= 0) + return bufptr; + otx2_aura_freeptr(pfvf, pool_id, + bufptr + OTX2_HEAD_ROOM); + } + otx2_get_page(pool); + } + + return 0; +fail: + otx2_mbox_reset(&pfvf->mbox.mbox, 0); + otx2_aura_pool_free(pfvf); + return err; +} + int otx2_config_npa(struct otx2_nic *pfvf) { struct otx2_qset *qset = &pfvf->qset; @@ -134,6 +812,14 @@ int otx2_attach_npa_nix(struct otx2_nic *pfvf) return err; } + pfvf->nix_blkaddr = BLKADDR_NIX0; + + /* If the platform has two NIX blocks then LF may be + * allocated from NIX1. + */ + if (otx2_read64(pfvf, RVU_PF_BLOCK_ADDRX_DISC(BLKADDR_NIX1)) & 0x1FFULL) + pfvf->nix_blkaddr = BLKADDR_NIX1; + /* Get NPA and NIX MSIX vector offsets */ msix = otx2_mbox_alloc_msg_msix_offset(&pfvf->mbox); if (!msix) { @@ -158,6 +844,43 @@ int otx2_attach_npa_nix(struct otx2_nic *pfvf) return 0; } +void otx2_ctx_disable(struct mbox *mbox, int type, bool npa) +{ + struct hwctx_disable_req *req; + + otx2_mbox_lock(mbox); + /* Request AQ to disable this context */ + if (npa) + req = otx2_mbox_alloc_msg_npa_hwctx_disable(mbox); + else + req = otx2_mbox_alloc_msg_nix_hwctx_disable(mbox); + + if (!req) { + otx2_mbox_unlock(mbox); + return; + } + + req->ctype = type; + + if (otx2_sync_mbox_msg(mbox)) + dev_err(mbox->pfvf->dev, "%s failed to disable context\n", + __func__); + + otx2_mbox_unlock(mbox); +} + +void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, + struct nix_txsch_alloc_rsp *rsp) +{ + int lvl, schq; + + /* Setup transmit scheduler list */ + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) + for (schq = 0; schq < rsp->schq[lvl]; schq++) + pf->hw.txschq_list[lvl][schq] = + rsp->schq_list[lvl][schq]; +} + /* Mbox message handlers */ void mbox_handler_npa_lf_alloc(struct otx2_nic *pfvf, struct npa_lf_alloc_rsp *rsp) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index cdb1c567d5dc..a4f1c604cb60 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -12,9 +12,11 @@ #define OTX2_COMMON_H #include +#include #include #include "otx2_reg.h" +#include "otx2_txrx.h" /* PCI device IDs */ #define PCI_DEVID_OCTEONTX2_RVU_PF 0xA063 @@ -25,15 +27,9 @@ #define NAME_SIZE 32 -struct otx2_pool { - struct qmem *stack; -}; - -struct otx2_qset { -#define OTX2_MAX_CQ_CNT 64 - u16 cq_cnt; - u16 xqe_size; /* Size of CQE i.e 128 or 512 bytes */ - struct otx2_pool *pool; +enum arua_mapped_qtypes { + AURA_NIX_RQ, + AURA_NIX_SQ, }; struct mbox { @@ -54,14 +50,21 @@ struct otx2_hw { u16 tx_queues; u16 max_queues; u16 pool_cnt; + u16 rqpool_cnt; + u16 sqpool_cnt; /* NPA */ u32 stack_pg_ptrs; /* No of ptrs per stack page */ u32 stack_pg_bytes; /* Size of stack page */ u16 sqb_size; + /* NIX */ + u16 txschq_list[NIX_TXSCH_LVL_CNT][MAX_TXSCHQ_PER_FUNC]; + + /* HW settings, coalescing etc */ u16 rx_chan_base; u16 tx_chan_base; + u16 rq_skid; /* MSI-X */ u16 npa_msixoff; /* Offset of NPA vectors */ @@ -73,6 +76,8 @@ struct otx2_hw { struct otx2_nic { void __iomem *reg_base; struct net_device *netdev; + void *iommu_domain; + u16 rbsize; /* Receive buffer size */ struct otx2_qset qset; struct otx2_hw hw; @@ -84,6 +89,9 @@ struct otx2_nic { struct workqueue_struct *mbox_wq; u16 pcifunc; /* RVU PF_FUNC */ + + /* Block address of NIX either BLKADDR_NIX0 or BLKADDR_NIX1 */ + int nix_blkaddr; }; /* Register read/write APIs */ @@ -93,7 +101,7 @@ static inline void __iomem *otx2_get_regaddr(struct otx2_nic *nic, u64 offset) switch ((offset >> RVU_FUNC_BLKADDR_SHIFT) & RVU_FUNC_BLKADDR_MASK) { case BLKTYPE_NIX: - blkaddr = BLKADDR_NIX0; + blkaddr = nic->nix_blkaddr; break; case BLKTYPE_NPA: blkaddr = BLKADDR_NPA; @@ -184,6 +192,72 @@ static inline void otx2_mbox_unlock(struct mbox *mbox) mutex_unlock(&mbox->lock); } +/* With the absence of API for 128-bit IO memory access for arm64, + * implement required operations at place. + */ +#if defined(CONFIG_ARM64) +static inline void otx2_write128(u64 lo, u64 hi, void __iomem *addr) +{ + __asm__ volatile("stp %x[x0], %x[x1], [%x[p1],#0]!" + ::[x0]"r"(lo), [x1]"r"(hi), [p1]"r"(addr)); +} + +static inline u64 otx2_atomic64_add(u64 incr, u64 *ptr) +{ + u64 result; + + __asm__ volatile(".cpu generic+lse\n" + "ldadd %x[i], %x[r], [%[b]]" + : [r]"=r"(result), "+m"(*ptr) + : [i]"r"(incr), [b]"r"(ptr) + : "memory"); + return result; +} + +#else +#define otx2_write128(lo, hi, addr) +#define otx2_atomic64_add(incr, ptr) ({ *ptr += incr; }) +#endif + +/* Alloc pointer from pool/aura */ +static inline u64 otx2_aura_allocptr(struct otx2_nic *pfvf, int aura) +{ + u64 *ptr = (u64 *)otx2_get_regaddr(pfvf, + NPA_LF_AURA_OP_ALLOCX(0)); + u64 incr = (u64)aura | BIT_ULL(63); + + return otx2_atomic64_add(incr, ptr); +} + +/* Free pointer to a pool/aura */ +static inline void otx2_aura_freeptr(struct otx2_nic *pfvf, + int aura, s64 buf) +{ + otx2_write128((u64)buf, (u64)aura | BIT_ULL(63), + otx2_get_regaddr(pfvf, NPA_LF_AURA_OP_FREE0)); +} + +/* Update page ref count */ +static inline void otx2_get_page(struct otx2_pool *pool) +{ + if (!pool->page) + return; + + if (pool->pageref) + page_ref_add(pool->page, pool->pageref); + pool->pageref = 0; + pool->page = NULL; +} + +static inline int otx2_get_pool_idx(struct otx2_nic *pfvf, int type, int idx) +{ + if (type == AURA_NIX_SQ) + return pfvf->hw.rqpool_cnt + idx; + + /* AURA_NIX_RQ */ + return idx; +} + /* Mbox APIs */ static inline int otx2_sync_mbox_msg(struct mbox *mbox) { @@ -263,11 +337,46 @@ MBOX_UP_CGX_MESSAGES #define RVU_PFVF_FUNC_SHIFT 0 #define RVU_PFVF_FUNC_MASK 0x3FF +static inline dma_addr_t otx2_dma_map_page(struct otx2_nic *pfvf, + struct page *page, + size_t offset, size_t size, + enum dma_data_direction dir) +{ + dma_addr_t iova; + + iova = dma_map_page_attrs(pfvf->dev, page, + offset, size, dir, DMA_ATTR_SKIP_CPU_SYNC); + if (unlikely(dma_mapping_error(pfvf->dev, iova))) + return (dma_addr_t)NULL; + return iova; +} + +static inline void otx2_dma_unmap_page(struct otx2_nic *pfvf, + dma_addr_t addr, size_t size, + enum dma_data_direction dir) +{ + dma_unmap_page_attrs(pfvf->dev, addr, size, + dir, DMA_ATTR_SKIP_CPU_SYNC); +} + /* RVU block related APIs */ int otx2_attach_npa_nix(struct otx2_nic *pfvf); int otx2_detach_resources(struct mbox *mbox); int otx2_config_npa(struct otx2_nic *pfvf); +int otx2_sq_aura_pool_init(struct otx2_nic *pfvf); +int otx2_rq_aura_pool_init(struct otx2_nic *pfvf); +void otx2_aura_pool_free(struct otx2_nic *pfvf); +void otx2_free_aura_ptr(struct otx2_nic *pfvf, int type); +void otx2_sq_free_sqbs(struct otx2_nic *pfvf); int otx2_config_nix(struct otx2_nic *pfvf); +int otx2_config_nix_queues(struct otx2_nic *pfvf); +int otx2_txschq_config(struct otx2_nic *pfvf, int lvl); +int otx2_txsch_alloc(struct otx2_nic *pfvf); +int otx2_txschq_stop(struct otx2_nic *pfvf); +void otx2_sqb_flush(struct otx2_nic *pfvf); +dma_addr_t otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, + gfp_t gfp); +void otx2_ctx_disable(struct mbox *mbox, int type, bool npa); /* Mbox handlers */ void mbox_handler_msix_offset(struct otx2_nic *pfvf, @@ -276,4 +385,6 @@ void mbox_handler_npa_lf_alloc(struct otx2_nic *pfvf, struct npa_lf_alloc_rsp *rsp); void mbox_handler_nix_lf_alloc(struct otx2_nic *pfvf, struct nix_lf_alloc_rsp *rsp); +void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, + struct nix_txsch_alloc_rsp *rsp); #endif /* OTX2_COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index ef5dba442abf..735188900de4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -17,7 +17,10 @@ #include #include +#include "otx2_reg.h" #include "otx2_common.h" +#include "otx2_txrx.h" +#include "otx2_struct.h" #define DRV_NAME "octeontx2-nicpf" #define DRV_STRING "Marvell OcteonTX2 NIC Physical Function Driver" @@ -123,6 +126,10 @@ static void otx2_process_pfaf_mbox_msg(struct otx2_nic *pf, case MBOX_MSG_NIX_LF_ALLOC: mbox_handler_nix_lf_alloc(pf, (struct nix_lf_alloc_rsp *)msg); break; + case MBOX_MSG_NIX_TXSCH_ALLOC: + mbox_handler_nix_txsch_alloc(pf, + (struct nix_txsch_alloc_rsp *)msg); + break; default: if (msg->rc) dev_err(pf->dev, @@ -379,26 +386,231 @@ static int otx2_set_real_num_queues(struct net_device *netdev, return err; } +static void otx2_free_cq_res(struct otx2_nic *pf) +{ + struct otx2_qset *qset = &pf->qset; + struct otx2_cq_queue *cq; + int qidx; + + /* Disable CQs */ + otx2_ctx_disable(&pf->mbox, NIX_AQ_CTYPE_CQ, false); + for (qidx = 0; qidx < qset->cq_cnt; qidx++) { + cq = &qset->cq[qidx]; + qmem_free(pf->dev, cq->cqe); + } +} + +static void otx2_free_sq_res(struct otx2_nic *pf) +{ + struct otx2_qset *qset = &pf->qset; + struct otx2_snd_queue *sq; + int qidx; + + /* Disable SQs */ + otx2_ctx_disable(&pf->mbox, NIX_AQ_CTYPE_SQ, false); + /* Free SQB pointers */ + otx2_sq_free_sqbs(pf); + for (qidx = 0; qidx < pf->hw.tx_queues; qidx++) { + sq = &qset->sq[qidx]; + qmem_free(pf->dev, sq->sqe); + kfree(sq->sqb_ptrs); + } +} + +static int otx2_init_hw_resources(struct otx2_nic *pf) +{ + struct mbox *mbox = &pf->mbox; + struct otx2_hw *hw = &pf->hw; + struct msg_req *req; + int err = 0, lvl; + + /* Set required NPA LF's pool counts + * Auras and Pools are used in a 1:1 mapping, + * so, aura count = pool count. + */ + hw->rqpool_cnt = hw->rx_queues; + hw->sqpool_cnt = hw->tx_queues; + hw->pool_cnt = hw->rqpool_cnt + hw->sqpool_cnt; + + /* Get the size of receive buffers to allocate */ + pf->rbsize = RCV_FRAG_LEN(pf->netdev->mtu); + + otx2_mbox_lock(mbox); + /* NPA init */ + err = otx2_config_npa(pf); + if (err) + goto exit; + + /* NIX init */ + err = otx2_config_nix(pf); + if (err) + goto err_free_npa_lf; + + /* Init Auras and pools used by NIX RQ, for free buffer ptrs */ + err = otx2_rq_aura_pool_init(pf); + if (err) { + otx2_mbox_unlock(mbox); + goto err_free_nix_lf; + } + /* Init Auras and pools used by NIX SQ, for queueing SQEs */ + err = otx2_sq_aura_pool_init(pf); + if (err) { + otx2_mbox_unlock(mbox); + goto err_free_rq_ptrs; + } + + err = otx2_txsch_alloc(pf); + if (err) { + otx2_mbox_unlock(mbox); + goto err_free_sq_ptrs; + } + + err = otx2_config_nix_queues(pf); + if (err) { + otx2_mbox_unlock(mbox); + goto err_free_txsch; + } + for (lvl = 0; lvl < NIX_TXSCH_LVL_CNT; lvl++) { + err = otx2_txschq_config(pf, lvl); + if (err) { + otx2_mbox_unlock(mbox); + goto err_free_nix_queues; + } + } + otx2_mbox_unlock(mbox); + return err; + +err_free_nix_queues: + otx2_free_sq_res(pf); + otx2_free_cq_res(pf); + otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false); +err_free_txsch: + if (otx2_txschq_stop(pf)) + dev_err(pf->dev, "%s failed to stop TX schedulers\n", __func__); +err_free_sq_ptrs: + otx2_sq_free_sqbs(pf); +err_free_rq_ptrs: + otx2_free_aura_ptr(pf, AURA_NIX_RQ); + otx2_ctx_disable(mbox, NPA_AQ_CTYPE_POOL, true); + otx2_ctx_disable(mbox, NPA_AQ_CTYPE_AURA, true); + otx2_aura_pool_free(pf); +err_free_nix_lf: + otx2_mbox_lock(mbox); + req = otx2_mbox_alloc_msg_nix_lf_free(mbox); + if (req) { + if (otx2_sync_mbox_msg(mbox)) + dev_err(pf->dev, "%s failed to free nixlf\n", __func__); + } +err_free_npa_lf: + /* Reset NPA LF */ + req = otx2_mbox_alloc_msg_npa_lf_free(mbox); + if (req) { + if (otx2_sync_mbox_msg(mbox)) + dev_err(pf->dev, "%s failed to free npalf\n", __func__); + } +exit: + otx2_mbox_unlock(mbox); + return err; +} + +static void otx2_free_hw_resources(struct otx2_nic *pf) +{ + struct mbox *mbox = &pf->mbox; + struct msg_req *req; + int err; + + /* Ensure all SQE are processed */ + otx2_sqb_flush(pf); + + /* Stop transmission */ + err = otx2_txschq_stop(pf); + if (err) + dev_err(pf->dev, "RVUPF: Failed to stop/free TX schedulers\n"); + + /* Disable RQs */ + otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false); + + otx2_free_sq_res(pf); + + /* Free RQ buffer pointers*/ + otx2_free_aura_ptr(pf, AURA_NIX_RQ); + + otx2_free_cq_res(pf); + + otx2_mbox_lock(mbox); + /* Reset NIX LF */ + req = otx2_mbox_alloc_msg_nix_lf_free(mbox); + if (req) { + if (otx2_sync_mbox_msg(mbox)) + dev_err(pf->dev, "%s failed to free nixlf\n", __func__); + } + otx2_mbox_unlock(mbox); + + /* Disable NPA Pool and Aura hw context */ + otx2_ctx_disable(mbox, NPA_AQ_CTYPE_POOL, true); + otx2_ctx_disable(mbox, NPA_AQ_CTYPE_AURA, true); + otx2_aura_pool_free(pf); + + otx2_mbox_lock(mbox); + /* Reset NPA LF */ + req = otx2_mbox_alloc_msg_npa_lf_free(mbox); + if (req) { + if (otx2_sync_mbox_msg(mbox)) + dev_err(pf->dev, "%s failed to free npalf\n", __func__); + } + otx2_mbox_unlock(mbox); +} + static int otx2_open(struct net_device *netdev) { struct otx2_nic *pf = netdev_priv(netdev); + struct otx2_qset *qset = &pf->qset; int err = 0; netif_carrier_off(netdev); pf->qset.cq_cnt = pf->hw.rx_queues + pf->hw.tx_queues; - /* NPA init */ - err = otx2_config_npa(pf); + /* CQ size of RQ */ + qset->rqe_cnt = qset->rqe_cnt ? qset->rqe_cnt : Q_COUNT(Q_SIZE_256); + /* CQ size of SQ */ + qset->sqe_cnt = qset->sqe_cnt ? qset->sqe_cnt : Q_COUNT(Q_SIZE_4K); + + err = -ENOMEM; + qset->cq = kcalloc(pf->qset.cq_cnt, + sizeof(struct otx2_cq_queue), GFP_KERNEL); + if (!qset->cq) + goto err_free_mem; + + qset->sq = kcalloc(pf->hw.tx_queues, + sizeof(struct otx2_snd_queue), GFP_KERNEL); + if (!qset->sq) + goto err_free_mem; + + err = otx2_init_hw_resources(pf); if (err) - return err; + goto err_free_mem; - /* NIX init */ - return otx2_config_nix(pf); + return 0; +err_free_mem: + kfree(qset->sq); + kfree(qset->cq); + return err; } static int otx2_stop(struct net_device *netdev) { + struct otx2_nic *pf = netdev_priv(netdev); + struct otx2_qset *qset = &pf->qset; + + otx2_free_hw_resources(pf); + + kfree(qset->sq); + kfree(qset->cq); + + /* Do not clear RQ/SQ ringsize settings */ + memset((void *)qset + offsetof(struct otx2_qset, sqe_cnt), 0, + sizeof(*qset) - offsetof(struct otx2_qset, sqe_cnt)); return 0; } @@ -557,6 +769,19 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto err_detach_rsrc; + /* NPA's pool is a stack to which SW frees buffer pointers via Aura. + * HW allocates buffer pointer from stack and uses it for DMA'ing + * ingress packet. In some scenarios HW can free back allocated buffer + * pointers to pool. This makes it impossible for SW to maintain a + * parallel list where physical addresses of buffer pointers (IOVAs) + * given to HW can be saved for later reference. + * + * So the only way to convert Rx packet's buffer address is to use + * IOMMU's iova_to_phys() handler which translates the address by + * walking through the translation tables. + */ + pf->iommu_domain = iommu_get_domain_for_dev(dev); + netdev->netdev_ops = &otx2_netdev_ops; err = register_netdev(netdev); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h index d0bd64ab4f1a..7963d418886a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_reg.h @@ -48,4 +48,100 @@ #define RVU_FUNC_BLKADDR_SHIFT 20 #define RVU_FUNC_BLKADDR_MASK 0x1FULL +/* NPA LF registers */ +#define NPA_LFBASE (BLKTYPE_NPA << RVU_FUNC_BLKADDR_SHIFT) +#define NPA_LF_AURA_OP_ALLOCX(a) (NPA_LFBASE | 0x10 | (a) << 3) +#define NPA_LF_AURA_OP_FREE0 (NPA_LFBASE | 0x20) +#define NPA_LF_AURA_OP_FREE1 (NPA_LFBASE | 0x28) +#define NPA_LF_AURA_OP_CNT (NPA_LFBASE | 0x30) +#define NPA_LF_AURA_OP_LIMIT (NPA_LFBASE | 0x50) +#define NPA_LF_AURA_OP_INT (NPA_LFBASE | 0x60) +#define NPA_LF_AURA_OP_THRESH (NPA_LFBASE | 0x70) +#define NPA_LF_POOL_OP_PC (NPA_LFBASE | 0x100) +#define NPA_LF_POOL_OP_AVAILABLE (NPA_LFBASE | 0x110) +#define NPA_LF_POOL_OP_PTR_START0 (NPA_LFBASE | 0x120) +#define NPA_LF_POOL_OP_PTR_START1 (NPA_LFBASE | 0x128) +#define NPA_LF_POOL_OP_PTR_END0 (NPA_LFBASE | 0x130) +#define NPA_LF_POOL_OP_PTR_END1 (NPA_LFBASE | 0x138) +#define NPA_LF_POOL_OP_INT (NPA_LFBASE | 0x160) +#define NPA_LF_POOL_OP_THRESH (NPA_LFBASE | 0x170) +#define NPA_LF_ERR_INT (NPA_LFBASE | 0x200) +#define NPA_LF_ERR_INT_W1S (NPA_LFBASE | 0x208) +#define NPA_LF_ERR_INT_ENA_W1C (NPA_LFBASE | 0x210) +#define NPA_LF_ERR_INT_ENA_W1S (NPA_LFBASE | 0x218) +#define NPA_LF_RAS (NPA_LFBASE | 0x220) +#define NPA_LF_RAS_W1S (NPA_LFBASE | 0x228) +#define NPA_LF_RAS_ENA_W1C (NPA_LFBASE | 0x230) +#define NPA_LF_RAS_ENA_W1S (NPA_LFBASE | 0x238) +#define NPA_LF_QINTX_CNT(a) (NPA_LFBASE | 0x300 | (a) << 12) +#define NPA_LF_QINTX_INT(a) (NPA_LFBASE | 0x310 | (a) << 12) +#define NPA_LF_QINTX_INT_W1S(a) (NPA_LFBASE | 0x318 | (a) << 12) +#define NPA_LF_QINTX_ENA_W1S(a) (NPA_LFBASE | 0x320 | (a) << 12) +#define NPA_LF_QINTX_ENA_W1C(a) (NPA_LFBASE | 0x330 | (a) << 12) + +/* NIX LF registers */ +#define NIX_LFBASE (BLKTYPE_NIX << RVU_FUNC_BLKADDR_SHIFT) +#define NIX_LF_RX_SECRETX(a) (NIX_LFBASE | 0x0 | (a) << 3) +#define NIX_LF_CFG (NIX_LFBASE | 0x100) +#define NIX_LF_GINT (NIX_LFBASE | 0x200) +#define NIX_LF_GINT_W1S (NIX_LFBASE | 0x208) +#define NIX_LF_GINT_ENA_W1C (NIX_LFBASE | 0x210) +#define NIX_LF_GINT_ENA_W1S (NIX_LFBASE | 0x218) +#define NIX_LF_ERR_INT (NIX_LFBASE | 0x220) +#define NIX_LF_ERR_INT_W1S (NIX_LFBASE | 0x228) +#define NIX_LF_ERR_INT_ENA_W1C (NIX_LFBASE | 0x230) +#define NIX_LF_ERR_INT_ENA_W1S (NIX_LFBASE | 0x238) +#define NIX_LF_RAS (NIX_LFBASE | 0x240) +#define NIX_LF_RAS_W1S (NIX_LFBASE | 0x248) +#define NIX_LF_RAS_ENA_W1C (NIX_LFBASE | 0x250) +#define NIX_LF_RAS_ENA_W1S (NIX_LFBASE | 0x258) +#define NIX_LF_SQ_OP_ERR_DBG (NIX_LFBASE | 0x260) +#define NIX_LF_MNQ_ERR_DBG (NIX_LFBASE | 0x270) +#define NIX_LF_SEND_ERR_DBG (NIX_LFBASE | 0x280) +#define NIX_LF_TX_STATX(a) (NIX_LFBASE | 0x300 | (a) << 3) +#define NIX_LF_RX_STATX(a) (NIX_LFBASE | 0x400 | (a) << 3) +#define NIX_LF_OP_SENDX(a) (NIX_LFBASE | 0x800 | (a) << 3) +#define NIX_LF_RQ_OP_INT (NIX_LFBASE | 0x900) +#define NIX_LF_RQ_OP_OCTS (NIX_LFBASE | 0x910) +#define NIX_LF_RQ_OP_PKTS (NIX_LFBASE | 0x920) +#define NIX_LF_OP_IPSEC_DYNO_CN (NIX_LFBASE | 0x980) +#define NIX_LF_SQ_OP_INT (NIX_LFBASE | 0xa00) +#define NIX_LF_SQ_OP_OCTS (NIX_LFBASE | 0xa10) +#define NIX_LF_SQ_OP_PKTS (NIX_LFBASE | 0xa20) +#define NIX_LF_SQ_OP_STATUS (NIX_LFBASE | 0xa30) +#define NIX_LF_CQ_OP_INT (NIX_LFBASE | 0xb00) +#define NIX_LF_CQ_OP_DOOR (NIX_LFBASE | 0xb30) +#define NIX_LF_CQ_OP_STATUS (NIX_LFBASE | 0xb40) +#define NIX_LF_QINTX_CNT(a) (NIX_LFBASE | 0xC00 | (a) << 12) +#define NIX_LF_QINTX_INT(a) (NIX_LFBASE | 0xC10 | (a) << 12) +#define NIX_LF_QINTX_INT_W1S(a) (NIX_LFBASE | 0xC18 | (a) << 12) +#define NIX_LF_QINTX_ENA_W1S(a) (NIX_LFBASE | 0xC20 | (a) << 12) +#define NIX_LF_QINTX_ENA_W1C(a) (NIX_LFBASE | 0xC30 | (a) << 12) +#define NIX_LF_CINTX_CNT(a) (NIX_LFBASE | 0xD00 | (a) << 12) +#define NIX_LF_CINTX_WAIT(a) (NIX_LFBASE | 0xD10 | (a) << 12) +#define NIX_LF_CINTX_INT(a) (NIX_LFBASE | 0xD20 | (a) << 12) +#define NIX_LF_CINTX_INT_W1S(a) (NIX_LFBASE | 0xD30 | (a) << 12) +#define NIX_LF_CINTX_ENA_W1S(a) (NIX_LFBASE | 0xD40 | (a) << 12) +#define NIX_LF_CINTX_ENA_W1C(a) (NIX_LFBASE | 0xD50 | (a) << 12) + +/* NIX AF transmit scheduler registers */ +#define NIX_AF_SMQX_CFG(a) (0x700 | (a) << 16) +#define NIX_AF_TL1X_SCHEDULE(a) (0xC00 | (a) << 16) +#define NIX_AF_TL1X_CIR(a) (0xC20 | (a) << 16) +#define NIX_AF_TL1X_TOPOLOGY(a) (0xC80 | (a) << 16) +#define NIX_AF_TL2X_PARENT(a) (0xE88 | (a) << 16) +#define NIX_AF_TL2X_SCHEDULE(a) (0xE00 | (a) << 16) +#define NIX_AF_TL3X_PARENT(a) (0x1088 | (a) << 16) +#define NIX_AF_TL3X_SCHEDULE(a) (0x1000 | (a) << 16) +#define NIX_AF_TL4X_PARENT(a) (0x1288 | (a) << 16) +#define NIX_AF_TL4X_SCHEDULE(a) (0x1200 | (a) << 16) +#define NIX_AF_MDQX_SCHEDULE(a) (0x1400 | (a) << 16) +#define NIX_AF_MDQX_PARENT(a) (0x1480 | (a) << 16) +#define NIX_AF_TL3_TL2X_LINKX_CFG(a, b) (0x1700 | (a) << 16 | (b) << 3) + +/* LMT LF registers */ +#define LMT_LFBASE BIT_ULL(RVU_FUNC_BLKADDR_SHIFT) +#define LMT_LF_LMTLINEX(a) (LMT_LFBASE | 0x000 | (a) << 12) +#define LMT_LF_LMTCANCEL (LMT_LFBASE | 0x400) + #endif /* OTX2_REG_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h index e37f89f6c965..13086b05e872 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h @@ -17,4 +17,15 @@ enum nix_cqesz_e { NIX_XQESZ_W16 = 0x1, }; +enum nix_sqes_e { + NIX_SQESZ_W16 = 0x0, + NIX_SQESZ_W8 = 0x1, +}; + +enum nix_send_ldtype { + NIX_SEND_LDTYPE_LDD = 0x0, + NIX_SEND_LDTYPE_LDT = 0x1, + NIX_SEND_LDTYPE_LDWB = 0x2, +}; + #endif /* OTX2_STRUCT_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h new file mode 100644 index 000000000000..ce6efcf2e8cf --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2020 Marvell International Ltd. + * + * 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. + */ + +#ifndef OTX2_TXRX_H +#define OTX2_TXRX_H + +#include +#include +#include + +#define LBK_CHAN_BASE 0x000 +#define SDP_CHAN_BASE 0x700 +#define CGX_CHAN_BASE 0x800 + +#define OTX2_DATA_ALIGN(X) ALIGN(X, OTX2_ALIGN) +#define OTX2_HEAD_ROOM OTX2_ALIGN + +/* Rx buffer size should be in multiples of 128bytes */ +#define RCV_FRAG_LEN1(x) \ + ((OTX2_HEAD_ROOM + OTX2_DATA_ALIGN(x)) + \ + OTX2_DATA_ALIGN(sizeof(struct skb_shared_info))) + +/* Prefer 2048 byte buffers for better last level cache + * utilization or data distribution across regions. + */ +#define RCV_FRAG_LEN(x) \ + ((RCV_FRAG_LEN1(x) < 2048) ? 2048 : RCV_FRAG_LEN1(x)) + +#define DMA_BUFFER_LEN(x) \ + ((x) - OTX2_HEAD_ROOM - \ + OTX2_DATA_ALIGN(sizeof(struct skb_shared_info))) + +struct otx2_snd_queue { + u8 aura_id; + u16 sqe_size; + u32 sqe_cnt; + u16 num_sqbs; + u8 sqe_per_sqb; + u64 io_addr; + u64 *aura_fc_addr; + u64 *lmt_addr; + void *sqe_base; + struct qmem *sqe; + u16 sqb_count; + u64 *sqb_ptrs; +} ____cacheline_aligned_in_smp; + +struct otx2_pool { + struct qmem *stack; + struct qmem *fc_addr; + u8 rbpage_order; + u16 rbsize; + u32 page_offset; + u16 pageref; + struct page *page; +}; + +enum cq_type { + CQ_RX, + CQ_TX, + CQS_PER_CINT = 2, /* RQ + SQ */ +}; + +struct otx2_cq_queue { + u8 cq_idx; + u8 cq_type; + u16 cqe_size; + u16 pool_ptrs; + u32 cqe_cnt; + void *cqe_base; + struct qmem *cqe; + struct otx2_pool *rbpool; +} ____cacheline_aligned_in_smp; + +struct otx2_qset { + u32 rqe_cnt; + u32 sqe_cnt; /* Keep these two at top */ +#define OTX2_MAX_CQ_CNT 64 + u16 cq_cnt; + u16 xqe_size; + struct otx2_pool *pool; + struct otx2_cq_queue *cq; + struct otx2_snd_queue *sq; +}; + +/* Translate IOVA to physical address */ +static inline u64 otx2_iova_to_phys(void *iommu_domain, dma_addr_t dma_addr) +{ + /* Translation is installed only when IOMMU is present */ + if (likely(iommu_domain)) + return iommu_iova_to_phys(iommu_domain, dma_addr); + return dma_addr; +} + +#endif /* OTX2_TXRX_H */ -- cgit v1.2.1 From 04a21ef303df74737259190d7d7cf77a129bb56a Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Mon, 27 Jan 2020 18:35:19 +0530 Subject: octeontx2-pf: Setup interrupts and NAPI handler Completion queue (CQ) is the one with which HW notifies SW on a packet reception or transmission. Each of the RQ and SQ are mapped to a unique CQ and again both CQs are mapped to same interrupt ie the CINT. So that each core has one interrupt source in whose handler both Rx and Tx notifications are processed. Also - Registered a NAPI handler for the CINT. - Setup coalescing parameters. - IRQ affinity hints etc Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- .../net/ethernet/marvell/octeontx2/nic/Makefile | 2 +- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 55 +++++++++ .../ethernet/marvell/octeontx2/nic/otx2_common.h | 54 +++++++++ .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 125 ++++++++++++++++++++- .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.c | 68 +++++++++++ .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.h | 38 ++++++- 6 files changed, 332 insertions(+), 10 deletions(-) create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile index 339fde892a36..0484d709fe68 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_OCTEONTX2_PF) += octeontx2_nicpf.o -octeontx2_nicpf-y := otx2_pf.o otx2_common.o +octeontx2_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o ccflags-y += -I$(srctree)/drivers/net/ethernet/marvell/octeontx2/af diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 3ebbf0462b8f..8f7b2cf40d7e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -15,6 +15,20 @@ #include "otx2_common.h" #include "otx2_struct.h" +void otx2_config_irq_coalescing(struct otx2_nic *pfvf, int qidx) +{ + /* Configure CQE interrupt coalescing parameters + * + * HW triggers an irq when ECOUNT > cq_ecount_wait, hence + * set 1 less than cq_ecount_wait. And cq_time_wait is in + * usecs, convert that to 100ns count. + */ + otx2_write64(pfvf, NIX_LF_CINTX_WAIT(qidx), + ((u64)(pfvf->hw.cq_time_wait * 10) << 48) | + ((u64)pfvf->hw.cq_qcount_wait << 32) | + (pfvf->hw.cq_ecount_wait - 1)); +} + dma_addr_t otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, gfp_t gfp) { @@ -904,6 +918,47 @@ void mbox_handler_msix_offset(struct otx2_nic *pfvf, pfvf->hw.nix_msixoff = rsp->nix_msixoff; } +void otx2_free_cints(struct otx2_nic *pfvf, int n) +{ + struct otx2_qset *qset = &pfvf->qset; + struct otx2_hw *hw = &pfvf->hw; + int irq, qidx; + + for (qidx = 0, irq = hw->nix_msixoff + NIX_LF_CINT_VEC_START; + qidx < n; + qidx++, irq++) { + int vector = pci_irq_vector(pfvf->pdev, irq); + + irq_set_affinity_hint(vector, NULL); + free_cpumask_var(hw->affinity_mask[irq]); + free_irq(vector, &qset->napi[qidx]); + } +} + +void otx2_set_cints_affinity(struct otx2_nic *pfvf) +{ + struct otx2_hw *hw = &pfvf->hw; + int vec, cpu, irq, cint; + + vec = hw->nix_msixoff + NIX_LF_CINT_VEC_START; + cpu = cpumask_first(cpu_online_mask); + + /* CQ interrupts */ + for (cint = 0; cint < pfvf->hw.cint_cnt; cint++, vec++) { + if (!alloc_cpumask_var(&hw->affinity_mask[vec], GFP_KERNEL)) + return; + + cpumask_set_cpu(cpu, hw->affinity_mask[vec]); + + irq = pci_irq_vector(pfvf->pdev, vec); + irq_set_affinity_hint(irq, hw->affinity_mask[vec]); + + cpu = cpumask_next(cpu, cpu_online_mask); + if (unlikely(cpu >= nr_cpu_ids)) + cpu = 0; + } +} + #define M(_name, _id, _fn_name, _req_type, _rsp_type) \ int __weak \ otx2_mbox_up_handler_ ## _fn_name(struct otx2_nic *pfvf, \ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index a4f1c604cb60..92e08f57b07d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -21,6 +21,8 @@ /* PCI device IDs */ #define PCI_DEVID_OCTEONTX2_RVU_PF 0xA063 +#define PCI_SUBSYS_DEVID_96XX_RVU_PFVF 0xB200 + /* PCI BAR nos */ #define PCI_CFG_REG_BAR_NUM 2 #define PCI_MBOX_BAR_NUM 4 @@ -32,6 +34,13 @@ enum arua_mapped_qtypes { AURA_NIX_SQ, }; +/* NIX LF interrupts range*/ +#define NIX_LF_QINT_VEC_START 0x00 +#define NIX_LF_CINT_VEC_START 0x40 +#define NIX_LF_GINT_VEC 0x80 +#define NIX_LF_ERR_VEC 0x81 +#define NIX_LF_POISON_VEC 0x82 + struct mbox { struct otx2_mbox mbox; struct work_struct mbox_wrk; @@ -64,9 +73,13 @@ struct otx2_hw { /* HW settings, coalescing etc */ u16 rx_chan_base; u16 tx_chan_base; + u16 cq_qcount_wait; + u16 cq_ecount_wait; u16 rq_skid; + u8 cq_time_wait; /* MSI-X */ + u8 cint_cnt; /* CQ interrupt count */ u16 npa_msixoff; /* Offset of NPA vectors */ u16 nix_msixoff; /* Offset of NIX vectors */ char *irq_name; @@ -94,6 +107,36 @@ struct otx2_nic { int nix_blkaddr; }; +static inline bool is_96xx_A0(struct pci_dev *pdev) +{ + return (pdev->revision == 0x00) && + (pdev->subsystem_device == PCI_SUBSYS_DEVID_96XX_RVU_PFVF); +} + +static inline bool is_96xx_B0(struct pci_dev *pdev) +{ + return (pdev->revision == 0x01) && + (pdev->subsystem_device == PCI_SUBSYS_DEVID_96XX_RVU_PFVF); +} + +static inline void otx2_setup_dev_hw_settings(struct otx2_nic *pfvf) +{ + pfvf->hw.cq_time_wait = CQ_TIMER_THRESH_DEFAULT; + pfvf->hw.cq_ecount_wait = CQ_CQE_THRESH_DEFAULT; + pfvf->hw.cq_qcount_wait = CQ_QCOUNT_DEFAULT; + + if (is_96xx_A0(pfvf->pdev)) { + /* Time based irq coalescing is not supported */ + pfvf->hw.cq_qcount_wait = 0x0; + + /* Due to HW issue previous silicons required minimum + * 600 unused CQE to avoid CQ overflow. + */ + pfvf->hw.rq_skid = 600; + pfvf->qset.rqe_cnt = Q_COUNT(Q_SIZE_1K); + } +} + /* Register read/write APIs */ static inline void __iomem *otx2_get_regaddr(struct otx2_nic *nic, u64 offset) { @@ -337,6 +380,11 @@ MBOX_UP_CGX_MESSAGES #define RVU_PFVF_FUNC_SHIFT 0 #define RVU_PFVF_FUNC_MASK 0x3FF +static inline int rvu_get_pf(u16 pcifunc) +{ + return (pcifunc >> RVU_PFVF_PF_SHIFT) & RVU_PFVF_PF_MASK; +} + static inline dma_addr_t otx2_dma_map_page(struct otx2_nic *pfvf, struct page *page, size_t offset, size_t size, @@ -359,6 +407,12 @@ static inline void otx2_dma_unmap_page(struct otx2_nic *pfvf, dir, DMA_ATTR_SKIP_CPU_SYNC); } +/* MSI-X APIs */ +void otx2_free_cints(struct otx2_nic *pfvf, int n); +void otx2_set_cints_affinity(struct otx2_nic *pfvf); + +void otx2_config_irq_coalescing(struct otx2_nic *pfvf, int qidx); + /* RVU block related APIs */ int otx2_attach_npa_nix(struct otx2_nic *pfvf); int otx2_detach_resources(struct mbox *mbox); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 735188900de4..32c8bc422024 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -386,6 +386,38 @@ static int otx2_set_real_num_queues(struct net_device *netdev, return err; } +static irqreturn_t otx2_cq_intr_handler(int irq, void *cq_irq) +{ + struct otx2_cq_poll *cq_poll = (struct otx2_cq_poll *)cq_irq; + struct otx2_nic *pf = (struct otx2_nic *)cq_poll->dev; + int qidx = cq_poll->cint_idx; + + /* Disable interrupts. + * + * Completion interrupts behave in a level-triggered interrupt + * fashion, and hence have to be cleared only after it is serviced. + */ + otx2_write64(pf, NIX_LF_CINTX_ENA_W1C(qidx), BIT_ULL(0)); + + /* Schedule NAPI */ + napi_schedule_irqoff(&cq_poll->napi); + + return IRQ_HANDLED; +} + +static void otx2_disable_napi(struct otx2_nic *pf) +{ + struct otx2_qset *qset = &pf->qset; + struct otx2_cq_poll *cq_poll; + int qidx; + + for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { + cq_poll = &qset->napi[qidx]; + napi_disable(&cq_poll->napi); + netif_napi_del(&cq_poll->napi); + } +} + static void otx2_free_cq_res(struct otx2_nic *pf) { struct otx2_qset *qset = &pf->qset; @@ -564,12 +596,21 @@ static void otx2_free_hw_resources(struct otx2_nic *pf) static int otx2_open(struct net_device *netdev) { struct otx2_nic *pf = netdev_priv(netdev); + struct otx2_cq_poll *cq_poll = NULL; struct otx2_qset *qset = &pf->qset; - int err = 0; + int err = 0, qidx, vec; + char *irq_name; netif_carrier_off(netdev); pf->qset.cq_cnt = pf->hw.rx_queues + pf->hw.tx_queues; + /* RQ and SQs are mapped to different CQs, + * so find out max CQ IRQs (i.e CINTs) needed. + */ + pf->hw.cint_cnt = max(pf->hw.rx_queues, pf->hw.tx_queues); + qset->napi = kcalloc(pf->hw.cint_cnt, sizeof(*cq_poll), GFP_KERNEL); + if (!qset->napi) + return -ENOMEM; /* CQ size of RQ */ qset->rqe_cnt = qset->rqe_cnt ? qset->rqe_cnt : Q_COUNT(Q_SIZE_256); @@ -591,23 +632,100 @@ static int otx2_open(struct net_device *netdev) if (err) goto err_free_mem; + /* Register NAPI handler */ + for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { + cq_poll = &qset->napi[qidx]; + cq_poll->cint_idx = qidx; + /* RQ0 & SQ0 are mapped to CINT0 and so on.. + * 'cq_ids[0]' points to RQ's CQ and + * 'cq_ids[1]' points to SQ's CQ and + */ + cq_poll->cq_ids[CQ_RX] = + (qidx < pf->hw.rx_queues) ? qidx : CINT_INVALID_CQ; + cq_poll->cq_ids[CQ_TX] = (qidx < pf->hw.tx_queues) ? + qidx + pf->hw.rx_queues : CINT_INVALID_CQ; + cq_poll->dev = (void *)pf; + netif_napi_add(netdev, &cq_poll->napi, + otx2_napi_handler, NAPI_POLL_WEIGHT); + napi_enable(&cq_poll->napi); + } + + /* Register CQ IRQ handlers */ + vec = pf->hw.nix_msixoff + NIX_LF_CINT_VEC_START; + for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { + irq_name = &pf->hw.irq_name[vec * NAME_SIZE]; + + snprintf(irq_name, NAME_SIZE, "%s-rxtx-%d", pf->netdev->name, + qidx); + + err = request_irq(pci_irq_vector(pf->pdev, vec), + otx2_cq_intr_handler, 0, irq_name, + &qset->napi[qidx]); + if (err) { + dev_err(pf->dev, + "RVUPF%d: IRQ registration failed for CQ%d\n", + rvu_get_pf(pf->pcifunc), qidx); + goto err_free_cints; + } + vec++; + + otx2_config_irq_coalescing(pf, qidx); + + /* Enable CQ IRQ */ + otx2_write64(pf, NIX_LF_CINTX_INT(qidx), BIT_ULL(0)); + otx2_write64(pf, NIX_LF_CINTX_ENA_W1S(qidx), BIT_ULL(0)); + } + + otx2_set_cints_affinity(pf); + return 0; + +err_free_cints: + otx2_free_cints(pf, qidx); + otx2_disable_napi(pf); + otx2_free_hw_resources(pf); err_free_mem: kfree(qset->sq); kfree(qset->cq); + kfree(qset->napi); return err; } static int otx2_stop(struct net_device *netdev) { struct otx2_nic *pf = netdev_priv(netdev); + struct otx2_cq_poll *cq_poll = NULL; struct otx2_qset *qset = &pf->qset; + int qidx, vec; + + netif_carrier_off(netdev); + netif_tx_stop_all_queues(netdev); + + /* Cleanup CQ NAPI and IRQ */ + vec = pf->hw.nix_msixoff + NIX_LF_CINT_VEC_START; + for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { + /* Disable interrupt */ + otx2_write64(pf, NIX_LF_CINTX_ENA_W1C(qidx), BIT_ULL(0)); + + synchronize_irq(pci_irq_vector(pf->pdev, vec)); + + cq_poll = &qset->napi[qidx]; + napi_synchronize(&cq_poll->napi); + vec++; + } + + netif_tx_disable(netdev); otx2_free_hw_resources(pf); + otx2_free_cints(pf, pf->hw.cint_cnt); + otx2_disable_napi(pf); + + for (qidx = 0; qidx < netdev->num_tx_queues; qidx++) + netdev_tx_reset_queue(netdev_get_tx_queue(netdev, qidx)); kfree(qset->sq); kfree(qset->cq); - + kfree(qset->napi); /* Do not clear RQ/SQ ringsize settings */ memset((void *)qset + offsetof(struct otx2_qset, sqe_cnt), 0, sizeof(*qset) - offsetof(struct otx2_qset, sqe_cnt)); @@ -646,7 +764,6 @@ static int otx2_realloc_msix_vectors(struct otx2_nic *pf) * upto NIX vector offset. */ num_vec = hw->nix_msixoff; -#define NIX_LF_CINT_VEC_START 0x40 num_vec += NIX_LF_CINT_VEC_START + hw->max_queues; otx2_disable_mbox_intr(pf); @@ -769,6 +886,8 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto err_detach_rsrc; + otx2_setup_dev_hw_settings(pf); + /* NPA's pool is a stack to which SW frees buffer pointers via Aura. * HW allocates buffer pointer from stack and uses it for DMA'ing * ingress packet. In some scenarios HW can free back allocated buffer diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c new file mode 100644 index 000000000000..b07082ec7d98 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2020 Marvell International Ltd. + * + * 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. + */ + +#include +#include + +#include "otx2_reg.h" +#include "otx2_common.h" +#include "otx2_struct.h" +#include "otx2_txrx.h" + +static int otx2_rx_napi_handler(struct otx2_nic *pfvf, + struct napi_struct *napi, + struct otx2_cq_queue *cq, int budget) +{ + /* Nothing to do, for now */ + return 0; +} + +static int otx2_tx_napi_handler(struct otx2_nic *pfvf, + struct otx2_cq_queue *cq, int budget) +{ + /* Nothing to do, for now */ + return 0; +} + +int otx2_napi_handler(struct napi_struct *napi, int budget) +{ + struct otx2_cq_poll *cq_poll; + int workdone = 0, cq_idx, i; + struct otx2_cq_queue *cq; + struct otx2_qset *qset; + struct otx2_nic *pfvf; + + cq_poll = container_of(napi, struct otx2_cq_poll, napi); + pfvf = (struct otx2_nic *)cq_poll->dev; + qset = &pfvf->qset; + + for (i = CQS_PER_CINT - 1; i >= 0; i--) { + cq_idx = cq_poll->cq_ids[i]; + if (unlikely(cq_idx == CINT_INVALID_CQ)) + continue; + cq = &qset->cq[cq_idx]; + if (cq->cq_type == CQ_RX) { + workdone += otx2_rx_napi_handler(pfvf, napi, + cq, budget); + } else { + workdone += otx2_tx_napi_handler(pfvf, cq, budget); + } + } + + /* Clear the IRQ */ + otx2_write64(pfvf, NIX_LF_CINTX_INT(cq_poll->cint_idx), BIT_ULL(0)); + + if (workdone < budget && napi_complete_done(napi, workdone)) { + /* Re-enable interrupts */ + otx2_write64(pfvf, NIX_LF_CINTX_ENA_W1S(cq_poll->cint_idx), + BIT_ULL(0)); + } + return workdone; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h index ce6efcf2e8cf..a81bdc633884 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -37,6 +37,22 @@ ((x) - OTX2_HEAD_ROOM - \ OTX2_DATA_ALIGN(sizeof(struct skb_shared_info))) +/* IRQ triggered when NIX_LF_CINTX_CNT[ECOUNT] + * is equal to this value. + */ +#define CQ_CQE_THRESH_DEFAULT 10 + +/* IRQ triggered when NIX_LF_CINTX_CNT[ECOUNT] + * is nonzero and this much time elapses after that. + */ +#define CQ_TIMER_THRESH_DEFAULT 1 /* 1 usec */ +#define CQ_TIMER_THRESH_MAX 25 /* 25 usec */ + +/* Min number of CQs (of the ones mapped to this CINT) + * with valid CQEs. + */ +#define CQ_QCOUNT_DEFAULT 1 + struct otx2_snd_queue { u8 aura_id; u16 sqe_size; @@ -52,6 +68,20 @@ struct otx2_snd_queue { u64 *sqb_ptrs; } ____cacheline_aligned_in_smp; +enum cq_type { + CQ_RX, + CQ_TX, + CQS_PER_CINT = 2, /* RQ + SQ */ +}; + +struct otx2_cq_poll { + void *dev; +#define CINT_INVALID_CQ 255 + u8 cint_idx; + u8 cq_ids[CQS_PER_CINT]; + struct napi_struct napi; +}; + struct otx2_pool { struct qmem *stack; struct qmem *fc_addr; @@ -62,12 +92,6 @@ struct otx2_pool { struct page *page; }; -enum cq_type { - CQ_RX, - CQ_TX, - CQS_PER_CINT = 2, /* RQ + SQ */ -}; - struct otx2_cq_queue { u8 cq_idx; u8 cq_type; @@ -86,6 +110,7 @@ struct otx2_qset { u16 cq_cnt; u16 xqe_size; struct otx2_pool *pool; + struct otx2_cq_poll *napi; struct otx2_cq_queue *cq; struct otx2_snd_queue *sq; }; @@ -99,4 +124,5 @@ static inline u64 otx2_iova_to_phys(void *iommu_domain, dma_addr_t dma_addr) return dma_addr; } +int otx2_napi_handler(struct napi_struct *napi, int budget); #endif /* OTX2_TXRX_H */ -- cgit v1.2.1 From abe0254333124598d1e7452c19e9e753c6bfaada Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Mon, 27 Jan 2020 18:35:20 +0530 Subject: octeontx2-pf: Receive packet handling support Added receive packet handling (NAPI) support, error stats, RX_ALL capability config option to passon error pkts to stack upon user request. In subsequent patches these error stats will be added to ethttool. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 2 + .../ethernet/marvell/octeontx2/nic/otx2_common.h | 44 +++++ .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 16 +- .../ethernet/marvell/octeontx2/nic/otx2_struct.h | 108 +++++++++++++ .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.c | 180 ++++++++++++++++++++- .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.h | 2 + 6 files changed, 349 insertions(+), 3 deletions(-) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 8f7b2cf40d7e..6b548313be3b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -334,6 +334,7 @@ static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) cq->cq_idx = qidx; if (qidx < pfvf->hw.rx_queues) { cq->cq_type = CQ_RX; + cq->cint_idx = qidx; cq->cqe_cnt = qset->rqe_cnt; } else { cq->cq_type = CQ_TX; @@ -364,6 +365,7 @@ static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) aq->cq.qsize = Q_SIZE(cq->cqe_cnt, 4); aq->cq.caching = 1; aq->cq.base = cq->cqe->iova; + aq->cq.cint_idx = cq->cint_idx; aq->cq.avg_level = 255; if (qidx < pfvf->hw.rx_queues) { diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 92e08f57b07d..622c4f8f3ca0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -41,6 +41,46 @@ enum arua_mapped_qtypes { #define NIX_LF_ERR_VEC 0x81 #define NIX_LF_POISON_VEC 0x82 +/* NIX (or NPC) RX errors */ +enum otx2_errlvl { + NPC_ERRLVL_RE, + NPC_ERRLVL_LID_LA, + NPC_ERRLVL_LID_LB, + NPC_ERRLVL_LID_LC, + NPC_ERRLVL_LID_LD, + NPC_ERRLVL_LID_LE, + NPC_ERRLVL_LID_LF, + NPC_ERRLVL_LID_LG, + NPC_ERRLVL_LID_LH, + NPC_ERRLVL_NIX = 0x0F, +}; + +enum otx2_errcodes_re { + /* NPC_ERRLVL_RE errcodes */ + ERRCODE_FCS = 0x7, + ERRCODE_FCS_RCV = 0x8, + ERRCODE_UNDERSIZE = 0x10, + ERRCODE_OVERSIZE = 0x11, + ERRCODE_OL2_LEN_MISMATCH = 0x12, + /* NPC_ERRLVL_NIX errcodes */ + ERRCODE_OL3_LEN = 0x10, + ERRCODE_OL4_LEN = 0x11, + ERRCODE_OL4_CSUM = 0x12, + ERRCODE_IL3_LEN = 0x20, + ERRCODE_IL4_LEN = 0x21, + ERRCODE_IL4_CSUM = 0x22, +}; + +/* Driver counted stats */ +struct otx2_drv_stats { + atomic_t rx_fcs_errs; + atomic_t rx_oversize_errs; + atomic_t rx_undersize_errs; + atomic_t rx_csum_errs; + atomic_t rx_len_errs; + atomic_t rx_other_errs; +}; + struct mbox { struct otx2_mbox mbox; struct work_struct mbox_wrk; @@ -84,6 +124,9 @@ struct otx2_hw { u16 nix_msixoff; /* Offset of NIX vectors */ char *irq_name; cpumask_var_t *affinity_mask; + + /* Stats */ + struct otx2_drv_stats drv_stats; }; struct otx2_nic { @@ -431,6 +474,7 @@ void otx2_sqb_flush(struct otx2_nic *pfvf); dma_addr_t otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, gfp_t gfp); void otx2_ctx_disable(struct mbox *mbox, int type, bool npa); +void otx2_cleanup_rx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); /* Mbox handlers */ void mbox_handler_msix_offset(struct otx2_nic *pfvf, diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 32c8bc422024..37362ef36683 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -547,9 +547,11 @@ exit: static void otx2_free_hw_resources(struct otx2_nic *pf) { + struct otx2_qset *qset = &pf->qset; struct mbox *mbox = &pf->mbox; + struct otx2_cq_queue *cq; struct msg_req *req; - int err; + int qidx, err; /* Ensure all SQE are processed */ otx2_sqb_flush(pf); @@ -562,6 +564,13 @@ static void otx2_free_hw_resources(struct otx2_nic *pf) /* Disable RQs */ otx2_ctx_disable(mbox, NIX_AQ_CTYPE_RQ, false); + /*Dequeue all CQEs */ + for (qidx = 0; qidx < qset->cq_cnt; qidx++) { + cq = &qset->cq[qidx]; + if (cq->cq_type == CQ_RX) + otx2_cleanup_rx_cqes(pf, cq); + } + otx2_free_sq_res(pf); /* Free RQ buffer pointers*/ @@ -901,6 +910,11 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) */ pf->iommu_domain = iommu_get_domain_for_dev(dev); + netdev->hw_features = NETIF_F_RXCSUM; + netdev->features |= netdev->hw_features; + + netdev->hw_features |= NETIF_F_RXALL; + netdev->netdev_ops = &otx2_netdev_ops; err = register_netdev(netdev); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h index 13086b05e872..dad73cb5e6f8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h @@ -28,4 +28,112 @@ enum nix_send_ldtype { NIX_SEND_LDTYPE_LDWB = 0x2, }; +/* NIX wqe/cqe types */ +enum nix_xqe_type { + NIX_XQE_TYPE_INVALID = 0x0, + NIX_XQE_TYPE_RX = 0x1, + NIX_XQE_TYPE_RX_IPSECS = 0x2, + NIX_XQE_TYPE_RX_IPSECH = 0x3, + NIX_XQE_TYPE_RX_IPSECD = 0x4, + NIX_XQE_TYPE_SEND = 0x8, +}; + +/* NIX CQE/SQE subdescriptor types */ +enum nix_subdc { + NIX_SUBDC_NOP = 0x0, + NIX_SUBDC_EXT = 0x1, + NIX_SUBDC_CRC = 0x2, + NIX_SUBDC_IMM = 0x3, + NIX_SUBDC_SG = 0x4, + NIX_SUBDC_MEM = 0x5, + NIX_SUBDC_JUMP = 0x6, + NIX_SUBDC_WORK = 0x7, + NIX_SUBDC_SOD = 0xf, +}; + +/* NIX CQE header structure */ +struct nix_cqe_hdr_s { + u64 flow_tag : 32; + u64 q : 20; + u64 reserved_52_57 : 6; + u64 node : 2; + u64 cqe_type : 4; +}; + +/* NIX CQE RX parse structure */ +struct nix_rx_parse_s { + u64 chan : 12; + u64 desc_sizem1 : 5; + u64 rsvd_17 : 1; + u64 express : 1; + u64 wqwd : 1; + u64 errlev : 4; + u64 errcode : 8; + u64 latype : 4; + u64 lbtype : 4; + u64 lctype : 4; + u64 ldtype : 4; + u64 letype : 4; + u64 lftype : 4; + u64 lgtype : 4; + u64 lhtype : 4; + u64 pkt_lenm1 : 16; /* W1 */ + u64 l2m : 1; + u64 l2b : 1; + u64 l3m : 1; + u64 l3b : 1; + u64 vtag0_valid : 1; + u64 vtag0_gone : 1; + u64 vtag1_valid : 1; + u64 vtag1_gone : 1; + u64 pkind : 6; + u64 rsvd_95_94 : 2; + u64 vtag0_tci : 16; + u64 vtag1_tci : 16; + u64 laflags : 8; /* W2 */ + u64 lbflags : 8; + u64 lcflags : 8; + u64 ldflags : 8; + u64 leflags : 8; + u64 lfflags : 8; + u64 lgflags : 8; + u64 lhflags : 8; + u64 eoh_ptr : 8; /* W3 */ + u64 wqe_aura : 20; + u64 pb_aura : 20; + u64 match_id : 16; + u64 laptr : 8; /* W4 */ + u64 lbptr : 8; + u64 lcptr : 8; + u64 ldptr : 8; + u64 leptr : 8; + u64 lfptr : 8; + u64 lgptr : 8; + u64 lhptr : 8; + u64 vtag0_ptr : 8; /* W5 */ + u64 vtag1_ptr : 8; + u64 flow_key_alg : 5; + u64 rsvd_383_341 : 43; + u64 rsvd_447_384; /* W6 */ +}; + +/* NIX CQE RX scatter/gather subdescriptor structure */ +struct nix_rx_sg_s { + u64 seg_size : 16; /* W0 */ + u64 seg2_size : 16; + u64 seg3_size : 16; + u64 segs : 2; + u64 rsvd_59_50 : 10; + u64 subdc : 4; + u64 seg_addr; + u64 seg2_addr; + u64 seg3_addr; +}; + +struct nix_cqe_rx_s { + struct nix_cqe_hdr_s hdr; + struct nix_rx_parse_s parse; + struct nix_rx_sg_s sg; +}; + #endif /* OTX2_STRUCT_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index b07082ec7d98..0c1519a05281 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -16,12 +16,167 @@ #include "otx2_struct.h" #include "otx2_txrx.h" +#define CQE_ADDR(CQ, idx) ((CQ)->cqe_base + ((CQ)->cqe_size * (idx))) + +static struct nix_cqe_hdr_s *otx2_get_next_cqe(struct otx2_cq_queue *cq) +{ + struct nix_cqe_hdr_s *cqe_hdr; + + cqe_hdr = (struct nix_cqe_hdr_s *)CQE_ADDR(cq, cq->cq_head); + if (cqe_hdr->cqe_type == NIX_XQE_TYPE_INVALID) + return NULL; + + cq->cq_head++; + cq->cq_head &= (cq->cqe_cnt - 1); + + return cqe_hdr; +} + +static void otx2_skb_add_frag(struct otx2_nic *pfvf, struct sk_buff *skb, + u64 iova, int len) +{ + struct page *page; + void *va; + + va = phys_to_virt(otx2_iova_to_phys(pfvf->iommu_domain, iova)); + page = virt_to_page(va); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, + va - page_address(page), len, pfvf->rbsize); + + otx2_dma_unmap_page(pfvf, iova - OTX2_HEAD_ROOM, + pfvf->rbsize, DMA_FROM_DEVICE); +} + +static bool otx2_check_rcv_errors(struct otx2_nic *pfvf, + struct nix_cqe_rx_s *cqe, int qidx) +{ + struct otx2_drv_stats *stats = &pfvf->hw.drv_stats; + struct nix_rx_parse_s *parse = &cqe->parse; + + if (parse->errlev == NPC_ERRLVL_RE) { + switch (parse->errcode) { + case ERRCODE_FCS: + case ERRCODE_FCS_RCV: + atomic_inc(&stats->rx_fcs_errs); + break; + case ERRCODE_UNDERSIZE: + atomic_inc(&stats->rx_undersize_errs); + break; + case ERRCODE_OVERSIZE: + atomic_inc(&stats->rx_oversize_errs); + break; + case ERRCODE_OL2_LEN_MISMATCH: + atomic_inc(&stats->rx_len_errs); + break; + default: + atomic_inc(&stats->rx_other_errs); + break; + } + } else if (parse->errlev == NPC_ERRLVL_NIX) { + switch (parse->errcode) { + case ERRCODE_OL3_LEN: + case ERRCODE_OL4_LEN: + case ERRCODE_IL3_LEN: + case ERRCODE_IL4_LEN: + atomic_inc(&stats->rx_len_errs); + break; + case ERRCODE_OL4_CSUM: + case ERRCODE_IL4_CSUM: + atomic_inc(&stats->rx_csum_errs); + break; + default: + atomic_inc(&stats->rx_other_errs); + break; + } + } else { + atomic_inc(&stats->rx_other_errs); + /* For now ignore all the NPC parser errors and + * pass the packets to stack. + */ + return false; + } + + /* If RXALL is enabled pass on packets to stack. */ + if (cqe->sg.segs && (pfvf->netdev->features & NETIF_F_RXALL)) + return false; + + /* Free buffer back to pool */ + if (cqe->sg.segs) + otx2_aura_freeptr(pfvf, qidx, cqe->sg.seg_addr & ~0x07ULL); + return true; +} + +static void otx2_rcv_pkt_handler(struct otx2_nic *pfvf, + struct napi_struct *napi, + struct otx2_cq_queue *cq, + struct nix_cqe_rx_s *cqe) +{ + struct nix_rx_parse_s *parse = &cqe->parse; + struct sk_buff *skb = NULL; + + if (unlikely(parse->errlev || parse->errcode)) { + if (otx2_check_rcv_errors(pfvf, cqe, cq->cq_idx)) + return; + } + + skb = napi_get_frags(napi); + if (unlikely(!skb)) + return; + + otx2_skb_add_frag(pfvf, skb, cqe->sg.seg_addr, cqe->sg.seg_size); + cq->pool_ptrs++; + + skb_record_rx_queue(skb, cq->cq_idx); + if (pfvf->netdev->features & NETIF_F_RXCSUM) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + napi_gro_frags(napi); +} + static int otx2_rx_napi_handler(struct otx2_nic *pfvf, struct napi_struct *napi, struct otx2_cq_queue *cq, int budget) { - /* Nothing to do, for now */ - return 0; + struct nix_cqe_rx_s *cqe; + int processed_cqe = 0; + s64 bufptr; + + while (likely(processed_cqe < budget)) { + cqe = (struct nix_cqe_rx_s *)CQE_ADDR(cq, cq->cq_head); + if (cqe->hdr.cqe_type == NIX_XQE_TYPE_INVALID || + !cqe->sg.seg_addr) { + if (!processed_cqe) + return 0; + break; + } + cq->cq_head++; + cq->cq_head &= (cq->cqe_cnt - 1); + + otx2_rcv_pkt_handler(pfvf, napi, cq, cqe); + + cqe->hdr.cqe_type = NIX_XQE_TYPE_INVALID; + cqe->sg.seg_addr = 0x00; + processed_cqe++; + } + + /* Free CQEs to HW */ + otx2_write64(pfvf, NIX_LF_CQ_OP_DOOR, + ((u64)cq->cq_idx << 32) | processed_cqe); + + if (unlikely(!cq->pool_ptrs)) + return 0; + + /* Refill pool with new buffers */ + while (cq->pool_ptrs) { + bufptr = otx2_alloc_rbuf(pfvf, cq->rbpool, GFP_ATOMIC); + if (unlikely(bufptr <= 0)) + break; + otx2_aura_freeptr(pfvf, cq->cq_idx, bufptr + OTX2_HEAD_ROOM); + cq->pool_ptrs--; + } + otx2_get_page(cq->rbpool); + + return processed_cqe; } static int otx2_tx_napi_handler(struct otx2_nic *pfvf, @@ -66,3 +221,24 @@ int otx2_napi_handler(struct napi_struct *napi, int budget) } return workdone; } + +void otx2_cleanup_rx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq) +{ + struct nix_cqe_rx_s *cqe; + int processed_cqe = 0; + u64 iova, pa; + + while ((cqe = (struct nix_cqe_rx_s *)otx2_get_next_cqe(cq))) { + if (!cqe->sg.subdc) + continue; + iova = cqe->sg.seg_addr - OTX2_HEAD_ROOM; + pa = otx2_iova_to_phys(pfvf->iommu_domain, iova); + otx2_dma_unmap_page(pfvf, iova, pfvf->rbsize, DMA_FROM_DEVICE); + put_page(virt_to_page(phys_to_virt(pa))); + processed_cqe++; + } + + /* Free CQEs to HW */ + otx2_write64(pfvf, NIX_LF_CQ_OP_DOOR, + ((u64)cq->cq_idx << 32) | processed_cqe); +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h index a81bdc633884..0944c176efa3 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -95,9 +95,11 @@ struct otx2_pool { struct otx2_cq_queue { u8 cq_idx; u8 cq_type; + u8 cint_idx; /* CQ interrupt id */ u16 cqe_size; u16 pool_ptrs; u32 cqe_cnt; + u32 cq_head; void *cqe_base; struct qmem *cqe; struct otx2_pool *rbpool; -- cgit v1.2.1 From 3ca6c4c882a7f34085b170d93cf0d0e843aa00e6 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Mon, 27 Jan 2020 18:35:21 +0530 Subject: octeontx2-pf: Add packet transmission support This patch adds the packet transmission support. For a given skb prepares send queue descriptors (SQEs) and pushes them to HW. Here driver doesn't maintain it's own SQ rings, SQEs are pushed to HW using a silicon specific operations called LMTST. From the instuction HW derives the transmit queue number and queues the SQE to that queue. These LMTST instructions are designed to avoid queue maintenance in SW and lockless behavior ie when multiple cores are trying to add SQEs to same queue then HW will takecare of serialization, no need for SW to hold locks. Also supports scatter/gather. Co-developed-by: Geetha sowjanya Signed-off-by: Geetha sowjanya Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 7 + .../ethernet/marvell/octeontx2/nic/otx2_common.h | 13 + .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 38 ++- .../ethernet/marvell/octeontx2/nic/otx2_struct.h | 109 ++++++++ .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.c | 291 ++++++++++++++++++++- .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.h | 14 + 6 files changed, 470 insertions(+), 2 deletions(-) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 6b548313be3b..e9bd2d6f6ebc 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -287,9 +287,15 @@ static int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura) return err; sq->sqe_base = sq->sqe->base; + sq->sg = kcalloc(qset->sqe_cnt, sizeof(struct sg_list), GFP_KERNEL); + if (!sq->sg) + return -ENOMEM; + sq->head = 0; sq->sqe_per_sqb = (pfvf->hw.sqb_size / sq->sqe_size) - 1; sq->num_sqbs = (qset->sqe_cnt + sq->sqe_per_sqb) / sq->sqe_per_sqb; + /* Set SQE threshold to 10% of total SQEs */ + sq->sqe_thresh = ((sq->num_sqbs * sq->sqe_per_sqb) * 10) / 100; sq->aura_id = sqb_aura; sq->aura_fc_addr = pool->fc_addr->base; sq->lmt_addr = (__force u64 *)(pfvf->reg_base + LMT_LF_LMTLINEX(qidx)); @@ -338,6 +344,7 @@ static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) cq->cqe_cnt = qset->rqe_cnt; } else { cq->cq_type = CQ_TX; + cq->cint_idx = qidx - pfvf->hw.rx_queues; cq->cqe_cnt = qset->sqe_cnt; } cq->cqe_size = pfvf->qset.xqe_size; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 622c4f8f3ca0..11948540dd4a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -300,9 +300,21 @@ static inline u64 otx2_atomic64_add(u64 incr, u64 *ptr) return result; } +static inline u64 otx2_lmt_flush(uint64_t addr) +{ + u64 result = 0; + + __asm__ volatile(".cpu generic+lse\n" + "ldeor xzr,%x[rf],[%[rs]]" + : [rf]"=r"(result) + : [rs]"r"(addr)); + return result; +} + #else #define otx2_write128(lo, hi, addr) #define otx2_atomic64_add(incr, ptr) ({ *ptr += incr; }) +#define otx2_lmt_flush(addr) ({ 0; }) #endif /* Alloc pointer from pool/aura */ @@ -475,6 +487,7 @@ dma_addr_t otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, gfp_t gfp); void otx2_ctx_disable(struct mbox *mbox, int type, bool npa); void otx2_cleanup_rx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); +void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); /* Mbox handlers */ void mbox_handler_msix_offset(struct otx2_nic *pfvf, diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 37362ef36683..a18bf5facaaf 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -445,6 +445,7 @@ static void otx2_free_sq_res(struct otx2_nic *pf) for (qidx = 0; qidx < pf->hw.tx_queues; qidx++) { sq = &qset->sq[qidx]; qmem_free(pf->dev, sq->sqe); + kfree(sq->sg); kfree(sq->sqb_ptrs); } } @@ -569,6 +570,8 @@ static void otx2_free_hw_resources(struct otx2_nic *pf) cq = &qset->cq[qidx]; if (cq->cq_type == CQ_RX) otx2_cleanup_rx_cqes(pf, cq); + else + otx2_cleanup_tx_cqes(pf, cq); } otx2_free_sq_res(pf); @@ -741,9 +744,41 @@ static int otx2_stop(struct net_device *netdev) return 0; } +static netdev_tx_t otx2_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct otx2_nic *pf = netdev_priv(netdev); + int qidx = skb_get_queue_mapping(skb); + struct otx2_snd_queue *sq; + struct netdev_queue *txq; + + /* Check for minimum packet length */ + if (skb->len <= ETH_HLEN) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + sq = &pf->qset.sq[qidx]; + txq = netdev_get_tx_queue(netdev, qidx); + + if (!otx2_sq_append_skb(netdev, sq, skb, qidx)) { + netif_tx_stop_queue(txq); + + /* Check again, incase SQBs got freed up */ + smp_mb(); + if (((sq->num_sqbs - *sq->aura_fc_addr) * sq->sqe_per_sqb) + > sq->sqe_thresh) + netif_tx_wake_queue(txq); + + return NETDEV_TX_BUSY; + } + + return NETDEV_TX_OK; +} + static const struct net_device_ops otx2_netdev_ops = { .ndo_open = otx2_open, .ndo_stop = otx2_stop, + .ndo_start_xmit = otx2_xmit, }; static int otx2_check_pf_usable(struct otx2_nic *nic) @@ -910,7 +945,8 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) */ pf->iommu_domain = iommu_get_domain_for_dev(dev); - netdev->hw_features = NETIF_F_RXCSUM; + netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | NETIF_F_SG); netdev->features |= netdev->hw_features; netdev->hw_features |= NETIF_F_RXALL; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h index dad73cb5e6f8..04a9f12be2e9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h @@ -28,6 +28,21 @@ enum nix_send_ldtype { NIX_SEND_LDTYPE_LDWB = 0x2, }; +/* CSUM offload */ +enum nix_sendl3type { + NIX_SENDL3TYPE_NONE = 0x0, + NIX_SENDL3TYPE_IP4 = 0x2, + NIX_SENDL3TYPE_IP4_CKSUM = 0x3, + NIX_SENDL3TYPE_IP6 = 0x4, +}; + +enum nix_sendl4type { + NIX_SENDL4TYPE_NONE, + NIX_SENDL4TYPE_TCP_CKSUM, + NIX_SENDL4TYPE_SCTP_CKSUM, + NIX_SENDL4TYPE_UDP_CKSUM, +}; + /* NIX wqe/cqe types */ enum nix_xqe_type { NIX_XQE_TYPE_INVALID = 0x0, @@ -51,6 +66,20 @@ enum nix_subdc { NIX_SUBDC_SOD = 0xf, }; +/* Algorithm for nix_sqe_mem_s header (value of the `alg` field) */ +enum nix_sendmemalg { + NIX_SENDMEMALG_E_SET = 0x0, + NIX_SENDMEMALG_E_SETTSTMP = 0x1, + NIX_SENDMEMALG_E_SETRSLT = 0x2, + NIX_SENDMEMALG_E_ADD = 0x8, + NIX_SENDMEMALG_E_SUB = 0x9, + NIX_SENDMEMALG_E_ADDLEN = 0xa, + NIX_SENDMEMALG_E_SUBLEN = 0xb, + NIX_SENDMEMALG_E_ADDMBUF = 0xc, + NIX_SENDMEMALG_E_SUBMBUF = 0xd, + NIX_SENDMEMALG_E_ENUM_LAST = 0xe, +}; + /* NIX CQE header structure */ struct nix_cqe_hdr_s { u64 flow_tag : 32; @@ -130,10 +159,90 @@ struct nix_rx_sg_s { u64 seg3_addr; }; +struct nix_send_comp_s { + u64 status : 8; + u64 sqe_id : 16; + u64 rsvd_24_63 : 40; +}; + struct nix_cqe_rx_s { struct nix_cqe_hdr_s hdr; struct nix_rx_parse_s parse; struct nix_rx_sg_s sg; }; +struct nix_cqe_tx_s { + struct nix_cqe_hdr_s hdr; + struct nix_send_comp_s comp; +}; + +/* NIX SQE header structure */ +struct nix_sqe_hdr_s { + u64 total : 18; /* W0 */ + u64 reserved_18 : 1; + u64 df : 1; + u64 aura : 20; + u64 sizem1 : 3; + u64 pnc : 1; + u64 sq : 20; + u64 ol3ptr : 8; /* W1 */ + u64 ol4ptr : 8; + u64 il3ptr : 8; + u64 il4ptr : 8; + u64 ol3type : 4; + u64 ol4type : 4; + u64 il3type : 4; + u64 il4type : 4; + u64 sqe_id : 16; + +}; + +/* NIX send extended header subdescriptor structure */ +struct nix_sqe_ext_s { + u64 lso_mps : 14; /* W0 */ + u64 lso : 1; + u64 tstmp : 1; + u64 lso_sb : 8; + u64 lso_format : 5; + u64 rsvd_31_29 : 3; + u64 shp_chg : 9; + u64 shp_dis : 1; + u64 shp_ra : 2; + u64 markptr : 8; + u64 markform : 7; + u64 mark_en : 1; + u64 subdc : 4; + u64 vlan0_ins_ptr : 8; /* W1 */ + u64 vlan0_ins_tci : 16; + u64 vlan1_ins_ptr : 8; + u64 vlan1_ins_tci : 16; + u64 vlan0_ins_ena : 1; + u64 vlan1_ins_ena : 1; + u64 rsvd_127_114 : 14; +}; + +struct nix_sqe_sg_s { + u64 seg1_size : 16; + u64 seg2_size : 16; + u64 seg3_size : 16; + u64 segs : 2; + u64 rsvd_54_50 : 5; + u64 i1 : 1; + u64 i2 : 1; + u64 i3 : 1; + u64 ld_type : 2; + u64 subdc : 4; +}; + +/* NIX send memory subdescriptor structure */ +struct nix_sqe_mem_s { + u64 offset : 16; /* W0 */ + u64 rsvd_52_16 : 37; + u64 wmem : 1; + u64 dsz : 2; + u64 alg : 4; + u64 subdc : 4; + u64 addr; /* W1 */ +}; + #endif /* OTX2_STRUCT_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index 0c1519a05281..f19f5d044110 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -32,6 +32,74 @@ static struct nix_cqe_hdr_s *otx2_get_next_cqe(struct otx2_cq_queue *cq) return cqe_hdr; } +static unsigned int frag_num(unsigned int i) +{ +#ifdef __BIG_ENDIAN + return (i & ~3) + 3 - (i & 3); +#else + return i; +#endif +} + +static dma_addr_t otx2_dma_map_skb_frag(struct otx2_nic *pfvf, + struct sk_buff *skb, int seg, int *len) +{ + const skb_frag_t *frag; + struct page *page; + int offset; + + /* First segment is always skb->data */ + if (!seg) { + page = virt_to_page(skb->data); + offset = offset_in_page(skb->data); + *len = skb_headlen(skb); + } else { + frag = &skb_shinfo(skb)->frags[seg - 1]; + page = skb_frag_page(frag); + offset = skb_frag_off(frag); + *len = skb_frag_size(frag); + } + return otx2_dma_map_page(pfvf, page, offset, *len, DMA_TO_DEVICE); +} + +static void otx2_dma_unmap_skb_frags(struct otx2_nic *pfvf, struct sg_list *sg) +{ + int seg; + + for (seg = 0; seg < sg->num_segs; seg++) { + otx2_dma_unmap_page(pfvf, sg->dma_addr[seg], + sg->size[seg], DMA_TO_DEVICE); + } + sg->num_segs = 0; +} + +static void otx2_snd_pkt_handler(struct otx2_nic *pfvf, + struct otx2_cq_queue *cq, + struct otx2_snd_queue *sq, + struct nix_cqe_tx_s *cqe, + int budget, int *tx_pkts, int *tx_bytes) +{ + struct nix_send_comp_s *snd_comp = &cqe->comp; + struct sk_buff *skb = NULL; + struct sg_list *sg; + + if (unlikely(snd_comp->status)) + net_err_ratelimited("%s: TX%d: Error in send CQ status:%x\n", + pfvf->netdev->name, cq->cint_idx, + snd_comp->status); + + sg = &sq->sg[snd_comp->sqe_id]; + skb = (struct sk_buff *)sg->skb; + if (unlikely(!skb)) + return; + + *tx_bytes += skb->len; + (*tx_pkts)++; + otx2_dma_unmap_skb_frags(pfvf, sg); + napi_consume_skb(skb, budget); + sg->skb = (u64)NULL; +} + static void otx2_skb_add_frag(struct otx2_nic *pfvf, struct sk_buff *skb, u64 iova, int len) { @@ -182,7 +250,39 @@ static int otx2_rx_napi_handler(struct otx2_nic *pfvf, static int otx2_tx_napi_handler(struct otx2_nic *pfvf, struct otx2_cq_queue *cq, int budget) { - /* Nothing to do, for now */ + int tx_pkts = 0, tx_bytes = 0; + struct nix_cqe_tx_s *cqe; + int processed_cqe = 0; + + while (likely(processed_cqe < budget)) { + cqe = (struct nix_cqe_tx_s *)otx2_get_next_cqe(cq); + if (unlikely(!cqe)) { + if (!processed_cqe) + return 0; + break; + } + otx2_snd_pkt_handler(pfvf, cq, &pfvf->qset.sq[cq->cint_idx], + cqe, budget, &tx_pkts, &tx_bytes); + + cqe->hdr.cqe_type = NIX_XQE_TYPE_INVALID; + processed_cqe++; + } + + /* Free CQEs to HW */ + otx2_write64(pfvf, NIX_LF_CQ_OP_DOOR, + ((u64)cq->cq_idx << 32) | processed_cqe); + + if (likely(tx_pkts)) { + struct netdev_queue *txq; + + txq = netdev_get_tx_queue(pfvf->netdev, cq->cint_idx); + netdev_tx_completed_queue(txq, tx_pkts, tx_bytes); + /* Check if queue was stopped earlier due to ring full */ + smp_mb(); + if (netif_tx_queue_stopped(txq) && + netif_carrier_ok(pfvf->netdev)) + netif_tx_wake_queue(txq); + } return 0; } @@ -222,6 +322,169 @@ int otx2_napi_handler(struct napi_struct *napi, int budget) return workdone; } +static void otx2_sqe_flush(struct otx2_snd_queue *sq, int size) +{ + u64 status; + + /* Packet data stores should finish before SQE is flushed to HW */ + dma_wmb(); + + do { + memcpy(sq->lmt_addr, sq->sqe_base, size); + status = otx2_lmt_flush(sq->io_addr); + } while (status == 0); + + sq->head++; + sq->head &= (sq->sqe_cnt - 1); +} + +#define MAX_SEGS_PER_SG 3 +/* Add SQE scatter/gather subdescriptor structure */ +static bool otx2_sqe_add_sg(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, + struct sk_buff *skb, int num_segs, int *offset) +{ + struct nix_sqe_sg_s *sg = NULL; + u64 dma_addr, *iova = NULL; + u16 *sg_lens = NULL; + int seg, len; + + sq->sg[sq->head].num_segs = 0; + + for (seg = 0; seg < num_segs; seg++) { + if ((seg % MAX_SEGS_PER_SG) == 0) { + sg = (struct nix_sqe_sg_s *)(sq->sqe_base + *offset); + sg->ld_type = NIX_SEND_LDTYPE_LDD; + sg->subdc = NIX_SUBDC_SG; + sg->segs = 0; + sg_lens = (void *)sg; + iova = (void *)sg + sizeof(*sg); + /* Next subdc always starts at a 16byte boundary. + * So if sg->segs is whether 2 or 3, offset += 16bytes. + */ + if ((num_segs - seg) >= (MAX_SEGS_PER_SG - 1)) + *offset += sizeof(*sg) + (3 * sizeof(u64)); + else + *offset += sizeof(*sg) + sizeof(u64); + } + dma_addr = otx2_dma_map_skb_frag(pfvf, skb, seg, &len); + if (dma_mapping_error(pfvf->dev, dma_addr)) + return false; + + sg_lens[frag_num(seg % MAX_SEGS_PER_SG)] = len; + sg->segs++; + *iova++ = dma_addr; + + /* Save DMA mapping info for later unmapping */ + sq->sg[sq->head].dma_addr[seg] = dma_addr; + sq->sg[sq->head].size[seg] = len; + sq->sg[sq->head].num_segs++; + } + + sq->sg[sq->head].skb = (u64)skb; + return true; +} + +/* Add SQE header subdescriptor structure */ +static void otx2_sqe_add_hdr(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, + struct nix_sqe_hdr_s *sqe_hdr, + struct sk_buff *skb, u16 qidx) +{ + int proto = 0; + + /* Check if SQE was framed before, if yes then no need to + * set these constants again and again. + */ + if (!sqe_hdr->total) { + /* Don't free Tx buffers to Aura */ + sqe_hdr->df = 1; + sqe_hdr->aura = sq->aura_id; + /* Post a CQE Tx after pkt transmission */ + sqe_hdr->pnc = 1; + sqe_hdr->sq = qidx; + } + sqe_hdr->total = skb->len; + /* Set SQE identifier which will be used later for freeing SKB */ + sqe_hdr->sqe_id = sq->head; + + /* Offload TCP/UDP checksum to HW */ + if (skb->ip_summed == CHECKSUM_PARTIAL) { + sqe_hdr->ol3ptr = skb_network_offset(skb); + sqe_hdr->ol4ptr = skb_transport_offset(skb); + /* get vlan protocol Ethertype */ + if (eth_type_vlan(skb->protocol)) + skb->protocol = vlan_get_protocol(skb); + + if (skb->protocol == htons(ETH_P_IP)) { + proto = ip_hdr(skb)->protocol; + /* In case of TSO, HW needs this to be explicitly set. + * So set this always, instead of adding a check. + */ + sqe_hdr->ol3type = NIX_SENDL3TYPE_IP4_CKSUM; + } else if (skb->protocol == htons(ETH_P_IPV6)) { + proto = ipv6_hdr(skb)->nexthdr; + } + + if (proto == IPPROTO_TCP) + sqe_hdr->ol4type = NIX_SENDL4TYPE_TCP_CKSUM; + else if (proto == IPPROTO_UDP) + sqe_hdr->ol4type = NIX_SENDL4TYPE_UDP_CKSUM; + } +} + +bool otx2_sq_append_skb(struct net_device *netdev, struct otx2_snd_queue *sq, + struct sk_buff *skb, u16 qidx) +{ + struct netdev_queue *txq = netdev_get_tx_queue(netdev, qidx); + struct otx2_nic *pfvf = netdev_priv(netdev); + int offset, num_segs, free_sqe; + struct nix_sqe_hdr_s *sqe_hdr; + + /* Check if there is room for new SQE. + * 'Num of SQBs freed to SQ's pool - SQ's Aura count' + * will give free SQE count. + */ + free_sqe = (sq->num_sqbs - *sq->aura_fc_addr) * sq->sqe_per_sqb; + + if (!free_sqe || free_sqe < sq->sqe_thresh) + return false; + + num_segs = skb_shinfo(skb)->nr_frags + 1; + + /* If SKB doesn't fit in a single SQE, linearize it. + * TODO: Consider adding JUMP descriptor instead. + */ + if (unlikely(num_segs > OTX2_MAX_FRAGS_IN_SQE)) { + if (__skb_linearize(skb)) { + dev_kfree_skb_any(skb); + return true; + } + num_segs = skb_shinfo(skb)->nr_frags + 1; + } + + /* Set SQE's SEND_HDR. + * Do not clear the first 64bit as it contains constant info. + */ + memset(sq->sqe_base + 8, 0, sq->sqe_size - 8); + sqe_hdr = (struct nix_sqe_hdr_s *)(sq->sqe_base); + otx2_sqe_add_hdr(pfvf, sq, sqe_hdr, skb, qidx); + offset = sizeof(*sqe_hdr); + + /* Add SG subdesc with data frags */ + if (!otx2_sqe_add_sg(pfvf, sq, skb, num_segs, &offset)) { + otx2_dma_unmap_skb_frags(pfvf, &sq->sg[sq->head]); + return false; + } + + sqe_hdr->sizem1 = (offset / 16) - 1; + + netdev_tx_sent_queue(txq, skb->len); + + /* Flush SQE to HW */ + otx2_sqe_flush(sq, offset); + + return true; +} + void otx2_cleanup_rx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq) { struct nix_cqe_rx_s *cqe; @@ -242,3 +505,29 @@ void otx2_cleanup_rx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq) otx2_write64(pfvf, NIX_LF_CQ_OP_DOOR, ((u64)cq->cq_idx << 32) | processed_cqe); } + +void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq) +{ + struct sk_buff *skb = NULL; + struct otx2_snd_queue *sq; + struct nix_cqe_tx_s *cqe; + int processed_cqe = 0; + struct sg_list *sg; + + sq = &pfvf->qset.sq[cq->cint_idx]; + + while ((cqe = (struct nix_cqe_tx_s *)otx2_get_next_cqe(cq))) { + sg = &sq->sg[cqe->comp.sqe_id]; + skb = (struct sk_buff *)sg->skb; + if (skb) { + otx2_dma_unmap_skb_frags(pfvf, sg); + dev_kfree_skb_any(skb); + sg->skb = (u64)NULL; + } + processed_cqe++; + } + + /* Free CQEs to HW */ + otx2_write64(pfvf, NIX_LF_CQ_OP_DOOR, + ((u64)cq->cq_idx << 32) | processed_cqe); +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h index 0944c176efa3..d9683c3bbc91 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -22,6 +22,8 @@ #define OTX2_DATA_ALIGN(X) ALIGN(X, OTX2_ALIGN) #define OTX2_HEAD_ROOM OTX2_ALIGN +#define OTX2_MAX_FRAGS_IN_SQE 9 + /* Rx buffer size should be in multiples of 128bytes */ #define RCV_FRAG_LEN1(x) \ ((OTX2_HEAD_ROOM + OTX2_DATA_ALIGN(x)) + \ @@ -53,17 +55,27 @@ */ #define CQ_QCOUNT_DEFAULT 1 +struct sg_list { + u16 num_segs; + u64 skb; + u64 size[OTX2_MAX_FRAGS_IN_SQE]; + u64 dma_addr[OTX2_MAX_FRAGS_IN_SQE]; +}; + struct otx2_snd_queue { u8 aura_id; + u16 head; u16 sqe_size; u32 sqe_cnt; u16 num_sqbs; + u16 sqe_thresh; u8 sqe_per_sqb; u64 io_addr; u64 *aura_fc_addr; u64 *lmt_addr; void *sqe_base; struct qmem *sqe; + struct sg_list *sg; u16 sqb_count; u64 *sqb_ptrs; } ____cacheline_aligned_in_smp; @@ -127,4 +139,6 @@ static inline u64 otx2_iova_to_phys(void *iommu_domain, dma_addr_t dma_addr) } int otx2_napi_handler(struct napi_struct *napi, int budget); +bool otx2_sq_append_skb(struct net_device *netdev, struct otx2_snd_queue *sq, + struct sk_buff *skb, u16 qidx); #endif /* OTX2_TXRX_H */ -- cgit v1.2.1 From 50fe6c02e5ad50727fb1069bb31099385fb59883 Mon Sep 17 00:00:00 2001 From: Linu Cherian Date: Mon, 27 Jan 2020 18:35:22 +0530 Subject: octeontx2-pf: Register and handle link notifications PF and AF (admin function) shares 64KB of reserved memory region for communication. This region is shared for - Messages sent by PF and responses sent by AF. - Notifications sent by AF and ACKs sent by PF. This patch adds infrastructure to handle notifications sent by AF and adds handlers to process them. One of the main usecase of notifications from AF is physical link changes. So this patch adds registration of PF with AF to receive link status change notifications and also adds the handler for that notification. Signed-off-by: Linu Cherian Signed-off-by: Tomasz Duszynski Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- .../ethernet/marvell/octeontx2/nic/otx2_common.h | 5 ++ .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 79 ++++++++++++++++++++++ .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.c | 25 +++++++ 3 files changed, 109 insertions(+) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 11948540dd4a..16d7dd70a2af 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -135,6 +135,9 @@ struct otx2_nic { void *iommu_domain; u16 rbsize; /* Receive buffer size */ +#define OTX2_FLAG_INTF_DOWN BIT_ULL(2) + u64 flags; + struct otx2_qset qset; struct otx2_hw hw; struct pci_dev *pdev; @@ -145,6 +148,7 @@ struct otx2_nic { struct workqueue_struct *mbox_wq; u16 pcifunc; /* RVU PF_FUNC */ + struct cgx_link_user_info linfo; /* Block address of NIX either BLKADDR_NIX0 or BLKADDR_NIX1 */ int nix_blkaddr; @@ -485,6 +489,7 @@ int otx2_txschq_stop(struct otx2_nic *pfvf); void otx2_sqb_flush(struct otx2_nic *pfvf); dma_addr_t otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, gfp_t gfp); +int otx2_rxtx_enable(struct otx2_nic *pfvf, bool enable); void otx2_ctx_disable(struct mbox *mbox, int type, bool npa); void otx2_cleanup_rx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index a18bf5facaaf..8cb1cf56d388 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -167,6 +167,38 @@ static void otx2_pfaf_mbox_handler(struct work_struct *work) otx2_mbox_reset(mbox, 0); } +static void otx2_handle_link_event(struct otx2_nic *pf) +{ + struct cgx_link_user_info *linfo = &pf->linfo; + struct net_device *netdev = pf->netdev; + + pr_info("%s NIC Link is %s %d Mbps %s duplex\n", netdev->name, + linfo->link_up ? "UP" : "DOWN", linfo->speed, + linfo->full_duplex ? "Full" : "Half"); + if (linfo->link_up) { + netif_carrier_on(netdev); + netif_tx_start_all_queues(netdev); + } else { + netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); + } +} + +int otx2_mbox_up_handler_cgx_link_event(struct otx2_nic *pf, + struct cgx_link_info_msg *msg, + struct msg_rsp *rsp) +{ + /* Copy the link info sent by AF */ + pf->linfo = msg->link_info; + + /* interface has not been fully configured yet */ + if (pf->flags & OTX2_FLAG_INTF_DOWN) + return 0; + + otx2_handle_link_event(pf); + return 0; +} + static int otx2_process_mbox_msg_up(struct otx2_nic *pf, struct mbox_msghdr *req) { @@ -367,6 +399,27 @@ exit: return err; } +static int otx2_cgx_config_linkevents(struct otx2_nic *pf, bool enable) +{ + struct msg_req *msg; + int err; + + otx2_mbox_lock(&pf->mbox); + if (enable) + msg = otx2_mbox_alloc_msg_cgx_start_linkevents(&pf->mbox); + else + msg = otx2_mbox_alloc_msg_cgx_stop_linkevents(&pf->mbox); + + if (!msg) { + otx2_mbox_unlock(&pf->mbox); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&pf->mbox); + otx2_mbox_unlock(&pf->mbox); + return err; +} + static int otx2_set_real_num_queues(struct net_device *netdev, int tx_queues, int rx_queues) { @@ -690,6 +743,18 @@ static int otx2_open(struct net_device *netdev) otx2_set_cints_affinity(pf); + pf->flags &= ~OTX2_FLAG_INTF_DOWN; + /* 'intf_down' may be checked on any cpu */ + smp_wmb(); + + /* we have already received link status notification */ + if (pf->linfo.link_up && !(pf->pcifunc & RVU_PFVF_FUNC_MASK)) + otx2_handle_link_event(pf); + + err = otx2_rxtx_enable(pf, true); + if (err) + goto err_free_cints; + return 0; err_free_cints: @@ -713,6 +778,13 @@ static int otx2_stop(struct net_device *netdev) netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); + pf->flags |= OTX2_FLAG_INTF_DOWN; + /* 'intf_down' may be checked on any cpu */ + smp_wmb(); + + /* First stop packet Rx/Tx */ + otx2_rxtx_enable(pf, false); + /* Cleanup CQ NAPI and IRQ */ vec = pf->hw.nix_msixoff + NIX_LF_CINT_VEC_START; for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { @@ -867,6 +939,7 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) pf->netdev = netdev; pf->pdev = pdev; pf->dev = dev; + pf->flags |= OTX2_FLAG_INTF_DOWN; hw = &pf->hw; hw->pdev = pdev; @@ -959,6 +1032,9 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_detach_rsrc; } + /* Enable link notifications */ + otx2_cgx_config_linkevents(pf, true); + return 0; err_detach_rsrc: @@ -987,6 +1063,9 @@ static void otx2_remove(struct pci_dev *pdev) pf = netdev_priv(netdev); + /* Disable link notifications */ + otx2_cgx_config_linkevents(pf, false); + unregister_netdev(netdev); otx2_detach_resources(&pf->mbox); otx2_disable_mbox_intr(pf); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index f19f5d044110..87b579a121ab 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -315,6 +315,10 @@ int otx2_napi_handler(struct napi_struct *napi, int budget) otx2_write64(pfvf, NIX_LF_CINTX_INT(cq_poll->cint_idx), BIT_ULL(0)); if (workdone < budget && napi_complete_done(napi, workdone)) { + /* If interface is going down, don't re-enable IRQ */ + if (pfvf->flags & OTX2_FLAG_INTF_DOWN) + return workdone; + /* Re-enable interrupts */ otx2_write64(pfvf, NIX_LF_CINTX_ENA_W1S(cq_poll->cint_idx), BIT_ULL(0)); @@ -531,3 +535,24 @@ void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq) otx2_write64(pfvf, NIX_LF_CQ_OP_DOOR, ((u64)cq->cq_idx << 32) | processed_cqe); } + +int otx2_rxtx_enable(struct otx2_nic *pfvf, bool enable) +{ + struct msg_req *msg; + int err; + + otx2_mbox_lock(&pfvf->mbox); + if (enable) + msg = otx2_mbox_alloc_msg_nix_lf_start_rx(&pfvf->mbox); + else + msg = otx2_mbox_alloc_msg_nix_lf_stop_rx(&pfvf->mbox); + + if (!msg) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + return err; +} -- cgit v1.2.1 From 34bfe0ebedb76aeb964341d96cf7c5135d14e1bf Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Mon, 27 Jan 2020 18:35:23 +0530 Subject: octeontx2-pf: MTU, MAC and RX mode config support This patch addes support to change interface MTU, MAC address retrieval and config, RX mode ie unicast, multicast and promiscuous. Also added link loopback support Signed-off-by: Tomasz Duszynski Signed-off-by: Subbaraya Sundeep Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/octeontx2/af/mbox.h | 8 +- .../net/ethernet/marvell/octeontx2/af/rvu_nix.c | 17 ++++ .../ethernet/marvell/octeontx2/nic/otx2_common.c | 108 +++++++++++++++++++++ .../ethernet/marvell/octeontx2/nic/otx2_common.h | 8 +- .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 103 +++++++++++++++++++- .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.h | 4 + 6 files changed, 242 insertions(+), 6 deletions(-) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h index a589748f1240..8bbc1f1d81f5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -210,7 +210,8 @@ M(NIX_SET_RX_CFG, 0x8010, nix_set_rx_cfg, nix_rx_cfg, msg_rsp) \ M(NIX_LSO_FORMAT_CFG, 0x8011, nix_lso_format_cfg, \ nix_lso_format_cfg, \ nix_lso_format_cfg_rsp) \ -M(NIX_RXVLAN_ALLOC, 0x8012, nix_rxvlan_alloc, msg_req, msg_rsp) +M(NIX_RXVLAN_ALLOC, 0x8012, nix_rxvlan_alloc, msg_req, msg_rsp) \ +M(NIX_GET_MAC_ADDR, 0x8018, nix_get_mac_addr, msg_req, nix_get_mac_addr_rsp) \ /* Messages initiated by AF (range 0xC00 - 0xDFF) */ #define MBOX_UP_CGX_MESSAGES \ @@ -618,6 +619,11 @@ struct nix_set_mac_addr { u8 mac_addr[ETH_ALEN]; /* MAC address to be set for this pcifunc */ }; +struct nix_get_mac_addr_rsp { + struct mbox_msghdr hdr; + u8 mac_addr[ETH_ALEN]; +}; + struct nix_mark_format_cfg { struct mbox_msghdr hdr; u8 offset; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 8a59f7d53fbf..eb5e542424e7 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -2546,6 +2546,23 @@ int rvu_mbox_handler_nix_set_mac_addr(struct rvu *rvu, return 0; } +int rvu_mbox_handler_nix_get_mac_addr(struct rvu *rvu, + struct msg_req *req, + struct nix_get_mac_addr_rsp *rsp) +{ + u16 pcifunc = req->hdr.pcifunc; + struct rvu_pfvf *pfvf; + + if (!is_nixlf_attached(rvu, pcifunc)) + return NIX_AF_ERR_AF_LF_INVALID; + + pfvf = rvu_get_pfvf(rvu, pcifunc); + + ether_addr_copy(rsp->mac_addr, pfvf->mac_addr); + + return 0; +} + int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, struct msg_rsp *rsp) { diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index e9bd2d6f6ebc..836b3c860e62 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -15,6 +15,97 @@ #include "otx2_common.h" #include "otx2_struct.h" +/* Sync MAC address with RVU AF */ +static int otx2_hw_set_mac_addr(struct otx2_nic *pfvf, u8 *mac) +{ + struct nix_set_mac_addr *req; + int err; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_nix_set_mac_addr(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + ether_addr_copy(req->mac_addr, mac); + + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + return err; +} + +static int otx2_hw_get_mac_addr(struct otx2_nic *pfvf, + struct net_device *netdev) +{ + struct nix_get_mac_addr_rsp *rsp; + struct mbox_msghdr *msghdr; + struct msg_req *req; + int err; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_nix_get_mac_addr(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + otx2_mbox_unlock(&pfvf->mbox); + return err; + } + + msghdr = otx2_mbox_get_rsp(&pfvf->mbox.mbox, 0, &req->hdr); + if (!msghdr) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + rsp = (struct nix_get_mac_addr_rsp *)msghdr; + ether_addr_copy(netdev->dev_addr, rsp->mac_addr); + otx2_mbox_unlock(&pfvf->mbox); + + return 0; +} + +int otx2_set_mac_address(struct net_device *netdev, void *p) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + if (!otx2_hw_set_mac_addr(pfvf, addr->sa_data)) + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + else + return -EPERM; + + return 0; +} + +int otx2_hw_set_mtu(struct otx2_nic *pfvf, int mtu) +{ + struct nix_frs_cfg *req; + int err; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_nix_set_hw_frs(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + + /* SMQ config limits maximum pkt size that can be transmitted */ + req->update_smq = true; + pfvf->max_frs = mtu + OTX2_ETH_HLEN; + req->maxlen = pfvf->max_frs; + + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + return err; +} + void otx2_config_irq_coalescing(struct otx2_nic *pfvf, int qidx) { /* Configure CQE interrupt coalescing parameters @@ -63,6 +154,20 @@ ret: return iova; } +void otx2_get_mac_from_af(struct net_device *netdev) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + int err; + + err = otx2_hw_get_mac_addr(pfvf, netdev); + if (err) + dev_warn(pfvf->dev, "Failed to read mac from hardware\n"); + + /* If AF doesn't provide a valid MAC, generate a random one */ + if (!is_valid_ether_addr(netdev->dev_addr)) + eth_hw_addr_random(netdev); +} + static int otx2_get_link(struct otx2_nic *pfvf) { int link = 0; @@ -97,6 +202,9 @@ int otx2_txschq_config(struct otx2_nic *pfvf, int lvl) /* Set topology e.t.c configuration */ if (lvl == NIX_TXSCH_LVL_SMQ) { req->reg[0] = NIX_AF_SMQX_CFG(schq); + req->regval[0] = ((pfvf->netdev->mtu + OTX2_ETH_HLEN) << 8) | + OTX2_MIN_MTU; + req->regval[0] |= (0x20ULL << 51) | (0x80ULL << 39) | (0x2ULL << 36); req->num_regs++; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 16d7dd70a2af..8c29b63b319e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -133,6 +133,7 @@ struct otx2_nic { void __iomem *reg_base; struct net_device *netdev; void *iommu_domain; + u16 max_frs; u16 rbsize; /* Receive buffer size */ #define OTX2_FLAG_INTF_DOWN BIT_ULL(2) @@ -469,7 +470,9 @@ static inline void otx2_dma_unmap_page(struct otx2_nic *pfvf, /* MSI-X APIs */ void otx2_free_cints(struct otx2_nic *pfvf, int n); void otx2_set_cints_affinity(struct otx2_nic *pfvf); - +int otx2_set_mac_address(struct net_device *netdev, void *p); +int otx2_hw_set_mtu(struct otx2_nic *pfvf, int mtu); +void otx2_get_mac_from_af(struct net_device *netdev); void otx2_config_irq_coalescing(struct otx2_nic *pfvf, int qidx); /* RVU block related APIs */ @@ -503,4 +506,7 @@ void mbox_handler_nix_lf_alloc(struct otx2_nic *pfvf, struct nix_lf_alloc_rsp *rsp); void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, struct nix_txsch_alloc_rsp *rsp); + +int otx2_open(struct net_device *netdev); +int otx2_stop(struct net_device *netdev); #endif /* OTX2_COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 8cb1cf56d388..60a67b90f2b0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -43,6 +43,24 @@ enum { TYPE_PFVF, }; +static int otx2_change_mtu(struct net_device *netdev, int new_mtu) +{ + bool if_up = netif_running(netdev); + int err = 0; + + if (if_up) + otx2_stop(netdev); + + netdev_info(netdev, "Changing MTU from %d to %d\n", + netdev->mtu, new_mtu); + netdev->mtu = new_mtu; + + if (if_up) + err = otx2_open(netdev); + + return err; +} + static void otx2_queue_work(struct mbox *mw, struct workqueue_struct *mbox_wq, int first, int mdevs, u64 intr, int type) { @@ -420,6 +438,27 @@ static int otx2_cgx_config_linkevents(struct otx2_nic *pf, bool enable) return err; } +static int otx2_cgx_config_loopback(struct otx2_nic *pf, bool enable) +{ + struct msg_req *msg; + int err; + + otx2_mbox_lock(&pf->mbox); + if (enable) + msg = otx2_mbox_alloc_msg_cgx_intlbk_enable(&pf->mbox); + else + msg = otx2_mbox_alloc_msg_cgx_intlbk_disable(&pf->mbox); + + if (!msg) { + otx2_mbox_unlock(&pf->mbox); + return -ENOMEM; + } + + err = otx2_sync_mbox_msg(&pf->mbox); + otx2_mbox_unlock(&pf->mbox); + return err; +} + static int otx2_set_real_num_queues(struct net_device *netdev, int tx_queues, int rx_queues) { @@ -519,7 +558,7 @@ static int otx2_init_hw_resources(struct otx2_nic *pf) hw->pool_cnt = hw->rqpool_cnt + hw->sqpool_cnt; /* Get the size of receive buffers to allocate */ - pf->rbsize = RCV_FRAG_LEN(pf->netdev->mtu); + pf->rbsize = RCV_FRAG_LEN(pf->netdev->mtu + OTX2_ETH_HLEN); otx2_mbox_lock(mbox); /* NPA init */ @@ -658,7 +697,7 @@ static void otx2_free_hw_resources(struct otx2_nic *pf) otx2_mbox_unlock(mbox); } -static int otx2_open(struct net_device *netdev) +int otx2_open(struct net_device *netdev) { struct otx2_nic *pf = netdev_priv(netdev); struct otx2_cq_poll *cq_poll = NULL; @@ -715,6 +754,11 @@ static int otx2_open(struct net_device *netdev) napi_enable(&cq_poll->napi); } + /* Set maximum frame size allowed in HW */ + err = otx2_hw_set_mtu(pf, netdev->mtu); + if (err) + goto err_disable_napi; + /* Register CQ IRQ handlers */ vec = pf->hw.nix_msixoff + NIX_LF_CINT_VEC_START; for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { @@ -759,6 +803,7 @@ static int otx2_open(struct net_device *netdev) err_free_cints: otx2_free_cints(pf, qidx); +err_disable_napi: otx2_disable_napi(pf); otx2_free_hw_resources(pf); err_free_mem: @@ -768,7 +813,7 @@ err_free_mem: return err; } -static int otx2_stop(struct net_device *netdev) +int otx2_stop(struct net_device *netdev) { struct otx2_nic *pf = netdev_priv(netdev); struct otx2_cq_poll *cq_poll = NULL; @@ -847,10 +892,53 @@ static netdev_tx_t otx2_xmit(struct sk_buff *skb, struct net_device *netdev) return NETDEV_TX_OK; } +static void otx2_set_rx_mode(struct net_device *netdev) +{ + struct otx2_nic *pf = netdev_priv(netdev); + struct nix_rx_mode *req; + + if (!(netdev->flags & IFF_UP)) + return; + + otx2_mbox_lock(&pf->mbox); + req = otx2_mbox_alloc_msg_nix_set_rx_mode(&pf->mbox); + if (!req) { + otx2_mbox_unlock(&pf->mbox); + return; + } + + req->mode = NIX_RX_MODE_UCAST; + + /* We don't support MAC address filtering yet */ + if (netdev->flags & IFF_PROMISC) + req->mode |= NIX_RX_MODE_PROMISC; + else if (netdev->flags & (IFF_ALLMULTI | IFF_MULTICAST)) + req->mode |= NIX_RX_MODE_ALLMULTI; + + otx2_sync_mbox_msg(&pf->mbox); + otx2_mbox_unlock(&pf->mbox); +} + +static int otx2_set_features(struct net_device *netdev, + netdev_features_t features) +{ + netdev_features_t changed = features ^ netdev->features; + struct otx2_nic *pf = netdev_priv(netdev); + + if ((changed & NETIF_F_LOOPBACK) && netif_running(netdev)) + return otx2_cgx_config_loopback(pf, + features & NETIF_F_LOOPBACK); + return 0; +} + static const struct net_device_ops otx2_netdev_ops = { .ndo_open = otx2_open, .ndo_stop = otx2_stop, .ndo_start_xmit = otx2_xmit, + .ndo_set_mac_address = otx2_set_mac_address, + .ndo_change_mtu = otx2_change_mtu, + .ndo_set_rx_mode = otx2_set_rx_mode, + .ndo_set_features = otx2_set_features, }; static int otx2_check_pf_usable(struct otx2_nic *nic) @@ -1005,6 +1093,9 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) otx2_setup_dev_hw_settings(pf); + /* Assign default mac address */ + otx2_get_mac_from_af(netdev); + /* NPA's pool is a stack to which SW frees buffer pointers via Aura. * HW allocates buffer pointer from stack and uses it for DMA'ing * ingress packet. In some scenarios HW can free back allocated buffer @@ -1022,10 +1113,14 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) NETIF_F_IPV6_CSUM | NETIF_F_SG); netdev->features |= netdev->hw_features; - netdev->hw_features |= NETIF_F_RXALL; + netdev->hw_features |= NETIF_F_LOOPBACK | NETIF_F_RXALL; netdev->netdev_ops = &otx2_netdev_ops; + /* MTU range: 64 - 9190 */ + netdev->min_mtu = OTX2_MIN_MTU; + netdev->max_mtu = OTX2_MAX_MTU; + err = register_netdev(netdev); if (err) { dev_err(dev, "Failed to register netdevice\n"); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h index d9683c3bbc91..bad225979aec 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -22,6 +22,10 @@ #define OTX2_DATA_ALIGN(X) ALIGN(X, OTX2_ALIGN) #define OTX2_HEAD_ROOM OTX2_ALIGN +#define OTX2_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN) +#define OTX2_MIN_MTU 64 +#define OTX2_MAX_MTU (9212 - OTX2_ETH_HLEN) + #define OTX2_MAX_FRAGS_IN_SQE 9 /* Rx buffer size should be in multiples of 128bytes */ -- cgit v1.2.1 From 4ff7d1488a8496fddcfdf40c42489eaa64892f3e Mon Sep 17 00:00:00 2001 From: Geetha sowjanya Date: Mon, 27 Jan 2020 18:35:24 +0530 Subject: octeontx2-pf: Error handling support HW reports many errors on the receive and transmit paths. Such as incorrect queue configuration, pkt transmission errors, LMTST instruction errors, transmit queue full etc. These are reported via QINT interrupt. Most of the errors are fatal and needs reinitialization. Also added support to allocate receive buffers in non-atomic context when allocation fails in NAPI context. Signed-off-by: Geetha sowjanya Signed-off-by: Aleksey Makarov Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 63 ++++++++++ .../ethernet/marvell/octeontx2/nic/otx2_common.h | 13 ++ .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 133 ++++++++++++++++++++- .../ethernet/marvell/octeontx2/nic/otx2_struct.h | 28 +++++ .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.c | 20 +++- .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.h | 1 + 6 files changed, 255 insertions(+), 3 deletions(-) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 836b3c860e62..2b503066d801 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -154,6 +154,13 @@ ret: return iova; } +void otx2_tx_timeout(struct net_device *netdev, unsigned int txq) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + schedule_work(&pfvf->reset_task); +} + void otx2_get_mac_from_af(struct net_device *netdev) { struct otx2_nic *pfvf = netdev_priv(netdev); @@ -362,6 +369,7 @@ static int otx2_rq_init(struct otx2_nic *pfvf, u16 qidx, u16 lpb_aura) aq->rq.lpb_sizem1 = (DMA_BUFFER_LEN(pfvf->rbsize) / 8) - 1; aq->rq.xqe_imm_size = 0; /* Copying of packet to CQE not needed */ aq->rq.flow_tagw = 32; /* Copy full 32bit flow_tag to CQE header */ + aq->rq.qint_idx = 0; aq->rq.lpb_drop_ena = 1; /* Enable RED dropping for AURA */ aq->rq.xqe_drop_ena = 1; /* Enable RED dropping for CQ/SSO */ aq->rq.xqe_pass = RQ_PASS_LVL_CQ(pfvf->hw.rq_skid, qset->rqe_cnt); @@ -424,6 +432,8 @@ static int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura) aq->sq.default_chan = pfvf->hw.tx_chan_base; aq->sq.sqe_stype = NIX_STYPE_STF; /* Cache SQB */ aq->sq.sqb_aura = sqb_aura; + aq->sq.sq_int_ena = NIX_SQINT_BITS; + aq->sq.qint_idx = 0; /* Due pipelining impact minimum 2000 unused SQ CQE's * need to maintain to avoid CQ overflow. */ @@ -470,6 +480,7 @@ static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) pool_id = ((cq->cq_type == CQ_RX) && (pfvf->hw.rqpool_cnt != pfvf->hw.rx_queues)) ? 0 : qidx; cq->rbpool = &qset->pool[pool_id]; + cq->refill_task_sched = false; /* Get memory to put this msg */ aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox); @@ -481,6 +492,8 @@ static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) aq->cq.caching = 1; aq->cq.base = cq->cqe->iova; aq->cq.cint_idx = cq->cint_idx; + aq->cq.cq_err_int_ena = NIX_CQERRINT_BITS; + aq->cq.qint_idx = 0; aq->cq.avg_level = 255; if (qidx < pfvf->hw.rx_queues) { @@ -496,6 +509,45 @@ static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) return otx2_sync_mbox_msg(&pfvf->mbox); } +static void otx2_pool_refill_task(struct work_struct *work) +{ + struct otx2_cq_queue *cq; + struct otx2_pool *rbpool; + struct refill_work *wrk; + int qidx, free_ptrs = 0; + struct otx2_nic *pfvf; + s64 bufptr; + + wrk = container_of(work, struct refill_work, pool_refill_work.work); + pfvf = wrk->pf; + qidx = wrk - pfvf->refill_wrk; + cq = &pfvf->qset.cq[qidx]; + rbpool = cq->rbpool; + free_ptrs = cq->pool_ptrs; + + while (cq->pool_ptrs) { + bufptr = otx2_alloc_rbuf(pfvf, rbpool, GFP_KERNEL); + if (bufptr <= 0) { + /* Schedule a WQ if we fails to free atleast half of the + * pointers else enable napi for this RQ. + */ + if (!((free_ptrs - cq->pool_ptrs) > free_ptrs / 2)) { + struct delayed_work *dwork; + + dwork = &wrk->pool_refill_work; + schedule_delayed_work(dwork, + msecs_to_jiffies(100)); + } else { + cq->refill_task_sched = false; + } + return; + } + otx2_aura_freeptr(pfvf, qidx, bufptr + OTX2_HEAD_ROOM); + cq->pool_ptrs--; + } + cq->refill_task_sched = false; +} + int otx2_config_nix_queues(struct otx2_nic *pfvf) { int qidx, err; @@ -525,6 +577,17 @@ int otx2_config_nix_queues(struct otx2_nic *pfvf) return err; } + /* Initialize work queue for receive buffer refill */ + pfvf->refill_wrk = devm_kcalloc(pfvf->dev, pfvf->qset.cq_cnt, + sizeof(struct refill_work), GFP_KERNEL); + if (!pfvf->refill_wrk) + return -ENOMEM; + + for (qidx = 0; qidx < pfvf->qset.cq_cnt; qidx++) { + pfvf->refill_wrk[qidx].pf = pfvf; + INIT_DELAYED_WORK(&pfvf->refill_wrk[qidx].pool_refill_work, + otx2_pool_refill_task); + } return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 8c29b63b319e..2a812110995f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -129,6 +129,11 @@ struct otx2_hw { struct otx2_drv_stats drv_stats; }; +struct refill_work { + struct delayed_work pool_refill_work; + struct otx2_nic *pf; +}; + struct otx2_nic { void __iomem *reg_base; struct net_device *netdev; @@ -151,6 +156,10 @@ struct otx2_nic { u16 pcifunc; /* RVU PF_FUNC */ struct cgx_link_user_info linfo; + u64 reset_count; + struct work_struct reset_task; + struct refill_work *refill_wrk; + /* Block address of NIX either BLKADDR_NIX0 or BLKADDR_NIX1 */ int nix_blkaddr; }; @@ -435,6 +444,9 @@ otx2_mbox_up_handler_ ## _fn_name(struct otx2_nic *pfvf, \ MBOX_UP_CGX_MESSAGES #undef M +/* Time to wait before watchdog kicks off */ +#define OTX2_TX_TIMEOUT (100 * HZ) + #define RVU_PFVF_PF_SHIFT 10 #define RVU_PFVF_PF_MASK 0x3F #define RVU_PFVF_FUNC_SHIFT 0 @@ -472,6 +484,7 @@ void otx2_free_cints(struct otx2_nic *pfvf, int n); void otx2_set_cints_affinity(struct otx2_nic *pfvf); int otx2_set_mac_address(struct net_device *netdev, void *p); int otx2_hw_set_mtu(struct otx2_nic *pfvf, int mtu); +void otx2_tx_timeout(struct net_device *netdev, unsigned int txq); void otx2_get_mac_from_af(struct net_device *netdev); void otx2_config_irq_coalescing(struct otx2_nic *pfvf, int qidx); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 60a67b90f2b0..171bab0ed662 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -478,6 +478,85 @@ static int otx2_set_real_num_queues(struct net_device *netdev, return err; } +static irqreturn_t otx2_q_intr_handler(int irq, void *data) +{ + struct otx2_nic *pf = data; + u64 val, *ptr; + u64 qidx = 0; + + /* CQ */ + for (qidx = 0; qidx < pf->qset.cq_cnt; qidx++) { + ptr = otx2_get_regaddr(pf, NIX_LF_CQ_OP_INT); + val = otx2_atomic64_add((qidx << 44), ptr); + + otx2_write64(pf, NIX_LF_CQ_OP_INT, (qidx << 44) | + (val & NIX_CQERRINT_BITS)); + if (!(val & (NIX_CQERRINT_BITS | BIT_ULL(42)))) + continue; + + if (val & BIT_ULL(42)) { + netdev_err(pf->netdev, "CQ%lld: error reading NIX_LF_CQ_OP_INT, NIX_LF_ERR_INT 0x%llx\n", + qidx, otx2_read64(pf, NIX_LF_ERR_INT)); + } else { + if (val & BIT_ULL(NIX_CQERRINT_DOOR_ERR)) + netdev_err(pf->netdev, "CQ%lld: Doorbell error", + qidx); + if (val & BIT_ULL(NIX_CQERRINT_CQE_FAULT)) + netdev_err(pf->netdev, "CQ%lld: Memory fault on CQE write to LLC/DRAM", + qidx); + } + + schedule_work(&pf->reset_task); + } + + /* SQ */ + for (qidx = 0; qidx < pf->hw.tx_queues; qidx++) { + ptr = otx2_get_regaddr(pf, NIX_LF_SQ_OP_INT); + val = otx2_atomic64_add((qidx << 44), ptr); + otx2_write64(pf, NIX_LF_SQ_OP_INT, (qidx << 44) | + (val & NIX_SQINT_BITS)); + + if (!(val & (NIX_SQINT_BITS | BIT_ULL(42)))) + continue; + + if (val & BIT_ULL(42)) { + netdev_err(pf->netdev, "SQ%lld: error reading NIX_LF_SQ_OP_INT, NIX_LF_ERR_INT 0x%llx\n", + qidx, otx2_read64(pf, NIX_LF_ERR_INT)); + } else { + if (val & BIT_ULL(NIX_SQINT_LMT_ERR)) { + netdev_err(pf->netdev, "SQ%lld: LMT store error NIX_LF_SQ_OP_ERR_DBG:0x%llx", + qidx, + otx2_read64(pf, + NIX_LF_SQ_OP_ERR_DBG)); + otx2_write64(pf, NIX_LF_SQ_OP_ERR_DBG, + BIT_ULL(44)); + } + if (val & BIT_ULL(NIX_SQINT_MNQ_ERR)) { + netdev_err(pf->netdev, "SQ%lld: Meta-descriptor enqueue error NIX_LF_MNQ_ERR_DGB:0x%llx\n", + qidx, + otx2_read64(pf, NIX_LF_MNQ_ERR_DBG)); + otx2_write64(pf, NIX_LF_MNQ_ERR_DBG, + BIT_ULL(44)); + } + if (val & BIT_ULL(NIX_SQINT_SEND_ERR)) { + netdev_err(pf->netdev, "SQ%lld: Send error, NIX_LF_SEND_ERR_DBG 0x%llx", + qidx, + otx2_read64(pf, + NIX_LF_SEND_ERR_DBG)); + otx2_write64(pf, NIX_LF_SEND_ERR_DBG, + BIT_ULL(44)); + } + if (val & BIT_ULL(NIX_SQINT_SQB_ALLOC_FAIL)) + netdev_err(pf->netdev, "SQ%lld: SQB allocation failed", + qidx); + } + + schedule_work(&pf->reset_task); + } + + return IRQ_HANDLED; +} + static irqreturn_t otx2_cq_intr_handler(int irq, void *cq_irq) { struct otx2_cq_poll *cq_poll = (struct otx2_cq_poll *)cq_irq; @@ -759,6 +838,24 @@ int otx2_open(struct net_device *netdev) if (err) goto err_disable_napi; + /* Register Queue IRQ handlers */ + vec = pf->hw.nix_msixoff + NIX_LF_QINT_VEC_START; + irq_name = &pf->hw.irq_name[vec * NAME_SIZE]; + + snprintf(irq_name, NAME_SIZE, "%s-qerr", pf->netdev->name); + + err = request_irq(pci_irq_vector(pf->pdev, vec), + otx2_q_intr_handler, 0, irq_name, pf); + if (err) { + dev_err(pf->dev, + "RVUPF%d: IRQ registration failed for QERR\n", + rvu_get_pf(pf->pcifunc)); + goto err_disable_napi; + } + + /* Enable QINT IRQ */ + otx2_write64(pf, NIX_LF_QINTX_ENA_W1S(0), BIT_ULL(0)); + /* Register CQ IRQ handlers */ vec = pf->hw.nix_msixoff + NIX_LF_CINT_VEC_START; for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { @@ -803,6 +900,11 @@ int otx2_open(struct net_device *netdev) err_free_cints: otx2_free_cints(pf, qidx); + vec = pci_irq_vector(pf->pdev, + pf->hw.nix_msixoff + NIX_LF_QINT_VEC_START); + otx2_write64(pf, NIX_LF_QINTX_ENA_W1C(0), BIT_ULL(0)); + synchronize_irq(vec); + free_irq(vec, pf); err_disable_napi: otx2_disable_napi(pf); otx2_free_hw_resources(pf); @@ -818,7 +920,7 @@ int otx2_stop(struct net_device *netdev) struct otx2_nic *pf = netdev_priv(netdev); struct otx2_cq_poll *cq_poll = NULL; struct otx2_qset *qset = &pf->qset; - int qidx, vec; + int qidx, vec, wrk; netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); @@ -830,6 +932,13 @@ int otx2_stop(struct net_device *netdev) /* First stop packet Rx/Tx */ otx2_rxtx_enable(pf, false); + /* Cleanup Queue IRQ */ + vec = pci_irq_vector(pf->pdev, + pf->hw.nix_msixoff + NIX_LF_QINT_VEC_START); + otx2_write64(pf, NIX_LF_QINTX_ENA_W1C(0), BIT_ULL(0)); + synchronize_irq(vec); + free_irq(vec, pf); + /* Cleanup CQ NAPI and IRQ */ vec = pf->hw.nix_msixoff + NIX_LF_CINT_VEC_START; for (qidx = 0; qidx < pf->hw.cint_cnt; qidx++) { @@ -852,6 +961,10 @@ int otx2_stop(struct net_device *netdev) for (qidx = 0; qidx < netdev->num_tx_queues; qidx++) netdev_tx_reset_queue(netdev_get_tx_queue(netdev, qidx)); + for (wrk = 0; wrk < pf->qset.cq_cnt; wrk++) + cancel_delayed_work_sync(&pf->refill_wrk[wrk].pool_refill_work); + devm_kfree(pf->dev, pf->refill_wrk); + kfree(qset->sq); kfree(qset->cq); kfree(qset->napi); @@ -931,6 +1044,19 @@ static int otx2_set_features(struct net_device *netdev, return 0; } +static void otx2_reset_task(struct work_struct *work) +{ + struct otx2_nic *pf = container_of(work, struct otx2_nic, reset_task); + + if (!netif_running(pf->netdev)) + return; + + otx2_stop(pf->netdev); + pf->reset_count++; + otx2_open(pf->netdev); + netif_trans_update(pf->netdev); +} + static const struct net_device_ops otx2_netdev_ops = { .ndo_open = otx2_open, .ndo_stop = otx2_stop, @@ -939,6 +1065,7 @@ static const struct net_device_ops otx2_netdev_ops = { .ndo_change_mtu = otx2_change_mtu, .ndo_set_rx_mode = otx2_set_rx_mode, .ndo_set_features = otx2_set_features, + .ndo_tx_timeout = otx2_tx_timeout, }; static int otx2_check_pf_usable(struct otx2_nic *nic) @@ -1115,12 +1242,16 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) netdev->hw_features |= NETIF_F_LOOPBACK | NETIF_F_RXALL; + netdev->watchdog_timeo = OTX2_TX_TIMEOUT; + netdev->netdev_ops = &otx2_netdev_ops; /* MTU range: 64 - 9190 */ netdev->min_mtu = OTX2_MIN_MTU; netdev->max_mtu = OTX2_MAX_MTU; + INIT_WORK(&pf->reset_task, otx2_reset_task); + err = register_netdev(netdev); if (err) { dev_err(dev, "Failed to register netdevice\n"); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h index 04a9f12be2e9..cba59ddf71bb 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_struct.h @@ -245,4 +245,32 @@ struct nix_sqe_mem_s { u64 addr; /* W1 */ }; +enum nix_cqerrint_e { + NIX_CQERRINT_DOOR_ERR = 0, + NIX_CQERRINT_WR_FULL = 1, + NIX_CQERRINT_CQE_FAULT = 2, +}; + +#define NIX_CQERRINT_BITS (BIT_ULL(NIX_CQERRINT_DOOR_ERR) | \ + BIT_ULL(NIX_CQERRINT_CQE_FAULT)) + +enum nix_rqint_e { + NIX_RQINT_DROP = 0, + NIX_RQINT_RED = 1, +}; + +#define NIX_RQINT_BITS (BIT_ULL(NIX_RQINT_DROP) | BIT_ULL(NIX_RQINT_RED)) + +enum nix_sqint_e { + NIX_SQINT_LMT_ERR = 0, + NIX_SQINT_MNQ_ERR = 1, + NIX_SQINT_SEND_ERR = 2, + NIX_SQINT_SQB_ALLOC_FAIL = 3, +}; + +#define NIX_SQINT_BITS (BIT_ULL(NIX_SQINT_LMT_ERR) | \ + BIT_ULL(NIX_SQINT_MNQ_ERR) | \ + BIT_ULL(NIX_SQINT_SEND_ERR) | \ + BIT_ULL(NIX_SQINT_SQB_ALLOC_FAIL)) + #endif /* OTX2_STRUCT_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index 87b579a121ab..94dac84de8dc 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -237,12 +237,23 @@ static int otx2_rx_napi_handler(struct otx2_nic *pfvf, /* Refill pool with new buffers */ while (cq->pool_ptrs) { bufptr = otx2_alloc_rbuf(pfvf, cq->rbpool, GFP_ATOMIC); - if (unlikely(bufptr <= 0)) + if (unlikely(bufptr <= 0)) { + struct refill_work *work; + struct delayed_work *dwork; + + work = &pfvf->refill_wrk[cq->cq_idx]; + dwork = &work->pool_refill_work; + /* Schedule a task if no other task is running */ + if (!cq->refill_task_sched) { + cq->refill_task_sched = true; + schedule_delayed_work(dwork, + msecs_to_jiffies(100)); + } break; + } otx2_aura_freeptr(pfvf, cq->cq_idx, bufptr + OTX2_HEAD_ROOM); cq->pool_ptrs--; } - otx2_get_page(cq->rbpool); return processed_cqe; } @@ -304,6 +315,11 @@ int otx2_napi_handler(struct napi_struct *napi, int budget) continue; cq = &qset->cq[cq_idx]; if (cq->cq_type == CQ_RX) { + /* If the RQ refill WQ task is running, skip napi + * scheduler for this queue. + */ + if (cq->refill_task_sched) + continue; workdone += otx2_rx_napi_handler(pfvf, napi, cq, budget); } else { diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h index bad225979aec..a889b491ea52 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -112,6 +112,7 @@ struct otx2_cq_queue { u8 cq_idx; u8 cq_type; u8 cint_idx; /* CQ interrupt id */ + u8 refill_task_sched; u16 cqe_size; u16 pool_ptrs; u32 cqe_cnt; -- cgit v1.2.1 From 85069e95e53161f8bcc1aa266f47c60c1f3e4ec3 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Mon, 27 Jan 2020 18:35:25 +0530 Subject: octeontx2-pf: Receive side scaling support Adds receive side scaling (RSS) support to distribute pkts/flows across multiple queues. Sets up key, indirection table etc. Also added extraction of HW calculated rxhash and adding to same to SKB ie NETIF_F_RXHASH offload support. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 122 +++++++++++++++++++++ .../ethernet/marvell/octeontx2/nic/otx2_common.h | 14 +++ .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 8 +- .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.c | 24 ++++ 4 files changed, 167 insertions(+), 1 deletion(-) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 2b503066d801..96751588e841 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -106,6 +106,126 @@ int otx2_hw_set_mtu(struct otx2_nic *pfvf, int mtu) return err; } +static int otx2_set_flowkey_cfg(struct otx2_nic *pfvf) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + struct nix_rss_flowkey_cfg *req; + int err; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_nix_rss_flowkey_cfg(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return -ENOMEM; + } + req->mcam_index = -1; /* Default or reserved index */ + req->flowkey_cfg = rss->flowkey_cfg; + req->group = DEFAULT_RSS_CONTEXT_GROUP; + + err = otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); + return err; +} + +static int otx2_set_rss_table(struct otx2_nic *pfvf) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + struct mbox *mbox = &pfvf->mbox; + struct nix_aq_enq_req *aq; + int idx, err; + + otx2_mbox_lock(mbox); + /* Get memory to put this msg */ + for (idx = 0; idx < rss->rss_size; idx++) { + aq = otx2_mbox_alloc_msg_nix_aq_enq(mbox); + if (!aq) { + /* The shared memory buffer can be full. + * Flush it and retry + */ + err = otx2_sync_mbox_msg(mbox); + if (err) { + otx2_mbox_unlock(mbox); + return err; + } + aq = otx2_mbox_alloc_msg_nix_aq_enq(mbox); + if (!aq) { + otx2_mbox_unlock(mbox); + return -ENOMEM; + } + } + + aq->rss.rq = rss->ind_tbl[idx]; + + /* Fill AQ info */ + aq->qidx = idx; + aq->ctype = NIX_AQ_CTYPE_RSS; + aq->op = NIX_AQ_INSTOP_INIT; + } + err = otx2_sync_mbox_msg(mbox); + otx2_mbox_unlock(mbox); + return err; +} + +static void otx2_set_rss_key(struct otx2_nic *pfvf) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + u64 *key = (u64 *)&rss->key[4]; + int idx; + + /* 352bit or 44byte key needs to be configured as below + * NIX_LF_RX_SECRETX0 = key<351:288> + * NIX_LF_RX_SECRETX1 = key<287:224> + * NIX_LF_RX_SECRETX2 = key<223:160> + * NIX_LF_RX_SECRETX3 = key<159:96> + * NIX_LF_RX_SECRETX4 = key<95:32> + * NIX_LF_RX_SECRETX5<63:32> = key<31:0> + */ + otx2_write64(pfvf, NIX_LF_RX_SECRETX(5), + (u64)(*((u32 *)&rss->key)) << 32); + idx = sizeof(rss->key) / sizeof(u64); + while (idx > 0) { + idx--; + otx2_write64(pfvf, NIX_LF_RX_SECRETX(idx), *key++); + } +} + +int otx2_rss_init(struct otx2_nic *pfvf) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + int idx, ret = 0; + + rss->rss_size = sizeof(rss->ind_tbl); + + /* Init RSS key if it is not setup already */ + if (!rss->enable) + netdev_rss_key_fill(rss->key, sizeof(rss->key)); + otx2_set_rss_key(pfvf); + + if (!netif_is_rxfh_configured(pfvf->netdev)) { + /* Default indirection table */ + for (idx = 0; idx < rss->rss_size; idx++) + rss->ind_tbl[idx] = + ethtool_rxfh_indir_default(idx, + pfvf->hw.rx_queues); + } + ret = otx2_set_rss_table(pfvf); + if (ret) + return ret; + + /* Flowkey or hash config to be used for generating flow tag */ + rss->flowkey_cfg = rss->enable ? rss->flowkey_cfg : + NIX_FLOW_KEY_TYPE_IPV4 | NIX_FLOW_KEY_TYPE_IPV6 | + NIX_FLOW_KEY_TYPE_TCP | NIX_FLOW_KEY_TYPE_UDP | + NIX_FLOW_KEY_TYPE_SCTP; + + ret = otx2_set_flowkey_cfg(pfvf); + if (ret) + return ret; + + rss->enable = true; + return 0; +} + void otx2_config_irq_coalescing(struct otx2_nic *pfvf, int qidx) { /* Configure CQE interrupt coalescing parameters @@ -608,6 +728,8 @@ int otx2_config_nix(struct otx2_nic *pfvf) nixlf->rq_cnt = pfvf->hw.rx_queues; nixlf->sq_cnt = pfvf->hw.tx_queues; nixlf->cq_cnt = pfvf->qset.cq_cnt; + nixlf->rss_sz = MAX_RSS_INDIR_TBL_SIZE; + nixlf->rss_grps = 1; /* Single RSS indir table supported, for now */ nixlf->xqe_sz = NIX_XQESZ_W16; /* We don't know absolute NPA LF idx attached. * AF will replace 'RVU_DEFAULT_PF_FUNC' with diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 2a812110995f..f9149efc8b03 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -41,6 +41,16 @@ enum arua_mapped_qtypes { #define NIX_LF_ERR_VEC 0x81 #define NIX_LF_POISON_VEC 0x82 +/* RSS configuration */ +struct otx2_rss_info { + u8 enable; + u32 flowkey_cfg; + u16 rss_size; + u8 ind_tbl[MAX_RSS_INDIR_TBL_SIZE]; +#define RSS_HASH_KEY_SIZE 44 /* 352 bit key */ + u8 key[RSS_HASH_KEY_SIZE]; +}; + /* NIX (or NPC) RX errors */ enum otx2_errlvl { NPC_ERRLVL_RE, @@ -95,6 +105,7 @@ struct mbox { struct otx2_hw { struct pci_dev *pdev; + struct otx2_rss_info rss_info; u16 rx_queues; u16 tx_queues; u16 max_queues; @@ -510,6 +521,9 @@ void otx2_ctx_disable(struct mbox *mbox, int type, bool npa); void otx2_cleanup_rx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); +/* RSS configuration APIs*/ +int otx2_rss_init(struct otx2_nic *pfvf); + /* Mbox handlers */ void mbox_handler_msix_offset(struct otx2_nic *pfvf, struct msix_offset_rsp *rsp); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 171bab0ed662..5f78215979f5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -838,6 +838,11 @@ int otx2_open(struct net_device *netdev) if (err) goto err_disable_napi; + /* Initialize RSS */ + err = otx2_rss_init(pf); + if (err) + goto err_disable_napi; + /* Register Queue IRQ handlers */ vec = pf->hw.nix_msixoff + NIX_LF_QINT_VEC_START; irq_name = &pf->hw.irq_name[vec * NAME_SIZE]; @@ -1237,7 +1242,8 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) pf->iommu_domain = iommu_get_domain_for_dev(dev); netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | - NETIF_F_IPV6_CSUM | NETIF_F_SG); + NETIF_F_IPV6_CSUM | NETIF_F_RXHASH | + NETIF_F_SG); netdev->features |= netdev->hw_features; netdev->hw_features |= NETIF_F_LOOPBACK | NETIF_F_RXALL; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index 94dac84de8dc..513b126feaac 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -115,6 +115,28 @@ static void otx2_skb_add_frag(struct otx2_nic *pfvf, struct sk_buff *skb, pfvf->rbsize, DMA_FROM_DEVICE); } +static void otx2_set_rxhash(struct otx2_nic *pfvf, + struct nix_cqe_rx_s *cqe, struct sk_buff *skb) +{ + enum pkt_hash_types hash_type = PKT_HASH_TYPE_NONE; + struct otx2_rss_info *rss; + u32 hash = 0; + + if (!(pfvf->netdev->features & NETIF_F_RXHASH)) + return; + + rss = &pfvf->hw.rss_info; + if (rss->flowkey_cfg) { + if (rss->flowkey_cfg & + ~(NIX_FLOW_KEY_TYPE_IPV4 | NIX_FLOW_KEY_TYPE_IPV6)) + hash_type = PKT_HASH_TYPE_L4; + else + hash_type = PKT_HASH_TYPE_L3; + hash = cqe->hdr.flow_tag; + } + skb_set_hash(skb, hash, hash_type); +} + static bool otx2_check_rcv_errors(struct otx2_nic *pfvf, struct nix_cqe_rx_s *cqe, int qidx) { @@ -194,6 +216,8 @@ static void otx2_rcv_pkt_handler(struct otx2_nic *pfvf, otx2_skb_add_frag(pfvf, skb, cqe->sg.seg_addr, cqe->sg.seg_size); cq->pool_ptrs++; + otx2_set_rxhash(pfvf, cqe, skb); + skb_record_rx_queue(skb, cq->cq_idx); if (pfvf->netdev->features & NETIF_F_RXCSUM) skb->ip_summed = CHECKSUM_UNNECESSARY; -- cgit v1.2.1 From 86d7476078b87874ce5dee39cbbd094055c73d49 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Mon, 27 Jan 2020 18:35:26 +0530 Subject: octeontx2-pf: TCP segmentation offload support Adds TCP segmentation offload (TSO) support. First version of the silicon didn't support TSO offload, for this driver level TSO support is added. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 8 + .../ethernet/marvell/octeontx2/nic/otx2_common.h | 11 + .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 9 +- .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.c | 247 ++++++++++++++++++++- .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.h | 2 + 5 files changed, 273 insertions(+), 4 deletions(-) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 96751588e841..c674171104c9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -10,6 +10,7 @@ #include #include +#include #include "otx2_reg.h" #include "otx2_common.h" @@ -522,6 +523,11 @@ static int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura) if (err) return err; + err = qmem_alloc(pfvf->dev, &sq->tso_hdrs, qset->sqe_cnt, + TSO_HEADER_SIZE); + if (err) + return err; + sq->sqe_base = sq->sqe->base; sq->sg = kcalloc(qset->sqe_cnt, sizeof(struct sg_list), GFP_KERNEL); if (!sq->sg) @@ -1211,6 +1217,8 @@ void mbox_handler_nix_lf_alloc(struct otx2_nic *pfvf, pfvf->hw.sqb_size = rsp->sqb_size; pfvf->hw.rx_chan_base = rsp->rx_chan_base; pfvf->hw.tx_chan_base = rsp->tx_chan_base; + pfvf->hw.lso_tsov4_idx = rsp->lso_tsov4_idx; + pfvf->hw.lso_tsov6_idx = rsp->lso_tsov6_idx; } void mbox_handler_msix_offset(struct otx2_nic *pfvf, diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index f9149efc8b03..1598396870af 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -129,6 +129,11 @@ struct otx2_hw { u16 rq_skid; u8 cq_time_wait; + /* For TSO segmentation */ + u8 lso_tsov4_idx; + u8 lso_tsov6_idx; + u8 hw_tso; + /* MSI-X */ u8 cint_cnt; /* CQ interrupt count */ u16 npa_msixoff; /* Offset of NPA vectors */ @@ -189,11 +194,17 @@ static inline bool is_96xx_B0(struct pci_dev *pdev) static inline void otx2_setup_dev_hw_settings(struct otx2_nic *pfvf) { + struct otx2_hw *hw = &pfvf->hw; + pfvf->hw.cq_time_wait = CQ_TIMER_THRESH_DEFAULT; pfvf->hw.cq_ecount_wait = CQ_CQE_THRESH_DEFAULT; pfvf->hw.cq_qcount_wait = CQ_QCOUNT_DEFAULT; + hw->hw_tso = true; + if (is_96xx_A0(pfvf->pdev)) { + hw->hw_tso = false; + /* Time based irq coalescing is not supported */ pfvf->hw.cq_qcount_wait = 0x0; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 5f78215979f5..d0809369c367 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -616,6 +616,7 @@ static void otx2_free_sq_res(struct otx2_nic *pf) for (qidx = 0; qidx < pf->hw.tx_queues; qidx++) { sq = &qset->sq[qidx]; qmem_free(pf->dev, sq->sqe); + qmem_free(pf->dev, sq->tso_hdrs); kfree(sq->sg); kfree(sq->sqb_ptrs); } @@ -986,8 +987,9 @@ static netdev_tx_t otx2_xmit(struct sk_buff *skb, struct net_device *netdev) struct otx2_snd_queue *sq; struct netdev_queue *txq; - /* Check for minimum packet length */ - if (skb->len <= ETH_HLEN) { + /* Check for minimum and maximum packet length */ + if (skb->len <= ETH_HLEN || + (!skb_shinfo(skb)->gso_size && skb->len > pf->max_frs)) { dev_kfree_skb(skb); return NETDEV_TX_OK; } @@ -1243,11 +1245,12 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXHASH | - NETIF_F_SG); + NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6); netdev->features |= netdev->hw_features; netdev->hw_features |= NETIF_F_LOOPBACK | NETIF_F_RXALL; + netdev->gso_max_segs = OTX2_MAX_GSO_SEGS; netdev->watchdog_timeo = OTX2_TX_TIMEOUT; netdev->netdev_ops = &otx2_netdev_ops; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index 513b126feaac..6a7ca3b4b9be 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -10,6 +10,7 @@ #include #include +#include #include "otx2_reg.h" #include "otx2_common.h" @@ -428,6 +429,38 @@ static bool otx2_sqe_add_sg(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, return true; } +/* Add SQE extended header subdescriptor */ +static void otx2_sqe_add_ext(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, + struct sk_buff *skb, int *offset) +{ + struct nix_sqe_ext_s *ext; + + ext = (struct nix_sqe_ext_s *)(sq->sqe_base + *offset); + ext->subdc = NIX_SUBDC_EXT; + if (skb_shinfo(skb)->gso_size) { + ext->lso = 1; + ext->lso_sb = skb_transport_offset(skb) + tcp_hdrlen(skb); + ext->lso_mps = skb_shinfo(skb)->gso_size; + + /* Only TSOv4 and TSOv6 GSO offloads are supported */ + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { + ext->lso_format = pfvf->hw.lso_tsov4_idx; + + /* HW adds payload size to 'ip_hdr->tot_len' while + * sending TSO segment, hence set payload length + * in IP header of the packet to just header length. + */ + ip_hdr(skb)->tot_len = + htons(ext->lso_sb - skb_network_offset(skb)); + } else { + ext->lso_format = pfvf->hw.lso_tsov6_idx; + ipv6_hdr(skb)->payload_len = + htons(ext->lso_sb - skb_network_offset(skb)); + } + } + *offset += sizeof(*ext); +} + /* Add SQE header subdescriptor structure */ static void otx2_sqe_add_hdr(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, struct nix_sqe_hdr_s *sqe_hdr, @@ -475,6 +508,209 @@ static void otx2_sqe_add_hdr(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, } } +static int otx2_dma_map_tso_skb(struct otx2_nic *pfvf, + struct otx2_snd_queue *sq, + struct sk_buff *skb, int sqe, int hdr_len) +{ + int num_segs = skb_shinfo(skb)->nr_frags + 1; + struct sg_list *sg = &sq->sg[sqe]; + u64 dma_addr; + int seg, len; + + sg->num_segs = 0; + + /* Get payload length at skb->data */ + len = skb_headlen(skb) - hdr_len; + + for (seg = 0; seg < num_segs; seg++) { + /* Skip skb->data, if there is no payload */ + if (!seg && !len) + continue; + dma_addr = otx2_dma_map_skb_frag(pfvf, skb, seg, &len); + if (dma_mapping_error(pfvf->dev, dma_addr)) + goto unmap; + + /* Save DMA mapping info for later unmapping */ + sg->dma_addr[sg->num_segs] = dma_addr; + sg->size[sg->num_segs] = len; + sg->num_segs++; + } + return 0; +unmap: + otx2_dma_unmap_skb_frags(pfvf, sg); + return -EINVAL; +} + +static u64 otx2_tso_frag_dma_addr(struct otx2_snd_queue *sq, + struct sk_buff *skb, int seg, + u64 seg_addr, int hdr_len, int sqe) +{ + struct sg_list *sg = &sq->sg[sqe]; + const skb_frag_t *frag; + int offset; + + if (seg < 0) + return sg->dma_addr[0] + (seg_addr - (u64)skb->data); + + frag = &skb_shinfo(skb)->frags[seg]; + offset = seg_addr - (u64)skb_frag_address(frag); + if (skb_headlen(skb) - hdr_len) + seg++; + return sg->dma_addr[seg] + offset; +} + +static void otx2_sqe_tso_add_sg(struct otx2_snd_queue *sq, + struct sg_list *list, int *offset) +{ + struct nix_sqe_sg_s *sg = NULL; + u16 *sg_lens = NULL; + u64 *iova = NULL; + int seg; + + /* Add SG descriptors with buffer addresses */ + for (seg = 0; seg < list->num_segs; seg++) { + if ((seg % MAX_SEGS_PER_SG) == 0) { + sg = (struct nix_sqe_sg_s *)(sq->sqe_base + *offset); + sg->ld_type = NIX_SEND_LDTYPE_LDD; + sg->subdc = NIX_SUBDC_SG; + sg->segs = 0; + sg_lens = (void *)sg; + iova = (void *)sg + sizeof(*sg); + /* Next subdc always starts at a 16byte boundary. + * So if sg->segs is whether 2 or 3, offset += 16bytes. + */ + if ((list->num_segs - seg) >= (MAX_SEGS_PER_SG - 1)) + *offset += sizeof(*sg) + (3 * sizeof(u64)); + else + *offset += sizeof(*sg) + sizeof(u64); + } + sg_lens[frag_num(seg % MAX_SEGS_PER_SG)] = list->size[seg]; + *iova++ = list->dma_addr[seg]; + sg->segs++; + } +} + +static void otx2_sq_append_tso(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, + struct sk_buff *skb, u16 qidx) +{ + struct netdev_queue *txq = netdev_get_tx_queue(pfvf->netdev, qidx); + int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + int tcp_data, seg_len, pkt_len, offset; + struct nix_sqe_hdr_s *sqe_hdr; + int first_sqe = sq->head; + struct sg_list list; + struct tso_t tso; + + /* Map SKB's fragments to DMA. + * It's done here to avoid mapping for every TSO segment's packet. + */ + if (otx2_dma_map_tso_skb(pfvf, sq, skb, first_sqe, hdr_len)) { + dev_kfree_skb_any(skb); + return; + } + + netdev_tx_sent_queue(txq, skb->len); + + tso_start(skb, &tso); + tcp_data = skb->len - hdr_len; + while (tcp_data > 0) { + char *hdr; + + seg_len = min_t(int, skb_shinfo(skb)->gso_size, tcp_data); + tcp_data -= seg_len; + + /* Set SQE's SEND_HDR */ + memset(sq->sqe_base, 0, sq->sqe_size); + sqe_hdr = (struct nix_sqe_hdr_s *)(sq->sqe_base); + otx2_sqe_add_hdr(pfvf, sq, sqe_hdr, skb, qidx); + offset = sizeof(*sqe_hdr); + + /* Add TSO segment's pkt header */ + hdr = sq->tso_hdrs->base + (sq->head * TSO_HEADER_SIZE); + tso_build_hdr(skb, hdr, &tso, seg_len, tcp_data == 0); + list.dma_addr[0] = + sq->tso_hdrs->iova + (sq->head * TSO_HEADER_SIZE); + list.size[0] = hdr_len; + list.num_segs = 1; + + /* Add TSO segment's payload data fragments */ + pkt_len = hdr_len; + while (seg_len > 0) { + int size; + + size = min_t(int, tso.size, seg_len); + + list.size[list.num_segs] = size; + list.dma_addr[list.num_segs] = + otx2_tso_frag_dma_addr(sq, skb, + tso.next_frag_idx - 1, + (u64)tso.data, hdr_len, + first_sqe); + list.num_segs++; + pkt_len += size; + seg_len -= size; + tso_build_data(skb, &tso, size); + } + sqe_hdr->total = pkt_len; + otx2_sqe_tso_add_sg(sq, &list, &offset); + + /* DMA mappings and skb needs to be freed only after last + * TSO segment is transmitted out. So set 'PNC' only for + * last segment. Also point last segment's sqe_id to first + * segment's SQE index where skb address and DMA mappings + * are saved. + */ + if (!tcp_data) { + sqe_hdr->pnc = 1; + sqe_hdr->sqe_id = first_sqe; + sq->sg[first_sqe].skb = (u64)skb; + } else { + sqe_hdr->pnc = 0; + } + + sqe_hdr->sizem1 = (offset / 16) - 1; + + /* Flush SQE to HW */ + otx2_sqe_flush(sq, offset); + } +} + +static bool is_hw_tso_supported(struct otx2_nic *pfvf, + struct sk_buff *skb) +{ + int payload_len, last_seg_size; + + if (!pfvf->hw.hw_tso) + return false; + + /* HW has an issue due to which when the payload of the last LSO + * segment is shorter than 16 bytes, some header fields may not + * be correctly modified, hence don't offload such TSO segments. + */ + if (!is_96xx_B0(pfvf->pdev)) + return true; + + payload_len = skb->len - (skb_transport_offset(skb) + tcp_hdrlen(skb)); + last_seg_size = payload_len % skb_shinfo(skb)->gso_size; + if (last_seg_size && last_seg_size < 16) + return false; + + return true; +} + +static int otx2_get_sqe_count(struct otx2_nic *pfvf, struct sk_buff *skb) +{ + if (!skb_shinfo(skb)->gso_size) + return 1; + + /* HW TSO */ + if (is_hw_tso_supported(pfvf, skb)) + return 1; + + /* SW TSO */ + return skb_shinfo(skb)->gso_segs; +} + bool otx2_sq_append_skb(struct net_device *netdev, struct otx2_snd_queue *sq, struct sk_buff *skb, u16 qidx) { @@ -489,7 +725,8 @@ bool otx2_sq_append_skb(struct net_device *netdev, struct otx2_snd_queue *sq, */ free_sqe = (sq->num_sqbs - *sq->aura_fc_addr) * sq->sqe_per_sqb; - if (!free_sqe || free_sqe < sq->sqe_thresh) + if (free_sqe < sq->sqe_thresh || + free_sqe < otx2_get_sqe_count(pfvf, skb)) return false; num_segs = skb_shinfo(skb)->nr_frags + 1; @@ -505,6 +742,11 @@ bool otx2_sq_append_skb(struct net_device *netdev, struct otx2_snd_queue *sq, num_segs = skb_shinfo(skb)->nr_frags + 1; } + if (skb_shinfo(skb)->gso_size && !is_hw_tso_supported(pfvf, skb)) { + otx2_sq_append_tso(pfvf, sq, skb, qidx); + return true; + } + /* Set SQE's SEND_HDR. * Do not clear the first 64bit as it contains constant info. */ @@ -513,6 +755,9 @@ bool otx2_sq_append_skb(struct net_device *netdev, struct otx2_snd_queue *sq, otx2_sqe_add_hdr(pfvf, sq, sqe_hdr, skb, qidx); offset = sizeof(*sqe_hdr); + /* Add extended header if needed */ + otx2_sqe_add_ext(pfvf, sq, skb, &offset); + /* Add SG subdesc with data frags */ if (!otx2_sqe_add_sg(pfvf, sq, skb, num_segs, &offset)) { otx2_dma_unmap_skb_frags(pfvf, &sq->sg[sq->head]); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h index a889b491ea52..107a261492ba 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -26,6 +26,7 @@ #define OTX2_MIN_MTU 64 #define OTX2_MAX_MTU (9212 - OTX2_ETH_HLEN) +#define OTX2_MAX_GSO_SEGS 255 #define OTX2_MAX_FRAGS_IN_SQE 9 /* Rx buffer size should be in multiples of 128bytes */ @@ -79,6 +80,7 @@ struct otx2_snd_queue { u64 *lmt_addr; void *sqe_base; struct qmem *sqe; + struct qmem *tso_hdrs; struct sg_list *sg; u16 sqb_count; u64 *sqb_ptrs; -- cgit v1.2.1 From e239d0c78c42e49558ea2c0e5857c4f9ca064f48 Mon Sep 17 00:00:00 2001 From: Geetha sowjanya Date: Mon, 27 Jan 2020 18:35:27 +0530 Subject: octeontx2-pf: Add ndo_get_stats64 Added ndo_get_stats64 which returns stats maintained by HW. Signed-off-by: Geetha sowjanya Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 47 +++++++++++++++++++++ .../ethernet/marvell/octeontx2/nic/otx2_common.h | 49 ++++++++++++++++++++++ .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 1 + 3 files changed, 97 insertions(+) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index c674171104c9..c4be7872d9f4 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -16,6 +16,53 @@ #include "otx2_common.h" #include "otx2_struct.h" +void otx2_get_dev_stats(struct otx2_nic *pfvf) +{ + struct otx2_dev_stats *dev_stats = &pfvf->hw.dev_stats; + +#define OTX2_GET_RX_STATS(reg) \ + otx2_read64(pfvf, NIX_LF_RX_STATX(reg)) +#define OTX2_GET_TX_STATS(reg) \ + otx2_read64(pfvf, NIX_LF_TX_STATX(reg)) + + dev_stats->rx_bytes = OTX2_GET_RX_STATS(RX_OCTS); + dev_stats->rx_drops = OTX2_GET_RX_STATS(RX_DROP); + dev_stats->rx_bcast_frames = OTX2_GET_RX_STATS(RX_BCAST); + dev_stats->rx_mcast_frames = OTX2_GET_RX_STATS(RX_MCAST); + dev_stats->rx_ucast_frames = OTX2_GET_RX_STATS(RX_UCAST); + dev_stats->rx_frames = dev_stats->rx_bcast_frames + + dev_stats->rx_mcast_frames + + dev_stats->rx_ucast_frames; + + dev_stats->tx_bytes = OTX2_GET_TX_STATS(TX_OCTS); + dev_stats->tx_drops = OTX2_GET_TX_STATS(TX_DROP); + dev_stats->tx_bcast_frames = OTX2_GET_TX_STATS(TX_BCAST); + dev_stats->tx_mcast_frames = OTX2_GET_TX_STATS(TX_MCAST); + dev_stats->tx_ucast_frames = OTX2_GET_TX_STATS(TX_UCAST); + dev_stats->tx_frames = dev_stats->tx_bcast_frames + + dev_stats->tx_mcast_frames + + dev_stats->tx_ucast_frames; +} + +void otx2_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct otx2_dev_stats *dev_stats; + + otx2_get_dev_stats(pfvf); + + dev_stats = &pfvf->hw.dev_stats; + stats->rx_bytes = dev_stats->rx_bytes; + stats->rx_packets = dev_stats->rx_frames; + stats->rx_dropped = dev_stats->rx_drops; + stats->multicast = dev_stats->rx_mcast_frames; + + stats->tx_bytes = dev_stats->tx_bytes; + stats->tx_packets = dev_stats->tx_frames; + stats->tx_dropped = dev_stats->tx_drops; +} + /* Sync MAC address with RVU AF */ static int otx2_hw_set_mac_addr(struct otx2_nic *pfvf, u8 *mac) { diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 1598396870af..ce7a552b478a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -81,6 +81,49 @@ enum otx2_errcodes_re { ERRCODE_IL4_CSUM = 0x22, }; +/* NIX TX stats */ +enum nix_stat_lf_tx { + TX_UCAST = 0x0, + TX_BCAST = 0x1, + TX_MCAST = 0x2, + TX_DROP = 0x3, + TX_OCTS = 0x4, + TX_STATS_ENUM_LAST, +}; + +/* NIX RX stats */ +enum nix_stat_lf_rx { + RX_OCTS = 0x0, + RX_UCAST = 0x1, + RX_BCAST = 0x2, + RX_MCAST = 0x3, + RX_DROP = 0x4, + RX_DROP_OCTS = 0x5, + RX_FCS = 0x6, + RX_ERR = 0x7, + RX_DRP_BCAST = 0x8, + RX_DRP_MCAST = 0x9, + RX_DRP_L3BCAST = 0xa, + RX_DRP_L3MCAST = 0xb, + RX_STATS_ENUM_LAST, +}; + +struct otx2_dev_stats { + u64 rx_bytes; + u64 rx_frames; + u64 rx_ucast_frames; + u64 rx_bcast_frames; + u64 rx_mcast_frames; + u64 rx_drops; + + u64 tx_bytes; + u64 tx_frames; + u64 tx_ucast_frames; + u64 tx_bcast_frames; + u64 tx_mcast_frames; + u64 tx_drops; +}; + /* Driver counted stats */ struct otx2_drv_stats { atomic_t rx_fcs_errs; @@ -142,6 +185,7 @@ struct otx2_hw { cpumask_var_t *affinity_mask; /* Stats */ + struct otx2_dev_stats dev_stats; struct otx2_drv_stats drv_stats; }; @@ -545,6 +589,11 @@ void mbox_handler_nix_lf_alloc(struct otx2_nic *pfvf, void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, struct nix_txsch_alloc_rsp *rsp); +/* Device stats APIs */ +void otx2_get_dev_stats(struct otx2_nic *pfvf); +void otx2_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats); + int otx2_open(struct net_device *netdev); int otx2_stop(struct net_device *netdev); #endif /* OTX2_COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index d0809369c367..3092634ef291 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1073,6 +1073,7 @@ static const struct net_device_ops otx2_netdev_ops = { .ndo_set_rx_mode = otx2_set_rx_mode, .ndo_set_features = otx2_set_features, .ndo_tx_timeout = otx2_tx_timeout, + .ndo_get_stats64 = otx2_get_stats64, }; static int otx2_check_pf_usable(struct otx2_nic *nic) -- cgit v1.2.1 From d45d8979840d9c9ac93d3fe8cfc8e794b7228445 Mon Sep 17 00:00:00 2001 From: Christina Jacob Date: Mon, 27 Jan 2020 18:35:28 +0530 Subject: octeontx2-pf: Add basic ethtool support This patch adds ethtool support for - Driver stats, Tx/Rx perqueue and CGX LMAC stats - Set/show Rx/Tx queue count - Set/show Rx/Tx ring sizes - Set/show IRQ coalescing parameters Signed-off-by: Christina Jacob Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- .../net/ethernet/marvell/octeontx2/nic/Makefile | 2 +- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 82 ++++- .../ethernet/marvell/octeontx2/nic/otx2_common.h | 10 + .../ethernet/marvell/octeontx2/nic/otx2_ethtool.c | 408 +++++++++++++++++++++ .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 16 +- .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.h | 11 + 6 files changed, 525 insertions(+), 4 deletions(-) create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile index 0484d709fe68..41bf00cf5b1d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_OCTEONTX2_PF) += octeontx2_nicpf.o -octeontx2_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o +octeontx2_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o ccflags-y += -I$(srctree)/drivers/net/ethernet/marvell/octeontx2/af diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index c4be7872d9f4..07d45596b62e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -16,6 +16,72 @@ #include "otx2_common.h" #include "otx2_struct.h" +static void otx2_nix_rq_op_stats(struct queue_stats *stats, + struct otx2_nic *pfvf, int qidx) +{ + u64 incr = (u64)qidx << 32; + u64 *ptr; + + ptr = (u64 *)otx2_get_regaddr(pfvf, NIX_LF_RQ_OP_OCTS); + stats->bytes = otx2_atomic64_add(incr, ptr); + + ptr = (u64 *)otx2_get_regaddr(pfvf, NIX_LF_RQ_OP_PKTS); + stats->pkts = otx2_atomic64_add(incr, ptr); +} + +static void otx2_nix_sq_op_stats(struct queue_stats *stats, + struct otx2_nic *pfvf, int qidx) +{ + u64 incr = (u64)qidx << 32; + u64 *ptr; + + ptr = (u64 *)otx2_get_regaddr(pfvf, NIX_LF_SQ_OP_OCTS); + stats->bytes = otx2_atomic64_add(incr, ptr); + + ptr = (u64 *)otx2_get_regaddr(pfvf, NIX_LF_SQ_OP_PKTS); + stats->pkts = otx2_atomic64_add(incr, ptr); +} + +void otx2_update_lmac_stats(struct otx2_nic *pfvf) +{ + struct msg_req *req; + + if (!netif_running(pfvf->netdev)) + return; + + otx2_mbox_lock(&pfvf->mbox); + req = otx2_mbox_alloc_msg_cgx_stats(&pfvf->mbox); + if (!req) { + otx2_mbox_unlock(&pfvf->mbox); + return; + } + + otx2_sync_mbox_msg(&pfvf->mbox); + otx2_mbox_unlock(&pfvf->mbox); +} + +int otx2_update_rq_stats(struct otx2_nic *pfvf, int qidx) +{ + struct otx2_rcv_queue *rq = &pfvf->qset.rq[qidx]; + + if (!pfvf->qset.rq) + return 0; + + otx2_nix_rq_op_stats(&rq->stats, pfvf, qidx); + return 1; +} + +int otx2_update_sq_stats(struct otx2_nic *pfvf, int qidx) +{ + struct otx2_snd_queue *sq = &pfvf->qset.sq[qidx]; + + if (!pfvf->qset.sq) + return 0; + + otx2_nix_sq_op_stats(&sq->stats, pfvf, qidx); + return 1; +} + void otx2_get_dev_stats(struct otx2_nic *pfvf) { struct otx2_dev_stats *dev_stats = &pfvf->hw.dev_stats; @@ -590,6 +656,9 @@ static int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura) sq->lmt_addr = (__force u64 *)(pfvf->reg_base + LMT_LF_LMTLINEX(qidx)); sq->io_addr = (__force u64)otx2_get_regaddr(pfvf, NIX_LF_OP_SENDX(0)); + sq->stats.bytes = 0; + sq->stats.pkts = 0; + /* Get memory to put this msg */ aq = otx2_mbox_alloc_msg_nix_aq_enq(&pfvf->mbox); if (!aq) @@ -1238,6 +1307,18 @@ void otx2_ctx_disable(struct mbox *mbox, int type, bool npa) otx2_mbox_unlock(mbox); } +/* Mbox message handlers */ +void mbox_handler_cgx_stats(struct otx2_nic *pfvf, + struct cgx_stats_rsp *rsp) +{ + int id; + + for (id = 0; id < CGX_RX_STATS_COUNT; id++) + pfvf->hw.cgx_rx_stats[id] = rsp->rx_stats[id]; + for (id = 0; id < CGX_TX_STATS_COUNT; id++) + pfvf->hw.cgx_tx_stats[id] = rsp->tx_stats[id]; +} + void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, struct nix_txsch_alloc_rsp *rsp) { @@ -1250,7 +1331,6 @@ void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, rsp->schq_list[lvl][schq]; } -/* Mbox message handlers */ void mbox_handler_npa_lf_alloc(struct otx2_nic *pfvf, struct npa_lf_alloc_rsp *rsp) { diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index ce7a552b478a..95557e8425a3 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -187,6 +187,8 @@ struct otx2_hw { /* Stats */ struct otx2_dev_stats dev_stats; struct otx2_drv_stats drv_stats; + u64 cgx_rx_stats[CGX_RX_STATS_COUNT]; + u64 cgx_tx_stats[CGX_TX_STATS_COUNT]; }; struct refill_work { @@ -588,12 +590,20 @@ void mbox_handler_nix_lf_alloc(struct otx2_nic *pfvf, struct nix_lf_alloc_rsp *rsp); void mbox_handler_nix_txsch_alloc(struct otx2_nic *pf, struct nix_txsch_alloc_rsp *rsp); +void mbox_handler_cgx_stats(struct otx2_nic *pfvf, + struct cgx_stats_rsp *rsp); /* Device stats APIs */ void otx2_get_dev_stats(struct otx2_nic *pfvf); void otx2_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats); +void otx2_update_lmac_stats(struct otx2_nic *pfvf); +int otx2_update_rq_stats(struct otx2_nic *pfvf, int qidx); +int otx2_update_sq_stats(struct otx2_nic *pfvf, int qidx); +void otx2_set_ethtool_ops(struct net_device *netdev); int otx2_open(struct net_device *netdev); int otx2_stop(struct net_device *netdev); +int otx2_set_real_num_queues(struct net_device *netdev, + int tx_queues, int rx_queues); #endif /* OTX2_COMMON_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c new file mode 100644 index 000000000000..b1f61e069ed8 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell OcteonTx2 RVU Ethernet driver + * + * Copyright (C) 2020 Marvell International Ltd. + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include "otx2_common.h" + +#define DRV_NAME "octeontx2-nicpf" + +struct otx2_stat { + char name[ETH_GSTRING_LEN]; + unsigned int index; +}; + +/* HW device stats */ +#define OTX2_DEV_STAT(stat) { \ + .name = #stat, \ + .index = offsetof(struct otx2_dev_stats, stat) / sizeof(u64), \ +} + +static const struct otx2_stat otx2_dev_stats[] = { + OTX2_DEV_STAT(rx_ucast_frames), + OTX2_DEV_STAT(rx_bcast_frames), + OTX2_DEV_STAT(rx_mcast_frames), + + OTX2_DEV_STAT(tx_ucast_frames), + OTX2_DEV_STAT(tx_bcast_frames), + OTX2_DEV_STAT(tx_mcast_frames), +}; + +/* Driver level stats */ +#define OTX2_DRV_STAT(stat) { \ + .name = #stat, \ + .index = offsetof(struct otx2_drv_stats, stat) / sizeof(atomic_t), \ +} + +static const struct otx2_stat otx2_drv_stats[] = { + OTX2_DRV_STAT(rx_fcs_errs), + OTX2_DRV_STAT(rx_oversize_errs), + OTX2_DRV_STAT(rx_undersize_errs), + OTX2_DRV_STAT(rx_csum_errs), + OTX2_DRV_STAT(rx_len_errs), + OTX2_DRV_STAT(rx_other_errs), +}; + +static const struct otx2_stat otx2_queue_stats[] = { + { "bytes", 0 }, + { "frames", 1 }, +}; + +static const unsigned int otx2_n_dev_stats = ARRAY_SIZE(otx2_dev_stats); +static const unsigned int otx2_n_drv_stats = ARRAY_SIZE(otx2_drv_stats); +static const unsigned int otx2_n_queue_stats = ARRAY_SIZE(otx2_queue_stats); + +static void otx2_dev_open(struct net_device *netdev) +{ + otx2_open(netdev); +} + +static void otx2_dev_stop(struct net_device *netdev) +{ + otx2_stop(netdev); +} + +static void otx2_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->bus_info, pci_name(pfvf->pdev), sizeof(info->bus_info)); +} + +static void otx2_get_qset_strings(struct otx2_nic *pfvf, u8 **data, int qset) +{ + int start_qidx = qset * pfvf->hw.rx_queues; + int qidx, stats; + + for (qidx = 0; qidx < pfvf->hw.rx_queues; qidx++) { + for (stats = 0; stats < otx2_n_queue_stats; stats++) { + sprintf(*data, "rxq%d: %s", qidx + start_qidx, + otx2_queue_stats[stats].name); + *data += ETH_GSTRING_LEN; + } + } + for (qidx = 0; qidx < pfvf->hw.tx_queues; qidx++) { + for (stats = 0; stats < otx2_n_queue_stats; stats++) { + sprintf(*data, "txq%d: %s", qidx + start_qidx, + otx2_queue_stats[stats].name); + *data += ETH_GSTRING_LEN; + } + } +} + +static void otx2_get_strings(struct net_device *netdev, u32 sset, u8 *data) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + int stats; + + if (sset != ETH_SS_STATS) + return; + + for (stats = 0; stats < otx2_n_dev_stats; stats++) { + memcpy(data, otx2_dev_stats[stats].name, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + + for (stats = 0; stats < otx2_n_drv_stats; stats++) { + memcpy(data, otx2_drv_stats[stats].name, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + + otx2_get_qset_strings(pfvf, &data, 0); + + for (stats = 0; stats < CGX_RX_STATS_COUNT; stats++) { + sprintf(data, "cgx_rxstat%d: ", stats); + data += ETH_GSTRING_LEN; + } + + for (stats = 0; stats < CGX_TX_STATS_COUNT; stats++) { + sprintf(data, "cgx_txstat%d: ", stats); + data += ETH_GSTRING_LEN; + } + + strcpy(data, "reset_count"); + data += ETH_GSTRING_LEN; +} + +static void otx2_get_qset_stats(struct otx2_nic *pfvf, + struct ethtool_stats *stats, u64 **data) +{ + int stat, qidx; + + if (!pfvf) + return; + for (qidx = 0; qidx < pfvf->hw.rx_queues; qidx++) { + if (!otx2_update_rq_stats(pfvf, qidx)) { + for (stat = 0; stat < otx2_n_queue_stats; stat++) + *((*data)++) = 0; + continue; + } + for (stat = 0; stat < otx2_n_queue_stats; stat++) + *((*data)++) = ((u64 *)&pfvf->qset.rq[qidx].stats) + [otx2_queue_stats[stat].index]; + } + + for (qidx = 0; qidx < pfvf->hw.tx_queues; qidx++) { + if (!otx2_update_sq_stats(pfvf, qidx)) { + for (stat = 0; stat < otx2_n_queue_stats; stat++) + *((*data)++) = 0; + continue; + } + for (stat = 0; stat < otx2_n_queue_stats; stat++) + *((*data)++) = ((u64 *)&pfvf->qset.sq[qidx].stats) + [otx2_queue_stats[stat].index]; + } +} + +/* Get device and per queue statistics */ +static void otx2_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, u64 *data) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + int stat; + + otx2_get_dev_stats(pfvf); + for (stat = 0; stat < otx2_n_dev_stats; stat++) + *(data++) = ((u64 *)&pfvf->hw.dev_stats) + [otx2_dev_stats[stat].index]; + + for (stat = 0; stat < otx2_n_drv_stats; stat++) + *(data++) = atomic_read(&((atomic_t *)&pfvf->hw.drv_stats) + [otx2_drv_stats[stat].index]); + + otx2_get_qset_stats(pfvf, stats, &data); + otx2_update_lmac_stats(pfvf); + for (stat = 0; stat < CGX_RX_STATS_COUNT; stat++) + *(data++) = pfvf->hw.cgx_rx_stats[stat]; + for (stat = 0; stat < CGX_TX_STATS_COUNT; stat++) + *(data++) = pfvf->hw.cgx_tx_stats[stat]; + *(data++) = pfvf->reset_count; +} + +static int otx2_get_sset_count(struct net_device *netdev, int sset) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + int qstats_count; + + if (sset != ETH_SS_STATS) + return -EINVAL; + + qstats_count = otx2_n_queue_stats * + (pfvf->hw.rx_queues + pfvf->hw.tx_queues); + + return otx2_n_dev_stats + otx2_n_drv_stats + qstats_count + + CGX_RX_STATS_COUNT + CGX_TX_STATS_COUNT + 1; +} + +/* Get no of queues device supports and current queue count */ +static void otx2_get_channels(struct net_device *dev, + struct ethtool_channels *channel) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + + channel->max_rx = pfvf->hw.max_queues; + channel->max_tx = pfvf->hw.max_queues; + + channel->rx_count = pfvf->hw.rx_queues; + channel->tx_count = pfvf->hw.tx_queues; +} + +/* Set no of Tx, Rx queues to be used */ +static int otx2_set_channels(struct net_device *dev, + struct ethtool_channels *channel) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + bool if_up = netif_running(dev); + int err = 0; + + if (!channel->rx_count || !channel->tx_count) + return -EINVAL; + + if (if_up) + otx2_dev_stop(dev); + + err = otx2_set_real_num_queues(dev, channel->tx_count, + channel->rx_count); + if (err) + goto fail; + + pfvf->hw.rx_queues = channel->rx_count; + pfvf->hw.tx_queues = channel->tx_count; + pfvf->qset.cq_cnt = pfvf->hw.tx_queues + pfvf->hw.rx_queues; + +fail: + if (if_up) + otx2_dev_open(dev); + + netdev_info(dev, "Setting num Tx rings to %d, Rx rings to %d success\n", + pfvf->hw.tx_queues, pfvf->hw.rx_queues); + + return err; +} + +static void otx2_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct otx2_qset *qs = &pfvf->qset; + + ring->rx_max_pending = Q_COUNT(Q_SIZE_MAX); + ring->rx_pending = qs->rqe_cnt ? qs->rqe_cnt : Q_COUNT(Q_SIZE_256); + ring->tx_max_pending = Q_COUNT(Q_SIZE_MAX); + ring->tx_pending = qs->sqe_cnt ? qs->sqe_cnt : Q_COUNT(Q_SIZE_4K); +} + +static int otx2_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + bool if_up = netif_running(netdev); + struct otx2_qset *qs = &pfvf->qset; + u32 rx_count, tx_count; + + if (ring->rx_mini_pending || ring->rx_jumbo_pending) + return -EINVAL; + + /* Permitted lengths are 16 64 256 1K 4K 16K 64K 256K 1M */ + rx_count = ring->rx_pending; + /* On some silicon variants a skid or reserved CQEs are + * needed to avoid CQ overflow. + */ + if (rx_count < pfvf->hw.rq_skid) + rx_count = pfvf->hw.rq_skid; + rx_count = Q_COUNT(Q_SIZE(rx_count, 3)); + + /* Due pipelining impact minimum 2000 unused SQ CQE's + * need to be maintained to avoid CQ overflow, hence the + * minimum 4K size. + */ + tx_count = clamp_t(u32, ring->tx_pending, + Q_COUNT(Q_SIZE_4K), Q_COUNT(Q_SIZE_MAX)); + tx_count = Q_COUNT(Q_SIZE(tx_count, 3)); + + if (tx_count == qs->sqe_cnt && rx_count == qs->rqe_cnt) + return 0; + + if (if_up) + otx2_dev_stop(netdev); + + /* Assigned to the nearest possible exponent. */ + qs->sqe_cnt = tx_count; + qs->rqe_cnt = rx_count; + + if (if_up) + otx2_dev_open(netdev); + return 0; +} + +static int otx2_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *cmd) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct otx2_hw *hw = &pfvf->hw; + + cmd->rx_coalesce_usecs = hw->cq_time_wait; + cmd->rx_max_coalesced_frames = hw->cq_ecount_wait; + cmd->tx_coalesce_usecs = hw->cq_time_wait; + cmd->tx_max_coalesced_frames = hw->cq_ecount_wait; + + return 0; +} + +static int otx2_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct otx2_hw *hw = &pfvf->hw; + int qidx; + + if (ec->use_adaptive_rx_coalesce || ec->use_adaptive_tx_coalesce || + ec->rx_coalesce_usecs_irq || ec->rx_max_coalesced_frames_irq || + ec->tx_coalesce_usecs_irq || ec->tx_max_coalesced_frames_irq || + ec->stats_block_coalesce_usecs || ec->pkt_rate_low || + ec->rx_coalesce_usecs_low || ec->rx_max_coalesced_frames_low || + ec->tx_coalesce_usecs_low || ec->tx_max_coalesced_frames_low || + ec->pkt_rate_high || ec->rx_coalesce_usecs_high || + ec->rx_max_coalesced_frames_high || ec->tx_coalesce_usecs_high || + ec->tx_max_coalesced_frames_high || ec->rate_sample_interval) + return -EOPNOTSUPP; + + if (!ec->rx_max_coalesced_frames || !ec->tx_max_coalesced_frames) + return 0; + + /* 'cq_time_wait' is 8bit and is in multiple of 100ns, + * so clamp the user given value to the range of 1 to 25usec. + */ + ec->rx_coalesce_usecs = clamp_t(u32, ec->rx_coalesce_usecs, + 1, CQ_TIMER_THRESH_MAX); + ec->tx_coalesce_usecs = clamp_t(u32, ec->tx_coalesce_usecs, + 1, CQ_TIMER_THRESH_MAX); + + /* Rx and Tx are mapped to same CQ, check which one + * is changed, if both then choose the min. + */ + if (hw->cq_time_wait == ec->rx_coalesce_usecs) + hw->cq_time_wait = ec->tx_coalesce_usecs; + else if (hw->cq_time_wait == ec->tx_coalesce_usecs) + hw->cq_time_wait = ec->rx_coalesce_usecs; + else + hw->cq_time_wait = min_t(u8, ec->rx_coalesce_usecs, + ec->tx_coalesce_usecs); + + /* Max ecount_wait supported is 16bit, + * so clamp the user given value to the range of 1 to 64k. + */ + ec->rx_max_coalesced_frames = clamp_t(u32, ec->rx_max_coalesced_frames, + 1, U16_MAX); + ec->tx_max_coalesced_frames = clamp_t(u32, ec->tx_max_coalesced_frames, + 1, U16_MAX); + + /* Rx and Tx are mapped to same CQ, check which one + * is changed, if both then choose the min. + */ + if (hw->cq_ecount_wait == ec->rx_max_coalesced_frames) + hw->cq_ecount_wait = ec->tx_max_coalesced_frames; + else if (hw->cq_ecount_wait == ec->tx_max_coalesced_frames) + hw->cq_ecount_wait = ec->rx_max_coalesced_frames; + else + hw->cq_ecount_wait = min_t(u16, ec->rx_max_coalesced_frames, + ec->tx_max_coalesced_frames); + + if (netif_running(netdev)) { + for (qidx = 0; qidx < pfvf->hw.cint_cnt; qidx++) + otx2_config_irq_coalescing(pfvf, qidx); + } + + return 0; +} + +static const struct ethtool_ops otx2_ethtool_ops = { + .get_drvinfo = otx2_get_drvinfo, + .get_strings = otx2_get_strings, + .get_ethtool_stats = otx2_get_ethtool_stats, + .get_sset_count = otx2_get_sset_count, + .set_channels = otx2_set_channels, + .get_channels = otx2_get_channels, + .get_ringparam = otx2_get_ringparam, + .set_ringparam = otx2_set_ringparam, + .get_coalesce = otx2_get_coalesce, + .set_coalesce = otx2_set_coalesce, +}; + +void otx2_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &otx2_ethtool_ops; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 3092634ef291..85f9b9ba6bd5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -148,6 +148,9 @@ static void otx2_process_pfaf_mbox_msg(struct otx2_nic *pf, mbox_handler_nix_txsch_alloc(pf, (struct nix_txsch_alloc_rsp *)msg); break; + case MBOX_MSG_CGX_STATS: + mbox_handler_cgx_stats(pf, (struct cgx_stats_rsp *)msg); + break; default: if (msg->rc) dev_err(pf->dev, @@ -459,8 +462,8 @@ static int otx2_cgx_config_loopback(struct otx2_nic *pf, bool enable) return err; } -static int otx2_set_real_num_queues(struct net_device *netdev, - int tx_queues, int rx_queues) +int otx2_set_real_num_queues(struct net_device *netdev, + int tx_queues, int rx_queues) { int err; @@ -812,6 +815,11 @@ int otx2_open(struct net_device *netdev) if (!qset->sq) goto err_free_mem; + qset->rq = kcalloc(pf->hw.rx_queues, + sizeof(struct otx2_rcv_queue), GFP_KERNEL); + if (!qset->rq) + goto err_free_mem; + err = otx2_init_hw_resources(pf); if (err) goto err_free_mem; @@ -917,6 +925,7 @@ err_disable_napi: err_free_mem: kfree(qset->sq); kfree(qset->cq); + kfree(qset->rq); kfree(qset->napi); return err; } @@ -973,6 +982,7 @@ int otx2_stop(struct net_device *netdev) kfree(qset->sq); kfree(qset->cq); + kfree(qset->rq); kfree(qset->napi); /* Do not clear RQ/SQ ringsize settings */ memset((void *)qset + offsetof(struct otx2_qset, sqe_cnt), 0, @@ -1268,6 +1278,8 @@ static int otx2_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_detach_rsrc; } + otx2_set_ethtool_ops(netdev); + /* Enable link notifications */ otx2_cgx_config_linkevents(pf, true); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h index 107a261492ba..4ab32d3adb78 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -60,6 +60,15 @@ */ #define CQ_QCOUNT_DEFAULT 1 +struct queue_stats { + u64 bytes; + u64 pkts; +}; + +struct otx2_rcv_queue { + struct queue_stats stats; +}; + struct sg_list { u16 num_segs; u64 skb; @@ -82,6 +91,7 @@ struct otx2_snd_queue { struct qmem *sqe; struct qmem *tso_hdrs; struct sg_list *sg; + struct queue_stats stats; u16 sqb_count; u64 *sqb_ptrs; } ____cacheline_aligned_in_smp; @@ -134,6 +144,7 @@ struct otx2_qset { struct otx2_cq_poll *napi; struct otx2_cq_queue *cq; struct otx2_snd_queue *sq; + struct otx2_rcv_queue *rq; }; /* Translate IOVA to physical address */ -- cgit v1.2.1 From 6e92d71bf88208ddcbd48b3c831c0e394d0f56e2 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Mon, 27 Jan 2020 18:35:29 +0530 Subject: octeontx2-pf: ethtool RSS config support Added support to show or configure RSS hash key, indirection table, 2,4 tuple via ethtool. Also added debug msg_level support to dump messages when HW reports errors in packet received or transmitted. Signed-off-by: Prakash Brahmajyosyula Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 6 +- .../ethernet/marvell/octeontx2/nic/otx2_common.h | 6 + .../ethernet/marvell/octeontx2/nic/otx2_ethtool.c | 254 +++++++++++++++++++++ .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.c | 7 +- 4 files changed, 269 insertions(+), 4 deletions(-) (limited to 'drivers/net/ethernet/marvell') diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 07d45596b62e..8247d21d0432 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -220,7 +220,7 @@ int otx2_hw_set_mtu(struct otx2_nic *pfvf, int mtu) return err; } -static int otx2_set_flowkey_cfg(struct otx2_nic *pfvf) +int otx2_set_flowkey_cfg(struct otx2_nic *pfvf) { struct otx2_rss_info *rss = &pfvf->hw.rss_info; struct nix_rss_flowkey_cfg *req; @@ -241,7 +241,7 @@ static int otx2_set_flowkey_cfg(struct otx2_nic *pfvf) return err; } -static int otx2_set_rss_table(struct otx2_nic *pfvf) +int otx2_set_rss_table(struct otx2_nic *pfvf) { struct otx2_rss_info *rss = &pfvf->hw.rss_info; struct mbox *mbox = &pfvf->mbox; @@ -280,7 +280,7 @@ static int otx2_set_rss_table(struct otx2_nic *pfvf) return err; } -static void otx2_set_rss_key(struct otx2_nic *pfvf) +void otx2_set_rss_key(struct otx2_nic *pfvf) { struct otx2_rss_info *rss = &pfvf->hw.rss_info; u64 *key = (u64 *)&rss->key[4]; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 95557e8425a3..320f3b7bf57f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -222,6 +222,9 @@ struct otx2_nic { struct work_struct reset_task; struct refill_work *refill_wrk; + /* Ethtool stuff */ + u32 msg_enable; + /* Block address of NIX either BLKADDR_NIX0 or BLKADDR_NIX1 */ int nix_blkaddr; }; @@ -580,6 +583,9 @@ void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq); /* RSS configuration APIs*/ int otx2_rss_init(struct otx2_nic *pfvf); +int otx2_set_flowkey_cfg(struct otx2_nic *pfvf); +void otx2_set_rss_key(struct otx2_nic *pfvf); +int otx2_set_rss_table(struct otx2_nic *pfvf); /* Mbox handlers */ void mbox_handler_msix_offset(struct otx2_nic *pfvf, diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c index b1f61e069ed8..60fcf82dd8cb 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c @@ -389,7 +389,253 @@ static int otx2_set_coalesce(struct net_device *netdev, return 0; } +static int otx2_get_rss_hash_opts(struct otx2_nic *pfvf, + struct ethtool_rxnfc *nfc) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + + if (!(rss->flowkey_cfg & + (NIX_FLOW_KEY_TYPE_IPV4 | NIX_FLOW_KEY_TYPE_IPV6))) + return 0; + + /* Mimimum is IPv4 and IPv6, SIP/DIP */ + nfc->data = RXH_IP_SRC | RXH_IP_DST; + + switch (nfc->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + if (rss->flowkey_cfg & NIX_FLOW_KEY_TYPE_TCP) + nfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; + case UDP_V4_FLOW: + case UDP_V6_FLOW: + if (rss->flowkey_cfg & NIX_FLOW_KEY_TYPE_UDP) + nfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; + case SCTP_V4_FLOW: + case SCTP_V6_FLOW: + if (rss->flowkey_cfg & NIX_FLOW_KEY_TYPE_SCTP) + nfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; + case AH_ESP_V4_FLOW: + case AH_V4_FLOW: + case ESP_V4_FLOW: + case IPV4_FLOW: + case AH_ESP_V6_FLOW: + case AH_V6_FLOW: + case ESP_V6_FLOW: + case IPV6_FLOW: + break; + default: + return -EINVAL; + } + return 0; +} + +static int otx2_set_rss_hash_opts(struct otx2_nic *pfvf, + struct ethtool_rxnfc *nfc) +{ + struct otx2_rss_info *rss = &pfvf->hw.rss_info; + u32 rxh_l4 = RXH_L4_B_0_1 | RXH_L4_B_2_3; + u32 rss_cfg = rss->flowkey_cfg; + + if (!rss->enable) { + netdev_err(pfvf->netdev, + "RSS is disabled, cannot change settings\n"); + return -EIO; + } + + /* Mimimum is IPv4 and IPv6, SIP/DIP */ + if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST)) + return -EINVAL; + + switch (nfc->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + /* Different config for v4 and v6 is not supported. + * Both of them have to be either 4-tuple or 2-tuple. + */ + switch (nfc->data & rxh_l4) { + case 0: + rss_cfg &= ~NIX_FLOW_KEY_TYPE_TCP; + break; + case (RXH_L4_B_0_1 | RXH_L4_B_2_3): + rss_cfg |= NIX_FLOW_KEY_TYPE_TCP; + break; + default: + return -EINVAL; + } + break; + case UDP_V4_FLOW: + case UDP_V6_FLOW: + switch (nfc->data & rxh_l4) { + case 0: + rss_cfg &= ~NIX_FLOW_KEY_TYPE_UDP; + break; + case (RXH_L4_B_0_1 | RXH_L4_B_2_3): + rss_cfg |= NIX_FLOW_KEY_TYPE_UDP; + break; + default: + return -EINVAL; + } + break; + case SCTP_V4_FLOW: + case SCTP_V6_FLOW: + switch (nfc->data & rxh_l4) { + case 0: + rss_cfg &= ~NIX_FLOW_KEY_TYPE_SCTP; + break; + case (RXH_L4_B_0_1 | RXH_L4_B_2_3): + rss_cfg |= NIX_FLOW_KEY_TYPE_SCTP; + break; + default: + return -EINVAL; + } + break; + case IPV4_FLOW: + case IPV6_FLOW: + rss_cfg = NIX_FLOW_KEY_TYPE_IPV4 | NIX_FLOW_KEY_TYPE_IPV6; + break; + default: + return -EINVAL; + } + + rss->flowkey_cfg = rss_cfg; + otx2_set_flowkey_cfg(pfvf); + return 0; +} + +static int otx2_get_rxnfc(struct net_device *dev, + struct ethtool_rxnfc *nfc, u32 *rules) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + switch (nfc->cmd) { + case ETHTOOL_GRXRINGS: + nfc->data = pfvf->hw.rx_queues; + ret = 0; + break; + case ETHTOOL_GRXFH: + return otx2_get_rss_hash_opts(pfvf, nfc); + default: + break; + } + return ret; +} + +static int otx2_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *nfc) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + switch (nfc->cmd) { + case ETHTOOL_SRXFH: + ret = otx2_set_rss_hash_opts(pfvf, nfc); + break; + default: + break; + } + + return ret; +} + +static u32 otx2_get_rxfh_key_size(struct net_device *netdev) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + struct otx2_rss_info *rss; + + rss = &pfvf->hw.rss_info; + + return sizeof(rss->key); +} + +static u32 otx2_get_rxfh_indir_size(struct net_device *dev) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + + return pfvf->hw.rss_info.rss_size; +} + +/* Get RSS configuration */ +static int otx2_get_rxfh(struct net_device *dev, u32 *indir, + u8 *hkey, u8 *hfunc) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + struct otx2_rss_info *rss; + int idx; + + rss = &pfvf->hw.rss_info; + + if (indir) { + for (idx = 0; idx < rss->rss_size; idx++) + indir[idx] = rss->ind_tbl[idx]; + } + + if (hkey) + memcpy(hkey, rss->key, sizeof(rss->key)); + + if (hfunc) + *hfunc = ETH_RSS_HASH_TOP; + + return 0; +} + +/* Configure RSS table and hash key */ +static int otx2_set_rxfh(struct net_device *dev, const u32 *indir, + const u8 *hkey, const u8 hfunc) +{ + struct otx2_nic *pfvf = netdev_priv(dev); + struct otx2_rss_info *rss; + int idx; + + if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) + return -EOPNOTSUPP; + + rss = &pfvf->hw.rss_info; + + if (!rss->enable) { + netdev_err(dev, "RSS is disabled, cannot change settings\n"); + return -EIO; + } + + if (indir) { + for (idx = 0; idx < rss->rss_size; idx++) + rss->ind_tbl[idx] = indir[idx]; + } + + if (hkey) { + memcpy(rss->key, hkey, sizeof(rss->key)); + otx2_set_rss_key(pfvf); + } + + otx2_set_rss_table(pfvf); + return 0; +} + +static u32 otx2_get_msglevel(struct net_device *netdev) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + return pfvf->msg_enable; +} + +static void otx2_set_msglevel(struct net_device *netdev, u32 val) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + pfvf->msg_enable = val; +} + +static u32 otx2_get_link(struct net_device *netdev) +{ + struct otx2_nic *pfvf = netdev_priv(netdev); + + return pfvf->linfo.link_up; +} + static const struct ethtool_ops otx2_ethtool_ops = { + .get_link = otx2_get_link, .get_drvinfo = otx2_get_drvinfo, .get_strings = otx2_get_strings, .get_ethtool_stats = otx2_get_ethtool_stats, @@ -400,6 +646,14 @@ static const struct ethtool_ops otx2_ethtool_ops = { .set_ringparam = otx2_set_ringparam, .get_coalesce = otx2_get_coalesce, .set_coalesce = otx2_set_coalesce, + .get_rxnfc = otx2_get_rxnfc, + .set_rxnfc = otx2_set_rxnfc, + .get_rxfh_key_size = otx2_get_rxfh_key_size, + .get_rxfh_indir_size = otx2_get_rxfh_indir_size, + .get_rxfh = otx2_get_rxfh, + .set_rxfh = otx2_set_rxfh, + .get_msglevel = otx2_get_msglevel, + .set_msglevel = otx2_set_msglevel, }; void otx2_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index 6a7ca3b4b9be..bef4c20fe314 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -84,7 +84,7 @@ static void otx2_snd_pkt_handler(struct otx2_nic *pfvf, struct sk_buff *skb = NULL; struct sg_list *sg; - if (unlikely(snd_comp->status)) + if (unlikely(snd_comp->status) && netif_msg_tx_err(pfvf)) net_err_ratelimited("%s: TX%d: Error in send CQ status:%x\n", pfvf->netdev->name, cq->cint_idx, snd_comp->status); @@ -144,6 +144,11 @@ static bool otx2_check_rcv_errors(struct otx2_nic *pfvf, struct otx2_drv_stats *stats = &pfvf->hw.drv_stats; struct nix_rx_parse_s *parse = &cqe->parse; + if (netif_msg_rx_err(pfvf)) + netdev_err(pfvf->netdev, + "RQ%d: Error pkt with errlev:0x%x errcode:0x%x\n", + qidx, parse->errlev, parse->errcode); + if (parse->errlev == NPC_ERRLVL_RE) { switch (parse->errcode) { case ERRCODE_FCS: -- cgit v1.2.1