diff mbox series

[net-next,v3] net: axienet: Use NAPI for TX completion path

Message ID 20220505203754.1905881-1-robert.hancock@calian.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series [net-next,v3] net: axienet: Use NAPI for TX completion path | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 2 this patch: 3
netdev/cc_maintainers warning 1 maintainers not CCed: linux@armlinux.org.uk
netdev/build_clang fail Errors and warnings before: 0 this patch: 1
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 2 this patch: 3
netdev/checkpatch warning WARNING: Duplicate signature WARNING: line length of 81 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns
netdev/kdoc fail Errors and warnings before: 2 this patch: 3
netdev/source_inline success Was 0 now: 0

Commit Message

Robert Hancock May 5, 2022, 8:37 p.m. UTC
This driver was using the TX IRQ handler to perform all TX completion
tasks. Under heavy TX network load, this can cause significant irqs-off
latencies (found to be in the hundreds of microseconds using ftrace).
This can cause other issues, such as overrunning serial UART FIFOs when
using high baud rates with limited UART FIFO sizes.

Switch to using a NAPI poll handler to perform the TX completion work
to get this out of hard IRQ context and avoid the IRQ latency impact.
A separate poll handler is used for TX and RX since they have separate
IRQs on this controller, so that the completion work for each of them
stays on the same CPU as the interrupt.

Testing on a Xilinx MPSoC ZU9EG platform using iperf3 from a Linux PC
through a switch at 1G link speed showed no significant change in TX or
RX throughput, with approximately 941 Mbps before and after. Hard IRQ
time in the TX throughput test was significantly reduced from 12% to
below 1% on the CPU handling TX interrupts, with total hard+soft IRQ CPU
usage dropping from about 56% down to 48%.

Signed-off-by: Robert Hancock <robert.hancock@calian.com>
---

Changed since v2: Use separate TX and RX NAPI poll handlers to keep
completion handling on same CPU as TX/RX IRQ. Added hard/soft IRQ
benchmark information to commit message.

Changed since v1: Added benchmark information to commit message, no
code changes.

 drivers/net/ethernet/xilinx/xilinx_axienet.h  |  53 +++----
 .../net/ethernet/xilinx/xilinx_axienet_main.c | 130 +++++++++++-------
 2 files changed, 105 insertions(+), 78 deletions(-)

Comments

kernel test robot May 5, 2022, 11:48 p.m. UTC | #1
Hi Robert,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net-next/master]

url:    https://github.com/intel-lab-lkp/linux/commits/Robert-Hancock/net-axienet-Use-NAPI-for-TX-completion-path/20220506-043932
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 1c1ed5a48411e1686997157c21633653fbe045c6
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20220506/202205060706.r3focpXn-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 11.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/89eb9e692229a3af81eaf2f599781bfae3091e93
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Robert-Hancock/net-axienet-Use-NAPI-for-TX-completion-path/20220506-043932
        git checkout 89eb9e692229a3af81eaf2f599781bfae3091e93
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=m68k SHELL=/bin/bash drivers/net/ethernet/xilinx/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/net/ethernet/xilinx/xilinx_axienet_main.c:765: warning: expecting prototype for axienet_start_xmit_done(). Prototype was for axienet_tx_poll() instead


vim +765 drivers/net/ethernet/xilinx/xilinx_axienet_main.c

