diff options
author | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2013-10-15 11:20:06 +0000 |
---|---|---|
committer | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2013-10-15 11:20:06 +0000 |
commit | 7eb33c19eaab6a73adaebf25ecc2b235f3de51d4 (patch) | |
tree | c362e3e912eb953bd9a8e9d8d7fde54c7cdcb257 | |
parent | 22be9a0e94d63305bf65f47baa57cbdd78fc9de9 (diff) |
mpc85xx: revert the napi polling code to the one from an older kernel version to fix stability issues (#14020)
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@38409 3c298f89-4303-0410-b956-a3cf2f4a3e73
-rw-r--r-- | target/linux/mpc85xx/patches-3.10/200-gianfar_napi_poll_revert.patch | 162 |
1 files changed, 162 insertions, 0 deletions
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 new file mode 100644 index 0000000000..9f2b727580 --- /dev/null +++ b/target/linux/mpc85xx/patches-3.10/200-gianfar_napi_poll_revert.patch @@ -0,0 +1,162 @@ +--- 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(®s->ievent, IEVENT_RTX_MASK); + +- rstat_rxf = gfar_read(®s->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(®s->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(®s->rstat, gfargrp->rstat); ++ /* Clear the halt bit in RSTAT */ ++ gfar_write(®s->rstat, gfargrp->rstat); + +- gfar_write(®s->imask, IMASK_DEFAULT); ++ gfar_write(®s->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 |