diff mbox

[net-next,V3,05/16] net: fec: reduce interrupts

Message ID 1459909562-22865-6-git-send-email-troy.kisky@boundarydevices.com (mailing list archive)
State New, archived
Headers show

Commit Message

Troy Kisky April 6, 2016, 2:25 a.m. UTC
By clearing the NAPI interrupts in the NAPI routine
and not in the interrupt handler, we can reduce the
number of interrupts. We also don't need any status
variables as the registers are still valid.

Also, notice that if budget pkts are received, the
next call to fec_enet_rx_napi will now continue to
receive the previously pending packets.

To test that this actually reduces interrupts, try
this command before/after patch

cat /proc/interrupts |grep ether; \
ping -s2800 192.168.0.201 -f -c1000 ; \
cat /proc/interrupts |grep ether

For me, before this patch is 2996 interrupts.
After patch is 2010 interrupts.

Signed-off-by: Troy Kisky <troy.kisky@boundarydevices.com>

---
v3:
Fix introduced bug of checking for FEC_ENET_TS_TIMER
before calling fec_ptp_check_pps_event

Changed commit message to show measured changes.

Used netdev_info instead of pr_info.

Fugang Duan suggested splitting TX and RX into two NAPI
contexts, but that should be a separate patch as it
is unrelated to what this patch does.
---
 drivers/net/ethernet/freescale/fec.h      |   6 +-
 drivers/net/ethernet/freescale/fec_main.c | 118 +++++++++++-------------------
 2 files changed, 45 insertions(+), 79 deletions(-)

Comments

Andy Duan April 6, 2016, 10:06 a.m. UTC | #1
From: Troy Kisky <troy.kisky@boundarydevices.com> Sent: Wednesday, April 06, 2016 10:26 AM
> To: netdev@vger.kernel.org; davem@davemloft.net; Fugang Duan
> <fugang.duan@nxp.com>; lznuaa@gmail.com
> Cc: Fabio Estevam <fabio.estevam@nxp.com>; l.stach@pengutronix.de;
> andrew@lunn.ch; tremyfr@gmail.com; gerg@uclinux.org; linux-arm-
> kernel@lists.infradead.org; johannes@sipsolutions.net;
> stillcompiling@gmail.com; sergei.shtylyov@cogentembedded.com;
> arnd@arndb.de; Troy Kisky <troy.kisky@boundarydevices.com>
> Subject: [PATCH net-next V3 05/16] net: fec: reduce interrupts
> 
> By clearing the NAPI interrupts in the NAPI routine and not in the interrupt
> handler, we can reduce the number of interrupts. We also don't need any status
> variables as the registers are still valid.
> 
> Also, notice that if budget pkts are received, the next call to fec_enet_rx_napi
> will now continue to receive the previously pending packets.
> 
> To test that this actually reduces interrupts, try this command before/after patch
> 
> cat /proc/interrupts |grep ether; \
> ping -s2800 192.168.0.201 -f -c1000 ; \
> cat /proc/interrupts |grep ether
> 
> For me, before this patch is 2996 interrupts.
> After patch is 2010 interrupts.
> 
> Signed-off-by: Troy Kisky <troy.kisky@boundarydevices.com>
>

As my previous comments on V2, if you want to improve performance, you can try to separate tx and rx napi process like calling netif_tx_napi_add() to initialize tx NAPI context.
David Miller April 6, 2016, 9:20 p.m. UTC | #2
From: Troy Kisky <troy.kisky@boundarydevices.com>
Date: Tue,  5 Apr 2016 19:25:51 -0700

> By clearing the NAPI interrupts in the NAPI routine
> and not in the interrupt handler, we can reduce the
> number of interrupts. We also don't need any status
> variables as the registers are still valid.
> 
> Also, notice that if budget pkts are received, the
> next call to fec_enet_rx_napi will now continue to
> receive the previously pending packets.
> 
> To test that this actually reduces interrupts, try
> this command before/after patch
> 
> cat /proc/interrupts |grep ether; \
> ping -s2800 192.168.0.201 -f -c1000 ; \
> cat /proc/interrupts |grep ether
> 
> For me, before this patch is 2996 interrupts.
> After patch is 2010 interrupts.
> 
> Signed-off-by: Troy Kisky <troy.kisky@boundarydevices.com>

I really don't think this is a good idea at all.