bb193e3db8b86a Robert Hancock  2022-01-18  749  
ab365c3393664f Andre Przywara  2020-03-24  750  /**
ab365c3393664f Andre Przywara  2020-03-24  751   * axienet_start_xmit_done - Invoked once a transmit is completed by the
ab365c3393664f Andre Przywara  2020-03-24  752   * Axi DMA Tx channel.
89eb9e692229a3 Robert Hancock  2022-05-05  753   * @napi:	Pointer to NAPI structure.
89eb9e692229a3 Robert Hancock  2022-05-05  754   * @budget:	Max number of TX packets to process.
89eb9e692229a3 Robert Hancock  2022-05-05  755   *
89eb9e692229a3 Robert Hancock  2022-05-05  756   * Return: Number of TX packets processed.
ab365c3393664f Andre Przywara  2020-03-24  757   *
89eb9e692229a3 Robert Hancock  2022-05-05  758   * This function is invoked from the NAPI processing to notify the completion
ab365c3393664f Andre Przywara  2020-03-24  759   * of transmit operation. It clears fields in the corresponding Tx BDs and
ab365c3393664f Andre Przywara  2020-03-24  760   * unmaps the corresponding buffer so that CPU can regain ownership of the
ab365c3393664f Andre Przywara  2020-03-24  761   * buffer. It finally invokes "netif_wake_queue" to restart transmission if
ab365c3393664f Andre Przywara  2020-03-24  762   * required.
ab365c3393664f Andre Przywara  2020-03-24  763   */
89eb9e692229a3 Robert Hancock  2022-05-05  764  static int axienet_tx_poll(struct napi_struct *napi, int budget)
ab365c3393664f Andre Przywara  2020-03-24 @765  {
89eb9e692229a3 Robert Hancock  2022-05-05  766  	struct axienet_local *lp = container_of(napi, struct axienet_local, napi_tx);
89eb9e692229a3 Robert Hancock  2022-05-05  767  	struct net_device *ndev = lp->ndev;
ab365c3393664f Andre Przywara  2020-03-24  768  	u32 size = 0;
89eb9e692229a3 Robert Hancock  2022-05-05  769  	int packets;
ab365c3393664f Andre Przywara  2020-03-24  770  
89eb9e692229a3 Robert Hancock  2022-05-05  771  	packets = axienet_free_tx_chain(lp, lp->tx_bd_ci, budget, false, &size, budget);
ab365c3393664f Andre Przywara  2020-03-24  772  
89eb9e692229a3 Robert Hancock  2022-05-05  773  	if (packets) {
ab365c3393664f Andre Przywara  2020-03-24  774  		lp->tx_bd_ci += packets;
ab365c3393664f Andre Przywara  2020-03-24  775  		if (lp->tx_bd_ci >= lp->tx_bd_num)
89eb9e692229a3 Robert Hancock  2022-05-05  776  			lp->tx_bd_ci %= lp->tx_bd_num;
ab365c3393664f Andre Przywara  2020-03-24  777  
8a3b7a252dca9f Daniel Borkmann 2012-01-19  778  		ndev->stats.tx_packets += packets;
8a3b7a252dca9f Daniel Borkmann 2012-01-19  779  		ndev->stats.tx_bytes += size;
7de44285c1f69c Robert Hancock  2019-06-06  780  
7de44285c1f69c Robert Hancock  2019-06-06  781  		/* Matches barrier in axienet_start_xmit */
7de44285c1f69c Robert Hancock  2019-06-06  782  		smp_mb();
7de44285c1f69c Robert Hancock  2019-06-06  783  
bb193e3db8b86a Robert Hancock  2022-01-18  784  		if (!axienet_check_tx_bd_space(lp, MAX_SKB_FRAGS + 1))
8a3b7a252dca9f Daniel Borkmann 2012-01-19  785  			netif_wake_queue(ndev);
8a3b7a252dca9f Daniel Borkmann 2012-01-19  786  	}
8a3b7a252dca9f Daniel Borkmann 2012-01-19  787  
89eb9e692229a3 Robert Hancock  2022-05-05  788  	if (packets < budget && napi_complete_done(napi, packets)) {
89eb9e692229a3 Robert Hancock  2022-05-05  789  		/* Re-enable TX completion interrupts. This should
89eb9e692229a3 Robert Hancock  2022-05-05  790  		 * cause an immediate interrupt if any TX packets are
89eb9e692229a3 Robert Hancock  2022-05-05  791  		 * already pending.
89eb9e692229a3 Robert Hancock  2022-05-05  792  		 */
89eb9e692229a3 Robert Hancock  2022-05-05  793  		axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr);
89eb9e692229a3 Robert Hancock  2022-05-05  794  	}
89eb9e692229a3 Robert Hancock  2022-05-05  795  	return packets;
89eb9e692229a3 Robert Hancock  2022-05-05  796  }
89eb9e692229a3 Robert Hancock  2022-05-05  797
kernel test robot May 6, 2022, 1:54 a.m. UTC | #2
Hi Robert,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net-next/master]

