diff options
author | Claudiu Manoil <claudiu.manoil@freescale.com> | 2013-10-14 10:05:09 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-10-18 15:54:43 -0400 |
commit | 3ba405db1c1b05d157474c71e559393f7ea436ad (patch) | |
tree | 89895769b4a4a7e78674f7d1b4503b94ce20635b /drivers/net/ethernet/freescale/gianfar.c | |
parent | 9877b25382e770618d0a36a3024d8a3c67eee9ea (diff) |
gianfar: Simplify MQ polling to avoid soft lockup
Under certain low traffic conditions, the single core
devices with multiple Rx/Tx queues (MQ mode) may reach
soft lockup due to gfar_poll not returning in proper time.
The following exception was obtained using iperf on a 100Mbit
half-duplex link, for a p1010 single core device:
BUG: soft lockup - CPU#0 stuck for 23s! [iperf:2847]
Modules linked in:
CPU: 0 PID: 2847 Comm: iperf Not tainted 3.12.0-rc3 #16
task: e8bf8000 ti: eeb16000 task.ti: ee646000
NIP: c0255b6c LR: c0367ae8 CTR: c0461c18
REGS: eeb17e70 TRAP: 0901 Not tainted (3.12.0-rc3)
MSR: 00029000 <CE,EE,ME> CR: 44228428 XER: 20000000
GPR00: c0367ad4 eeb17f20 e8bf8000 ee01f4b4 00000008 ffffffff ffffffff
00000000
GPR08: 000000c0 00000008 000000ff ffffffc0 000193fe
NIP [c0255b6c] find_next_bit+0xb8/0xc4
LR [c0367ae8] gfar_poll+0xc8/0x1d8
Call Trace:
[eeb17f20] [c0367ad4] gfar_poll+0xb4/0x1d8 (unreliable)
[eeb17f70] [c0422100] net_rx_action+0xa4/0x158
[eeb17fa0] [c003ec6c] __do_softirq+0xcc/0x17c
[eeb17ff0] [c000c28c] call_do_softirq+0x24/0x3c
[ee647cc0] [c0004660] do_softirq+0x6c/0x94
[ee647ce0] [c003eb9c] local_bh_enable+0x9c/0xa0
[ee647cf0] [c0454fe8] tcp_prequeue_process+0xa4/0xdc
[ee647d10] [c0457e44] tcp_recvmsg+0x498/0x96c
[ee647d80] [c047b630] inet_recvmsg+0x40/0x64
[ee647da0] [c040ca8c] sock_recvmsg+0x90/0xc0
[ee647e30] [c040edb8] SyS_recvfrom+0x98/0xfc
To prevent this, the outer while() loop has been removed
allowing gfar_poll() to return faster even if there's
still budget left. Also, there's no need to recompute
the budget per Rx queue anymore.
Signed-off-by: Claudiu Manoil <claudiu.manoil@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/freescale/gianfar.c')
-rw-r--r-- | drivers/net/ethernet/freescale/gianfar.c | 87 |
1 files changed, 38 insertions, 49 deletions
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index c4eaadeb572f..186dc4a489a4 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c | |||
@@ -2900,7 +2900,7 @@ static int gfar_poll(struct napi_struct *napi, int budget) | |||
2900 | struct gfar_priv_rx_q *rx_queue = NULL; | 2900 | struct gfar_priv_rx_q *rx_queue = NULL; |
2901 | int work_done = 0, work_done_per_q = 0; | 2901 | int work_done = 0, work_done_per_q = 0; |
2902 | int i, budget_per_q = 0; | 2902 | int i, budget_per_q = 0; |
2903 | int has_tx_work; | 2903 | int has_tx_work = 0; |
2904 | unsigned long rstat_rxf; | 2904 | unsigned long rstat_rxf; |
2905 | int num_act_queues; | 2905 | int num_act_queues; |
2906 | 2906 | ||
@@ -2915,62 +2915,51 @@ static int gfar_poll(struct napi_struct *napi, int budget) | |||
2915 | if (num_act_queues) | 2915 | if (num_act_queues) |
2916 | budget_per_q = budget/num_act_queues; | 2916 | budget_per_q = budget/num_act_queues; |
2917 | 2917 | ||
2918 | while (1) { | 2918 | for_each_set_bit(i, &gfargrp->tx_bit_map, priv->num_tx_queues) { |
2919 | has_tx_work = 0; | 2919 | tx_queue = priv->tx_queue[i]; |
2920 | for_each_set_bit(i, &gfargrp->tx_bit_map, priv->num_tx_queues) { | 2920 | /* run Tx cleanup to completion */ |
2921 | tx_queue = priv->tx_queue[i]; | 2921 | if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) { |
2922 | /* run Tx cleanup to completion */ | 2922 | gfar_clean_tx_ring(tx_queue); |
2923 | if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) { | 2923 | has_tx_work = 1; |
2924 | gfar_clean_tx_ring(tx_queue); | ||
2925 | has_tx_work = 1; | ||
2926 | } | ||
2927 | } | 2924 | } |
2925 | } | ||
2928 | 2926 | ||
2929 | for_each_set_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) { | 2927 | for_each_set_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) { |
2930 | /* skip queue if not active */ | 2928 | /* skip queue if not active */ |
2931 | if (!(rstat_rxf & (RSTAT_CLEAR_RXF0 >> i))) | 2929 | if (!(rstat_rxf & (RSTAT_CLEAR_RXF0 >> i))) |
2932 | continue; | 2930 | continue; |
2933 | |||
2934 | rx_queue = priv->rx_queue[i]; | ||
2935 | work_done_per_q = | ||
2936 | gfar_clean_rx_ring(rx_queue, budget_per_q); | ||
2937 | work_done += work_done_per_q; | ||
2938 | |||
2939 | /* finished processing this queue */ | ||
2940 | if (work_done_per_q < budget_per_q) { | ||
2941 | /* clear active queue hw indication */ | ||
2942 | gfar_write(®s->rstat, | ||
2943 | RSTAT_CLEAR_RXF0 >> i); | ||
2944 | rstat_rxf &= ~(RSTAT_CLEAR_RXF0 >> i); | ||
2945 | num_act_queues--; | ||
2946 | |||
2947 | if (!num_act_queues) | ||
2948 | break; | ||
2949 | /* recompute budget per Rx queue */ | ||
2950 | budget_per_q = | ||
2951 | (budget - work_done) / num_act_queues; | ||
2952 | } | ||
2953 | } | ||
2954 | 2931 | ||
2955 | if (work_done >= budget) | 2932 | rx_queue = priv->rx_queue[i]; |
2956 | break; | 2933 | work_done_per_q = |
2934 | gfar_clean_rx_ring(rx_queue, budget_per_q); | ||
2935 | work_done += work_done_per_q; | ||
2936 | |||
2937 | /* finished processing this queue */ | ||
2938 | if (work_done_per_q < budget_per_q) { | ||
2939 | /* clear active queue hw indication */ | ||
2940 | gfar_write(®s->rstat, | ||
2941 | RSTAT_CLEAR_RXF0 >> i); | ||
2942 | num_act_queues--; | ||
2943 | |||
2944 | if (!num_act_queues) | ||
2945 | break; | ||
2946 | } | ||
2947 | } | ||
2957 | 2948 | ||
2958 | if (!num_act_queues && !has_tx_work) { | 2949 | if (!num_act_queues && !has_tx_work) { |
2959 | 2950 | ||
2960 | napi_complete(napi); | 2951 | napi_complete(napi); |
2961 | 2952 | ||
2962 | /* Clear the halt bit in RSTAT */ | 2953 | /* Clear the halt bit in RSTAT */ |
2963 | gfar_write(®s->rstat, gfargrp->rstat); | 2954 | gfar_write(®s->rstat, gfargrp->rstat); |
2964 | 2955 | ||
2965 | gfar_write(®s->imask, IMASK_DEFAULT); | 2956 | gfar_write(®s->imask, IMASK_DEFAULT); |
2966 | 2957 | ||
2967 | /* If we are coalescing interrupts, update the timer | 2958 | /* If we are coalescing interrupts, update the timer |
2968 | * Otherwise, clear it | 2959 | * Otherwise, clear it |
2969 | */ | 2960 | */ |
2970 | gfar_configure_coalescing(priv, gfargrp->rx_bit_map, | 2961 | gfar_configure_coalescing(priv, gfargrp->rx_bit_map, |
2971 | gfargrp->tx_bit_map); | 2962 | gfargrp->tx_bit_map); |
2972 | break; | ||
2973 | } | ||
2974 | } | 2963 | } |
2975 | 2964 | ||
2976 | return work_done; | 2965 | return work_done; |