I would instead really rather see you stash away the
status register values into some piece of software state,
and then re-read them before you are about to finish a
NAPI poll cycle.
Troy Kisky April 7, 2016, 12:42 a.m. UTC | #3
On 4/6/2016 2:20 PM, David Miller wrote:
> From: Troy Kisky <troy.kisky@boundarydevices.com>
> Date: Tue,  5 Apr 2016 19:25:51 -0700
> 
>> By clearing the NAPI interrupts in the NAPI routine
>> and not in the interrupt handler, we can reduce the
>> number of interrupts. We also don't need any status
>> variables as the registers are still valid.
>>
>> Also, notice that if budget pkts are received, the
>> next call to fec_enet_rx_napi will now continue to
>> receive the previously pending packets.
>>
>> To test that this actually reduces interrupts, try
>> this command before/after patch
>>
>> cat /proc/interrupts |grep ether; \
>> ping -s2800 192.168.0.201 -f -c1000 ; \
>> cat /proc/interrupts |grep ether
>>
>> For me, before this patch is 2996 interrupts.
>> After patch is 2010 interrupts.
>>
>> Signed-off-by: Troy Kisky <troy.kisky@boundarydevices.com>
> 
> I really don't think this is a good idea at all.
> 
> I would instead really rather see you stash away the
> status register values into some piece of software state,
> and then re-read them before you are about to finish a
> NAPI poll cycle.
> 
> 

Sure, that's an easy change. But if a TX int is what caused the interrupt
and masks them, and then a RX packet happens before napi runs, do you want
the TX serviced 1st, or RX ?
David Miller April 7, 2016, 3:57 a.m. UTC | #4
From: Troy Kisky <troy.kisky@boundarydevices.com>
Date: Wed, 6 Apr 2016 17:42:47 -0700

> Sure, that's an easy change. But if a TX int is what caused the
> interrupt and masks them, and then a RX packet happens before napi
> runs, do you want the TX serviced 1st, or RX ?

If you properly split your driver up into seperate interrupt/poll
instances, one for TX one for RX, you wouldn't need to ask me
that question now would you?

:-)
Troy Kisky April 7, 2016, 3:50 p.m. UTC | #5
On 4/6/2016 8:57 PM, David Miller wrote:
> From: Troy Kisky <troy.kisky@boundarydevices.com>
> Date: Wed, 6 Apr 2016 17:42:47 -0700
> 
>> Sure, that's an easy change. But if a TX int is what caused the
>> interrupt and masks them, and then a RX packet happens before napi
>> runs, do you want the TX serviced 1st, or RX ?
> 
> If you properly split your driver up into seperate interrupt/poll
> instances, one for TX one for RX, you wouldn't need to ask me
> that question now would you?
> 
> :-)
> 

I absolutely claim no ownership :-)
diff mbox

Patch

diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 6dd0ba8..9d5bdc6 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -505,11 +505,7 @@  struct fec_enet_private {
 
 	unsigned int total_tx_ring_size;
 	unsigned int total_rx_ring_size;
-
-	unsigned long work_tx;
-	unsigned long work_rx;
-	unsigned long work_ts;
-	unsigned long work_mdio;
+	uint	events;
 
 	struct	platform_device *pdev;
 
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index b4d46f8..918ac82 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -70,8 +70,6 @@  static void fec_enet_itr_coal_init(struct net_device *ndev);
 
 #define DRIVER_NAME	"fec"
 
-#define FEC_ENET_GET_QUQUE(_x) ((_x == 0) ? 1 : ((_x == 1) ? 2 : 0))
-
 /* Pause frame feild and FIFO threshold */
 #define FEC_ENET_FCE	(1 << 5)
 #define FEC_ENET_RSEM_V	0x84
@@ -1257,21 +1255,6 @@  static void fec_txq(struct net_device *ndev, struct fec_enet_priv_tx_q *txq)
 		writel(0, txq->bd.reg_desc_active);
 }
 
-static void
-fec_enet_tx(struct net_device *ndev)
-{
-	struct fec_enet_private *fep = netdev_priv(ndev);
-	struct fec_enet_priv_tx_q *txq;
-	u16 queue_id;
-	/* First process class A queue, then Class B and Best Effort queue */
-	for_each_set_bit(queue_id, &fep->work_tx, FEC_ENET_MAX_TX_QS) {
-		clear_bit(queue_id, &fep->work_tx);
-		txq = fep->tx_queue[FEC_ENET_GET_QUQUE(queue_id)];
-		fec_txq(ndev, txq);
-	}
-	return;
-}
-
 static int
 fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff *skb)
 {
@@ -1505,70 +1488,34 @@  rx_processing_done:
 	return pkt_received;
 }
 