url:    https://github.com/intel-lab-lkp/linux/commits/Robert-Hancock/net-axienet-Use-NAPI-for-TX-completion-path/20220506-043932
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git 1c1ed5a48411e1686997157c21633653fbe045c6
config: x86_64-randconfig-c007 (https://download.01.org/0day-ci/archive/20220506/202205060910.pO104P2e-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project 5e004fb787698440a387750db7f8028e7cb14cfc)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/89eb9e692229a3af81eaf2f599781bfae3091e93
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Robert-Hancock/net-axienet-Use-NAPI-for-TX-completion-path/20220506-043932
        git checkout 89eb9e692229a3af81eaf2f599781bfae3091e93
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/net/ethernet/xilinx/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/net/ethernet/xilinx/xilinx_axienet_main.c:765: warning: expecting prototype for axienet_start_xmit_done(). Prototype was for axienet_tx_poll() instead


vim +765 drivers/net/ethernet/xilinx/xilinx_axienet_main.c

bb193e3db8b86a Robert Hancock  2022-01-18  749  
ab365c3393664f Andre Przywara  2020-03-24  750  /**
ab365c3393664f Andre Przywara  2020-03-24  751   * axienet_start_xmit_done - Invoked once a transmit is completed by the
ab365c3393664f Andre Przywara  2020-03-24  752   * Axi DMA Tx channel.
89eb9e692229a3 Robert Hancock  2022-05-05  753   * @napi:	Pointer to NAPI structure.
89eb9e692229a3 Robert Hancock  2022-05-05  754   * @budget:	Max number of TX packets to process.
89eb9e692229a3 Robert Hancock  2022-05-05  755   *
89eb9e692229a3 Robert Hancock  2022-05-05  756   * Return: Number of TX packets processed.
ab365c3393664f Andre Przywara  2020-03-24  757   *
89eb9e692229a3 Robert Hancock  2022-05-05  758   * This function is invoked from the NAPI processing to notify the completion
ab365c3393664f Andre Przywara  2020-03-24  759   * of transmit operation. It clears fields in the corresponding Tx BDs and
ab365c3393664f Andre Przywara  2020-03-24  760   * unmaps the corresponding buffer so that CPU can regain ownership of the
ab365c3393664f Andre Przywara  2020-03-24  761   * buffer. It finally invokes "netif_wake_queue" to restart transmission if
ab365c3393664f Andre Przywara  2020-03-24  762   * required.
ab365c3393664f Andre Przywara  2020-03-24  763   */
89eb9e692229a3 Robert Hancock  2022-05-05  764  static int axienet_tx_poll(struct napi_struct *napi, int budget)
ab365c3393664f Andre Przywara  2020-03-24 @765  {
89eb9e692229a3 Robert Hancock  2022-05-05  766  	struct axienet_local *lp = container_of(napi, struct axienet_local, napi_tx);
89eb9e692229a3 Robert Hancock  2022-05-05  767  	struct net_device *ndev = lp->ndev;
ab365c3393664f Andre Przywara  2020-03-24  768  	u32 size = 0;
89eb9e692229a3 Robert Hancock  2022-05-05  769  	int packets;
ab365c3393664f Andre Przywara  2020-03-24  770  
89eb9e692229a3 Robert Hancock  2022-05-05  771  	packets = axienet_free_tx_chain(lp, lp->tx_bd_ci, budget, false, &size, budget);
ab365c3393664f Andre Przywara  2020-03-24  772  
89eb9e692229a3 Robert Hancock  2022-05-05  773  	if (packets) {
ab365c3393664f Andre Przywara  2020-03-24  774  		lp->tx_bd_ci += packets;
ab365c3393664f Andre Przywara  2020-03-24  775  		if (lp->tx_bd_ci >= lp->tx_bd_num)
89eb9e692229a3 Robert Hancock  2022-05-05  776  			lp->tx_bd_ci %= lp->tx_bd_num;
ab365c3393664f Andre Przywara  2020-03-24  777  
8a3b7a252dca9f Daniel Borkmann 2012-01-19  778  		ndev->stats.tx_packets += packets;
8a3b7a252dca9f Daniel Borkmann 2012-01-19  779  		ndev->stats.tx_bytes += size;
7de44285c1f69c Robert Hancock  2019-06-06  780  
7de44285c1f69c Robert Hancock  2019-06-06  781  		/* Matches barrier in axienet_start_xmit */
7de44285c1f69c Robert Hancock  2019-06-06  782  		smp_mb();
7de44285c1f69c Robert Hancock  2019-06-06  783  
bb193e3db8b86a Robert Hancock  2022-01-18  784  		if (!axienet_check_tx_bd_space(lp, MAX_SKB_FRAGS + 1))
8a3b7a252dca9f Daniel Borkmann 2012-01-19  785  			netif_wake_queue(ndev);
8a3b7a252dca9f Daniel Borkmann 2012-01-19  786  	}
8a3b7a252dca9f Daniel Borkmann 2012-01-19  787  
89eb9e692229a3 Robert Hancock  2022-05-05  788  	if (packets < budget && napi_complete_done(napi, packets)) {
89eb9e692229a3 Robert Hancock  2022-05-05  789  		/* Re-enable TX completion interrupts. This should
89eb9e692229a3 Robert Hancock  2022-05-05  790  		 * cause an immediate interrupt if any TX packets are
89eb9e692229a3 Robert Hancock  2022-05-05  791  		 * already pending.
89eb9e692229a3 Robert Hancock  2022-05-05  792  		 */
89eb9e692229a3 Robert Hancock  2022-05-05  793  		axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr);
89eb9e692229a3 Robert Hancock  2022-05-05  794  	}
89eb9e692229a3 Robert Hancock  2022-05-05  795  	return packets;
89eb9e692229a3 Robert Hancock  2022-05-05  796  }
89eb9e692229a3 Robert Hancock  2022-05-05  797
diff mbox series

