mpc85xx: Fix NAPI poll mechanism in GIANFAR ethernet driver
authorFelix Fietkau <nbd@openwrt.org>
Tue, 19 Nov 2013 13:00:37 +0000 (13:00 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Tue, 19 Nov 2013 13:00:37 +0000 (13:00 +0000)
This patch fixes the NAPI poll mechanism in the GIANFAR ethernet driver, which
was not properly working since Linus Kernel Version 3,8.
Therefore the workaround patch to downgrade the GIANFAR ethernet driver to
Kernelversion v3.8 is obsoete.
This patch was extensivly testes with different network loads and types of
traffic. There is quite a substantial user base that reports proper Ethernet
function with TPlink-4900. This patch is based on the fixes from GINAFAR
maintainer Claudiu Manoli.

Signed-off-by: Thomas Huehn <thomas@net.t-labs.tu-berlin.de>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
SVN-Revision: 38865

target/linux/mpc85xx/patches-3.10/200-fix_gianfar_napi_poll.patch [new file with mode: 0644]
target/linux/mpc85xx/patches-3.10/200-gianfar_napi_poll_revert.patch [deleted file]

diff --git a/target/linux/mpc85xx/patches-3.10/200-fix_gianfar_napi_poll.patch b/target/linux/mpc85xx/patches-3.10/200-fix_gianfar_napi_poll.patch
new file mode 100644 (file)
index 0000000..a1877be
--- /dev/null
@@ -0,0 +1,109 @@
+--- a/drivers/net/ethernet/freescale/gianfar.c
++++ b/drivers/net/ethernet/freescale/gianfar.c
+@@ -2835,7 +2835,7 @@ static int gfar_poll(struct napi_struct 
+       struct gfar_priv_rx_q *rx_queue = NULL;
+       int work_done = 0, work_done_per_q = 0;
+       int i, budget_per_q = 0;
+-      int has_tx_work;
++      int has_tx_work = 0;
+       unsigned long rstat_rxf;
+       int num_act_queues;
+@@ -2850,62 +2850,48 @@ static int gfar_poll(struct napi_struct 
+       if (num_act_queues)
+               budget_per_q = budget/num_act_queues;
+-      while (1) {
+-              has_tx_work = 0;
+-              for_each_set_bit(i, &gfargrp->tx_bit_map, priv->num_tx_queues) {
+-                      tx_queue = priv->tx_queue[i];
+-                      /* run Tx cleanup to completion */
+-                      if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) {
+-                              gfar_clean_tx_ring(tx_queue);
+-                              has_tx_work = 1;
+-                      }
+-              }
+-
+-              for_each_set_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) {
+-                      /* skip queue if not active */
+-                      if (!(rstat_rxf & (RSTAT_CLEAR_RXF0 >> i)))
+-                              continue;
+-
+-                      rx_queue = priv->rx_queue[i];
+-                      work_done_per_q =
+-                              gfar_clean_rx_ring(rx_queue, budget_per_q);
+-                      work_done += work_done_per_q;
+-
+-                      /* finished processing this queue */
+-                      if (work_done_per_q < budget_per_q) {
+-                              /* clear active queue hw indication */
+-                              gfar_write(&regs->rstat,
+-                                         RSTAT_CLEAR_RXF0 >> i);
+-                              rstat_rxf &= ~(RSTAT_CLEAR_RXF0 >> i);
+-                              num_act_queues--;
+-
+-                              if (!num_act_queues)
+-                                      break;
+-                              /* recompute budget per Rx queue */
+-                              budget_per_q =
+-                                      (budget - work_done) / num_act_queues;
+-                      }
++      for_each_set_bit(i, &gfargrp->tx_bit_map, priv->num_tx_queues) {
++              tx_queue = priv->tx_queue[i];
++              /* run Tx cleanup to completion */
++              if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) {
++                      gfar_clean_tx_ring(tx_queue);
++                      has_tx_work = 1;
+               }
++      }
+-              if (work_done >= budget)
+-                      break;
++      for_each_set_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) {
++              /* skip queue if not active */
++              if (!(rstat_rxf & (RSTAT_CLEAR_RXF0 >> i)))
++                      continue;
++
++              rx_queue = priv->rx_queue[i];
++              work_done_per_q = gfar_clean_rx_ring(rx_queue, budget_per_q);
++              work_done += work_done_per_q;
++
++              /* finished processing this queue */
++              if (work_done_per_q < budget_per_q) {
++                      /* clear active queue hw indication */
++                      gfar_write(&regs->rstat, RSTAT_CLEAR_RXF0 >> i);
++                      num_act_queues--;
+-              if (!num_act_queues && !has_tx_work) {
++                      if (!num_act_queues)
++                              break;
++              }
++      }
+-                      napi_complete(napi);
++      if (!num_act_queues && !has_tx_work) {
++              napi_complete(napi);
+-                      /* Clear the halt bit in RSTAT */
+-                      gfar_write(&regs->rstat, gfargrp->rstat);
++              /* Clear the halt bit in RSTAT */
++              gfar_write(&regs->rstat, gfargrp->rstat);
+-                      gfar_write(&regs->imask, IMASK_DEFAULT);
++              gfar_write(&regs->imask, IMASK_DEFAULT);
+-                      /* If we are coalescing interrupts, update the timer
+-                       * Otherwise, clear it
+-                       */
+-                      gfar_configure_coalescing(priv, gfargrp->rx_bit_map,
+-                                                gfargrp->tx_bit_map);
+-                      break;
+-              }
++              /* If we are coalescing interrupts, update the timer
++               * Otherwise, clear it
++               */
++              gfar_configure_coalescing(priv, gfargrp->rx_bit_map,
++                                        gfargrp->tx_bit_map);
+       }
+       return work_done;
diff --git a/target/linux/mpc85xx/patches-3.10/200-gianfar_napi_poll_revert.patch b/target/linux/mpc85xx/patches-3.10/200-gianfar_napi_poll_revert.patch
deleted file mode 100644 (file)
index 7b0e6c9..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
---- a/drivers/net/ethernet/freescale/gianfar.c
-+++ b/drivers/net/ethernet/freescale/gianfar.c
-@@ -132,7 +132,7 @@ static int gfar_poll(struct napi_struct
- static void gfar_netpoll(struct net_device *dev);
- #endif
- int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit);
--static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue);
-+static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue);
- static void gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
-                              int amount_pull, struct napi_struct *napi);
- void gfar_halt(struct net_device *dev);
-@@ -2475,7 +2475,7 @@ static void gfar_align_skb(struct sk_buf
- }
- /* Interrupt Handler for Transmit complete */
--static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
-+static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue)
- {
-       struct net_device *dev = tx_queue->dev;
-       struct netdev_queue *txq;
-@@ -2575,6 +2575,8 @@ static void gfar_clean_tx_ring(struct gf
-       tx_queue->dirty_tx = bdp;
-       netdev_tx_completed_queue(txq, howmany, bytes_sent);
-+
-+      return howmany;
- }
- static void gfar_schedule_cleanup(struct gfar_priv_grp *gfargrp)
-@@ -2833,82 +2835,62 @@ static int gfar_poll(struct napi_struct
-       struct gfar __iomem *regs = gfargrp->regs;
-       struct gfar_priv_tx_q *tx_queue = NULL;
-       struct gfar_priv_rx_q *rx_queue = NULL;
--      int work_done = 0, work_done_per_q = 0;
--      int i, budget_per_q = 0;
--      int has_tx_work;
--      unsigned long rstat_rxf;
--      int num_act_queues;
-+      int rx_cleaned = 0, budget_per_queue = 0, rx_cleaned_per_queue = 0;
-+      int tx_cleaned = 0, i, left_over_budget = budget;
-+      unsigned long serviced_queues = 0;
-+      int num_queues = 0;
-+
-+      num_queues = gfargrp->num_rx_queues;
-+      budget_per_queue = budget/num_queues;
-       /* Clear IEVENT, so interrupts aren't called again
-        * because of the packets that have already arrived
-        */
-       gfar_write(&regs->ievent, IEVENT_RTX_MASK);
--      rstat_rxf = gfar_read(&regs->rstat) & RSTAT_RXF_MASK;
--
--      num_act_queues = bitmap_weight(&rstat_rxf, MAX_RX_QS);
--      if (num_act_queues)
--              budget_per_q = budget/num_act_queues;
--
--      while (1) {
--              has_tx_work = 0;
--              for_each_set_bit(i, &gfargrp->tx_bit_map, priv->num_tx_queues) {
--                      tx_queue = priv->tx_queue[i];
--                      /* run Tx cleanup to completion */
--                      if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) {
--                              gfar_clean_tx_ring(tx_queue);
--                              has_tx_work = 1;
--                      }
--              }
-+      while (num_queues && left_over_budget) {
-+              budget_per_queue = left_over_budget/num_queues;
-+              left_over_budget = 0;
-               for_each_set_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) {
--                      /* skip queue if not active */
--                      if (!(rstat_rxf & (RSTAT_CLEAR_RXF0 >> i)))
-+                      if (test_bit(i, &serviced_queues))
-                               continue;
--
-                       rx_queue = priv->rx_queue[i];
--                      work_done_per_q =
--                              gfar_clean_rx_ring(rx_queue, budget_per_q);
--                      work_done += work_done_per_q;
--
--                      /* finished processing this queue */
--                      if (work_done_per_q < budget_per_q) {
--                              /* clear active queue hw indication */
--                              gfar_write(&regs->rstat,
--                                         RSTAT_CLEAR_RXF0 >> i);
--                              rstat_rxf &= ~(RSTAT_CLEAR_RXF0 >> i);
--                              num_act_queues--;
--
--                              if (!num_act_queues)
--                                      break;
--                              /* recompute budget per Rx queue */
--                              budget_per_q =
--                                      (budget - work_done) / num_act_queues;
-+                      tx_queue = priv->tx_queue[rx_queue->qindex];
-+
-+                      tx_cleaned += gfar_clean_tx_ring(tx_queue);
-+                      rx_cleaned_per_queue =
-+                              gfar_clean_rx_ring(rx_queue, budget_per_queue);
-+                      rx_cleaned += rx_cleaned_per_queue;
-+                      if (rx_cleaned_per_queue < budget_per_queue) {
-+                              left_over_budget = left_over_budget +
-+                                      (budget_per_queue -
-+                                       rx_cleaned_per_queue);
-+                              set_bit(i, &serviced_queues);
-+                              num_queues--;
-                       }
-               }
-+      }
--              if (work_done >= budget)
--                      break;
--
--              if (!num_act_queues && !has_tx_work) {
-+      if (tx_cleaned)
-+              return budget;
--                      napi_complete(napi);
-+      if (rx_cleaned < budget) {
-+              napi_complete(napi);
--                      /* Clear the halt bit in RSTAT */
--                      gfar_write(&regs->rstat, gfargrp->rstat);
-+              /* Clear the halt bit in RSTAT */
-+              gfar_write(&regs->rstat, gfargrp->rstat);
--                      gfar_write(&regs->imask, IMASK_DEFAULT);
-+              gfar_write(&regs->imask, IMASK_DEFAULT);
--                      /* If we are coalescing interrupts, update the timer
--                       * Otherwise, clear it
--                       */
--                      gfar_configure_coalescing(priv, gfargrp->rx_bit_map,
--                                                gfargrp->tx_bit_map);
--                      break;
--              }
-+              /* If we are coalescing interrupts, update the timer
-+               * Otherwise, clear it
-+               */
-+              gfar_configure_coalescing(priv, gfargrp->rx_bit_map,
-+                                        gfargrp->tx_bit_map);
-       }
--      return work_done;
-+      return rx_cleaned;
- }
- #ifdef CONFIG_NET_POLL_CONTROLLER
---- a/drivers/net/ethernet/freescale/gianfar.h
-+++ b/drivers/net/ethernet/freescale/gianfar.h
-@@ -291,9 +291,7 @@ extern const char gfar_driver_version[];
- #define RCTRL_PADDING(x)      ((x << 16) & RCTRL_PAL_MASK)
--#define RSTAT_CLEAR_RHALT     0x00800000
--#define RSTAT_CLEAR_RXF0      0x00000080
--#define RSTAT_RXF_MASK                0x000000ff
-+#define RSTAT_CLEAR_RHALT       0x00800000
- #define TCTRL_IPCSEN          0x00004000
- #define TCTRL_TUCSEN          0x00002000