mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-16 00:06:46 +07:00
0290bd291c
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 (<ARGV>) { 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 <mst@redhat.com> Acked-by: Heiner Kallweit <hkallweit1@gmail.com> Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com> Acked-by: Shannon Nelson <snelson@pensando.io> Reviewed-by: Martin Habets <mhabets@solarflare.com> 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 <davem@davemloft.net>
1351 lines
34 KiB
C
1351 lines
34 KiB
C
/* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver
|
|
*
|
|
* Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com)
|
|
*
|
|
* This program is dual-licensed; you may select either version 2 of
|
|
* the GNU General Public License ("GPL") or BSD license ("BSD").
|
|
*
|
|
* This Synopsys DWC XLGMAC software driver and associated documentation
|
|
* (hereinafter the "Software") is an unsupported proprietary work of
|
|
* Synopsys, Inc. unless otherwise expressly agreed to in writing between
|
|
* Synopsys and you. The Software IS NOT an item of Licensed Software or a
|
|
* Licensed Product under any End User Software License Agreement or
|
|
* Agreement for Licensed Products with Synopsys or any supplement thereto.
|
|
* Synopsys is a registered trademark of Synopsys, Inc. Other names included
|
|
* in the SOFTWARE may be the trademarks of their respective owners.
|
|
*/
|
|
|
|
#include <linux/netdevice.h>
|
|
#include <linux/tcp.h>
|
|
#include <linux/interrupt.h>
|
|
|
|
#include "dwc-xlgmac.h"
|
|
#include "dwc-xlgmac-reg.h"
|
|
|
|
static int xlgmac_one_poll(struct napi_struct *, int);
|
|
static int xlgmac_all_poll(struct napi_struct *, int);
|
|
|
|
static inline unsigned int xlgmac_tx_avail_desc(struct xlgmac_ring *ring)
|
|
{
|
|
return (ring->dma_desc_count - (ring->cur - ring->dirty));
|
|
}
|
|
|
|
static inline unsigned int xlgmac_rx_dirty_desc(struct xlgmac_ring *ring)
|
|
{
|
|
return (ring->cur - ring->dirty);
|
|
}
|
|
|
|
static int xlgmac_maybe_stop_tx_queue(
|
|
struct xlgmac_channel *channel,
|
|
struct xlgmac_ring *ring,
|
|
unsigned int count)
|
|
{
|
|
struct xlgmac_pdata *pdata = channel->pdata;
|
|
|
|
if (count > xlgmac_tx_avail_desc(ring)) {
|
|
netif_info(pdata, drv, pdata->netdev,
|
|
"Tx queue stopped, not enough descriptors available\n");
|
|
netif_stop_subqueue(pdata->netdev, channel->queue_index);
|
|
ring->tx.queue_stopped = 1;
|
|
|
|
/* If we haven't notified the hardware because of xmit_more
|
|
* support, tell it now
|
|
*/
|
|
if (ring->tx.xmit_more)
|
|
pdata->hw_ops.tx_start_xmit(channel, ring);
|
|
|
|
return NETDEV_TX_BUSY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void xlgmac_prep_vlan(struct sk_buff *skb,
|
|
struct xlgmac_pkt_info *pkt_info)
|
|
{
|
|
if (skb_vlan_tag_present(skb))
|
|
pkt_info->vlan_ctag = skb_vlan_tag_get(skb);
|
|
}
|
|
|
|
static int xlgmac_prep_tso(struct sk_buff *skb,
|
|
struct xlgmac_pkt_info *pkt_info)
|
|
{
|
|
int ret;
|
|
|
|
if (!XLGMAC_GET_REG_BITS(pkt_info->attributes,
|
|
TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS,
|
|
TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN))
|
|
return 0;
|
|
|
|
ret = skb_cow_head(skb, 0);
|
|
if (ret)
|
|
return ret;
|
|
|
|
pkt_info->header_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
|
|
pkt_info->tcp_header_len = tcp_hdrlen(skb);
|
|
pkt_info->tcp_payload_len = skb->len - pkt_info->header_len;
|
|
pkt_info->mss = skb_shinfo(skb)->gso_size;
|
|
|
|
XLGMAC_PR("header_len=%u\n", pkt_info->header_len);
|
|
XLGMAC_PR("tcp_header_len=%u, tcp_payload_len=%u\n",
|
|
pkt_info->tcp_header_len, pkt_info->tcp_payload_len);
|
|
XLGMAC_PR("mss=%u\n", pkt_info->mss);
|
|
|
|
/* Update the number of packets that will ultimately be transmitted
|
|
* along with the extra bytes for each extra packet
|
|
*/
|
|
pkt_info->tx_packets = skb_shinfo(skb)->gso_segs;
|
|
pkt_info->tx_bytes += (pkt_info->tx_packets - 1) * pkt_info->header_len;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int xlgmac_is_tso(struct sk_buff *skb)
|
|
{
|
|
if (skb->ip_summed != CHECKSUM_PARTIAL)
|
|
return 0;
|
|
|
|
if (!skb_is_gso(skb))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void xlgmac_prep_tx_pkt(struct xlgmac_pdata *pdata,
|
|
struct xlgmac_ring *ring,
|
|
struct sk_buff *skb,
|
|
struct xlgmac_pkt_info *pkt_info)
|
|
{
|
|
skb_frag_t *frag;
|
|
unsigned int context_desc;
|
|
unsigned int len;
|
|
unsigned int i;
|
|
|
|
pkt_info->skb = skb;
|
|
|
|
context_desc = 0;
|
|
pkt_info->desc_count = 0;
|
|
|
|
pkt_info->tx_packets = 1;
|
|
pkt_info->tx_bytes = skb->len;
|
|
|
|
if (xlgmac_is_tso(skb)) {
|
|
/* TSO requires an extra descriptor if mss is different */
|
|
if (skb_shinfo(skb)->gso_size != ring->tx.cur_mss) {
|
|
context_desc = 1;
|
|
pkt_info->desc_count++;
|
|
}
|
|
|
|
/* TSO requires an extra descriptor for TSO header */
|
|
pkt_info->desc_count++;
|
|
|
|
pkt_info->attributes = XLGMAC_SET_REG_BITS(
|
|
pkt_info->attributes,
|
|
TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS,
|
|
TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN,
|
|
1);
|
|
pkt_info->attributes = XLGMAC_SET_REG_BITS(
|
|
pkt_info->attributes,
|
|
TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS,
|
|
TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN,
|
|
1);
|
|
} else if (skb->ip_summed == CHECKSUM_PARTIAL)
|
|
pkt_info->attributes = XLGMAC_SET_REG_BITS(
|
|
pkt_info->attributes,
|
|
TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS,
|
|
TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN,
|
|
1);
|
|
|
|
if (skb_vlan_tag_present(skb)) {
|
|
/* VLAN requires an extra descriptor if tag is different */
|
|
if (skb_vlan_tag_get(skb) != ring->tx.cur_vlan_ctag)
|
|
/* We can share with the TSO context descriptor */
|
|
if (!context_desc) {
|
|
context_desc = 1;
|
|
pkt_info->desc_count++;
|
|
}
|
|
|
|
pkt_info->attributes = XLGMAC_SET_REG_BITS(
|
|
pkt_info->attributes,
|
|
TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS,
|
|
TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN,
|
|
1);
|
|
}
|
|
|
|
for (len = skb_headlen(skb); len;) {
|
|
pkt_info->desc_count++;
|
|
len -= min_t(unsigned int, len, XLGMAC_TX_MAX_BUF_SIZE);
|
|
}
|
|
|
|
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
|
frag = &skb_shinfo(skb)->frags[i];
|
|
for (len = skb_frag_size(frag); len; ) {
|
|
pkt_info->desc_count++;
|
|
len -= min_t(unsigned int, len, XLGMAC_TX_MAX_BUF_SIZE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int xlgmac_calc_rx_buf_size(struct net_device *netdev, unsigned int mtu)
|
|
{
|
|
unsigned int rx_buf_size;
|
|
|
|
if (mtu > XLGMAC_JUMBO_PACKET_MTU) {
|
|
netdev_alert(netdev, "MTU exceeds maximum supported value\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rx_buf_size = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
|
|
rx_buf_size = clamp_val(rx_buf_size, XLGMAC_RX_MIN_BUF_SIZE, PAGE_SIZE);
|
|
|
|
rx_buf_size = (rx_buf_size + XLGMAC_RX_BUF_ALIGN - 1) &
|
|
~(XLGMAC_RX_BUF_ALIGN - 1);
|
|
|
|
return rx_buf_size;
|
|
}
|
|
|
|
static void xlgmac_enable_rx_tx_ints(struct xlgmac_pdata *pdata)
|
|
{
|
|
struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
struct xlgmac_channel *channel;
|
|
enum xlgmac_int int_id;
|
|
unsigned int i;
|
|
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
|
if (channel->tx_ring && channel->rx_ring)
|
|
int_id = XLGMAC_INT_DMA_CH_SR_TI_RI;
|
|
else if (channel->tx_ring)
|
|
int_id = XLGMAC_INT_DMA_CH_SR_TI;
|
|
else if (channel->rx_ring)
|
|
int_id = XLGMAC_INT_DMA_CH_SR_RI;
|
|
else
|
|
continue;
|
|
|
|
hw_ops->enable_int(channel, int_id);
|
|
}
|
|
}
|
|
|
|
static void xlgmac_disable_rx_tx_ints(struct xlgmac_pdata *pdata)
|
|
{
|
|
struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
struct xlgmac_channel *channel;
|
|
enum xlgmac_int int_id;
|
|
unsigned int i;
|
|
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
|
if (channel->tx_ring && channel->rx_ring)
|
|
int_id = XLGMAC_INT_DMA_CH_SR_TI_RI;
|
|
else if (channel->tx_ring)
|
|
int_id = XLGMAC_INT_DMA_CH_SR_TI;
|
|
else if (channel->rx_ring)
|
|
int_id = XLGMAC_INT_DMA_CH_SR_RI;
|
|
else
|
|
continue;
|
|
|
|
hw_ops->disable_int(channel, int_id);
|
|
}
|
|
}
|
|
|
|
static irqreturn_t xlgmac_isr(int irq, void *data)
|
|
{
|
|
unsigned int dma_isr, dma_ch_isr, mac_isr;
|
|
struct xlgmac_pdata *pdata = data;
|
|
struct xlgmac_channel *channel;
|
|
struct xlgmac_hw_ops *hw_ops;
|
|
unsigned int i, ti, ri;
|
|
|
|
hw_ops = &pdata->hw_ops;
|
|
|
|
/* The DMA interrupt status register also reports MAC and MTL
|
|
* interrupts. So for polling mode, we just need to check for
|
|
* this register to be non-zero
|
|
*/
|
|
dma_isr = readl(pdata->mac_regs + DMA_ISR);
|
|
if (!dma_isr)
|
|
return IRQ_HANDLED;
|
|
|
|
netif_dbg(pdata, intr, pdata->netdev, "DMA_ISR=%#010x\n", dma_isr);
|
|
|
|
for (i = 0; i < pdata->channel_count; i++) {
|
|
if (!(dma_isr & (1 << i)))
|
|
continue;
|
|
|
|
channel = pdata->channel_head + i;
|
|
|
|
dma_ch_isr = readl(XLGMAC_DMA_REG(channel, DMA_CH_SR));
|
|
netif_dbg(pdata, intr, pdata->netdev, "DMA_CH%u_ISR=%#010x\n",
|
|
i, dma_ch_isr);
|
|
|
|
/* The TI or RI interrupt bits may still be set even if using
|
|
* per channel DMA interrupts. Check to be sure those are not
|
|
* enabled before using the private data napi structure.
|
|
*/
|
|
ti = XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TI_POS,
|
|
DMA_CH_SR_TI_LEN);
|
|
ri = XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RI_POS,
|
|
DMA_CH_SR_RI_LEN);
|
|
if (!pdata->per_channel_irq && (ti || ri)) {
|
|
if (napi_schedule_prep(&pdata->napi)) {
|
|
/* Disable Tx and Rx interrupts */
|
|
xlgmac_disable_rx_tx_ints(pdata);
|
|
|
|
pdata->stats.napi_poll_isr++;
|
|
/* Turn on polling */
|
|
__napi_schedule_irqoff(&pdata->napi);
|
|
}
|
|
}
|
|
|
|
if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TPS_POS,
|
|
DMA_CH_SR_TPS_LEN))
|
|
pdata->stats.tx_process_stopped++;
|
|
|
|
if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RPS_POS,
|
|
DMA_CH_SR_RPS_LEN))
|
|
pdata->stats.rx_process_stopped++;
|
|
|
|
if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TBU_POS,
|
|
DMA_CH_SR_TBU_LEN))
|
|
pdata->stats.tx_buffer_unavailable++;
|
|
|
|
if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RBU_POS,
|
|
DMA_CH_SR_RBU_LEN))
|
|
pdata->stats.rx_buffer_unavailable++;
|
|
|
|
/* Restart the device on a Fatal Bus Error */
|
|
if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_FBE_POS,
|
|
DMA_CH_SR_FBE_LEN)) {
|
|
pdata->stats.fatal_bus_error++;
|
|
schedule_work(&pdata->restart_work);
|
|
}
|
|
|
|
/* Clear all interrupt signals */
|
|
writel(dma_ch_isr, XLGMAC_DMA_REG(channel, DMA_CH_SR));
|
|
}
|
|
|
|
if (XLGMAC_GET_REG_BITS(dma_isr, DMA_ISR_MACIS_POS,
|
|
DMA_ISR_MACIS_LEN)) {
|
|
mac_isr = readl(pdata->mac_regs + MAC_ISR);
|
|
|
|
if (XLGMAC_GET_REG_BITS(mac_isr, MAC_ISR_MMCTXIS_POS,
|
|
MAC_ISR_MMCTXIS_LEN))
|
|
hw_ops->tx_mmc_int(pdata);
|
|
|
|
if (XLGMAC_GET_REG_BITS(mac_isr, MAC_ISR_MMCRXIS_POS,
|
|
MAC_ISR_MMCRXIS_LEN))
|
|
hw_ops->rx_mmc_int(pdata);
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t xlgmac_dma_isr(int irq, void *data)
|
|
{
|
|
struct xlgmac_channel *channel = data;
|
|
|
|
/* Per channel DMA interrupts are enabled, so we use the per
|
|
* channel napi structure and not the private data napi structure
|
|
*/
|
|
if (napi_schedule_prep(&channel->napi)) {
|
|
/* Disable Tx and Rx interrupts */
|
|
disable_irq_nosync(channel->dma_irq);
|
|
|
|
/* Turn on polling */
|
|
__napi_schedule_irqoff(&channel->napi);
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void xlgmac_tx_timer(struct timer_list *t)
|
|
{
|
|
struct xlgmac_channel *channel = from_timer(channel, t, tx_timer);
|
|
struct xlgmac_pdata *pdata = channel->pdata;
|
|
struct napi_struct *napi;
|
|
|
|
napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi;
|
|
|
|
if (napi_schedule_prep(napi)) {
|
|
/* Disable Tx and Rx interrupts */
|
|
if (pdata->per_channel_irq)
|
|
disable_irq_nosync(channel->dma_irq);
|
|
else
|
|
xlgmac_disable_rx_tx_ints(pdata);
|
|
|
|
pdata->stats.napi_poll_txtimer++;
|
|
/* Turn on polling */
|
|
__napi_schedule(napi);
|
|
}
|
|
|
|
channel->tx_timer_active = 0;
|
|
}
|
|
|
|
static void xlgmac_init_timers(struct xlgmac_pdata *pdata)
|
|
{
|
|
struct xlgmac_channel *channel;
|
|
unsigned int i;
|
|
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
|
if (!channel->tx_ring)
|
|
break;
|
|
|
|
timer_setup(&channel->tx_timer, xlgmac_tx_timer, 0);
|
|
}
|
|
}
|
|
|
|
static void xlgmac_stop_timers(struct xlgmac_pdata *pdata)
|
|
{
|
|
struct xlgmac_channel *channel;
|
|
unsigned int i;
|
|
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
|
if (!channel->tx_ring)
|
|
break;
|
|
|
|
del_timer_sync(&channel->tx_timer);
|
|
}
|
|
}
|
|
|
|
static void xlgmac_napi_enable(struct xlgmac_pdata *pdata, unsigned int add)
|
|
{
|
|
struct xlgmac_channel *channel;
|
|
unsigned int i;
|
|
|
|
if (pdata->per_channel_irq) {
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
|
if (add)
|
|
netif_napi_add(pdata->netdev, &channel->napi,
|
|
xlgmac_one_poll,
|
|
NAPI_POLL_WEIGHT);
|
|
|
|
napi_enable(&channel->napi);
|
|
}
|
|
} else {
|
|
if (add)
|
|
netif_napi_add(pdata->netdev, &pdata->napi,
|
|
xlgmac_all_poll, NAPI_POLL_WEIGHT);
|
|
|
|
napi_enable(&pdata->napi);
|
|
}
|
|
}
|
|
|
|
static void xlgmac_napi_disable(struct xlgmac_pdata *pdata, unsigned int del)
|
|
{
|
|
struct xlgmac_channel *channel;
|
|
unsigned int i;
|
|
|
|
if (pdata->per_channel_irq) {
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
|
napi_disable(&channel->napi);
|
|
|
|
if (del)
|
|
netif_napi_del(&channel->napi);
|
|
}
|
|
} else {
|
|
napi_disable(&pdata->napi);
|
|
|
|
if (del)
|
|
netif_napi_del(&pdata->napi);
|
|
}
|
|
}
|
|
|
|
static int xlgmac_request_irqs(struct xlgmac_pdata *pdata)
|
|
{
|
|
struct net_device *netdev = pdata->netdev;
|
|
struct xlgmac_channel *channel;
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
ret = devm_request_irq(pdata->dev, pdata->dev_irq, xlgmac_isr,
|
|
IRQF_SHARED, netdev->name, pdata);
|
|
if (ret) {
|
|
netdev_alert(netdev, "error requesting irq %d\n",
|
|
pdata->dev_irq);
|
|
return ret;
|
|
}
|
|
|
|
if (!pdata->per_channel_irq)
|
|
return 0;
|
|
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
|
snprintf(channel->dma_irq_name,
|
|
sizeof(channel->dma_irq_name) - 1,
|
|
"%s-TxRx-%u", netdev_name(netdev),
|
|
channel->queue_index);
|
|
|
|
ret = devm_request_irq(pdata->dev, channel->dma_irq,
|
|
xlgmac_dma_isr, 0,
|
|
channel->dma_irq_name, channel);
|
|
if (ret) {
|
|
netdev_alert(netdev, "error requesting irq %d\n",
|
|
channel->dma_irq);
|
|
goto err_irq;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_irq:
|
|
/* Using an unsigned int, 'i' will go to UINT_MAX and exit */
|
|
for (i--, channel--; i < pdata->channel_count; i--, channel--)
|
|
devm_free_irq(pdata->dev, channel->dma_irq, channel);
|
|
|
|
devm_free_irq(pdata->dev, pdata->dev_irq, pdata);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void xlgmac_free_irqs(struct xlgmac_pdata *pdata)
|
|
{
|
|
struct xlgmac_channel *channel;
|
|
unsigned int i;
|
|
|
|
devm_free_irq(pdata->dev, pdata->dev_irq, pdata);
|
|
|
|
if (!pdata->per_channel_irq)
|
|
return;
|
|
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++)
|
|
devm_free_irq(pdata->dev, channel->dma_irq, channel);
|
|
}
|
|
|
|
static void xlgmac_free_tx_data(struct xlgmac_pdata *pdata)
|
|
{
|
|
struct xlgmac_desc_ops *desc_ops = &pdata->desc_ops;
|
|
struct xlgmac_desc_data *desc_data;
|
|
struct xlgmac_channel *channel;
|
|
struct xlgmac_ring *ring;
|
|
unsigned int i, j;
|
|
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
|
ring = channel->tx_ring;
|
|
if (!ring)
|
|
break;
|
|
|
|
for (j = 0; j < ring->dma_desc_count; j++) {
|
|
desc_data = XLGMAC_GET_DESC_DATA(ring, j);
|
|
desc_ops->unmap_desc_data(pdata, desc_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void xlgmac_free_rx_data(struct xlgmac_pdata *pdata)
|
|
{
|
|
struct xlgmac_desc_ops *desc_ops = &pdata->desc_ops;
|
|
struct xlgmac_desc_data *desc_data;
|
|
struct xlgmac_channel *channel;
|
|
struct xlgmac_ring *ring;
|
|
unsigned int i, j;
|
|
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
|
ring = channel->rx_ring;
|
|
if (!ring)
|
|
break;
|
|
|
|
for (j = 0; j < ring->dma_desc_count; j++) {
|
|
desc_data = XLGMAC_GET_DESC_DATA(ring, j);
|
|
desc_ops->unmap_desc_data(pdata, desc_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int xlgmac_start(struct xlgmac_pdata *pdata)
|
|
{
|
|
struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
struct net_device *netdev = pdata->netdev;
|
|
int ret;
|
|
|
|
hw_ops->init(pdata);
|
|
xlgmac_napi_enable(pdata, 1);
|
|
|
|
ret = xlgmac_request_irqs(pdata);
|
|
if (ret)
|
|
goto err_napi;
|
|
|
|
hw_ops->enable_tx(pdata);
|
|
hw_ops->enable_rx(pdata);
|
|
netif_tx_start_all_queues(netdev);
|
|
|
|
return 0;
|
|
|
|
err_napi:
|
|
xlgmac_napi_disable(pdata, 1);
|
|
hw_ops->exit(pdata);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void xlgmac_stop(struct xlgmac_pdata *pdata)
|
|
{
|
|
struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
struct net_device *netdev = pdata->netdev;
|
|
struct xlgmac_channel *channel;
|
|
struct netdev_queue *txq;
|
|
unsigned int i;
|
|
|
|
netif_tx_stop_all_queues(netdev);
|
|
xlgmac_stop_timers(pdata);
|
|
hw_ops->disable_tx(pdata);
|
|
hw_ops->disable_rx(pdata);
|
|
xlgmac_free_irqs(pdata);
|
|
xlgmac_napi_disable(pdata, 1);
|
|
hw_ops->exit(pdata);
|
|
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
|
if (!channel->tx_ring)
|
|
continue;
|
|
|
|
txq = netdev_get_tx_queue(netdev, channel->queue_index);
|
|
netdev_tx_reset_queue(txq);
|
|
}
|
|
}
|
|
|
|
static void xlgmac_restart_dev(struct xlgmac_pdata *pdata)
|
|
{
|
|
/* If not running, "restart" will happen on open */
|
|
if (!netif_running(pdata->netdev))
|
|
return;
|
|
|
|
xlgmac_stop(pdata);
|
|
|
|
xlgmac_free_tx_data(pdata);
|
|
xlgmac_free_rx_data(pdata);
|
|
|
|
xlgmac_start(pdata);
|
|
}
|
|
|
|
static void xlgmac_restart(struct work_struct *work)
|
|
{
|
|
struct xlgmac_pdata *pdata = container_of(work,
|
|
struct xlgmac_pdata,
|
|
restart_work);
|
|
|
|
rtnl_lock();
|
|
|
|
xlgmac_restart_dev(pdata);
|
|
|
|
rtnl_unlock();
|
|
}
|
|
|
|
static int xlgmac_open(struct net_device *netdev)
|
|
{
|
|
struct xlgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct xlgmac_desc_ops *desc_ops;
|
|
int ret;
|
|
|
|
desc_ops = &pdata->desc_ops;
|
|
|
|
/* TODO: Initialize the phy */
|
|
|
|
/* Calculate the Rx buffer size before allocating rings */
|
|
ret = xlgmac_calc_rx_buf_size(netdev, netdev->mtu);
|
|
if (ret < 0)
|
|
return ret;
|
|
pdata->rx_buf_size = ret;
|
|
|
|
/* Allocate the channels and rings */
|
|
ret = desc_ops->alloc_channles_and_rings(pdata);
|
|
if (ret)
|
|
return ret;
|
|
|
|
INIT_WORK(&pdata->restart_work, xlgmac_restart);
|
|
xlgmac_init_timers(pdata);
|
|
|
|
ret = xlgmac_start(pdata);
|
|
if (ret)
|
|
goto err_channels_and_rings;
|
|
|
|
return 0;
|
|
|
|
err_channels_and_rings:
|
|
desc_ops->free_channels_and_rings(pdata);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int xlgmac_close(struct net_device *netdev)
|
|
{
|
|
struct xlgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct xlgmac_desc_ops *desc_ops;
|
|
|
|
desc_ops = &pdata->desc_ops;
|
|
|
|
/* Stop the device */
|
|
xlgmac_stop(pdata);
|
|
|
|
/* Free the channels and rings */
|
|
desc_ops->free_channels_and_rings(pdata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void xlgmac_tx_timeout(struct net_device *netdev, unsigned int txqueue)
|
|
{
|
|
struct xlgmac_pdata *pdata = netdev_priv(netdev);
|
|
|
|
netdev_warn(netdev, "tx timeout, device restarting\n");
|
|
schedule_work(&pdata->restart_work);
|
|
}
|
|
|
|
static int xlgmac_xmit(struct sk_buff *skb, struct net_device *netdev)
|
|
{
|
|
struct xlgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct xlgmac_pkt_info *tx_pkt_info;
|
|
struct xlgmac_desc_ops *desc_ops;
|
|
struct xlgmac_channel *channel;
|
|
struct xlgmac_hw_ops *hw_ops;
|
|
struct netdev_queue *txq;
|
|
struct xlgmac_ring *ring;
|
|
int ret;
|
|
|
|
desc_ops = &pdata->desc_ops;
|
|
hw_ops = &pdata->hw_ops;
|
|
|
|
XLGMAC_PR("skb->len = %d\n", skb->len);
|
|
|
|
channel = pdata->channel_head + skb->queue_mapping;
|
|
txq = netdev_get_tx_queue(netdev, channel->queue_index);
|
|
ring = channel->tx_ring;
|
|
tx_pkt_info = &ring->pkt_info;
|
|
|
|
if (skb->len == 0) {
|
|
netif_err(pdata, tx_err, netdev,
|
|
"empty skb received from stack\n");
|
|
dev_kfree_skb_any(skb);
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
/* Prepare preliminary packet info for TX */
|
|
memset(tx_pkt_info, 0, sizeof(*tx_pkt_info));
|
|
xlgmac_prep_tx_pkt(pdata, ring, skb, tx_pkt_info);
|
|
|
|
/* Check that there are enough descriptors available */
|
|
ret = xlgmac_maybe_stop_tx_queue(channel, ring,
|
|
tx_pkt_info->desc_count);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = xlgmac_prep_tso(skb, tx_pkt_info);
|
|
if (ret) {
|
|
netif_err(pdata, tx_err, netdev,
|
|
"error processing TSO packet\n");
|
|
dev_kfree_skb_any(skb);
|
|
return ret;
|
|
}
|
|
xlgmac_prep_vlan(skb, tx_pkt_info);
|
|
|
|
if (!desc_ops->map_tx_skb(channel, skb)) {
|
|
dev_kfree_skb_any(skb);
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
/* Report on the actual number of bytes (to be) sent */
|
|
netdev_tx_sent_queue(txq, tx_pkt_info->tx_bytes);
|
|
|
|
/* Configure required descriptor fields for transmission */
|
|
hw_ops->dev_xmit(channel);
|
|
|
|
if (netif_msg_pktdata(pdata))
|
|
xlgmac_print_pkt(netdev, skb, true);
|
|
|
|
/* Stop the queue in advance if there may not be enough descriptors */
|
|
xlgmac_maybe_stop_tx_queue(channel, ring, XLGMAC_TX_MAX_DESC_NR);
|
|
|
|
return NETDEV_TX_OK;
|
|
}
|
|
|
|
static void xlgmac_get_stats64(struct net_device *netdev,
|
|
struct rtnl_link_stats64 *s)
|
|
{
|
|
struct xlgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct xlgmac_stats *pstats = &pdata->stats;
|
|
|
|
pdata->hw_ops.read_mmc_stats(pdata);
|
|
|
|
s->rx_packets = pstats->rxframecount_gb;
|
|
s->rx_bytes = pstats->rxoctetcount_gb;
|
|
s->rx_errors = pstats->rxframecount_gb -
|
|
pstats->rxbroadcastframes_g -
|
|
pstats->rxmulticastframes_g -
|
|
pstats->rxunicastframes_g;
|
|
s->multicast = pstats->rxmulticastframes_g;
|
|
s->rx_length_errors = pstats->rxlengtherror;
|
|
s->rx_crc_errors = pstats->rxcrcerror;
|
|
s->rx_fifo_errors = pstats->rxfifooverflow;
|
|
|
|
s->tx_packets = pstats->txframecount_gb;
|
|
s->tx_bytes = pstats->txoctetcount_gb;
|
|
s->tx_errors = pstats->txframecount_gb - pstats->txframecount_g;
|
|
s->tx_dropped = netdev->stats.tx_dropped;
|
|
}
|
|
|
|
static int xlgmac_set_mac_address(struct net_device *netdev, void *addr)
|
|
{
|
|
struct xlgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
struct sockaddr *saddr = addr;
|
|
|
|
if (!is_valid_ether_addr(saddr->sa_data))
|
|
return -EADDRNOTAVAIL;
|
|
|
|
memcpy(netdev->dev_addr, saddr->sa_data, netdev->addr_len);
|
|
|
|
hw_ops->set_mac_address(pdata, netdev->dev_addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int xlgmac_ioctl(struct net_device *netdev,
|
|
struct ifreq *ifreq, int cmd)
|
|
{
|
|
if (!netif_running(netdev))
|
|
return -ENODEV;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int xlgmac_change_mtu(struct net_device *netdev, int mtu)
|
|
{
|
|
struct xlgmac_pdata *pdata = netdev_priv(netdev);
|
|
int ret;
|
|
|
|
ret = xlgmac_calc_rx_buf_size(netdev, mtu);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
pdata->rx_buf_size = ret;
|
|
netdev->mtu = mtu;
|
|
|
|
xlgmac_restart_dev(pdata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int xlgmac_vlan_rx_add_vid(struct net_device *netdev,
|
|
__be16 proto,
|
|
u16 vid)
|
|
{
|
|
struct xlgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
|
|
set_bit(vid, pdata->active_vlans);
|
|
hw_ops->update_vlan_hash_table(pdata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int xlgmac_vlan_rx_kill_vid(struct net_device *netdev,
|
|
__be16 proto,
|
|
u16 vid)
|
|
{
|
|
struct xlgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
|
|
clear_bit(vid, pdata->active_vlans);
|
|
hw_ops->update_vlan_hash_table(pdata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
static void xlgmac_poll_controller(struct net_device *netdev)
|
|
{
|
|
struct xlgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct xlgmac_channel *channel;
|
|
unsigned int i;
|
|
|
|
if (pdata->per_channel_irq) {
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++)
|
|
xlgmac_dma_isr(channel->dma_irq, channel);
|
|
} else {
|
|
disable_irq(pdata->dev_irq);
|
|
xlgmac_isr(pdata->dev_irq, pdata);
|
|
enable_irq(pdata->dev_irq);
|
|
}
|
|
}
|
|
#endif /* CONFIG_NET_POLL_CONTROLLER */
|
|
|
|
static int xlgmac_set_features(struct net_device *netdev,
|
|
netdev_features_t features)
|
|
{
|
|
netdev_features_t rxhash, rxcsum, rxvlan, rxvlan_filter;
|
|
struct xlgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
int ret = 0;
|
|
|
|
rxhash = pdata->netdev_features & NETIF_F_RXHASH;
|
|
rxcsum = pdata->netdev_features & NETIF_F_RXCSUM;
|
|
rxvlan = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_RX;
|
|
rxvlan_filter = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_FILTER;
|
|
|
|
if ((features & NETIF_F_RXHASH) && !rxhash)
|
|
ret = hw_ops->enable_rss(pdata);
|
|
else if (!(features & NETIF_F_RXHASH) && rxhash)
|
|
ret = hw_ops->disable_rss(pdata);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if ((features & NETIF_F_RXCSUM) && !rxcsum)
|
|
hw_ops->enable_rx_csum(pdata);
|
|
else if (!(features & NETIF_F_RXCSUM) && rxcsum)
|
|
hw_ops->disable_rx_csum(pdata);
|
|
|
|
if ((features & NETIF_F_HW_VLAN_CTAG_RX) && !rxvlan)
|
|
hw_ops->enable_rx_vlan_stripping(pdata);
|
|
else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) && rxvlan)
|
|
hw_ops->disable_rx_vlan_stripping(pdata);
|
|
|
|
if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && !rxvlan_filter)
|
|
hw_ops->enable_rx_vlan_filtering(pdata);
|
|
else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) && rxvlan_filter)
|
|
hw_ops->disable_rx_vlan_filtering(pdata);
|
|
|
|
pdata->netdev_features = features;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void xlgmac_set_rx_mode(struct net_device *netdev)
|
|
{
|
|
struct xlgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
|
|
hw_ops->config_rx_mode(pdata);
|
|
}
|
|
|
|
static const struct net_device_ops xlgmac_netdev_ops = {
|
|
.ndo_open = xlgmac_open,
|
|
.ndo_stop = xlgmac_close,
|
|
.ndo_start_xmit = xlgmac_xmit,
|
|
.ndo_tx_timeout = xlgmac_tx_timeout,
|
|
.ndo_get_stats64 = xlgmac_get_stats64,
|
|
.ndo_change_mtu = xlgmac_change_mtu,
|
|
.ndo_set_mac_address = xlgmac_set_mac_address,
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
.ndo_do_ioctl = xlgmac_ioctl,
|
|
.ndo_vlan_rx_add_vid = xlgmac_vlan_rx_add_vid,
|
|
.ndo_vlan_rx_kill_vid = xlgmac_vlan_rx_kill_vid,
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
.ndo_poll_controller = xlgmac_poll_controller,
|
|
#endif
|
|
.ndo_set_features = xlgmac_set_features,
|
|
.ndo_set_rx_mode = xlgmac_set_rx_mode,
|
|
};
|
|
|
|
const struct net_device_ops *xlgmac_get_netdev_ops(void)
|
|
{
|
|
return &xlgmac_netdev_ops;
|
|
}
|
|
|
|
static void xlgmac_rx_refresh(struct xlgmac_channel *channel)
|
|
{
|
|
struct xlgmac_pdata *pdata = channel->pdata;
|
|
struct xlgmac_ring *ring = channel->rx_ring;
|
|
struct xlgmac_desc_data *desc_data;
|
|
struct xlgmac_desc_ops *desc_ops;
|
|
struct xlgmac_hw_ops *hw_ops;
|
|
|
|
desc_ops = &pdata->desc_ops;
|
|
hw_ops = &pdata->hw_ops;
|
|
|
|
while (ring->dirty != ring->cur) {
|
|
desc_data = XLGMAC_GET_DESC_DATA(ring, ring->dirty);
|
|
|
|
/* Reset desc_data values */
|
|
desc_ops->unmap_desc_data(pdata, desc_data);
|
|
|
|
if (desc_ops->map_rx_buffer(pdata, ring, desc_data))
|
|
break;
|
|
|
|
hw_ops->rx_desc_reset(pdata, desc_data, ring->dirty);
|
|
|
|
ring->dirty++;
|
|
}
|
|
|
|
/* Make sure everything is written before the register write */
|
|
wmb();
|
|
|
|
/* Update the Rx Tail Pointer Register with address of
|
|
* the last cleaned entry
|
|
*/
|
|
desc_data = XLGMAC_GET_DESC_DATA(ring, ring->dirty - 1);
|
|
writel(lower_32_bits(desc_data->dma_desc_addr),
|
|
XLGMAC_DMA_REG(channel, DMA_CH_RDTR_LO));
|
|
}
|
|
|
|
static struct sk_buff *xlgmac_create_skb(struct xlgmac_pdata *pdata,
|
|
struct napi_struct *napi,
|
|
struct xlgmac_desc_data *desc_data,
|
|
unsigned int len)
|
|
{
|
|
unsigned int copy_len;
|
|
struct sk_buff *skb;
|
|
u8 *packet;
|
|
|
|
skb = napi_alloc_skb(napi, desc_data->rx.hdr.dma_len);
|
|
if (!skb)
|
|
return NULL;
|
|
|
|
/* Start with the header buffer which may contain just the header
|
|
* or the header plus data
|
|
*/
|
|
dma_sync_single_range_for_cpu(pdata->dev, desc_data->rx.hdr.dma_base,
|
|
desc_data->rx.hdr.dma_off,
|
|
desc_data->rx.hdr.dma_len,
|
|
DMA_FROM_DEVICE);
|
|
|
|
packet = page_address(desc_data->rx.hdr.pa.pages) +
|
|
desc_data->rx.hdr.pa.pages_offset;
|
|
copy_len = (desc_data->rx.hdr_len) ? desc_data->rx.hdr_len : len;
|
|
copy_len = min(desc_data->rx.hdr.dma_len, copy_len);
|
|
skb_copy_to_linear_data(skb, packet, copy_len);
|
|
skb_put(skb, copy_len);
|
|
|
|
len -= copy_len;
|
|
if (len) {
|
|
/* Add the remaining data as a frag */
|
|
dma_sync_single_range_for_cpu(pdata->dev,
|
|
desc_data->rx.buf.dma_base,
|
|
desc_data->rx.buf.dma_off,
|
|
desc_data->rx.buf.dma_len,
|
|
DMA_FROM_DEVICE);
|
|
|
|
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
|
|
desc_data->rx.buf.pa.pages,
|
|
desc_data->rx.buf.pa.pages_offset,
|
|
len, desc_data->rx.buf.dma_len);
|
|
desc_data->rx.buf.pa.pages = NULL;
|
|
}
|
|
|
|
return skb;
|
|
}
|
|
|
|
static int xlgmac_tx_poll(struct xlgmac_channel *channel)
|
|
{
|
|
struct xlgmac_pdata *pdata = channel->pdata;
|
|
struct xlgmac_ring *ring = channel->tx_ring;
|
|
struct net_device *netdev = pdata->netdev;
|
|
unsigned int tx_packets = 0, tx_bytes = 0;
|
|
struct xlgmac_desc_data *desc_data;
|
|
struct xlgmac_dma_desc *dma_desc;
|
|
struct xlgmac_desc_ops *desc_ops;
|
|
struct xlgmac_hw_ops *hw_ops;
|
|
struct netdev_queue *txq;
|
|
int processed = 0;
|
|
unsigned int cur;
|
|
|
|
desc_ops = &pdata->desc_ops;
|
|
hw_ops = &pdata->hw_ops;
|
|
|
|
/* Nothing to do if there isn't a Tx ring for this channel */
|
|
if (!ring)
|
|
return 0;
|
|
|
|
cur = ring->cur;
|
|
|
|
/* Be sure we get ring->cur before accessing descriptor data */
|
|
smp_rmb();
|
|
|
|
txq = netdev_get_tx_queue(netdev, channel->queue_index);
|
|
|
|
while ((processed < XLGMAC_TX_DESC_MAX_PROC) &&
|
|
(ring->dirty != cur)) {
|
|
desc_data = XLGMAC_GET_DESC_DATA(ring, ring->dirty);
|
|
dma_desc = desc_data->dma_desc;
|
|
|
|
if (!hw_ops->tx_complete(dma_desc))
|
|
break;
|
|
|
|
/* Make sure descriptor fields are read after reading
|
|
* the OWN bit
|
|
*/
|
|
dma_rmb();
|
|
|
|
if (netif_msg_tx_done(pdata))
|
|
xlgmac_dump_tx_desc(pdata, ring, ring->dirty, 1, 0);
|
|
|
|
if (hw_ops->is_last_desc(dma_desc)) {
|
|
tx_packets += desc_data->tx.packets;
|
|
tx_bytes += desc_data->tx.bytes;
|
|
}
|
|
|
|
/* Free the SKB and reset the descriptor for re-use */
|
|
desc_ops->unmap_desc_data(pdata, desc_data);
|
|
hw_ops->tx_desc_reset(desc_data);
|
|
|
|
processed++;
|
|
ring->dirty++;
|
|
}
|
|
|
|
if (!processed)
|
|
return 0;
|
|
|
|
netdev_tx_completed_queue(txq, tx_packets, tx_bytes);
|
|
|
|
if ((ring->tx.queue_stopped == 1) &&
|
|
(xlgmac_tx_avail_desc(ring) > XLGMAC_TX_DESC_MIN_FREE)) {
|
|
ring->tx.queue_stopped = 0;
|
|
netif_tx_wake_queue(txq);
|
|
}
|
|
|
|
XLGMAC_PR("processed=%d\n", processed);
|
|
|
|
return processed;
|
|
}
|
|
|
|
static int xlgmac_rx_poll(struct xlgmac_channel *channel, int budget)
|
|
{
|
|
struct xlgmac_pdata *pdata = channel->pdata;
|
|
struct xlgmac_ring *ring = channel->rx_ring;
|
|
struct net_device *netdev = pdata->netdev;
|
|
unsigned int len, dma_desc_len, max_len;
|
|
unsigned int context_next, context;
|
|
struct xlgmac_desc_data *desc_data;
|
|
struct xlgmac_pkt_info *pkt_info;
|
|
unsigned int incomplete, error;
|
|
struct xlgmac_hw_ops *hw_ops;
|
|
unsigned int received = 0;
|
|
struct napi_struct *napi;
|
|
struct sk_buff *skb;
|
|
int packet_count = 0;
|
|
|
|
hw_ops = &pdata->hw_ops;
|
|
|
|
/* Nothing to do if there isn't a Rx ring for this channel */
|
|
if (!ring)
|
|
return 0;
|
|
|
|
incomplete = 0;
|
|
context_next = 0;
|
|
|
|
napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi;
|
|
|
|
desc_data = XLGMAC_GET_DESC_DATA(ring, ring->cur);
|
|
pkt_info = &ring->pkt_info;
|
|
while (packet_count < budget) {
|
|
/* First time in loop see if we need to restore state */
|
|
if (!received && desc_data->state_saved) {
|
|
skb = desc_data->state.skb;
|
|
error = desc_data->state.error;
|
|
len = desc_data->state.len;
|
|
} else {
|
|
memset(pkt_info, 0, sizeof(*pkt_info));
|
|
skb = NULL;
|
|
error = 0;
|
|
len = 0;
|
|
}
|
|
|
|
read_again:
|
|
desc_data = XLGMAC_GET_DESC_DATA(ring, ring->cur);
|
|
|
|
if (xlgmac_rx_dirty_desc(ring) > XLGMAC_RX_DESC_MAX_DIRTY)
|
|
xlgmac_rx_refresh(channel);
|
|
|
|
if (hw_ops->dev_read(channel))
|
|
break;
|
|
|
|
received++;
|
|
ring->cur++;
|
|
|
|
incomplete = XLGMAC_GET_REG_BITS(
|
|
pkt_info->attributes,
|
|
RX_PACKET_ATTRIBUTES_INCOMPLETE_POS,
|
|
RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN);
|
|
context_next = XLGMAC_GET_REG_BITS(
|
|
pkt_info->attributes,
|
|
RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_POS,
|
|
RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_LEN);
|
|
context = XLGMAC_GET_REG_BITS(
|
|
pkt_info->attributes,
|
|
RX_PACKET_ATTRIBUTES_CONTEXT_POS,
|
|
RX_PACKET_ATTRIBUTES_CONTEXT_LEN);
|
|
|
|
/* Earlier error, just drain the remaining data */
|
|
if ((incomplete || context_next) && error)
|
|
goto read_again;
|
|
|
|
if (error || pkt_info->errors) {
|
|
if (pkt_info->errors)
|
|
netif_err(pdata, rx_err, netdev,
|
|
"error in received packet\n");
|
|
dev_kfree_skb(skb);
|
|
goto next_packet;
|
|
}
|
|
|
|
if (!context) {
|
|
/* Length is cumulative, get this descriptor's length */
|
|
dma_desc_len = desc_data->rx.len - len;
|
|
len += dma_desc_len;
|
|
|
|
if (dma_desc_len && !skb) {
|
|
skb = xlgmac_create_skb(pdata, napi, desc_data,
|
|
dma_desc_len);
|
|
if (!skb)
|
|
error = 1;
|
|
} else if (dma_desc_len) {
|
|
dma_sync_single_range_for_cpu(
|
|
pdata->dev,
|
|
desc_data->rx.buf.dma_base,
|
|
desc_data->rx.buf.dma_off,
|
|
desc_data->rx.buf.dma_len,
|
|
DMA_FROM_DEVICE);
|
|
|
|
skb_add_rx_frag(
|
|
skb, skb_shinfo(skb)->nr_frags,
|
|
desc_data->rx.buf.pa.pages,
|
|
desc_data->rx.buf.pa.pages_offset,
|
|
dma_desc_len,
|
|
desc_data->rx.buf.dma_len);
|
|
desc_data->rx.buf.pa.pages = NULL;
|
|
}
|
|
}
|
|
|
|
if (incomplete || context_next)
|
|
goto read_again;
|
|
|
|
if (!skb)
|
|
goto next_packet;
|
|
|
|
/* Be sure we don't exceed the configured MTU */
|
|
max_len = netdev->mtu + ETH_HLEN;
|
|
if (!(netdev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
|
|
(skb->protocol == htons(ETH_P_8021Q)))
|
|
max_len += VLAN_HLEN;
|
|
|
|
if (skb->len > max_len) {
|
|
netif_err(pdata, rx_err, netdev,
|
|
"packet length exceeds configured MTU\n");
|
|
dev_kfree_skb(skb);
|
|
goto next_packet;
|
|
}
|
|
|
|
if (netif_msg_pktdata(pdata))
|
|
xlgmac_print_pkt(netdev, skb, false);
|
|
|
|
skb_checksum_none_assert(skb);
|
|
if (XLGMAC_GET_REG_BITS(pkt_info->attributes,
|
|
RX_PACKET_ATTRIBUTES_CSUM_DONE_POS,
|
|
RX_PACKET_ATTRIBUTES_CSUM_DONE_LEN))
|
|
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
|
|
if (XLGMAC_GET_REG_BITS(pkt_info->attributes,
|
|
RX_PACKET_ATTRIBUTES_VLAN_CTAG_POS,
|
|
RX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN)) {
|
|
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
|
|
pkt_info->vlan_ctag);
|
|
pdata->stats.rx_vlan_packets++;
|
|
}
|
|
|
|
if (XLGMAC_GET_REG_BITS(pkt_info->attributes,
|
|
RX_PACKET_ATTRIBUTES_RSS_HASH_POS,
|
|
RX_PACKET_ATTRIBUTES_RSS_HASH_LEN))
|
|
skb_set_hash(skb, pkt_info->rss_hash,
|
|
pkt_info->rss_hash_type);
|
|
|
|
skb->dev = netdev;
|
|
skb->protocol = eth_type_trans(skb, netdev);
|
|
skb_record_rx_queue(skb, channel->queue_index);
|
|
|
|
napi_gro_receive(napi, skb);
|
|
|
|
next_packet:
|
|
packet_count++;
|
|
}
|
|
|
|
/* Check if we need to save state before leaving */
|
|
if (received && (incomplete || context_next)) {
|
|
desc_data = XLGMAC_GET_DESC_DATA(ring, ring->cur);
|
|
desc_data->state_saved = 1;
|
|
desc_data->state.skb = skb;
|
|
desc_data->state.len = len;
|
|
desc_data->state.error = error;
|
|
}
|
|
|
|
XLGMAC_PR("packet_count = %d\n", packet_count);
|
|
|
|
return packet_count;
|
|
}
|
|
|
|
static int xlgmac_one_poll(struct napi_struct *napi, int budget)
|
|
{
|
|
struct xlgmac_channel *channel = container_of(napi,
|
|
struct xlgmac_channel,
|
|
napi);
|
|
int processed = 0;
|
|
|
|
XLGMAC_PR("budget=%d\n", budget);
|
|
|
|
/* Cleanup Tx ring first */
|
|
xlgmac_tx_poll(channel);
|
|
|
|
/* Process Rx ring next */
|
|
processed = xlgmac_rx_poll(channel, budget);
|
|
|
|
/* If we processed everything, we are done */
|
|
if (processed < budget) {
|
|
/* Turn off polling */
|
|
napi_complete_done(napi, processed);
|
|
|
|
/* Enable Tx and Rx interrupts */
|
|
enable_irq(channel->dma_irq);
|
|
}
|
|
|
|
XLGMAC_PR("received = %d\n", processed);
|
|
|
|
return processed;
|
|
}
|
|
|
|
static int xlgmac_all_poll(struct napi_struct *napi, int budget)
|
|
{
|
|
struct xlgmac_pdata *pdata = container_of(napi,
|
|
struct xlgmac_pdata,
|
|
napi);
|
|
struct xlgmac_channel *channel;
|
|
int processed, last_processed;
|
|
int ring_budget;
|
|
unsigned int i;
|
|
|
|
XLGMAC_PR("budget=%d\n", budget);
|
|
|
|
processed = 0;
|
|
ring_budget = budget / pdata->rx_ring_count;
|
|
do {
|
|
last_processed = processed;
|
|
|
|
channel = pdata->channel_head;
|
|
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
|
/* Cleanup Tx ring first */
|
|
xlgmac_tx_poll(channel);
|
|
|
|
/* Process Rx ring next */
|
|
if (ring_budget > (budget - processed))
|
|
ring_budget = budget - processed;
|
|
processed += xlgmac_rx_poll(channel, ring_budget);
|
|
}
|
|
} while ((processed < budget) && (processed != last_processed));
|
|
|
|
/* If we processed everything, we are done */
|
|
if (processed < budget) {
|
|
/* Turn off polling */
|
|
napi_complete_done(napi, processed);
|
|
|
|
/* Enable Tx and Rx interrupts */
|
|
xlgmac_enable_rx_tx_ints(pdata);
|
|
}
|
|
|
|
XLGMAC_PR("received = %d\n", processed);
|
|
|
|
return processed;
|
|
}
|