Patch

diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index d5c1e5c4a508..76fdaac13146 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -385,7 +385,6 @@  struct axidma_bd {
  * @phy_node:	Pointer to device node structure
  * @phylink:	Pointer to phylink instance
  * @phylink_config: phylink configuration settings
- * @napi:	NAPI control structure
  * @pcs_phy:	Reference to PCS/PMA PHY if used
  * @pcs:	phylink pcs structure for PCS PHY
  * @switch_x_sgmii: Whether switchable 1000BaseX/SGMII mode is enabled in the core
@@ -396,27 +395,30 @@  struct axidma_bd {
  * @regs_start: Resource start for axienet device addresses
  * @regs:	Base address for the axienet_local device address space
  * @dma_regs:	Base address for the axidma device address space
+ * @napi_rx:	NAPI RX control structure
  * @rx_dma_cr:  Nominal content of RX DMA control register
- * @dma_err_task: Work structure to process Axi DMA errors
- * @tx_irq:	Axidma TX IRQ number
- * @rx_irq:	Axidma RX IRQ number
- * @eth_irq:	Ethernet core IRQ number
- * @phy_mode:	Phy type to identify between MII/GMII/RGMII/SGMII/1000 Base-X
- * @options:	AxiEthernet option word
- * @features:	Stores the extended features supported by the axienet hw
- * @tx_bd_v:	Virtual address of the TX buffer descriptor ring
- * @tx_bd_p:	Physical address(start address) of the TX buffer descr. ring
- * @tx_bd_num:	Size of TX buffer descriptor ring
  * @rx_bd_v:	Virtual address of the RX buffer descriptor ring
  * @rx_bd_p:	Physical address(start address) of the RX buffer descr. ring
  * @rx_bd_num:	Size of RX buffer descriptor ring
+ * @rx_bd_ci:	Stores the index of the Rx buffer descriptor in the ring being
+ *		accessed currently.
+ * @napi_tx:	NAPI TX control structure
+ * @tx_dma_cr:  Nominal content of TX DMA control register
+ * @tx_bd_v:	Virtual address of the TX buffer descriptor ring
+ * @tx_bd_p:	Physical address(start address) of the TX buffer descr. ring
+ * @tx_bd_num:	Size of TX buffer descriptor ring
  * @tx_bd_ci:	Stores the index of the Tx buffer descriptor in the ring being
  *		accessed currently. Used while alloc. BDs before a TX starts
  * @tx_bd_tail:	Stores the index of the Tx buffer descriptor in the ring being
  *		accessed currently. Used while processing BDs after the TX
  *		completed.
- * @rx_bd_ci:	Stores the index of the Rx buffer descriptor in the ring being
- *		accessed currently.
+ * @dma_err_task: Work structure to process Axi DMA errors
+ * @tx_irq:	Axidma TX IRQ number
+ * @rx_irq:	Axidma RX IRQ number
+ * @eth_irq:	Ethernet core IRQ number
+ * @phy_mode:	Phy type to identify between MII/GMII/RGMII/SGMII/1000 Base-X
+ * @options:	AxiEthernet option word
+ * @features:	Stores the extended features supported by the axienet hw
  * @max_frm_size: Stores the maximum size of the frame that can be that
  *		  Txed/Rxed in the existing hardware. If jumbo option is
  *		  supported, the maximum frame size would be 9k. Else it is
@@ -436,8 +438,6 @@  struct axienet_local {
 	struct phylink *phylink;
 	struct phylink_config phylink_config;
 
-	struct napi_struct napi;
-
 	struct mdio_device *pcs_phy;
 	struct phylink_pcs pcs;
 
@@ -453,7 +453,20 @@  struct axienet_local {
 	void __iomem *regs;
 	void __iomem *dma_regs;
 
+	struct napi_struct napi_rx;
 	u32 rx_dma_cr;
+	struct axidma_bd *rx_bd_v;
+	dma_addr_t rx_bd_p;
+	u32 rx_bd_num;
+	u32 rx_bd_ci;
+
+	struct napi_struct napi_tx;
+	u32 tx_dma_cr;
+	struct axidma_bd *tx_bd_v;
+	dma_addr_t tx_bd_p;
+	u32 tx_bd_num;
+	u32 tx_bd_ci;
+	u32 tx_bd_tail;
 
 	struct work_struct dma_err_task;
 
@@ -465,16 +478,6 @@  struct axienet_local {
 	u32 options;
 	u32 features;
 
-	struct axidma_bd *tx_bd_v;
-	dma_addr_t tx_bd_p;
-	u32 tx_bd_num;
-	struct axidma_bd *rx_bd_v;
-	dma_addr_t rx_bd_p;
-	u32 rx_bd_num;
-	u32 tx_bd_ci;
-	u32 tx_bd_tail;
-	u32 rx_bd_ci;
-
 	u32 max_frm_size;
 	u32 rxmem;
 
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index d6fc3f7acdf0..a68856fc881c 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -254,8 +254,6 @@  static u32 axienet_usec_to_timer(struct axienet_local *lp, u32 coalesce_usec)
  */
 static void axienet_dma_start(struct axienet_local *lp)
 {
-	u32 tx_cr;
-
 	/* Start updating the Rx channel control register */
 	lp->rx_dma_cr = (lp->coalesce_count_rx << XAXIDMA_COALESCE_SHIFT) |
 			XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_ERROR_MASK;
@@ -269,16 +267,16 @@  static void axienet_dma_start(struct axienet_local *lp)
 	axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, lp->rx_dma_cr);
 
 	/* Start updating the Tx channel control register */
-	tx_cr = (lp->coalesce_count_tx << XAXIDMA_COALESCE_SHIFT) |
-		XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_ERROR_MASK;
+	lp->tx_dma_cr = (lp->coalesce_count_tx << XAXIDMA_COALESCE_SHIFT) |
+			XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_ERROR_MASK;
 	/* Only set interrupt delay timer if not generating an interrupt on
 	 * the first TX packet. Otherwise leave at 0 to disable delay interrupt.
 	 */
 	if (lp->coalesce_count_tx > 1)
-		tx_cr |= (axienet_usec_to_timer(lp, lp->coalesce_usec_tx)
-				<< XAXIDMA_DELAY_SHIFT) |
-			 XAXIDMA_IRQ_DELAY_MASK;
-	axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, tx_cr);
+		lp->tx_dma_cr |= (axienet_usec_to_timer(lp, lp->coalesce_usec_tx)
+					<< XAXIDMA_DELAY_SHIFT) |
+				 XAXIDMA_IRQ_DELAY_MASK;
+	axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr);
 
 	/* Populate the tail pointer and bring the Rx Axi DMA engine out of
 	 * halted state. This will make the Rx side ready for reception.
@@ -294,8 +292,8 @@  static void axienet_dma_start(struct axienet_local *lp)
 	 * tail pointer register that the Tx channel will start transmitting.
 	 */
 	axienet_dma_out_addr(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p);
-	tx_cr |= XAXIDMA_CR_RUNSTOP_MASK;
-	axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, tx_cr);
+	lp->tx_dma_cr |= XAXIDMA_CR_RUNSTOP_MASK;
+	axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr);
 }
 
 /**
@@ -666,37 +664,34 @@  static int axienet_device_reset(struct net_device *ndev)
 
 /**
  * axienet_free_tx_chain - Clean up a series of linked TX descriptors.
- * @ndev:	Pointer to the net_device structure
+ * @lp:		Pointer to the axienet_local structure
  * @first_bd:	Index of first descriptor to clean up
- * @nr_bds:	Number of descriptors to clean up, can be -1 if unknown.
+ * @nr_bds:	Max number of descriptors to clean up
+ * @force:	Whether to clean descriptors even if not complete
  * @sizep:	Pointer to a u32 filled with the total sum of all bytes
  * 		in all cleaned-up descriptors. Ignored if NULL.
+ * @budget:	NAPI budget (use 0 when not called from NAPI poll)
  *
  * Would either be called after a successful transmit operation, or after
  * there was an error when setting up the chain.
  * Returns the number of descriptors handled.
  */