-static int
-fec_enet_rx(struct net_device *ndev, int budget)
-{
-	int     pkt_received = 0;
-	u16	queue_id;
-	struct fec_enet_private *fep = netdev_priv(ndev);
-	struct fec_enet_priv_rx_q *rxq;
-
-	for_each_set_bit(queue_id, &fep->work_rx, FEC_ENET_MAX_RX_QS) {
-		clear_bit(queue_id, &fep->work_rx);
-		rxq = fep->rx_queue[FEC_ENET_GET_QUQUE(queue_id)];
-		pkt_received += fec_rxq(ndev, rxq, budget - pkt_received);
-	}
-	return pkt_received;
-}
-
-static bool
-fec_enet_collect_events(struct fec_enet_private *fep, uint int_events)
-{
-	if (int_events == 0)
-		return false;
-
-	if (int_events & FEC_ENET_RXF_0)
-		fep->work_rx |= (1 << 2);
-	if (int_events & FEC_ENET_RXF_1)
-		fep->work_rx |= (1 << 0);
-	if (int_events & FEC_ENET_RXF_2)
-		fep->work_rx |= (1 << 1);
-
-	if (int_events & FEC_ENET_TXF_0)
-		fep->work_tx |= (1 << 2);
-	if (int_events & FEC_ENET_TXF_1)
-		fep->work_tx |= (1 << 0);
-	if (int_events & FEC_ENET_TXF_2)
-		fep->work_tx |= (1 << 1);
-
-	return true;
-}
-
 static irqreturn_t
 fec_enet_interrupt(int irq, void *dev_id)
 {
 	struct net_device *ndev = dev_id;
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	uint int_events;
 	irqreturn_t ret = IRQ_NONE;
+	uint eir = readl(fep->hwp + FEC_IEVENT);
+	uint int_events = eir & readl(fep->hwp + FEC_IMASK);
 
-	int_events = readl(fep->hwp + FEC_IEVENT);
-	writel(int_events, fep->hwp + FEC_IEVENT);
-	fec_enet_collect_events(fep, int_events);
-
-	if ((fep->work_tx || fep->work_rx) && fep->link) {
-		ret = IRQ_HANDLED;
-
+	if (int_events & (FEC_ENET_RXF | FEC_ENET_TXF)) {
 		if (napi_schedule_prep(&fep->napi)) {
 			/* Disable the NAPI interrupts */
 			writel(FEC_NAPI_IMASK, fep->hwp + FEC_IMASK);
 			__napi_schedule(&fep->napi);
+			int_events &= ~(FEC_ENET_RXF | FEC_ENET_TXF);
+			ret = IRQ_HANDLED;
+		} else {
+			fep->events |= int_events;
+			netdev_info(ndev, "couldn't schedule NAPI\n");
 		}
 	}
 
-	if (int_events & FEC_ENET_MII) {
+	if (int_events) {
 		ret = IRQ_HANDLED;
-		complete(&fep->mdio_done);
+		writel(int_events, fep->hwp + FEC_IEVENT);
+		if (int_events & FEC_ENET_MII) {
+			complete(&fep->mdio_done);
+		}
 	}
 
 	if (fep->ptp_clock)
@@ -1581,16 +1528,39 @@  static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
 {
 	struct net_device *ndev = napi->dev;
 	struct fec_enet_private *fep = netdev_priv(ndev);
-	int pkts;
-
-	pkts = fec_enet_rx(ndev, budget);
+	int pkts = 0;
+	uint events;
 
-	fec_enet_tx(ndev);
+	do {
+		events = readl(fep->hwp + FEC_IEVENT);
+		if (fep->events) {
+			events |= fep->events;
+			fep->events = 0;
+		}
+		events &= FEC_ENET_RXF | FEC_ENET_TXF;
+		if (!events) {
+			if (budget) {
+				napi_complete(napi);
+				writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+			}
+			return pkts;
+		}
 
-	if (pkts < budget) {
-		napi_complete(napi);
-		writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
-	}
+		writel(events, fep->hwp + FEC_IEVENT);
+		if (events & FEC_ENET_RXF_1)
+			pkts += fec_rxq(ndev, fep->rx_queue[1], budget - pkts);
+		if (events & FEC_ENET_RXF_2)
+			pkts += fec_rxq(ndev, fep->rx_queue[2], budget - pkts);
+		if (events & FEC_ENET_RXF_0)
+			pkts += fec_rxq(ndev, fep->rx_queue[0], budget - pkts);
+		if (events & FEC_ENET_TXF_1)
+			fec_txq(ndev, fep->tx_queue[1]);
+		if (events & FEC_ENET_TXF_2)
+			fec_txq(ndev, fep->tx_queue[2]);
+		if (events & FEC_ENET_TXF_0)
+			fec_txq(ndev, fep->tx_queue[0]);
+	} while (pkts < budget);
+	fep->events |= events & FEC_ENET_RXF;	/* save for next callback */
 	return pkts;
 }