-static int axienet_free_tx_chain(struct net_device *ndev, u32 first_bd,
-				 int nr_bds, u32 *sizep)
+static int axienet_free_tx_chain(struct axienet_local *lp, u32 first_bd,
+				 int nr_bds, bool force, u32 *sizep, int budget)
 {
-	struct axienet_local *lp = netdev_priv(ndev);
 	struct axidma_bd *cur_p;
-	int max_bds = nr_bds;
 	unsigned int status;
 	dma_addr_t phys;
 	int i;
 
-	if (max_bds == -1)
-		max_bds = lp->tx_bd_num;
-
-	for (i = 0; i < max_bds; i++) {
+	for (i = 0; i < nr_bds; i++) {
 		cur_p = &lp->tx_bd_v[(first_bd + i) % lp->tx_bd_num];
 		status = cur_p->status;
 
-		/* If no number is given, clean up *all* descriptors that have
-		 * been completed by the MAC.
+		/* If force is not specified, clean up only descriptors
+		 * that have been completed by the MAC.
 		 */
-		if (nr_bds == -1 && !(status & XAXIDMA_BD_STS_COMPLETE_MASK))
+		if (!force && !(status & XAXIDMA_BD_STS_COMPLETE_MASK))
 			break;
 
 		/* Ensure we see complete descriptor update */
@@ -707,7 +702,7 @@  static int axienet_free_tx_chain(struct net_device *ndev, u32 first_bd,
 				 DMA_TO_DEVICE);
 
 		if (cur_p->skb && (status & XAXIDMA_BD_STS_COMPLETE_MASK))
-			dev_consume_skb_irq(cur_p->skb);
+			napi_consume_skb(cur_p->skb, budget);
 
 		cur_p->app0 = 0;
 		cur_p->app1 = 0;
@@ -755,34 +750,49 @@  static inline int axienet_check_tx_bd_space(struct axienet_local *lp,
 /**
  * axienet_start_xmit_done - Invoked once a transmit is completed by the
  * Axi DMA Tx channel.
- * @ndev:	Pointer to the net_device structure
+ * @napi:	Pointer to NAPI structure.
+ * @budget:	Max number of TX packets to process.
+ *
+ * Return: Number of TX packets processed.
  *
- * This function is invoked from the Axi DMA Tx isr to notify the completion
+ * This function is invoked from the NAPI processing to notify the completion
  * of transmit operation. It clears fields in the corresponding Tx BDs and
  * unmaps the corresponding buffer so that CPU can regain ownership of the
  * buffer. It finally invokes "netif_wake_queue" to restart transmission if
  * required.
  */
-static void axienet_start_xmit_done(struct net_device *ndev)
+static int axienet_tx_poll(struct napi_struct *napi, int budget)
 {
-	struct axienet_local *lp = netdev_priv(ndev);
-	u32 packets = 0;
+	struct axienet_local *lp = container_of(napi, struct axienet_local, napi_tx);
+	struct net_device *ndev = lp->ndev;
 	u32 size = 0;
+	int packets;
 
-	packets = axienet_free_tx_chain(ndev, lp->tx_bd_ci, -1, &size);
+	packets = axienet_free_tx_chain(lp, lp->tx_bd_ci, budget, false, &size, budget);
 
-	lp->tx_bd_ci += packets;
-	if (lp->tx_bd_ci >= lp->tx_bd_num)
-		lp->tx_bd_ci -= lp->tx_bd_num;
+	if (packets) {
+		lp->tx_bd_ci += packets;
+		if (lp->tx_bd_ci >= lp->tx_bd_num)
+			lp->tx_bd_ci %= lp->tx_bd_num;
 
-	ndev->stats.tx_packets += packets;
-	ndev->stats.tx_bytes += size;
+		ndev->stats.tx_packets += packets;
+		ndev->stats.tx_bytes += size;
 
-	/* Matches barrier in axienet_start_xmit */
-	smp_mb();
+		/* Matches barrier in axienet_start_xmit */
+		smp_mb();
 
-	if (!axienet_check_tx_bd_space(lp, MAX_SKB_FRAGS + 1))
-		netif_wake_queue(ndev);
+		if (!axienet_check_tx_bd_space(lp, MAX_SKB_FRAGS + 1))
+			netif_wake_queue(ndev);
+	}
+
+	if (packets < budget && napi_complete_done(napi, packets)) {
+		/* Re-enable TX completion interrupts. This should
+		 * cause an immediate interrupt if any TX packets are
+		 * already pending.
+		 */
+		axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr);
+	}
+	return packets;
 }
 
 /**
@@ -864,8 +874,8 @@  axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 			if (net_ratelimit())
 				netdev_err(ndev, "TX DMA mapping error\n");
 			ndev->stats.tx_dropped++;
-			axienet_free_tx_chain(ndev, orig_tail_ptr, ii + 1,
-					      NULL);
+			axienet_free_tx_chain(lp, orig_tail_ptr, ii + 1,
+					      true, NULL, 0);
 			lp->tx_bd_tail = orig_tail_ptr;
 
 			return NETDEV_TX_OK;
@@ -899,13 +909,13 @@  axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 }
 
 /**
- * axienet_poll - Triggered by RX ISR to complete the received BD processing.
+ * axienet_rx_poll - Triggered by RX ISR to complete the BD processing.
  * @napi:	Pointer to NAPI structure.
- * @budget:	Max number of packets to process.
+ * @budget:	Max number of RX packets to process.
  *
  * Return: Number of RX packets processed.
  */
-static int axienet_poll(struct napi_struct *napi, int budget)
+static int axienet_rx_poll(struct napi_struct *napi, int budget)
 {
 	u32 length;
 	u32 csumstatus;
@@ -914,7 +924,7 @@  static int axienet_poll(struct napi_struct *napi, int budget)
 	dma_addr_t tail_p = 0;
 	struct axidma_bd *cur_p;
 	struct sk_buff *skb, *new_skb;
-	struct axienet_local *lp = container_of(napi, struct axienet_local, napi);
+	struct axienet_local *lp = container_of(napi, struct axienet_local, napi_rx);
 
 	cur_p = &lp->rx_bd_v[lp->rx_bd_ci];
 
@@ -1040,7 +1050,15 @@  static irqreturn_t axienet_tx_irq(int irq, void *_ndev)
 			   (lp->tx_bd_v[lp->tx_bd_ci]).phys);
 		schedule_work(&lp->dma_err_task);
 	} else {
-		axienet_start_xmit_done(lp->ndev);
+		/* Disable further TX completion interrupts and schedule
+		 * NAPI to handle the completions.
+		 */
+		u32 cr = lp->tx_dma_cr;
+
+		cr &= ~(XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK);
+		axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr);
+
+		napi_schedule(&lp->napi_tx);
 	}
 
 	return IRQ_HANDLED;
@@ -1084,7 +1102,7 @@  static irqreturn_t axienet_rx_irq(int irq, void *_ndev)
 		cr &= ~(XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK);
 		axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
 
-		napi_schedule(&lp->napi);
+		napi_schedule(&lp->napi_rx);
 	}
 
 	return IRQ_HANDLED;
@@ -1160,7 +1178,8 @@  static int axienet_open(struct net_device *ndev)
 	/* Enable worker thread for Axi DMA error handling */
 	INIT_WORK(&lp->dma_err_task, axienet_dma_err_handler);
 
-	napi_enable(&lp->napi);
+	napi_enable(&lp->napi_rx);
+	napi_enable(&lp->napi_tx);
 
 	/* Enable interrupts for Axi DMA Tx */
 	ret = request_irq(lp->tx_irq, axienet_tx_irq, IRQF_SHARED,
@@ -1187,7 +1206,8 @@  static int axienet_open(struct net_device *ndev)
 err_rx_irq:
 	free_irq(lp->tx_irq, ndev);
 err_tx_irq:
-	napi_disable(&lp->napi);
+	napi_disable(&lp->napi_tx);
+	napi_disable(&lp->napi_rx);
 	phylink_stop(lp->phylink);
 	phylink_disconnect_phy(lp->phylink);
 	cancel_work_sync(&lp->dma_err_task);
@@ -1211,7 +1231,8 @@  static int axienet_stop(struct net_device *ndev)
 
 	dev_dbg(&ndev->dev, "axienet_close()\n");
 
-	napi_disable(&lp->napi);
+	napi_disable(&lp->napi_tx);
+	napi_disable(&lp->napi_rx);
 
 	phylink_stop(lp->phylink);
 	phylink_disconnect_phy(lp->phylink);
@@ -1732,7 +1753,8 @@  static void axienet_dma_err_handler(struct work_struct *work)
 						dma_err_task);
 	struct net_device *ndev = lp->ndev;
 
-	napi_disable(&lp->napi);
+	napi_disable(&lp->napi_tx);
+	napi_disable(&lp->napi_rx);
 
 	axienet_setoptions(ndev, lp->options &
 			   ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
@@ -1798,7 +1820,8 @@  static void axienet_dma_err_handler(struct work_struct *work)
 	axienet_set_mac_address(ndev, NULL);
 	axienet_set_multicast_list(ndev);
 	axienet_setoptions(ndev, lp->options);
-	napi_enable(&lp->napi);
+	napi_enable(&lp->napi_rx);
+	napi_enable(&lp->napi_tx);
 }
 
 /**
@@ -1847,7 +1870,8 @@  static int axienet_probe(struct platform_device *pdev)
 	lp->rx_bd_num = RX_BD_NUM_DEFAULT;
 	lp->tx_bd_num = TX_BD_NUM_DEFAULT;
 
-	netif_napi_add(ndev, &lp->napi, axienet_poll, NAPI_POLL_WEIGHT);
+	netif_napi_add(ndev, &lp->napi_rx, axienet_rx_poll, NAPI_POLL_WEIGHT);
+	netif_napi_add(ndev, &lp->napi_tx, axienet_tx_poll, NAPI_POLL_WEIGHT);
 
 	lp->axi_clk = devm_clk_get_optional(&pdev->dev, "s_axi_lite_clk");
 	if (!lp->axi_clk) {